]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/libthr/thread/thr_suspend_np.c
zfs: merge openzfs/zfs@a4bf6baae
[FreeBSD/FreeBSD.git] / lib / libthr / thread / thr_suspend_np.c
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 }