..
   This file is part of Logtalk <https://logtalk.org/>
   SPDX-FileCopyrightText: 1998-2026 Paulo Moura <pmoura@logtalk.org>
   SPDX-License-Identifier: Apache-2.0

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.


.. _events_events:

Event-driven programming
========================

The addition of event-driven programming capacities to the Logtalk
language [Moura94]_ is based on a simple but powerful idea:

   The computations must result not only from message-sending but also
   from the **observation** of message-sending.

The need to associate computations to the occurrence of events was very
early recognized in knowledge representation languages, programming languages
[Stefik_et_al_86]_, [Moon86]_, operative systems [Tanenbaum87]_, and
graphical user interfaces.

With the integration between object-oriented and event-driven
programming, we intend to achieve the following goals:

*  Minimize the *coupling* between objects. An object should only contain
   what is intrinsic to it. If an object observes another object, that
   means that it should depend only on the public protocol of the
   object observed and not on the implementation of that protocol.

*  Provide a mechanism for building *reflexive systems* in Logtalk based
   on the dynamic behavior of objects in complement to the reflective
   information on object predicates and relations.

*  Provide a mechanism for easily defining method *pre- and
   post-conditions* that can be toggled using the :ref:`events <flag_events>`
   compiler flag. The pre- and post-conditions may be defined in the same
   object containing the methods or distributed between several objects
   acting as method monitors.

*  Provide a *publish-subscribe* mechanism where public messages play the
   role of events.

.. _events_definitions:

Definitions
-----------

The words *event* and *monitor* have multiple meanings in computer
science. To avoid misunderstandings, we start by defining them in
the Logtalk context.

.. _events_event:

Event
~~~~~

In an object-oriented system, all computations start through message
sending. It thus becomes quite natural to declare that the only event
that can occur in this kind of system is precisely the sending of a
message. An event can thus be represented by the ordered tuple
``(Object, Message, Sender)``.

If we consider message processing an indivisible activity, we can
interpret the sending of a message and the return of the control to the
object that has sent the message as two distinct events. This
distinction allows us to have more precise control over a system's
dynamic behavior. In Logtalk, these two types of events have been named
``before`` and ``after``, respectively for sending a message and for
returning control to the sender. Therefore, we refine our event
representation using the ordered tuple ``(Event, Object, Message, Sender)``.

The implementation of events in Logtalk enjoys the following properties:

Independence between the two types of events
   We can choose to watch only one event type or to process each one of
   the events associated with a message-sending goal in an independent way.

All events are automatically generated by the message-sending mechanism
   The task of generating events is transparently accomplished
   by the message-sending mechanism. The user only needs to define the
   events that will be monitored.

The events watched at any moment can be dynamically changed during program execution
   The notion of event allows the user not only to have the possibility
   of observing but also of controlling and modifying an application
   behavior, namely by dynamically changing the observed events during
   program execution. It is our goal to provide the user with the
   possibility of modeling the largest number of situations.

.. _events_monitor:

Monitor
~~~~~~~

Complementary to the notion of event is the notion of monitor. A monitor
is an object that is automatically notified by the message-sending
mechanism whenever a registered event occurs. Any object that defines
the event-handling predicates can play the role of a monitor.

The implementation of monitors in Logtalk enjoys the following
properties:

Any object can act as a monitor
   The monitor status is a role that any object can perform during its
   existence. The minimum protocol necessary is declared in the built-in
   :ref:`monitoring <apis:monitoring/0>` protocol. Strictly speaking, the
   reference to this protocol is only needed when specializing event handlers.
   Nevertheless, it is considered good programming practice to always
   refer to the protocol when defining event handlers.

Unlimited number of monitors for each event
   Several monitors can observe the same event for distinct reasons.
   Therefore, the number of monitors per event is bounded only by the
   available computing resources.

The monitor status of an object can be dynamically changed at runtime
   This property does not imply that an object must be dynamic to act as
   a monitor (the monitor status of an object is not stored in the
   object).

Event handlers cannot modify the event arguments
   Notably, if the message contains unbound variables, these cannot be
   bound by the calls to the monitor event handlers.

.. _events_generation:

Event generation
----------------

Assuming that the :ref:`events <flag_events>` flag is set to ``allow`` for
the object (or category) sending the messages we want to observe, for each
message that is sent using the :ref:`control_send_to_object_2` control
construct, the runtime system automatically generates two events.
The first — *before event* — is generated when the message is sent. The
second — *after event* — is generated after the message has successfully
been executed.

Note that *self* messages (using the :ref:`control_send_to_self_1` control
construct) and *super* calls (using the :ref:`control_call_super_1` control
construct) don't generate events.

.. _events_communicating:

Communicating events to monitors
--------------------------------

Whenever a spied event occurs, the message-sending mechanism calls the
corresponding event handlers directly for all registered monitors. These
calls are internally made, thus bypassing the message-sending primitives
in order to avoid potential endless loops. The event handlers consist of
user definitions for the public predicates declared in the built-in
:ref:`monitoring <apis:monitoring/0>` protocol (see below for more details).

.. _events_performance:

Performance concerns
--------------------

Ideally, the existence of monitored messages should not affect the
processing of the remaining messages. On the other hand, for each
message that has been sent, the system must verify if its respective
event is monitored. Whenever possible, this verification should be
performed in constant time and independently of the number of monitored
events. The representation of events takes advantage of the first
argument indexing performed by most Prolog compilers, which ensure — in
the general case — access in constant time.

Event support can be turned off on a per-object (or per-category) basis
using the :ref:`events <flag_events>` compiler flag. With event support
turned off, Logtalk uses optimized code for processing message-sending
calls that skips the checking of monitored events, resulting in a small
but measurable performance improvement.

.. _events_semantics:

Monitor semantics
-----------------

The established semantics for monitor actions consists of considering
its success as a necessary condition so that a message can succeed:

-  All actions associated with events of type ``before`` must succeed
   so that the message processing can start.

-  All actions associated with events of type ``after`` also have to
   succeed so that the message itself succeeds. The failure of any
   action associated with an event of type ``after`` forces backtracking
   over the message execution (the failure of a monitor never causes
   backtracking over the preceding monitor actions).

Note that this is the most general choice. If we require a transparent
presence of monitors in a message processing, we just have to define the
monitor actions in such a way that they never fail (which is very simple
to accomplish).

.. _events_order:

Activation order of monitors
----------------------------

Ideally, whenever there are several monitors defined for the same event,
the calling order should not interfere with the result. However, this is
not always possible. In the case of an event of type ``before``, the
failure of a monitor prevents a message from being sent and prevents the
execution of the remaining monitors. In the case of an event of type
``after``, a monitor failure will force backtracking over message
execution. Different orders of monitor activation can therefore lead to
different results if the monitor actions imply object modifications
unrecoverable in case of backtracking. Therefore, the order for monitor
activation should be assumed as arbitrary. In effect, to assume or to
try to impose a specific sequence requires a global knowledge of an
application dynamics, which is not always possible. Furthermore, that
knowledge can reveal itself as incorrect if there is any change in the
execution conditions. Note that, given the independence between
monitors, it does not make sense that a failure forces backtracking over
the actions previously executed.

.. _events_handling:

Event handling
--------------

Logtalk provides three built-in predicates for event handling. These
predicates support defining, enumerating, and abolishing events.
Applications that use events extensively usually define a set of objects
that use these built-in predicates to implement more sophisticated and
higher-level behavior.

.. _events_defining:

Defining new events
~~~~~~~~~~~~~~~~~~~

New events can be defined using the :ref:`predicates_define_events_5`
built-in predicate:

.. code-block:: text

   | ?- define_events(Event, Object, Message, Sender, Monitor).

Note that if any of the ``Event``, ``Object``, ``Message``, and
``Sender`` arguments is a free variable or contains free variables, this
call will define a **set** of matching events.

.. _events_abolishing:

Abolishing defined events
~~~~~~~~~~~~~~~~~~~~~~~~~

Events that are no longer needed may be abolished using the
:ref:`predicates_abolish_events_5` built-in predicate:

.. code-block:: text

   | ?- abolish_events(Event, Object, Message, Sender, Monitor).

If called with free variables, this goal will remove all matching
events.

.. _events_finding:

Finding defined events
~~~~~~~~~~~~~~~~~~~~~~

The events that are currently defined can be retrieved using the
:ref:`predicates_current_event_5` built-in predicate:

.. code-block:: text

   | ?- current_event(Event, Object, Message, Sender, Monitor).

Note that this predicate will return **sets** of matching events if some
of the returned arguments are free variables or contain free variables.

.. _events_handlers:

Defining event handlers
~~~~~~~~~~~~~~~~~~~~~~~

The :ref:`monitoring <apis:monitoring/0>` built-in protocol declares two
public predicates, :ref:`methods_before_3` and :ref:`methods_after_3`, that
are automatically called to handle ``before`` and ``after`` events. Any
object that plays the role of monitor must define one or both of these
event handler methods:

::

   before(Object, Message, Sender) :-
       ... .

   after(Object, Message, Sender) :-
       ... .

The arguments in both methods are instantiated by the message-sending
mechanism when a monitored event occurs. For example, assume that we
want to define a monitor called ``tracer`` that will track any message
sent to an object by printing a descriptive text to the standard output.
Its definition could be something like:

::

   :- object(tracer,
       % built-in protocol for event handler methods
       implements(monitoring)).

       before(Object, Message, Sender) :-
           write('call: '), writeq(Object),
           write(' <-- '), writeq(Message),
           write(' from '), writeq(Sender), nl.

       after(Object, Message, Sender) :-
           write('exit: '), writeq(Object),
           write(' <-- '), writeq(Message),
           write(' from '), writeq(Sender), nl.

   :- end_object.

Assume that we also have the following object:

::

   :- object(any).

       :- public(bar/1).
       bar(bar).

       :- public(foo/1).
       foo(foo).

   :- end_object.

After compiling and loading both objects and setting the
:ref:`events <flag_events>` flag to ``allow``, we can start tracing
every message sent to any object by calling the
:ref:`predicates_define_events_5` built-in predicate:

.. code-block:: text

   | ?- set_logtalk_flag(events, allow).

   yes

   | ?- define_events(_, _, _, _, tracer).

   yes

From now on, every message sent from ``user`` to any object will be
traced to the standard output stream:

.. code-block:: text

   | ?- any::bar(X).

   call: any <-- bar(X) from user
   exit: any <-- bar(bar) from user
   X = bar

   yes

To stop tracing, we can use the :ref:`predicates_abolish_events_5`
built-in predicate:

.. code-block:: text

   | ?- abolish_events(_, _, _, _, tracer).

   yes

The :ref:`monitoring <apis:monitoring/0>` protocol declares the event
handlers as public predicates. If necessary, :ref:`protected or private
implementation of the protocol <protocols_implementing>` may be used in
order to change the scope of the event handler predicates. Note that the
message-sending processing mechanism is able to call the event handlers
irrespective of their scope. Nevertheless, the scope of the event handlers
may be restricted in order to prevent other objects from calling them.

The pseudo-object :ref:`user <objects_user>` can also act as a monitor.
This object expects the ``before/3`` and ``after/3`` predicates to be
defined in the plain Prolog database. To avoid predicate existence errors
when setting ``user`` as a monitor, this object declares the predicates
multifile. Thus, any plain Prolog code defining the predicates should
include the directives:

::

   :- multifile(before/3).
   :- multifile(after/3).
