xref: /freebsd/lib/libthr/thread/thr_suspend_np.c (revision 02e9120893770924227138ba49df1edb3896112a)
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 #include "namespace.h"
34 #include <errno.h>
35 #include <pthread.h>
36 #include <pthread_np.h>
37 #include "un-namespace.h"
38 
39 #include "thr_private.h"
40 
41 static int suspend_common(struct pthread *, struct pthread *,
42 		int);
43 
44 __weak_reference(_pthread_suspend_np, pthread_suspend_np);
45 __weak_reference(_pthread_suspend_all_np, pthread_suspend_all_np);
46 
47 /* Suspend a thread: */
48 int
49 _pthread_suspend_np(pthread_t thread)
50 {
51 	struct pthread *curthread = _get_curthread();
52 	int ret;
53 
54 	/* Suspending the current thread doesn't make sense. */
55 	if (thread == _get_curthread())
56 		ret = EDEADLK;
57 
58 	/* Add a reference to the thread: */
59 	else if ((ret = _thr_ref_add(curthread, thread, /*include dead*/0))
60 	    == 0) {
61 		/* Lock the threads scheduling queue: */
62 		THR_THREAD_LOCK(curthread, thread);
63 		suspend_common(curthread, thread, 1);
64 		/* Unlock the threads scheduling queue: */
65 		THR_THREAD_UNLOCK(curthread, thread);
66 
67 		/* Don't forget to remove the reference: */
68 		_thr_ref_delete(curthread, thread);
69 	}
70 	return (ret);
71 }
72 
73 void
74 _thr_suspend_all_lock(struct pthread *curthread)
75 {
76 	int old;
77 
78 	THR_LOCK_ACQUIRE(curthread, &_suspend_all_lock);
79 	while (_single_thread != NULL) {
80 		old = _suspend_all_cycle;
81 		_suspend_all_waiters++;
82 		THR_LOCK_RELEASE(curthread, &_suspend_all_lock);
83 		_thr_umtx_wait_uint(&_suspend_all_cycle, old, NULL, 0);
84 		THR_LOCK_ACQUIRE(curthread, &_suspend_all_lock);
85 		_suspend_all_waiters--;
86 	}
87 	_single_thread = curthread;
88 	THR_LOCK_RELEASE(curthread, &_suspend_all_lock);
89 }
90 
91 void
92 _thr_suspend_all_unlock(struct pthread *curthread)
93 {
94 
95 	THR_LOCK_ACQUIRE(curthread, &_suspend_all_lock);
96 	_single_thread = NULL;
97 	if (_suspend_all_waiters != 0) {
98 		_suspend_all_cycle++;
99 		_thr_umtx_wake(&_suspend_all_cycle, INT_MAX, 0);
100 	}
101 	THR_LOCK_RELEASE(curthread, &_suspend_all_lock);
102 }
103 
104 void
105 _pthread_suspend_all_np(void)
106 {
107 	struct pthread *curthread = _get_curthread();
108 	struct pthread *thread;
109 	int old_nocancel;
110 	int ret;
111 
112 	old_nocancel = curthread->no_cancel;
113 	curthread->no_cancel = 1;
114 	_thr_suspend_all_lock(curthread);
115 	THREAD_LIST_RDLOCK(curthread);
116 	TAILQ_FOREACH(thread, &_thread_list, tle) {
117 		if (thread != curthread) {
118 			THR_THREAD_LOCK(curthread, thread);
119 			if (thread->state != PS_DEAD &&
120 	      		   !(thread->flags & THR_FLAGS_SUSPENDED))
121 			    thread->flags |= THR_FLAGS_NEED_SUSPEND;
122 			THR_THREAD_UNLOCK(curthread, thread);
123 		}
124 	}
125 	thr_kill(-1, SIGCANCEL);
126 
127 restart:
128 	TAILQ_FOREACH(thread, &_thread_list, tle) {
129 		if (thread != curthread) {
130 			/* First try to suspend the thread without waiting */
131 			THR_THREAD_LOCK(curthread, thread);
132 			ret = suspend_common(curthread, thread, 0);
133 			if (ret == 0) {
134 				THREAD_LIST_UNLOCK(curthread);
135 				/* Can not suspend, try to wait */
136 				THR_REF_ADD(curthread, thread);
137 				suspend_common(curthread, thread, 1);
138 				THR_REF_DEL(curthread, thread);
139 				_thr_try_gc(curthread, thread);
140 				/* thread lock released */
141 
142 				THREAD_LIST_RDLOCK(curthread);
143 				/*
144 				 * Because we were blocked, things may have
145 				 * been changed, we have to restart the
146 				 * process.
147 				 */
148 				goto restart;
149 			}
150 			THR_THREAD_UNLOCK(curthread, thread);
151 		}
152 	}
153 	THREAD_LIST_UNLOCK(curthread);
154 	_thr_suspend_all_unlock(curthread);
155 	curthread->no_cancel = old_nocancel;
156 	_thr_testcancel(curthread);
157 }
158 
159 static int
160 suspend_common(struct pthread *curthread, struct pthread *thread,
161 	int waitok)
162 {
163 	uint32_t tmp;
164 
165 	while (thread->state != PS_DEAD &&
166 	      !(thread->flags & THR_FLAGS_SUSPENDED)) {
167 		thread->flags |= THR_FLAGS_NEED_SUSPEND;
168 		/* Thread is in creation. */
169 		if (thread->tid == TID_TERMINATED)
170 			return (1);
171 		tmp = thread->cycle;
172 		_thr_send_sig(thread, SIGCANCEL);
173 		THR_THREAD_UNLOCK(curthread, thread);
174 		if (waitok) {
175 			_thr_umtx_wait_uint(&thread->cycle, tmp, NULL, 0);
176 			THR_THREAD_LOCK(curthread, thread);
177 		} else {
178 			THR_THREAD_LOCK(curthread, thread);
179 			return (0);
180 		}
181 	}
182 
183 	return (1);
184 }
185