Recent

Author Topic: MessageLoop in NON-GUI shared library  (Read 1847 times)

Zvoni

  • Sr. Member
  • ****
  • Posts: 267
MessageLoop in NON-GUI shared library
« on: March 03, 2019, 04:08:09 pm »
Hi folks,

scenario:
I have a shared library which exports some flat functions.
Inside that library i want to start some threads to do some work (fetching data from somewhere) until a Terminate-signal is sent to the thread(s).
Since it's non-gui i understand, that i have to set up my own messageloop to execute CheckSynchronize.

Could someone give me some pointers as to how to set up such a Loop without deadlocking myself?

The exported function-calls work. I get my reference-variables back as well as the function-result.
The moment i setup a messageloop, control doesn't return back to the calling application and i'm deadlocked.

I even tried a
Code: [Select]
//in Library
Type
    TApp=Class(TCustomApplication);

Var
MyApp:TApp;
{.........}
Begin
MyApp:=TApp.Create(nil);
MyApp.Run; //----Deadlock!
End;
Help?

EDIT: Right now, i'm using a TFPTimer to get control back, and use its Timer-Event to execute CheckSynchronize.
See posts below.
« Last Edit: March 03, 2019, 09:51:26 pm by Zvoni »
One System to rule them all, One IDE to find them,
One Code to bring them all, and to the Framework bind them,
in the Land of Redmond, where the Windows lie
---------------------------------------------------------------------
People call me crazy, because i'm jumping out of perfectly fine aircrafts

Cyrax

  • Hero Member
  • *****
  • Posts: 758
Re: MessageLoop in NON-GUI shared library
« Reply #1 on: March 03, 2019, 05:20:35 pm »
You need to make your messageloop as a thread, too.

jamie

  • Hero Member
  • *****
  • Posts: 2072
Re: MessageLoop in NON-GUI shared library
« Reply #2 on: March 03, 2019, 07:12:59 pm »
I better method would be to supply a window handle when you initiate the thread in the DLL.

This handle could be the main form in your app. You send a USER_MESSAGE with some data in it.

The DLL thread can use the PostMessage  (not sendMessage) to place the message in the que so that it can be
handled when the main thread is active.

 Supply a function in the DLL that then allows you to gather collected information.

 P.S.

  Use a Synchronized procedure when sending the message.
Number 1 at blue screen app creations!

engkin

  • Hero Member
  • *****
  • Posts: 2513
Re: MessageLoop in NON-GUI shared library
« Reply #3 on: March 03, 2019, 07:14:33 pm »
By design, CheckSynchronize requires to be called from within the main GUI thread:
Code: Pascal  [Select]
  1. function CheckSynchronize(timeout : longint=0) : boolean;
  2. ...
  3.   { first sanity check }
  4.   if Not IsMultiThread then
  5.     Exit
  6.   { second sanity check }
  7.   else if GetCurrentThreadID<>MainThreadID then
  8.     raise EThread.CreateFmt(SCheckSynchronizeError,[GetCurrentThreadID]);
  9. ...
« Last Edit: March 03, 2019, 07:16:35 pm by engkin »

Zvoni

  • Sr. Member
  • ****
  • Posts: 267
Re: MessageLoop in NON-GUI shared library
« Reply #4 on: March 03, 2019, 09:40:00 pm »
By design, CheckSynchronize requires to be called from within the main GUI thread:
Code: Pascal  [Select]
  1. function CheckSynchronize(timeout : longint=0) : boolean;
  2. ...
  3.   { first sanity check }
  4.   if Not IsMultiThread then
  5.     Exit
  6.   { second sanity check }
  7.   else if GetCurrentThreadID<>MainThreadID then
  8.     raise EThread.CreateFmt(SCheckSynchronizeError,[GetCurrentThreadID]);
  9. ...

I logged GetCurrentThreadID inside the loop and MainThreadID during first call to the library. Both are the same, so it should pass the second sanity check
From my Log
Quote
Enter DLLContext
IsMultiThreaded=True
MainThreadID=140737350458240
From my Loop
Quote
CheckSynchronize=False
CurrentThreadID=140737350458240
It's still not collecting the Sync's fired by the thread.
I always only got the Log for the last run before stopping/Destroying the thread (yes i use MyThread.WaitFor there)
One System to rule them all, One IDE to find them,
One Code to bring them all, and to the Framework bind them,
in the Land of Redmond, where the Windows lie
---------------------------------------------------------------------
People call me crazy, because i'm jumping out of perfectly fine aircrafts

engkin

  • Hero Member
  • *****
  • Posts: 2513
Re: MessageLoop in NON-GUI shared library
« Reply #5 on: March 03, 2019, 11:04:17 pm »
That was the first hurdle, not the last.

Where do you create the threads?  If what you want is at all possible, you have to create them inside the shared library. Because now you have two copies of basic units. For instance, thread queue variables are in unit classes. You have two different copies of this unit, one in the shared library, while the other in the main app.

Zvoni

  • Sr. Member
  • ****
  • Posts: 267
Re: MessageLoop in NON-GUI shared library
« Reply #6 on: March 03, 2019, 11:26:33 pm »
That was the first hurdle, not the last.

Where do you create the threads?  If what you want is at all possible, you have to create them inside the shared library. Because now you have two copies of basic units. For instance, thread queue variables are in unit classes. You have two different copies of this unit, one in the shared library, while the other in the main app.
Yes, i create them in the Library through a call to one of the exported functions.
As Result i pass the ThreadID and a general Status-Message back
One System to rule them all, One IDE to find them,
One Code to bring them all, and to the Framework bind them,
in the Land of Redmond, where the Windows lie
---------------------------------------------------------------------
People call me crazy, because i'm jumping out of perfectly fine aircrafts

engkin

  • Hero Member
  • *****
  • Posts: 2513
Re: MessageLoop in NON-GUI shared library
« Reply #7 on: March 03, 2019, 11:35:07 pm »
I just saw that you edited your first post. You are calling CheckSynchronize in a different thread. TFPTimer creates a thread to run.

Edit:
Depends on UseTimerThread.
« Last Edit: March 03, 2019, 11:41:31 pm by engkin »

Zvoni

  • Sr. Member
  • ****
  • Posts: 267
Re: MessageLoop in NON-GUI shared library
« Reply #8 on: March 03, 2019, 11:43:47 pm »
I just saw that you edited your first post. You are calling CheckSynchronize in a different thread. TFPTimer creates a thread to run.
That's the Thing: I check GetCurrentThreadID (and call CheckSynchronize) inside the OnTimer-Event, and it returns the same value as MainThreadID
One System to rule them all, One IDE to find them,
One Code to bring them all, and to the Framework bind them,
in the Land of Redmond, where the Windows lie
---------------------------------------------------------------------
People call me crazy, because i'm jumping out of perfectly fine aircrafts

engkin

  • Hero Member
  • *****
  • Posts: 2513
Re: MessageLoop in NON-GUI shared library
« Reply #9 on: March 03, 2019, 11:55:05 pm »
What OS are you on?

Zvoni

  • Sr. Member
  • ****
  • Posts: 267
Re: MessageLoop in NON-GUI shared library
« Reply #10 on: March 04, 2019, 12:08:11 am »
Right now: Linux.
Tomorrow i'll test further on a Win7-32Bit

Just found something weird:
I think it's the Timer.
The Timer-Event only get's fired once, and i have the Interval at 100

EDIT: There seems to be something wrong with TFPTimer.
It never enters the StartTimer-Method!
« Last Edit: March 04, 2019, 12:13:22 am by Zvoni »
One System to rule them all, One IDE to find them,
One Code to bring them all, and to the Framework bind them,
in the Land of Redmond, where the Windows lie
---------------------------------------------------------------------
People call me crazy, because i'm jumping out of perfectly fine aircrafts

engkin

  • Hero Member
  • *****
  • Posts: 2513
Re: MessageLoop in NON-GUI shared library
« Reply #11 on: March 04, 2019, 01:08:04 am »
I just gave it a try.

The main application:
Code: Pascal  [Select]
  1. program proj;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. uses
  6.   {$IFDEF UNIX}{$IFDEF UseCThreads}
  7.   cthreads,
  8.   {$ENDIF}{$ENDIF}
  9.   Classes, dynlibs
  10.   { you can add units after this };
  11.  
  12. type
  13.   PProcedure = ^TProcedure;
  14. var
  15.   Terminate: TProcedure;
  16.   lib: TLibHandle;
  17.  
  18. begin
  19.   WriteLn('Hello');
  20.  
  21.   lib := LoadLibrary('lib.dll');
  22.   Terminate := TProcedure(GetProcAddress(Lib, 'Terminate'));
  23.  
  24.   ReadLn;
  25.  
  26.   Terminate;
  27.   WriteLn('Good bye.');
  28.   UnloadLibrary(lib);
  29. end.

The library:
Code: Pascal  [Select]
  1. library lib;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. uses
  6.   Classes
  7.   { you can add units after this };
  8.  
  9. type
  10.  
  11.   { TTestThread }
  12.  
  13.   TTestThread=class(TThread)
  14.   protected
  15.     i: integer;
  16.     procedure Sync;
  17.   public
  18.     procedure Execute; override;
  19.   end;
  20.  
  21.   { TFakeMainThread }
  22.  
  23.   TFakeMainThread=class(TThread)
  24.   public
  25.     procedure Execute; override;
  26.   end;
  27.  
  28. { TTestThread }
  29.  
  30. procedure TTestThread.Sync;
  31. begin
  32.   WriteLn('ThreadID: ',ThreadID,': ',i);
  33. end;
  34.  
  35. procedure TTestThread.Execute;
  36. begin
  37.   i := 0;
  38.   while not Terminated do
  39.   begin
  40.     inc(i);
  41.     if (i mod 10000000)=0 then
  42.        Synchronize(@Sync);
  43.   end;
  44. end;
  45.  
  46. { TFakeMainThread }
  47.  
  48. procedure TFakeMainThread.Execute;
  49. var
  50.   TestThread1: TTestThread;
  51.   TestThread2: TTestThread;
  52. begin
  53.   TestThread1 := TTestThread.Create(false);
  54.   TestThread2 := TTestThread.Create(false);
  55.  
  56.   while not Terminated do
  57.     CheckSynchronize();
  58.  
  59.   TestThread1.Terminate;
  60.   TestThread2.Terminate;
  61. end;
  62.  
  63. var
  64.   FakeMainThread: TFakeMainThread=nil;
  65.   RealMainThreadID: TThreadID;
  66.  
  67. procedure Terminate;
  68. begin
  69.   if assigned(FakeMainThread) then
  70.     FakeMainThread.Terminate;
  71. end;
  72.  
  73. exports
  74.   Terminate;
  75.  
  76. begin
  77.   RealMainThreadID := MainThreadID;
  78.   FakeMainThread := TFakeMainThread.Create(True);
  79.   MainThreadID := FakeMainThread.ThreadID;
  80.  
  81.   FakeMainThread.Start;
  82. end.

Sample of the output:
Quote
Hello
ThreadID: 1324: 10000000
ThreadID: 2024: 10000000
ThreadID: 1324: 20000000
ThreadID: 2024: 20000000
ThreadID: 1324: 30000000
ThreadID: 2024: 30000000

Good bye.

The code needs to be cleaned.

Zvoni

  • Sr. Member
  • ****
  • Posts: 267
Re: MessageLoop in NON-GUI shared library
« Reply #12 on: March 04, 2019, 08:14:50 am »
Thx@engkin

Will report back if i got it working.
One System to rule them all, One IDE to find them,
One Code to bring them all, and to the Framework bind them,
in the Land of Redmond, where the Windows lie
---------------------------------------------------------------------
People call me crazy, because i'm jumping out of perfectly fine aircrafts

Zvoni

  • Sr. Member
  • ****
  • Posts: 267
Re: MessageLoop in NON-GUI shared library
« Reply #13 on: March 04, 2019, 08:27:48 pm »
Reporting back.

Got it the way engkin did, except i defined/assigned a Method of the FakeMainThread to receive the Sync's from the ChildThreads

@engkin
+10
Many thx

Last Question: Is it a good/better idea to implement a Critical Section inside the Sync-Procedure of the ChildThread?
Scenario:
FakeMainThread is running. FakeMainThread has a MyMethod which is assigned to the OnStatusChange-Property of each ChildThread.
A ChildThread is running its Execute and meets the condition to fire Synchronize with its own Sync-Procedure.
In this Sync-Procedue i call the FOnStatusChange-Field of the ChildThread which fires the MyMethod of FakeMainThread.

Now, should in this Sync-Procedure be a Critical Section, since it is possible, that multiple ChildThreads could reach the same point and want to execute MyMethod in its Parent at the same time?
One System to rule them all, One IDE to find them,
One Code to bring them all, and to the Framework bind them,
in the Land of Redmond, where the Windows lie
---------------------------------------------------------------------
People call me crazy, because i'm jumping out of perfectly fine aircrafts

engkin

  • Hero Member
  • *****
  • Posts: 2513
Re: MessageLoop in NON-GUI shared library
« Reply #14 on: March 04, 2019, 09:09:15 pm »
If I understood you correctly, I don't think you need to use any critical section. Calls to sync procedures are actually run inside the fake main thread in the order they were added to the queue. When the fake main thread gets interrupted amid one of the sync calls, next time it will continue that same call before it moves to the next one.

If you were to use some common (global or static) variable then you need to provide some protection, since it may be accessed by more than one thread at the same time.