CoAPnet
CoAPnet copied to clipboard
CoapClient.Dispose blocking for some reason
For some reason, that I wasn't able to find yet, the Dispose function of the client is somehow blocked / won't exit.
It doesn't matter If I run the code in a unit test or a console application.
I'm using a using
statement, just like in the sample code. The code runs fine. I implemented a couple of functions to work with the Ikea Gateway.
But as soon as my code hits the end of the using statement, it will just run forever. I also tried the example code from the wiki and I have the same behavior. So that could serve as my example code for the issue. But if I could provide any more details that can help you fix that, please let me know.
I'm using a MacBook Pro with Big Sur 11.2.3 and .NET 5 (5.0.102).
That sounds strange to me. Please try with netstandard because I assume the following line may be the problem: https://github.com/chkr1011/CoAPnet/blob/master/Source/CoAPnet/Transport/TcpCoapTransportLayer.cs#L35
The tcp client is not set and not closed with the ? operator. I assume that also .NET 5.0 must be added to the compiler pragma because it should use the same code path as netstandard I guess.
I'm not sure about the netstandard part. The nuget package is available for .NetStandard 2.0 and .NET Framework 4.5.2. So it's compiled for these two targets, correct?
With .NET5, the netstandard target is used already. Would it help, if I post the stack at the end of the program?
Main Thread:
ManualResetEventSlim.Wait()
Task.SpinThenBlockingWait()
Task.InternalWaitCore()
TaskAwaiter.HandleNonSuccessAndDebuggerNotification()
TaskAwaiter.GetResult()
Program.<Main>()
Other threads:
SpinWait.SpinOnceCore()
SpinWait.SpinOnce()
SafeSocketHandle.CloseAsIs()
Socket.Dispose()
Socket.Dispose()
UdpTransport.Dispose()
UdpTransport.Close()
DtlsCoapTransportLayer.<ReceiveAsync>b__13_0()
CancellationToken.<>c.<.cctor>b__26_0()
CancellationTokenSource.CallbackNode.<>c.<ExecuteCallback>b__9_0()
ExecutionContext.RunInternal() [4]
CancellationTokenSource.CallbackNode.ExecuteCallback()
CancellationTokenSource.ExecuteCallbackHandlers()
CancellationTokenSource.NotifyCancellation()
CancellationTokenSource.Cancel()
CoapClient.Dispose()
async Class1.Method1()
AsyncTaskMethodBuilder<VoidTaskResult>.AsyncStateMachineBox<__Canon>.ExecutionContextCallback()
ExecutionContext.RunInternal() [3]
AsyncTaskMethodBuilder<VoidTaskResult>.AsyncStateMachineBox<Class1.<Method1>d__0>.MoveNext()
AsyncTaskMethodBuilder<VoidTaskResult>.AsyncStateMachineBox<__Canon>.MoveNext()
AwaitTaskContinuation.RunOrScheduleAction() [3]
Task.RunContinuations() [3]
Task.FinishContinuations() [3]
Task<__Canon>.TrySetResult() [3]
AsyncTaskMethodBuilder<__Canon>.SetExistingTaskResult() [3]
AsyncTaskMethodBuilder<CoapResponse>.SetResult()
CoapClient.<RequestAsync>d__12.MoveNext()
AsyncTaskMethodBuilder<CoapResponse>.AsyncStateMachineBox<CoapClient.<RequestAsync>d__12>.ExecutionContextCallback()
ExecutionContext.RunInternal() [2]
AsyncTaskMethodBuilder<CoapResponse>.AsyncStateMachineBox<CoapClient.<RequestAsync>d__12>.MoveNext()
AsyncTaskMethodBuilder<CoapResponse>.AsyncStateMachineBox<CoapClient.<RequestAsync>d__12>.MoveNext()
AwaitTaskContinuation.RunOrScheduleAction() [2]
Task.RunContinuations() [2]
Task.FinishContinuations() [2]
Task<__Canon>.TrySetResult() [2]
AsyncTaskMethodBuilder<__Canon>.SetExistingTaskResult() [2]
AsyncTaskMethodBuilder<CoapMessage>.SetResult() [2]
CoapClient.<RequestAsync>d__15.MoveNext()
AsyncTaskMethodBuilder<CoapMessage>.AsyncStateMachineBox<CoapClient.<RequestAsync>d__15>.ExecutionContextCallback()
ExecutionContext.RunInternal() [1]
AsyncTaskMethodBuilder<CoapMessage>.AsyncStateMachineBox<CoapClient.<RequestAsync>d__15>.MoveNext()
AsyncTaskMethodBuilder<CoapMessage>.AsyncStateMachineBox<CoapClient.<RequestAsync>d__15>.MoveNext()
AwaitTaskContinuation.RunOrScheduleAction() [1]
Task.RunContinuations() [1]
Task.FinishContinuations() [1]
Task<__Canon>.TrySetResult() [1]
AsyncTaskMethodBuilder<__Canon>.SetExistingTaskResult() [1]
AsyncTaskMethodBuilder<CoapMessage>.SetResult() [1]
CoapMessageAwaiter.<WaitOneAsync>d__4.MoveNext()
AsyncTaskMethodBuilder<CoapMessage>.AsyncStateMachineBox<CoapMessageAwaiter.<WaitOneAsync>d__4>.ExecutionContextCallback()
ExecutionContext.RunFromThreadPoolDispatchLoop()
AsyncTaskMethodBuilder<CoapMessage>.AsyncStateMachineBox<CoapMessageAwaiter.<WaitOneAsync>d__4>.MoveNext()
AsyncTaskMethodBuilder<CoapMessage>.AsyncStateMachineBox<CoapMessageAwaiter.<WaitOneAsync>d__4>.ExecuteFromThreadPool()
ThreadPoolWorkQueue.Dispatch()
_ThreadPoolWaitCallback.PerformWaitCallback()
[Native to Managed Transition]
async .()
SocketPal.SysReceive()
SocketPal.TryCompleteReceiveFrom()
SocketAsyncContext.ReceiveFrom()
SocketPal.ReceiveFrom()
Socket.ReceiveFrom()
UdpTransport.Receive()
DtlsRecordLayer.ReceiveRecord()
DtlsRecordLayer.Receive()
DtlsTransport.Receive()
DtlsCoapTransportLayer.ReceiveAsync()
CoapTransportLayerAdapter.<ReceiveAsync>d__5.MoveNext()
AsyncMethodBuilderCore.Start<CoAPnet.Transport.CoapTransportLayerAdapter.<ReceiveAsync>d__5>()
AsyncTaskMethodBuilder<int>.Start<CoAPnet.Transport.CoapTransportLayerAdapter.<ReceiveAsync>d__5>()
CoapTransportLayerAdapter.ReceiveAsync()
LowLevelCoapClient.<ReceiveAsync>d__9.MoveNext()
AsyncMethodBuilderCore.Start<CoAPnet.LowLevelClient.LowLevelCoapClient.<ReceiveAsync>d__9>()
AsyncTaskMethodBuilder<CoapMessage>.Start<CoAPnet.LowLevelClient.LowLevelCoapClient.<ReceiveAsync>d__9>()
LowLevelCoapClient.ReceiveAsync()
CoapClient.<ReceiveMessages>d__17.MoveNext()
AsyncMethodBuilderCore.Start<CoAPnet.Client.CoapClient.<ReceiveMessages>d__17>()
AsyncTaskMethodBuilder.Start<CoAPnet.Client.CoapClient.<ReceiveMessages>d__17>()
CoapClient.ReceiveMessages()
CoapClient.<ConnectAsync>b__11_0()
Task<Task>.InnerInvoke()
Task.<>c.<.cctor>b__277_0()
ExecutionContext.RunInternal() [2]
Task.ExecuteWithThreadLocal()
Task.ExecuteEntryUnsafe()
ThreadPoolTaskScheduler.<>c.<.cctor>b__10_0()
ThreadHelper.ThreadStart_Context()
ExecutionContext.RunInternal() [1]
ThreadHelper.ThreadStart()
[Native to Managed Transition]
You maybe need to upgrade bouncycastle. I saw that newer versions got some DTLS fixes with infinite loops etc. https://www.bouncycastle.org/csharp/#RELEASENOTES189
I updated the BouncyCastle package, but that did not fix it. I forked your project. Maybe I can take a look at the problem. When I Iook at the second stack trace, it looks like the call to "UdpTransport.Close()" that was initialized by the CancellationToken leads to some kind of lock.
SpinWait.SpinOnceCore()
SpinWait.SpinOnce()
SafeSocketHandle.CloseAsIs()
Socket.Dispose()
Socket.Dispose()
UdpTransport.Dispose()
UdpTransport.Close() <-- 3) The CancellationToken has a registered callback to call Close() on the UdpTransport
DtlsCoapTransportLayer.<ReceiveAsync>b__13_0()
CancellationToken.<>c.<.cctor>b__26_0()
CancellationTokenSource.CallbackNode.<>c.<ExecuteCallback>b__9_0()
ExecutionContext.RunInternal() [4]
CancellationTokenSource.CallbackNode.ExecuteCallback()
CancellationTokenSource.ExecuteCallbackHandlers()
CancellationTokenSource.NotifyCancellation()
CancellationTokenSource.Cancel() <-- 2) ... and that call the cancel method
CoapClient.Dispose() <-- 1) I think this is triggered by the end of the using statement
...
But having a lock in these classes cannot be fixed in this library I guess. Could you please try to call socket.Shutdown() before disposing the socket? What happens when you completely remove the call to socket.Dispose().
Shutdown throws an exception, that the socket isn't open.
When I remove the _socket?.Dispose()
in UdpTransport.cs
, it seems to work fine.
Maybe it is related to an issue like this one: https://github.com/dotnet/runtime/issues/42686
That looks similar, indeed.
@n-develop Is this issue not yet fixed with a new .NET version?