Author: Pekka Riikonen <priikone@silcnet.org>
- Copyright (C) 1998 - 2001 Pekka Riikonen
+ Copyright (C) 1998 - 2004 Pekka Riikonen
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
+ the Free Software Foundation; version 2 of the License.
+
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 the
/* Calls normal select() system call. */
-int silc_select(SilcScheduleFd fds, SilcUInt32 fds_count,
+int silc_select(SilcScheduleFd fds, SilcUInt32 fds_count,
struct timeval *timeout)
{
fd_set in, out;
return ret;
}
+#define SIGNAL_COUNT 32
+
+typedef struct {
+ SilcUInt32 signal;
+ SilcTaskCallback callback;
+ void *context;
+ bool call;
+} SilcUnixSignal;
+
/* Internal context. */
typedef struct {
+ void *app_context;
int wakeup_pipe[2];
SilcTask wakeup_task;
sigset_t signals;
sigset_t signals_blocked;
+ SilcUnixSignal signal_call[SIGNAL_COUNT];
} *SilcUnixScheduler;
#ifdef SILC_THREADS
the scheduler needs to be wakenup when tasks are added or removed from
the task queues. Returns context to the platform specific scheduler. */
-void *silc_schedule_internal_init(SilcSchedule schedule)
+void *silc_schedule_internal_init(SilcSchedule schedule,
+ void *app_context)
{
SilcUnixScheduler internal;
#ifdef SILC_THREADS
if (pipe(internal->wakeup_pipe)) {
+ SILC_LOG_ERROR(("pipe() fails: %s", strerror(errno)));
silc_free(internal);
return NULL;
}
- internal->wakeup_task =
+ internal->wakeup_task =
silc_schedule_task_add(schedule, internal->wakeup_pipe[0],
silc_schedule_wakeup_cb, internal,
- 0, 0, SILC_TASK_FD,
+ 0, 0, SILC_TASK_FD,
SILC_TASK_PRI_NORMAL);
if (!internal->wakeup_task) {
+ SILC_LOG_ERROR(("Could not add a wakeup task, threads won't work"));
close(internal->wakeup_pipe[0]);
close(internal->wakeup_pipe[1]);
silc_free(internal);
}
#endif
+ internal->app_context = app_context;
+
return (void *)internal;
}
+void silc_schedule_internal_signals_block(void *context);
+void silc_schedule_internal_signals_unblock(void *context);
+
/* Uninitializes the platform specific scheduler context. */
void silc_schedule_internal_uninit(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;
+
+ if (!internal)
+ return;
+
+ SILC_LOG_DEBUG(("Registering signal %d", signal));
+
+ silc_schedule_internal_signals_block(context);
+
+ 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;
+ break;
+ }
+ }
+
+ silc_schedule_internal_signals_unblock(context);
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;
+
+ if (!internal)
+ return;
+
+ SILC_LOG_DEBUG(("Unregistering signal %d", signal));
+
+ silc_schedule_internal_signals_block(context);
+
+ 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;
+ }
+ }
+
+ silc_schedule_internal_signals_unblock(context);
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;
+
+ if (!internal)
+ return;
+
+ silc_schedule_internal_signals_block(context);
+
+ for (i = 0; i < SIGNAL_COUNT; i++) {
+ if (internal->signal_call[i].signal == signal) {
+ internal->signal_call[i].call = TRUE;
+ SILC_LOG_DEBUG(("Scheduling signal %d to be called",
+ internal->signal_call[i].signal));
+ }
+ }
+
+ silc_schedule_internal_signals_unblock(context);
+}
+
+/* Call all signals */
+
+void silc_schedule_internal_signals_call(void *context,
+ SilcSchedule schedule)
+{
+ SilcUnixScheduler internal = (SilcUnixScheduler)context;
+ int i;
+
+ SILC_LOG_DEBUG(("Start"));
+
+ if (!internal)
+ return;
+
+ silc_schedule_internal_signals_block(context);
+
+ for (i = 0; i < SIGNAL_COUNT; i++) {
+ if (internal->signal_call[i].call &&
+ internal->signal_call[i].callback) {
+ SILC_LOG_DEBUG(("Calling signal %d callback",
+ internal->signal_call[i].signal));
+ internal->signal_call[i].callback(schedule, internal->app_context,
+ SILC_TASK_INTERRUPT,
+ internal->signal_call[i].signal,
+ internal->signal_call[i].context);
+ internal->signal_call[i].call = FALSE;
+ }
+ }
+
+ silc_schedule_internal_signals_unblock(context);
+}
+
/* Block registered signals in scheduler. */
void silc_schedule_internal_signals_block(void *context)
{
SilcUnixScheduler internal = (SilcUnixScheduler)context;
+
+ if (!internal)
+ return;
+
sigprocmask(SIG_BLOCK, &internal->signals, &internal->signals_blocked);
}
void silc_schedule_internal_signals_unblock(void *context)
{
SilcUnixScheduler internal = (SilcUnixScheduler)context;
+
+ if (!internal)
+ return;
+
sigprocmask(SIG_SETMASK, &internal->signals_blocked, NULL);
}