merijnb 4 Posted January 15, 2021 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
Primož Gabrijelčič 223 Posted January 16, 2021 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
merijnb 4 Posted January 18, 2021 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
Primož Gabrijelčič 223 Posted January 18, 2021 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. 1 Share this post Link to post
merijnb 4 Posted January 18, 2021 (edited) 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 January 18, 2021 by merijnb found what I did wrong :) Share this post Link to post
merijnb 4 Posted January 20, 2021 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
Primož Gabrijelčič 223 Posted January 21, 2021 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 1 Share this post Link to post
CoMPi74 3 Posted July 12, 2023 @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
Primož Gabrijelčič 223 Posted July 12, 2023 Use fController := CreateTask(TWorker.Create()).MsgWait.Run; Note the extra (). I don't know why, but compiler requires them. 1 Share this post Link to post
David Heffernan 2345 Posted July 12, 2023 I wish the language would require parens for parameterless calls. 1 1 Share this post Link to post