- newtask = silc_calloc(1, sizeof(*newtask));
- newtask->fd = fd;
- newtask->context = context;
- newtask->callback = callback;
- newtask->valid = TRUE;
- newtask->priority = priority;
- newtask->type = type;
- newtask->next = newtask;
- newtask->prev = newtask;
-
- /* Create timeout if marked to be timeout task */
- if (((seconds + useconds) > 0) && (type == SILC_TASK_TIMEOUT)) {
- silc_gettimeofday(&newtask->timeout);
- newtask->timeout.tv_sec += seconds + (useconds / 1000000L);
- newtask->timeout.tv_usec += (useconds % 1000000L);
- if (newtask->timeout.tv_usec > 999999L) {
- newtask->timeout.tv_sec += 1;
- newtask->timeout.tv_usec -= 1000000L;
+ SILC_SCHEDULE_LOCK(schedule);
+
+ if (silc_likely(type == SILC_TASK_TIMEOUT)) {
+ SilcTaskTimeout tmp, prev, ttask;
+ SilcList list;
+
+ silc_list_start(schedule->free_tasks);
+ ttask = silc_list_get(schedule->free_tasks);
+ if (silc_unlikely(!ttask)) {
+ ttask = silc_calloc(1, sizeof(*ttask));
+ if (silc_unlikely(!ttask))
+ goto out;
+ } else
+ silc_list_del(schedule->free_tasks, ttask);
+
+ ttask->header.type = 1;
+ ttask->header.callback = callback;
+ ttask->header.context = context;
+ ttask->header.valid = TRUE;
+
+ /* Add timeout */
+ silc_gettimeofday(&ttask->timeout);
+ if ((seconds + useconds) > 0) {
+ ttask->timeout.tv_sec += seconds + (useconds / 1000000L);
+ ttask->timeout.tv_usec += (useconds % 1000000L);
+ if (ttask->timeout.tv_usec >= 1000000L) {
+ ttask->timeout.tv_sec += 1;
+ ttask->timeout.tv_usec -= 1000000L;
+ }
+ }
+
+ SILC_LOG_DEBUG(("New timeout task %p: sec=%d, usec=%d", ttask,
+ seconds, useconds));
+
+ /* Add task to correct spot so that the first task in the list has
+ the earliest timeout. */
+ list = schedule->timeout_queue;
+ silc_list_start(list);
+ prev = NULL;
+ while ((tmp = silc_list_get(list)) != SILC_LIST_END) {
+ /* If we have shorter timeout, we have found our spot */
+ if (silc_compare_timeval(&ttask->timeout, &tmp->timeout) < 0) {
+ silc_list_insert(schedule->timeout_queue, prev, ttask);
+ break;
+ }
+ prev = tmp;
+ }
+ if (!tmp)
+ silc_list_add(schedule->timeout_queue, ttask);
+
+ task = (SilcTask)ttask;
+
+ /* Call notify callback */
+ if (schedule->notify)
+ schedule->notify(schedule, TRUE, task, FALSE, 0, 0, seconds, useconds,
+ schedule->notify_context);
+
+ } else if (silc_likely(type == SILC_TASK_FD)) {
+ SilcTaskFd ftask;
+
+ /* Check if fd is already added */
+ if (silc_unlikely(silc_hash_table_find(schedule->fd_queue,
+ SILC_32_TO_PTR(fd),
+ NULL, (void *)&task))) {
+ if (task->valid)
+ goto out;
+
+ /* Remove invalid task. We must have unique fd key to hash table. */
+ silc_schedule_task_remove(schedule, task);
+ }
+
+ /* Check max tasks */
+ if (silc_unlikely(schedule->max_tasks > 0 &&
+ silc_hash_table_count(schedule->fd_queue) >=
+ schedule->max_tasks)) {
+ SILC_LOG_WARNING(("Scheduler task limit reached: cannot add new task"));
+ task = NULL;
+ silc_set_errno(SILC_ERR_LIMIT);
+ goto out;
+ }
+
+ ftask = silc_calloc(1, sizeof(*ftask));
+ if (silc_unlikely(!ftask)) {
+ task = NULL;
+ goto out;
+ }
+
+ SILC_LOG_DEBUG(("New fd task %p fd=%d", ftask, fd));
+
+ ftask->header.type = 0;
+ ftask->header.callback = callback;
+ ftask->header.context = context;
+ ftask->header.valid = TRUE;
+ ftask->events = SILC_TASK_READ;
+ ftask->fd = fd;
+
+ /* Add task and schedule it */
+ if (!silc_hash_table_add(schedule->fd_queue, SILC_32_TO_PTR(fd), ftask)) {
+ silc_free(ftask);
+ task = NULL;
+ goto out;
+ }
+ if (!schedule_ops.schedule_fd(schedule, schedule->internal,
+ ftask, ftask->events)) {
+ silc_hash_table_del(schedule->fd_queue, SILC_32_TO_PTR(fd));
+ task = NULL;
+ goto out;