1 /* 2 * Copyright (c) 1995-1998 John Birrell <jb@cimlogic.com.au>. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. Neither the name of the author nor the names of any co-contributors 14 * may be used to endorse or promote products derived from this software 15 * without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 */ 29 30 #include <sys/cdefs.h> 31 __FBSDID("$FreeBSD$"); 32 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