OLD | NEW |
| (Empty) |
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 #define NACL_LOG_MODULE_NAME "Plugin::ServiceRuntime" | |
8 | |
9 #include "native_client/src/trusted/plugin/service_runtime.h" | |
10 | |
11 #include <string.h> | |
12 #include <map> | |
13 #include <set> | |
14 #include <string> | |
15 #include <utility> | |
16 #include <vector> | |
17 | |
18 #include "native_client/src/include/portability_io.h" | |
19 #include "native_client/src/include/nacl_scoped_ptr.h" | |
20 #include "native_client/src/include/nacl_macros.h" | |
21 #include "native_client/src/include/nacl_string.h" | |
22 #include "native_client/src/shared/imc/nacl_imc.h" | |
23 #include "native_client/src/shared/platform/nacl_check.h" | |
24 #include "native_client/src/shared/platform/nacl_log.h" | |
25 #include "native_client/src/shared/platform/nacl_sync.h" | |
26 #include "native_client/src/shared/platform/nacl_sync_checked.h" | |
27 #include "native_client/src/shared/platform/nacl_sync_raii.h" | |
28 #include "native_client/src/shared/platform/scoped_ptr_refcount.h" | |
29 #include "native_client/src/trusted/desc/nacl_desc_imc.h" | |
30 #include "native_client/src/trusted/desc/nrd_xfer.h" | |
31 #include "native_client/src/trusted/desc/nrd_xfer_effector.h" | |
32 #include "native_client/src/trusted/handle_pass/browser_handle.h" | |
33 #include "native_client/src/trusted/nonnacl_util/sel_ldr_launcher.h" | |
34 | |
35 #include "native_client/src/trusted/plugin/browser_interface.h" | |
36 #include "native_client/src/trusted/plugin/manifest.h" | |
37 #include "native_client/src/trusted/plugin/plugin.h" | |
38 #include "native_client/src/trusted/plugin/plugin_error.h" | |
39 #include "native_client/src/trusted/plugin/scriptable_handle.h" | |
40 #include "native_client/src/trusted/plugin/srpc_client.h" | |
41 #include "native_client/src/trusted/plugin/utility.h" | |
42 | |
43 #include "native_client/src/trusted/weak_ref/call_on_main_thread.h" | |
44 | |
45 #include "native_client/src/trusted/service_runtime/nacl_error_code.h" | |
46 #include "native_client/src/trusted/service_runtime/include/sys/nacl_imc_api.h" | |
47 | |
48 #include "ppapi/c/pp_errors.h" | |
49 #include "ppapi/cpp/core.h" | |
50 #include "ppapi/cpp/completion_callback.h" | |
51 | |
52 using std::vector; | |
53 | |
54 namespace plugin { | |
55 | |
56 PluginReverseInterface::PluginReverseInterface( | |
57 nacl::WeakRefAnchor* anchor, | |
58 Plugin* plugin, | |
59 pp::CompletionCallback init_done_cb) | |
60 : anchor_(anchor), | |
61 plugin_(plugin), | |
62 shutting_down_(false), | |
63 init_done_cb_(init_done_cb) { | |
64 NaClXMutexCtor(&mu_); | |
65 NaClXCondVarCtor(&cv_); | |
66 } | |
67 | |
68 PluginReverseInterface::~PluginReverseInterface() { | |
69 NaClCondVarDtor(&cv_); | |
70 NaClMutexDtor(&mu_); | |
71 } | |
72 | |
73 void PluginReverseInterface::ShutDown() { | |
74 nacl::MutexLocker take(&mu_); | |
75 shutting_down_ = true; | |
76 NaClXCondVarBroadcast(&cv_); | |
77 } | |
78 | |
79 void PluginReverseInterface::Log(nacl::string message) { | |
80 LogToJavaScriptConsoleResource* continuation = | |
81 new LogToJavaScriptConsoleResource(message); | |
82 CHECK(continuation != NULL); | |
83 NaClLog(4, "PluginReverseInterface::Log(%s)\n", message.c_str()); | |
84 plugin::WeakRefCallOnMainThread( | |
85 anchor_, | |
86 0, /* delay in ms */ | |
87 this, | |
88 &plugin::PluginReverseInterface::Log_MainThreadContinuation, | |
89 continuation); | |
90 } | |
91 | |
92 void PluginReverseInterface::StartupInitializationComplete() { | |
93 NaClLog(0, "PluginReverseInterface::StartupInitializationComplete\n"); | |
94 if (init_done_cb_.pp_completion_callback().func != NULL) { | |
95 NaClLog(0, | |
96 "PluginReverseInterface::StartupInitializationComplete:" | |
97 " invoking CB\n"); | |
98 pp::Module::Get()->core()->CallOnMainThread(0, init_done_cb_, PP_OK); | |
99 } else { | |
100 NaClLog(0, | |
101 "PluginReverseInterface::StartupInitializationComplete:" | |
102 " init_done_cb_ not valid, skipping.\n"); | |
103 } | |
104 } | |
105 | |
106 void PluginReverseInterface::Log_MainThreadContinuation( | |
107 LogToJavaScriptConsoleResource* p, | |
108 int32_t err) { | |
109 UNREFERENCED_PARAMETER(err); | |
110 NaClLog(4, | |
111 "PluginReverseInterface::Log_MainThreadContinuation(%s)\n", | |
112 p->message.c_str()); | |
113 plugin_->browser_interface()->AddToConsole(static_cast<Plugin*>(plugin_), | |
114 p->message); | |
115 } | |
116 | |
117 bool PluginReverseInterface::EnumerateManifestKeys( | |
118 std::set<nacl::string>* out_keys) { | |
119 Manifest const* mp = plugin_->manifest(); | |
120 | |
121 if (!mp->GetFileKeys(out_keys)) { | |
122 return false; | |
123 } | |
124 | |
125 return true; | |
126 } | |
127 | |
128 // TODO(bsy): OpenManifestEntry should use the manifest to ResolveKey | |
129 // and invoke StreamAsFile with a completion callback that invokes | |
130 // GetPOSIXFileDesc. | |
131 bool PluginReverseInterface::OpenManifestEntry(nacl::string url_key, | |
132 int32_t* out_desc) { | |
133 ErrorInfo error_info; | |
134 bool is_portable = false; | |
135 bool op_complete = false; // NB: mu_ and cv_ also controls access to this! | |
136 OpenManifestEntryResource* to_open = | |
137 new OpenManifestEntryResource(url_key, out_desc, | |
138 &error_info, &is_portable, &op_complete); | |
139 CHECK(to_open != NULL); | |
140 NaClLog(4, "PluginReverseInterface::OpenManifestEntry: %s\n", | |
141 url_key.c_str()); | |
142 // This assumes we are not on the main thread. If false, we deadlock. | |
143 plugin::WeakRefCallOnMainThread( | |
144 anchor_, | |
145 0, | |
146 this, | |
147 &plugin::PluginReverseInterface::OpenManifestEntry_MainThreadContinuation, | |
148 to_open); | |
149 NaClLog(4, | |
150 "PluginReverseInterface::OpenManifestEntry:" | |
151 " waiting on main thread\n"); | |
152 bool shutting_down; | |
153 do { | |
154 nacl::MutexLocker take(&mu_); | |
155 for (;;) { | |
156 NaClLog(4, | |
157 "PluginReverseInterface::OpenManifestEntry:" | |
158 " got lock, checking shutdown and completion: (%s, %s)\n", | |
159 shutting_down_ ? "yes" : "no", | |
160 op_complete ? "yes" : "no"); | |
161 shutting_down = shutting_down_; | |
162 if (op_complete || shutting_down) { | |
163 NaClLog(4, | |
164 "PluginReverseInterface::OpenManifestEntry:" | |
165 " done!\n"); | |
166 break; | |
167 } | |
168 NaClXCondVarWait(&cv_, &mu_); | |
169 } | |
170 } while (0); | |
171 if (shutting_down) { | |
172 NaClLog(4, | |
173 "PluginReverseInterface::OpenManifestEntry:" | |
174 " plugin is shutting down\n"); | |
175 return false; | |
176 } | |
177 // out_desc has the returned descriptor if successful, else -1. | |
178 | |
179 // The caller is responsible for not closing *out_desc. If it is | |
180 // closed prematurely, then another open could re-use the OS | |
181 // descriptor, confusing the opened_ map. If the caller is going to | |
182 // want to make a NaClDesc object and transfer it etc., then the | |
183 // caller should DUP the descriptor (but remember the original | |
184 // value) for use by the NaClDesc object, which closes when the | |
185 // object is destroyed. | |
186 NaClLog(4, | |
187 "PluginReverseInterface::OpenManifestEntry:" | |
188 " *out_desc = %d\n", | |
189 *out_desc); | |
190 if (*out_desc == -1) { | |
191 // TODO(bsy,ncbray): what else should we do with the error? This | |
192 // is a runtime error that may simply be a programming error in | |
193 // the untrusted code, or it may be something else wrong w/ the | |
194 // manifest. | |
195 NaClLog(4, | |
196 "OpenManifestEntry: failed for key %s, code %d (%s)\n", | |
197 url_key.c_str(), | |
198 error_info.error_code(), | |
199 error_info.message().c_str()); | |
200 } | |
201 return true; | |
202 } | |
203 | |
204 void PluginReverseInterface::OpenManifestEntry_MainThreadContinuation( | |
205 OpenManifestEntryResource* p, | |
206 int32_t err) { | |
207 OpenManifestEntryResource *open_cont; | |
208 UNREFERENCED_PARAMETER(err); | |
209 // CallOnMainThread continuations always called with err == PP_OK. | |
210 | |
211 NaClLog(4, "Entered OpenManifestEntry_MainThreadContinuation\n"); | |
212 | |
213 std::string mapped_url; | |
214 if (!plugin_->manifest()->ResolveKey(p->url, &mapped_url, | |
215 p->error_info, p->is_portable)) { | |
216 NaClLog(4, "OpenManifestEntry_MainThreadContinuation: ResolveKey failed\n"); | |
217 // Failed, and error_info has the details on what happened. Wake | |
218 // up requesting thread -- we are done. | |
219 nacl::MutexLocker take(&mu_); | |
220 *p->op_complete_ptr = true; // done... | |
221 *p->out_desc = -1; // but failed. | |
222 NaClXCondVarBroadcast(&cv_); | |
223 return; | |
224 } | |
225 NaClLog(4, | |
226 "OpenManifestEntry_MainThreadContinuation: ResolveKey: %s -> %s\n", | |
227 p->url.c_str(), mapped_url.c_str()); | |
228 | |
229 open_cont = new OpenManifestEntryResource(*p); // copy ctor! | |
230 CHECK(open_cont != NULL); | |
231 open_cont->url = mapped_url; | |
232 pp::CompletionCallback stream_cc = WeakRefNewCallback( | |
233 anchor_, | |
234 this, | |
235 &PluginReverseInterface::StreamAsFile_MainThreadContinuation, | |
236 open_cont); | |
237 if (!plugin_->StreamAsFile(mapped_url, stream_cc.pp_completion_callback())) { | |
238 NaClLog(4, | |
239 "OpenManifestEntry_MainThreadContinuation: StreamAsFile failed\n"); | |
240 nacl::MutexLocker take(&mu_); | |
241 *p->op_complete_ptr = true; // done... | |
242 *p->out_desc = -1; // but failed. | |
243 p->error_info->SetReport(ERROR_MANIFEST_OPEN, | |
244 "ServiceRuntime: StreamAsFile failed"); | |
245 NaClXCondVarBroadcast(&cv_); | |
246 return; | |
247 } | |
248 NaClLog(4, | |
249 "OpenManifestEntry_MainThreadContinuation: StreamAsFile okay\n"); | |
250 // p is deleted automatically | |
251 } | |
252 | |
253 void PluginReverseInterface::StreamAsFile_MainThreadContinuation( | |
254 OpenManifestEntryResource* p, | |
255 int32_t result) { | |
256 NaClLog(4, | |
257 "Entered StreamAsFile_MainThreadContinuation\n"); | |
258 | |
259 nacl::MutexLocker take(&mu_); | |
260 if (result == PP_OK) { | |
261 NaClLog(4, "StreamAsFile_MainThreadContinuation: GetPOSIXFileDesc(%s)\n", | |
262 p->url.c_str()); | |
263 *p->out_desc = plugin_->GetPOSIXFileDesc(p->url); | |
264 NaClLog(4, | |
265 "StreamAsFile_MainThreadContinuation: PP_OK, desc %d\n", | |
266 *p->out_desc); | |
267 } else { | |
268 NaClLog(4, | |
269 "StreamAsFile_MainThreadContinuation: !PP_OK, setting desc -1\n"); | |
270 *p->out_desc = -1; | |
271 p->error_info->SetReport(ERROR_MANIFEST_OPEN, | |
272 "Plugin StreamAsFile failed at callback"); | |
273 } | |
274 *p->op_complete_ptr = true; | |
275 NaClXCondVarBroadcast(&cv_); | |
276 } | |
277 | |
278 bool PluginReverseInterface::CloseManifestEntry(int32_t desc) { | |
279 bool op_complete; | |
280 bool op_result; | |
281 CloseManifestEntryResource* to_close = | |
282 new CloseManifestEntryResource(desc, &op_complete, &op_result); | |
283 | |
284 bool shutting_down; | |
285 plugin::WeakRefCallOnMainThread( | |
286 anchor_, | |
287 0, | |
288 this, | |
289 &plugin::PluginReverseInterface:: | |
290 CloseManifestEntry_MainThreadContinuation, | |
291 to_close); | |
292 // wait for completion or surf-away. | |
293 do { | |
294 nacl::MutexLocker take(&mu_); | |
295 for (;;) { | |
296 shutting_down = shutting_down_; | |
297 if (op_complete || shutting_down) { | |
298 break; | |
299 } | |
300 NaClXCondVarWait(&cv_, &mu_); | |
301 } | |
302 } while (0); | |
303 | |
304 if (shutting_down) return false; | |
305 // op_result true if close was successful; false otherwise (e.g., bad desc). | |
306 return op_result; | |
307 } | |
308 | |
309 void PluginReverseInterface::CloseManifestEntry_MainThreadContinuation( | |
310 CloseManifestEntryResource* cls, | |
311 int32_t err) { | |
312 UNREFERENCED_PARAMETER(err); | |
313 | |
314 nacl::MutexLocker take(&mu_); | |
315 // TODO(bsy): once the plugin has a reliable way to report that the | |
316 // file usage is done -- and sel_ldr uses this RPC call -- we should | |
317 // tell the plugin that the associated resources can be freed. | |
318 *cls->op_result_ptr = true; | |
319 *cls->op_complete_ptr = true; | |
320 NaClXCondVarBroadcast(&cv_); | |
321 // cls automatically deleted | |
322 } | |
323 | |
324 ServiceRuntime::ServiceRuntime(Plugin* plugin, | |
325 pp::CompletionCallback init_done_cb) | |
326 : plugin_(plugin), | |
327 browser_interface_(plugin->browser_interface()), | |
328 reverse_service_(NULL), | |
329 subprocess_(NULL), | |
330 async_receive_desc_(NULL), | |
331 async_send_desc_(NULL), | |
332 anchor_(new nacl::WeakRefAnchor()), | |
333 rev_interface_(new PluginReverseInterface(anchor_, plugin, | |
334 init_done_cb)) { | |
335 NaClSrpcChannelInitialize(&command_channel_); | |
336 } | |
337 | |
338 bool ServiceRuntime::InitCommunication(nacl::DescWrapper* nacl_desc, | |
339 ErrorInfo* error_info) { | |
340 PLUGIN_PRINTF(("ServiceRuntime::InitCommunication" | |
341 " (this=%p, subprocess=%p)\n", | |
342 static_cast<void*>(this), | |
343 static_cast<void*>(subprocess_.get()))); | |
344 // Create the command channel to the sel_ldr and load the nexe from nacl_desc. | |
345 if (!subprocess_->SetupCommandAndLoad(&command_channel_, nacl_desc)) { | |
346 error_info->SetReport(ERROR_SEL_LDR_COMMUNICATION_CMD_CHANNEL, | |
347 "ServiceRuntime: command channel creation failed"); | |
348 return false; | |
349 } | |
350 // Hook up the reverse service channel. We are the IMC client, but | |
351 // provide SRPC service. | |
352 NaClDesc* out_conn_cap; | |
353 NaClSrpcResultCodes rpc_result = | |
354 NaClSrpcInvokeBySignature(&command_channel_, | |
355 "reverse_setup::h", | |
356 &out_conn_cap); | |
357 | |
358 if (NACL_SRPC_RESULT_OK != rpc_result) { | |
359 error_info->SetReport(ERROR_SEL_LDR_COMMUNICATION_REV_SETUP, | |
360 "ServiceRuntime: reverse setup rpc failed"); | |
361 return false; | |
362 } | |
363 // Get connection capability to service runtime where the IMC | |
364 // server/SRPC client is waiting for a rendezvous. | |
365 PLUGIN_PRINTF(("ServiceRuntime: got 0x%"NACL_PRIxPTR"\n", | |
366 (uintptr_t) out_conn_cap)); | |
367 nacl::DescWrapper* conn_cap = plugin_->wrapper_factory()->MakeGenericCleanup( | |
368 out_conn_cap); | |
369 if (conn_cap == NULL) { | |
370 error_info->SetReport(ERROR_SEL_LDR_COMMUNICATION_WRAPPER, | |
371 "ServiceRuntime: wrapper allocation failure"); | |
372 return false; | |
373 } | |
374 out_conn_cap = NULL; // ownership passed | |
375 reverse_service_ = new nacl::ReverseService(conn_cap, rev_interface_->Ref()); | |
376 if (!reverse_service_->Start()) { | |
377 error_info->SetReport(ERROR_SEL_LDR_COMMUNICATION_REV_SERVICE, | |
378 "ServiceRuntime: starting reverse services failed"); | |
379 return false; | |
380 } | |
381 | |
382 #if NACL_WINDOWS && !defined(NACL_STANDALONE) | |
383 // Establish the communication for handle passing protocol | |
384 struct NaClDesc* desc = NaClHandlePassBrowserGetSocketAddress(); | |
385 | |
386 DWORD my_pid = GetCurrentProcessId(); | |
387 nacl::Handle my_handle = GetCurrentProcess(); | |
388 nacl::Handle my_handle_in_selldr; | |
389 | |
390 if (!DuplicateHandle(GetCurrentProcess(), | |
391 my_handle, | |
392 subprocess_->child_process(), | |
393 &my_handle_in_selldr, | |
394 PROCESS_DUP_HANDLE, | |
395 FALSE, | |
396 0)) { | |
397 error_info->SetReport(ERROR_SEL_LDR_HANDLE_PASSING, | |
398 "ServiceRuntime: failed handle passing protocol"); | |
399 return false; | |
400 } | |
401 | |
402 rpc_result = | |
403 NaClSrpcInvokeBySignature(&command_channel_, | |
404 "init_handle_passing:hii:", | |
405 desc, | |
406 my_pid, | |
407 reinterpret_cast<int>(my_handle_in_selldr)); | |
408 | |
409 if (NACL_SRPC_RESULT_OK != rpc_result) { | |
410 error_info->SetReport(ERROR_SEL_LDR_HANDLE_PASSING, | |
411 "ServiceRuntime: failed handle passing protocol"); | |
412 return false; | |
413 } | |
414 #endif | |
415 // start the module. otherwise we cannot connect for multimedia | |
416 // subsystem since that is handled by user-level code (not secure!) | |
417 // in libsrpc. | |
418 int load_status = -1; | |
419 rpc_result = | |
420 NaClSrpcInvokeBySignature(&command_channel_, | |
421 "start_module::i", | |
422 &load_status); | |
423 | |
424 if (NACL_SRPC_RESULT_OK != rpc_result) { | |
425 error_info->SetReport(ERROR_SEL_LDR_START_MODULE, | |
426 "ServiceRuntime: could not start nacl module"); | |
427 return false; | |
428 } | |
429 PLUGIN_PRINTF(("ServiceRuntime::InitCommunication (load_status=%d)\n", | |
430 load_status)); | |
431 plugin_->ReportSelLdrLoadStatus(load_status); | |
432 if (LOAD_OK != load_status) { | |
433 error_info->SetReport( | |
434 ERROR_SEL_LDR_START_STATUS, | |
435 NaClErrorString(static_cast<NaClErrorCode>(load_status))); | |
436 return false; | |
437 } | |
438 return true; | |
439 } | |
440 | |
441 bool ServiceRuntime::Start(nacl::DescWrapper* nacl_desc, | |
442 ErrorInfo* error_info) { | |
443 PLUGIN_PRINTF(("ServiceRuntime::Start (nacl_desc=%p)\n", | |
444 reinterpret_cast<void*>(nacl_desc))); | |
445 | |
446 nacl::scoped_ptr<nacl::SelLdrLauncher> | |
447 tmp_subprocess(new(std::nothrow) nacl::SelLdrLauncher()); | |
448 if (NULL == tmp_subprocess.get()) { | |
449 PLUGIN_PRINTF(("ServiceRuntime::Start (subprocess create failed)\n")); | |
450 error_info->SetReport(ERROR_SEL_LDR_CREATE_LAUNCHER, | |
451 "ServiceRuntime: failed to create sel_ldr launcher"); | |
452 return false; | |
453 } | |
454 nacl::Handle sockets[3]; | |
455 if (!tmp_subprocess->Start(NACL_ARRAY_SIZE(sockets), sockets)) { | |
456 PLUGIN_PRINTF(("ServiceRuntime::Start (start failed)\n")); | |
457 error_info->SetReport(ERROR_SEL_LDR_LAUNCH, | |
458 "ServiceRuntime: failed to start"); | |
459 return false; | |
460 } | |
461 | |
462 async_receive_desc_.reset( | |
463 plugin()->wrapper_factory()->MakeImcSock(sockets[1])); | |
464 async_send_desc_.reset(plugin()->wrapper_factory()->MakeImcSock(sockets[2])); | |
465 | |
466 subprocess_.reset(tmp_subprocess.release()); | |
467 if (!InitCommunication(nacl_desc, error_info)) { | |
468 subprocess_.reset(NULL); | |
469 return false; | |
470 } | |
471 | |
472 PLUGIN_PRINTF(("ServiceRuntime::Start (return 1)\n")); | |
473 return true; | |
474 } | |
475 | |
476 SrpcClient* ServiceRuntime::SetupAppChannel() { | |
477 PLUGIN_PRINTF(("ServiceRuntime::SetupAppChannel (subprocess_=%p)\n", | |
478 reinterpret_cast<void*>(subprocess_.get()))); | |
479 nacl::DescWrapper* connect_desc = subprocess_->socket_addr()->Connect(); | |
480 if (NULL == connect_desc) { | |
481 PLUGIN_PRINTF(("ServiceRuntime::SetupAppChannel (connect failed)\n")); | |
482 return NULL; | |
483 } else { | |
484 PLUGIN_PRINTF(("ServiceRuntime::SetupAppChannel (conect_desc=%p)\n", | |
485 static_cast<void*>(connect_desc))); | |
486 SrpcClient* srpc_client = SrpcClient::New(plugin(), connect_desc); | |
487 PLUGIN_PRINTF(("ServiceRuntime::SetupAppChannel (srpc_client=%p)\n", | |
488 static_cast<void*>(srpc_client))); | |
489 return srpc_client; | |
490 } | |
491 } | |
492 | |
493 bool ServiceRuntime::Kill() { | |
494 return subprocess_->KillChildProcess(); | |
495 } | |
496 | |
497 bool ServiceRuntime::Log(int severity, nacl::string msg) { | |
498 NaClSrpcResultCodes rpc_result = | |
499 NaClSrpcInvokeBySignature(&command_channel_, | |
500 "log:is:", | |
501 severity, | |
502 strdup(msg.c_str())); | |
503 return (NACL_SRPC_RESULT_OK == rpc_result); | |
504 } | |
505 | |
506 void ServiceRuntime::Shutdown() { | |
507 if (subprocess_ != NULL) { | |
508 Kill(); | |
509 } | |
510 rev_interface_->ShutDown(); | |
511 anchor_->Abandon(); | |
512 // Abandon callbacks, tell service threads to quit if they were | |
513 // blocked waiting for main thread operations to finish. Note that | |
514 // some callbacks must still await their completion event, e.g., | |
515 // CallOnMainThread must still wait for the time out, or I/O events | |
516 // must finish, so resources associated with pending events cannot | |
517 // be deallocated. | |
518 | |
519 // Note that this does waitpid() to get rid of any zombie subprocess. | |
520 subprocess_.reset(NULL); | |
521 | |
522 NaClSrpcDtor(&command_channel_); | |
523 | |
524 // subprocess_ killed, but threads waiting on messages from the | |
525 // service runtime may not have noticed yet. The low-level | |
526 // NaClSimpleRevService code takes care to refcount the data objects | |
527 // that it needs, and reverse_service_ is also refcounted. We wait | |
528 // for the service threads to get their EOF indications. | |
529 if (reverse_service_ != NULL) { | |
530 reverse_service_->WaitForServiceThreadsToExit(); | |
531 reverse_service_->Unref(); | |
532 reverse_service_ = NULL; | |
533 } | |
534 } | |
535 | |
536 ServiceRuntime::~ServiceRuntime() { | |
537 PLUGIN_PRINTF(("ServiceRuntime::~ServiceRuntime (this=%p)\n", | |
538 static_cast<void*>(this))); | |
539 // We do this just in case Shutdown() was not called. | |
540 subprocess_.reset(NULL); | |
541 if (reverse_service_ != NULL) { | |
542 reverse_service_->Unref(); | |
543 } | |
544 | |
545 rev_interface_->Unref(); | |
546 | |
547 anchor_->Unref(); | |
548 } | |
549 | |
550 } // namespace plugin | |
OLD | NEW |