Suppose I have a server that can both receive messages from the client and push messages unprompted. My sense is that the most natural way to write it is
- Fork a thread (
push-thread) for pushing that calls sendTextData or similar.
- Have the main thread (
pull-thread) calling receiveDataMessage or similar.
- In
pull-thread, catch CloseRequest and use it to kill push-thread.
But that's vulnerable to race conditions, because (afaict) it's possible for push-thread to try to send data in between pull-thread completing a close handshake and killing push-thread.
As-is, I can think of two ways around this.
- Don't use
receiveDataMessage. Just use receive and handle control messages manually.
conn' = conn { connectionWrite = onClose conn.connectionWrite act } where onClose oldWrite act msgs checks if any of the msgs is a Close and calls act if so; then regardless it passes them all on to oldWrite.
I don't love either of these. As an alternative, can we have a hook intended for this use case, something like
data CloseInitiatedBy = CloseInitiatedByUs | CloseInitiatedByThem
connectionOnClose :: CloseInitiatedBy -> IO ()
that gets called immediately before we send a close message?
(Though I think figuring out who initiated isn't possible right now... we'd could try something like connectionReceivedClose :: IORef Bool, but that could theoretically fail if both sides try to initiate around the same time. But that situation is already awkward because you might try to send two Close requests and one of them will throw an error. So I dunno, maybe it's fine? Or just not having CloseInitiatedBy in the hook and tell the user they need to figure this out if necessary?)
Suppose I have a server that can both receive messages from the client and push messages unprompted. My sense is that the most natural way to write it is
push-thread) for pushing that callssendTextDataor similar.pull-thread) callingreceiveDataMessageor similar.pull-thread, catchCloseRequestand use it to killpush-thread.But that's vulnerable to race conditions, because (afaict) it's possible for
push-threadto try to send data in betweenpull-threadcompleting a close handshake and killingpush-thread.As-is, I can think of two ways around this.
receiveDataMessage. Just usereceiveand handle control messages manually.conn' = conn { connectionWrite = onClose conn.connectionWrite act }whereonClose oldWrite act msgschecks if any of themsgsis aCloseand callsactif so; then regardless it passes them all on tooldWrite.I don't love either of these. As an alternative, can we have a hook intended for this use case, something like
that gets called immediately before we send a close message?
(Though I think figuring out who initiated isn't possible right now... we'd could try something like
connectionReceivedClose :: IORef Bool, but that could theoretically fail if both sides try to initiate around the same time. But that situation is already awkward because you might try to send twoCloserequests and one of them will throw an error. So I dunno, maybe it's fine? Or just not havingCloseInitiatedByin the hook and tell the user they need to figure this out if necessary?)