-
Notifications
You must be signed in to change notification settings - Fork 83
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
WFME( waitAll = true ) resets AutoReset Events before all Events are set #9
Comments
Hi, thanks for the PR. I agree that it would be best to mimic the Win32 behavior here, both for conformity but especially because code depending on those guarantees might not (or more likely, would not) work without them. Do you have suggestions on how this can be cleanly implemented with the current approach? |
Yes. With the current implementation, the solution is 6-fold. There are opportunities for optimizations, but the general idea is as follows:
Given the current implementation along with the above modifications: When calling ResetEvent, as long as knowledge is held that all shared states have or have not been set by a given event, subsequent calls to SetEvent will still properly signal the shared state. The beauty of calling WFE( 0 ) on all events in sequence when WFME has been signaled is that you don't need to keep track of whether the event is auto or manual reset, as each event already knows whether or not it must be reset on successful wait. You also don't need to bother holding locks as is necessary when checking the state of each event and adding the wait states, because all you need to do is attempt to reset auto reset events. It doesn't even have to be successful because any race conditions caused by concurrent WFME, ResetEvent, SetEvent, or WFE calls on any of the events during a given WFME call is totally undefined behavior, which is exactly how WaitForMultipleObjects works. So simply calling the public WFE( 0 ) for each event after a successful wait satisfies the contract. My derivative implementation uses the above approach and I can't come up with any test cases where it is broken. I would appreciate scrutiny because it only took me a day to come up with this surprisingly-simple approach when I ran into this issue with my own implementation. I'm appreciative of your work on this and since I derived mine from yours, I'm happy to provide my solution as stated above. |
@qwertymaster617 I know it's been some time, but this has been on the back of my mind. Your approach only works if we can take the following for granted:
Otherwise performance will necessarily suffer because a global lock (or some equivalent) would be required to guarantee that a WFMO waiter awaiting n objects all of which are currently in a signalled state can atomically claim all n objects. The problem is that I don't see anything that documents the race behavior that you describe. I believe the only undocumented/undefined behavior for I don't think there's any ambiguity when it comes to auto reset events: they must never allow more than one awaiting thread to wake for any given call to |
Test contributed by @qwertymaster617, see [0] for associated issue. [0]: #9
This patch significantly changes the behavior (and to an extent, the performance) of pevents to more closely mimic the documented behavior of `WaitForMultipleObjects` and `WaitForMultipleObjectsEx`. As reported in #9, the previous behavior did not make any atomicity guarantees when a call to `WaitForMultipleEvents()` was made with `waitAll = true`, and WFME would attempt to serially obtain the events in question, which could lead to a deadlock in case of circular locking and auto-reset events. The WFMO behavior documented on MSDN makes it clear that the Windows implementation does not modify the signalled state of any of the manual or auto reset events being awaited until the WFMO call is ready to return, at which point either the one event in question or all the events being awaited (dependent on `waitAll`) are atomically awaited.
This patch significantly changes the behavior (and to an extent, the performance) of pevents to more closely mimic the documented behavior of `WaitForMultipleObjects` and `WaitForMultipleObjectsEx`. As reported in #9, the previous behavior did not make any atomicity guarantees when a call to `WaitForMultipleEvents()` was made with `waitAll = true`, and WFME would attempt to serially obtain the events in question, which could lead to a deadlock in case of circular locking and auto-reset events. The WFMO behavior documented on MSDN makes it clear that the Windows implementation does not modify the signalled state of any of the manual or auto reset events being awaited until the WFMO call is ready to return, at which point either the one event in question or all the events being awaited (dependent on `waitAll`) are atomically awaited.
This patch significantly changes the behavior (and to an extent, the performance) of pevents to more closely mimic the documented behavior of `WaitForMultipleObjects` and `WaitForMultipleObjectsEx`. As reported in #9, the previous behavior did not make any atomicity guarantees when a call to `WaitForMultipleEvents()` was made with `waitAll = true`, and WFME would attempt to serially obtain the events in question, which could lead to a deadlock in case of circular locking and auto-reset events. The WFMO behavior documented on MSDN makes it clear that the Windows implementation does not modify the signalled state of any of the manual or auto reset events being awaited until the WFMO call is ready to return, at which point either the one event in question or all the events being awaited (dependent on `waitAll`) are atomically awaited.
Test contributed by @qwertymaster617, see [0] for associated issue. [0]: #9
This patch significantly changes the behavior (and to an extent, the performance) of pevents to more closely mimic the documented behavior of `WaitForMultipleObjects` and `WaitForMultipleObjectsEx`. As reported in #9, the previous behavior did not make any atomicity guarantees when a call to `WaitForMultipleEvents()` was made with `waitAll = true`, and WFME would attempt to serially obtain the events in question, which could lead to a deadlock in case of circular locking and auto-reset events. The WFMO behavior documented on MSDN makes it clear that the Windows implementation does not modify the signalled state of any of the manual or auto reset events being awaited until the WFMO call is ready to return, at which point either the one event in question or all the events being awaited (dependent on `waitAll`) are atomically awaited.
You're right. Indeed, when I made the comment last year I had this exact scenario in mind. However, when I was compiling this last year with MSVC on Win10 with Win32 versions of WFSO/WFMO/SetEvent each on their own thread with a single event, I was getting memory access violations on all 3 calls. It was strange. So I just figured that since the behavior was not specified at all on MSDN in this manner that it was unspecified or even undefined. When I compile the same program today with MSVC 16.10.3, I no longer have this issue and can seemingly wait on events in arbitrary and ridiculous ways and it seems to work as one would expect, so maybe I just ran into unfortunate bugs at the time? Anyway, the solution presented seems sound to me. I thought long and hard about it. I can't think of anything that breaks it, except for possible starvation because of the somewhat unbounded amount of spinning that can occur now on Even so, this seems to be a pretty good solution to a hairy problem! I've finally had a chance this weekend to implement it and it works well. I also made a few comments on commit 2db23ff. I'm a bit concerned about possible starvation and other issues where we single out one WFME waiter and wake them up when we're an auto-reset event. I understand fully why it's done, but possible over-reliance on priority inversion, the inability to reset events after being set, and inability to ever keep the event in the set state whenever there is at least one concurrent WFME call, but that's probably for a seperate discussion. With this fix, I'm about 99% confident this is a spirit-and-letter API-compatible WaitForMultipleObjects clone. Thanks! |
Test contributed by @qwertymaster617, see [0] for associated issue. [0]: #9
This patch significantly changes the behavior (and to an extent, the performance) of pevents to more closely mimic the documented behavior of `WaitForMultipleObjects` and `WaitForMultipleObjectsEx`. As reported in #9, the previous behavior did not make any atomicity guarantees when a call to `WaitForMultipleEvents()` was made with `waitAll = true`, and WFME would attempt to serially obtain the events in question, which could lead to a deadlock in case of circular locking and auto-reset events. The WFMO behavior documented on MSDN makes it clear that the Windows implementation does not modify the signalled state of any of the manual or auto reset events being awaited until the WFMO call is ready to return, at which point either the one event in question or all the events being awaited (dependent on `waitAll`) are atomically awaited.
Test contributed by @qwertymaster617, see [0] for associated issue. [0]: #9
This patch significantly changes the behavior (and to an extent, the performance) of pevents to more closely mimic the documented behavior of `WaitForMultipleObjects` and `WaitForMultipleObjectsEx`. As reported in #9, the previous behavior did not make any atomicity guarantees when a call to `WaitForMultipleEvents()` was made with `waitAll = true`, and WFME would attempt to serially obtain the events in question, which could lead to a deadlock in case of circular locking and auto-reset events. The WFMO behavior documented on MSDN makes it clear that the Windows implementation does not modify the signalled state of any of the manual or auto reset events being awaited until the WFMO call is ready to return, at which point either the one event in question or all the events being awaited (dependent on `waitAll`) are atomically awaited.
Note that I've rebased the |
Test contributed by @qwertymaster617, see [0] for associated issue. [0]: #9
This patch significantly changes the behavior (and to an extent, the performance) of pevents to more closely mimic the documented behavior of `WaitForMultipleObjects` and `WaitForMultipleObjectsEx`. As reported in #9, the previous behavior did not make any atomicity guarantees when a call to `WaitForMultipleEvents()` was made with `waitAll = true`, and WFME would attempt to serially obtain the events in question, which could lead to a deadlock in case of circular locking and auto-reset events. The WFMO behavior documented on MSDN makes it clear that the Windows implementation does not modify the signalled state of any of the manual or auto reset events being awaited until the WFMO call is ready to return, at which point either the one event in question or all the events being awaited (dependent on `waitAll`) are atomically awaited.
This patch significantly changes the behavior (and to an extent, the performance) of pevents to more closely mimic the documented behavior of `WaitForMultipleObjects` and `WaitForMultipleObjectsEx`. As reported in #9, the previous behavior did not make any atomicity guarantees when a call to `WaitForMultipleEvents()` was made with `waitAll = true`, and WFME would attempt to serially obtain the events in question, which could lead to a deadlock in case of circular locking and auto-reset events. The WFMO behavior documented on MSDN makes it clear that the Windows implementation does not modify the signalled state of any of the manual or auto reset events being awaited until the WFMO call is ready to return, at which point either the one event in question or all the events being awaited (dependent on `waitAll`) are atomically awaited.
Test contributed by @qwertymaster617, see [0] for associated issue. [0]: #9
This patch significantly changes the behavior (and to an extent, the performance) of pevents to more closely mimic the documented behavior of `WaitForMultipleObjects` and `WaitForMultipleObjectsEx`. As reported in #9, the previous behavior did not make any atomicity guarantees when a call to `WaitForMultipleEvents()` was made with `waitAll = true`, and WFME would attempt to serially obtain the events in question, which could lead to a deadlock in case of circular locking and auto-reset events. The WFMO behavior documented on MSDN makes it clear that the Windows implementation does not modify the signalled state of any of the manual or auto reset events being awaited until the WFMO call is ready to return, at which point either the one event in question or all the events being awaited (dependent on `waitAll`) are atomically awaited.
Test contributed by @qwertymaster617, see [0] for associated issue. [0]: #9
This patch significantly changes the behavior (and to an extent, the performance) of pevents to more closely mimic the documented behavior of `WaitForMultipleObjects` and `WaitForMultipleObjectsEx`. As reported in #9, the previous behavior did not make any atomicity guarantees when a call to `WaitForMultipleEvents()` was made with `waitAll = true`, and WFME would attempt to serially obtain the events in question, which could lead to a deadlock in case of circular locking and auto-reset events. The WFMO behavior documented on MSDN makes it clear that the Windows implementation does not modify the signalled state of any of the manual or auto reset events being awaited until the WFMO call is ready to return, at which point either the one event in question or all the events being awaited (dependent on `waitAll`) are atomically awaited.
Test contributed by @qwertymaster617, see [0] for associated issue. [0]: #9
This patch significantly changes the behavior (and to an extent, the performance) of pevents to more closely mimic the documented behavior of `WaitForMultipleObjects` and `WaitForMultipleObjectsEx`. As reported in #9, the previous behavior did not make any atomicity guarantees when a call to `WaitForMultipleEvents()` was made with `waitAll = true`, and WFME would attempt to serially obtain the events in question, which could lead to a deadlock in case of circular locking and auto-reset events. The WFMO behavior documented on MSDN makes it clear that the Windows implementation does not modify the signalled state of any of the manual or auto reset events being awaited until the WFMO call is ready to return, at which point either the one event in question or all the events being awaited (dependent on `waitAll`) are atomically awaited.
Since pevents is supposed to be a WFMO clone, I'm not sure if this is a bug report or a feature request, because the implementation just doesn't do this right now.
From the Remarks section of the documentation for WaitForMultipleObjects on MSDN (emphasis mine):
This means that during a WFME call where waitAll = true, the state of events shall not be reset, in the specific case of auto-reset Events, until all are simultaneously in the set state. This would apply to manual-reset Events too, except that their state cannot change due to a WaitForEvent/WFME call unless an explicit call to ResetEvent is made. Note that even though WMFE( waitAll = true, milliseconds = UINT64_MAX ) shall return only if all events are signaled, this bug applies to waitAll = true calls to WFME with any timeout value because the state shall only change once all events are in the set state, and the current implementation immediately resets the event before waiting if the Event is set.
This seems to be a significant change (hence, a feature request?) simply because the wait state is never added to any Event that is already in the set state, which is incorrect because another thread could call ResetEvent on any already-set Event, change the state back to not-set, and thus should increment the EventsLeft shared state member.
The following test case exhibits the error:
The text was updated successfully, but these errors were encountered: