3

I have an issue with a simple TTimer that's initiated and have its OnTimer event executed in the main app thread, the code looks like this:

procedure TForm1.DoSomeStuff();
begin
     OmniLock.Acquire;
     try
        Parallel.Pipeline.NumTasks(MaxThreads).Stage(StageProc).Run;

        if (MyTimer = nil) then
        begin
             MyTimer := TTimer.Create(nil);
             MyTimer.Interval := 60 * 1000;  // timer fired every 60 seconds
             MyTimer.OnTimer := MyTimerEvent;
             MyTimer.Enabled := True;
         end;
      finally
             OmniLock.Release;
      end;    // try/finally
 end;

Everthing work just fine when I execute the code in a simple project/demo, but in my app (which uses Omni Thread Library v3), the timer event is never fired

I'm pretty sure it's nothing, I just can't figure out what's wrong!

I triple checked: MyTimer is only assigned once in my code, its OnTimer event is correctly assigned, etc...

I'm using Delphi 2010

Anyone knows how to fix this?

gabr
  • 26,580
  • 9
  • 75
  • 141
TheDude
  • 3,045
  • 4
  • 46
  • 95
  • can you show how DoSomeStuff is called – David Heffernan May 14 '12 at 19:28
  • It's called from another function in the main form, nothing fancy: just a simple `DoSomeStuff();` call. And, I'm only calling it **once** in my app – TheDude May 14 '12 at 19:32
  • 2
    Is it called from an event handler perhaps? Why won't you tell us? – David Heffernan May 14 '12 at 19:33
  • 2
    If 'initiated and have its OnTimer event executed in the main app thread', why is there a lock around it? – Martin James May 14 '12 at 19:35
  • Thank you David and Martin...my code was like `FormCreate() --> LoadApp() --> Scan() --> DoSomeStuff()`, I moved the timer initialization to the function `Scan()`, **removed the locking** and now everything works just fine – TheDude May 14 '12 at 20:18
  • There should be no reason (OmniThreadLibrary-wise) for this behaviour. Pipeline.Run is nonblocking and should not affect your timer creation. If you can create a small program that is not working correctly, I'd be glad to look at it to see if this is a bug in the OTL that is causing the problem. – gabr May 15 '12 at 07:04
  • BTW, as your are running one stage on mulitple threads, better abstractions may be ForEach, ParallelTask or BackgroundWorker. – gabr May 15 '12 at 07:05
  • @gabr: `LoadApp()` & `Scan()` routines are not meant to be multi-threaded. I'll try to reproduce this on a smaller scale & see if I can reproduce the issue... – TheDude May 15 '12 at 10:37
  • @gabr PS. Does your suggestion still applies if I'm using it [in this context](http://otl.17slon.com/forum/index.php/topic,354.0.html)? – TheDude May 15 '12 at 10:38
  • 2
    @Gdhami Yes, BackgroundWorker is basically a single-stage pipeline with additional cancellation mechanism. I'm not saying that using Pipeline is this context is wrong, it's just that there may be a more appropriate mechanism built into the OTL. – gabr May 15 '12 at 10:46
  • Thank you gabr, I'll take a look at BackgroundWorker! – TheDude May 15 '12 at 10:48

1 Answers1

7

TTimer is a message based timer. Whatever thread context the TTimer is created in must have an active message loop in order for TTimer to process its WM_TIMER messages.

TTimer is not a thread-safe timer. In order to receive the WM_TIMER messages, it has to allocate an HWND window handle for itself. It does so using the VCL's AllocateHWnd() function, which is not thread-safe and must not be called from outside the context of the main thread.

If you need a thread-safe timer, either call CreateWindow() directly and pump/process the WM_TIMER messages directly, or else use a different timer mechanism, such as a threaded multimedia timer via timeSetEvent(), or even just a simple busy loop via Sleep() or WaitForSingleObject(). Without knowing what you are using the timer for, it is difficult to pin-point an alternative that suits your needs.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • According to the question, the timer is created in the main thread. That's what that bold text says. – David Heffernan May 14 '12 at 20:05
  • Thank you Remy, I'm not sure but maybe the fact that I initialized the timer (in the main thread but) **just after** I called the OmniThreadLibrary (using the `Paralell.Pipeline...run()` code) caused the issue, moving the initialization code to a different routine fixed this – TheDude May 14 '12 at 20:23
  • Sounds like Omni was blocking the main thread from processing messages. – Remy Lebeau May 14 '12 at 22:05
  • Does parallel.pipeline.run return immediately or is it a blocking call? – Warren P May 14 '12 at 22:28