[fpc-pascal] TFPTimer component.
Vinzent Hoefler
JeLlyFish.software at gmx.net
Mon Jun 25 09:46:52 CEST 2007
On Friday 22 June 2007 07:28, 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.
More information about the fpc-pascal
mailing list