1 : /*
2 : * Copyright (c) 2011 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 <string.h>
8 :
9 : #include <limits>
10 : #include <string>
11 :
12 : #include "native_client/src/trusted/reverse_service/reverse_service.h"
13 :
14 : #include "native_client/src/include/nacl_compiler_annotations.h"
15 : #include "native_client/src/include/nacl_scoped_ptr.h"
16 : #include "native_client/src/include/portability_io.h"
17 : #include "native_client/src/shared/platform/nacl_check.h"
18 : #include "native_client/src/shared/platform/nacl_host_desc.h"
19 : #include "native_client/src/shared/platform/nacl_log.h"
20 : #include "native_client/src/shared/platform/nacl_sync.h"
21 : #include "native_client/src/shared/platform/nacl_sync_checked.h"
22 : #include "native_client/src/shared/platform/nacl_threads.h"
23 : #include "native_client/src/shared/srpc/nacl_srpc.h"
24 :
25 : #include "native_client/src/trusted/desc/nacl_desc_invalid.h"
26 : #include "native_client/src/trusted/desc/nacl_desc_io.h"
27 :
28 : #include "native_client/src/trusted/service_runtime/include/sys/fcntl.h"
29 :
30 : namespace {
31 :
32 : void Test(NaClSrpcRpc* rpc,
33 : NaClSrpcArg** in_args,
34 : NaClSrpcArg** out_args,
35 0 : NaClSrpcClosure* done) {
36 0 : char *msg = in_args[0]->arrays.str;
37 0 : NaClSrpcClosureRunner on_return(done);
38 :
39 : UNREFERENCED_PARAMETER(out_args);
40 : // use rpc->channel rather than rpc->channel->server_instance_data
41 : // to show that Test RPCs arrive in different channels.
42 : NaClLog(1, "Test: [%"NACL_PRIxPTR"] %s\n",
43 0 : reinterpret_cast<uintptr_t>(rpc->channel), msg);
44 0 : rpc->result = NACL_SRPC_RESULT_OK;
45 0 : }
46 :
47 : void AddChannel(NaClSrpcRpc* rpc,
48 : NaClSrpcArg** in_args,
49 : NaClSrpcArg** out_args,
50 3 : NaClSrpcClosure* done) {
51 : nacl::ReverseService* service = reinterpret_cast<nacl::ReverseService*>(
52 3 : rpc->channel->server_instance_data);
53 3 : NaClSrpcClosureRunner on_return(done);
54 :
55 : UNREFERENCED_PARAMETER(in_args);
56 :
57 3 : NaClLog(4, "Entered AddChannel\n");
58 3 : out_args[0]->u.bval = service->Start(false);
59 3 : NaClLog(4, "Leaving AddChannel\n");
60 3 : rpc->result = NACL_SRPC_RESULT_OK;
61 3 : }
62 :
63 : void RevLog(NaClSrpcRpc* rpc,
64 : NaClSrpcArg** in_args,
65 : NaClSrpcArg** out_args,
66 0 : NaClSrpcClosure* done) {
67 : nacl::ReverseService* service = reinterpret_cast<nacl::ReverseService*>(
68 0 : rpc->channel->server_instance_data);
69 0 : char* message = in_args[0]->arrays.str;
70 0 : NaClSrpcClosureRunner on_return(done);
71 :
72 : UNREFERENCED_PARAMETER(out_args);
73 :
74 0 : if (NULL == service->reverse_interface()) {
75 0 : NaClLog(1, "Log RPC, no reverse_interface. Message: %s\n", message);
76 : } else {
77 0 : service->reverse_interface()->Log(message);
78 0 : }
79 0 : }
80 :
81 : void ModuleInitDoneRpc(NaClSrpcRpc* rpc,
82 : NaClSrpcArg** in_args,
83 : NaClSrpcArg** out_args,
84 2 : NaClSrpcClosure* done) {
85 : nacl::ReverseService* service = reinterpret_cast<nacl::ReverseService*>(
86 2 : rpc->channel->server_instance_data);
87 2 : NaClSrpcClosureRunner on_return(done);
88 :
89 : UNREFERENCED_PARAMETER(in_args);
90 : UNREFERENCED_PARAMETER(out_args);
91 :
92 2 : NaClLog(4, "Entered ModuleInitDone\n");
93 2 : NaClLog(4, "service: 0x%"NACL_PRIxPTR, (uintptr_t) service);
94 2 : if (NULL == service->reverse_interface()) {
95 0 : NaClLog(4, "ModuleInitDone: no reverse_interface. Nothing to do\n");
96 : } else {
97 2 : NaClLog(4, "ModuleInitDone: invoking StartupInitializationComplete\n");
98 2 : service->reverse_interface()->StartupInitializationComplete();
99 : }
100 2 : NaClLog(4, "Leaving ModuleInitDoneRpc\n");
101 2 : rpc->result = NACL_SRPC_RESULT_OK;
102 2 : }
103 :
104 : void ModuleExitRpc(NaClSrpcRpc* rpc,
105 : NaClSrpcArg** in_args,
106 : NaClSrpcArg** out_args,
107 2 : NaClSrpcClosure* done) {
108 : nacl::ReverseService* service = reinterpret_cast<nacl::ReverseService*>(
109 2 : rpc->channel->server_instance_data);
110 2 : NaClSrpcClosureRunner on_return(done);
111 :
112 : UNREFERENCED_PARAMETER(out_args);
113 :
114 2 : NaClLog(4, "Entered ModuleExitRpc\n");
115 2 : NaClLog(4, "service: 0x%"NACL_PRIxPTR, (uintptr_t) service);
116 :
117 2 : int exit_status = in_args[0]->u.ival;
118 2 : NaClLog(4, "exit_status: 0x%d\n", exit_status);
119 :
120 2 : if (NULL == service->reverse_interface()) {
121 0 : NaClLog(4, "ModuleExitRpc: no reverse_interface. Nothing to do.\n");
122 : } else {
123 2 : NaClLog(4, "ModuleExitRpc: invoking ReportExitStatus\n");
124 2 : service->reverse_interface()->ReportExitStatus(exit_status);
125 : }
126 2 : NaClLog(4, "Leaving ModuleExitRpc\n");
127 2 : rpc->result = NACL_SRPC_RESULT_OK;
128 2 : }
129 :
130 : void PostMessageRpc(NaClSrpcRpc* rpc,
131 : NaClSrpcArg** in_args,
132 : NaClSrpcArg** out_args,
133 0 : NaClSrpcClosure* done) {
134 : nacl::ReverseService* service = reinterpret_cast<nacl::ReverseService*>(
135 0 : rpc->channel->server_instance_data);
136 0 : char* message = in_args[0]->arrays.carr;
137 0 : nacl_abi_size_t nbytes = in_args[0]->u.count;
138 0 : NaClSrpcClosureRunner on_return(done);
139 :
140 0 : NaClLog(4, "Entered PostMessageRpc\n");
141 0 : if (NULL == service->reverse_interface()) {
142 : NaClLog(1, "PostMessage RPC, no reverse_interface. Message: %s\n",
143 0 : message);
144 : } else {
145 0 : service->reverse_interface()->DoPostMessage(std::string(message, nbytes));
146 : }
147 0 : out_args[0]->u.ival = (int32_t) nbytes;
148 0 : NaClLog(4, "Leaving PostMessageRpc\n");
149 0 : rpc->result = NACL_SRPC_RESULT_OK;
150 0 : }
151 :
152 : // Manifest name service, internal APIs.
153 : //
154 : // Manifest file lookups result in read-only file descriptors with a
155 : // handle. When the descriptor is closed, the service runtime must
156 : // inform the plugin of this using the handle, so that the File object
157 : // reference can be closed (thereby allowing the browser to delete or
158 : // otherwise garbage collect the file). Files, being from the
159 : // manifest, cannot be deleted. The manifest is also a read-only
160 : // object, so no new entries can be made to it.
161 : //
162 : // Read-only proxies do not require quota support per se, since we do
163 : // not limit read bandwidth. Quota support is needed for storage
164 : // limits, though could also be used to limit write bandwidth (prevent
165 : // disk output saturation, limit malicious code's ability to cause
166 : // disk failures, especially with flash disks with limited write
167 : // cycles).
168 :
169 : // NACL_MANIFEST_LIST list::C -- enumerate all names in the manifest
170 : void ManifestListRpc(NaClSrpcRpc* rpc,
171 : NaClSrpcArg** in_args,
172 : NaClSrpcArg** out_args,
173 2 : NaClSrpcClosure* done) {
174 : nacl::ReverseService* service = reinterpret_cast<nacl::ReverseService*>(
175 2 : rpc->channel->server_instance_data);
176 2 : NaClSrpcClosureRunner on_return(done);
177 :
178 : UNREFERENCED_PARAMETER(in_args);
179 :
180 2 : if (NULL == service->reverse_interface()) {
181 0 : NaClLog(1, "ManifestList RPC, no reverse_interface.\n");
182 0 : out_args[0]->u.count = 0;
183 0 : rpc->result = NACL_SRPC_RESULT_OK;
184 0 : return;
185 : }
186 :
187 2 : std::set<nacl::string> manifest_keys;
188 2 : if (!service->reverse_interface()->EnumerateManifestKeys(&manifest_keys)) {
189 0 : NaClLog(LOG_WARNING, "ManifestList RPC: EnumerateManifestKeys failed\n");
190 0 : out_args[0]->u.count = 0;
191 0 : rpc->result = NACL_SRPC_RESULT_OK;
192 : return;
193 : }
194 2 : size_t sofar = 0;
195 2 : size_t space = out_args[0]->u.count;
196 2 : char* dest = out_args[0]->arrays.carr;
197 : size_t to_write;
198 :
199 4 : for (std::set<nacl::string>::iterator it = manifest_keys.begin();
200 : it != manifest_keys.end();
201 : ++it) {
202 2 : NaClLog(3, "ManifestList RPC: appending %s\n", it->c_str());
203 :
204 2 : if (sofar >= space) {
205 0 : NaClLog(3, "ManifestList RPC: buffer too small, breaking\n");
206 0 : break;
207 : }
208 2 : to_write = space - sofar;
209 2 : if (it->size() + 1 < to_write) {
210 2 : to_write = it->size() + 1;
211 : } else {
212 0 : NaClLog(3, "ManifestList RPC: truncating entry %s\n", it->c_str());
213 : }
214 2 : strncpy(dest + sofar, it->c_str(), to_write);
215 2 : NaClLog(3, "ManifestList RPC: %.*s\n", (int) to_write, dest + sofar);
216 2 : sofar += to_write;
217 : }
218 :
219 2 : NaClLog(3, "ManifestList RPC: total size %"NACL_PRIdS"\n", sofar);
220 :
221 2 : out_args[0]->u.count = static_cast<nacl_abi_size_t>(sofar);
222 2 : rpc->result = NACL_SRPC_RESULT_OK;
223 : }
224 :
225 : // NACL_MANIFEST_LOOKUP lookup:si:ihC -- look up by string name,
226 : // resulting in a handle (if name is in the preimage), a object proxy
227 : // handle, and an error code.
228 : void ManifestLookupRpc(NaClSrpcRpc* rpc,
229 : NaClSrpcArg** in_args,
230 : NaClSrpcArg** out_args,
231 0 : NaClSrpcClosure* done) {
232 0 : char* fname = in_args[0]->arrays.str;
233 0 : int flags = in_args[0]->u.ival;
234 0 : int32_t posix_desc = -1;
235 : nacl::ReverseService* service = reinterpret_cast<nacl::ReverseService*>(
236 0 : rpc->channel->server_instance_data);
237 0 : NaClSrpcClosureRunner on_return(done);
238 :
239 0 : NaClLog(0, "ManifestLookupRpc: %s, %d\n", fname, flags);
240 :
241 0 : out_args[2]->u.count = 0;
242 : // by default we return a failure, so no proxy handle
243 0 : if (NULL == service->reverse_interface()) {
244 0 : NaClLog(0, "ManifestLookupRpc: no reverse service, returning error\n");
245 : // no reverse interface, all file open requests get -1.
246 0 : out_args[0]->u.ival = 0; // ok, but failed.
247 0 : out_args[1]->u.hval = (struct NaClDesc*) NaClDescInvalidMake();
248 0 : rpc->result = NACL_SRPC_RESULT_OK;
249 : return;
250 : }
251 :
252 0 : NaClLog(0, "ManifestLookupRpc: invoking OpenManifestEntry\n");
253 0 : if (!service->reverse_interface()->OpenManifestEntry(fname,
254 : &posix_desc)
255 : || -1 == posix_desc) {
256 0 : NaClLog(0, "ManifestLookupRpc: OpenManifestEntry failed.\n");
257 0 : out_args[0]->u.ival = 0; // ok, but failed.
258 0 : out_args[1]->u.hval = (struct NaClDesc*) NaClDescInvalidMake();
259 0 : rpc->result = NACL_SRPC_RESULT_OK;
260 0 : return;
261 : }
262 : NaClLog(0,
263 : "ManifestLookupRpc: OpenManifestEntry returned desc %d.\n",
264 0 : posix_desc);
265 : struct NaClHostDesc *hd = reinterpret_cast<struct NaClHostDesc*>(
266 0 : malloc(sizeof *hd));
267 0 : CHECK(hd != NULL);
268 0 : CHECK(NaClHostDescPosixTake(hd, posix_desc, NACL_ABI_O_RDONLY) == 0);
269 0 : struct NaClDescIoDesc *diod = NaClDescIoDescMake(hd);
270 0 : CHECK(diod != NULL);
271 0 : out_args[1]->u.hval = (struct NaClDesc *) diod;
272 0 : out_args[2]->u.count = 10;
273 0 : strncpy(out_args[2]->arrays.carr, "123456789", 10);
274 0 : rpc->result = NACL_SRPC_RESULT_OK;
275 : }
276 :
277 : // NACL_MANIFEST_UNREF unref:C:i -- dereferences the file by object
278 : // proxy handle. The file descriptor should have been closed on the
279 : // other side (it was a copy due to the sendmsg anyway).
280 : void ManifestUnrefRpc(NaClSrpcRpc* rpc,
281 : NaClSrpcArg** in_args,
282 : NaClSrpcArg** out_args,
283 0 : NaClSrpcClosure* done) {
284 0 : char* proxy_handle = in_args[0]->arrays.carr;
285 0 : NaClSrpcClosureRunner on_return(done);
286 :
287 0 : NaClLog(0, "ManifestUnrefRpc: %.*s\n", 10, proxy_handle);
288 : // Placeholder. This RPC will be replaced by real code that
289 : // looks up the object proxy handle to close the Pepper file object.
290 : //
291 : // TODO(bsy): replace with real code.
292 0 : out_args[0]->u.ival = 0; // ok
293 0 : rpc->result = NACL_SRPC_RESULT_OK;
294 0 : }
295 :
296 : void RequestQuotaForWriteRpc(NaClSrpcRpc* rpc,
297 : NaClSrpcArg** in_args,
298 : NaClSrpcArg** out_args,
299 0 : NaClSrpcClosure* done) {
300 : nacl::ReverseService* service = reinterpret_cast<nacl::ReverseService*>(
301 0 : rpc->channel->server_instance_data);
302 : nacl::string file_id = nacl::string(in_args[0]->arrays.carr,
303 0 : in_args[0]->u.count);
304 0 : int64_t offset = in_args[1]->u.lval;
305 0 : int64_t length = in_args[2]->u.lval;
306 0 : int64_t quota_granted = 0;
307 0 : NaClSrpcClosureRunner on_return(done);
308 0 : NaClLog(4, "Entered RequestQuotaForWriteRpc\n");
309 0 : if (NULL == service->reverse_interface()) {
310 0 : NaClLog(1, "RequestQuotaForWrite RPC, no reverse_interface.\n");
311 : } else {
312 : quota_granted =
313 : service->reverse_interface()->RequestQuotaForWrite(file_id,
314 : offset,
315 0 : length);
316 : }
317 0 : out_args[0]->u.lval = quota_granted;
318 0 : NaClLog(4, "Leaving RequestQuotaForWriteRpc\n");
319 0 : rpc->result = NACL_SRPC_RESULT_OK;
320 0 : }
321 :
322 : } // namespace
323 :
324 : namespace nacl {
325 :
326 : // need NaClThreadIfFactoryFunction that keeps "this" to do counting
327 :
328 : struct ReverseCountingThreadInterface {
329 : struct NaClThreadInterface base NACL_IS_REFCOUNT_SUBCLASS;
330 : nacl::ReverseService* rev;
331 : };
332 :
333 : /* fwd */ extern NaClThreadInterfaceVtbl const kReverseThreadInterfaceVtbl;
334 :
335 : int ReverseThreadIfFactoryFn(
336 : void* factory_data,
337 : NaClThreadIfStartFunction fn_ptr,
338 : void* thread_data,
339 : size_t thread_stack_size,
340 : NaClThreadInterface** out_new_thread); // fwd
341 :
342 : int ReverseThreadIfCtor_protected(
343 : ReverseCountingThreadInterface* self,
344 : void* factory_data,
345 : NaClThreadIfStartFunction fn_ptr,
346 : void* thread_data,
347 5 : size_t thread_stack_size) {
348 5 : ReverseService* rev = reinterpret_cast<ReverseService*>(factory_data);
349 :
350 5 : if (!NaClThreadInterfaceCtor_protected(
351 : reinterpret_cast<NaClThreadInterface*>(self),
352 : ReverseThreadIfFactoryFn,
353 : reinterpret_cast<void*>(rev->Ref()),
354 : fn_ptr,
355 : thread_data,
356 : thread_stack_size)) {
357 : NaClLog(4,
358 : ("ReverseService::ReverseThreadIfFactoryFn:"
359 0 : " placement base class ctor failed\n"));
360 0 : return 0;
361 : }
362 5 : self->rev = rev;
363 5 : rev->IncrThreadCount();
364 : NACL_VTBL(NaClRefCount, self) =
365 5 : reinterpret_cast<NaClRefCountVtbl const*>(&kReverseThreadInterfaceVtbl);
366 5 : return 1;
367 : }
368 :
369 : /*
370 : * Takes ownership of rev reference. Caller should Ref() the argument
371 : * and Unref on failure if caller does not wish to pass ownership.
372 : */
373 : int ReverseThreadIfFactoryFn(
374 : void* factory_data,
375 : NaClThreadIfStartFunction fn_ptr,
376 : void* thread_data,
377 : size_t thread_stack_size,
378 5 : NaClThreadInterface** out_new_thread) {
379 5 : NaClLog(4, "ReverseService::ReverseThreadIfFactoryFn\n");
380 :
381 5 : nacl::scoped_ptr_malloc<ReverseCountingThreadInterface> new_thread;
382 :
383 : new_thread.reset(reinterpret_cast<ReverseCountingThreadInterface*>(
384 5 : malloc(sizeof *new_thread.get())));
385 5 : if (NULL == new_thread.get()) {
386 0 : return 0;
387 : }
388 5 : if (!ReverseThreadIfCtor_protected(new_thread.get(),
389 : factory_data,
390 : fn_ptr,
391 : thread_data,
392 : thread_stack_size)) {
393 0 : return 0;
394 : }
395 : *out_new_thread = reinterpret_cast<NaClThreadInterface*>(
396 5 : new_thread.release());
397 5 : return 1;
398 : }
399 :
400 5 : void ReverseThreadIfDtor(struct NaClRefCount* vself) {
401 : ReverseCountingThreadInterface* self =
402 5 : (ReverseCountingThreadInterface*) vself;
403 :
404 5 : self->rev->Unref();
405 5 : self->rev = NULL;
406 :
407 5 : NACL_VTBL(NaClRefCount, self) = &kNaClRefCountVtbl;
408 5 : (*NACL_VTBL(NaClRefCount, self)->Dtor)(vself);
409 5 : }
410 :
411 5 : void ReverseThreadIfLaunchCallback(struct NaClThreadInterface* self) {
412 : NaClLog(4,
413 : ("ReverseService::ReverseThreadIfLaunchCallback: thread 0x%"
414 : NACL_PRIxPTR" is launching\n"),
415 5 : (uintptr_t) self);
416 5 : }
417 :
418 : void ReverseThreadIfExit(struct NaClThreadInterface* vself,
419 5 : void* exit_code) {
420 : ReverseCountingThreadInterface *self =
421 5 : reinterpret_cast<ReverseCountingThreadInterface*>(vself);
422 : NaClLog(4,
423 : ("ReverseService::ReverseThreadIfExit: thread 0x%"NACL_PRIxPTR
424 : " is exiting\n"),
425 5 : (uintptr_t) vself);
426 :
427 5 : self->rev->DecrThreadCount();
428 :
429 5 : NaClRefCountUnref(reinterpret_cast<struct NaClRefCount*>(self));
430 5 : NaClThreadExit(static_cast<int>(reinterpret_cast<uintptr_t>(exit_code)));
431 0 : }
432 :
433 : NaClThreadInterfaceVtbl const kReverseThreadInterfaceVtbl = {
434 : {
435 : ReverseThreadIfDtor,
436 : },
437 : NaClThreadInterfaceStartThread,
438 : ReverseThreadIfLaunchCallback,
439 : ReverseThreadIfExit,
440 : };
441 :
442 : NaClSrpcHandlerDesc const ReverseService::handlers[] = {
443 : { NACL_REVERSE_CONTROL_TEST, Test, },
444 : { NACL_REVERSE_CONTROL_LOG, RevLog, },
445 : { NACL_REVERSE_CONTROL_ADD_CHANNEL, AddChannel, },
446 : { NACL_REVERSE_CONTROL_INIT_DONE, ModuleInitDoneRpc, },
447 : { NACL_REVERSE_CONTROL_REPORT_STATUS, ModuleExitRpc, },
448 : { NACL_REVERSE_CONTROL_POST_MESSAGE, PostMessageRpc, },
449 : { NACL_MANIFEST_LIST, ManifestListRpc, },
450 : { NACL_MANIFEST_LOOKUP, ManifestLookupRpc, },
451 : { NACL_MANIFEST_UNREF, ManifestUnrefRpc, },
452 : { NACL_REVERSE_REQUEST_QUOTA_FOR_WRITE, RequestQuotaForWriteRpc, },
453 : { NULL, NULL, },
454 : };
455 :
456 : ReverseService::ReverseService(nacl::DescWrapper* conn_cap,
457 2 : ReverseInterface* rif)
458 : : service_socket_(NULL),
459 : reverse_interface_(rif),
460 2 : thread_count_(0) {
461 : /*
462 : * We wait for service threads to exit before dtor'ing, so the
463 : * reference to this passed to the ctor for the service_socket_ for
464 : * use by the thread factory will have a longer lifetime than this
465 : * object.
466 : */
467 2 : NaClLog(4, "ReverseService::ReverseService ctor invoked\n");
468 2 : NaClXMutexCtor(&mu_);
469 2 : NaClXCondVarCtor(&cv_);
470 : service_socket_.reset(new ReverseSocket(conn_cap,
471 : handlers,
472 : ReverseThreadIfFactoryFn,
473 2 : reinterpret_cast<void*>(this)));
474 2 : }
475 :
476 2 : ReverseService::~ReverseService() {
477 2 : if (thread_count_ != 0) {
478 0 : NaClLog(LOG_FATAL, "ReverseService dtor when thread count is nonzero\n");
479 : }
480 2 : NaClCondVarDtor(&cv_);
481 2 : NaClMutexDtor(&mu_);
482 2 : }
483 :
484 :
485 : static void RevServiceCbBinder(void *state,
486 2 : int server_loop_ret) {
487 2 : ReverseService *obj = reinterpret_cast<ReverseService *>(state);
488 :
489 : UNREFERENCED_PARAMETER(server_loop_ret);
490 2 : obj->reverse_interface()->ReportCrash();
491 2 : }
492 :
493 :
494 5 : bool ReverseService::Start(bool crash_report) {
495 5 : NaClLog(4, "Entered ReverseService::Start\n");
496 5 : if (crash_report) {
497 : return service_socket_->StartServiceCb(RevServiceCbBinder,
498 2 : reinterpret_cast<void*>(this));
499 : } else {
500 : return service_socket_->StartServiceCb(NULL,
501 3 : reinterpret_cast<void*>(this));
502 : }
503 : }
504 :
505 2 : void ReverseService::WaitForServiceThreadsToExit() {
506 2 : NaClLog(4, "ReverseService::WaitForServiceThreadsToExit\n");
507 2 : NaClXMutexLock(&mu_);
508 6 : while (0 != thread_count_) {
509 2 : NaClXCondVarWait(&cv_, &mu_);
510 2 : NaClLog(5, "ReverseService::WaitForServiceThreadsToExit: woke up\n");
511 : }
512 2 : NaClXMutexUnlock(&mu_);
513 2 : NaClLog(4, "ReverseService::WaitForServiceThreadsToExit ALL DONE\n");
514 2 : }
515 :
516 5 : void ReverseService::IncrThreadCount() {
517 5 : NaClLog(5, "ReverseService::IncrThreadCount\n");
518 5 : NaClXMutexLock(&mu_);
519 5 : if (0 == ++thread_count_) {
520 : NaClLog(LOG_FATAL,
521 0 : "ReverseService::IncrThreadCount Thread count overflow!\n");
522 : }
523 5 : NaClXMutexUnlock(&mu_);
524 5 : }
525 :
526 5 : void ReverseService::DecrThreadCount() {
527 5 : NaClLog(5, "ReverseService::DecrThreadCount\n");
528 5 : NaClXMutexLock(&mu_);
529 5 : if (0 == thread_count_) {
530 : NaClLog(LOG_FATAL,
531 : ("ReverseService::DecrThreadCount:"
532 0 : " Decrementing thread count when count is zero\n"));
533 : }
534 5 : if (0 == --thread_count_) {
535 2 : NaClXCondVarBroadcast(&cv_);
536 : }
537 5 : NaClXMutexUnlock(&mu_);
538 5 : }
539 :
540 24 : } // namespace nacl
|