Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(3)

Side by Side Diff: src/trusted/service_runtime/linux/thread_suspension.c

Issue 10392005: Thread suspension: Implement for Linux (Closed) Base URL: svn://svn.chromium.org/native_client/trunk/src/native_client
Patch Set: Comment about docs Created 8 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 /*
2 * Copyright (c) 2012 The Native Client Authors. All rights reserved.
3 * Use of this source code is governed by a BSD-style license that can be
4 * found in the LICENSE file.
5 */
6
7 #include <errno.h>
8 #include <linux/futex.h>
9 #include <signal.h>
10 #include <sys/syscall.h>
11
12 #include "native_client/src/shared/platform/nacl_check.h"
13 #include "native_client/src/shared/platform/nacl_exit.h"
14 #include "native_client/src/shared/platform/nacl_sync_checked.h"
15 #include "native_client/src/trusted/service_runtime/nacl_app_thread.h"
16 #include "native_client/src/trusted/service_runtime/nacl_globals.h"
17 #include "native_client/src/trusted/service_runtime/nacl_tls.h"
18 #include "native_client/src/trusted/service_runtime/sel_ldr.h"
19
20
21 /*
22 * If |*addr| still contains |value|, this waits to be woken up by a
23 * FutexWake(addr,...) call from another thread; otherwise, it returns
24 * immediately.
25 *
26 * Note that if this is interrupted by a signal, the system call will
27 * get restarted, but it will recheck whether |*addr| still contains
28 * |value|.
29 *
30 * We use the *_PRIVATE variant to use process-local futexes which are
31 * slightly faster than shared futexes. (Private futexes are
32 * well-known but are not covered by the Linux man page for futex(),
33 * which is very out-of-date.)
34 */
35 static void FutexWait(Atomic32 *addr, Atomic32 value) {
36 if (syscall(SYS_futex, addr, FUTEX_WAIT_PRIVATE, value, 0, 0, 0) != 0) {
37 /*
38 * We get EWOULDBLOCK if *addr != value (EAGAIN is a synonym).
39 * We get EINTR if interrupted by a signal.
40 */
41 if (errno != EINTR && errno != EWOULDBLOCK) {
42 NaClLog(LOG_FATAL, "FutexWait: futex() failed with error %d\n", errno);
43 }
44 }
45 }
46
47 /*
48 * This wakes up threads that are waiting on |addr| using FutexWait().
49 * |waiters| is the maximum number of threads that will be woken up.
50 */
51 static void FutexWake(Atomic32 *addr, int waiters) {
52 if (syscall(SYS_futex, addr, FUTEX_WAKE_PRIVATE, waiters, 0, 0, 0) < 0) {
53 NaClLog(LOG_FATAL, "FutexWake: futex() failed with error %d\n", errno);
54 }
55 }
56
57 void NaClAppThreadSetSuspendState(struct NaClAppThread *natp,
58 enum NaClSuspendState old_state,
59 enum NaClSuspendState new_state) {
60 while (1) {
61 Atomic32 state = natp->suspend_state;
62 if ((state & NACL_APP_THREAD_SUSPENDING) != 0) {
63 /* We have been asked to suspend, so wait. */
64 FutexWait(&natp->suspend_state, state);
65 continue; /* Retry */
66 }
67
68 CHECK(state == (Atomic32) old_state);
69 if (CompareAndSwap(&natp->suspend_state, old_state, new_state)
70 != (Atomic32) old_state) {
71 continue; /* Retry */
72 }
73 break;
74 }
75 }
76
77 void NaClSuspendSignalHandler(void) {
78 uint32_t tls_idx = NaClTlsGetIdx();
79 struct NaClAppThread *natp = nacl_thread[tls_idx];
80
81 /*
82 * Indicate that we have suspended by setting
83 * NACL_APP_THREAD_SUSPENDED. We should not need an atomic
84 * operation for this since the calling thread will not be trying to
85 * change suspend_state.
86 */
87 if (natp->suspend_state != (NACL_APP_THREAD_UNTRUSTED |
88 NACL_APP_THREAD_SUSPENDING)) {
89 NaClSignalErrorMessage("NaClSuspendSignalHandler: "
90 "Unexpected suspend_state\n");
91 NaClAbort();
92 }
93 natp->suspend_state |= NACL_APP_THREAD_SUSPENDED;
94 FutexWake(&natp->suspend_state, 1);
95
96 /* Wait until we are asked to resume. */
97 while (1) {
98 Atomic32 state = natp->suspend_state;
99 if ((state & NACL_APP_THREAD_SUSPENDED) != 0) {
100 FutexWait(&natp->suspend_state, state);
101 continue; /* Retry */
102 }
103 break;
104 }
105 }
106
107 /* Wait for the thread to indicate that it has suspended. */
108 static void WaitForUntrustedThreadToSuspend(struct NaClAppThread *natp) {
109 const Atomic32 kBaseState = (NACL_APP_THREAD_UNTRUSTED |
110 NACL_APP_THREAD_SUSPENDING);
111 while (1) {
112 Atomic32 state = natp->suspend_state;
113 if (state == kBaseState) {
114 FutexWait(&natp->suspend_state, state);
115 continue; /* Retry */
116 }
117 if (state != (kBaseState | NACL_APP_THREAD_SUSPENDED)) {
118 NaClLog(LOG_FATAL, "Unexpected state: %d\n", state);
119 }
120 break;
121 }
122 }
123
124 void NaClUntrustedThreadSuspend(struct NaClAppThread *natp) {
125 Atomic32 old_state;
126 Atomic32 suspending_state;
127
128 /*
129 * Note that if we are being called from a NaCl syscall (which is
130 * likely), natp could be the thread we are running in. That is
131 * fine, because this thread will be in the NACL_APP_THREAD_TRUSTED
132 * state, and so we will not try to interrupt it.
133 */
134
135 /*
136 * We do not want the thread to enter a NaCl syscall and start
137 * taking locks when pthread_kill() takes effect, so we ask the
138 * thread to suspend even if it is currently running untrusted code.
139 */
140 while (1) {
141 old_state = natp->suspend_state;
142 DCHECK((old_state & NACL_APP_THREAD_SUSPENDING) == 0);
143 suspending_state = old_state | NACL_APP_THREAD_SUSPENDING;
144 if (CompareAndSwap(&natp->suspend_state, old_state, suspending_state)
145 != old_state) {
146 continue; /* Retry */
147 }
148 break;
149 }
150 /*
151 * Once the thread has NACL_APP_THREAD_SUSPENDING set, it may not
152 * change state itself, so there should be no race condition in this
153 * check.
154 */
155 DCHECK(natp->suspend_state == suspending_state);
156
157 if (old_state == NACL_APP_THREAD_UNTRUSTED) {
158 if (pthread_kill(natp->thread.tid, NACL_THREAD_SUSPEND_SIGNAL) != 0) {
159 NaClLog(LOG_FATAL, "NaClUntrustedThreadsSuspend: "
160 "pthread_kill() call failed\n");
161 }
162 WaitForUntrustedThreadToSuspend(natp);
163 }
164 }
165
166 void NaClUntrustedThreadResume(struct NaClAppThread *natp) {
167 Atomic32 old_state;
168 Atomic32 new_state;
169 while (1) {
170 old_state = natp->suspend_state;
171 new_state = old_state & ~(NACL_APP_THREAD_SUSPENDING |
172 NACL_APP_THREAD_SUSPENDED);
173 DCHECK((old_state & NACL_APP_THREAD_SUSPENDING) != 0);
174 if (CompareAndSwap(&natp->suspend_state, old_state, new_state)
175 != old_state) {
176 continue; /* Retry */
177 }
178 break;
179 }
180
181 /*
182 * TODO(mseaborn): A refinement would be to wake up the thread only
183 * if it actually suspended during the context switch.
184 */
185 FutexWake(&natp->suspend_state, 1);
186 }
187
188 /*
189 * NaClUntrustedThreadsSuspend() ensures that any untrusted code is
190 * temporarily suspended.
191 *
192 * If a thread is currently executing a NaCl syscall, we tell the
193 * thread not to return to untrusted code yet. If a thread is
194 * currently executing untrusted code, we suspend it.
195 *
196 * This returns with the lock threads_mu held, because we need to pin
197 * the list of threads. NaClUntrustedThreadsResume() must be called
198 * to undo this.
199 */
200 void NaClUntrustedThreadsSuspendAll(struct NaClApp *nap) {
201 size_t index;
202
203 NaClXMutexLock(&nap->threads_mu);
204
205 for (index = 0; index < nap->threads.num_entries; index++) {
206 struct NaClAppThread *natp = NaClGetThreadMu(nap, (int) index);
207 if (natp != NULL) {
208 NaClUntrustedThreadSuspend(natp);
209 }
210 }
211 }
212
213 void NaClUntrustedThreadsResumeAll(struct NaClApp *nap) {
214 size_t index;
215 for (index = 0; index < nap->threads.num_entries; index++) {
216 struct NaClAppThread *natp = NaClGetThreadMu(nap, (int) index);
217 if (natp != NULL) {
218 NaClUntrustedThreadResume(natp);
219 }
220 }
221
222 NaClXMutexUnlock(&nap->threads_mu);
223 }
OLDNEW
« no previous file with comments | « src/trusted/service_runtime/generic/thread_suspension.c ('k') | src/trusted/service_runtime/nacl_app_thread.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698