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();
Or the class can be used with methods:
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();
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 The SIG hash and signals
and Creating Subclasses).
Including the server in your program
Client order for simultaneous events
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:
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();
And defined as methods:
$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();
use EventServer;
to import the functions, or
require EventServer;
if used as a class.
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:
&FUNCREF(OBJECT,REGISTRY_KEY,some method specific args,ARG);
where:
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);
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.
&FUNCREF(OBJECT,REGISTRY_KEY,INTERVAL,ARG);
&FUNCREF(OBJECT,REGISTRY_KEY,TIMEOUT,ARG);
&RFUNCREF(OBJECT,REGISTRY_KEY,HANDLE,ARG);
if output is possible on HANDLE, this triggers the call
&WFUNCREF(OBJECT,REGISTRY_KEY,HANDLE,ARG);
and if both input and output won't block, then this triggers the call
&RWFUNCREF(OBJECT,REGISTRY_KEY,HANDLE,ARG);
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).
The client is triggered after the signal is trapped (and after the signal handler has exited). Triggering effects the function call
&FUNCREF(OBJECT,REGISTRY_KEY,SIGNAL,NSIGS,ARG);
where
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.)
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 Starting the server).
All signal handlers are reset to default if the server loop exits (see Questions and Answers).
See also The SIG hash and signals.
&FUNCREF(OBJECT,REGISTRY_KEY,DATA,ARG);
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().
add_event(EVENT)) then this will trigger the
call
&FUNCREF(OBJECT,REGISTRY_KEY,EVENT,ARG);
for this client. This allows clients for user defined events
register_event_client() function) are triggered.
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.
This can be achieved using the following function:
$r1 = register_...;
$r2 = register_...;
push(@{ordered_keys_ref()},$r2,$r1);
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):
$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);
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:
&FUNCREF(OBJECT,REGISTRY_KEY,method specific args,ARG);
where the 'method specific args' are determined by the type of registration used (as specified in the section Registering clients (methods)), and the other terms are as previously defined.
alarm() should not be used (see IMPORTANT). Instead, a function/method has been provided which allows for nested
timeouts.
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.
sub wrapper { (scalar_call(@_)) }
and FUNCREF = \&wrapper), with arguments ARGS. i.e the call is
@ret = &FUNCREF(ARGS);
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.
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.
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.
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
EventServer::sunos->timeClass()
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
['EventServer::Gettimeofday',116];
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.
%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 Creating Subclasses).
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 Possible problems.
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 Timeouts within client code, IMPORTANT and the entries for methods register_signal_client() and
register_child_termination_client().
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
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__
In making a subclass of the server, the following points are of note:
1. The server class is specified in the variable
$EventServer::SERVER_CLASS.
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
package MyServer;
BEGIN {$EventServer::SERVER_CLASS ||= MyServer;}
@ISA = qw(EventServer);
require EventServer;
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:
EINTR EBADF EINVAL EFAULT WNOHANG
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:
sub _setConstants {
my($self) = @_;
$self->_setEINTR(0x4);
$self->_setEBADF(0x9);
$self->_setEINVAL(0x16);
$self->_setEFAULT(0xe);
$self->_setWNOHANG(0x1);
}
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:
EventServer::<osname>->timeClass()
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:
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
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
start_server();
or
EventServer->startServer();
or
MyServer->startServer();
since startServer() actually starts the server using the class
specified in $EventServer::SERVER_CLASS
############################################################
# 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);
}
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__
>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...
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:
@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__
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
Error: No clients are registered with the server ...
to STDERR and then exit.
To change the default behaviour, create a subclass which redefines noClients, and use that subclass. For example
package MyServer;
BEGIN {$EventServer::SERVER_CLASS ||= MyServer;}
@ISA = qw(EventServer);
require EventServer;
sub noClients {0} # Just terminate the loop if no clients left.
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.
$EventServer::SERVER_CLASS ||= MyServer;
@MyServer::ISA = qw(EventServer);
require EventServer;
sub MyServer::noClients {0}
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.
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.
%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.
%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.