Sign in Join

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

Mailing lists / fpc-pascal / 2016-05

What to use when porting WaitForMultipleObjects to Linux?

5 messages · 3 people · –

Reply by email Subscribe to this list Your subscription

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

Bo Berglund

When porting a Delphi console program on Windows to FreePascal on Linux, what can one do with API calls like WaitForMultipleObjects, which is used to synchronize processing between threads?

I am not very familiar with the concept but was given advice on the Delphi forum to use it in a TCP/IP application to let a thread handle the (blocking) socket transfers. The first tests were done on Windows with Delphi2007 but the aim is to port it to Linux. Hence I need to get it into FreePascal.

Bo Berglund Developer in Sweden
Dmitry Boyarintsev
to Bo Berglund: When porting a Delphi console program on Windows to FreePascal on Linux, what can one do…
Bo Berglund wrote
When porting a Delphi console program on Windows to FreePascal on Linux, what can one do with API calls like WaitForMultipleObjects, which is used to synchronize processing between threads?

It depends if you're actually using WaitForMultipleObjects to sync with *multiple* objects.
AFAIK, Posix API (implemented by Linux) do not have any routine to handle/sync multiple objects at the same.
Thus you might need to build something similar yourself.

The closest API in linux system is select() function, that can check the status of multiple file-descriptors in one call. Some of file-descriptor objects could be used for synchronization purposes.

thanks,
Dmitry

Bo Berglund
to Dmitry Boyarintsev
Dmitry Boyarintsev wrote 9 lines
On Mon, May 16, 2016 at 6:22 AM, Bo Berglund wrote: > When porting a Delphi console program on Windows to FreePascal on > Linux, what can one do with API calls like WaitForMultipleObjects, > which is used to synchronize processing between threads? > It depends if you're actually using WaitForMultipleObjects to sync with *multiple* objects.

I should have included an example...
Below is an excerpt of a TThread desdcendant used to handle the data in and out of a TIdTcpClient object.

The Execute procedure handles everything going on and it acts on event signals picked up (I believe) by the WaitForMultipleObjects calls.

What I would like to know is how exactly to handle this in FreePascal on Linux....

---- Code snippets follow ------
type
  TSSRxEvent = procedure (const Buffer: TIdBytes) of object;
...
  TSSCommThread = class(TThread)
  private
    FClient: TIdTCPClient;
    FTxBufferLock: TCriticalSection;
...
    FOnRxData: TSSRxEvent;
...
procedure TSSCommThread.Execute;
{While the client is connected this procedure will handle the incoming
data}
var
  LBuffer: TIdBytes;
  LConnectEvents: array[0..2] of THandle;
  LDisconnectEvents: array[0..1] of THandle;
  LDataTransmitEvents: array[0..0] of THandle;
  LSignaledEvent: THandleObject;
  Ret: DWORD;
begin
  LConnectEvents[0] := FConnectEvent.Handle;
  LConnectEvents[1] := FDisconnectEvent.Handle;
  LConnectEvents[2] := FTerminateEvent.Handle;
  LDisconnectEvents[0] := FDisconnectEvent.Handle;
  LDisconnectEvents[1] := FTerminateEvent.Handle;
  LDataTransmitEvents[0] := FTransmitEvent.Handle;
  while not Terminated do
  begin
    Ret := WaitForMultipleObjects(3, PWOHandleArray(@LConnectEvents),
False, INFINITE);
    if (Ret >= WAIT_OBJECT_0) and (Ret < (WAIT_OBJECT_0 + 3)) then
      Dec(Ret, WAIT_OBJECT_0)
    else
      Exit;
    if Terminated or (LSignaledEvent = FTerminateEvent) then Exit;
    if LSignaledEvent = FDisconnectEvent then
    begin
      FDisconnectEvent.ResetEvent;
      FDisconnectedEvent.SetEvent;
      Continue;
    end;
    if LSignaledEvent = FConnectEvent then
    begin
      FDisconnectedEvent.ResetEvent;
      try
        FClient.Connect;
        try
          if Assigned(FOnConnect) then
            FOnConnect();
          while (WaitForMultipleObjects(2,
PWOHandleArray(@LDisconnectEvents), False, 0) = WAIT_TIMEOUT) do
          begin
            // TODO: check pending queue for outgoing data..
            if FTransmitEvent.WaitFor(0) = wrSignaled then
            begin
              FTxBufferLock.Enter;
              try
                LBuffer := FTxBuffer;
                FTxBuffer := nil;
                FTransmitEvent.ResetEvent;
              finally
                FTxBufferLock.Leave;
              end;
              FClient.IOHandler.Write(LBuffer);
            end;
            // Check for incoming data
            if FClient.IOHandler.CheckForDataOnSource(10) then
            begin
              SetLength(LBuffer, 0);
              FClient.IOHandler.ReadBytes(LBuffer, -1, false);
              if Length(LBuffer) > 0 then
              begin
                // TODO: add data to a thread-safe receive queue
                if Assigned(FOnRxData) then
                  FOnRxData(LBuffer);
              end;
            end;
          end;
        finally
          FClient.Disconnect;
          FDisconnectedEvent.SetEvent;
          if Assigned(FOnDisconnect) then
            FOnDisconnect();
        end;
      except
        on E: Exception do
        begin
          if Assigned(FOnError) then
            OnError(E.Message);
          WaitForMultipleObjects(2,
PWOHandleArray(@LDisconnectEvents), False, 5000);
        end;
      end;
    end
  else
      Exit;
  end;
end;
procedure TSSCommThread.Stop;
begin
  Terminate;
  FTerminateEvent.SetEvent;
end;
procedure TSSCommThread.Send(Buffer: TIdBytes; BufLen: Integer);
var
  Idx: Integer;
begin
  // TODO: add data to a thread-safe transmit queue, signal event that
data is waiting
  if BufLen > 0 then
  begin
    FTxBufferLock.Enter;
    try
      Idx := Length(FTxBuffer);
      SetLength(FTxBuffer, Idx + BufLen);
      Move(Buffer[0], FTxBuffer[Idx], BufLen);
      FTransmitEvent.SetEvent;
    finally
      FTxBufferLock.Leave;
    end;
  end;
end;

...

Bo Berglund Developer in Sweden
Dmitry Boyarintsev
to Bo Berglund
Bo Berglund wrote 6 lines
The Execute procedure handles everything going on and it acts on event signals picked up (I believe) by the WaitForMultipleObjects calls. What I would like to know is how exactly to handle this in FreePascal on Linux....

I presume FConnectEvent, FDisconnectEvent (and others) are of TEvent type (from FCLs syncobjs)

In this case, I'd think you could implement WaitForMultipleObjects() yourself, via a simple event waiting loop:

uses ... syncobjs, SysUtils ;
const
  WAIT_TIMEOUT = $102;
function WaitForMultipleObject(cnt : integer; const handle: array of
TEventHandle;
  waitforall: Boolean; timeOutMls: integer): integer;
var
  event : Integer;
  evNum : integer;
  i     : integer;
  ev    : array of boolean;
  wr    : TWaitResult;
  any   : Boolean;
  d     : TDateTime; // please forgive the usage of now() :)
  dm    : integer;
const
  YIELD_TIME=0;
begin
  evNum:=0;
  SetLength(ev, cnt);
  Result:=WAIT_TIMEOUT;
  // create a loop of waiting for each event to fire
  repeat
    any:=false;
    if timeOutMls>0 then d:=now;
    for i:=0 to cnt-1 do
      if not ev[i] then begin
        // refer to synobj sources for .WaitFor() method of TEvent
        wr:=TWaitResult(basiceventWaitFor(0, Handle[i]));
        if wr=wrSignaled then begin
          ev[i]:=true;
          inc(evNum);
          Result:=i;
          if not waitforall then Break;
        end;
      end;
    if not any and (timeOutMls>0) then begin
      // if has timeout, must yeld the time for some time
      // and then repeat the try
      Sleep(YIELD_TIME);
      if timeOutMls>0 then begin
        d:=now-d;
        dm:=round(d*MSecsPerDay);
        dec(timeOutMls, dm);
        if (timeOutMls<0) then timeOutMls:=0;
      end;
    end;
    // btw, actual support for "waitforall" is not needed in this case
  until (evNum>0) and (not waitforall or (cnt=evNum)) or (timeOutMls=0);
  if waitforall and (evNum>0) then Result:=0;
end;

I've not tested this loop, but it should give you an idea of what you could do.

The fact that you're using the handles of the same type allows you to implement the loop yourself.

thanks,
Dmitry