Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Poller.remove socket disposed fix #835
base: master
Are you sure you want to change the base?
Poller.remove socket disposed fix #835
Changes from 11 commits
3b8dbb9
d0f2725
53e1833
68429d7
dea90f4
6b344ca
c879804
54c2931
bc362a9
98a0f00
a3819e1
4f3b1e6
33816cb
bda8b3f
cb16939
544bb94
87b7649
92163b1
51457d1
fce2bb0
38910d8
3bacfb8
455f6b0
52fa20e
884d4ea
9f17258
3ea0693
72ebdbc
c14f9f9
0a767c6
fa5e49b
83a34b6
217e4f4
File filter
Filter by extension
Conversations
Jump to
There are no files selected for viewing
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Breaking public API is not a good idea, and goes against the C4.1 process that this project uses.
I agree that returning a task would be better. It'd be better to create overloads:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
100% agree, but was trying to avoid that because I originally thought only the
Remove
methods were public, and chainging only the non-publicRunAsync
method would avoid breaking changes...Why is
RunAsync
even public?The only reason I can see is to work around the original thread race bug when calling
Remove()
?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
NetMQPoller
is aTaskScheduler
and can be used to schedule work to run on the same synchronisation context (in this case: thread) as the poller.RunAsync
is useful when you need to schedule work that needs to be on that thread, which is not an uncommon requirement.Regardless, it's public API and must be preserved.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Got that.
I think a public
RunAsync
is a design flaw. NetMQ objects,NetMQPoller
in this case could and should handle marshalling work that needs to be done on the polling thread internally. Making it public exposes a large path for external code to breakNetMQPoller
and is the root source of the issue being discussed. The only case I can think of currently is this case (add/remove a socket from the polled group). Certainly, the primary use case for this method is the async polling of the sockets in the list for data... for which all the rest of the framework code is either internal to NetMQ or protected/private, which makes sense.I suggest that the
public RunAsync
be marked as deprecated..... this doesn't break the API, but does start the process toward resolving the root issue.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rather than instantiate a new completed task for each invocation, cache an instance in a
CompletedTask
field and reuse it. You can do that in an internal utility class if you like, maybe calledTasks
.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This same pattern is also in existing overloaded method
ContainsAsync
. Also, I'm not convinced it is a good idea to shareTaskCompletionSource
instances among (potentially) multiple threads...?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A completed task is safe to share. Use
Task.CompletedTask
for TFMs that support it, and cache your own (from aTaskCompletionSource<>
) for remaining TFMs.The benefit of doing so is that this code path will not perform any heap allocations.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Task.Factory.StartNew
is preferred overnew Task(...).Start
(article).There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ahh.... yes, I was wishing
Thread.Start()
was fluent.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Even if
Thread.Start
did exist, it would be too heavyweight. We don't need or want to start a new thread for this background work. We need this task to run asynchronously on the poller's thread, which is achieved by passingthis
as theTask
'sTaskScheduler
.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sry, I said
Thread.Start
but I meantTask.Start()
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd remove this from the code and discuss in an issue/PR instead where conversation can happen more easily.
The rationale here was to help people discover when they're tearing things down in the wrong order, as that is a common cause of deadlocks and other problems. If you're going to fail some of the time, it's better to fail all of the time as it makes it easier to write code correctly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, this is a note to do that =)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Calling
Wait
on tasks can lead to deadlocks in complex code.I'd suggest making a
RemoveAsync
overload that returnsTask
.As an aside, it's better to use
GetAwaiter().GetResult()
thanWait()
, mostly because the latter wraps any thrown exception in a redundantAggregateException
which can make debugging more difficult. Still, synchronous blocking waits in library code like this should be avoided. There are definitely user scenarios where deadlocks can occur.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Lol.
@ first i tried
await
, but there was an issue usingawait
in .NET40.... so I changed it to Wait().... forgot about these methods.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The problem likely was that you cannot
await
in a non-async
method.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the latest addresses this by adding new
RemoveAsync()
method, and reverting the old version, along with the original issue.I marked the
Remove
methods asObsolete
...There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Again, synchronous waits are not safe. This would have to be a
RemoveAsync
overload.