A handler is defined using a call like this
my_handler(Event) :- <code to deal with Event> :- set_event_handler(hello, my_handler/1).The handler's first argument is the event identifier, in this case the atom 'hello'.
The handler for a defined event can be obtained by get_event_handler/3.
Note that to ensure the handling of all events, an event handler should not directly fail or raise an exception. This is because the system will backtrack if the handler fails or raise an exception, and any other raised events that has not yet been handled will not be handled, and thus the system will seem to `forget' about such events. The event handler itself should also be run at the highest priority (1), and if failure is desired, this can be done indirectly through triggering a suspended goal which runs at a lower priority.
ec_post_event(ec_atom(ec_did("hello",0)));This works both when the foreign code is called from ECLiPSe or when ECLiPSe has been called from the foreign code.
It is also possible to post an event from within an interrupt handler by setting the interrupt handler to event/1. This is the recommended mechanism to translate an asynchronous interrupt into a synchronous event. E.g.
:- set_interrupt_handler(alrm, event/1). :- set_event_handler(alrm, handle_alarm/1).
An event can also be raised by the running program itself, using event/1:
..., event(hello), ...However, this is mainly useful for test purposes, since it is almost the same as calling the handler directly.
ECLiPSe provides support for setting up an event which is then triggered
after a specified amount
of elapsed time. The event is then handled sychronously by ECLiPSe.
Previous to version 4.2, the user can program this
functionality using the (now obsolete) low level OS dependent
set_timer/2
primitives. From version 4.2, a higher level
interface is provided, allowing for multiple independent timed events
to be set up in a standardised way. These events are known as after
events, as they are set up so that the event occurs after a
certain amount of elapsed time. The elapsed time used is either
elapsed real time (real) or elapsed user CPU time (virtual). They are
setup by two predicates:
Once an after event has been set up, it is pending until it is raised. In
the case of event_after_every/2
, the event will always be pending
because it is rasied repeatedly. A pending event can be cancelled so that
it will not be raised:
More precisely, Time is actually the minimum of elapsed time before the event is raised. Factors constraining the actual time of raising of the event include the granularity of the system clock, and also that ECLiPSe must be in a state where it can synchronously process the event - it needs to be where it can make a procedure call.
The event is raised and executed at priority 1, so that it cannot be interrupted by execution of woken goals in the middle of handling the event. In addition, the after event timer is frozen (i.e. the countdown is stopped) during the execution of the event handler, so that no new pending after events will be raised while handling the after events. The event handling code is executed once when the event is triggered, i.e. it would not be backtracked into. Failures in the event handling code are not propagated beyond the handler, and exceptions (aborts) should also not be raised during the event handling, as this would prevent other already raised events from being processed. It is advisable that the event handling code be as simple as possible - if more complex actions needs to be performed, it should be done via the event handler triggering a suspended goal, executing at a lower priority than 1, and for which the event timer would not be frozen, and where exceptions and failures can be propagated.
By default, the after event feature uses the real timer. The timer can be switched to the virtual timer, in which case the elapsed time measured is user CPU time13.1 This default is specified by the ECLiPSe environment flag after_event_timer (see get_flag/2, set_flag/2). Note that if the timer is changed while some after event is still pending, these events will no longer be processed. The after event mechanism is also set to the unfrozen state. It is strongly recommended that the user does not change the timer once after events are initiated.
Currently, the virtual timer is not available on the Windows platform. In addition, the user should should not make use of these timers for their own purpose if they plan on using the after event mechanism.
The after event mechanism allows multiple events to make use of the timing
mechanism independently of each other. However, the same event can be setup
multiple times with multiple calls to event_after/2
and
event_after_every/2
. The cancel_after_event/1
will cancel all
instances of an event.
Using the suspension and event handling mechanisms, the user can cause a goal to be added to the resolvent which would then be executed after a defined elapsed time. The goal will be suspended and attached to a symbolic trigger, which is triggered by the event handler. The goal behaves `logically', in that if the execution backtracks pass the point in which the suspended goal is created, the goal will disappear from the resolvent as expected and thus not be executed. The event will still be raised, but there will not be a suspended goal to wake up. Note that if the execution finishes before the suspended goal is due to be woken up, it will also not enter the resolvent and is thus not executed.
The following is an example for waking a goal with a timed event.
Once monitor(X)
is called, the current value of X will be
printed every second while the query monitor(X) is part of is executing:
:- set_event_handler(monvar, trigger/1). monitor(Var) :- suspend(m(Var), 3, trigger(monvar)), event_after_every(monvar, 1). :- demon m/1. m(Var) :- writeln(Var). :- monitor(Var), <do_something>.Note the need to declare
m/1
as a demon: otherwise, once m/1
is woken up once, it will
disappear from the resolvent and the next monvar
event will not have
a suspended m/1
to wake up.
Note also that it is necessary to connect the event machanism to
the waking mechanism by setting the event handler to trigger/1.