Jump to content
merijnb

Messageloop in Omnithread task

Recommended Posts

Hey all,

 

I have some stuff which now works as a descendant from TThread. In the execute method there is a normal message loop.
If I want to rework this to omni, how can I approach this?

It boils down to a situation where I can create something like a timer in the omnithread task, then run a message loop and thus will get OnTimer events on scope of the tasks thread.

Share this post


Link to post

For a timer, call Task.SetTimer and timer event will be called automatically. 

 

Something like this:

uses
  OtlTaskControl;

type
  TWorker = class(TOmniWorker)
  protected
    function  Initialize: boolean; override;
  public
    procedure TimerCallback;
  end;

function TWorker.Initialize: boolean;
begin
  Result := inherited Initialize;
  if Result then
    Task.SetTimer(1, 100, TimerCallback);
end;

procedure TWorker.TimerCallback;
begin
  // called every 100 ms
end;

var
  task: IOmniTaskControl;

task := CreateTask(TWorker.Create()).Run;

 

Share this post


Link to post

Thanks Primoz,

 

The timer was just an example though, I need a message loop in a thread for several async stuff which is going on there.

Share this post


Link to post

Variant 1: Use a 'procedure' worker (`procedure Worker(const task: IOmniTask)'). Then you can implement your message loop inside.

 

Variant 2: Use a `TOmniWorker` descendant (as in my example). It will implement its own message loop.

 

Variant 2a: Override parts of the TOmniWorker message loop (but I actually don't remember anymore how that is done 😠 ).

 

You can handle most stuff with Variant 2. In addition to timers you can call Task.RegisterWaitObject to connect code to any waitable handle. See demo 31_WaitableObjects for more information. Read more here: http://www.omnithreadlibrary.com/book/chap07.html#lowlevel-iomnitask-registerwaitobject.

  • Like 1

Share this post


Link to post
On 1/16/2021 at 7:24 PM, Primož Gabrijelčič said:

For a timer, call Task.SetTimer and timer event will be called automatically. 

 

Something like this:


uses
  OtlTaskControl;

type
  TWorker = class(TOmniWorker)
  protected
    function  Initialize: boolean; override;
  public
    procedure TimerCallback;
  end;

function TWorker.Initialize: boolean;
begin
  Result := inherited Initialize;
  if Result then
    Task.SetTimer(1, 100, TimerCallback);
end;

procedure TWorker.TimerCallback;
begin
  // called every 100 ms
end;

var
  task: IOmniTaskControl;

task := CreateTask(TWorker.Create()).Run;

 

Hi Primoz,

 

I've tried this exact code, but the TimerCallback isn't called.
I also tried to create a normal timer in Initialize, but it's also not called. Is there something missing here which causes the message loop not to run or am I missing something else?

 

Nvm: I found my brainfart 🙂

 

Edited by merijnb
found what I did wrong :)

Share this post


Link to post
On 1/18/2021 at 10:01 AM, Primož Gabrijelčič said:

Variant 1: Use a 'procedure' worker (`procedure Worker(const task: IOmniTask)'). Then you can implement your message loop inside.

 

Variant 2: Use a `TOmniWorker` descendant (as in my example). It will implement its own message loop.

 

Variant 2a: Override parts of the TOmniWorker message loop (but I actually don't remember anymore how that is done 😠 ).

 

You can handle most stuff with Variant 2. In addition to timers you can call Task.RegisterWaitObject to connect code to any waitable handle. See demo 31_WaitableObjects for more information. Read more here: http://www.omnithreadlibrary.com/book/chap07.html#lowlevel-iomnitask-registerwaitobject.

While playing around with this, I still run into something I don't quite get, please see this code:

 


uses
  OtlTaskControl;

type
  TWorker = class(TOmniWorker)
  protected
    fTimer: TTimer; 
  
    procedure OnTimer(Sender: TObject);
    function  Initialize: boolean; override;
  public
    procedure TimerCallback;
  end;

function TWorker.Initialize: boolean;
begin
  Result := inherited Initialize;
  if Result then
  begin
    Task.SetTimer(1, 100, TimerCallback);
	
    fTimer := TTimer.Create(nil);
    fTimer.OnTimer := OnTimer; 
    fTimer.Interval := 1000;
    fTimer.Enabled := true;
 end;
end;

procedure TWorker.TimerCallback;
begin
  // called every 100 ms
end;

procedure TWorker.OnTimer(Sender: TObject);
begin
  // called every 1000 ms
end;

var
  task: IOmniTaskControl;

task := CreateTask(TWorker.Create()).Run;

In this case, the OnTimer event is fired as expected, so the message loop from TOmniWorker seems to be running fine. However, it only works as long as I also do Task.SetTimer, as soon as I remove that line - Task.SetTimer(1, 100, TimerCallback) -, my OnTimer event isn't fired anymore. It seems SetTimer does something needed to get the message loop going? What can I do to get this working without calling Task.SetTimer()?

Thanks in advance!

Share this post


Link to post

By default, an OTL task does not process windows messages (which is requirement for TTimer to work). To enfore windows message processing, call the MsgWait method of your task controller interface before running/scheduling the task. Like this:

task := CreateTask(TWorker.Create()).MsgWait.Run;

Read more here: http://www.omnithreadlibrary.com/book/chap07.html#lowlevel-tomniworker-msgwait

  • Thanks 1

Share this post


Link to post

@Primož Gabrijelčič Trying to compile following code: 

  fController := CreateTask(TWorker.Create).MsgWait.Run;

 I got an error with a message:

 

[dcc32 Error] Unit1.pas(269): E2250 There is no overloaded version of 'CreateTask' that can be called with these arguments.

 

 

Any clue?

 

 

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×