- 24.5k
- 4
- 53
- 89
Your code does have race conditions, so it won't work correctly. I think your main problem is that you make some "destructive" operation (e.g. dequeue a handler from the queue) without knowing that the followup operation will actually succeed and not handling the eventual failure in any way.
For example, the following sequence of events could happen:
- The class is created in unset state.
WaitAsync()
is called, creating aHandler
, adding it to the queue and returning aTask
.Set()
is called, settingisSet
to1
.- The handler from step 2 is dequeued.
WaitAsync()
is called from another thread. SinceisSet
is1
, it setsisSet
back to0
and immediately returns a completedTask
.- Back on the previous thread,
TryUnsubscribe()
is called in the handler from step 4 and succeeds. - The handler is
Invoke()
d, butTryReset()
fails. Set()
now returns.
After this, the Task
from step 2 is now orphaned, it will never be completed, because its handler is not in the queue anymore.
I suggest you don't write code like this by yourself, but instead use code written by others with more experience in concurrent programming. Specifically, you could use Stephen Toub's AsyncAutoResetEvent
Stephen Toub's AsyncAutoResetEvent
or AsyncAutoResetEvent
from Stephen Cleary's AsyncEx library.
Your code does have race conditions, so it won't work correctly. I think your main problem is that you make some "destructive" operation (e.g. dequeue a handler from the queue) without knowing that the followup operation will actually succeed and not handling the eventual failure in any way.
For example, the following sequence of events could happen:
- The class is created in unset state.
WaitAsync()
is called, creating aHandler
, adding it to the queue and returning aTask
.Set()
is called, settingisSet
to1
.- The handler from step 2 is dequeued.
WaitAsync()
is called from another thread. SinceisSet
is1
, it setsisSet
back to0
and immediately returns a completedTask
.- Back on the previous thread,
TryUnsubscribe()
is called in the handler from step 4 and succeeds. - The handler is
Invoke()
d, butTryReset()
fails. Set()
now returns.
After this, the Task
from step 2 is now orphaned, it will never be completed, because its handler is not in the queue anymore.
I suggest you don't write code like this by yourself, but instead use code written by others with more experience in concurrent programming. Specifically, you could use Stephen Toub's AsyncAutoResetEvent
or AsyncAutoResetEvent
from Stephen Cleary's AsyncEx library.
Your code does have race conditions, so it won't work correctly. I think your main problem is that you make some "destructive" operation (e.g. dequeue a handler from the queue) without knowing that the followup operation will actually succeed and not handling the eventual failure in any way.
For example, the following sequence of events could happen:
- The class is created in unset state.
WaitAsync()
is called, creating aHandler
, adding it to the queue and returning aTask
.Set()
is called, settingisSet
to1
.- The handler from step 2 is dequeued.
WaitAsync()
is called from another thread. SinceisSet
is1
, it setsisSet
back to0
and immediately returns a completedTask
.- Back on the previous thread,
TryUnsubscribe()
is called in the handler from step 4 and succeeds. - The handler is
Invoke()
d, butTryReset()
fails. Set()
now returns.
After this, the Task
from step 2 is now orphaned, it will never be completed, because its handler is not in the queue anymore.
I suggest you don't write code like this by yourself, but instead use code written by others with more experience in concurrent programming. Specifically, you could use Stephen Toub's AsyncAutoResetEvent
or AsyncAutoResetEvent
from Stephen Cleary's AsyncEx library.
Your code does have race conditions, so it won't work correctly. I think your main problem is that you make some "destructive" operation (e.g. dequeue a handler from the queue) without knowing that the followup operation will actually succeed and not handling the eventual failure in any way.
For example, the following sequence of events could happen:
- The class is created in unset state.
WaitAsync()
is called, creating aHandler
, adding it to the queue and returning aTask
.Set()
is called, settingisSet
to1
.- The handler from step 2 is dequeued.
WaitAsync()
is called from another thread. SinceisSet
is1
, it setsisSet
back to0
and immediately returns a completedTask
.- Back on the previous thread,
TryUnsubscribe()
is called in the handler from step 4 and succeeds. - The handler is
Invoke()
d, butTryReset()
fails. Set()
now returns.
After this, the Task
from step 2 is now orphaned, it will never be completed, because its handler is not in the queue anymore.
I suggest you don't write code like this by yourself, but instead use code written by others with more experience in concurrent programming. Specifically, you could use Stephen Toub's AsyncAutoResetEvent
Stephen Toub's AsyncAutoResetEvent
or AsyncAutoResetEvent
from Stephen Cleary's AsyncEx library.
Your code does have race conditions, so it won't work correctly. I think your main problem is that you make some "destructive" operation (e.g. dequeue a handler from the queue) without knowing that the followup operation will actually succeed and not handling the eventual failure in any way.
For example, the following sequence of events could happen:
- The class is created in unset state.
WaitAsync()
is called, creating aHandler
, adding it to the queue and returning aTask
.Set()
is called, settingisSet
to1
.- The handler from step 2 is dequeued.
WaitAsync()
is called from another thread. SinceisSet
is1
, it setsisSet
back to0
and immediately returns a completedTask
.- Back on the previous thread,
TryUnsubscribe()
is called in the handler from step 4 and succeeds. - The handler is
Invoke()
d, butTryReset()
fails. Set()
now returns.
After this, the Task
from step 2 is now orphaned, it will never be completed, because its handler is not in the queue anymore.
I suggest you don't write code like this by yourself, but instead use code written by others with more experience in concurrent programming. Specifically, you could use Stephen Toub's AsyncAutoResetEvent
or AsyncAutoResetEvent
from Stephen Cleary's AsyncEx library.
Your code does have race conditions, so it won't work correctly. I think your main problem is that you make some "destructive" operation (e.g. dequeue a handler from the queue) without knowing that the followup operation will actually succeed and not handling the eventual failure in any way.
For example, the following sequence of events could happen:
- The class is created in unset state.
WaitAsync()
is called, creating aHandler
, adding it to the queue and returning aTask
.Set()
is called, settingisSet
to1
.- The handler from step 2 is dequeued.
WaitAsync()
is called from another thread. SinceisSet
is1
, it setsisSet
back to0
and immediately returns a completedTask
.- Back on the previous thread,
TryUnsubscribe()
is called in the handler from step 4 and succeeds. - The handler is
Invoke()
d, butTryReset()
fails. Set()
now returns.
After this, the Task
from step 2 is now orphaned, it will never be completed, because its handler is not in the queue anymore.
I suggest you don't write code like this by yourself, but instead use code written by others with more experience in concurrent programming. Specifically, you could use Stephen Toub's AsyncAutoResetEvent
or AsyncAutoResetEvent
from Stephen Cleary's AsyncEx library.
Your code does have race conditions, so it won't work correctly. I think your main problem is that you make some "destructive" operation (e.g. dequeue a handler from the queue) without knowing that the followup operation will actually succeed and not handling the eventual failure in any way.
For example, the following sequence of events could happen:
- The class is created in unset state.
WaitAsync()
is called, creating aHandler
, adding it to the queue and returning aTask
.Set()
is called, settingisSet
to1
.- The handler from step 2 is dequeued.
WaitAsync()
is called from another thread. SinceisSet
is1
, it setsisSet
back to0
and immediately returns a completedTask
.- Back on the previous thread,
TryUnsubscribe()
is called in the handler from step 4 and succeeds. - The handler is
Invoke()
d, butTryReset()
fails. Set()
now returns.
After this, the Task
from step 2 is now orphaned, it will never be completed, because its handler is not in the queue anymore.
I suggest you don't write code like this by yourself, but instead use code written by others with more experience in concurrent programming. Specifically, you could use Stephen Toub's AsyncAutoResetEvent
or AsyncAutoResetEvent
from Stephen Cleary's AsyncEx library.