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 
31 #include "namespace.h"
32 #include <errno.h>
33 #include <pthread.h>
34 #include <pthread_np.h>
35 #include "un-namespace.h"
36 
37 #include "thr_private.h"
38 
39 static int suspend_common(struct pthread *, struct pthread *,
40 		int);
41 
42 /* Suspend a thread: */
43 int
44 _pthread_suspend_np(pthread_t thread)
45 {
46 	struct pthread *curthread = tls_get_curthread();
47 	int ret;
48 
49 	/* Suspending the current thread doesn't make sense. */
50 	if (thread == curthread)
51 		ret = EDEADLK;
52 
53 	/* Add a reference to the thread: */
54 	else if ((ret = _thr_ref_add(curthread, thread, /*include dead*/0))
55 	    == 0) {
56 		/* Lock the threads scheduling queue: */
57 		THR_THREAD_LOCK(curthread, thread);
58 		suspend_common(curthread, thread, 1);
59 		/* Unlock the threads scheduling queue: */
60 		THR_THREAD_UNLOCK(curthread, thread);
61 
62 		/* Don't forget to remove the reference: */
63 		_thr_ref_delete(curthread, thread);
64 	}
65 	return (ret);
66 }
67 
68 void
69 _pthread_suspend_all_np(void)
70 {
71 	struct pthread *curthread = tls_get_curthread();
72 	struct pthread *thread;
73 	int ret;
74 
75 	THREAD_LIST_LOCK(curthread);
76 
77 	TAILQ_FOREACH(thread, &_thread_list, tle) {
78 		if (thread != curthread) {
79 			THR_THREAD_LOCK(curthread, thread);
80 			if (thread->state != PS_DEAD &&
81 			   !(thread->flags & THR_FLAGS_SUSPENDED))
82 			    thread->flags |= THR_FLAGS_NEED_SUSPEND;
83 			THR_THREAD_UNLOCK(curthread, thread);
84 		}
85 	}
86 	/* thr_kill(-1, SIGCANCEL); */
87 
88 restart:
89 	TAILQ_FOREACH(thread, &_thread_list, tle) {
90 		if (thread != curthread) {
91 			/* First try to suspend the thread without waiting */
92 			THR_THREAD_LOCK(curthread, thread);
93 			ret = suspend_common(curthread, thread, 0);
94 			if (ret == 0) {
95 				/* Can not suspend, try to wait */
96 				thread->refcount++;
97 				THREAD_LIST_UNLOCK(curthread);
98 				suspend_common(curthread, thread, 1);
99 				THR_THREAD_UNLOCK(curthread, thread);
100 				THREAD_LIST_LOCK(curthread);
101 				_thr_ref_delete_unlocked(curthread, thread);
102 				/*
103 				 * Because we were blocked, things may have
104 				 * been changed, we have to restart the
105 				 * process.
106 				 */
107 				goto restart;
108 			}
109 			THR_THREAD_UNLOCK(curthread, thread);
110 		}
111 	}
112 
113 	THREAD_LIST_UNLOCK(curthread);
114 }
115 
116 static int
117 suspend_common(struct pthread *curthread, struct pthread *thread,
118 	int waitok)
119 {
120 	umtx_t tmp;
121 
122 	while (thread->state != PS_DEAD &&
123 	      !(thread->flags & THR_FLAGS_SUSPENDED)) {
124 		thread->flags |= THR_FLAGS_NEED_SUSPEND;
125 		tmp = thread->cycle;
126 		_thr_send_sig(thread, SIGCANCEL);
127 		THR_THREAD_UNLOCK(curthread, thread);
128 		if (waitok) {
129 			_thr_umtx_wait(&thread->cycle, tmp, NULL, 0);
130 			THR_THREAD_LOCK(curthread, thread);
131 		} else {
132 			THR_THREAD_LOCK(curthread, thread);
133 			return (0);
134 		}
135 	}
136 
137 	return (1);
138 }
139 
140 __strong_reference(_pthread_suspend_np, pthread_suspend_np);
141 __strong_reference(_pthread_suspend_all_np, pthread_suspend_all_np);
142