1/*-
2 * SPDX-License-Identifier: BSD-3-Clause
3 *
4 * Copyright (c) 1995-1998 John Birrell <jb@cimlogic.com.au>.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the author nor the names of any co-contributors
16 *    may be used to endorse or promote products derived from this software
17 *    without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32#include <sys/cdefs.h>
33__FBSDID("$FreeBSD$");
34
35#include "namespace.h"
36#include <errno.h>
37#include <pthread.h>
38#include <pthread_np.h>
39#include "un-namespace.h"
40
41#include "thr_private.h"
42
43static int suspend_common(struct pthread *, struct pthread *,
44		int);
45
46__weak_reference(_pthread_suspend_np, pthread_suspend_np);
47__weak_reference(_pthread_suspend_all_np, pthread_suspend_all_np);
48
49/* Suspend a thread: */
50int
51_pthread_suspend_np(pthread_t thread)
52{
53	struct pthread *curthread = _get_curthread();
54	int ret;
55
56	/* Suspending the current thread doesn't make sense. */
57	if (thread == _get_curthread())
58		ret = EDEADLK;
59
60	/* Add a reference to the thread: */
61	else if ((ret = _thr_ref_add(curthread, thread, /*include dead*/0))
62	    == 0) {
63		/* Lock the threads scheduling queue: */
64		THR_THREAD_LOCK(curthread, thread);
65		suspend_common(curthread, thread, 1);
66		/* Unlock the threads scheduling queue: */
67		THR_THREAD_UNLOCK(curthread, thread);
68
69		/* Don't forget to remove the reference: */
70		_thr_ref_delete(curthread, thread);
71	}
72	return (ret);
73}
74
75void
76_thr_suspend_all_lock(struct pthread *curthread)
77{
78	int old;
79
80	THR_LOCK_ACQUIRE(curthread, &_suspend_all_lock);
81	while (_single_thread != NULL) {
82		old = _suspend_all_cycle;
83		_suspend_all_waiters++;
84		THR_LOCK_RELEASE(curthread, &_suspend_all_lock);
85		_thr_umtx_wait_uint(&_suspend_all_cycle, old, NULL, 0);
86		THR_LOCK_ACQUIRE(curthread, &_suspend_all_lock);
87		_suspend_all_waiters--;
88	}
89	_single_thread = curthread;
90	THR_LOCK_RELEASE(curthread, &_suspend_all_lock);
91}
92
93void
94_thr_suspend_all_unlock(struct pthread *curthread)
95{
96
97	THR_LOCK_ACQUIRE(curthread, &_suspend_all_lock);
98	_single_thread = NULL;
99	if (_suspend_all_waiters != 0) {
100		_suspend_all_cycle++;
101		_thr_umtx_wake(&_suspend_all_cycle, INT_MAX, 0);
102	}
103	THR_LOCK_RELEASE(curthread, &_suspend_all_lock);
104}
105
106void
107_pthread_suspend_all_np(void)
108{
109	struct pthread *curthread = _get_curthread();
110	struct pthread *thread;
111	int old_nocancel;
112	int ret;
113
114	old_nocancel = curthread->no_cancel;
115	curthread->no_cancel = 1;
116	_thr_suspend_all_lock(curthread);
117	THREAD_LIST_RDLOCK(curthread);
118	TAILQ_FOREACH(thread, &_thread_list, tle) {
119		if (thread != curthread) {
120			THR_THREAD_LOCK(curthread, thread);
121			if (thread->state != PS_DEAD &&
122	      		   !(thread->flags & THR_FLAGS_SUSPENDED))
123			    thread->flags |= THR_FLAGS_NEED_SUSPEND;
124			THR_THREAD_UNLOCK(curthread, thread);
125		}
126	}
127	thr_kill(-1, SIGCANCEL);
128
129restart:
130	TAILQ_FOREACH(thread, &_thread_list, tle) {
131		if (thread != curthread) {
132			/* First try to suspend the thread without waiting */
133			THR_THREAD_LOCK(curthread, thread);
134			ret = suspend_common(curthread, thread, 0);
135			if (ret == 0) {
136				THREAD_LIST_UNLOCK(curthread);
137				/* Can not suspend, try to wait */
138				THR_REF_ADD(curthread, thread);
139				suspend_common(curthread, thread, 1);
140				THR_REF_DEL(curthread, thread);
141				_thr_try_gc(curthread, thread);
142				/* thread lock released */
143
144				THREAD_LIST_RDLOCK(curthread);
145				/*
146				 * Because we were blocked, things may have
147				 * been changed, we have to restart the
148				 * process.
149				 */
150				goto restart;
151			}
152			THR_THREAD_UNLOCK(curthread, thread);
153		}
154	}
155	THREAD_LIST_UNLOCK(curthread);
156	_thr_suspend_all_unlock(curthread);
157	curthread->no_cancel = old_nocancel;
158	_thr_testcancel(curthread);
159}
160
161static int
162suspend_common(struct pthread *curthread, struct pthread *thread,
163	int waitok)
164{
165	uint32_t tmp;
166
167	while (thread->state != PS_DEAD &&
168	      !(thread->flags & THR_FLAGS_SUSPENDED)) {
169		thread->flags |= THR_FLAGS_NEED_SUSPEND;
170		/* Thread is in creation. */
171		if (thread->tid == TID_TERMINATED)
172			return (1);
173		tmp = thread->cycle;
174		_thr_send_sig(thread, SIGCANCEL);
175		THR_THREAD_UNLOCK(curthread, thread);
176		if (waitok) {
177			_thr_umtx_wait_uint(&thread->cycle, tmp, NULL, 0);
178			THR_THREAD_LOCK(curthread, thread);
179		} else {
180			THR_THREAD_LOCK(curthread, thread);
181			return (0);
182		}
183	}
184
185	return (1);
186}
187