Or the class can be used with methods:
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:
And defined as methods:
to import the functions, or
if used as a class.
1. $SERVER is assumed to be Server::Server::EventDriven 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).
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:
where:
This call to FUNCREF takes place within a timeout. The current maximum timeout value can be retreived using maximum_inactive_server_time() , and can be set using set_maximum_inactive_server_time() . (These access and set the global $Server::Server::EventDriven::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.
if output is possible on HANDLE, this triggers the call
and if both input and output won't block, then this triggers the call
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
where
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 .
Note that if forking the server, you should use fork_with_child_retaining_clients() rather than just a fork().
for this client. This allows clients for user defined events
This can be achieved using the following function:
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 Server::Server::EventDriven::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):
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:
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.
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.
and FUNCREF = \&wrapper), with arguments ARGS. i.e the call is
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.
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.
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 'Server::Server::EventDriven::'.
For example, if this module is run on SunOS, Config says that the OS name ('osname' parameter) is 'sunos', in which case the call
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
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.
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 EventDriven 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 Server/Server/EventDriven.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
In making a subclass of the server, the following points are of note:
1. The server class is specified in the variable
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 Server::Server::EventDriven. This is best done as
Note that the @ISA call _MUST_ be before the 'require' since the require contains initialization calls that need to do method lookups on $Server::Server::EventDriven::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 Server::Server::EventDriven class.
Initialization occurs when Server::Server::EventDriven is require'd.
3. The initialization sets several system constants:
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:
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:
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 'Server::Server::EventDriven::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 'Server::Server::EventDriven::Time' using this method, or a completely different class.
If you roll your own time class, it must have the following methods implemented appropriately:
The method timeClass() gives the class being used to handle times. Available are Server::Server::EventDriven::Time using the time() function in Perl (resolution 1 second) and Server::Server::EventDriven::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 $Server::Server::EventDriven::Signal:<SIGNAME>, e.g. the 'TERM' signal handler should increment the global $Server::Server::EventDriven::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
or
or
since startServer() actually starts the server using the class specified in $Server::Server::EventDriven::SERVER_CLASS
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:
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
to STDERR and then exit.
To change the default behaviour, create a subclass which redefines noClients, and use that subclass. For 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.
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.
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 EventDriven. 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.
-w
clean (though the filehandles
need 'no strict' in 3 places). Documentation altered.