/* * Yield, so that another thread can run. This is easy: * just call the scheduler, and it will pick a new thread to * run. */ void sthread_yield() { /* This is an entry point to the scheduler. We need the lock to stop * multiple contexts from being saved and restored. */ __sthread_lock(); __sthread_schedule(); }
/* * This function is called automatically when a thread's function returns, * so that the thread can be marked as "finished" and can then be reclaimed. * The scheduler will call __sthread_delete() on the thread before scheduling * a new thread for execution. * * This function is global because it needs to be referenced from assembly. */ void __sthread_finish(void) { /* This was already in the assignment. */ __sthread_lock(); printf("Thread 0x%08x has finished executing.\n", (unsigned int) current); current->state = ThreadFinished; __sthread_schedule(); }
/* * Add an integer to the buffer. Yield control to another * thread if the buffer is full. */ void bounded_buffer_add(BoundedBuffer *bufp, const BufferElem *elem) { // We only want one thread modifying the buffer at a time __sthread_lock(); // Decrement the count of available space (we're adding) // Blocks when available count = 0 (buffer is full) semaphore_wait(bufp->avail); bufp->buffer[(bufp->first + bufp->count) % bufp->length] = *elem; bufp->count++; // Increment the count of used space semaphore_signal(bufp->used); __sthread_unlock(); }
/* * Get an integer from the buffer. Yield control to another * thread if the buffer is empty. */ void bounded_buffer_take(BoundedBuffer *bufp, BufferElem *elem) { // We only want one thread modifying the buffer at a time __sthread_lock(); // Decrement count of used space (we're freeing) // Blocks when there are no used elements (buffer is empty) semaphore_wait(bufp->used); /* Copy the element from the buffer, and clear the record */ *elem = bufp->buffer[bufp->first]; bufp->buffer[bufp->first] = empty; bufp->count--; bufp->first = (bufp->first + 1) % bufp->length; // Increment count of available space semaphore_signal(bufp->avail); __sthread_unlock(); }
/* * Decrement the semaphore. * This operation must be atomic, and it blocks iff the semaphore is zero. */ void semaphore_wait(Semaphore *semp) { /* This must be atomic, so make sure no other threads interfere. */ __sthread_lock(); while (semp->i == 0) { /* Semaphore cannot handle another thread, so block current one. */ /* Add to queue of blocked threads. */ struct thread_node * current = (struct thread_node *) malloc(sizeof(struct thread_node)); /* The thread to be held is the currently executing one. */ current->thread = sthread_current(); /* Will be at the end of queue so next is NULL. */ current->next = NULL; /* Add to queue. */ if (semp->head == NULL) { /* Empty queue, head and tail are the new thread_node. */ semp->head = current; semp->tail = semp->head; } else { /* This will be the next of tail, and new tail. */ semp->tail->next = current; semp->tail = semp->tail->next; } /* Block it. */ sthread_block(); } /* Decrement semaphore count, should still be non-negative. */ semp->i--; assert(semp->i >= 0); /* Can unlock now that operations are done. */ __sthread_unlock(); }
/* * Increment the semaphore. * This operation must be atomic. */ void semaphore_signal(Semaphore *semp) { /* This must be atomic, so make sure no other threads interfere. */ __sthread_lock(); /* Incrememnt semaphore count. */ semp->i++; /* Get next in queue to run. */ if (semp->head == NULL) { /* Queue empty! Do nothing. */ } else { /* Save the current head to free it later. */ struct thread_node * old_head = semp->head; /* Unblock the head. */ sthread_unblock(semp->head->thread); /* Remove from queue and free. */ if (semp->head->next == NULL) { /* If this has no next them queue is empty now. */ semp->head = NULL; semp->tail = NULL; } else { /* Move head to the next. */ semp->head = semp->head->next; } /* Free it. */ free(old_head); } /* Can unlock now that operations are done. */ __sthread_unlock(); }
/* * Block the current thread. Set the state of the current thread * to Blocked, and call the scheduler. */ void sthread_block() { /* This is an entry point to the scheduler. */ __sthread_lock(); current->state = ThreadBlocked; __sthread_schedule(); }