Sign in Join

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

Mailing lists / fpc-pascal / 2007-06

TFPTimer component.

4 messages · 3 people · –

Reply by email Subscribe to this list Your subscription

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

Michael Van Canneyt

Hello,

Some time ago, Graeme Geldenhuys (hope I spelled this correct from memory) posted some TFPTimer component to this list.

The component was changed so another timer mechanism can be plugged in, and the result has been added to the FCL. A small test program is provided.

The Lazarus people may want to have a look at it: this component can be used in other than GUI programs. In time, it may be useful to recode TTimer so it bases itself on TFPTimer, and to use a native driver mechanism when a GUI is available.

With my thanks to Graeme for his contribution.

Michael.

Reply by email

Graeme Geldenhuys
to Michael Van Canneyt
Michael Van Canneyt wrote
Some time ago, Graeme Geldenhuys (hope I spelled this correct from memory) posted some TFPTimer component to this list.

The name is 100% correct. :-)
Thanks for taking my idea further and submitting it to FCL. This is what I love about OpenSource - get the idea out there and let others extend and improve it if they wish.

Regards,
- Graeme -

Reply by email

Michael Van Canneyt
to Graeme Geldenhuys
Graeme Geldenhuys wrote 9 lines
On 22/06/07, Michael Van Canneyt wrote: > > Some time ago, Graeme Geldenhuys (hope I spelled this correct from memory) > posted some TFPTimer component to this list. The name is 100% correct. :-) Thanks for taking my idea further and submitting it to FCL. This is what I love about OpenSource - get the idea out there and let others extend and improve it if they wish.

Absolutely...

There are already 2 ideas for even further improvement:
- Use 1 thread to fire events for all timers.
Should be relatively easy.

- Don't use synchronize to fire the timer, because that limits the use of timers to the main thread.
This one is harder, and I must confess I don't have a clue on how to implement this in a platform independent manner...

Michael.

Reply by email

Vinzent Hoefler
to Michael Van Canneyt
Michael Van Canneyt wrote
- Don't use synchronize to fire the timer, because that limits the use of timers to the main thread. This one is harder, and I must confess I don't have a clue on how to implement this in a platform independent manner...

Well, independently from Graeme I implemented something like that already, closely resembling the Ada2005 Ada.Real_Time.Timing_Events package. Implemented targets would be Win32 and Unix (tested for Linux, but as I am only using POSIX-functionality it should run on any other Unix, too).

Problems are:

1) All fired events are currently executed in a single thread, meaning, the can quite easily block out each other. Could be easily changed, although starting thousand of threads may turn out to be a bad idea. ;)

Currently I am thinking about getting away from a global scheduler and setting up several ones, this would cut down the number of threads in case of many timing events being active and could be used to implement a sort of user defined priorization (fast executing/important vs. slow executing/less important events).

2) Event execution is always independent (outside) of the main thread, so a user must take care of all the multi-threading hassles.

3) With the current implementation you can't use "Destroy" on such a Timing_Event, because those objects are used in another thread, so instead of calling Destroy you'd have to used "Finalize" to mark such an event as done. It will be freed later from the "scheduling" loop.

4) On Unix it uses a signal to wake up the thread on changes in the timing event queue, so someone must ensure that this signal is blocked in all other threads and may not be used otherwise.

6) It uses some more modules (especially some Calendar unit which uses integers for duration types to avoid drifting issues you'd have if you'd used the floating point TDateTime).

7) As I am currently rewriting the stuff for the priority queue I am going to be using, I won't post any sourcecode yet, apart from the interface:

-- 8< --
//-- @abstract(Provides  a (crude) scheduling object to install handlers
//--           to be executed at some future point in time.)
//-- It closely resembles the @code(Ada2005 Ada.Real_Time.Timing_Events)
//-- package,  so for the correct semantics of the operations (which are
//-- currently  not 100% guaranteed in this implementation) you may also
//-- refer to the Ada2005 language reference manual.
unit
   Timing_Events;
interface

uses
Calendar,

   SyncObjs;
type
   Timing_Event = class; // Forward  declaration  for  criss-cross  type
                         // dependency.
type
   //-- @abstract(Type for the event handler @italic(callback).)
   Timing_Event_Handler =
         procedure (const Event : Timing_Event) of object;
type
   //-- @abstract(The base type indicating a timing event.)
   //-- It  stores  the  next  scheduled execution and the handler to be
   //-- executed.
   Timing_Event = class (tObject)
   public
      {/= Create =====================================================\}
      {                                                                }
      {\==============================================================/}
      //-- @abstract(The  usual constructor.  Initializes all the needed
      //--           internals.)
      constructor Create;
      {/= Destroy ====================================================\}
      {                                                                }
      {\==============================================================/}
      //-- @abstract(Cleans up object internals.)
      //-- Due  to  its inherent asynchronous usage,  DO NOT call @code(
      //-- Free)  or even @code(Destroy) directly.  Leave the freeing up
      //-- to the @link(Finalize) subroutine.)
      destructor Destroy; override;
      {/= Finalize ===================================================\}
      {                                                                }
      {\==============================================================/}
      //-- @abstract(Marks the object for finalization.)
      //-- This  is  a replacement for @code(Free) which can not be used
      //-- in  this  asynchronous  context.  Instead  if you call @code(
      //-- Finalize)  the  event  will be marked for deletion and @code(
      //-- Free)  will  then be called on the next run of the scheduler.
      //-- Basically  this  just means that the freeing will be deferred
      //-- an indefinite time.  In case the event in currently scheduled
      //-- to run in a moment, this subroutine will not return until the
      //-- run is over.
      //--
      //-- @bold(Do NOT)  call  any methods of the instance after having
      //-- called @code(Finalize)!
      procedure Finalize; virtual;
      {/= Set_Handler ================================================\}
      {                                                                }
      {\==============================================================/}
      //-- @abstract(Set  a  handler  routine for a (single-shot) timing
      //--           event.)
      //-- @param(At_Time     Sets the next scheduled execution event to
      //--                    @code(At_Time).)
      //-- @param(Handler     Associates  the  subroutine to be executed
      //--                    with  the event.  If it is @code(NIL) then
      //--                    the event will be cleared.  Basically this
      //--                    would   be  the  same  as  calling  @link(
      //--                    Cancel_Handler).)
      procedure Set_Handler (const At_Time : Calendar.Time;
                             const Handler : Timing_Event_Handler);
      {/= Set_Handler ================================================\}
      {                                                                }
      {\==============================================================/}
      //-- @abstract(Set a handler routine for a timing event.)
      //-- @param(In_Time     Sets the next scheduled execution event to
      //--                    @code(Calendar.Clock + In_Time).)
      //-- @param(Handler     Associates  the  subroutine to be executed
      //--                    with  the event.  If it is @code(NIL) then
      //--                    the event will be cleared.  Basically this
      //--                    would   be  the  same  as  calling  @link(
      //--                    Cancel_Handler).)
      procedure Set_Handler (const In_Time : Calendar.Duration;
                             const Handler : Timing_Event_Handler);
      {/= Current_Handler ============================================\}
      {                                                                }
      {\==============================================================/}
      //-- @abstract(Returns the current handler.)
      //-- @returns(The  subroutine  associated with the event or @code(
      //--          NIL) if no such event is set.)
      function Current_Handler : Timing_Event_Handler;
      {/= Cancel_Handler =============================================\}
      {                                                                }
      {\==============================================================/}
      //-- @abstract(Clears the given timing event.)
      //-- @param(Cancelled   Upon return from the subroutine it will be
      //--                    set to @code(True) if the event was set at
      //--                    the   time   of  the  call,   @code(False)
      //--                    otherwise.)
      procedure Cancel_Handler (out Cancelled : Boolean);
      {/= Time_Of_Event ==============================================\}
      {                                                                }
      {\==============================================================/}
      //-- @abstract(Return  the  projected  time  of  execution  of the
      //--           event if it is set, or a zero time otherwise.)
      //-- @returns(If  the  event is set the subroutine will return the
      //--          time  of  the  event  to  happen,  otherwise it will
      //--          return @link(Calendar.ZERO_TIME).)
      //-- @bold(NOTE):  The current implementation has a race condition
      //-- here  where  @code(Time_Of_Event) may still not return a zero
      //-- time  although the handler just finished execution and is not
      //-- set  yet.  This  is  due  to the fact that the Handler itself
      //-- should still be able to evaluate its own time of execution.
      function Time_Of_Event : Calendar.Time;
   private
      [...]
   end {Timing_Event};
-- 8< --

So, if someone is still interested, drop me a private mail.

Regards,

Vinzent.

Reply by email