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'.
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 elasped time. 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
elasped time. 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 elasped 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. However, any other events that are raised during the execution of the event handler will interrupt the execution of the original event handler. It is thus advisable to keep the event handling code as short as possible - if more complex actions needs to be performed, it should be done via the event handler triggering a suspended goal, which will execute at a lower priority than 1.
The after event make use of the vtalrm signal where this signal exists, or the alrm signal if it doesn't, so elasped time is normally measured in elasped user cpu time, or in real time in the case of alrm. Currently, alrm is used only on the Windows platform. The user should not make use of these signals 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 elasped 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.
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:
:- 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).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.