/* * 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(); }