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

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: Use %d 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_sync_checked.h"
14 #include "native_client/src/trusted/service_runtime/nacl_app_thread.h"
15 #include "native_client/src/trusted/service_runtime/nacl_globals.h"
16 #include "native_client/src/trusted/service_runtime/nacl_tls.h"
17 #include "native_client/src/trusted/service_runtime/sel_ldr.h"
18
19
20 static void FutexWait(Atomic32 *addr, Atomic32 value) {
21 if (syscall(SYS_futex, addr, FUTEX_WAIT, value, 0, 0, 0) != 0) {
22 if (errno != EINTR && errno != EAGAIN) {
bradn 2012/05/18 19:56:35 Not seeing in the docs where EAGAIN is a valid ret
bradn 2012/05/18 19:56:35 Might be nice to have a comment like: If *addr sti
Mark Seaborn 2012/05/21 15:03:57 You get EAGAIN if *addr != value. I've added a co
Mark Seaborn 2012/05/21 15:03:57 Done.
23 NaClLog(LOG_FATAL, "FutexWait: futex() failed with error %d\n", errno);
24 }
25 }
26 }
27
28 static void FutexWake(Atomic32 *addr, int waiters) {
29 if (syscall(SYS_futex, addr, FUTEX_WAKE, waiters, 0, 0, 0) < 0) {
30 NaClLog(LOG_FATAL, "FutexWake: futex() failed with error %d\n", errno);
bradn 2012/05/18 19:56:35 Comment: Wake up to 'waiters' threads waiting on a
Mark Seaborn 2012/05/21 15:03:57 Done.
31 }
32 }
33
34 void NaClAppThreadSetSuspendState(struct NaClAppThread *natp,
35 enum NaClSuspendState old_state,
36 enum NaClSuspendState new_state) {
37 while (1) {
38 Atomic32 state = natp->suspend_state;
39 if ((state & NACL_APP_THREAD_SUSPENDING) != 0) {
bradn 2012/05/18 19:56:35 I find the !=0 confusing (and a deviation from the
Mark Seaborn 2012/05/21 15:03:57 It's what we do in the existing win/thread_suspens
40 /* We have been asked to suspend, so wait. */
41 FutexWait(&natp->suspend_state, state);
42 continue; /* Retry */
43 }
44
45 CHECK(state == (Atomic32) old_state);
46 if (CompareAndSwap(&natp->suspend_state, old_state, new_state)
47 != (Atomic32) old_state) {
48 continue; /* Retry */
49 }
50 break;
51 }
52 }
53
54 void NaClSuspendSignalHandler(void) {
55 uint32_t tls_idx = NaClTlsGetIdx();
56 struct NaClAppThread *natp = nacl_thread[tls_idx];
57
58 /*
59 * Indicate that we have suspended by setting
60 * NACL_APP_THREAD_SUSPENDED. We should not need an atomic
61 * operation for this since the calling thread will not be trying to
62 * change suspend_state.
63 */
64 DCHECK(natp->suspend_state == (NACL_APP_THREAD_UNTRUSTED |
65 NACL_APP_THREAD_SUSPENDING));
66 natp->suspend_state |= NACL_APP_THREAD_SUSPENDED;
67 FutexWake(&natp->suspend_state, 1);
68
69 /* Wait until we are asked to resume. */
70 while (1) {
71 Atomic32 state = natp->suspend_state;
72 if ((state & NACL_APP_THREAD_SUSPENDED) != 0) {
bradn 2012/05/18 19:56:35 drop the !=0
Mark Seaborn 2012/05/21 15:03:57 See other reply.
73 FutexWait(&natp->suspend_state, state);
74 continue; /* Retry */
75 }
76 break;
77 }
78 }
79
80 /* Wait for the thread to indicate that it has suspended. */
81 static void WaitForSuspension(struct NaClAppThread *natp,
82 Atomic32 suspending_state) {
bradn 2012/05/18 19:56:35 I get the motivation for keeping all the state bit
Mark Seaborn 2012/05/21 15:03:57 OK, renamed.
83 while (1) {
84 Atomic32 state = natp->suspend_state;
85 if (state == suspending_state) {
86 FutexWait(&natp->suspend_state, state);
87 continue; /* Retry */
88 }
89 if (state != (suspending_state | NACL_APP_THREAD_SUSPENDED)) {
90 NaClLog(LOG_FATAL, "Unexpected state: %d\n", state);
91 }
92 break;
93 }
94 }
95
96 void NaClUntrustedThreadSuspend(struct NaClAppThread *natp) {
97 Atomic32 old_state;
98 Atomic32 suspending_state;
99
100 /*
101 * Note that if we are being called from a NaCl syscall (which is
102 * likely), natp could be the thread we are running in. That is
103 * fine, because this thread will be in the NACL_APP_THREAD_TRUSTED
104 * state, and so we will not try to interrupt it.
105 */
106
107 /*
108 * We do not want the thread to enter a NaCl syscall and start
109 * taking locks when pthread_kill() takes effect, so we ask the
110 * thread to suspend even if it is currently running untrusted code.
111 */
112 while (1) {
113 old_state = natp->suspend_state;
114 DCHECK((old_state & NACL_APP_THREAD_SUSPENDING) == 0);
115 suspending_state = old_state | NACL_APP_THREAD_SUSPENDING;
116 if (CompareAndSwap(&natp->suspend_state, old_state, suspending_state)
117 != old_state) {
118 continue; /* Retry */
119 }
120 break;
121 }
122 /*
123 * Once the thread has NACL_APP_THREAD_SUSPENDING set, it may not
124 * change state itself, so there should be no race condition in this
125 * check.
126 */
127 DCHECK(natp->suspend_state == suspending_state);
128
129 if (old_state == NACL_APP_THREAD_UNTRUSTED) {
130 if (pthread_kill(natp->thread.tid, NACL_THREAD_SUSPEND_SIGNAL) != 0) {
131 NaClLog(LOG_FATAL, "NaClUntrustedThreadsSuspend: "
132 "pthread_kill() call failed\n");
133 }
134 WaitForSuspension(natp, suspending_state);
135 }
136 }
137
138 void NaClUntrustedThreadResume(struct NaClAppThread *natp) {
139 Atomic32 old_state;
140 Atomic32 new_state;
141 while (1) {
142 old_state = natp->suspend_state;
143 new_state = old_state & ~(NACL_APP_THREAD_SUSPENDING |
144 NACL_APP_THREAD_SUSPENDED);
145 DCHECK((old_state & NACL_APP_THREAD_SUSPENDING) != 0);
146 if (CompareAndSwap(&natp->suspend_state, old_state, new_state)
147 != old_state) {
148 continue; /* Retry */
149 }
150 break;
151 }
152
153 /*
154 * TODO(mseaborn): A refinement would be to wake up the thread only
155 * if it actually suspended during the context switch.
156 */
157 FutexWake(&natp->suspend_state, 1);
158 }
159
160 /*
161 * NaClUntrustedThreadsSuspend() ensures that any untrusted code is
162 * temporarily suspended.
163 *
164 * If a thread is currently executing a NaCl syscall, we tell the
165 * thread not to return to untrusted code yet. If a thread is
166 * currently executing untrusted code, we suspend it.
167 *
168 * This returns with the lock threads_mu held, because we need to pin
169 * the list of threads. NaClUntrustedThreadsResume() must be called
170 * to undo this.
171 */
172 void NaClUntrustedThreadsSuspendAll(struct NaClApp *nap) {
173 size_t index;
174
175 NaClXMutexLock(&nap->threads_mu);
176
177 for (index = 0; index < nap->threads.num_entries; index++) {
178 struct NaClAppThread *natp = NaClGetThreadMu(nap, (int) index);
179 if (natp != NULL) {
180 NaClUntrustedThreadSuspend(natp);
181 }
182 }
183 }
184
185 void NaClUntrustedThreadsResumeAll(struct NaClApp *nap) {
186 size_t index;
187 for (index = 0; index < nap->threads.num_entries; index++) {
188 struct NaClAppThread *natp = NaClGetThreadMu(nap, (int) index);
189 if (natp != NULL) {
190 NaClUntrustedThreadResume(natp);
191 }
192 }
193
194 NaClXMutexUnlock(&nap->threads_mu);
195 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698