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 43 static 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: */ 50 int 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 75 void 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 93 void 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 106 void 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 129 restart: 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 161 static int 162 suspend_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