# Copyright (C) 2001-2010, Parrot Foundation.
# $Id: pdd24_events.pod 45652 2010-04-14 02:57:43Z coke $

=head1 PDD 24: Events

=head2 Abstract

This document defines the requirements and implementation strategy for
Parrot's event subsystem.

=head2 Version

$Revision: 45652 $

=head2 Description

=over 4

=item - Events are objects

=item - Events are typed

=item - Events can be fatal or non-fatal

=item - Event handlers are code objects

=item - Event handlers can be final or non-final

=back

=head2 Definitions

An event is a notification that something has happened: the user has
manipulated a GUI element, an I/O request has completed, a signal has
been triggered, or a timer has expired.  Most systems these days have an
event handler (often two or three, which is something of a problem),
because handling events is so fundamental to modern GUI programming.

=head2 Implementation

Parrot's event handling system is integrated with the central concurrency
scheduler. When an event is created (by a GUI element, etc), it is added to
concurrency task list. By default, events have a higher priority in the task
list than asynchronous I/O operations or threaded code operations. At
predetermined points in the execution cycle (between low-level discrete
operations for safety, behind the scenes during "blocking" operations, at the
same operational lulls where GC runs are performed, etc), the task list is
queried, and tasks are dispatched.  Events are dispatched to event handlers.

Event handlers are registered with the concurrency scheduler. When dispatching
an event, the concurrency scheduler compares the type of the event to the type
of the event handler and selects the closest match. An exact type match is
always highest ranked, then a parent type, and finally a general handler
designed to handle all types. In addition to the type of an event, an event
handler may check the data attribute of an event and decide whether to accept
or decline that event based on its data. (For example, a "key press" may
contain a data attribute value specifying that it's a "key 'a' press".)

In the simple case, the concurrency scheduler runs within the single
interpreter thread. In more complex cases (particularly on multi-processor
machines), the concurrency scheduler runs in its own thread. In the
multi-threaded case, each individual thread may register an event handler with
the concurrency scheduler, and the event that matches the registered handler
will be dispatched to the thread that registered it. In the most complex case,
each thread runs a lightweight concurrency scheduler that coordinates with the
central scheduler (so, for example, the mini-scheduler can decide when to run
an event handler dispatched by the central scheduler).

An event handler may mark itself as a final event handler, removing the event
from the task list, or it may be a non-final handler, leaving the event in the
task list for another thread to collect.

Most events are non-fatal, so if a handler isn't found for them when they're
extracted from the task list, they just expire and drop out of the task list.
Events can also be fatal, in which case the interpreter will exit if a handler
isn't found (essentially the same effect as an exception). When a non-final
event handler leaves an event in the task list, it will expire if no further
relevant event handlers can be found for the event.

The operation to query the concurrency scheduler and find if it has any tasks
to process is as cheap as possible, so it may be queried at regular intervals.

=head3 Event API

An event is a Task PMC object that contains a type, data for the event,
and a priority.

The type of an event is only used to match the event with an event handler,
but is notionally similar to the class of an object.

The data for the event is a PMC and could be any data passed to the event
handler by the code that originates the event.

The priority of an event affects when it is processed by the task list. Higher
priority events are processed before lower priority events. Age is also a
relevant factor, when two events have the same priority, the older one is
processed first. An event handler or the scheduler may also be set to ignore
events below a certain threshold of priority. When the central scheduler
ignores an event because of its priority level, the event remains in the task
list until the priority threshold changes.

An instance of the Task PMC acting as an event uses 4 internal
attributes, which are:

=over 4

=item 1

The type of the task, which is "event"

=item 2

The subtype of this particular event

=item 3

The priority of the event

=item 4

A PMC containing any additional data associated with the event, which may be
used by the event handler.

=back

In addition to the attributes, an event uses a private PMC flag to mark itself
as fatal or non-fatal.

=head4 Event Vtable Entries

=over 4

=item get_string

    STRING * get_string()

Returns a simple string name for the event, suitable for printing in error
messages.

=item get_pmc

  PMC * get_pmc()

Returns the data attribute of the event, or PMCNULL if the event has no data.

=item isa

  INTVAL isa(STRING *)

Returns true or false if the event is of the type passed in the string
parameter, or has a parent of that type.

=back

=head3 Event Handler API

An event handler contains a code object, as well as metainformation about
where it was registered. More specifically, it contains a continuation object,
capturing the full state of the interpreter where it was created. In many
cases, this has no more effect than guaranteeing that the event handler code
executes within the appropriate context of the thread that registered the
handler.

Because events are handled in mainline code, they don't have the restrictions
commonly associated with interrupt-level code. It's safe and acceptable for an
event handler to throw an exception, allocate memory, or manipulate thread or
global state safely. Event handlers can even acquire locks if they need to,
though it's not a good idea to have an event handler blocking on lock
acquisition.

An instance of the EventHandler PMC has 3 internal attributes, which are:

=over 4

=item 1

The type of event it handles

=item 2

A minimum threshhold of priority, below which it will ignore the event even if
it is the right type. The default threshhold is 0, accepting all event
priorities.

=item 3

The core code object of the event handler.

=item 4

A pointer to the interpreter that registered the event handler. The default is
the interpreter in which the event handler was created.

=back

The interpreter pointer may be a proxy that simply provides the interface of
an interpreter object, and knows how to communicate with its remote
interpreter object (in threaded or clustering concurrency).

=head4 Event Handler Vtable Entries

=over 4

=item init_pmc

    void init_pmc(PMC *)

Initializes the PMC, either with a single sub object for the core code, or
with a hash object containing arguments for the code object, type, priority
threshold, and interpreter object for the handler.

=item set_string_native

    void set_string_native(STRING *)

Set the type of event this handler responds to. {{NOTE: These basic attribute
setting operations could be set_attr_str and get_attr_str instead.}}

=item get_string

    STRING* get_string()

Retrieve the type of event this handler responds to.

=item set_integer_native

    set_integer_native(INTVAL)

Set the minimum threshhold of priority.

=item set_pmc(PMC *)

Set the interpreter object for this event handler.

=item invoke

    opcode_t * invoke(void *)

Invoke the event handler.

=back

=head3 Opcodes

The following opcodes are used with the event system:

=over 4

=item new

=begin PIR_FRAGMENT

  $P1 = new 'Event'
  $P1 = new 'EventHandler'
  $P1 = new 'EventHandler', $P2

=end PIR_FRAGMENT


Creates a new event or event handler.

=item schedule

=begin PIR_FRAGMENT

  $P0 = new 'Event'
  # set attributes
  schedule $P0

=end PIR_FRAGMENT

Register an event with the concurrency scheduler. If the concurrency scheduler
for this interpreter (thread/clustered instance) is linked to another
"primary" concurrency scheduler, this will pass along the event to the
primary. All details about the event (its type, whether it's fatal or
non-fatal, whether it's a timer, etc) are stored within the event PMC.

{{NOTE: other possibilities 'raise' and 'fire' by KJS}}

=item addhandler

=begin PIR_FRAGMENT

  $P0 = new 'EventHandler'
  # set attributes
  addhandler $P0

=end PIR_FRAGMENT

Register an event handler with the concurrency scheduler. If the concurrency
scheduler for this interpreter (thread/clustered instance) is linked to
another "primary" concurrency scheduler, this will pass along the event
handler to the primary.

=back

=head3 Event Type Hierarchy

Parrot defines a core set of event types. Users may also define their own
event types. An event type of 'allevents' is effectively the parent of all
event types, and will respond to any event in the task list that isn't handled
by other event handlers in a final way.

  allevents

  ioevent
      packetsent
      packetreceived
      fileopened
      fileclosed
      readcomplete

  keyevent
      keydown
      keypress
      keyup

  mouseevent
      click
      doubleclick
      mousedown
      mouseup
      mousemove
          mouseenter
          mouseleave

  windowevent
      blur
      focus
      scroll
      textselect
      resize
          minimize
          maximize
          restore

  signal
      alarm
      interrupt
      childdie

=head3 Signals

Signals are a special form of event. Parrot presents them as mildly special,
as a remnant of Perl's Unix heritage, but under the hood they're not treated
any differently from events generated by other sources.

Signals can be divided into two categories, those handled by the operating
system, and those passed on to the process.

OS-handled signals are things like SIGKILL, which kills a process, or SIGSEGV,
which indicates that the process has tried to access memory that isn't part of
your process. Because these signals are handled before they reach the process,
there's no way for Parrot to catch them.

Signals Parrot can handle include things like SIGCHLD, indicating that a child
process has died, or SIGINT, indicating that the user has hit C<^C> on the
keyboard, or SIGALRM, the timer expiration signal. Parrot turns these signals
into events and puts them in the event task list, and will be processed and
dispatched to an event handler in order of priority and age just like other
events.

=head3 Timers

A timer is a special kind of event with a time delay. A timer can act as any
builtin or custom event type. Timers can be be flagged as repeating. Parrot
provides builtin timers for greater portability. Some platforms provide their
own implementations of timers, which may be used when performance on a
particular platform is more important than portability.

=head2 References

F<src/events.c>

L<http://www.seas.upenn.edu/~lipeng/homepage/unify.html>

L<http://developer.apple.com/documentation/Carbon/Conceptual/Carbon_Event_Manager/Concept/chapter_2_section_2.html>

L<http://www.quirksmode.org/js/events_compinfo.html>

=cut

__END__
Local Variables:
  fill-column:78
End:
