1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "ppapi/native_client/src/trusted/plugin/plugin.h"
6
7 #include <sys/stat.h>
8 #include <sys/types.h>
9
10 #include <string>
11
12 #include "native_client/src/include/nacl_base.h"
13 #include "native_client/src/include/nacl_macros.h"
14 #include "native_client/src/include/nacl_scoped_ptr.h"
15 #include "native_client/src/include/nacl_string.h"
16 #include "native_client/src/include/portability.h"
17 #include "native_client/src/include/portability_io.h"
18 #include "native_client/src/include/portability_string.h"
19 #include "native_client/src/public/nacl_file_info.h"
20 #include "native_client/src/shared/platform/nacl_check.h"
21 #include "native_client/src/trusted/desc/nacl_desc_wrapper.h"
22 #include "native_client/src/trusted/nonnacl_util/sel_ldr_launcher.h"
23 #include "native_client/src/trusted/service_runtime/nacl_error_code.h"
24
25 #include "ppapi/c/pp_errors.h"
26 #include "ppapi/c/private/ppb_nacl_private.h"
27 #include "ppapi/cpp/dev/url_util_dev.h"
28 #include "ppapi/cpp/module.h"
29
30 #include "ppapi/native_client/src/trusted/plugin/nacl_subprocess.h"
31 #include "ppapi/native_client/src/trusted/plugin/plugin_error.h"
32 #include "ppapi/native_client/src/trusted/plugin/service_runtime.h"
33 #include "ppapi/native_client/src/trusted/plugin/utility.h"
34
35 namespace plugin {
36
37 namespace {
38
39 // Up to 20 seconds
40 const int64_t kTimeSmallMin = 1; // in ms
41 const int64_t kTimeSmallMax = 20000; // in ms
42 const uint32_t kTimeSmallBuckets = 100;
43
44 } // namespace
45
ShutDownSubprocesses()46 void Plugin::ShutDownSubprocesses() {
47 PLUGIN_PRINTF(("Plugin::ShutDownSubprocesses (this=%p)\n",
48 static_cast<void*>(this)));
49 PLUGIN_PRINTF(("Plugin::ShutDownSubprocesses (%s)\n",
50 main_subprocess_.detailed_description().c_str()));
51
52 // Shut down service runtime. This must be done before all other calls so
53 // they don't block forever when waiting for the upcall thread to exit.
54 main_subprocess_.Shutdown();
55
56 PLUGIN_PRINTF(("Plugin::ShutDownSubprocess (this=%p, return)\n",
57 static_cast<void*>(this)));
58 }
59
HistogramTimeSmall(const std::string & name,int64_t ms)60 void Plugin::HistogramTimeSmall(const std::string& name,
61 int64_t ms) {
62 if (ms < 0) return;
63 uma_interface_.HistogramCustomTimes(name,
64 ms,
65 kTimeSmallMin, kTimeSmallMax,
66 kTimeSmallBuckets);
67 }
68
LoadHelperNaClModule(PP_FileHandle file_handle,NaClSubprocess * subprocess,const SelLdrStartParams & params)69 bool Plugin::LoadHelperNaClModule(PP_FileHandle file_handle,
70 NaClSubprocess* subprocess,
71 const SelLdrStartParams& params) {
72 CHECK(!pp::Module::Get()->core()->IsMainThread());
73 ServiceRuntime* service_runtime =
74 new ServiceRuntime(this,
75 false, // No main_service_runtime.
76 false, // No non-SFI mode (i.e. in SFI-mode).
77 pp::BlockUntilComplete(), pp::BlockUntilComplete());
78 subprocess->set_service_runtime(service_runtime);
79 PLUGIN_PRINTF(("Plugin::LoadHelperNaClModule "
80 "(service_runtime=%p)\n",
81 static_cast<void*>(service_runtime)));
82
83 // Now start the SelLdr instance. This must be created on the main thread.
84 bool service_runtime_started = false;
85 pp::CompletionCallback sel_ldr_callback =
86 callback_factory_.NewCallback(&Plugin::SignalStartSelLdrDone,
87 &service_runtime_started,
88 service_runtime);
89 pp::CompletionCallback callback =
90 callback_factory_.NewCallback(&Plugin::StartSelLdrOnMainThread,
91 service_runtime, params,
92 sel_ldr_callback);
93 pp::Module::Get()->core()->CallOnMainThread(0, callback, 0);
94 if (!service_runtime->WaitForSelLdrStart()) {
95 PLUGIN_PRINTF(("Plugin::LoadHelperNaClModule "
96 "WaitForSelLdrStart timed out!\n"));
97 return false;
98 }
99 PLUGIN_PRINTF(("Plugin::LoadHelperNaClModule (service_runtime_started=%d)\n",
100 service_runtime_started));
101 if (!service_runtime_started)
102 return false;
103
104 PP_NaClFileInfo info;
105 info.handle = file_handle;
106 info.token_lo = 0;
107 info.token_hi = 0;
108
109 // Now actually load the nexe, which can happen on a background thread.
110 //
111 // We can't use pp::BlockUntilComplete() inside an in-process plugin, so we
112 // have to roll our own blocking logic, similar to WaitForSelLdrStart()
113 // above, except without timeout logic.
114 bool nexe_started = false;
115 pp::CompletionCallback nexe_started_callback = callback_factory_.NewCallback(
116 &Plugin::SignalNexeStarted, &nexe_started, service_runtime);
117 pp::Module::Get()->core()->CallOnMainThread(
118 0,
119 callback_factory_.NewCallback(
120 &Plugin::LoadNexeAndStart,
121 service_runtime, info, nexe_started_callback));
122 service_runtime->WaitForNexeStart();
123
124 return nexe_started;
125 }
126
StartSelLdrOnMainThread(int32_t pp_error,ServiceRuntime * service_runtime,const SelLdrStartParams & params,pp::CompletionCallback callback)127 void Plugin::StartSelLdrOnMainThread(int32_t pp_error,
128 ServiceRuntime* service_runtime,
129 const SelLdrStartParams& params,
130 pp::CompletionCallback callback) {
131 if (pp_error != PP_OK) {
132 PLUGIN_PRINTF(("Plugin::StartSelLdrOnMainThread: non-PP_OK arg "
133 "-- SHOULD NOT HAPPEN\n"));
134 pp::Module::Get()->core()->CallOnMainThread(0, callback, pp_error);
135 return;
136 }
137 service_runtime->StartSelLdr(params, callback);
138 }
139
SignalStartSelLdrDone(int32_t pp_error,bool * started,ServiceRuntime * service_runtime)140 void Plugin::SignalStartSelLdrDone(int32_t pp_error,
141 bool* started,
142 ServiceRuntime* service_runtime) {
143 *started = (pp_error == PP_OK);
144 service_runtime->SignalStartSelLdrDone();
145 }
146
SignalNexeStarted(int32_t pp_error,bool * started,ServiceRuntime * service_runtime)147 void Plugin::SignalNexeStarted(int32_t pp_error,
148 bool* started,
149 ServiceRuntime* service_runtime) {
150 *started = (pp_error == PP_OK);
151 service_runtime->SignalNexeStarted();
152 }
153
LoadNaClModule(PP_NaClFileInfo file_info,bool uses_nonsfi_mode,bool enable_dyncode_syscalls,bool enable_exception_handling,bool enable_crash_throttling,const pp::CompletionCallback & init_done_cb,const pp::CompletionCallback & crash_cb)154 void Plugin::LoadNaClModule(PP_NaClFileInfo file_info,
155 bool uses_nonsfi_mode,
156 bool enable_dyncode_syscalls,
157 bool enable_exception_handling,
158 bool enable_crash_throttling,
159 const pp::CompletionCallback& init_done_cb,
160 const pp::CompletionCallback& crash_cb) {
161 CHECK(pp::Module::Get()->core()->IsMainThread());
162 // Before forking a new sel_ldr process, ensure that we do not leak
163 // the ServiceRuntime object for an existing subprocess, and that any
164 // associated listener threads do not go unjoined because if they
165 // outlive the Plugin object, they will not be memory safe.
166 ShutDownSubprocesses();
167 pp::Var manifest_base_url =
168 pp::Var(pp::PASS_REF, nacl_interface_->GetManifestBaseURL(pp_instance()));
169 std::string manifest_base_url_str = manifest_base_url.AsString();
170 SelLdrStartParams params(manifest_base_url_str,
171 true /* uses_irt */,
172 true /* uses_ppapi */,
173 enable_dyncode_syscalls,
174 enable_exception_handling,
175 enable_crash_throttling);
176 ErrorInfo error_info;
177 ServiceRuntime* service_runtime = new ServiceRuntime(
178 this, true, uses_nonsfi_mode, init_done_cb, crash_cb);
179 main_subprocess_.set_service_runtime(service_runtime);
180 PLUGIN_PRINTF(("Plugin::LoadNaClModule (service_runtime=%p)\n",
181 static_cast<void*>(service_runtime)));
182 if (NULL == service_runtime) {
183 error_info.SetReport(
184 PP_NACL_ERROR_SEL_LDR_INIT,
185 "sel_ldr init failure " + main_subprocess_.description());
186 ReportLoadError(error_info);
187 return;
188 }
189
190 // We don't take any action once nexe loading has completed, so pass an empty
191 // callback here for |callback|.
192 pp::CompletionCallback callback = callback_factory_.NewCallback(
193 &Plugin::LoadNexeAndStart,
194 service_runtime, file_info, pp::CompletionCallback());
195 StartSelLdrOnMainThread(
196 static_cast<int32_t>(PP_OK), service_runtime, params, callback);
197 }
198
LoadNexeAndStart(int32_t pp_error,ServiceRuntime * service_runtime,PP_NaClFileInfo file_info,const pp::CompletionCallback & callback)199 void Plugin::LoadNexeAndStart(int32_t pp_error,
200 ServiceRuntime* service_runtime,
201 PP_NaClFileInfo file_info,
202 const pp::CompletionCallback& callback) {
203 CHECK(pp::Module::Get()->core()->IsMainThread());
204 if (pp_error != PP_OK)
205 return;
206 service_runtime->LoadNexeAndStart(file_info, callback);
207 }
208
LoadNaClModuleContinuationIntern()209 bool Plugin::LoadNaClModuleContinuationIntern() {
210 ErrorInfo error_info;
211 if (!uses_nonsfi_mode_) {
212 if (!main_subprocess_.StartSrpcServices()) {
213 // The NaCl process probably crashed. On Linux, a crash causes this
214 // error, while on other platforms, the error is detected below, when we
215 // attempt to start the proxy. Report a module initialization error here,
216 // to make it less confusing for developers.
217 NaClLog(LOG_ERROR, "LoadNaClModuleContinuationIntern: "
218 "StartSrpcServices failed\n");
219 error_info.SetReport(PP_NACL_ERROR_START_PROXY_MODULE,
220 "could not initialize module.");
221 ReportLoadError(error_info);
222 return false;
223 }
224 }
225
226 bool result = PP_ToBool(nacl_interface_->StartPpapiProxy(pp_instance()));
227 if (result) {
228 PLUGIN_PRINTF(("Plugin::LoadNaClModule (%s)\n",
229 main_subprocess_.detailed_description().c_str()));
230 }
231 return result;
232 }
233
LoadHelperNaClModule(const nacl::string & helper_url,PP_FileHandle file_handle,ErrorInfo * error_info)234 NaClSubprocess* Plugin::LoadHelperNaClModule(const nacl::string& helper_url,
235 PP_FileHandle file_handle,
236 ErrorInfo* error_info) {
237 nacl::scoped_ptr<NaClSubprocess> nacl_subprocess(
238 new NaClSubprocess("helper module", NULL, NULL));
239 if (NULL == nacl_subprocess.get()) {
240 error_info->SetReport(PP_NACL_ERROR_SEL_LDR_INIT,
241 "unable to allocate helper subprocess.");
242 return NULL;
243 }
244
245 // Do not report UMA stats for translator-related nexes.
246 // TODO(sehr): define new UMA stats for translator related nexe events.
247 // NOTE: The PNaCl translator nexes are not built to use the IRT. This is
248 // done to save on address space and swap space.
249 SelLdrStartParams params(helper_url,
250 false /* uses_irt */,
251 false /* uses_ppapi */,
252 false /* enable_dyncode_syscalls */,
253 false /* enable_exception_handling */,
254 true /* enable_crash_throttling */);
255
256 // Helper NaCl modules always use the PNaCl manifest, as there is no
257 // corresponding NMF.
258 if (!LoadHelperNaClModule(file_handle, nacl_subprocess.get(), params)) {
259 return NULL;
260 }
261 // We need not wait for the init_done callback. We can block
262 // here in StartSrpcServices, since helper NaCl modules
263 // are spawned from a private thread.
264 //
265 // TODO(bsy): if helper module crashes, we should abort.
266 // crash_cb is not used here, so we are relying on crashes
267 // being detected in StartSrpcServices or later.
268 //
269 // NB: More refactoring might be needed, however, if helper
270 // NaCl modules have their own manifest. Currently the
271 // manifest is a per-plugin-instance object, not a per
272 // NaClSubprocess object.
273 if (!nacl_subprocess->StartSrpcServices()) {
274 error_info->SetReport(PP_NACL_ERROR_SRPC_CONNECTION_FAIL,
275 "SRPC connection failure for " +
276 nacl_subprocess->description());
277 return NULL;
278 }
279
280 PLUGIN_PRINTF(("Plugin::LoadHelperNaClModule (%s, %s)\n",
281 helper_url.c_str(),
282 nacl_subprocess.get()->detailed_description().c_str()));
283
284 return nacl_subprocess.release();
285 }
286
287 // All failures of this function will show up as "Missing Plugin-in", so
288 // there is no need to log to JS console that there was an initialization
289 // failure. Note that module loading functions will log their own errors.
Init(uint32_t argc,const char * argn[],const char * argv[])290 bool Plugin::Init(uint32_t argc, const char* argn[], const char* argv[]) {
291 PLUGIN_PRINTF(("Plugin::Init (argc=%" NACL_PRIu32 ")\n", argc));
292 nacl_interface_->InitializePlugin(pp_instance(), argc, argn, argv);
293 wrapper_factory_ = new nacl::DescWrapperFactory();
294 pp::CompletionCallback open_cb =
295 callback_factory_.NewCallback(&Plugin::NaClManifestFileDidOpen);
296 nacl_interface_->RequestNaClManifest(pp_instance(),
297 open_cb.pp_completion_callback());
298 return true;
299 }
300
Plugin(PP_Instance pp_instance)301 Plugin::Plugin(PP_Instance pp_instance)
302 : pp::Instance(pp_instance),
303 main_subprocess_("main subprocess", NULL, NULL),
304 uses_nonsfi_mode_(false),
305 wrapper_factory_(NULL),
306 nacl_interface_(NULL),
307 uma_interface_(this) {
308 PLUGIN_PRINTF(("Plugin::Plugin (this=%p, pp_instance=%"
309 NACL_PRId32 ")\n", static_cast<void*>(this), pp_instance));
310 callback_factory_.Initialize(this);
311 nacl_interface_ = GetNaClInterface();
312 CHECK(nacl_interface_ != NULL);
313
314 // Notify PPB_NaCl_Private that the instance is created before altering any
315 // state that it tracks.
316 nacl_interface_->InstanceCreated(pp_instance);
317 // We call set_exit_status() here to ensure that the 'exitStatus' property is
318 // set. This can only be called when nacl_interface_ is not NULL.
319 set_exit_status(-1);
320 nexe_file_info_.handle = PP_kInvalidFileHandle;
321 nexe_file_info_.token_lo = 0;
322 nexe_file_info_.token_hi = 0;
323 }
324
325
~Plugin()326 Plugin::~Plugin() {
327 int64_t shutdown_start = NaClGetTimeOfDayMicroseconds();
328
329 PLUGIN_PRINTF(("Plugin::~Plugin (this=%p)\n",
330 static_cast<void*>(this)));
331 // Destroy the coordinator while the rest of the data is still there
332 pnacl_coordinator_.reset(NULL);
333
334 nacl_interface_->InstanceDestroyed(pp_instance());
335
336 // ShutDownSubprocesses shuts down the main subprocess, which shuts
337 // down the main ServiceRuntime object, which kills the subprocess.
338 // As a side effect of the subprocess being killed, the reverse
339 // services thread(s) will get EOF on the reverse channel(s), and
340 // the thread(s) will exit. In ServiceRuntime::Shutdown, we invoke
341 // ReverseService::WaitForServiceThreadsToExit(), so that there will
342 // not be an extent thread(s) hanging around. This means that the
343 // ~Plugin will block until this happens. This is a requirement,
344 // since the renderer should be free to unload the plugin code, and
345 // we cannot have threads running code that gets unloaded before
346 // they exit.
347 //
348 // By waiting for the threads here, we also ensure that the Plugin
349 // object and the subprocess and ServiceRuntime objects is not
350 // (fully) destroyed while the threads are running, so resources
351 // that are destroyed after ShutDownSubprocesses (below) are
352 // guaranteed to be live and valid for access from the service
353 // threads.
354 //
355 // The main_subprocess object, which wraps the main service_runtime
356 // object, is dtor'd implicitly after the explicit code below runs,
357 // so the main service runtime object will not have been dtor'd,
358 // though the Shutdown method may have been called, during the
359 // lifetime of the service threads.
360 ShutDownSubprocesses();
361
362 delete wrapper_factory_;
363
364 HistogramTimeSmall(
365 "NaCl.Perf.ShutdownTime.Total",
366 (NaClGetTimeOfDayMicroseconds() - shutdown_start)
367 / NACL_MICROS_PER_MILLI);
368
369 PLUGIN_PRINTF(("Plugin::~Plugin (this=%p, return)\n",
370 static_cast<void*>(this)));
371 }
372
HandleDocumentLoad(const pp::URLLoader & url_loader)373 bool Plugin::HandleDocumentLoad(const pp::URLLoader& url_loader) {
374 PLUGIN_PRINTF(("Plugin::HandleDocumentLoad (this=%p)\n",
375 static_cast<void*>(this)));
376 // We don't know if the plugin will handle the document load, but return
377 // true in order to give it a chance to respond once the proxy is started.
378 return true;
379 }
380
NexeFileDidOpen(int32_t pp_error)381 void Plugin::NexeFileDidOpen(int32_t pp_error) {
382 if (pp_error != PP_OK)
383 return;
384
385 NaClLog(4, "NexeFileDidOpen: invoking LoadNaClModule\n");
386 LoadNaClModule(
387 nexe_file_info_,
388 uses_nonsfi_mode_,
389 true, /* enable_dyncode_syscalls */
390 true, /* enable_exception_handling */
391 false, /* enable_crash_throttling */
392 callback_factory_.NewCallback(&Plugin::NexeFileDidOpenContinuation),
393 callback_factory_.NewCallback(&Plugin::NexeDidCrash));
394 }
395
NexeFileDidOpenContinuation(int32_t pp_error)396 void Plugin::NexeFileDidOpenContinuation(int32_t pp_error) {
397 UNREFERENCED_PARAMETER(pp_error);
398 NaClLog(4, "Entered NexeFileDidOpenContinuation\n");
399 if (LoadNaClModuleContinuationIntern()) {
400 NaClLog(4, "NexeFileDidOpenContinuation: success;"
401 " setting histograms\n");
402 int64_t nexe_size = nacl_interface_->GetNexeSize(pp_instance());
403 nacl_interface_->ReportLoadSuccess(
404 pp_instance(), program_url_.c_str(), nexe_size, nexe_size);
405 } else {
406 NaClLog(4, "NexeFileDidOpenContinuation: failed.");
407 }
408 NaClLog(4, "Leaving NexeFileDidOpenContinuation\n");
409 }
410
NexeDidCrash(int32_t pp_error)411 void Plugin::NexeDidCrash(int32_t pp_error) {
412 PLUGIN_PRINTF(("Plugin::NexeDidCrash (pp_error=%" NACL_PRId32 ")\n",
413 pp_error));
414 if (pp_error != PP_OK) {
415 PLUGIN_PRINTF(("Plugin::NexeDidCrash: CallOnMainThread callback with"
416 " non-PP_OK arg -- SHOULD NOT HAPPEN\n"));
417 }
418
419 std::string crash_log =
420 main_subprocess_.service_runtime()->GetCrashLogOutput();
421 nacl_interface_->NexeDidCrash(pp_instance(), crash_log.c_str());
422 }
423
BitcodeDidTranslate(int32_t pp_error)424 void Plugin::BitcodeDidTranslate(int32_t pp_error) {
425 PLUGIN_PRINTF(("Plugin::BitcodeDidTranslate (pp_error=%" NACL_PRId32 ")\n",
426 pp_error));
427 if (pp_error != PP_OK) {
428 // Error should have been reported by pnacl. Just return.
429 PLUGIN_PRINTF(("Plugin::BitcodeDidTranslate error in Pnacl\n"));
430 return;
431 }
432
433 // Inform JavaScript that we successfully translated the bitcode to a nexe.
434 PP_FileHandle handle = pnacl_coordinator_->TakeTranslatedFileHandle();
435
436 PP_NaClFileInfo info;
437 info.handle = handle;
438 info.token_lo = 0;
439 info.token_hi = 0;
440 LoadNaClModule(
441 info,
442 false, /* uses_nonsfi_mode */
443 false, /* enable_dyncode_syscalls */
444 false, /* enable_exception_handling */
445 true, /* enable_crash_throttling */
446 callback_factory_.NewCallback(&Plugin::BitcodeDidTranslateContinuation),
447 callback_factory_.NewCallback(&Plugin::NexeDidCrash));
448 }
449
BitcodeDidTranslateContinuation(int32_t pp_error)450 void Plugin::BitcodeDidTranslateContinuation(int32_t pp_error) {
451 NaClLog(4, "Entered BitcodeDidTranslateContinuation\n");
452 UNREFERENCED_PARAMETER(pp_error);
453 if (LoadNaClModuleContinuationIntern()) {
454 int64_t loaded;
455 int64_t total;
456 // TODO(teravest): Tighten this up so we can get rid of
457 // GetCurrentProgress(). loaded should always equal total.
458 pnacl_coordinator_->GetCurrentProgress(&loaded, &total);
459 nacl_interface_->ReportLoadSuccess(
460 pp_instance(), program_url_.c_str(), loaded, total);
461 }
462 }
463
NaClManifestFileDidOpen(int32_t pp_error)464 void Plugin::NaClManifestFileDidOpen(int32_t pp_error) {
465 PLUGIN_PRINTF(("Plugin::NaClManifestFileDidOpen (pp_error=%"
466 NACL_PRId32 ")\n", pp_error));
467 if (pp_error != PP_OK)
468 return;
469
470 PP_Var pp_program_url;
471 PP_PNaClOptions pnacl_options = {PP_FALSE, PP_FALSE, 2};
472 PP_Bool uses_nonsfi_mode;
473 if (nacl_interface_->GetManifestProgramURL(
474 pp_instance(), &pp_program_url, &pnacl_options, &uses_nonsfi_mode)) {
475 program_url_ = pp::Var(pp::PASS_REF, pp_program_url).AsString();
476 // TODO(teravest): Make ProcessNaClManifest take responsibility for more of
477 // this function.
478 nacl_interface_->ProcessNaClManifest(pp_instance(), program_url_.c_str());
479 uses_nonsfi_mode_ = PP_ToBool(uses_nonsfi_mode);
480 if (pnacl_options.translate) {
481 pp::CompletionCallback translate_callback =
482 callback_factory_.NewCallback(&Plugin::BitcodeDidTranslate);
483 pnacl_coordinator_.reset(
484 PnaclCoordinator::BitcodeToNative(this,
485 program_url_,
486 pnacl_options,
487 translate_callback));
488 return;
489 } else {
490 pp::CompletionCallback open_callback =
491 callback_factory_.NewCallback(&Plugin::NexeFileDidOpen);
492 // Will always call the callback on success or failure.
493 nacl_interface_->DownloadNexe(pp_instance(),
494 program_url_.c_str(),
495 &nexe_file_info_,
496 open_callback.pp_completion_callback());
497 return;
498 }
499 }
500 }
501
ReportLoadError(const ErrorInfo & error_info)502 void Plugin::ReportLoadError(const ErrorInfo& error_info) {
503 nacl_interface_->ReportLoadError(pp_instance(),
504 error_info.error_code(),
505 error_info.message().c_str());
506 }
507
DocumentCanRequest(const std::string & url)508 bool Plugin::DocumentCanRequest(const std::string& url) {
509 CHECK(pp::Module::Get()->core()->IsMainThread());
510 CHECK(pp::URLUtil_Dev::Get() != NULL);
511 return pp::URLUtil_Dev::Get()->DocumentCanRequest(this, pp::Var(url));
512 }
513
set_exit_status(int exit_status)514 void Plugin::set_exit_status(int exit_status) {
515 pp::Core* core = pp::Module::Get()->core();
516 if (core->IsMainThread()) {
517 SetExitStatusOnMainThread(PP_OK, exit_status);
518 } else {
519 pp::CompletionCallback callback =
520 callback_factory_.NewCallback(&Plugin::SetExitStatusOnMainThread,
521 exit_status);
522 core->CallOnMainThread(0, callback, 0);
523 }
524 }
525
SetExitStatusOnMainThread(int32_t pp_error,int exit_status)526 void Plugin::SetExitStatusOnMainThread(int32_t pp_error,
527 int exit_status) {
528 DCHECK(pp::Module::Get()->core()->IsMainThread());
529 DCHECK(nacl_interface_);
530 nacl_interface_->SetExitStatus(pp_instance(), exit_status);
531 }
532
533
534 } // namespace plugin
535