kerryjiang / WebSocket4Net

A popular .NET WebSocket Client
752 stars 272 forks source link

Unhandled "cannot access a disposed object" exception #141

Open LolliDepp opened 5 years ago

LolliDepp commented 5 years ago

This seems to have happened seconds after the client handled a disconnect from the server. The client had been running for about 2 hours already

I didn't delve in the source just yet but it looks like this may be solved just by checking if the timer is disposed

Here are the logs:

Cannot access a disposed object. at System.Threading.TimerQueueTimer.Change(UInt32 dueTime, UInt32 period) at WebSocket4Net.WebSocket.ClearTimer() at WebSocket4Net.WebSocket.FireClosed() at WebSocket4Net.WebSocket.OnClosed() at SuperSocket.ClientEngine.ClientSession.OnClosed() at SuperSocket.ClientEngine.AsyncTcpSession.ProcessReceive(SocketAsyncEventArgs e) at System.Net.Sockets.SocketAsyncEventArgs.OnCompleted(SocketAsyncEventArgs e) at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx) at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx) at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state) at System.Net.Sockets.SocketAsyncEventArgs.FinishOperationSuccess(SocketError socketError, Int32 bytesTransferred, SocketFlags flags) at System.Net.Sockets.SocketAsyncEventArgs.CompletionPortCallback(UInt32 errorCode, UInt32 numBytes, NativeOverlapped nativeOverlapped) at System.Threading._IOCompletionCallback.PerformIOCompletionCallback(UInt32 errorCode, UInt32 numBytes, NativeOverlapped pOVERLAP)

aady501 commented 4 years ago

Faced similar issue. Though I see a fix already applied for handling a disposed timer here - a4bcac3, using v0.15.2

Polimat commented 4 years ago

Catched same exception in v0.15.2.11

NektoDron commented 4 years ago

me to System.ObjectDisposedException: Cannot access a disposed object. at System.Threading.TimerQueueTimer.Change(UInt32 dueTime, UInt32 period) at System.Threading.Timer.Change(Int32 dueTime, Int32 period) at WebSocket4Net.WebSocket.ClearTimer() at WebSocket4Net.WebSocket.FireClosed() at WebSocket4Net.WebSocket.OnClosed() at SuperSocket.ClientEngine.AuthenticatedStreamTcpSession.SendInternal(PosList`1 items) at System.Net.LazyAsyncResult.Complete(IntPtr userToken) at System.Net.LazyAsyncResult.ProtectedInvokeCallback(Object result, IntPtr userToken) at System.Net.Security._SslStream.StartWriting(Byte[] buffer, Int32 offset, Int32 count, AsyncProtocolRequest asyncRequest) at System.Net.Security._SslStream.WriteCallback(IAsyncResult transportResult) at System.Net.LazyAsyncResult.Complete(IntPtr userToken) at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx) at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx) at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state) at System.Net.ContextAwareResult.Complete(IntPtr userToken) at System.Net.LazyAsyncResult.ProtectedInvokeCallback(Object result, IntPtr userToken) at System.Net.Sockets.BaseOverlappedAsyncResult.CompletionPortCallback(UInt32 errorCode, UInt32 numBytes, NativeOverlapped nativeOverlapped) at System.Threading._IOCompletionCallback.PerformIOCompletionCallback(UInt32 errorCode, UInt32 numBytes, NativeOverlapped pOVERLAP)

aubiyko commented 4 years ago

Can also confirm. Interlocked.CompareExchange was changed to lockstatement at 1ab610a. I think this change is not equivalent. In my case, this bug happens when both client and server close connection. I suppose that two threads may enter that lock section: my code and data receiving thread. When my code calls Close first, then ping timer is replaced by closing handshake timer. But at the same time receiving thread may also call ClearTimer, so both will simultaneously enter lock. Receiving thread code will check m_WebSocketTimer that will not be null, then dispose old local timer copy that have already been disposed by my code.

MarkoPielic commented 1 year ago

Same error here as well

Cannot access a disposed object. at System.Threading.TimerQueueTimer.Change(UInt32 dueTime, UInt32 period) at System.Threading.Timer.Change(Int32 dueTime, Int32 period) at WebSocket4Net.WebSocket.ClearTimer() at WebSocket4Net.WebSocket.FireClosed() at WebSocket4Net.WebSocket.OnClosed() at SuperSocket.ClientEngine.AuthenticatedStreamTcpSession.OnDataRead(IAsyncResult result) at System.Net.LazyAsyncResult.Complete(IntPtr userToken) at System.Net.LazyAsyncResult.ProtectedInvokeCallback(Object result, IntPtr userToken) at System.Net.AsyncProtocolRequest.CompleteWithError(Exception e) at System.Net.Security._SslStream.ReadHeaderCallback(AsyncProtocolRequest asyncRequest) at System.Net.AsyncProtocolRequest.CompleteRequest(Int32 result) at System.Net.FixedSizeReader.CheckCompletionBeforeNextRead(Int32 bytes) at System.Net.FixedSizeReader.ReadCallback(IAsyncResult transportResult) at System.Net.LazyAsyncResult.Complete(IntPtr userToken) at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx) at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx) at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state) at System.Net.ContextAwareResult.Complete(IntPtr userToken) at System.Net.LazyAsyncResult.ProtectedInvokeCallback(Object result, IntPtr userToken) at System.Net.Sockets.BaseOverlappedAsyncResult.CompletionPortCallback(UInt32 errorCode, UInt32 numBytes, NativeOverlapped nativeOverlapped) at System.Threading._IOCompletionCallback.PerformIOCompletionCallback(UInt32 errorCode, UInt32 numBytes, NativeOverlapped pOVERLAP)

julbra commented 1 year ago

We are hitting this routinely during stress tests.

Could it be caused by the double-checked locking on a non-volatile field used inside ClearTimer() here: https://github.com/kerryjiang/WebSocket4Net/blob/v0.15/WebSocket4Net/WebSocket.cs#L710