/* * The scheduler is called with the context of the current thread, * or NULL when the scheduler is first started. * * The general operation of this function is: * 1. Save the context argument into the current thread. * 2. Either queue up or deallocate the current thread, * based on its state. * 3. Select a new "ready" thread to run, and set the "current" * variable to that thread. * - If no "ready" thread is available, examine the system * state to handle this situation properly. * 4. Return the context of the thread to run, so that a context- * switch will be performed to start running the next thread. * * This function is global because it needs to be called from the assembly. */ ThreadContext *__sthread_scheduler(ThreadContext *context) { // Check if we actually have a context to work with if (context != NULL) { // 1. Save the context argument into the current thread current->context = context; // 2. Either queue up or deallocate the current thread, based on its state switch (current->state) { case ThreadFinished: // Delete the thread __sthread_delete(current); break; case ThreadRunning: // Change state of thread, and queue it current->state = ThreadReady; queue_add(current); break; case ThreadBlocked: // Queue thread queue_add(current); break; case ThreadReady: // Should not be switching from a ready thread printf("ERROR: Switching from a ready thread\n"); assert(0); break; } } // 3. Select a new "ready" thread to run, and set the "current" variable // to that thread. If there are no ready threads and there are no blocked // threads, all threads in the program have successfully completed. However, // if there are one more more blocked threads in the blocked queue, the // program has become deadblocked. current = queue_take(&ready_queue); if (current == NULL && queue_empty(&blocked_queue)) { printf("All threads in the program have successfully completed, exiting\n"); exit(0); } else if (current == NULL && !queue_empty(&blocked_queue)) { printf("Program has become deadlocked, exiting\n"); exit(1); } current->state = ThreadRunning; // 4. Return the next thread to resume executing. return current->context; }
/* * The scheduler is called with the context of the current thread, * or NULL when the scheduler is first started. * * The general operation of this function is: * 1. Save the context argument into the current thread. * 2. Either queue up or deallocate the current thread, * based on its state. * 3. Select a new "ready" thread to run, and set the "current" * variable to that thread. * - If no "ready" thread is available, examine the system * state to handle this situation properly. * 4. Return the context of the thread to run, so that a context- * switch will be performed to start running the next thread. * * This function is global because it needs to be called from the assembly. */ ThreadContext *__sthread_scheduler(ThreadContext *context) { if (context != NULL) { /* Save the context argument into the current thread. */ current->context = context; /* Either queue up or deallocate current thread, based on its state. */ if (current->state == ThreadFinished) { /* Deallocatate because finished. */ __sthread_delete(current); } else if (current->state == ThreadBlocked) { /* Queue up because blocked. */ queue_add(current); } else if (current->state == ThreadRunning) { /* Queue up because yielding and make it ready. */ current->state = ThreadReady; queue_add(current); } } if (queue_empty(&ready_queue)) { if (queue_empty(&blocked_queue)) { /* Nothing ready and nothing blocked, so done! */ printf("Done.\n"); exit(0); } else { /* Nothing ready but still blocked threads, so deadlock. */ printf("Deadlock.\n"); /* Exit with error. */ exit(1); } } else { /* Select a new "ready" thread to run, and set the "current" variable to * that thread. */ current = queue_take(&ready_queue); current->state = ThreadRunning; } return current->context; }
/* * The scheduler is called with the context of the current thread, * or NULL when the scheduler is first started. * * The general operation of this function is: * 1. Save the context argument into the current thread. * 2. Either queue up or deallocate the current thread, * based on its state. * 3. Select a new "ready" thread to run, and set the "current" * variable to that thread. * - If no "ready" thread is available, examine the system * state to handle this situation properly. * 4. Return the context of the thread to run, so that a context- * switch will be performed to start running the next thread. * * This function is global because it needs to be called from the assembly. */ ThreadContext *__sthread_scheduler(ThreadContext *context) { /* Add the current thread to the ready queue */ if (context != NULL) { assert(current != NULL); if (current->state == ThreadRunning) current->state = ThreadReady; if (current->state != ThreadFinished) { current->context = context; queue_add(current); } else { __sthread_delete(current); } } /* * Choose a new process from the ready queue. * Abort if the queue is empty. */ current = queue_take(&ready_queue); if (current == NULL) { if (queue_empty(&blocked_queue)) { fprintf(stderr, "All threads completed, exiting.\n"); exit(0); } else { fprintf(stderr, "The system is deadlocked!\n"); exit(1); } } current->state = ThreadRunning; /* Return the next thread to resume executing. */ return current->context; }
/* * The scheduler is called with the context of the current thread, * or NULL when the scheduler is first started. * * The general operation of this function is: * 1. Save the context argument into the current thread. * 2. Either queue up or deallocate the current thread, * based on its state. * 3. Select a new "ready" thread to run, and set the "current" * variable to that thread. * - If no "ready" thread is available, examine the system * state to handle this situation properly. * 4. Return the context of the thread to run, so that a context- * switch will be performed to start running the next thread. * * This function is global because it needs to be called from the assembly. */ ThreadContext *__sthread_scheduler(ThreadContext *context) { /* If this is not the first call to scheduler (= no current thread), * 1. Save the context argument into the current thread. */ if (context != NULL) { current->context = context; /* 2. Queue up or deallocate the current thread. */ if (current->state == ThreadRunning) { current->state = ThreadReady; queue_add(current); } else if (current->state == ThreadBlocked) { queue_add(current); } else if (current->state == ThreadFinished) { __sthread_delete(current); } } /* 3. Select a new 'ready' thread to run. */ current = queue_take(&ready_queue); /* No Ready threads. */ if (current == NULL && queue_take(&blocked_queue) == NULL) { fprintf(stderr, "All threads finished successfully.\n"); exit(0); } else if (current == NULL) { fprintf(stderr, "Program deadlocked!\n"); exit(1); } current->state = ThreadRunning; /* 4. Return the next thread to resume executing. */ return current->context; }