Esempio n. 1
0
/*
 * Find a thread in the linked list of active threads and add a reference
 * to it.  Threads with positive reference counts will not be deallocated
 * until all references are released.
 */
int
_thr_ref_add(struct pthread *curthread, struct pthread *thread,
    int include_dead)
{
	kse_critical_t crit;
	struct pthread *pthread;
	struct kse *curkse;

	if (thread == NULL)
		/* Invalid thread: */
		return (EINVAL);

	crit = _kse_critical_enter();
	curkse = _get_curkse();
	KSE_LOCK_ACQUIRE(curkse, &_thread_list_lock);
	pthread = _thr_hash_find(thread);
	if (pthread) {
		if ((include_dead == 0) &&
		    ((pthread->state == PS_DEAD) ||
		    ((pthread->state == PS_DEADLOCK) ||
		    ((pthread->flags & THR_FLAGS_EXITING) != 0))))
			pthread = NULL;
		else {
			pthread->refcount++;
			if (curthread != NULL)
				curthread->critical_count++;
		}
	}
	KSE_LOCK_RELEASE(curkse, &_thread_list_lock);
	_kse_critical_leave(crit);

	/* Return zero if the thread exists: */
	return ((pthread != NULL) ? 0 : ESRCH);
}
Esempio n. 2
0
int
_sigpending(sigset_t *set)
{
	struct pthread *curthread = _get_curthread();
	kse_critical_t crit;
	sigset_t sigset;
	int ret = 0;

	/* Check for a null signal set pointer: */
	if (set == NULL) {
		/* Return an invalid argument: */
		ret = EINVAL;
	}
	else {
		if (curthread->attr.flags & PTHREAD_SCOPE_SYSTEM)
			return (__sys_sigpending(set));

		crit = _kse_critical_enter();
		KSE_SCHED_LOCK(curthread->kse, curthread->kseg);
		sigset = curthread->sigpend;
		KSE_SCHED_UNLOCK(curthread->kse, curthread->kseg);
		KSE_LOCK_ACQUIRE(curthread->kse, &_thread_signal_lock);
		SIGSETOR(sigset, _thr_proc_sigpending);
		KSE_LOCK_RELEASE(curthread->kse, &_thread_signal_lock);
		_kse_critical_leave(crit);
		*set = sigset;
	}
	/* Return the completion status: */
	return (ret);
}
Esempio n. 3
0
static void
free_stack(struct pthread_attr *pattr)
{
	struct kse *curkse;
	kse_critical_t crit;

	if ((pattr->flags & THR_STACK_USER) == 0) {
		crit = _kse_critical_enter();
		curkse = _get_curkse();
		KSE_LOCK_ACQUIRE(curkse, &_thread_list_lock);
		/* Stack routines don't use malloc/free. */
		_thr_stack_free(pattr);
		KSE_LOCK_RELEASE(curkse, &_thread_list_lock);
		_kse_critical_leave(crit);
	}
}
Esempio n. 4
0
void
_thr_ref_delete(struct pthread *curthread, struct pthread *thread)
{
	kse_critical_t crit;
	struct kse *curkse;

	if (thread != NULL) {
		crit = _kse_critical_enter();
		curkse = _get_curkse();
		KSE_LOCK_ACQUIRE(curkse, &_thread_list_lock);
		thread->refcount--;
		if (curthread != NULL)
			curthread->critical_count--;
		if ((thread->refcount == 0) &&
		    (thread->tlflags & TLFLAGS_GC_SAFE) != 0)
			THR_GCLIST_ADD(thread);
		KSE_LOCK_RELEASE(curkse, &_thread_list_lock);
		_kse_critical_leave(crit);
	}
}
Esempio n. 5
0
void
_pthread_exit(void *status)
{
	struct pthread *curthread = _get_curthread();
	kse_critical_t crit;
	struct kse *curkse;

	/* Check if this thread is already in the process of exiting: */
	if ((curthread->flags & THR_FLAGS_EXITING) != 0) {
		char msg[128];
		snprintf(msg, sizeof(msg), "Thread %p has called "
		    "pthread_exit() from a destructor. POSIX 1003.1 "
		    "1996 s16.2.5.2 does not allow this!", curthread);
		PANIC(msg);
	}

	/*
	 * Flag this thread as exiting.  Threads should now be prevented
	 * from joining to this thread.
	 */
	THR_SCHED_LOCK(curthread, curthread);
	curthread->flags |= THR_FLAGS_EXITING;
	THR_SCHED_UNLOCK(curthread, curthread);
	
	/*
	 * To avoid signal-lost problem, if signals had already been
	 * delivered to us, handle it. we have already set EXITING flag
	 * so no new signals should be delivered to us.
	 * XXX this is not enough if signal was delivered just before
	 * thread called sigprocmask and masked it! in this case, we
	 * might have to re-post the signal by kill() if the signal
	 * is targeting process (not for a specified thread).
	 * Kernel has same signal-lost problem, a signal may be delivered
	 * to a thread which is on the way to call sigprocmask or thr_exit()!
	 */
	if (curthread->check_pending)
		_thr_sig_check_pending(curthread);
	/* Save the return value: */
	curthread->ret = status;
	while (curthread->cleanup != NULL) {
		_pthread_cleanup_pop(1);
	}
	if (curthread->attr.cleanup_attr != NULL) {
		curthread->attr.cleanup_attr(curthread->attr.arg_attr);
	}
	/* Check if there is thread specific data: */
	if (curthread->specific != NULL) {
		/* Run the thread-specific data destructors: */
		_thread_cleanupspecific();
	}
	if (!_kse_isthreaded())
		exit(0);
	crit = _kse_critical_enter();
	curkse = _get_curkse();
	KSE_LOCK_ACQUIRE(curkse, &_thread_list_lock);
	/* Use thread_list_lock */
	_thread_active_threads--;
	if ((_thread_scope_system <= 0 && _thread_active_threads == 1) ||
	    (_thread_scope_system > 0 && _thread_active_threads == 0)) {
		KSE_LOCK_RELEASE(curkse, &_thread_list_lock);
		_kse_critical_leave(crit);
		exit(0);
		/* Never reach! */
	}
	KSE_LOCK_RELEASE(curkse, &_thread_list_lock);

	/* This thread will never be re-scheduled. */
	KSE_LOCK(curkse);
	THR_SET_STATE(curthread, PS_DEAD);
	_thr_sched_switch_unlocked(curthread);
	/* Never reach! */

	/* This point should not be reached. */
	PANIC("Dead thread has resumed");
}
Esempio n. 6
0
static int
lib_sigtimedwait(const sigset_t *set, siginfo_t *info,
	const struct timespec *timeout)
{
	struct pthread	*curthread = _get_curthread();
	int		ret = 0;
	int		i;
	struct sigwait_data waitdata;
	sigset_t	waitset;
	kse_critical_t  crit;
	siginfo_t	siginfo;

	if (curthread->attr.flags & PTHREAD_SCOPE_SYSTEM) {
		if (info == NULL)
			info = &siginfo;
		return (__sys_sigtimedwait(set, info, timeout));
	}

	/*
	 * Initialize the set of signals that will be waited on:
	 */
	waitset = *set;

	/* These signals can't be waited on. */
	SIGDELSET(waitset, SIGKILL);
	SIGDELSET(waitset, SIGSTOP);

	/*
	 * POSIX says that the _application_ must explicitly install
	 * a dummy handler for signals that are SIG_IGN in order
	 * to sigwait on them. Note that SIG_IGN signals are left in
	 * the mask because a subsequent sigaction could enable an
	 * ignored signal.
	 */

	crit = _kse_critical_enter();
	KSE_SCHED_LOCK(curthread->kse, curthread->kseg);
	for (i = 1; i <= _SIG_MAXSIG; ++i) {
		if (SIGISMEMBER(waitset, i) &&
		    SIGISMEMBER(curthread->sigpend, i)) {
			SIGDELSET(curthread->sigpend, i);
			siginfo = curthread->siginfo[i - 1];
			KSE_SCHED_UNLOCK(curthread->kse,
				curthread->kseg);
			_kse_critical_leave(crit);
			ret = i;
			goto OUT;
		}
	}
	curthread->timeout = 0;
	curthread->interrupted = 0;
	_thr_set_timeout(timeout);
	/* Wait for a signal: */
	siginfo.si_signo = 0;
	waitdata.waitset = &waitset;
	waitdata.siginfo = &siginfo;
	curthread->data.sigwait = &waitdata;
	THR_SET_STATE(curthread, PS_SIGWAIT);
	_thr_sched_switch_unlocked(curthread);
	/*
	 * Return the signal number to the caller:
	 */
	if (siginfo.si_signo > 0) {
		ret = siginfo.si_signo;
	} else {
		if (curthread->interrupted)
			errno = EINTR;
		else if (curthread->timeout)
			errno = EAGAIN;
		ret = -1;
	}
	curthread->timeout = 0;
	curthread->interrupted = 0;
	/*
	 * Probably unnecessary, but since it's in a union struct
	 * we don't know how it could be used in the future.
	 */
	curthread->data.sigwait = NULL;

OUT:
	if (ret > 0 && info != NULL)
		*info = siginfo;

	return (ret);
}
Esempio n. 7
0
#ifndef SYSTEM_SCOPE_ONLY

static void *
sig_daemon(void *arg __unused)
{
	int i;
	kse_critical_t crit;
	struct timespec ts;
	sigset_t set;
	struct kse *curkse;
	struct pthread *curthread = _get_curthread();

	DBG_MSG("signal daemon started(%p)\n", curthread);
	
	curthread->name = strdup("signal thread");
	crit = _kse_critical_enter();
	curkse = _get_curkse();

	/*
	 * Daemon thread is a bound thread and we must be created with
	 * all signals masked
	 */
#if 0	
	SIGFILLSET(set);
	__sys_sigprocmask(SIG_SETMASK, &set, NULL);
#endif	
	__sys_sigpending(&set);
	ts.tv_sec = 0;
	ts.tv_nsec = 0;
	while (1) {
		KSE_LOCK_ACQUIRE(curkse, &_thread_signal_lock);
Esempio n. 8
0
int
_pthread_join(pthread_t pthread, void **thread_return)
{
	struct pthread *curthread = _get_curthread();
	void *tmp;
	kse_critical_t crit;
	int ret = 0;
 
	_thr_cancel_enter(curthread);

	/* Check if the caller has specified an invalid thread: */
	if (pthread == NULL || pthread->magic != THR_MAGIC) {
		/* Invalid thread: */
		_thr_cancel_leave(curthread, 1);
		return (EINVAL);
	}

	/* Check if the caller has specified itself: */
	if (pthread == curthread) {
		/* Avoid a deadlock condition: */
		_thr_cancel_leave(curthread, 1);
		return (EDEADLK);
	}

	/*
	 * Find the thread in the list of active threads or in the
	 * list of dead threads:
	 */
	if ((ret = _thr_ref_add(curthread, pthread, /*include dead*/1)) != 0) {
		/* Return an error: */
		_thr_cancel_leave(curthread, 1);
		return (ESRCH);
	}

	THR_SCHED_LOCK(curthread, pthread);
	/* Check if this thread has been detached: */
	if ((pthread->attr.flags & PTHREAD_DETACHED) != 0) {
		THR_SCHED_UNLOCK(curthread, pthread);
		/* Remove the reference and return an error: */
		_thr_ref_delete(curthread, pthread);
		ret = EINVAL;
	} else {
		/* Lock the target thread while checking its state. */
		if (pthread->state == PS_DEAD) {
			/* Return the thread's return value: */
			tmp = pthread->ret;

			/* Detach the thread. */
			pthread->attr.flags |= PTHREAD_DETACHED;

			/* Unlock the thread. */
			THR_SCHED_UNLOCK(curthread, pthread);

			/*
			 * Remove the thread from the list of active
			 * threads and add it to the GC list.
			 */
			crit = _kse_critical_enter();
			KSE_LOCK_ACQUIRE(curthread->kse, &_thread_list_lock);
			THR_LIST_REMOVE(pthread);
			THR_GCLIST_ADD(pthread);
			KSE_LOCK_RELEASE(curthread->kse, &_thread_list_lock);
			_kse_critical_leave(crit);

			/* Remove the reference. */
			_thr_ref_delete(curthread, pthread);
			if (thread_return != NULL)
				*thread_return = tmp;
		}
		else if (pthread->joiner != NULL) {
			/* Unlock the thread and remove the reference. */
			THR_SCHED_UNLOCK(curthread, pthread);
			_thr_ref_delete(curthread, pthread);

			/* Multiple joiners are not supported. */
			ret = ENOTSUP;
		}
		else {
			/* Set the running thread to be the joiner: */
			pthread->joiner = curthread;

			/* Keep track of which thread we're joining to: */
			curthread->join_status.thread = pthread;

			/* Unlock the thread and remove the reference. */
			THR_SCHED_UNLOCK(curthread, pthread);
			_thr_ref_delete(curthread, pthread);

			THR_SCHED_LOCK(curthread, curthread);
			while (curthread->join_status.thread == pthread) {
				THR_SET_STATE(curthread, PS_JOIN);
				THR_SCHED_UNLOCK(curthread, curthread);
				/* Schedule the next thread: */
				_thr_sched_switch(curthread);
				THR_SCHED_LOCK(curthread, curthread);
			}
			THR_SCHED_UNLOCK(curthread, curthread);

			if ((curthread->cancelflags & THR_CANCELLING) &&
			   !(curthread->cancelflags & PTHREAD_CANCEL_DISABLE)) {
				if (_thr_ref_add(curthread, pthread, 1) == 0) {
					THR_SCHED_LOCK(curthread, pthread);
					pthread->joiner = NULL;
					THR_SCHED_UNLOCK(curthread, pthread);
					_thr_ref_delete(curthread, pthread);
				}
				_pthread_exit(PTHREAD_CANCELED);
			}

			/*
			 * The thread return value and error are set by the
			 * thread we're joining to when it exits or detaches:
			 */
			ret = curthread->join_status.error;
			if ((ret == 0) && (thread_return != NULL))
				*thread_return = curthread->join_status.ret;
		}
	}
	_thr_cancel_leave(curthread, 1);

	/* Return the completion status: */
	return (ret);
}
Esempio n. 9
0
/*
 * Some notes on new thread creation and first time initializion
 * to enable multi-threading.
 *
 * There are basically two things that need to be done.
 *
 *   1) The internal library variables must be initialized.
 *   2) Upcalls need to be enabled to allow multiple threads
 *      to be run.
 *
 * The first may be done as a result of other pthread functions
 * being called.  When _thr_initial is null, _libpthread_init is
 * called to initialize the internal variables; this also creates
 * or sets the initial thread.  It'd be nice to automatically
 * have _libpthread_init called on program execution so we don't
 * have to have checks throughout the library.
 *
 * The second part is only triggered by the creation of the first
 * thread (other than the initial/main thread).  If the thread
 * being created is a scope system thread, then a new KSE/KSEG
 * pair needs to be allocated.  Also, if upcalls haven't been
 * enabled on the initial thread's KSE, they must be now that
 * there is more than one thread; this could be delayed until
 * the initial KSEG has more than one thread.
 */
int
_pthread_create(pthread_t * thread, const pthread_attr_t * attr,
	       void *(*start_routine) (void *), void *arg)
{
	struct pthread *curthread, *new_thread;
	struct kse *kse = NULL;
	struct kse_group *kseg = NULL;
	kse_critical_t crit;
	int ret = 0;

	if (_thr_initial == NULL)
		_libpthread_init(NULL);

	/*
	 * Turn on threaded mode, if failed, it is unnecessary to
	 * do further work.
	 */
	if (_kse_isthreaded() == 0 && _kse_setthreaded(1)) {
		return (EAGAIN);
	}
	curthread = _get_curthread();

	/*
	 * Allocate memory for the thread structure.
	 * Some functions use malloc, so don't put it
	 * in a critical region.
	 */
	if ((new_thread = _thr_alloc(curthread)) == NULL) {
		/* Insufficient memory to create a thread: */
		ret = EAGAIN;
	} else {
		/* Check if default thread attributes are required: */
		if (attr == NULL || *attr == NULL)
			/* Use the default thread attributes: */
			new_thread->attr = _pthread_attr_default;
		else {
			new_thread->attr = *(*attr);
			if ((*attr)->sched_inherit == PTHREAD_INHERIT_SCHED) {
				/* inherit scheduling contention scop */
				if (curthread->attr.flags & PTHREAD_SCOPE_SYSTEM)
					new_thread->attr.flags |= PTHREAD_SCOPE_SYSTEM;
				else
					new_thread->attr.flags &= ~PTHREAD_SCOPE_SYSTEM;
				/*
				 * scheduling policy and scheduling parameters will be
				 * inherited in following code.
				 */
			}
		}
		if (_thread_scope_system > 0)
			new_thread->attr.flags |= PTHREAD_SCOPE_SYSTEM;
		else if ((_thread_scope_system < 0)
		    && (thread != &_thr_sig_daemon))
			new_thread->attr.flags &= ~PTHREAD_SCOPE_SYSTEM;
		if (create_stack(&new_thread->attr) != 0) {
			/* Insufficient memory to create a stack: */
			ret = EAGAIN;
			_thr_free(curthread, new_thread);
		}
		else if (((new_thread->attr.flags & PTHREAD_SCOPE_SYSTEM) != 0) &&
		    (((kse = _kse_alloc(curthread, 1)) == NULL)
		    || ((kseg = _kseg_alloc(curthread)) == NULL))) {
			/* Insufficient memory to create a new KSE/KSEG: */
			ret = EAGAIN;
			if (kse != NULL) {
				kse->k_kcb->kcb_kmbx.km_flags |= KMF_DONE;
				_kse_free(curthread, kse);
			}
			free_stack(&new_thread->attr);
			_thr_free(curthread, new_thread);
		}
		else {
			if (kseg != NULL) {
				/* Add the KSE to the KSEG's list of KSEs. */
				TAILQ_INSERT_HEAD(&kseg->kg_kseq, kse, k_kgqe);
				kseg->kg_ksecount = 1;
				kse->k_kseg = kseg;
				kse->k_schedq = &kseg->kg_schedq;
			}
			/*
			 * Write a magic value to the thread structure
			 * to help identify valid ones:
			 */
			new_thread->magic = THR_MAGIC;

			new_thread->slice_usec = -1;
			new_thread->start_routine = start_routine;
			new_thread->arg = arg;
			new_thread->cancelflags = PTHREAD_CANCEL_ENABLE |
			    PTHREAD_CANCEL_DEFERRED;

			/* No thread is wanting to join to this one: */
			new_thread->joiner = NULL;

			/*
			 * Initialize the machine context.
			 * Enter a critical region to get consistent context.
			 */
			crit = _kse_critical_enter();
			THR_GETCONTEXT(&new_thread->tcb->tcb_tmbx.tm_context);
			/* Initialize the thread for signals: */
			new_thread->sigmask = curthread->sigmask;
			_kse_critical_leave(crit);

			new_thread->tcb->tcb_tmbx.tm_udata = new_thread;
			new_thread->tcb->tcb_tmbx.tm_context.uc_sigmask =
			    new_thread->sigmask;
			new_thread->tcb->tcb_tmbx.tm_context.uc_stack.ss_size =
			    new_thread->attr.stacksize_attr;
			new_thread->tcb->tcb_tmbx.tm_context.uc_stack.ss_sp =
			    new_thread->attr.stackaddr_attr;
			makecontext(&new_thread->tcb->tcb_tmbx.tm_context,
			    (void (*)(void))thread_start, 3, new_thread,
			    start_routine, arg);
			/*
			 * Check if this thread is to inherit the scheduling
			 * attributes from its parent:
			 */
			if (new_thread->attr.sched_inherit == PTHREAD_INHERIT_SCHED) {
				/*
				 * Copy the scheduling attributes.
				 * Lock the scheduling lock to get consistent
				 * scheduling parameters.
				 */
				THR_SCHED_LOCK(curthread, curthread);
				new_thread->base_priority =
				    curthread->base_priority &
				    ~THR_SIGNAL_PRIORITY;
				new_thread->attr.prio =
				    curthread->base_priority &
				    ~THR_SIGNAL_PRIORITY;
				new_thread->attr.sched_policy =
				    curthread->attr.sched_policy;
				THR_SCHED_UNLOCK(curthread, curthread);
			} else {
				/*
				 * Use just the thread priority, leaving the
				 * other scheduling attributes as their
				 * default values:
				 */
				new_thread->base_priority =
				    new_thread->attr.prio;
			}
			new_thread->active_priority = new_thread->base_priority;
			new_thread->inherited_priority = 0;

			/* Initialize the mutex queue: */
			TAILQ_INIT(&new_thread->mutexq);

			/* Initialise hooks in the thread structure: */
			new_thread->specific = NULL;
			new_thread->specific_data_count = 0;
			new_thread->cleanup = NULL;
			new_thread->flags = 0;
			new_thread->tlflags = 0;
			new_thread->sigbackout = NULL;
			new_thread->continuation = NULL;
			new_thread->wakeup_time.tv_sec = -1;
			new_thread->lock_switch = 0;
			sigemptyset(&new_thread->sigpend);
			new_thread->check_pending = 0;
			new_thread->locklevel = 0;
			new_thread->rdlock_count = 0;
			new_thread->sigstk.ss_sp = 0;
			new_thread->sigstk.ss_size = 0;
			new_thread->sigstk.ss_flags = SS_DISABLE;
			new_thread->oldsigmask = NULL;

			if (new_thread->attr.suspend == THR_CREATE_SUSPENDED) {
				new_thread->state = PS_SUSPENDED;
				new_thread->flags = THR_FLAGS_SUSPENDED;
			}
			else
				new_thread->state = PS_RUNNING;

			/*
			 * System scope threads have their own kse and
			 * kseg.  Process scope threads are all hung
			 * off the main process kseg.
			 */
			if ((new_thread->attr.flags & PTHREAD_SCOPE_SYSTEM) == 0) {
				new_thread->kseg = _kse_initial->k_kseg;
				new_thread->kse = _kse_initial;
			}
			else {
				kse->k_curthread = NULL;
				kse->k_kseg->kg_flags |= KGF_SINGLE_THREAD;
				new_thread->kse = kse;
				new_thread->kseg = kse->k_kseg;
				kse->k_kcb->kcb_kmbx.km_udata = kse;
				kse->k_kcb->kcb_kmbx.km_curthread = NULL;
			}

			/*
			 * Schedule the new thread starting a new KSEG/KSE
			 * pair if necessary.
			 */
			ret = _thr_schedule_add(curthread, new_thread);
			if (ret != 0)
				free_thread(curthread, new_thread);
			else {
				/* Return a pointer to the thread structure: */
				(*thread) = new_thread;
			}
		}
	}

	/* Return the status: */
	return (ret);
}
Esempio n. 10
0
int
_sigaltstack(stack_t *_ss, stack_t *_oss)
{
	struct pthread *curthread = _get_curthread();
	stack_t ss, oss;
	int oonstack, errsave, ret;
	kse_critical_t crit;

	if (curthread->attr.flags & PTHREAD_SCOPE_SYSTEM) {
		crit = _kse_critical_enter();
		ret = __sys_sigaltstack(_ss, _oss);
		errsave = errno;
		/* Get a copy */
		if (ret == 0 && _ss != NULL)
			curthread->sigstk = *_ss;
		_kse_critical_leave(crit);
		errno = errsave;
		return (ret);
	}

	if (_ss)
		ss = *_ss;
	if (_oss)
		oss = *_oss;

	/* Should get and set stack in atomic way */
	crit = _kse_critical_enter();
	oonstack = _thr_sigonstack(&ss);
	if (_oss != NULL) {
		oss = curthread->sigstk;
		oss.ss_flags = (curthread->sigstk.ss_flags & SS_DISABLE)
		    ? SS_DISABLE : ((oonstack) ? SS_ONSTACK : 0);
	}

	if (_ss != NULL) {
		if (oonstack) {
			_kse_critical_leave(crit);
			errno = EPERM;
			return (-1);
		}
		if ((ss.ss_flags & ~SS_DISABLE) != 0) {
			_kse_critical_leave(crit);
			errno = EINVAL;
			return (-1);
		}
		if (!(ss.ss_flags & SS_DISABLE)) {
			if (ss.ss_size < MINSIGSTKSZ) {
				_kse_critical_leave(crit);
				errno = ENOMEM;
				return (-1);
			}
			curthread->sigstk = ss;
		} else {
			curthread->sigstk.ss_flags |= SS_DISABLE;
		}
	}
	_kse_critical_leave(crit);
	if (_oss != NULL)
		*_oss = oss;
	return (0);
}