+ SilcHashTableList htl;
+ SilcList list;
+ SilcBool ret = FALSE;
+
+ SILC_LOG_DEBUG(("Unregister task by callback"));
+
+ if (!schedule) {
+ schedule = silc_schedule_get_global();
+ SILC_VERIFY(schedule);
+ if (!schedule) {
+ silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
+ return FALSE;
+ }
+ }
+
+ SILC_SCHEDULE_LOCK(schedule);
+
+ /* Delete from fd queue */
+ silc_hash_table_list(schedule->fd_queue, &htl);
+ while (silc_hash_table_get(&htl, NULL, (void *)&task)) {
+ if (task->callback == callback) {
+ task->valid = FALSE;
+
+ /* Call notify callback */
+ if (schedule->notify)
+ schedule->notify(schedule, FALSE, task, TRUE,
+ ((SilcTaskFd)task)->fd, 0, 0, 0,
+ schedule->notify_context);
+ ret = TRUE;
+ }
+ }
+ silc_hash_table_list_reset(&htl);
+
+ /* Delete from timeout queue */
+ list = schedule->timeout_queue;
+ silc_list_start(list);
+ while ((task = (SilcTask)silc_list_get(list))) {
+ if (task->callback == callback) {
+ task->valid = FALSE;
+
+ /* Call notify callback */
+ if (schedule->notify)
+ schedule->notify(schedule, FALSE, task, FALSE, 0, 0, 0, 0,
+ schedule->notify_context);
+ ret = TRUE;
+ }
+ }
+
+ SILC_SCHEDULE_UNLOCK(schedule);
+
+ if (ret == FALSE)
+ silc_set_errno(SILC_ERR_NOT_FOUND);
+
+ return ret;
+}
+
+/* Invalidate task by context. */
+
+SilcBool silc_schedule_task_del_by_context(SilcSchedule schedule,
+ void *context)
+{
+ SilcTask task;
+ SilcHashTableList htl;
+ SilcList list;
+ SilcBool ret = FALSE;
+
+ SILC_LOG_DEBUG(("Unregister task by context"));
+
+ if (!schedule) {
+ schedule = silc_schedule_get_global();
+ SILC_VERIFY(schedule);
+ if (!schedule) {
+ silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
+ return FALSE;
+ }
+ }
+
+ SILC_SCHEDULE_LOCK(schedule);
+
+ /* Delete from fd queue */
+ silc_hash_table_list(schedule->fd_queue, &htl);
+ while (silc_hash_table_get(&htl, NULL, (void *)&task)) {
+ if (task->context == context) {
+ task->valid = FALSE;
+
+ /* Call notify callback */
+ if (schedule->notify)
+ schedule->notify(schedule, FALSE, task, TRUE,
+ ((SilcTaskFd)task)->fd, 0, 0, 0,
+ schedule->notify_context);
+ ret = TRUE;
+ }
+ }
+ silc_hash_table_list_reset(&htl);
+
+ /* Delete from timeout queue */
+ list = schedule->timeout_queue;
+ silc_list_start(list);
+ while ((task = (SilcTask)silc_list_get(list))) {
+ if (task->context == context) {
+ task->valid = FALSE;
+
+ /* Call notify callback */
+ if (schedule->notify)
+ schedule->notify(schedule, FALSE, task, FALSE, 0, 0, 0, 0,
+ schedule->notify_context);
+ ret = TRUE;
+ }
+ }
+
+ SILC_SCHEDULE_UNLOCK(schedule);
+
+ if (ret == FALSE)
+ silc_set_errno(SILC_ERR_NOT_FOUND);
+
+ return ret;
+}
+
+/* Invalidate task by all */
+
+SilcBool silc_schedule_task_del_by_all(SilcSchedule schedule, int fd,
+ SilcTaskCallback callback,
+ void *context)
+{
+ SilcTask task;
+ SilcList list;
+ SilcBool ret = FALSE;
+
+ SILC_LOG_DEBUG(("Unregister task by fd, callback and context"));
+
+ /* For fd task, callback and context is irrelevant as fd is unique */
+ if (fd)
+ return silc_schedule_task_del_by_fd(schedule, fd);
+
+ if (!schedule) {
+ schedule = silc_schedule_get_global();
+ SILC_VERIFY(schedule);
+ if (!schedule) {
+ silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
+ return FALSE;
+ }
+ }
+
+ SILC_SCHEDULE_LOCK(schedule);
+
+ /* Delete from timeout queue */
+ list = schedule->timeout_queue;
+ silc_list_start(list);
+ while ((task = (SilcTask)silc_list_get(list))) {
+ if (task->callback == callback && task->context == context) {
+ task->valid = FALSE;
+
+ /* Call notify callback */
+ if (schedule->notify)
+ schedule->notify(schedule, FALSE, task, FALSE, 0, 0, 0, 0,
+ schedule->notify_context);
+ ret = TRUE;
+ }
+ }
+
+ SILC_SCHEDULE_UNLOCK(schedule);
+
+ if (ret == FALSE)
+ silc_set_errno(SILC_ERR_NOT_FOUND);
+
+ return TRUE;
+}
+
+/* Sets a file descriptor to be listened by scheduler. One can call this
+ directly if wanted. This can be called multiple times for one file
+ descriptor to set different iomasks. */
+
+SilcBool silc_schedule_set_listen_fd(SilcSchedule schedule, SilcUInt32 fd,
+ SilcTaskEvent mask, SilcBool send_events)
+{
+ SilcTaskFd task;
+
+ if (!schedule) {
+ schedule = silc_schedule_get_global();
+ SILC_VERIFY(schedule);
+ if (!schedule) {
+ silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
+ return FALSE;
+ }
+ }
+
+ if (silc_unlikely(!schedule->valid)) {
+ silc_set_errno(SILC_ERR_NOT_VALID);
+ return FALSE;
+ }
+
+ SILC_SCHEDULE_LOCK(schedule);
+
+ if (silc_hash_table_find(schedule->fd_queue, SILC_32_TO_PTR(fd),
+ NULL, (void *)&task)) {
+ if (!schedule_ops.schedule_fd(schedule, schedule->internal, task, mask)) {
+ SILC_SCHEDULE_UNLOCK(schedule);
+ return FALSE;
+ }
+ task->events = mask;
+ if (silc_unlikely(send_events) && mask) {
+ task->revents = mask;
+ silc_schedule_dispatch_fd(schedule);
+ }
+
+ /* Call notify callback */
+ if (schedule->notify)
+ schedule->notify(schedule, TRUE, (SilcTask)task,
+ TRUE, task->fd, mask, 0, 0,
+ schedule->notify_context);
+ }
+
+ SILC_SCHEDULE_UNLOCK(schedule);
+
+ return TRUE;
+}
+
+/* Returns the file descriptor's current requested event mask. */
+
+SilcTaskEvent silc_schedule_get_fd_events(SilcSchedule schedule,
+ SilcUInt32 fd)
+{
+ SilcTaskFd task;
+ SilcTaskEvent event = 0;
+
+ if (!schedule) {
+ schedule = silc_schedule_get_global();
+ SILC_VERIFY(schedule);
+ if (!schedule) {
+ silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
+ return 0;
+ }
+ }
+
+ if (silc_unlikely(!schedule->valid)) {
+ silc_set_errno(SILC_ERR_NOT_VALID);
+ return 0;
+ }
+
+ SILC_SCHEDULE_LOCK(schedule);
+ if (silc_hash_table_find(schedule->fd_queue, SILC_32_TO_PTR(fd),
+ NULL, (void *)&task))
+ event = task->events;
+ SILC_SCHEDULE_UNLOCK(schedule);
+
+ return event;
+}
+
+/* Removes a file descriptor from listen list. */
+
+void silc_schedule_unset_listen_fd(SilcSchedule schedule, SilcUInt32 fd)
+{
+ silc_schedule_set_listen_fd(schedule, fd, 0, FALSE);
+}
+
+/*************************** Asynchronous Events ****************************/
+
+/* Add event */
+
+SilcTask silc_schedule_task_add_event(SilcSchedule schedule,
+ const char *event, ...)
+{
+ SilcEventTask task;
+ SilcSchedule parent;
+
+ if (!schedule) {
+ schedule = silc_schedule_get_global();
+ SILC_VERIFY(schedule);
+ if (!schedule) {
+ silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
+ return NULL;
+ }
+ }
+
+ /* Get parent scheduler */
+ parent = silc_schedule_get_parent(schedule);
+
+ SILC_LOG_DEBUG(("Adding event '%s' to scheduler %p", event, parent));
+
+ SILC_SCHEDULE_LOCK(parent);
+
+ /* Create events hash table if not already done */
+ if (!parent->events) {
+ parent->events = silc_hash_table_alloc(NULL, 3,
+ silc_hash_string, NULL,
+ silc_hash_string_compare, NULL,
+ NULL, NULL, FALSE);
+ if (!parent->events) {
+ SILC_SCHEDULE_UNLOCK(parent);
+ return NULL;
+ }
+ }
+
+ /* Check if this event is added already */
+ if (silc_hash_table_find(parent->events, (void *)event, NULL, NULL)) {
+ SILC_SCHEDULE_UNLOCK(parent);
+ return NULL;
+ }
+
+ /* Add new event */
+ task = silc_calloc(1, sizeof(*task));
+ if (!task) {
+ SILC_SCHEDULE_UNLOCK(parent);
+ return NULL;
+ }
+
+ task->header.type = SILC_TASK_EVENT;
+ task->header.valid = TRUE;
+ task->event = silc_strdup(event);
+ if (!task->event) {
+ SILC_SCHEDULE_UNLOCK(parent);
+ silc_free(task);
+ return NULL;
+ }
+ silc_list_init(task->connections, struct SilcScheduleEventConnectionStruct,
+ next);
+
+ if (!silc_hash_table_add(parent->events, task->event, task)) {
+ SILC_SCHEDULE_UNLOCK(parent);
+ silc_free(task->event);
+ silc_free(task);
+ return NULL;
+ }
+
+ SILC_SCHEDULE_UNLOCK(parent);
+
+ return (SilcTask)task;
+}