示例#1
0
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);
}
示例#2
0
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, &current);
                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, &current) > 0) {
		    SUB_MACH_TIMESPEC(&completion, &current);
		    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;
}
示例#5
0
/*
 * 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;
}