From: Pekka Riikonen Date: Wed, 20 Mar 2002 10:09:14 +0000 (+0000) Subject: Improved signals support in scheduler. X-Git-Tag: silc.client.0.8.2~4 X-Git-Url: http://git.silcnet.org/gitweb/?p=silc.git;a=commitdiff_plain;h=3b831c969f27cc1d590d87f52035838b705a4905 Improved signals support in scheduler. --- diff --git a/CHANGES b/CHANGES index 6e419f7f..a5b0b35b 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,13 @@ +Wed Mar 20 11:06:57 CET 2002 Pekka Riikonen + + * Improved the signal support in SILC Schedule. Added new + function silc_schedule_signal_call which is used by application + to mark a signal to be called. It is now safe to perform + any kind of tasks in signal callbacks since it is guaranteed + that the application specified signal callback is called + after the signal is over. Affected files are + lib/silcutil/silcschedule.[ch], lib/silcutil/*/silc*schedule.c. + Tue Mar 19 20:42:41 EET 2002 Pekka Riikonen * Added `name' field to SilcChannelPrivateKey to represent diff --git a/apps/silcd/silcd.c b/apps/silcd/silcd.c index 616f50c4..e5f45e3b 100644 --- a/apps/silcd/silcd.c +++ b/apps/silcd/silcd.c @@ -127,14 +127,20 @@ static void silc_server_checkpid(SilcServer silcd) } } -static void got_hup(int z) +static void signal_handler(int sig) +{ + /* Mark the signal to be caller after this signal is over. */ + silc_schedule_signal_call(silcd->schedule, sig); +} + +SILC_TASK_CALLBACK(got_hup) { /* First, reset all log files (they might have been deleted) */ silc_log_reset_all(); silc_log_flush_all(); } -static void stop_server(int z) +SILC_TASK_CALLBACK(stop_server) { /* Stop scheduler, the program will stop eventually after noticing that the scheduler is down. */ @@ -251,15 +257,13 @@ int main(int argc, char **argv) sa.sa_flags = 0; sigemptyset(&sa.sa_mask); sigaction(SIGPIPE, &sa, NULL); - sa.sa_handler = got_hup; + sa.sa_handler = signal_handler; sigaction(SIGHUP, &sa, NULL); - sa.sa_handler = stop_server; sigaction(SIGTERM, &sa, NULL); - sa.sa_handler = stop_server; sigaction(SIGINT, &sa, NULL); - silc_schedule_signal_register(silcd->schedule, SIGHUP); - silc_schedule_signal_register(silcd->schedule, SIGTERM); - silc_schedule_signal_register(silcd->schedule, SIGINT); + silc_schedule_signal_register(silcd->schedule, SIGHUP, got_hup, NULL); + silc_schedule_signal_register(silcd->schedule, SIGTERM, stop_server, NULL); + silc_schedule_signal_register(silcd->schedule, SIGINT, stop_server, NULL); /* Before running the server, fork to background. */ if (!foreground) diff --git a/lib/silcutil/os2/silcos2schedule.c b/lib/silcutil/os2/silcos2schedule.c index 54cfa374..53904de4 100644 --- a/lib/silcutil/os2/silcos2schedule.c +++ b/lib/silcutil/os2/silcos2schedule.c @@ -114,8 +114,11 @@ void silc_schedule_internal_wakeup(void *context) } /* Register signal */ + void silc_schedule_internal_signal_register(void *context, - SilcUInt32 signal) + SilcUInt32 signal, + SilcTaskCallback callback, + void *callback_context) { } @@ -123,7 +126,24 @@ void silc_schedule_internal_signal_register(void *context, /* Unregister signal */ void silc_schedule_internal_signal_unregister(void *context, - SilcUInt32 signal) + SilcUInt32 signal, + SilcTaskCallback callback, + void *callback_context) +{ + +} + +/* Mark signal to be called later. */ + +void silc_schedule_internal_signal_call(void *context, SilcUInt32 signal) +{ + +} + +/* Call all signals */ + +void silc_schedule_internal_signals_call(void *context, + SilcSchedule schedule) { } diff --git a/lib/silcutil/silcschedule.c b/lib/silcutil/silcschedule.c index cb4a9693..b7627350 100644 --- a/lib/silcutil/silcschedule.c +++ b/lib/silcutil/silcschedule.c @@ -45,11 +45,22 @@ void silc_schedule_internal_wakeup(void *context); /* Register signal */ void silc_schedule_internal_signal_register(void *context, - SilcUInt32 signal); + SilcUInt32 signal, + SilcTaskCallback callback, + void *callback_context); /* Unregister signal */ void silc_schedule_internal_signal_unregister(void *context, - SilcUInt32 signal); + SilcUInt32 signal, + SilcTaskCallback callback, + void *callback_context); + +/* Mark signal to be called later. */ +void silc_schedule_internal_signal_call(void *context, SilcUInt32 signal); + +/* Call all signals */ +void silc_schedule_internal_signals_call(void *context, + SilcSchedule schedule); /* Block registered signals in scheduler. */ void silc_schedule_internal_signals_block(void *context); @@ -190,6 +201,11 @@ struct SilcTaskQueueStruct { Scheduler lock. + bool signal_tasks + + TRUE when tasks has been registered from signals. Next round in + scheduler will call the callbacks when this is TRUE. + */ struct SilcScheduleStruct { SilcTaskQueue fd_queue; @@ -203,6 +219,7 @@ struct SilcScheduleStruct { void *internal; SILC_MUTEX_DEFINE(lock); bool is_locked; + bool signal_tasks; }; /* Initializes the scheduler. This returns the scheduler context that @@ -238,9 +255,6 @@ SilcSchedule silc_schedule_init(int max_tasks) /* Initialize the platform specific scheduler. */ schedule->internal = silc_schedule_internal_init(schedule); -#ifdef SILC_UNIX - silc_schedule_signal_register(schedule, SIGALRM); -#endif return schedule; } @@ -573,6 +587,12 @@ bool silc_schedule_one(SilcSchedule schedule, int timeout_usecs) SILC_SCHEDULE_UNLOCK(schedule); + /* Deliver signals if any has been set to be called */ + if (schedule->signal_tasks) { + silc_schedule_internal_signals_call(schedule->internal, schedule); + schedule->signal_tasks = FALSE; + } + /* This is the main select(). The program blocks here until some of the selected file descriptors change status or the selected timeout expires. */ @@ -876,16 +896,29 @@ void silc_schedule_unset_listen_fd(SilcSchedule schedule, SilcUInt32 fd) /* Register a new signal */ -void silc_schedule_signal_register(SilcSchedule schedule, SilcUInt32 signal) +void silc_schedule_signal_register(SilcSchedule schedule, SilcUInt32 signal, + SilcTaskCallback callback, void *context) { - silc_schedule_internal_signal_register(schedule->internal, signal); + silc_schedule_internal_signal_register(schedule->internal, signal, + callback, context); } /* Unregister a new signal */ -void silc_schedule_signal_unregister(SilcSchedule schedule, SilcUInt32 signal) +void silc_schedule_signal_unregister(SilcSchedule schedule, SilcUInt32 signal, + SilcTaskCallback callback, void *context) +{ + silc_schedule_internal_signal_unregister(schedule->internal, signal, + callback, context); +} + +/* Call signal indicated by `signal'. */ + +void silc_schedule_signal_call(SilcSchedule schedule, SilcUInt32 signal) { - silc_schedule_internal_signal_unregister(schedule->internal, signal); + /* Mark that signals needs to be delivered later. */ + silc_schedule_internal_signal_call(schedule->internal, signal); + schedule->signal_tasks = TRUE; } /* Allocates a newtask task queue into the scheduler */ diff --git a/lib/silcutil/silcschedule.h b/lib/silcutil/silcschedule.h index 4b23067d..953dbe73 100644 --- a/lib/silcutil/silcschedule.h +++ b/lib/silcutil/silcschedule.h @@ -165,6 +165,7 @@ typedef enum { SILC_TASK_READ = 0x0001, /* Reading */ SILC_TASK_WRITE = 0x0002, /* Writing */ SILC_TASK_EXPIRE = 0x0004, /* Timeout */ + SILC_TASK_INTERRUPT = 0x0004, /* Signal */ } SilcTaskEvent; /***/ @@ -574,25 +575,60 @@ void silc_schedule_unset_listen_fd(SilcSchedule schedule, SilcUInt32 fd); * SYNOPSIS * * void silc_schedule_signal_register(SilcSchedule schedule, - * SilcUInt32 signal); + * SilcUInt32 signal, + * SilcTaskCallback callback, + * void *context); * * DESCRIPTION * * Register signal indicated by `signal' to the scheduler. Application * should register all signals it is going to use to the scheduler. - * To unregister a signal call silc_schedule_signal_unregister. On - * platform that does not support signals calling this function has not - * effect. + * The `callback' with `context' will be called after the application + * has called silc_schedule_signal_call function in the real signal + * callback. Application is responsible of calling that, and the + * signal system will not work without calling silc_schedule_signal_call + * function. The specified `signal' value will be also delivered to + * the `callback' as the fd-argument. The event type in the callback + * will be SILC_TASK_INTERRUPT. It is safe to use any SILC routines, + * in the `callback' since it is actually called after the signal really + * happened. + * + * On platform that does not support signals calling this function has + * not effect. + * + * EXAMPLE + * + * Typical signal usage case on Unix systems: + * + * struct sigaction sa; + * sa.sa_handler = signal_handler; + * sigaction(SIGHUP, &sa, NULL); + * sigaction(SIGINT, &sa, NULL); + * silc_schedule_signal_register(schedule, SIGHUP, hup_signal, context); + * silc_schedule_signal_register(schedule, SIGINT, int_signal, context); + * + * static void signal_handler(int sig) + * { + * silc_schedule_signal_call(schedule, sig); + * } + * + * The `signal_handler' can be used as generic signal callback in the + * application that merely calls silc_schedule_signal_call, which then + * eventually will deliver for example the `hup_signal' callback. The + * same `signal_handler' can be used with all signals. * ***/ -void silc_schedule_signal_register(SilcSchedule schedule, SilcUInt32 signal); +void silc_schedule_signal_register(SilcSchedule schedule, SilcUInt32 signal, + SilcTaskCallback callback, void *context); /****f* silcutil/SilcScheduleAPI/silc_schedule_signal_unregister * * SYNOPSIS * * void silc_schedule_signal_unregister(SilcSchedule schedule, - * SilcUInt32 signal); + * SilcUInt32 signal, + * SilcTaskCallback callback, + * void *context); * * DESCRIPTION * @@ -601,6 +637,26 @@ void silc_schedule_signal_register(SilcSchedule schedule, SilcUInt32 signal); * effect. * ***/ -void silc_schedule_signal_unregister(SilcSchedule schedule, SilcUInt32 signal); +void silc_schedule_signal_unregister(SilcSchedule schedule, SilcUInt32 signal, + SilcTaskCallback callback, void *context); + +/****f* silcutil/SilcScheduleAPI/silc_schedule_signal_call + * + * SYNOPSIS + * + * void silc_schedule_signal_call(SilcSchedule schedule, + * SilcUInt32 signal); + * + * DESCRIPTION + * + * Mark the `signal' to be called later. Every signal that has been + * registered by silc_schedule_signal_register is delivered by calling + * this function. When signal really occurs, the application is + * responsible of calling this function int the signal handler. After + * signal is over the scheduler will then safely deliver the callback + * that was given to silc_schedule_signal_register function. + * + ***/ +void silc_schedule_signal_call(SilcSchedule schedule, SilcUInt32 signal); #endif diff --git a/lib/silcutil/unix/silcunixschedule.c b/lib/silcutil/unix/silcunixschedule.c index bf12f7b2..ae2ce912 100644 --- a/lib/silcutil/unix/silcunixschedule.c +++ b/lib/silcutil/unix/silcunixschedule.c @@ -65,12 +65,22 @@ int silc_select(SilcScheduleFd fds, SilcUInt32 fds_count, return ret; } +#define SIGNAL_COUNT 32 + +typedef struct { + SilcUInt32 signal; + SilcTaskCallback callback; + void *context; + bool call; +} SilcUnixSignal; + /* Internal context. */ typedef struct { int wakeup_pipe[2]; SilcTask wakeup_task; sigset_t signals; sigset_t signals_blocked; + SilcUnixSignal signal_call[SIGNAL_COUNT]; } *SilcUnixScheduler; #ifdef SILC_THREADS @@ -154,19 +164,79 @@ void silc_schedule_internal_wakeup(void *context) } void silc_schedule_internal_signal_register(void *context, - SilcUInt32 signal) + SilcUInt32 signal, + SilcTaskCallback callback, + void *callback_context) { SilcUnixScheduler internal = (SilcUnixScheduler)context; + int i; + + for (i = 0; i < SIGNAL_COUNT; i++) { + if (!internal->signal_call[i].signal) { + internal->signal_call[i].signal = signal; + internal->signal_call[i].callback = callback; + internal->signal_call[i].context = callback_context; + internal->signal_call[i].call = FALSE; + } + } + sigaddset(&internal->signals, signal); } void silc_schedule_internal_signal_unregister(void *context, - SilcUInt32 signal) + SilcUInt32 signal, + SilcTaskCallback callback, + void *callback_context) { SilcUnixScheduler internal = (SilcUnixScheduler)context; + int i; + + for (i = 0; i < SIGNAL_COUNT; i++) { + if (internal->signal_call[i].signal == signal && + internal->signal_call[i].callback == callback && + internal->signal_call[i].context == callback_context) { + internal->signal_call[i].signal = 0; + internal->signal_call[i].callback = NULL; + internal->signal_call[i].context = NULL; + internal->signal_call[i].call = FALSE; + } + } + sigdelset(&internal->signals, signal); } +/* Mark signal to be called later. */ + +void silc_schedule_internal_signal_call(void *context, SilcUInt32 signal) +{ + SilcUnixScheduler internal = (SilcUnixScheduler)context; + int i; + + for (i = 0; i < SIGNAL_COUNT; i++) { + if (internal->signal_call[i].signal == signal) + internal->signal_call[i].call = TRUE; + } +} + +/* Call all signals */ + +void silc_schedule_internal_signals_call(void *context, + SilcSchedule schedule) +{ + SilcUnixScheduler internal = (SilcUnixScheduler)context; + int i; + + for (i = 0; i < SIGNAL_COUNT; i++) { + if (internal->signal_call[i].call && + internal->signal_call[i].callback) { + internal->signal_call[i].callback(schedule, SILC_TASK_INTERRUPT, + internal->signal_call[i].signal, + internal->signal_call[i].context); + internal->signal_call[i].call = FALSE; + } + } +} + /* Block registered signals in scheduler. */ void silc_schedule_internal_signals_block(void *context) diff --git a/lib/silcutil/win32/silcwin32schedule.c b/lib/silcutil/win32/silcwin32schedule.c index 66b732ab..e6aa6c9d 100644 --- a/lib/silcutil/win32/silcwin32schedule.c +++ b/lib/silcutil/win32/silcwin32schedule.c @@ -255,7 +255,9 @@ void silc_schedule_internal_wakeup(void *context) /* Register signal */ void silc_schedule_internal_signal_register(void *context, - SilcUInt32 signal) + SilcUInt32 signal, + SilcTaskCallback callback, + void *callback_context) { } @@ -263,7 +265,24 @@ void silc_schedule_internal_signal_register(void *context, /* Unregister signal */ void silc_schedule_internal_signal_unregister(void *context, - SilcUInt32 signal) + SilcUInt32 signal, + SilcTaskCallback callback, + void *callback_context) +{ + +} + +/* Mark signal to be called later. */ + +void silc_schedule_internal_signal_call(void *context, SilcUInt32 signal) +{ + +} + +/* Call all signals */ + +void silc_schedule_internal_signals_call(void *context, + SilcSchedule schedule) { }