/* Forward declarations */
typedef struct SilcTaskQueueStruct *SilcTaskQueue;
-/* System specific routines. Implemented under unix/ and win32/. */
+/* System specific routines. Implemented under unix/, win32/ and such. */
/* System specific select(). Returns same values as normal select(). */
int silc_select(SilcScheduleFd fds, SilcUInt32 fds_count,
struct timeval *timeout);
-/* Initializes the wakeup of the scheduler. In multi-threaded environment
+/* Initializes the platform specific scheduler. This for example initializes
+ the wakeup mechanism of the scheduler. In multi-threaded environment
the scheduler needs to be wakenup when tasks are added or removed from
- the task queues. This will initialize the wakeup for the scheduler.
- Any tasks that needs to be registered must be registered to the `queue'.
- It is guaranteed that the scheduler will automatically free any
- registered tasks in this queue. This is system specific routine. */
-void *silc_schedule_wakeup_init(SilcSchedule schedule);
+ the task queues. Returns context to the platform specific scheduler. */
+void *silc_schedule_internal_init(SilcSchedule schedule);
-/* Uninitializes the system specific wakeup. */
-void silc_schedule_wakeup_uninit(void *context);
+/* Uninitializes the platform specific scheduler context. */
+void silc_schedule_internal_uninit(void *context);
/* Wakes up the scheduler. This is platform specific routine */
-void silc_schedule_wakeup_internal(void *context);
+void silc_schedule_internal_wakeup(void *context);
+/* Register signal */
+void silc_schedule_internal_signal_register(void *context,
+ SilcUInt32 signal,
+ SilcTaskCallback callback,
+ void *callback_context);
+
+/* Unregister signal */
+void silc_schedule_internal_signal_unregister(void *context,
+ 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);
+
+/* Unblock registered signals in schedule. */
+void silc_schedule_internal_signals_unblock(void *context);
/* Internal task management routines. */
static void silc_task_del_by_fd(SilcTaskQueue queue, SilcUInt32 fd);
/* Returns the task queue by task type */
-#define SILC_SCHEDULE_GET_QUEUE(type) \
- (type == SILC_TASK_FD ? schedule->fd_queue : \
- type == SILC_TASK_TIMEOUT ? schedule->timeout_queue : \
+#define SILC_SCHEDULE_GET_QUEUE(type) \
+ (type == SILC_TASK_FD ? schedule->fd_queue : \
+ type == SILC_TASK_TIMEOUT ? schedule->timeout_queue : \
schedule->generic_queue)
-/* Locks */
-#define SILC_SCHEDULE_LOCK(schedule) silc_mutex_lock(schedule->lock)
-#define SILC_SCHEDULE_UNLOCK(schedule) silc_mutex_unlock(schedule->lock)
+/* Locks. These also blocks signals that we care about and thus guarantee
+ that while we are in scheduler no signals can happen. This way we can
+ synchronise signals with SILC Scheduler. */
+#define SILC_SCHEDULE_LOCK(schedule) \
+do { \
+ silc_schedule_internal_signals_block(schedule->internal); \
+ silc_mutex_lock(schedule->lock); \
+} while (0)
+#define SILC_SCHEDULE_UNLOCK(schedule) \
+do { \
+ silc_mutex_unlock(schedule->lock); \
+ silc_schedule_internal_signals_unblock(schedule->internal); \
+} while (0)
/* SILC Task object. Represents one task in the scheduler. */
struct SilcTaskStruct {
SilcUInt32 fd;
- struct timeval timeout;
- SilcTaskCallback callback;
- void *context;
- bool valid;
- SilcTaskPriority priority;
- SilcTaskType type;
+ SilcTaskCallback callback; /* Task callback */
+ void *context; /* Task callback context */
+ struct timeval timeout; /* Set for timeout tasks */
+ unsigned int valid : 1; /* Set when task is valid */
+ unsigned int priority : 2; /* Priority of the task */
+ unsigned int type : 5; /* Type of the task */
/* Pointers forming doubly linked circular list */
struct SilcTaskStruct *next;
File descriptor sets for select(). These are automatically managed
by the scheduler and should not be touched otherwise.
- void *wakeup
+ void *internal
- System specific wakeup context. On multi-threaded environments the
- scheduler needs to be wakenup (in the thread) when tasks are added
- or removed. This is initialized by silc_schedule_wakeup_init.
+ System specific scheduler context.
SILC_MUTEX_DEFINE(lock)
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;
SilcUInt32 last_fd;
struct timeval *timeout;
bool valid;
- void *wakeup;
+ void *internal;
SILC_MUTEX_DEFINE(lock);
bool is_locked;
+ bool signal_tasks;
};
/* Initializes the scheduler. This returns the scheduler context that
/* Allocate scheduler lock */
silc_mutex_alloc(&schedule->lock);
- /* Initialize the wakeup, for multi-threads support */
- schedule->wakeup = silc_schedule_wakeup_init(schedule);
+ /* Initialize the platform specific scheduler. */
+ schedule->internal = silc_schedule_internal_init(schedule);
return schedule;
}
silc_free(schedule->fd_list);
- /* Uninit the wakeup */
- silc_schedule_wakeup_uninit(schedule->wakeup);
+ /* Uninit the platform specific scheduler. */
+ silc_schedule_internal_uninit(schedule->internal);
silc_mutex_free(schedule->lock);
void silc_schedule_stop(SilcSchedule schedule)
{
SILC_LOG_DEBUG(("Stopping scheduler"));
+ SILC_SCHEDULE_LOCK(schedule);
schedule->valid = FALSE;
+ SILC_SCHEDULE_UNLOCK(schedule);
}
/* Executes nontimeout tasks. It then checks whether any of ther fd tasks
if (!schedule->is_locked)
SILC_SCHEDULE_LOCK(schedule);
+ /* Deliver signals if any has been set to be called */
+ if (schedule->signal_tasks) {
+ SILC_SCHEDULE_UNLOCK(schedule);
+ silc_schedule_internal_signals_call(schedule->internal, schedule);
+ schedule->signal_tasks = FALSE;
+ SILC_SCHEDULE_LOCK(schedule);
+ }
+
/* If the task queues aren't initialized or we aren't valid anymore
we will return */
if ((!schedule->fd_queue && !schedule->timeout_queue
#ifdef SILC_THREADS
SILC_LOG_DEBUG(("Wakeup scheduler"));
SILC_SCHEDULE_LOCK(schedule);
- silc_schedule_wakeup_internal(schedule->wakeup);
+ silc_schedule_internal_wakeup(schedule->internal);
SILC_SCHEDULE_UNLOCK(schedule);
#endif
}
SILC_SCHEDULE_UNLOCK(schedule);
}
+/* Register a new signal */
+
+void silc_schedule_signal_register(SilcSchedule schedule, SilcUInt32 signal,
+ SilcTaskCallback callback, void *context)
+{
+ silc_schedule_internal_signal_register(schedule->internal, signal,
+ callback, context);
+}
+
+/* Unregister a new 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)
+{
+ /* 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 */
static void silc_task_queue_alloc(SilcTaskQueue *queue)
static void silc_task_queue_free(SilcTaskQueue queue)
{
silc_mutex_free(queue->lock);
+ memset(queue, 'F', sizeof(*queue));
silc_free(queue);
}
next = first;
while(1) {
- next = next->next;
- silc_free(next->prev);
- if (next == first)
+ old = next->next;
+ silc_free(next);
+ if (old == first)
break;
+ next = old;
}
queue->task = NULL;