feda/lib/sue/sue_base.h
2026-03-19 06:21:19 +05:00

310 lines
12 KiB
C++

#ifndef SUE_BASE_H_SENTRY
#define SUE_BASE_H_SENTRY
/*! \file sue_base.h
\brief The SUE library plain C base
This file implements the main plain C 'classes' of the library:
- sue_event_selector
- sue_fd_handler
- sue_timeout_handler
- sue_signal_handler
- sue_loop_hook
These are likely needed in almost any event-driven application.
*/
//! Use the default malloc/free
/*! This function must be called before anything else is done with the
library, unless you want to use your custom allocation implementation.
See the comment in the sue_aloc.h file for the details.
*/
void sue_alloc_init_default();
//! Event selector
/*! This 'class' provides an object-oriented framework for the unix pselect(2)
system call. It uses objects of:
- sue_fd_handler for file descriptor oriented events,
- sue_timeout_handler class for time events,
- sue_signal_handler class for signal events,
- sue_loop_hook to handle various postponed tasks (called
on every iteration of the main loop)
The 'class' consists of the sue_event_selector structure and the
methods named sue_sel_XXX.
You need to create an object of this class (that is, to create a
variable of the struct sue_event_selector type, and then call the
sue_sel_init() method on it), then decide what events you
wish to handle from the very start, create the appropriate objects
for these events, register them using the appropriate methods, then
call the sue_sel_go() and enjoy. Certainly you can register
more events and unregister some of the registered events at any time you
want, but please note if you define no events before calling the 'go'
method then it will loop forever. Remember, you're creating an
event-driven application!
\note This class is THE good point to start with. You *ALWAYS* need
one (and in almost all cases only one) object of this class.
The 'go' method of this class is the main loop of your application,
and the sue_sel_break() is used to break the main loop.
*/
struct sue_event_selector {
//! Sorted list of timeout handlers
struct sue_sel_timeout_item *timeouts;
//! Signal wanters list
struct sue_sel_signal_item *signalhandlers;
//! File descriptor handlers
/*! Array indexed by descriptor values themselves.
That is, fdhandlers[3] is a pointer to the FdHandler
for fd=3, if any, or NULL. Array is resized as necessary.
*/
struct sue_fd_handler** fdhandlers;
//! Current size of the fdhandlers array
int fdhandlerssize;
//! Max. value of currently used descriptors
int max_fd;
//! List of loop hooks
struct sue_sel_loophook_item *loophooks;
//! Is it time to break the main loop?
/*! This flag is cleared by 'go' method and may be set by
'break' method. The main loop checks the flag after all the
handlers are called, and if it is set, the main loop quits.
*/
int breakflag;
};
//! Constructor
void sue_sel_init(struct sue_event_selector *s);
//! Destructor
void sue_sel_done(struct sue_event_selector *s);
struct sue_fd_handler;
struct sue_timeout_handler;
struct sue_signal_handler;
struct sue_loop_hook;
//! Register file descriptor handler
/*! Registers an FD to be watched.
\note The FD handler remains registered unless it is explicitly
unregistered with remove_fd method.
\warning It is assumed FD doesn't change when the handler is
registered.
\warning If another handler for the same FD is registered,
it is silently replaced.
*/
void
sue_sel_register_fd(struct sue_event_selector *s, struct sue_fd_handler *h);
//! Removes the specified handler
/*! Removes (unregisters) an FD handler. In case the handler is
not registered, silently ignores the call
*/
void sue_sel_remove_fd(struct sue_event_selector *s, struct sue_fd_handler *h);
//! Register a timeout handler
/*! Registers a time moment at which to wake up and call the
notification method. Time value (in seconds and microseconds)
is specified by a sue_fd_handler subclass object which also
provides a callback function for notification.
\note The timeout handler remains registered unless
either the moment comes and notification function is called
OR it is explicitly unregistered with the remove method.
\warning It is assumed that the value of the timeout doesn't
change when it is registered. Changind it will lead to
unpredictable behaviour. If you need to change the timeout,
then first unregister it, then change and register again.
*/
void sue_sel_register_timeout(struct sue_event_selector *s,
struct sue_timeout_handler *h);
//! Removes the specified handler
/*! Removes (unregisters) a timeout handler. In case the handler is
not registered, silently ignores the call
*/
void sue_sel_remove_timeout(struct sue_event_selector *s,
struct sue_timeout_handler *h);
//! Register a signal handler
/*! Registers a signal specified with SUESignalHandler object
*/
void sue_sel_register_signal(struct sue_event_selector *s,
struct sue_signal_handler *h);
//! Remove a signal handler
void sue_sel_remove_signal(struct sue_event_selector *s,
struct sue_signal_handler *h);
//! Register a loop hook
void sue_sel_register_loop_hook(struct sue_event_selector *s,
struct sue_loop_hook *h);
//! Remove a loop hook
void sue_sel_remove_loop_hook(struct sue_event_selector *s,
struct sue_loop_hook *h);
//! Main loop
/*! This function
- sets up the fd_set's for read, write and except notifications
in accordance to the set of registered file handlers
- chooses the closest time event from the set of registered
timeout handlers
- sets the signal handling functions as appropriate (XXX ?!)
- calls pselect(2)
- checks if any handled signals happened
- examines the fd_set's for FDs changed state and calls the
appropriate callback methods
- examines the timeouts list, selects those are now in
the past, removes each of them from the list of registered
objects and calls its notification method
- checks for the loop breaking flag. If it is set, breaks the
loop and returns. Otherwise, continues from the beginning.
\par
The function is intended to be the main loop of your process.
\returns 0 in case it was correctly broken by
sue_sel_break(), otherwise (in case pselect caused
an error) returns -1.
*/
int sue_sel_go(struct sue_event_selector *s);
//! Cause main loop to break (the 'go' method to return)
/*! This function sets the loop breaking flag which causes
the main loop to return. The flag is cleared each time
sue_sel_go() is called. The flag is checked
after the main sequence of actions is performed, before
repeating it
*/
void sue_sel_break(struct sue_event_selector *s);
//! File descriptor handler for sue_event_selector
/*!
When we've got a file descriptor to be handled with pselect()
(that is, with sue_event_selector), an instance of this structure is to
be created. What events to wait for on this descriptor is determined by
want_read, want_write and want_except flags; once an event is detected,
the function pointed to by fd_handle_event is called.
The userdata pointer can be used in whatever manner the user wants,
e.g. to point to the user's own object that represents the session.
\warning It is assumed that the descriptor doesn't change after the object
is registered. If you need to change it, then unregister the object,
change the descriptor and then register the object again.
\note The callback function (the one pointed to by fd_handle_event) is
free and welcome to change the want_XXX fields to reflect the needs of
the user.
*/
struct sue_fd_handler {
int fd;
char want_read, want_write, want_except;
void *userdata;
//! The callback function
/*! The first parameter points to the sue_fd_hanler object
for which the function is called; the resting three
params are set to 0 or 1 (false/true) to reflect whether
the descriptor detected to become ready to read, ready to
write or whether an exception event happened
*/
void (*handle_fd_event)(struct sue_fd_handler*, int, int, int);
};
//! Timeout handler for sue_event_selector
/*!
Whenever we need to leave pelect() on (or after) some specific moments
in time, every such moment is to be specified with an instance of
this structure.
\note The object remains registered until the timeout happens OR
you unregister it explicitly. Once the timeout is handled, the user
is free to set the timeout to a new value and reuse the existing
object.
\warning Changing the time fields when the object is registered leads
to an unpredictable behaviour.
\warning From within the handle_timeout function, you should never try
to remove other timeout_handler objects. You can, however, remove any
handlers of other types if you need, and add anything as well.
*/
struct sue_timeout_handler {
//! seconds since epoch when the timeout is to happen
long sec;
//! microseconds (in addition to sec)
long usec;
void *userdata;
void (*handle_timeout)(struct sue_timeout_handler*);
};
//! Set the timeout relative to the current time
/*! Set the time interval after which the timeout is to happen.
The method actually calls gettimeofday(3) and adds the arguments
to the values returned.
\param a_sec - seconds
\param a_usec - microseconds
*/
void sue_timeout_set_from_now(struct sue_timeout_handler *th,
long a_sec, long a_usec);
//! Signal handler for sue_event_selector
/*! When we wish the sue_event_selector to catch some signals and notify us
we create a sue_signal_handler object. There can be several objects
for the same signal, in which case callback functions will be called for
all the objects when the signal comes.
\note It is the Selector's duty to establish the appropriate signal
handlers. The existing signal disposition is saved when the handler
is installed; if you unregister the last sue_signal_handler for a
given signal, the saved disposition is then restored.
\note All the signals handled by the library are blocked outside of
pselect() and are only enabled for the time your application is blocked
on the pselect() call. In an event-based application, you souldn't be
using blocking calls anyway, but if you do, please note your
application will not respond to signals during such blockings.
*/
struct sue_signal_handler {
int signo;
void *userdata;
void (*handle_signal)(struct sue_signal_handler*, int cnt);
};
//! Loop hook for sue_event_selector
/*! This object is to be used in case we have (or can have) something
to be done at the end of each main loop iteration, such as to perform
tasks postponed from various event handlers.
\warning It is okay for the hook to de-register itself in the selector
object from within the loop_hook function, and it can
register/remove handlers of other types, but it must NEVER
try to de-register other sue_loop_hook objects.
*/
struct sue_loop_hook {
void *userdata;
void (*loop_hook)(struct sue_loop_hook*);
};
#ifndef SUE_DEBUG
#define SUE_DEBUG 1
#endif
#if SUE_DEBUG
#include <assert.h>
#define SUE_ASSERT(x) assert((x))
/* actually, this implementation is to change one day;
definitely we shouldn't force the 'standard' assert in */
#else
#define SUE_ASSERT(x) do{}while(0)
#endif
#endif