Author: tjansen Date: Tue Oct 13 13:01:37 2009 New Revision: 1102 Log: First draft of the scheduler. Do not add tasks yet, it's not tested. Modified: trunk/libpisa/scheduler.c Modified: trunk/libpisa/scheduler.c ============================================================================== --- trunk/libpisa/scheduler.c Tue Oct 13 13:00:13 2009 (r1101) +++ trunk/libpisa/scheduler.c Tue Oct 13 13:01:37 2009 (r1102) @@ -11,6 +11,7 @@ */ #include <pthread.h> +#include <sys/time.h> #include "scheduler.h" @@ -26,21 +27,63 @@ static pthread_t thread; /** + * Mutex for locking the task list + */ +static pthread_mutex_t mutex_list; + +/** * Head of the task list. */ static pisa_sched_task *head = NULL; /** + * Pipe indicates that the scheduler should wake up before the first task + */ +static int pipefd[2]; + +static int pisa_time_before(struct timeval *t1, struct timeval *t2) +{ + if (t1->tv_sec == t2->tv_sec) + return t1->tv_usec < t2->tv_usec; + return t1->tv_sec < t2->tv_sec; +} + +/** * Main function of the scheduler thread */ static void *pisa_sched_main(void *arg) { - /* TODO: sleep until first element is due. */ + fd_set fds; + struct timeval timeout, now; + + timerclear(&timeout); + while (1) { - if (head == NULL) - sleep(1); - else - sleep(2); + FD_ZERO(&fds); + FD_SET(pipefd[0], &fds); + + select(pipefd[0] + 1, &fds, NULL, NULL, &timeout); + if (FD_ISSET(pipefd[0], &fds)) { + /* Someone woke us up to reschedule, read the bytes */ + char c; + read(pipefd[0], &c, 1); + } + + pthread_mutex_lock(&mutex_list); + gettimeofday(&now, NULL); + if (pisa_time_before(&head->due, &now)) { + /* (at least) the first action is due now, raise the + * flag. As we have to reschedule afterwards, sleep + * for a _long_ time. We don't care about tv_usec. */ + *flag = 1; + timeout.tv_sec = 1000000; + } else { + /* No action is due now, sleep until the head is due + * (or we are woken up for rescheduling). */ + *flag = 0; + timersub(&head->due, &now, &timeout); + } + pthread_mutex_unlock(&mutex_list); } return NULL; @@ -58,6 +101,14 @@ flag = shared; + result = pipe(pipefd); + if (result != 0) + PISA_ERROR("Could not create pipe for scheduler.\n"); + + result = pthread_mutex_init(&mutex_list, NULL); + if (result != 0) + PISA_ERROR("Could not create mutex for scheduler.\n"); + result = pthread_create(&thread, NULL, pisa_sched_main, NULL); if (result != 0) PISA_ERROR("Could not create maintenance thread.\n"); @@ -71,19 +122,62 @@ { pthread_cancel(thread); - /* TODO: free the task list */ + close(pipefd[0]); + close(pipefd[1]); + + pthread_mutex_destroy(&mutex_list); + + while (head) + pisa_sched_remove(head); *flag = 0; } /** + * Remove a task from the list. This function assumes that the mutex is + * locked. + * + * @param task task to be removed from the schedule + */ +static void pisa_sched_remove_internal(pisa_sched_task *task) +{ + pisa_sched_task *cur = head; + + if (task == head) { + head = head->next; + } else { + while (task != cur->next) { + cur = cur->next; + if (cur == NULL) + return; + } + cur->next = cur->next->next; + } + + free(task); +} + +/** * Run scheduled actions. Called by the main thread eventually if we set the * shared variable to a value != 0. Handle all actions that are due and reset * the shared variable. */ void pisa_sched_run(void) { + struct timeval now; + + pthread_mutex_lock(&mutex_list); + + gettimeofday(&now, NULL); + while (head && pisa_time_before(&head->due, &now)) { + head->func(head->data); + pisa_sched_remove_internal(head); + } + *flag = 0; + write(pipefd[1], "0", 1); + + pthread_mutex_unlock(&mutex_list); } /** @@ -95,8 +189,32 @@ */ pisa_sched_task *pisa_sched_add(pisa_sched_func func, struct timeval delay, void *data) { - /* TODO: malloc new task, fill it, add it to the list (ordered) */ - return NULL; + pisa_sched_task *cur = head, *task = malloc(sizeof(pisa_sched_task)); + struct timeval now; + + pthread_mutex_lock(&mutex_list); + + gettimeofday(&now, NULL); + + task->func = func; + task->data = data; + timeradd(&now, &delay, &task->due); + + if (head == NULL || pisa_time_before(&task->due, &head->due)) { + task->next = head; + head = task; + } else { + while (cur->next && pisa_time_before(&cur->next->due, &task->due)) + cur = cur->next; + task->next = cur->next; + cur->next = task; + + } + write(pipefd[1], "0", 1); + + pthread_mutex_unlock(&mutex_list); + + return task; } /** @@ -106,5 +224,10 @@ */ void pisa_sched_remove(pisa_sched_task *task) { - /* TODO: remove from list, free */ + pthread_mutex_unlock(&mutex_list); + + pisa_sched_remove_internal(task); + write(pipefd[1], "0", 1); + + pthread_mutex_unlock(&mutex_list); }