Wait for multiple events lazarus / 23-Operating Systems ---------------------------------------------------------------------- alpine · 2024-09-01 21:08 Hi, Is there a portable way to wait for multiple TEventObject without using an OS-specific call such as e.g. WaitForMultipleObjects? Thanks in advance! ---------------------------------------------------------------------- Wallaby · 2024-09-02 11:02 Nope. And apparently there is no easy way to do it, I have checked in Delphi, waiting for multiple events is only available for Windows. ---------------------------------------------------------------------- Joanna · 2024-09-02 11:15 Quote from: alpine on September 01, 2024, 11:08:58 pmHi, Is there a portable way to wait for multiple TEventObject without using an OS-specific call such as e.g. WaitForMultipleObjects? Thanks in advance! I’m curious what controls you are working with ? ---------------------------------------------------------------------- Thaddy · 2024-09-02 13:14 There are several ways to simulate it though, using pthread mutexes, but also through this library: https://github.com/NeoSmart/PEvents which looks like being able to be translated/flattened to FreePascal for Posix platforms. ---------------------------------------------------------------------- flowCRANE · 2025-02-20 12:59 Quote from: alpine on September 01, 2024, 11:08:58 pmIs there a portable way to wait for multiple TEventObject without using an OS- specific call such as e.g. WaitForMultipleObjects? You can wait for all of them in a normal loop, using the WaitFor method. Regardless of the order in which the events are signaled, such a loop will end only if all events have been signaled. I have a task like this, where the main thread signals the worker threads to perform distributed calculations and waits until they all finish — then the main thread is resumed and in the next frame this scenario repeats itself (this is for the game engine). The worker threads are created once and when they have nothing to do, they are to be frozen so that they do not waste CPU time. Here is the test program that simulates such scenario — with no error checking, to simplify the tester. The main loop performs 5 times, uses 4 worker threads and self-resetting event objects. Worker threads pretend to do something and only display a message that they have completed the job. First in pure Win32 API: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i uses  Windows; const  THREAD_NUM = 4; type  PParameter = ^TParameter;  TParameter = record    Index:      LONG;    Terminated: BOOL;  end; var  Thread:      array [0 .. THREAD_NUM - 1] of HANDLE;  ThreadID:    array [0 .. THREAD_NUM - 1] of DWORD;  Parameter:   array [0 .. THREAD_NUM - 1] of TParameter;  EventStart:  array [0 .. THREAD_NUM - 1] of HANDLE;  EventFinish: array [0 .. THREAD_NUM - 1] of HANDLE;   function ThreadFunc (AParameter: PParameter): DWORD; stdcall;  begin    repeat      WaitForSingleObject(EventStart[AParameter^.Index], INFINITE);       if not AParameter^.Terminated then      begin        WriteLn('Thread ', AParameter^.Index, ' did its job.');        SetEvent(EventFinish[AParameter^.Index]);      end;    until AParameter^.Terminated;     Result := 0;  end; var  Index:      LONG;  IndexEvent: LONG;begin  for Index := 0 to THREAD_NUM - 1 do  begin    Parameter[Index].Index      := Index;    Parameter[Index].Terminated := False;     EventStart[Index]  := CreateEvent(nil, False, False, '');    EventFinish[Index] := CreateEvent(nil, False, False, '');     Thread[Index] := CreateThread(nil, 0, @ThreadFunc, @Parameter[Index], 0, ThreadID[Index]);  end;   for Index := 1 to 5 do  begin    for IndexEvent := 0 to THREAD_NUM - 1 do      SetEvent(EventStart[IndexEvent]);     WaitForMultipleObjects(THREAD_NUM, @EventFinish, True, INFINITE);    WriteLn();  end;   for Index := 0 to THREAD_NUM - 1 do  begin    Parameter[Index].Terminated := True;    SetEvent(EventStart[Index]);  end;   WaitForMultipleObjects(THREAD_NUM, @Thread, True, INFINITE);   for Index := 0 to THREAD_NUM - 1 do  begin    CloseHandle(Thread[Index]);    CloseHandle(EventStart[Index]);    CloseHandle(EventFinish[Index]);  end;   ReadLn();end. And here is the same but using Free Pascal classes: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i uses  SysUtils,  Classes,  SyncObjs; type  TWorkerThread = class(TThread)  private    FIndex: Integer;  protected    procedure Execute(); override;  public    constructor Create(AIndex: Integer);  end; const  THREAD_NUM = 4; var  Threads:     array [0 .. THREAD_NUM - 1] of TWorkerThread;  EventStart:  array [0 .. THREAD_NUM - 1] of TEvent;  EventFinish: array [0 .. THREAD_NUM - 1] of TEvent;   constructor TWorkerThread.Create(AIndex: Integer);  begin    FIndex := AIndex;    inherited Create(False);  end;   procedure TWorkerThread.Execute();  begin    repeat      EventStart[FIndex].WaitFor(INFINITE);       if not Terminated then      begin        WriteLn('Thread ', FIndex, ' did some job.');        EventFinish[FIndex].SetEvent();      end;    until Terminated;  end; var  Index:      Integer;  IndexEvent: Integer;begin  for Index := 0 to THREAD_NUM - 1 do  begin    EventStart[Index]  := TEvent.Create(nil, False, False, '');    EventFinish[Index] := TEvent.Create(nil, False, False, '');     Threads[Index] := TWorkerThread.Create(Index);  end;   for Index := 1 to 5 do  begin    for IndexEvent := 0 to THREAD_NUM - 1 do      EventStart[IndexEvent].SetEvent();     for IndexEvent := 0 to THREAD_NUM - 1 do      EventFinish[IndexEvent].WaitFor(INFINITE);     WriteLn();  end;   for Index := 0 to THREAD_NUM - 1 do  begin    Threads[Index].Terminate();    EventStart[Index].SetEvent();     Threads[Index].WaitFor();    Threads[Index].Free();     EventStart[Index].Free();    EventFinish[Index].Free();  end;   ReadLn();end. Example output: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i Thread 0 did its job.Thread 2 did its job.Thread 1 did its job.Thread 3 did its job. Thread 0 did its job.Thread 1 did its job.Thread 2 did its job.Thread 3 did its job. Thread 0 did its job.Thread 3 did its job.Thread 2 did its job.Thread 1 did its job. Thread 0 did its job.Thread 2 did its job.Thread 3 did its job.Thread 1 did its job. Thread 1 did its job.Thread 0 did its job.Thread 2 did its job.Thread 3 did its job. You can play around and change the number of threads to higher. I haven't tested it on platforms other than Windows, but the effect should be exactly the same. I hope this helps you. ---------------------------------------------------------------------- marcov · 2025-02-20 13:12 Flowcrane: such workarounds are not for all scenarios. waitformultiple can also exit if one is signaled, and then you can process that one, and block on the remaining ones, enabling more parallelism. Maybe the ntsync kernel module or futex_waitv will finally enable something comparable for Linux. However till now those seem mostly meant for Windows emulation (read "wine"), not general purpose app use. ---------------------------------------------------------------------- flowCRANE · 2025-02-20 17:57 Quote from: marcov on February 20, 2025, 02:12:16 pmFlowcrane: such workarounds are not for all scenarios. Of course but, there is no one universal answer to this question. You can wait for multiple signals without using anything fancy, like in the scenario my test program implements. In other scenarios, this won't be an option. To be able to provide specific advice, one would need to know the exact context, and the OP did not describe this. Who knows what he need to implement. Maybe my test program will be useful to him, or maybe to someone else. In any case, in order to give any sensible advice, the OP needs to provide more information about what exactly he wants to achieve. ---------------------------------------------------------------------- alpine · 2025-02-20 19:11 Quote from: flowCRANE on February 20, 2025, 06:57:00 pmQuote from: marcov on February 20, 2025, 02:12:16 pmFlowcrane: such workarounds are not for all scenarios. Of course but, there is no one universal answer to this question. You can wait for multiple signals without using anything fancy, like in the scenario my test program implements. In other scenarios, this won't be an option. To be able to provide specific advice, one would need to know the exact context, and the OP did not describe this. Who knows what he need to implement. Maybe my test program will be useful to him, or maybe to someone else. In any case, in order to give any sensible advice, the OP needs to provide more information about what exactly he wants to achieve. I apologise for not being so specific, the use case was the one that resembles the select semantics, as marcov suggests. Not so trivial as the one for waiting all events set. The question was for a portable way to do this. Anyway, I appreciate the help. ---------------------------------------------------------------------- JollyHong · 2026-08-20 11:00 I’d also test it with a small, isolated example first rather than changing the whole project at once. That makes it much easier to see whether the issue is actually coming from the code or the component itself. ---------------------------------------------------------------------- Thaddy · 2026-08-20 12:27 I described a safe way in the wiki a few months ago. Needs trunk/main, though: https://wiki.freepascal.org/Multithreaded_Application_Tutorial#Waiting _for_another_thread_part_2,_the_future This is fully cross platform if threads are supported. (cthreads or windows)