Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Mailing lists / fpc-devel / 2020-08

Exact details for RTLeventWaitFor and related ?

6 messages · 5 people ·

Reply by email Subscribe to this list Your subscription

Read-only archive. Use j and k to move between messages.

Martin

I am tracing a race condition in lazCollections TLazThreadedQueue on win-64

What is supposed to happen in the following case:

2 or more threads are waiting on the some event
  RTLeventWaitFor(UniqueEvent)

1 thread sets it
  RTLeventSetEvent(UniqueEvent)

I expect that wakes up exactly ONE thread (never mind wich)? Or ALL?

If no one resets the event (NO call to RTLeventResetEvent), then that does not change anything? It is still just the one thread that got woken up?

Thanks for any feedback
Martin

p.s.
Background

TLazThreadedQueue.PopItem works like this

EnterCritical
  If item avail then return item
ExitCritical
   // race condition between those lines.
RTLeventWaitFor(itemAdded)
EnterCritical
  If item avail then return
ExitCritical

If 2 or more reading (popping) threads are both at the indicated location, and if during this time 2 or more items are added
then there is a problem (I believe)

adding (pushing) an item does
  RTLeventSet(itemAdded)
adding 2 items (with no pop in between) calls Set twice    =>
https://lazarus-ccr.sourceforge.io/docs/rtl/system/rtleventsetevent.html     It is unclear if the 2nd RTLeventSet has any effect ?

According to my tests:
- I "sleep()" 2 listeners at the critical line, so they both will WaitFor after the sleep
- while they sleep I add 2 items (2 calls to RTLeventSet, no one listening yet)
- Both start RTLeventWaitFor
  => only one listener wakes up.
Is that the expected behaviour?

Reply by email

Kirinn
to Martin

When I researched synchronisation behavior and wrote the wiki page "Threads", I found that RTL events behave like this:

- When the event is set, a single waiting thread is released (in FIFO order), and the event is automatically immediately reset.

- The event can only be in a set or unset state; multiple sets do not stack. Even if the event is set twice or ten times, a single WaitFor or Reset will change it to unset. This is a likely cause for deadlocks.

- Because the event is automatically reset when a waiting thread is released, calling ResetEvent is generally unnecessary.

For set/reset counting we'd need a semaphore instead of an RTL event, but I don't think we have an actively supported semaphore implementation at this time.

Caveat: The above was true for FPC 3.0.4 on Windows and Linux. 3.2.0 or trunk may have changed something, and I can't vouch for other platforms.

~Kirinn

Martin wrote 61 lines
I am tracing a race condition in lazCollections TLazThreadedQueue on win-64 What is supposed to happen in the following case: 2 or more threads are waiting on the some event   RTLeventWaitFor(UniqueEvent) 1 thread sets it   RTLeventSetEvent(UniqueEvent) I expect that wakes up exactly ONE thread (never mind wich)? Or ALL? If no one resets the event (NO call to RTLeventResetEvent), then that does not change anything? It is still just the one thread that got woken up? Thanks for any feedback Martin p.s. Background TLazThreadedQueue.PopItem works like this EnterCritical   If item avail then return item ExitCritical    // race condition between those lines. RTLeventWaitFor(itemAdded) EnterCritical   If item avail then return ExitCritical If 2 or more reading (popping) threads are both at the indicated location, and if during this time 2 or more items are added then there is a problem (I believe) adding (pushing) an item does   RTLeventSet(itemAdded) adding 2 items (with no pop in between) calls Set twice    => https://lazarus-ccr.sourceforge.io/docs/rtl/system/rtleventsetevent.html     It is unclear if the 2nd RTLeventSet has any effect ? According to my tests: - I "sleep()" 2 listeners at the critical line, so they both will WaitFor after the sleep - while they sleep I add 2 items (2 calls to RTLeventSet, no one listening yet) - Both start RTLeventWaitFor   => only one listener wakes up. Is that the expected behaviour? _______________________________________________ fpc-devel maillist  -  https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-devel

Reply by email

Pascal Riekenberg
to Kirinn
Show quoted text 5 lines
Kirinn via fpc-devel hat am 22. August 2020 um 03:50 geschrieben: For set/reset counting we'd need a semaphore instead of an RTL event, but I don't think we have an actively supported semaphore implementation at this time.

I can provide one. If you like to, i can send the source for review.

Pascal

Reply by email

Sven Barth
to Martin: I am tracing a race condition in lazCollections TLazThreadedQueue on win-64 What is…
Martin wrote 17 lines
I am tracing a race condition in lazCollections TLazThreadedQueue on win-64 What is supposed to happen in the following case: 2 or more threads are waiting on the some event   RTLeventWaitFor(UniqueEvent) 1 thread sets it   RTLeventSetEvent(UniqueEvent) I expect that wakes up exactly ONE thread (never mind wich)? Or ALL? If no one resets the event (NO call to RTLeventResetEvent), then that does not change anything? It is still just the one thread that got woken up?

On Windows these are direct calls to the WinAPI functions WaitForSingleObject and SetEvent (and the created event is an Auto Reset Event), so you should probably take a look at the MSDN documentation for them...

Regards,
Sven

Reply by email

Sven Barth
to Pascal Riekenberg: I can provide one. If you like to, i can send the source for review. Pascal
Pascal Riekenberg wrote 6 lines
> Kirinn via fpc-devel hat am 22. August 2020 um 03:50 geschrieben: > > For set/reset counting we'd need a semaphore instead of an RTL event, > but I don't think we have an actively supported semaphore implementation > at this time. I can provide one. If you like to, i can send the source for review.

If we add a semaphore implementation to the RTL it would need to be compatible to Delphi's SyncObjs.TSemaphore (
http://docwiki.embarcadero.com/Libraries/Tokyo/en/System.SyncObjs.TSemaphore ).

Regards,
Sven

Reply by email

Martin Frb
to Kirinn: When I researched synchronisation behavior and wrote the wiki page "Threads", I found…
Kirinn wrote 10 lines
When I researched synchronisation behavior and wrote the wiki page "Threads", I found that RTL events behave like this: - When the event is set, a single waiting thread is released (in FIFO order), and the event is automatically immediately reset. - The event can only be in a set or unset state; multiple sets do not stack. Even if the event is set twice or ten times, a single WaitFor or Reset will change it to unset. This is a likely cause for deadlocks. - Because the event is automatically reset when a waiting thread is released, calling ResetEvent is generally unnecessary.

I can confirm this on Windows.

- If I set the event, and then 2 threads enter WaitFor => only 1 thread resumes
- If I set the event, and then 1 threads enter WaitFor, resumes and enters WaitFor a 2nd time => then 2nd time does NOT resume
- If 2 threads enter WaitFor, and I set the event (once) while they are waiting => only 1 thread resumes

For set/reset counting we'd need a semaphore instead of an RTL event, but I don't think we have an actively supported semaphore implementation at this time.

I can solve the issue at hand without a semaphore. I just needed to know.

Sven Barth wrote
On Windows these are direct calls to the WinAPI functions WaitForSingleObject and SetEvent (and the created event is an Auto Reset Event), so you should probably take a look at the MSDN documentation for them...

Indeed....

https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-setevent SetEvent

Show quoted text 8 lines
The state of a manual-reset event object remains signaled until it is set explicitly to the nonsignaled state by the ResetEvent <https://docs.microsoft.com/en-us/windows/desktop/api/synchapi/nf-synchapi-resetevent> function. Any number of waiting threads, or threads that subsequently begin wait operations for the specified event object by calling one of the wait functions <https://docs.microsoft.com/en-us/windows/desktop/Sync/wait-functions>, can be released while the object's state is signaled.

https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-waitforsingleobject WaitForSingleObject

The function modifies the state of some types of synchronization objects. Modification occurs only for the object whose signaled state caused the function to return. For example, the count of a semaphore object is decreased by one.

On current Win10, it definitely only wakes one thread once. WaitForSingleObject gives semaphores as example, but does not say which other objects it may modify.

In any case, I can safely RTLEventReset the event in whatever thread wakes first, and then immediately RTLEventSet it again (if needed).

That should be safe. It should also work, for any OS, on which multiple threads where released.
(The last one will Reset, but not Set it => so it will be cleared when all is done / in the use-case I am looking at)

Thanks to everyone for the detailed responses.
Martin

Reply by email