int _sem_wait(sem_t *sem) { struct pthread *curthread; int retval; if (sem_check_validity(sem) != 0) return (-1); curthread = _get_curthread(); if ((*sem)->syssem != 0) { _thr_cancel_enter(curthread); retval = ksem_wait((*sem)->semid); _thr_cancel_leave(curthread, retval != 0); } else { _pthread_testcancel(); _pthread_mutex_lock(&(*sem)->lock); while ((*sem)->count <= 0) { (*sem)->nwaiters++; THR_CLEANUP_PUSH(curthread, decrease_nwaiters, sem); _pthread_cond_wait(&(*sem)->gtzero, &(*sem)->lock); THR_CLEANUP_POP(curthread, 0); (*sem)->nwaiters--; } (*sem)->count--; _pthread_mutex_unlock(&(*sem)->lock); retval = 0; } return (retval); }
int nanosleep(const struct timespec *requested_time, struct timespec *remaining_time) { kern_return_t kret; int ret; mach_timespec_t current; mach_timespec_t completion; if (__unix_conforming == 0) __unix_conforming = 1; #ifdef VARIANT_CANCELABLE _pthread_testcancel(pthread_self(), 1); #endif /* VARIANT_CANCELABLE */ if ((requested_time == NULL) || (requested_time->tv_sec < 0) || (requested_time->tv_nsec >= NSEC_PER_SEC)) { errno = EINVAL; return -1; } if (remaining_time != NULL) { /* once we add requested_time, this will be the completion time */ kret = clock_get_time(clock_port, &completion); if (kret != KERN_SUCCESS) { fprintf(stderr, "clock_get_time() failed: %s\n", mach_error_string(kret)); errno = EINVAL; return -1; } } ret = SEMWAIT_SIGNAL(clock_sem, MACH_PORT_NULL, 1, 1, (int64_t)requested_time->tv_sec, (int32_t)requested_time->tv_nsec); if (ret < 0) { if (errno == ETIMEDOUT) { return 0; } else if (errno == EINTR) { if (remaining_time != NULL) { ret = clock_get_time(clock_port, ¤t); if (ret != KERN_SUCCESS) { fprintf(stderr, "clock_get_time() failed: %s\n", mach_error_string(ret)); return -1; } /* This depends on the layout of a mach_timespec_t and timespec_t being equivalent */ ADD_MACH_TIMESPEC(&completion, requested_time); /* We have to compare first, since mach_timespect_t contains unsigned integers */ if(CMP_MACH_TIMESPEC(&completion, ¤t) > 0) { SUB_MACH_TIMESPEC(&completion, ¤t); remaining_time->tv_sec = completion.tv_sec; remaining_time->tv_nsec = completion.tv_nsec; } else { bzero(remaining_time, sizeof(*remaining_time)); } } } else { errno = EINVAL; } } return -1; }
int sigwait(const sigset_t * set, int * sig) { #if __DARWIN_UNIX03 int err = 0; if (__unix_conforming == 0) __unix_conforming = 1; #ifdef VARIANT_CANCELABLE _pthread_testcancel(pthread_self(), 1); #endif /* VARIANT_CANCELABLE */ if (__sigwait(set, sig) == -1) { err = errno; /* * EINTR that isn't a result of pthread_cancel() * is translated to 0. */ if (err == EINTR) { err = 0; } } return(err); #else /* __DARWIN_UNIX03 */ if (__sigwait(set, sig) == -1) { /* * EINTR that isn't a result of pthread_cancel() * is translated to 0. */ if (errno != EINTR) { return -1; } } return 0; #endif /* __DARWIN_UNIX03 */ }
/* * Wait for a thread to terminate and obtain its exit value. */ int pthread_join(pthread_t thread, void **value_ptr) { int res = 0; pthread_t self = pthread_self(); mach_port_t kthport; int conforming = 0; #if !__DARWIN_UNIX03 kern_return_t kern_res; #endif #if __DARWIN_UNIX03 if (__unix_conforming == 0) __unix_conforming = 1; #ifdef VARIANT_CANCELABLE _pthread_testcancel(self, 1); #endif /* VARIANT_CANCELABLE */ #endif /* __DARWIN_UNIX03 */ if ((res = _pthread_lookup_thread(thread, &kthport, 1)) != 0) return(res); if (thread->sig == _PTHREAD_SIG) { if (thread->newstyle == 0) { semaphore_t death = new_sem_from_pool(); /* in case we need it */ LOCK(thread->lock); if ((thread->detached & PTHREAD_CREATE_JOINABLE) && thread->death == SEMAPHORE_NULL) { assert(thread->joiner == NULL); if (thread != self && (self == NULL || self->joiner != thread)) { int already_exited = (thread->detached & _PTHREAD_EXITED); thread->death = death; thread->joiner = self; UNLOCK(thread->lock); if (!already_exited) { #if __DARWIN_UNIX03 /* Wait for it to signal... */ pthread_cleanup_push(__posix_join_cleanup, (void *)thread); do { res = __semwait_signal(death, 0, 0, 0, (int64_t)0, (int32_t)0); } while ((res < 0) && (errno == EINTR)); pthread_cleanup_pop(0); #else /* __DARWIN_UNIX03 */ /* Wait for it to signal... */ do { PTHREAD_MACH_CALL(semaphore_wait(death), kern_res); } while (kern_res != KERN_SUCCESS); #endif /* __DARWIN_UNIX03 */ } LOCK(_pthread_list_lock); TAILQ_REMOVE(&__pthread_head, thread, plist); #if WQ_TRACE __kdebug_trace(0x9000010, thread, 0, 0, 16, 0); #endif UNLOCK(_pthread_list_lock); /* ... and wait for it to really be dead */ while ((res = _pthread_reap_thread(thread, thread->kernel_thread, value_ptr, __unix_conforming)) == EAGAIN) { sched_yield(); } } else { UNLOCK(thread->lock); res = EDEADLK; } } else { UNLOCK(thread->lock); res = EINVAL; } restore_sem_to_pool(death); return res; } else { /* new style */ semaphore_t death = SEMAPHORE_NULL; /* in case we need it */ semaphore_t joinsem = SEMAPHORE_NULL; if (thread->joiner_notify == MACH_PORT_NULL) death = new_sem_from_pool(); LOCK(thread->lock); if ((thread->detached & PTHREAD_CREATE_JOINABLE) && (thread->joiner == NULL)) { assert(thread->kernel_thread == kthport); if (thread != self && (self == NULL || self->joiner != thread)) { if (thread->joiner_notify == MACH_PORT_NULL) { if (death == SEMAPHORE_NULL) LIBC_ABORT("thread %p: death == SEMAPHORE_NULL", thread); thread->joiner_notify = death; death = SEMAPHORE_NULL; } joinsem = thread->joiner_notify; thread->joiner = self; UNLOCK(thread->lock); if (death != SEMAPHORE_NULL) { restore_sem_to_pool(death); death = SEMAPHORE_NULL; } #if __DARWIN_UNIX03 /* Wait for it to signal... */ pthread_cleanup_push(__posix_join_cleanup, (void *)thread); do { res = __semwait_signal(joinsem, 0, 0, 0, (int64_t)0, (int32_t)0); } while ((res < 0) && (errno == EINTR)); pthread_cleanup_pop(0); #else /* __DARWIN_UNIX03 */ /* Wait for it to signal... */ do { PTHREAD_MACH_CALL(semaphore_wait(joinsem), kern_res); } while (kern_res != KERN_SUCCESS); #endif /* __DARWIN_UNIX03 */ restore_sem_to_pool(joinsem); res = _pthread_join_cleanup(thread, value_ptr, conforming); } else { UNLOCK(thread->lock); res = EDEADLK; } } else { UNLOCK(thread->lock); res = EINVAL; } if (death != SEMAPHORE_NULL) restore_sem_to_pool(death); return res; }/* end of new style */ } return ESRCH; }
/* * Suspend waiting for a condition variable. * Note: we have to keep a list of condition variables which are using * this same mutex variable so we can detect invalid 'destroy' sequences. * If isconforming < 0, we skip the _pthread_testcancel(), but keep the * remaining conforming behavior.. */ __private_extern__ int _pthread_cond_wait(pthread_cond_t *ocond, pthread_mutex_t *omutex, const struct timespec *abstime, int isRelative, int isconforming) { int res; _pthread_cond *cond = (_pthread_cond *)ocond; _pthread_mutex *mutex = (_pthread_mutex *)omutex; struct timespec then = { 0, 0 }; uint32_t mtxgen, mtxugen, flags=0, updateval; uint32_t lcntval, ucntval, scntval; uint32_t nlval, ulval, savebits; volatile uint32_t *c_lseqcnt, *c_useqcnt, *c_sseqcnt; uint64_t oldval64, newval64, mugen, cvlsgen; uint32_t *npmtx = NULL; extern void _pthread_testcancel(pthread_t thread, int isconforming); res = _pthread_cond_check_init(cond, NULL); if (res != 0) { return res; } if (isconforming) { if (mutex->sig != _PTHREAD_MUTEX_SIG && (mutex->sig & _PTHREAD_MUTEX_SIG_init_MASK) != _PTHREAD_MUTEX_SIG_CMP) { return EINVAL; } if (isconforming > 0) { _pthread_testcancel(pthread_self(), 1); } } /* send relative time to kernel */ if (abstime) { if (isRelative == 0) { struct timespec now; struct timeval tv; __gettimeofday(&tv, NULL); TIMEVAL_TO_TIMESPEC(&tv, &now); /* Compute relative time to sleep */ then.tv_nsec = abstime->tv_nsec - now.tv_nsec; then.tv_sec = abstime->tv_sec - now.tv_sec; if (then.tv_nsec < 0) { then.tv_nsec += NSEC_PER_SEC; then.tv_sec--; } if (then.tv_sec < 0 || (then.tv_sec == 0 && then.tv_nsec == 0)) { return ETIMEDOUT; } if (isconforming && (abstime->tv_sec < 0 || abstime->tv_nsec < 0 || abstime->tv_nsec >= NSEC_PER_SEC)) { return EINVAL; } } else { then.tv_sec = abstime->tv_sec; then.tv_nsec = abstime->tv_nsec; if ((then.tv_sec == 0) && (then.tv_nsec == 0)) { return ETIMEDOUT; } } if (isconforming && (then.tv_sec < 0 || then.tv_nsec < 0)) { return EINVAL; } if (then.tv_nsec >= NSEC_PER_SEC) { return EINVAL; } } if (cond->busy != NULL && cond->busy != mutex) { return EINVAL; } COND_GETSEQ_ADDR(cond, &c_lseqcnt, &c_useqcnt, &c_sseqcnt); do { lcntval = *c_lseqcnt; ucntval = *c_useqcnt; scntval = *c_sseqcnt; oldval64 = (((uint64_t)scntval) << 32); oldval64 |= lcntval; /* remove c and p bits on S word */ savebits = scntval & PTH_RWS_CV_BITSALL; ulval = (scntval & PTHRW_COUNT_MASK); nlval = lcntval + PTHRW_INC; newval64 = (((uint64_t)ulval) << 32); newval64 |= nlval; } while (OSAtomicCompareAndSwap64Barrier(oldval64, newval64, (volatile int64_t *)c_lseqcnt) != TRUE); cond->busy = mutex; res = __mtx_droplock(mutex, &flags, &npmtx, &mtxgen, &mtxugen); /* TBD: cases are for normal (non owner for recursive mutex; error checking)*/ if (res != 0) { return EINVAL; } if ((flags & _PTHREAD_MTX_OPT_NOTIFY) == 0) { npmtx = NULL; mugen = 0; } else { mugen = ((uint64_t)mtxugen << 32) | mtxgen; } flags &= ~_PTHREAD_MTX_OPT_MUTEX; /* reset the mutex bit as this is cvar */ cvlsgen = ((uint64_t)(ulval | savebits)<< 32) | nlval; // SUSv3 requires pthread_cond_wait to be a cancellation point if (isconforming) { pthread_cleanup_push(_pthread_cond_cleanup, (void *)cond); updateval = __psynch_cvwait(ocond, cvlsgen, ucntval, (pthread_mutex_t *)npmtx, mugen, flags, (int64_t)then.tv_sec, (int32_t)then.tv_nsec); _pthread_testcancel(pthread_self(), isconforming); pthread_cleanup_pop(0); } else { updateval = __psynch_cvwait(ocond, cvlsgen, ucntval, (pthread_mutex_t *)npmtx, mugen, flags, (int64_t)then.tv_sec, (int32_t)then.tv_nsec); } if (updateval == (uint32_t)-1) { int err = errno; switch (err & 0xff) { case ETIMEDOUT: res = ETIMEDOUT; break; case EINTR: // spurious wakeup (unless canceled) res = 0; break; default: res = EINVAL; break; } // add unlock ref to show one less waiter _pthread_cond_updateval(cond, err, 0); } else if (updateval != 0) { // Successful wait // The return due to prepost and might have bit states // update S and return for prepo if needed _pthread_cond_updateval(cond, 0, updateval); } pthread_mutex_lock(omutex); return res; }