Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 /* | 1 /* |
| 2 * Copyright (c) 2012 The Chromium Authors. All rights reserved. | 2 * Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 3 * Use of this source code is governed by a BSD-style license that can be | 3 * Use of this source code is governed by a BSD-style license that can be |
| 4 * found in the LICENSE file. | 4 * found in the LICENSE file. |
| 5 */ | 5 */ |
| 6 | 6 |
| 7 #define NACL_LOG_MODULE_NAME "Plugin_ServiceRuntime" | 7 #define NACL_LOG_MODULE_NAME "Plugin_ServiceRuntime" |
| 8 | 8 |
| 9 #include "ppapi/native_client/src/trusted/plugin/service_runtime.h" | 9 #include "ppapi/native_client/src/trusted/plugin/service_runtime.h" |
| 10 | 10 |
| (...skipping 418 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 429 | 429 |
| 430 void PluginReverseInterface::ReportExitStatus(int exit_status) { | 430 void PluginReverseInterface::ReportExitStatus(int exit_status) { |
| 431 service_runtime_->set_exit_status(exit_status); | 431 service_runtime_->set_exit_status(exit_status); |
| 432 } | 432 } |
| 433 | 433 |
| 434 int64_t PluginReverseInterface::RequestQuotaForWrite( | 434 int64_t PluginReverseInterface::RequestQuotaForWrite( |
| 435 nacl::string file_id, int64_t offset, int64_t bytes_to_write) { | 435 nacl::string file_id, int64_t offset, int64_t bytes_to_write) { |
| 436 return bytes_to_write; | 436 return bytes_to_write; |
| 437 } | 437 } |
| 438 | 438 |
| 439 // Thin wrapper for the arguments of LoadNexeAndStart(), as WeakRefNewCallback | |
| 440 // can take only one argument. Also, this dtor has the responsibility to invoke | |
| 441 // callbacks on destruction. | |
| 442 struct ServiceRuntime::LoadNexeAndStartData { | |
| 443 explicit LoadNexeAndStartData(const pp::CompletionCallback& callback) | |
| 444 : callback(callback) { | |
| 445 } | |
| 446 | |
| 447 ~LoadNexeAndStartData() { | |
| 448 // We must call the callbacks here if they are not yet called, otherwise | |
| 449 // the resource would be leaked. | |
| 450 if (callback.pp_completion_callback().func) | |
| 451 callback.RunAndClear(PP_ERROR_ABORTED); | |
| 452 } | |
| 453 | |
| 454 // On success path, this must be invoked manually. Otherwise the dtor would | |
| 455 // invoke callbacks with error code unexpectedly. | |
| 456 void Clear() { | |
| 457 callback = pp::CompletionCallback(); | |
| 458 } | |
| 459 | |
| 460 pp::CompletionCallback callback; | |
| 461 }; | |
| 462 | |
| 463 ServiceRuntime::ServiceRuntime(Plugin* plugin, | 439 ServiceRuntime::ServiceRuntime(Plugin* plugin, |
| 464 bool main_service_runtime, | 440 bool main_service_runtime, |
| 465 bool uses_nonsfi_mode, | 441 bool uses_nonsfi_mode, |
| 466 pp::CompletionCallback init_done_cb, | 442 pp::CompletionCallback init_done_cb, |
| 467 pp::CompletionCallback crash_cb) | 443 pp::CompletionCallback crash_cb) |
| 468 : plugin_(plugin), | 444 : plugin_(plugin), |
| 469 main_service_runtime_(main_service_runtime), | 445 main_service_runtime_(main_service_runtime), |
| 470 uses_nonsfi_mode_(uses_nonsfi_mode), | 446 uses_nonsfi_mode_(uses_nonsfi_mode), |
| 471 reverse_service_(NULL), | 447 reverse_service_(NULL), |
| 472 anchor_(new nacl::WeakRefAnchor()), | 448 anchor_(new nacl::WeakRefAnchor()), |
| 473 rev_interface_(new PluginReverseInterface(anchor_, plugin, this, | 449 rev_interface_(new PluginReverseInterface(anchor_, plugin, this, |
| 474 init_done_cb, crash_cb)), | 450 init_done_cb, crash_cb)), |
| 475 start_sel_ldr_done_(false), | 451 start_sel_ldr_done_(false), |
| 476 nexe_started_(false) { | 452 nexe_started_(false), |
| 453 nexe_started_ok_(false) { | |
| 477 NaClSrpcChannelInitialize(&command_channel_); | 454 NaClSrpcChannelInitialize(&command_channel_); |
| 478 NaClXMutexCtor(&mu_); | 455 NaClXMutexCtor(&mu_); |
| 479 NaClXCondVarCtor(&cond_); | 456 NaClXCondVarCtor(&cond_); |
| 480 } | 457 } |
| 481 | 458 |
| 482 void ServiceRuntime::LoadNexeAndStartAfterLoadModule( | |
| 483 LoadNexeAndStartData* data, int32_t pp_error) { | |
| 484 if (pp_error != PP_OK) { | |
| 485 DidLoadNexeAndStart(data, pp_error); | |
| 486 return; | |
| 487 } | |
| 488 | |
| 489 // Here, LoadModule is successfully done. So the remaining task is just | |
| 490 // calling StartModule(), here. | |
| 491 DidLoadNexeAndStart(data, StartModule() ? PP_OK : PP_ERROR_FAILED); | |
| 492 } | |
| 493 | |
| 494 void ServiceRuntime::DidLoadNexeAndStart( | |
| 495 LoadNexeAndStartData* data, int32_t pp_error) { | |
| 496 if (pp_error == PP_OK) { | |
| 497 NaClLog(4, "ServiceRuntime::LoadNexeAndStart (success)\n"); | |
|
hidehiko
2014/06/18 05:07:04
Probably we want to keep this lo, too.
| |
| 498 } else { | |
| 499 // On a load failure the service runtime does not crash itself to | |
| 500 // avoid a race where the no-more-senders error on the reverse | |
| 501 // channel esrvice thread might cause the crash-detection logic to | |
| 502 // kick in before the start_module RPC reply has been received. So | |
| 503 // we induce a service runtime crash here. We do not release | |
| 504 // subprocess_ since it's needed to collect crash log output after | |
| 505 // the error is reported. | |
| 506 Log(LOG_FATAL, "reap logs"); | |
| 507 if (NULL == reverse_service_) { | |
| 508 // No crash detector thread. | |
| 509 NaClLog(LOG_ERROR, "scheduling to get crash log\n"); | |
| 510 // Invoking rev_interface's method is workaround to avoid crash_cb | |
| 511 // gets called twice or more. We should clean this up later. | |
| 512 rev_interface_->ReportCrash(); | |
| 513 NaClLog(LOG_ERROR, "should fire soon\n"); | |
| 514 } else { | |
| 515 NaClLog(LOG_ERROR, "Reverse service thread will pick up crash log\n"); | |
| 516 } | |
| 517 } | |
| 518 | |
| 519 pp::Module::Get()->core()->CallOnMainThread(0, data->callback, pp_error); | |
| 520 | |
| 521 // Because the ownership of data is taken by caller, we must clear it | |
| 522 // manually here. Otherwise, its dtor invokes callbacks again. | |
| 523 data->Clear(); | |
| 524 } | |
| 525 | |
| 526 bool ServiceRuntime::SetupCommandChannel() { | 459 bool ServiceRuntime::SetupCommandChannel() { |
| 527 NaClLog(4, "ServiceRuntime::SetupCommand (this=%p, subprocess=%p)\n", | 460 NaClLog(4, "ServiceRuntime::SetupCommand (this=%p, subprocess=%p)\n", |
| 528 static_cast<void*>(this), | 461 static_cast<void*>(this), |
| 529 static_cast<void*>(subprocess_.get())); | 462 static_cast<void*>(subprocess_.get())); |
| 530 if (!subprocess_->SetupCommand(&command_channel_)) { | 463 if (!subprocess_->SetupCommand(&command_channel_)) { |
| 531 if (main_service_runtime_) { | 464 if (main_service_runtime_) { |
| 532 ErrorInfo error_info; | 465 ErrorInfo error_info; |
| 533 error_info.SetReport(PP_NACL_ERROR_SEL_LDR_COMMUNICATION_CMD_CHANNEL, | 466 error_info.SetReport(PP_NACL_ERROR_SEL_LDR_COMMUNICATION_CMD_CHANNEL, |
| 534 "ServiceRuntime: command channel creation failed"); | 467 "ServiceRuntime: command channel creation failed"); |
| 535 plugin_->ReportLoadError(error_info); | 468 plugin_->ReportLoadError(error_info); |
| 536 } | 469 } |
| 537 return false; | 470 return false; |
| 538 } | 471 } |
| 539 return true; | 472 return true; |
| 540 } | 473 } |
| 541 | 474 |
| 542 void ServiceRuntime::LoadModule(PP_NaClFileInfo file_info, | |
| 543 pp::CompletionCallback callback) { | |
| 544 NaClFileInfo nacl_file_info; | |
| 545 nacl_file_info.desc = ConvertFileDescriptor(file_info.handle, true); | |
| 546 nacl_file_info.file_token.lo = file_info.token_lo; | |
| 547 nacl_file_info.file_token.hi = file_info.token_hi; | |
| 548 NaClDesc* desc = NaClDescIoFromFileInfo(nacl_file_info, O_RDONLY); | |
| 549 if (desc == NULL) { | |
| 550 DidLoadModule(callback, PP_ERROR_FAILED); | |
| 551 return; | |
| 552 } | |
| 553 | |
| 554 // We don't use a scoped_ptr here since we would immediately release the | |
| 555 // DescWrapper to LoadModule(). | |
| 556 nacl::DescWrapper* wrapper = | |
| 557 plugin_->wrapper_factory()->MakeGenericCleanup(desc); | |
| 558 | |
| 559 // TODO(teravest, hidehiko): Replace this by Chrome IPC. | |
| 560 bool result = subprocess_->LoadModule(&command_channel_, wrapper); | |
| 561 DidLoadModule(callback, result ? PP_OK : PP_ERROR_FAILED); | |
| 562 } | |
| 563 | |
| 564 void ServiceRuntime::DidLoadModule(pp::CompletionCallback callback, | |
| 565 int32_t pp_error) { | |
| 566 if (pp_error != PP_OK) { | |
| 567 ErrorInfo error_info; | |
| 568 error_info.SetReport(PP_NACL_ERROR_SEL_LDR_COMMUNICATION_CMD_CHANNEL, | |
| 569 "ServiceRuntime: load module failed"); | |
| 570 plugin_->ReportLoadError(error_info); | |
| 571 } | |
| 572 callback.Run(pp_error); | |
| 573 } | |
| 574 | |
| 575 bool ServiceRuntime::InitReverseService() { | 475 bool ServiceRuntime::InitReverseService() { |
| 576 if (uses_nonsfi_mode_) { | 476 if (uses_nonsfi_mode_) { |
| 577 // In non-SFI mode, no reverse service is set up. Just returns success. | 477 // In non-SFI mode, no reverse service is set up. Just returns success. |
| 578 return true; | 478 return true; |
| 579 } | 479 } |
| 580 | 480 |
| 581 // Hook up the reverse service channel. We are the IMC client, but | 481 // Hook up the reverse service channel. We are the IMC client, but |
| 582 // provide SRPC service. | 482 // provide SRPC service. |
| 583 NaClDesc* out_conn_cap; | 483 NaClDesc* out_conn_cap; |
| 584 NaClSrpcResultCodes rpc_result = | 484 NaClSrpcResultCodes rpc_result = |
| (...skipping 153 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 738 } | 638 } |
| 739 return start_sel_ldr_done_; | 639 return start_sel_ldr_done_; |
| 740 } | 640 } |
| 741 | 641 |
| 742 void ServiceRuntime::SignalStartSelLdrDone() { | 642 void ServiceRuntime::SignalStartSelLdrDone() { |
| 743 nacl::MutexLocker take(&mu_); | 643 nacl::MutexLocker take(&mu_); |
| 744 start_sel_ldr_done_ = true; | 644 start_sel_ldr_done_ = true; |
| 745 NaClXCondVarSignal(&cond_); | 645 NaClXCondVarSignal(&cond_); |
| 746 } | 646 } |
| 747 | 647 |
| 748 void ServiceRuntime::WaitForNexeStart() { | 648 bool ServiceRuntime::WaitForNexeStart() { |
| 749 nacl::MutexLocker take(&mu_); | 649 nacl::MutexLocker take(&mu_); |
| 750 while (!nexe_started_) | 650 while (!nexe_started_) |
| 751 NaClXCondVarWait(&cond_, &mu_); | 651 NaClXCondVarWait(&cond_, &mu_); |
| 752 // Reset nexe_started_ here in case we run again. | 652 return nexe_started_ok_; |
| 753 nexe_started_ = false; | |
| 754 } | 653 } |
| 755 | 654 |
| 756 void ServiceRuntime::SignalNexeStarted() { | 655 void ServiceRuntime::SignalNexeStarted(bool ok) { |
| 757 nacl::MutexLocker take(&mu_); | 656 nacl::MutexLocker take(&mu_); |
| 758 nexe_started_ = true; | 657 nexe_started_ = true; |
|
hidehiko
2014/06/18 05:07:04
Probably we should rename this variable (and the m
Nick Bray (chromium)
2014/06/18 18:29:44
Done, but please provide suggestions when names ar
| |
| 658 nexe_started_ok_ = ok; | |
| 759 NaClXCondVarSignal(&cond_); | 659 NaClXCondVarSignal(&cond_); |
| 760 } | 660 } |
| 761 | 661 |
| 762 void ServiceRuntime::LoadNexeAndStart(PP_NaClFileInfo file_info, | 662 void ServiceRuntime::LoadNexeAndStart(PP_NaClFileInfo file_info) { |
| 763 const pp::CompletionCallback& callback) { | |
| 764 NaClLog(4, "ServiceRuntime::LoadNexeAndStart (handle_valid=%d " | 663 NaClLog(4, "ServiceRuntime::LoadNexeAndStart (handle_valid=%d " |
| 765 "token_lo=%" NACL_PRIu64 " token_hi=%" NACL_PRIu64 ")\n", | 664 "token_lo=%" NACL_PRIu64 " token_hi=%" NACL_PRIu64 ")\n", |
| 766 file_info.handle != PP_kInvalidFileHandle, | 665 file_info.handle != PP_kInvalidFileHandle, |
| 767 file_info.token_lo, | 666 file_info.token_lo, |
| 768 file_info.token_hi); | 667 file_info.token_hi); |
| 769 | 668 |
| 770 nacl::scoped_ptr<LoadNexeAndStartData> data( | 669 bool ok = LoadNexeAndStartInternal(file_info); |
| 771 new LoadNexeAndStartData(callback)); | 670 if (!ok) { |
| 772 if (!SetupCommandChannel() || !InitReverseService()) { | 671 ReapLogs(); |
| 773 DidLoadNexeAndStart(data.get(), PP_ERROR_FAILED); | |
| 774 return; | |
| 775 } | 672 } |
| 673 // This only matters if a background thread is waiting, but we signal in all | |
| 674 // cases to simplify the code. | |
| 675 SignalNexeStarted(ok); | |
| 676 } | |
| 776 | 677 |
| 777 LoadModule( | 678 bool ServiceRuntime::LoadNexeAndStartInternal(PP_NaClFileInfo file_info) { |
| 778 file_info, | 679 if(!SetupCommandChannel()) { |
| 779 WeakRefNewCallback(anchor_, | 680 return false; |
| 780 this, | 681 } |
| 781 &ServiceRuntime::LoadNexeAndStartAfterLoadModule, | 682 if (!InitReverseService()) { |
| 782 data.release())); // Delegate the ownership. | 683 return false; |
| 684 } | |
| 685 NaClFileInfo nacl_file_info; | |
| 686 nacl_file_info.desc = ConvertFileDescriptor(file_info.handle, true); | |
| 687 nacl_file_info.file_token.lo = file_info.token_lo; | |
| 688 nacl_file_info.file_token.hi = file_info.token_hi; | |
| 689 NaClDesc* desc = NaClDescIoFromFileInfo(nacl_file_info, O_RDONLY); | |
| 690 if (desc == NULL) { | |
| 691 // TODO(ncbray): better error reporting? | |
| 692 ErrorInfo error_info; | |
|
hidehiko
2014/06/18 05:07:04
Can we avoid dup?
Probably, we should refactor Lo
Nick Bray (chromium)
2014/06/18 18:29:44
Done. Good call. I am not 100% happy with report
| |
| 693 error_info.SetReport(PP_NACL_ERROR_SEL_LDR_COMMUNICATION_CMD_CHANNEL, | |
| 694 "ServiceRuntime: load module failed"); | |
| 695 plugin_->ReportLoadError(error_info); | |
| 696 return false; | |
| 697 } | |
| 698 // We don't use a scoped_ptr here since we would immediately release the | |
| 699 // DescWrapper to LoadModule(). | |
| 700 nacl::DescWrapper* wrapper = | |
| 701 plugin_->wrapper_factory()->MakeGenericCleanup(desc); | |
| 702 // TODO(teravest, hidehiko): Replace this by Chrome IPC. | |
| 703 if (!subprocess_->LoadModule(&command_channel_, wrapper)) { | |
| 704 ErrorInfo error_info; | |
| 705 error_info.SetReport(PP_NACL_ERROR_SEL_LDR_COMMUNICATION_CMD_CHANNEL, | |
| 706 "ServiceRuntime: load module failed"); | |
| 707 plugin_->ReportLoadError(error_info); | |
| 708 return false; | |
| 709 } | |
| 710 if (!StartModule()) { | |
|
teravest
2014/06/18 01:37:52
We had initially planned on LoadModule() being asy
Nick Bray (chromium)
2014/06/18 02:02:38
I think the rest of the cleanup is still good (?),
| |
| 711 return false; | |
| 712 } | |
| 713 return true; | |
| 714 } | |
| 715 | |
| 716 void ServiceRuntime::ReapLogs() { | |
| 717 // On a load failure the service runtime does not crash itself to | |
| 718 // avoid a race where the no-more-senders error on the reverse | |
| 719 // channel esrvice thread might cause the crash-detection logic to | |
| 720 // kick in before the start_module RPC reply has been received. So | |
| 721 // we induce a service runtime crash here. We do not release | |
| 722 // subprocess_ since it's needed to collect crash log output after | |
| 723 // the error is reported. | |
| 724 NaClLog(LOG_ERROR, "reap logs\n"); | |
|
hidehiko
2014/06/18 05:07:04
Why not fatal?
Nick Bray (chromium)
2014/06/18 18:29:44
Oh. This is even more subtle than I realized. It
| |
| 725 if (NULL == reverse_service_) { | |
| 726 // No crash detector thread. | |
| 727 NaClLog(LOG_ERROR, "scheduling to get crash log\n"); | |
| 728 // Invoking rev_interface's method is workaround to avoid crash_cb | |
| 729 // gets called twice or more. We should clean this up later. | |
| 730 rev_interface_->ReportCrash(); | |
| 731 NaClLog(LOG_ERROR, "should fire soon\n"); | |
| 732 } else { | |
| 733 NaClLog(LOG_ERROR, "Reverse service thread will pick up crash log\n"); | |
| 734 } | |
| 783 } | 735 } |
| 784 | 736 |
| 785 SrpcClient* ServiceRuntime::SetupAppChannel() { | 737 SrpcClient* ServiceRuntime::SetupAppChannel() { |
| 786 NaClLog(4, "ServiceRuntime::SetupAppChannel (subprocess_=%p)\n", | 738 NaClLog(4, "ServiceRuntime::SetupAppChannel (subprocess_=%p)\n", |
| 787 reinterpret_cast<void*>(subprocess_.get())); | 739 reinterpret_cast<void*>(subprocess_.get())); |
| 788 nacl::DescWrapper* connect_desc = subprocess_->socket_addr()->Connect(); | 740 nacl::DescWrapper* connect_desc = subprocess_->socket_addr()->Connect(); |
| 789 if (NULL == connect_desc) { | 741 if (NULL == connect_desc) { |
| 790 NaClLog(LOG_ERROR, "ServiceRuntime::SetupAppChannel (connect failed)\n"); | 742 NaClLog(LOG_ERROR, "ServiceRuntime::SetupAppChannel (connect failed)\n"); |
| 791 return NULL; | 743 return NULL; |
| 792 } else { | 744 } else { |
| (...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 859 | 811 |
| 860 nacl::string ServiceRuntime::GetCrashLogOutput() { | 812 nacl::string ServiceRuntime::GetCrashLogOutput() { |
| 861 if (NULL != subprocess_.get()) { | 813 if (NULL != subprocess_.get()) { |
| 862 return subprocess_->GetCrashLogOutput(); | 814 return subprocess_->GetCrashLogOutput(); |
| 863 } else { | 815 } else { |
| 864 return std::string(); | 816 return std::string(); |
| 865 } | 817 } |
| 866 } | 818 } |
| 867 | 819 |
| 868 } // namespace plugin | 820 } // namespace plugin |
| OLD | NEW |