@node EventServer, Exporter, Env, Module List
@unnumbered the all singing, all dancing server. Handles i/o driven clients, timer driven clients, and interrupt driven clients, all at the same time. Allows user defined events also. If there are any more types of clients, please tell me.


@unnumberedsec NAME

EventServer - the all singing, all dancing server.
Handles i/o driven clients, timer driven clients, and interrupt
driven clients, all at the same time. Allows user defined events
also. If there are any more types of clients, please tell me.

@unnumberedsec SYNOPSIS

Functions can be imported and used:

@example
use EventServer;

$r1 = register_timed_client($obj1,$time1,$coderef1);
$r2 = register_interval_client($obj2,$time2,$coderef2);
$r3 = register_signal_client($obj3,$signal3,$coderef3);
$r4 = register_io_client($obj4,$mode4,$coderef4_r,
		$coderef4_w,$coderef4_rw);
$r5 = register_child_termination_client($obj5,$pid5,$coderef5);
$r6 = register_event_client($obj6,$eventName6,$coderef6);

trigger_on_deregistering($r1,$coderef7);
cancel_registration($r3);
$ordered_keys_ref = ordered_keys_ref();

$time = maximum_inactive_server_time();
set_maximum_inactive_server_time($time);
execute_in_array_context_with_timeout($timeout,$timeout_retcode,
		$error_retcode,$coderef,@@args);

fork_with_child_retaining_clients($r1,$r2,...);

sub something @{add_event($eventName)@}

start_server();
@end example

Or the class can be used with methods:

@example
require EventServer;
$S = EventServer;

$r1 = $S->registerTimedClient($obj1,$time1,$coderef1);
$r2 = $S->registerIntervalClient($obj2,$time2,$coderef2);
$r3 = $S->registerSignalClient($obj3,$signal3,$coderef3);
$r4 = $S->registerIOClient($obj4,$mode4,$coderef4_r,
		$coderef4_w,$coderef4_rw);
$r5 = $S->registerChildTerminationClient($obj5,$pid5,$coderef5);
$r6 = $S->registerEventClient($obj6,$eventName6,$coderef6);

$S->triggerOnDeregistering($r1,$coderef7);
$S->cancelRegistration($r3);
$ordered_keys_ref = $S->orderedKeysRef();

$time = $S->maximumInactiveServerTime();
$S->setMaximumInactiveServerTime($time);
$S->executeInArrayContextWithTimeout($timeout,$timeout_retcode,
		$error_retcode,$coderef,@@args);

$S->forkWithChildRetainingClients($r1,$r2,...);

sub something @{$S->addEvent($eventName)@}

$S->startServer();
@end example

@unnumberedsec IMPORTANT

The @'ALRM@' signal is used extensively, as is alarm().
You should NOT call the function alarm() as this will
corrupt the internal logic of the server. Similarly sleep()
should not be used either, as this is often implemented
in terms of alarm().

Instead use execute_in_array_context_with_timeout() which is better
anyway since it allows multiple clients to set alarms simultaneously
and allows nested alarms. However, for this reason, registering
a client to receive @'ALRM@' signals is probably of no use.

Also, if you assign to the %SIG hash, or install signal
handlers through POSIX yourself, then you may corrupt
the logic of the server. If you need to do this for something
other than a signal (e.g. __WARN__), that should be okay,
otherwise you should probably create a subclass to install
the handlers you want (see @samp{"The SIG hash and signals"} in this node
and @samp{"Creating Subclasses"} in this node).

@unnumberedsec CONTENTS

@samp{"NAME"} in this node

@samp{"SYNOPSIS"} in this node

@samp{"IMPORTANT"} in this node

@samp{"CONTENTS"} in this node

@samp{"Function and Method Summary"} in this node

@samp{"Including the server in your program"} in this node

@samp{"Starting the server"} in this node

@samp{"Registering clients (general)"} in this node

@samp{"Registering clients (methods)"} in this node

@samp{"Client order for simultaneous events"} in this node

@samp{"Deregistering clients"} in this node

@samp{"Timeouts within client code"} in this node

@samp{"Forking child processes"} in this node

@samp{"Times and Timing"} in this node

@samp{"The SIG hash and signals"} in this node

@samp{"Example"} in this node

@samp{"Creating Subclasses"} in this node

@samp{"Example subclasses"} in this node

@samp{"Possible problems"} in this node

@samp{"Questions and Answers"} in this node

@samp{"AUTHOR"} in this node

@samp{"COPYRIGHT"} in this node

@samp{"MODIFICATION HISTORY"} in this node

@unnumberedsec Function and Method Summary

There are 15 public functions/methods:

8 dealing with registering clients;

1 to add user defined events

3 dealing with executing code and timeouts;

1 to fork the process;

and 1 to start the server.

Functions are:

@example
register_interval_client(O/R,INTERVAL,FUNCREF,ARG)
register_timed_client(O/R,TIMEOUT,FUNCREF,ARG)
register_io_client(O/R,MODE,HANDLE,RFUNCREF,WFUNCREF,RWFUNCREF,ARG)
register_signal_client(O/R,SIGNAL,FUNCREF,ARG)
register_child_termination_client(O/R,PID,FUNCREF,ARG)
register_event_client(O/R,EVENT,FUNCREF,ARG)

trigger_on_deregistering(REGISTRY_KEY,FUNCREF)
cancel_registration(REGISTRY_KEY)

add_event(EVENT)

maximum_inactive_server_time()
set_maximum_inactive_server_time(TIME)
execute_in_array_context_with_timeout(TIMEOUT,TRET,ERET,FUNCREF,ARGS)

fork_with_child_retaining_clients(LIST_OF_REGISTRY_KEYS)

start_server();
@end example

And defined as methods:

@example
$SERVER->registerIntervalClient(O/R,INTERVAL,FUNCREF,ARG)
$SERVER->registerTimedClient(O/R,TIMEOUT,FUNCREF,ARG)
$SERVER->registerIOClient(O/R,MODE,HANDLE,RFUNCREF,WFUNCREF,RWFUNCREF,ARG)
$SERVER->registerSignalClient(O/R,SIGNAL,FUNCREF,ARG)
$SERVER->registerChildTerminationClient(O/R,PID,FUNCREF,ARG)
$SERVER->registerEventClient(O/R,EVENT,FUNCREF,ARG)

$SERVER->triggerOnDeregistering(REGISTRY_KEY,FUNCREF);
$SERVER->cancelRegistration(REGISTRY_KEY);

$SERVER->addEvent(EVENT)

$SERVER->maximumInactiveServerTime()
$SERVER->setMaximumInactiveServerTime(TIME)
$SERVER->executeInArrayContextWithTimeout(TIMEOUT,TRET,ERET,FUNCREF,ARGS)

$SERVER->forkWithChildRetainingClients(LIST_OF_REGISTRY_KEYS)

$SERVER->startServer();
@end example

@unnumberedsec Including the server in your program

The server is included in your program with the line

@example
use EventServer;
@end example

to import the functions, or

@example
require EventServer;
@end example

if used as a class.

@unnumberedsec Starting the server

The server is started by executing the function or method

@table @asis
@item start_server();
@itemx EventServer->startServer();
In either case, if a subclass has been defined correctly,
then the server will be started using that subclass.

@end table
@unnumberedsec Registering clients (general)

Clients are registered with the server using any of the 6 registering
methods listed in the next section. They all have various points in common:

1. $SERVER is assumed to be EventServer or a subclass;

2. All registration methods return a RegistryKey object on success
which holds the registration key, and false on failure. (Note
previous versions returned a string - the current version should
be fully compatible with previous versions). The registration key
is unique to the registration, depending on all the parameters passed
to the registration method - i.e a single object can be registered
multiple times using different parameters or registration
methods (multiple *identical* registrations will return the same key,
and will result in only one registration). To alter the parameters
of an existing registration, pass the registration key to the
registration method instead of the object (see @'O/R@' below).
But note that this generates a new RegistryKey object since
the registration parameters are now different (the old RegistryKey
object is deregistered, and is essentially useless). Reregistering
an existing registration so that it is identical to another
registration will just derigister the first registration,
returning the existing identical RegistryKey object (i.e.
as stated above, there will only be one registry entry
for identical parameters regardless of how you register them).

3. @'O/R@' is the object being registered or the registration key
of an already registered object. The object can be anything
(previous versions restricted it to be class names or objects
that returned true ref() values). This object is passed to
FUNCREF (see below) as the first argument.

4. @'ARG@' is anything. It is passed to FUNCREF (see below) as the last argument.
If nothing is passed, then ARG is defaulted to undef();

5. At least one @'FUNCREF@' argument is required. All FUNCREF arguments are
CODE references to the function which is executed when the client
is triggered. Where there is more than one FUNCREF to be specified,
the one called will depend on the trigger type. When triggered,
the FUNCREF is called as:

@example
&FUNCREF(OBJECT,REGISTRY_KEY,some method specific args,ARG);
@end example

where:

@example
OBJECT is the object registered (the @'O@' in @'O/R@' above);
REGISTRY_KEY is the registration key for that registration
   (the @'R@' in @'O/R@' above, returned by registration methods);
ARG is the last argument passed to the registration method
   (@'ARG@' above);
@end example

This call to FUNCREF takes place within a timeout. The current
maximum timeout value can be retrieved using
maximum_inactive_server_time(), and can be set using
set_maximum_inactive_server_time(). (These access and set the
global $EventServer::MAX_INACTIVE_SERVER_TIME.)
The default value is 60 seconds. Any fatal errors caused by
executing FUNCREF are trapped, and cause the client to be deregistered.
A timeout will also cause the client to be deregistered.

NOTE however that a call to exit() cannot be trapped and will
cause the server process to exit. Similarly, a call to dump() also
cannot be trapped and will cause the server process to core dump.

@unnumberedsec Registering clients (methods)

@table @asis
@item register_interval_client (O/R,INTERVAL,FUNCREF,ARG)
@itemx $SERVER->registerIntervalClient(O/R,INTERVAL,FUNCREF,ARG)
INTERVAL is a time (see @samp{"Times and Timing"} in this node). The client is triggered
after every INTERVAL seconds. Triggering effects the function call

@example
&FUNCREF(OBJECT,REGISTRY_KEY,INTERVAL,ARG);
@end example

@item register_timed_client (O/R,TIMEOUT,FUNCREF,ARG)
@itemx $SERVER->registerTimedClient(O/R,TIMEOUT,FUNCREF,ARG)
TIMEOUT is a time (see @samp{"Times and Timing"} in this node). The client is triggered
after TIMEOUT seconds and then deregistered. Triggering effects the
function call

@example
&FUNCREF(OBJECT,REGISTRY_KEY,TIMEOUT,ARG);
@end example

@item register_io_client(O/R,MODE,HANDLE,RFUNCREF,WFUNCREF,RWFUNCREF,ARG)
@itemx $SERVER->registerIOClient(O/R,MODE,HANDLE,RFUNCREF,WFUNCREF,RWFUNCREF,ARG)
MODE is @'r@', @'w@' or @'rw@' depending on whether the trigger
should be for input pending (read won@'t block), output
possible (write won@'t block) or both. 
HANDLE is the fully qualified package name of the filehandle
which has already been opened, on which i/o is tested.
RFUNCREF, WFUNCREF and RWFUNCREF are three @'FUNCREF@'s (see above).
If input is pending on HANDLE, this triggers the call

@example
&RFUNCREF(OBJECT,REGISTRY_KEY,HANDLE,ARG);
@end example

if output is possible on HANDLE, this triggers the call

@example
&WFUNCREF(OBJECT,REGISTRY_KEY,HANDLE,ARG);
@end example

and if both input and output won@'t block, then this triggers the call

@example
&RWFUNCREF(OBJECT,REGISTRY_KEY,HANDLE,ARG);
@end example

If MODE @'r@' has been specified, then obviously only RFUNCREF
can ever get called, and similarly if MODE @'w@' has been specified,
then only WFUNCREF can ever get called. However, if MODE @'rw@'
has been specified, then any of the three functions could be
called depending on what becomes non-blocking first.

In all cases of MODE, all three FUNCREF@'s must be CODE references.

Note, unlike previous versions, now if you make multiple registrations
for a specific filehandle, then client functions are still only
triggered when they are guaranteed to be non-blocking. To paraphrase,
if any FUNCREF is called, you are guaranteed to be able
to do a sysread(), syswrite() or accept() (whichever is appropriate).

@item register_signal_client (O/R,SIGNAL,FUNCREF,ARG)
@itemx $SERVER->registerSignalClient(O/R,SIGNAL,FUNCREF,ARG)
SIGNAL is a valid trappable signal. The signals are obtained
from the Config module. (Previous versions specified them
explicitly in subroutines). The @'allSignals@' method
retuns the list of signals.

The client is triggered after the signal is trapped (and after
the signal handler has exited). Triggering effects the function
call

@example
&FUNCREF(OBJECT,REGISTRY_KEY,SIGNAL,NSIGS,ARG);
@end example

where

@example
NSIGS is the number of times the signal was
    received since this function was last called.
and SIGNAL is the canonical name for the signal
(which may be different from what was passed in the
case of @'CHLD@'/@'CLD@'. You can always use either - the
correct signal name for the system will be used.)
@end example

Note that @'ALRM@' and @'CLD@' (or @'CHLD@' or @'CHILD@') are specially
handled, and registering for these signals is of little use.
For alarms, use execute_in_array_context_with_timeout(),
and to find out when a child process has died, register
with register_child_termination_client().

Signals which have no clients registered for them will
cause the default action to occur (i.e. they will not 
be trapped).

Signals are not passed to the clients immediately, they
are put into the queue and clients are triggered when
the signal queue is checked. If you need some action to occur
IMMEDIATELY on receipt of the signal, you will need to
create a subclass to handle this. (This is because setting up
an @'immediately signalled@' type of client is fraught with
difficulties, and is likely to lead to an unstable
process - I tried it. And that was even without having signal
handlers stacked through recursive calls to it. Mind you,
it should be doable with POSIX signals, and is almost,
but some bug that I haven@'t tracked down yet seems to
propagate a die past an eval if called from within the
handler, so its not yet implemented for POSIX signals
in the server.)

Signal handlers are NOT installed until the server has
been started (see @samp{"Starting the server"} in this node).

All signal handlers are reset to default if the server
loop exits (see @samp{"Questions and Answers"} in this node).

See also @samp{"The SIG hash and signals"} in this node.

@item register_child_termination_client (O/R,PID,FUNCREF,ARG)
@itemx $SERVER->registerChildTerminationClient(O/R,PID,FUNCREF,ARG)
PID is the process id of the child process. When that
child dies this triggers the function call

@example
&FUNCREF(OBJECT,REGISTRY_KEY,DATA,ARG);
@end example

Where data is either: the process id of the terminated
child; or an array reference with two items in the
array - the process id and the child termination status
as given by @'$?@' . The choice of which is returned is set
by calling always_return_child_termination_status() with
a boolean argument - true means return the array reference,
false means return the pid only. The default is false
for backward compatibility.

Note that if forking the server, you should use
fork_with_child_retaining_clients() rather than
just a fork().

@item register_event_client (O/R,EVENT,FUNCREF,ARG)
@itemx $SERVER->registerEventClient(O/R,EVENT,FUNCREF,ARG)
EVENT is any string. If any client adds the event EVENT into
the server@'s event loop (using add_event(EVENT)) then this
will trigger the call

@example
&FUNCREF(OBJECT,REGISTRY_KEY,EVENT,ARG);
@end example

for this client. This allows clients for user defined events

@item add_event (EVENT)
@itemx $SERVER->addEvent(EVENT)
Simply adds the string EVENT to the end of the event queue.
Any clients waiting for this event (registered using the
register_event_client() function) are triggered.

@item always_return_child_termination_status(BOOLEAN)
@itemx $SERVER->alwaysReturnChildTerminationStatus(BOOLEAN)
Sets whether the register_child_termination_client() call
will trigger a callback with just the child@'s pid as the third
argument (BOOLEAN true), or a reference to an array holding the
pid and the termination status (BOOLEAN false). Note that this
affects the call dynamically - the trigger checks as its
triggering to see what type of argument it should pass.

The default is false for backward compatibility.

@end table
@unnumberedsec Client order for simultaneous events

If two events occur simultaneously, or an event occurs
for which more than one client is registered, then more
than one client will be triggered in the same server loop.
You may want to ensure that for any pair of clients, a specific
client is always called before another in this situation.

This can be achieved using the following function:

@table @asis
@item ordered_keys_ref()
@itemx $SERVER->orderedKeysRef()
This method/function returns a reference to an ARRAY type object.
This object holds RegistryKey objects in whatever order you
want to specify. In cases where more than one client is
to be triggered within a single server loop, the order
of the keys within this array determines the ordering of
client activation. For example, this

@example
$r1 = register_...;
$r2 = register_...;
push(@@@{ordered_keys_ref()@},$r2,$r1);
@end example

will ensure that in such a case, the client registered on key @'$r2@'
will always be called before the client registered on key @'$r1@'.

The object returned by ordered_keys_ref() is actually an object
of class EventServer::OrderedKeys, and there
are several methods in this class which may make it easier
for you to manipulate the array (though just treating it as
an array reference is absolutely fine):

@example
$order = ordered_keys_ref();
$order->push_keys(LIST_OF_KEYS);
$order->pop_key();
$order->shift_key();
$order->unshift_keys(LIST_OF_KEYS);
$order->insert_keys_before(INDEX,LIST_OF_KEYS);
$order->delete_key_at(INDEX);
@end example

@end table
@unnumberedsec Deregistering clients

There are two methods for deregistering clients. One is to use the
fact that FUNCREF calls have fatal die() errors trapped - which means
that a client can die() when it is triggered, and this will cause that
client to be deregistered. (Timing out will have the same effect,
but is a silly way to do it since all other clients may be blocked
until the timeout is finished).

NOTE that generating an @'ALRM@' signal (e.g. with "kill @'ALRM,$$")
will produce a die() since the alarm handler dies. This means
that if you produce an ALRM signal, you are effectively timing
out the client, and hence deregistering it.

The second method is to use the function/method provided:

@table @asis
@item cancel_registration (REGISTRY_KEY);
@itemx $SERVER->cancelRegistration(REGISTRY_KEY);
This deregisters the client that was registered on the key
REGISTRY_KEY.

@end table
The server will deregister a client if there are any problems
with it. You can find out when a client is deregistered by
setting a function to be triggered when the client is deregistered
using the function/method:

@table @asis
@item trigger_on_deregistering (REGISTRY_KEY,FUNCREF);
@itemx $SERVER->triggerOnDeregistering(REGISTRY_KEY,FUNCREF);
This returns true (REGISTRY_KEY) on success, false (undef)
on failure. On success, the code reference FUNCREF has
been added to the clients registration such that when
the client is deregistered, this triggers the call:

@example
&FUNCREF(OBJECT,REGISTRY_KEY,method specific args,ARG);
@end example

where the @'method specific args@' are determined by the type
of registration used (as specified in the
section @samp{"Registering clients (methods)"} in this node), and the other terms
are as previously defined.

@end table
@unnumberedsec Timeouts within client code

Note alarm() should not be used (see @samp{"IMPORTANT"} in this node). Instead,
a function/method has been provided which allows for nested timeouts.

@table @asis
@item execute_in_array_context_with_timeout (TIMEOUT,TRET,ERET,FUNCREF,ARGS)
@itemx $SERVER->executeInArrayContextWithTimeout(TIMEOUT,TRET,ERET,FUNCREF,ARGS)
TIMEOUT is a time (see @samp{"Times and Timing"} in this node). This sets the timeout
for the call (note that times are rounded up to the next
integer number of seconds);

TRET is the value/object returned as the first element of the
return array if the call is timed out;

ERET is the value/object returned as the first element of the
return array if the call produces a fatal error;

FUNCREF is the CODE reference which is called;

ARGS are the arguments which are passed to FUNCREF when it is called.

This method calls FUNCREF in an array context (if you want to
make a call in a scalar context, wrap the function and pass
the wrapped function reference, e.g.

@example
sub wrapper @{ (scalar_call(@@_)) @}
@end example

and FUNCREF = \&wrapper), with arguments ARGS. i.e the call is

@example
@@ret = &FUNCREF(ARGS);
@end example

If the call is not timed out, and does not produce an error,
then the array returned by the FUNCREF call (@@ret) is returned.
If a timeout occured, then the array (TRET) is returned, and
if an error occurred during the FUNCREF call, then the
array (ERET, $@@) is returned.

This method allows timeouts to be nested - i.e. you can call
this method within another function which is being timed
out by this method.

@item maximum_inactive_server_time()
@itemx $SERVER->maximumInactiveServerTime()
Returns the current value that this is set to. This determines
the maximum time before triggered clients are timed out. Default
is 60 (seconds).

@item set_maximum_inactive_server_time (TIME)
@itemx $SERVER->setMaximumInactiveServerTime(TIME)
Sets this value. It should be a positive value.

@end table
@unnumberedsec Forking child processes

The call fork() works fine, but the resulting child is a copy of the
server with all the clients retained. If the fork is to be followed
by an exec, this is fine. But otherwise, you need to know which
clients are still registered, and which ones you don@'t want.

Instead of worrying about this, I provide a function/method to fork the
server retaining ONLY those clients you know you want. All other clients
are deregistered in the child.

@table @asis
@item fork_with_child_retaining_clients (LIST_OF_REGISTRY_KEYS)
@itemx $SERVER->forkWithChildRetainingClients(LIST_OF_REGISTRY_KEYS)
This function/method works and returns as fork(): On failure,
undef is returned, on success the process is forked
and the child gets 0 returned while the parent gets the
process id of the child returned.

In addition, only those clients with registry keys specified
as arguments when this method is called, have their registration
retained in the child. (Note that if you are handling signals
in addition to whatever else, you may want to retain those
signal handling clients in the child).

This saves you from needing to think about which clients
need to be deregistered in the child - you only need to consider
which ones need to be kept.

@end table
@unnumberedsec Times and Timing

Note that all times should be specified in seconds, and can
be fractional (e.g. 2.35). However the fractional part may
be of no use depending on where it is used.

Currently, timing-out code using execute_in_array_context_with_timeout()
has values rounded up to the next highest integer , e.g. @'2.35@'
will be used as @'3@', and @'2@' will be used as @'3@' (this
latter use is because alarm() can be up to one second less).
This is because alarm() is being used to time out code
in this function, and alarm() only has a 1 second resolution.

Timing in the Interval and Timer client registration is
dependent on the resolution available from a clock timer
used from Perl. If the default time() is used, then
fractional seconds are effectively rounded up to the
next integer, since the times can only be ticked down
in seconds. Resolutions will specify how many digits
after the decimal point are used. The maximum resolution
is one microsecond (six digits after the decimal point).
Non-significant digits may be rounded up or down.

The server specifies the timing method during initialization.
Currently, if syscall() and the gettimeofday() system call
are available, these are used, otherwise time() is used.

However, the availability of the gettimeofday() call
is established with a call to the method timeClass()
in the OS specific class given by the OS name as obtained
from Config, appended to @'EventServer::@'.

For example, if this module is run on SunOS, Config
says that the OS name (@'osname@' parameter) is @'sunos@',
in which case the call

@example
EventServer::sunos->timeClass()
@end example

is made. If this call produces a die(), that is trapped,
and the default time class (using time()) is used.
If this does not die, it is assumed to return a
reference to an array, with first element being
the time class to use, and the second any initialization.

For example, in the case of SunOS, this returns

@example
[@'EventServer::Gettimeofday@',116];
@end example

which specifies to use the Gettimeofday class, and
initializes this class with the syscall number required
to make the call to gettimeofday().

Please tell me what is best on any specific platform,
I@'ll try to include support for it. Currently automatically
supported are SunOS 4.*, IRIX 5.*, and Linux. You can
add specific OS support just be adding the package
and timeClass() method as shown.

Remember, you can always let it default to the plain Time
class - this is usually sufficient.

@unnumberedsec The SIG hash and signals

If you assign to the %SIG hash, or install signal
handlers through POSIX yourself, then you may corrupt
the logic of the server. If you need to do this for anything
other than a signal (e.g. __WARN__), that should be okay,
otherwise you should probably create a subclass to install
the handlers you want (see @samp{"Creating Subclasses"} in this node).

If you want to trap a signal, do it by registering a signal client.
If you want to trap a signal and need to have control during the 
signal handler, then subclass the EventServer class and set
the handler in the subclass. And note that any handler which
dies will deregister any client which sends a signal for
that handler. Its usually a bad idea to do too
much in a signal handler (see @samp{"Possible problems"} in this node. 

However, if you are definitely not going to register any clients
for a particular signal, you can assign your own signal handler
for that signal (though not for ALRM and CHLD).

Terminating children have their pid@'s removed from the process
list before clients receive the @'CLD@' signal. For this reason
you should not wait() for terminating children. If you want to
be notified of this, use the register_child_termination_client()
registration method. For this reason, registering a client
to receive @'CLD@' signals is probably of no use.

Signals which have no clients registered for them will
not be trapped.

See also @samp{"Timeouts within client code"} in this node, @samp{"IMPORTANT"} in this node and
the entries for methods register_signal_client()
and register_child_termination_client().

@unnumberedsec Example

Note that you can execute this example with
@code{perl5 -x EventServer.pm} assuming you are in
the perl lib directory where you installed this module.

The example program below registers all the various types of clients.

o A timer client (expiring after 3 seconds), which is also told
that it is being deregistered when it dies;

o an interval client (sending a SIGCONT every 4.3 seconds for 4 times,
then deregistering) - on the fourth triggering this client calls
a function to test nested timeouts. That should timeout after 3 seconds,
though an interrupt could terminate it quicker;

o a signal client which also tests re-registering (triggered on receiving
the first @'CONT@' from the interval client, at which point it reregisters,
changing the function that is called to @'cont_test2@' which makes it catch
the second SIGCONT from the interval client, and then deregister);

o an event client, which waits for the event @'CHECK@' - that
event is sent on the third triggering of the interval client.
The Event client calls a nested timeout which tests the
functionality of nested timeouts. That should timeout after 3 seconds,
though an interrupt could terminate it quicker;

o an i/o client, which waits for some input on STDIN (requires a <RETURN>
to be triggered) and then deregisters;

o a child termination client (the process forks right at the beginning,
and the child sleeps for 10 seconds then terminates);

o and finally another signal client which will take two SIGINT@'s (usually
generated by typing cntrl-C) then deregisters, which means that the next
SIGINT will cause the default signal action to occur (program termination).

Note that the server will terminate when all clients are deregistered
so if you want to see everything you need to run this at least
twice - once you can terminate by giving three cntrl-C@'s BEFORE all the
other clients have deregistered (you can keep the io client registered
by not typing <RETURN>), and the second time you can let the program
terminate by letting all the clients deregister (two cntrl-C@'s and
a <RETURN> get rid of the SIGINT client and the io client - all other
clients get deregistered within the first 20 seconds).

#!perl5

@example
BEGIN @{print "Initializing, process id is $$\n";@}
use EventServer;

# Timer test client (after 3 seconds)
$r = register_timed_client([],3,sub @{print STDERR "Timed test\n"@})
	|| die "Timed test not registered";

# Deregistering Trigger test
trigger_on_deregistering($r,
  sub @{print STDERR "Deregistering Trigger test\n"@}) ||
	 die "Deregistering Trigger test not registered";

# Interval test client (every 4.3 seconds, 4 times)
register_interval_client([],4.3,\&interval_test)
	|| die "Interval test not registered";

sub interval_test @{
    $C++;print STDERR "Interval test $C\n";
    kill @'CONT@',$$;
    if ($C == 3) @{
	 add_event(@'CHECK@');
    @} elsif ($C > 3) @{
	 $t=time;
	 execute_in_array_context_with_timeout(2.5,0,0,\&t4_test);
	 print STDERR @'Nested timeout returned after @',time-$t," secs\n";
	 die;
    @}
@}

sub t3_test @{
    execute_in_array_context_with_timeout(2.5,0,0,
					   sub @{select(undef,undef,undef,9)@});
@}

sub t4_test @{
    execute_in_array_context_with_timeout(6.5,0,0,
					   sub @{select(undef,undef,undef,9)@});
@}

sub t1_test @{
    print STDERR "Event client test\n";
    $t=time;
    execute_in_array_context_with_timeout(6.5,0,0,\&t3_test);
    print STDERR @'Nested timeout returned after @',time-$t," secs\n";
    die;
@}

register_event_client([],@'CHECK@',\&t1_test) ||
   die "Event test not registered";

# Signal test client (once after first Interval test)
$r = register_signal_client([],@'CONT@',\&cont_test)
	|| die "Signal test not registered";

# Reregistration test client (once after second Interval test)
sub cont_test @{
  print STDERR "Signal test\n";
  register_signal_client($r,@'CONT@',\&cont_test2)
@}
sub cont_test2 @{print STDERR "Reregistering test\n";die@}

# IO test client (once after user types <RETURN>)
register_io_client([],@'r@',STDIN,\&io,\&io,\&io) || 
	die "STDIN test not registered";
sub io @{$l=<STDIN>;print STDERR "IO test: $l";die@}

# Child Termination test client (after 10 seconds)
defined($pid = fork) || die "Couldn@'t fork";
if($pid==0)@{
  #Keep the child around for 10 seconds
  $SIG@{@'INT@'@} = @'IGNORE@';sleep(10);warn "Child Died\n";exit(23);
@}
print STDERR "Start child process pid = $pid\n";
always_return_child_termination_status(1);
register_child_termination_client([],$pid,
  sub @{print STDERR "Child (pid=$_[2]->[0]) terminated with status ",$_[2]->[1]>>8,"\n"@}) ||
	die "Not registered";

# Signal test client (catches 2 ^C, then uses default SIGINT)
register_signal_client([],@'INT@',
  sub @{$A++;print STDERR "INT caught $A\n";$A>1 && die@})
	|| die "Signal test not registered";

print "Starting server now\n";
start_server();

__END__
@end example

@unnumberedsec Creating Subclasses

The EventServer server is designed with subclassing in mind.
There is only so much generality that can be catered for in
any class, and specific applications will do much better
by subclassing and specializing.

In making a subclass of the server, the following points are of note:

1.  The server class is specified in the variable

@example
$EventServer::SERVER_CLASS.
@end example

To allow your subclass to handle ALL methods (including signal handling,
initialization and exporting of functions) you need to specify
this variable before require@'ing the EventServer.
This is best done as

@example
package MyServer;
BEGIN @{$EventServer::SERVER_CLASS ||= MyServer;@}
@@ISA = qw(EventServer);
require EventServer;
@end example

Note that the @@ISA call _MUST_ be before the @'require@' since
the require contains initialization calls that need to do
method lookups on $EventServer::SERVER_CLASS.

Making the assignment conditional on the variable being false
allows your class to be subclassed as well.

2.  The initialization is a method called init().
Specifying the SERVER_CLASS variable above will
ensure that the init method is called in the subclass
rather than the EventServer class.

Initialization occurs when EventServer
is require@'d.

3.  The initialization sets several system constants:

@example
EINTR EBADF EINVAL EFAULT WNOHANG
@end example

and will produce a fatal error if they cannot be set.

These are set when the method _setConstantsAndTimeClass()
is called from init(), which in turn calls _setConstants().
The constants are set using the
methods _setEINTR(), _setEBADF(), _setEINVAL(), _setEFAULT(),
and _setWNOHANG().

So, for example, to specify the values for SunOS 4, you could
declare the following method in a subclass:

@example
sub _setConstants @{
   my($self) = @@_;
   $self->_setEINTR(0x4);
   $self->_setEBADF(0x9);
   $self->_setEINVAL(0x16);
   $self->_setEFAULT(0xe);
   $self->_setWNOHANG(0x1);
@}
@end example

4.  The initialization sets and initializes the variable time
class to use. It does this by finding the OS name from Config
($Config@{@'osname@'@}) and making the call:

@example
EventServer::<osname>->timeClass()
@end example

where <osname> is the OS name as found from CONFIG.
If this call does not die() (any call to die() is trapped),
then it is assumed to return an array reference to an array
consisting of the time class to use as the first element, and
values to initialize the time class for subsequent elements.

Typically, this would be @'EventServer::Gettimeofday@'
as the first element, and the syscall number for the
gettimeofday call as the second element (e.g. SYS_gettimeofday
from syscall.h on many systems). However, you could
explicitly specify the default @'EventServer::Time@'
using this method, or a completely different class.

If you roll your own time class, it must have the following
methods implemented appropriately:

@example
initialize(?)		# Whatever
now()			# Return an object representing the time now
newFromSeconds(SECONDS)# Return an object representing SECONDS
copy()			# Return new object representing the time in @'self@'
newFromDiff(OTHER)	# Return an object representing the time difference 
			# between @'self@' and OTHER
original()		# Return the time in its original format
isPositive		# Is the time positive? Return boolean
smallerTime(OTHER)	# Return object with smaller time, @'self@' or OTHER
time()			# Return the time as a number (a float if needed)
wholeSecondsRoundedDown()# Return time as an integer, ignoring fractions
@end example

The method timeClass() gives the class being used to handle
times. Available are EventServer::Time using
the time() function in Perl (resolution 1 second)
and EventServer::Gettimeofday which uses
the gettimeofday() C system call using syscall.

5.  The init() sets the list of signals that can
be registered for. The list is obtained from the
Config module, minus the untrappable KILL and STOP
signals.

6.  The setSignalHandlers() method

The setSignalHandlers() method creates the signal handlers if
necessary, and installs those that are to be permanently
installed. All signals have a signal handler assigned.

Unlike previous versions, in order to elminate possible
reentrancy bugs, the signal handlers do not execute
in subclasses. They are functions in their own namespace
which do the absolute minimum possible (mostly just
incrementing a variable).

To reimplement a signal handler, you need to respecify
the signalHandlerFor() method. This method takes as
argument the signal name, and returns the name of the
handler. The handlers should increment the
global $EventServer::Signal::<SIGNAME>,
e.g. the @'TERM@' signal handler should increment
the global $EventServer::Signal::TERM.
(This is all they do by default).

The ALRM handler is implemented slightly differently, and
should not be reimplemented unless you know what you@'re doing.

Handlers are normally only installed when a client registers
for that signal. However, ALRM and CHLD are permanently
registered. You can specify which handlers are permanently
registered by reimplementing the isSpecialSignalHandler()
method. This returns true for those signals which should
have permanently installed handlers. But note that if
you reimplement this, you should include ALRM and
CHLD (or CLD) among the set of signals which return true.

Note that any handler which is set to die on receipt of
a signal will deregister any client which sends a that
signal.

7.  The server can be started using

@example
start_server();
@end example

or

@example
EventServer->startServer();
@end example

or

@example
MyServer->startServer();
@end example

since startServer() actually starts the server using
the class specified in  $EventServer::SERVER_CLASS

@unnumberedsec Example subclasses

The SunOS example is not necessary, and is just here for
illustrative purposes (though can be used).

@example
############################################################
# Subclass for SunOS4. Speeds up initialization and
# ensures the use of gettimeofday(2) system call.
# Also SunOS doesn@'t need to have handlers reinstalled
# when they are called.
# NOTE that you can use EventServer
# on SunOS or any other OS without this subclass.
# 
package EventServer_SunOS4;

BEGIN @{
  if (@`/bin/uname -sr@` =~ /^SunOS\s+4/i) @{
     $EventServer::SERVER_CLASS ||= 
	EventServer_SunOS4;
  @} else @{
     warn "Warning: system is not SunOS4 - using plain EventServer class\n";
  @}
@}

@@ISA = qw(EventServer);
require EventServer;

sub _setConstantsAndTimeClass @{
    my($self) = @@_;
    $self->_setConstants();
    $self->_setTimeClass(EventServer::Gettimeofday,116);
@}
@end example

@example
sub _setConstants @{
    my($self) = @@_;
    $self->_setEINTR(0x4);
    $self->_setEBADF(0x9);
    $self->_setEINVAL(0x16);
    $self->_setEFAULT(0xe);
    $self->_setWNOHANG(0x1);
@}

# No need to reset signal handlers within signal handlers for SunOS
# Though this is redundant, since POSIX handlers will be used anyway.
sub signalHandlerForSpecialSignal @{
    my($self,$signal) = @@_;
    $signal =~ tr/A-Z/a-z/;
    @'EventServer::Signal::posix_@' . $signal;
@}
sub defaultSignalHandlerFor @{
    my($self,$signal) = @@_;

    my $handler = $self->_handlerPrefix() . $signal;
    unless ( defined(&@{$handler@}) ) @{
	 eval sprintf(@'sub %s @{$%s++;die "\n"@} $%s=0;@',
	 	     $handler,$handler,$handler);
    @}
    $handler;
@}

1;
__END__
@end example

@unnumberedsec Possible problems

Posting from Todd Hoff

@example
>From: tmh@@ictv.com (Todd Hoff)
Newsgroups: comp.lang.perl
Subject: Re: Perl 5: alarm BSD vs. SysV
Date: 3 Apr 1995 10:38:35 -0700
Organization: ICTV, Inc.
Lines: 24
Message-ID: <3lpbqr$gbm@@anxious.ictv.com>

In article <3lomapINN334@@calvin.lif.icnet.uk>,
>Have you guys tried re-setting the signal handler within the
>handler. Some systems reset the signal handler to default
>after it is called.
>
>sig handler @{
>   $SIG@{@'ALRM@'@} = @'handler@';
>   ...
>@}

Each UNIX vendor has chosen which version of the "old" signal semantics
to emulate, thus signal work is not very portable and bug prone.
Setting the handler in the handler breaks miserably because an interrupt
can occur before the handler is set. What sucks is that you are
unlikley to see problems unless you have a loaded machine or
high interrupt rate, both of which i usually have :-(

The only solution is for perl to use POSIX signals which are safe 
(but harder to understand). As an aside do not do anything in a signal 
handler but set a flag which tells you if you should call a handler
in the main line logic. Reentrancy bugs are intermitent and nasty.
-- 
Todd Hoff     | My words are my own.
tmh@@ictv.com  | And i have all this extra white space...
@end example

In addition, perl has the problem that signals can interrupt
a malloc - and this seems prone to causing a SIGSEGV.

The problems are decreased in this server because most of the time it
will probably be in the select call, in which case signals are likely
to hit it mostly during a select call, not a malloc. But you should be
prepared for your server to die, and have some automated procedure to
restart it - like a cron job. This is a general problem of signals and
perl (and C), not a specific problem of the server.

If you want the general problem illustrated in a simple way, the
following is nice and clear, and will give a core dump after a
few seconds:

@example
@@a = qw(1, 2, 3, 4);
$sig_happened = 0;

$SIG@{@'ALRM@'@} = @'sig_handler@';
alarm(1);

while (1)
@{
    foreach $z (@@a)
    @{
	reset_handler() if ($sig_happened);
    @}
@}

sub reset_handler
@{
    print "Reset the handler\n";
    $sig_happened = 0;
    $SIG@{@'ALRM@'@} = @'sig_handler@';
    alarm(1);
@}

sub sig_handler
@{
    $sig_happened = 1;
@}
__END__
@end example

@unnumberedsec Questions and Answers

Q1. How do I exit the start_server loop.

A1. When there are no more clients registered with the
server, the method noClients() is called. If this method
returns a false value then the start_server loop terminates.
If this returns a true value, then the loop continues.

The default action is for the server to print the message

@example
Error: No clients are registered with the server ...
@end example

to STDERR and then exit.

To change the default behaviour, create a subclass
which redefines noClients, and use that subclass.
For example

@example
package MyServer;
BEGIN @{$EventServer::SERVER_CLASS ||= MyServer;@}
@@ISA = qw(EventServer);
require EventServer;
sub noClients @{0@} # Just terminate the loop if no clients left.
@end example

Note that you don@'t need this to go into a separate module -
it can be in your main program as an initialization if this
is all you need, e.g.

@example
$EventServer::SERVER_CLASS ||= MyServer;
@@MyServer::ISA = qw(EventServer);
require EventServer;
sub MyServer::noClients @{0@}
@end example

@unnumberedsec AUTHOR

This software was developed by Jack Shirazi in the Biomedical
Informatics Unit at the Imperial Cancer Research Fund, and was partly
funded by the European Union Computer Executive Committee under
EP6708 @`APPLAUSE: Application and Assessment of Parallel Programming
Using Logic@'.

@unnumberedsec COPYRIGHT

Copyright 1995 Imperial Cancer Research Fund, UK. All rights reserved.

This software is distributed under the same terms as Perl.

This program is free software; you can redistribute it and/or modify
it under the terms of either:

a) the GNU General Public License as published by the Free
Software Foundation; either version 1, or (at your option) any
later version, or

b) the "Artistic License" which comes with Perl.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See either
the GNU General Public License or the Artistic License for more details.

@unnumberedsec MODIFICATION HISTORY

@table @asis
@item Future Versions (to do)
Use setitimer if available. Make into SelfLoader class(es)
when SelfLoader comes on stream? Allow clients option
of executing in own process (psuedo-threaded). Global
store across processes. Mobile server. Shadow server.
Automatic termination on dregistration of specific clients.

@item Version 2.3, 5th February 1996
Added option to return child termination status. Fixed missing
assignment of arguments in EventServer::_resetVectors which
prevented closed handles from being deregistered. Made
into EventServer package only, with Server::Server::EventDriven
subclass for backward compatibility. Fixed docs. Fixed
posix_alarm and resetting_alarm to posix_alrm and resetting_alrm.
Altered registering semantics in-line with docs (was not returning
existing registry key for identical registration).

@item Version 2.2, 8th August 1995
Bug fix - isReady had wrong number of args.

@item Version 2.1, 10th July 1995
Altered signal installers to make SEGV, ILL, PIPE and BUS
special handlers which are permanently installed.
Added @'Free software@' copyright.

@item Version 2.0, 14th June 1995
Added Andrew Wilcox patch to fix recursive includes in _tryH.
Invalid arguments to register methods now produce a croak.
Added Linux subclass (courtesy of Andrew Wilcox). Added
Exportable function wrappers to methods. Enabled server
loop to terminate. Podified, cleaned and added to documentation.
Removed any redundant %SIG @'tie@' code. Added dummy buffer
for gettimeofday syscall to workaround perl 5.000 (and 5.001?)
bug - now works with any perl5 version.

Changed constants retrieval to not try so hard - now just
looks at POSIX, Errno and Wait (in perl4 & 5 versions) and
uses those - also uses classes to get gettimeofday syscall
value. Now uses signals listed in Config. Now asks the os
specific class (obtained from Config) for time class and any
time class initialization.

Rewrote sig handlers to mostly do nothing except set a
global. Rewrote and modularized server loop so that it
is easier to alter the behaviour in a subclass. Wrapper
objects now trigger on same @'trigger@' method. Loop
goes through one iteration, then triggers all clients
in a user defined order (or random order for any not
in the user defined order). IO clients are guaranteed
to be triggered only if ready - even if multiple clients
are registered on the same handle.

Added support for POSIX signals - uses them if available.
Fixed leaked alarm time logic. Added nested alarm time tests
to example. Changed all classes to be nested under EventServer.
Changed registry keys to be RegistryKey objects. Added client
defined events. Altered documentation. Put server loop
in eval loop. Added signal unblocking to handle IRIX bug.

@item Version 1.2, 10th April 1995
Altered various internal methods (mainly associated with init)
to allow subclassing to be more straightforward. Provided
example subclasses for SunOS4 and IRIX5.
Altered signal handlers to reset signal handler after being
called to provide support for systems which need it.
Removed tie on %SIG due to flakiness (%SIG no longer read-only).
Moved the methods required by tie into a separate package
space. Altered _tryH and _tryCompiling. Fixed bug in 
executeInArrayContextWithTimeout (wasn@'t handling recursive
timeouts !). Made @'use strict@' and -w clean (though the filehandles
need @'no strict@' in 3 places). Documentation altered.

@item Version 1.1, 5th April 1995
EventServer::Time::copy &
EventServer::Time::newFromDiff bugfixed,
EventServer::_noClients error message changed,
added triggerOnDeregistering method and support methods
and altered documentation appropriately.

@item Version 1.0, 10th March 1995
Base version.

@end table
