src/Arcp.Client/Transport/WebSocket.fs documents that concurrent sends are serialized through sendLock, but sendOne only holds the lock while creating the socket.SendAsync task. The task is awaited after the lock has been released, so two callers can enter SendAsync concurrently on the same WebSocket. The BCL WebSocket contract allows only one send operation at a time, and overlapping sends from auto-ack, pong, submit, cancel, or close paths can throw or corrupt message ordering under load.
Fix prompt: replace the synchronous obj lock in WebSocketClientTransport.sendOne with an async-aware serializer such as SemaphoreSlim. Await WaitAsync ct, call and await socket.SendAsync while the semaphore is held, and release it in a finally block. Add a focused unit or integration test using a fake or loopback WebSocket transport that starts multiple SendAsync calls concurrently and verifies the underlying sends do not overlap and preserve envelope order.
src/Arcp.Client/Transport/WebSocket.fsdocuments that concurrent sends are serialized throughsendLock, butsendOneonly holds the lock while creating thesocket.SendAsynctask. The task is awaited after the lock has been released, so two callers can enterSendAsyncconcurrently on the sameWebSocket. The BCL WebSocket contract allows only one send operation at a time, and overlapping sends from auto-ack, pong, submit, cancel, or close paths can throw or corrupt message ordering under load.Fix prompt: replace the synchronous
objlock inWebSocketClientTransport.sendOnewith an async-aware serializer such asSemaphoreSlim. AwaitWaitAsync ct, call and awaitsocket.SendAsyncwhile the semaphore is held, and release it in afinallyblock. Add a focused unit or integration test using a fake or loopback WebSocket transport that starts multipleSendAsynccalls concurrently and verifies the underlying sends do not overlap and preserve envelope order.