int sem_wait(sem_t * sem) { long oldstatus, newstatus; volatile pthread_t self = thread_self(); pthread_t * th; while (1) { do { oldstatus = sem->sem_status; if ((oldstatus & 1) && (oldstatus != 1)) newstatus = oldstatus - 2; else { newstatus = (long) self; self->p_nextwaiting = (pthread_t) oldstatus; } } while (! compare_and_swap(sem, oldstatus, newstatus)); if (newstatus & 1) /* We got the semaphore. */ return 0; /* Wait for sem_post or cancellation */ suspend_with_cancellation(self); /* This is a cancellation point */ if (self->p_canceled && self->p_cancelstate == PTHREAD_CANCEL_ENABLE) { /* Remove ourselves from the waiting list if we're still on it */ /* First check if we're at the head of the list. */ do { oldstatus = sem->sem_status; if (oldstatus != (long) self) break; newstatus = (long) self->p_nextwaiting; } while (! compare_and_swap(sem, oldstatus, newstatus)); /* Now, check if we're somewhere in the list. There's a race condition with sem_post here, but it does not matter: the net result is that at the time pthread_exit is called, self is no longer reachable from sem->sem_status. */ if (oldstatus != (long) self && (oldstatus & 1) == 0) { th = &(((pthread_t) oldstatus)->p_nextwaiting); while (*th != (pthread_t) 1 && *th != NULL) { if (*th == self) { *th = self->p_nextwaiting; break; } th = &((*th)->p_nextwaiting); } } pthread_exit(PTHREAD_CANCELED); } } }
int pthread_join(pthread_t thread_id, void ** thread_return) { volatile pthread_descr self = thread_self(); struct pthread_request request; pthread_handle handle = thread_handle(thread_id); pthread_descr th; acquire(&handle->h_spinlock); if (invalid_handle(handle, thread_id)) { release(&handle->h_spinlock); return ESRCH; } th = handle->h_descr; if (th == self) { release(&handle->h_spinlock); return EDEADLK; } /* If detached or already joined, error */ if (th->p_detached || th->p_joining != NULL) { release(&handle->h_spinlock); return EINVAL; } /* If not terminated yet, suspend ourselves. */ if (! th->p_terminated) { th->p_joining = self; release(&handle->h_spinlock); suspend_with_cancellation(self); /* This is a cancellation point */ if (self->p_canceled && self->p_cancelstate == PTHREAD_CANCEL_ENABLE) { th->p_joining = NULL; pthread_exit(PTHREAD_CANCELED); } acquire(&handle->h_spinlock); } /* Get return value */ if (thread_return != NULL) *thread_return = th->p_retval; release(&handle->h_spinlock); /* Send notification to thread manager */ if (__pthread_manager_request >= 0) { request.req_thread = self; request.req_kind = REQ_FREE; request.req_args.free.thread = th; write(__pthread_manager_request, (char *) &request, sizeof(request)); } return 0; }