// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. #include #include "vm/message_handler.h" #include "vm/dart.h" #include "vm/heap/safepoint.h" #include "vm/isolate.h" #include "vm/lockers.h" #include "vm/object.h" #include "vm/object_store.h" #include "vm/os.h" #include "vm/port.h" #include "vm/thread_interrupter.h" namespace dart { DECLARE_FLAG(bool, trace_service_pause_events); class MessageHandlerTask : public ThreadPool::Task { public: explicit MessageHandlerTask(MessageHandler* handler) : handler_(handler) { ASSERT(handler != NULL); } virtual void Run() { ASSERT(handler_ != NULL); handler_->TaskCallback(); } private: MessageHandler* handler_; DISALLOW_COPY_AND_ASSIGN(MessageHandlerTask); }; // static const char* MessageHandler::MessageStatusString(MessageStatus status) { switch (status) { case kOK: return "OK"; case kError: return "Error"; case kRestart: return "Restart"; case kShutdown: return "Shutdown"; default: UNREACHABLE(); return "Illegal"; } } MessageHandler::MessageHandler() : queue_(new MessageQueue()), oob_queue_(new MessageQueue()), oob_message_handling_allowed_(true), paused_for_messages_(false), live_ports_(0), paused_(0), #if !defined(PRODUCT) should_pause_on_start_(false), should_pause_on_exit_(false), is_paused_on_start_(false), is_paused_on_exit_(false), paused_timestamp_(-1), #endif task_running_(false), delete_me_(false), pool_(NULL), start_callback_(NULL), end_callback_(NULL), callback_data_(0) { ASSERT(queue_ != NULL); ASSERT(oob_queue_ != NULL); } MessageHandler::~MessageHandler() { delete queue_; delete oob_queue_; queue_ = NULL; oob_queue_ = NULL; pool_ = NULL; } const char* MessageHandler::name() const { return ""; } #if defined(DEBUG) void MessageHandler::CheckAccess() { // By default there is no checking. } #endif void MessageHandler::MessageNotify(Message::Priority priority) { // By default, there is no custom message notification. } bool MessageHandler::Run(ThreadPool* pool, StartCallback start_callback, EndCallback end_callback, CallbackData data) { MonitorLocker ml(&monitor_); if (FLAG_trace_isolates) { OS::PrintErr( "[+] Starting message handler:\n" "\thandler: %s\n", name()); } ASSERT(pool_ == NULL); ASSERT(!delete_me_); pool_ = pool; start_callback_ = start_callback; end_callback_ = end_callback; callback_data_ = data; task_running_ = true; bool result = pool_->Run(this); if (!result) { pool_ = nullptr; start_callback_ = nullptr; end_callback_ = nullptr; callback_data_ = 0; task_running_ = false; } return result; } void MessageHandler::PostMessage(std::unique_ptr message, bool before_events) { Message::Priority saved_priority; { MonitorLocker ml(&monitor_); if (FLAG_trace_isolates) { Isolate* source_isolate = Isolate::Current(); if (source_isolate != nullptr) { OS::PrintErr( "[>] Posting message:\n" "\tlen: %" Pd "\n\tsource: (%" Pd64 ") %s\n\tdest: %s\n" "\tdest_port: %" Pd64 "\n", message->Size(), static_cast(source_isolate->main_port()), source_isolate->name(), name(), message->dest_port()); } else { OS::PrintErr( "[>] Posting message:\n" "\tlen: %" Pd "\n\tsource: \n" "\tdest: %s\n" "\tdest_port: %" Pd64 "\n", message->Size(), name(), message->dest_port()); } } saved_priority = message->priority(); if (message->IsOOB()) { oob_queue_->Enqueue(std::move(message), before_events); } else { queue_->Enqueue(std::move(message), before_events); } if (paused_for_messages_) { ml.Notify(); } if (pool_ != nullptr && !task_running_) { ASSERT(!delete_me_); task_running_ = true; const bool launched_successfully = pool_->Run(this); ASSERT(launched_successfully); } } // Invoke any custom message notification. MessageNotify(saved_priority); } std::unique_ptr MessageHandler::DequeueMessage( Message::Priority min_priority) { // TODO(turnidge): Add assert that monitor_ is held here. std::unique_ptr message = oob_queue_->Dequeue(); if ((message == nullptr) && (min_priority < Message::kOOBPriority)) { message = queue_->Dequeue(); } return message; } void MessageHandler::ClearOOBQueue() { oob_queue_->Clear(); } MessageHandler::MessageStatus MessageHandler::HandleMessages( MonitorLocker* ml, bool allow_normal_messages, bool allow_multiple_normal_messages) { ASSERT(monitor_.IsOwnedByCurrentThread()); // Scheduling of the mutator thread during the isolate start can cause this // thread to safepoint. // We want to avoid holding the message handler monitor during the safepoint // operation to avoid possible deadlocks, which can occur if other threads are // sending messages to this message handler. // // If isolate() returns nullptr [StartIsolateScope] does nothing. ml->Exit(); StartIsolateScope start_isolate(isolate()); ml->Enter(); auto idle_time_handler = isolate() != nullptr ? isolate()->group()->idle_time_handler() : nullptr; MessageStatus max_status = kOK; Message::Priority min_priority = ((allow_normal_messages && !paused()) ? Message::kNormalPriority : Message::kOOBPriority); std::unique_ptr message = DequeueMessage(min_priority); while (message != nullptr) { intptr_t message_len = message->Size(); if (FLAG_trace_isolates) { OS::PrintErr( "[<] Handling message:\n" "\tlen: %" Pd "\n" "\thandler: %s\n" "\tport: %" Pd64 "\n", message_len, name(), message->dest_port()); } // Release the monitor_ temporarily while we handle the message. // The monitor was acquired in MessageHandler::TaskCallback(). ml->Exit(); Message::Priority saved_priority = message->priority(); Dart_Port saved_dest_port = message->dest_port(); MessageStatus status = kOK; { DisableIdleTimerScope disable_idle_timer(idle_time_handler); status = HandleMessage(std::move(message)); } if (status > max_status) { max_status = status; } ml->Enter(); if (FLAG_trace_isolates) { OS::PrintErr( "[.] Message handled (%s):\n" "\tlen: %" Pd "\n" "\thandler: %s\n" "\tport: %" Pd64 "\n", MessageStatusString(status), message_len, name(), saved_dest_port); } // If we are shutting down, do not process any more messages. if (status == kShutdown) { ClearOOBQueue(); break; } // Remember time since the last message. Don't consider OOB messages so // using Observatory doesn't trigger additional idle tasks. if ((FLAG_idle_timeout_micros != 0) && (saved_priority == Message::kNormalPriority)) { if (idle_time_handler != nullptr) { idle_time_handler->UpdateStartIdleTime(); } } // Some callers want to process only one normal message and then quit. At // the same time it is OK to process multiple OOB messages. if ((saved_priority == Message::kNormalPriority) && !allow_multiple_normal_messages) { // We processed one normal message. Allow no more. allow_normal_messages = false; } // Reevaluate the minimum allowable priority. The paused state // may have changed as part of handling the message. We may also // have encountered an error during message processing. // // Even if we encounter an error, we still process pending OOB // messages so that we don't lose the message notification. min_priority = (((max_status == kOK) && allow_normal_messages && !paused()) ? Message::kNormalPriority : Message::kOOBPriority); message = DequeueMessage(min_priority); } return max_status; } MessageHandler::MessageStatus MessageHandler::HandleNextMessage() { // We can only call HandleNextMessage when this handler is not // assigned to a thread pool. MonitorLocker ml(&monitor_); ASSERT(pool_ == NULL); ASSERT(!delete_me_); #if defined(DEBUG) CheckAccess(); #endif return HandleMessages(&ml, true, false); } MessageHandler::MessageStatus MessageHandler::PauseAndHandleAllMessages( int64_t timeout_millis) { MonitorLocker ml(&monitor_, /*no_safepoint_scope=*/false); ASSERT(task_running_); ASSERT(!delete_me_); #if defined(DEBUG) CheckAccess(); #endif paused_for_messages_ = true; while (queue_->IsEmpty() && oob_queue_->IsEmpty()) { Monitor::WaitResult wr; { // Ensure this thread is at a safepoint while we wait for new messages to // arrive. TransitionVMToNative transition(Thread::Current()); wr = ml.Wait(timeout_millis); } ASSERT(task_running_); ASSERT(!delete_me_); if (wr == Monitor::kTimedOut) { break; } if (queue_->IsEmpty()) { // There are only OOB messages. Handle them and then continue waiting for // normal messages unless there is an error. MessageStatus status = HandleMessages(&ml, false, false); if (status != kOK) { paused_for_messages_ = false; return status; } } } paused_for_messages_ = false; return HandleMessages(&ml, true, true); } MessageHandler::MessageStatus MessageHandler::HandleOOBMessages() { if (!oob_message_handling_allowed_) { return kOK; } MonitorLocker ml(&monitor_); ASSERT(!delete_me_); #if defined(DEBUG) CheckAccess(); #endif return HandleMessages(&ml, false, false); } #if !defined(PRODUCT) bool MessageHandler::ShouldPauseOnStart(MessageStatus status) const { Isolate* owning_isolate = isolate(); if (owning_isolate == NULL) { return false; } // If we are restarting or shutting down, we do not want to honor // should_pause_on_start or should_pause_on_exit. return (status != MessageHandler::kRestart && status != MessageHandler::kShutdown) && should_pause_on_start() && owning_isolate->is_runnable(); } bool MessageHandler::ShouldPauseOnExit(MessageStatus status) const { Isolate* owning_isolate = isolate(); if (owning_isolate == NULL) { return false; } return (status != MessageHandler::kRestart && status != MessageHandler::kShutdown) && should_pause_on_exit() && owning_isolate->is_runnable(); } #endif bool MessageHandler::HasOOBMessages() { MonitorLocker ml(&monitor_); return !oob_queue_->IsEmpty(); } bool MessageHandler::HasMessages() { MonitorLocker ml(&monitor_); return !queue_->IsEmpty(); } void MessageHandler::TaskCallback() { ASSERT(Isolate::Current() == NULL); MessageStatus status = kOK; bool run_end_callback = false; bool delete_me = false; EndCallback end_callback = NULL; CallbackData callback_data = 0; { // We will occasionally release and reacquire this monitor in this // function. Whenever we reacquire the monitor we *must* process // all pending OOB messages, or we may miss a request for vm // shutdown. MonitorLocker ml(&monitor_); // This method is running on the message handler task. Which means no // other message handler tasks will be started until this one sets // [task_running_] to false. ASSERT(task_running_); #if !defined(PRODUCT) if (ShouldPauseOnStart(kOK)) { if (!is_paused_on_start()) { PausedOnStartLocked(&ml, true); } // More messages may have come in before we (re)acquired the monitor. status = HandleMessages(&ml, false, false); if (ShouldPauseOnStart(status)) { // Still paused. ASSERT(oob_queue_->IsEmpty()); task_running_ = false; // No task in queue. return; } else { PausedOnStartLocked(&ml, false); } } if (is_paused_on_exit()) { status = HandleMessages(&ml, false, false); if (ShouldPauseOnExit(status)) { // Still paused. ASSERT(oob_queue_->IsEmpty()); task_running_ = false; // No task in queue. return; } else { PausedOnExitLocked(&ml, false); } } #endif // !defined(PRODUCT) if (status == kOK) { if (start_callback_ != nullptr) { // Initialize the message handler by running its start function, // if we have one. For an isolate, this will run the isolate's // main() function. // // Release the monitor_ temporarily while we call the start callback. ml.Exit(); status = start_callback_(callback_data_); ASSERT(Isolate::Current() == NULL); start_callback_ = NULL; ml.Enter(); } // Handle any pending messages for this message handler. if (status != kShutdown) { status = HandleMessages(&ml, (status == kOK), true); } } // The isolate exits when it encounters an error or when it no // longer has live ports. if (status != kOK || !HasLivePorts()) { #if !defined(PRODUCT) if (ShouldPauseOnExit(status)) { if (FLAG_trace_service_pause_events) { OS::PrintErr( "Isolate %s paused before exiting. " "Use the Observatory to release it.\n", name()); } PausedOnExitLocked(&ml, true); // More messages may have come in while we released the monitor. status = HandleMessages(&ml, false, false); if (ShouldPauseOnExit(status)) { // Still paused. ASSERT(oob_queue_->IsEmpty()); task_running_ = false; // No task in queue. return; } else { PausedOnExitLocked(&ml, false); } } #endif // !defined(PRODUCT) if (FLAG_trace_isolates) { if (status != kOK && thread() != NULL) { const Error& error = Error::Handle(thread()->sticky_error()); OS::PrintErr( "[-] Stopping message handler (%s):\n" "\thandler: %s\n" "\terror: %s\n", MessageStatusString(status), name(), error.ToCString()); } else { OS::PrintErr( "[-] Stopping message handler (%s):\n" "\thandler: %s\n", MessageStatusString(status), name()); } } pool_ = NULL; // Decide if we have a callback before releasing the monitor. end_callback = end_callback_; callback_data = callback_data_; run_end_callback = end_callback_ != NULL; delete_me = delete_me_; } // Clear task_running_ last. This allows other tasks to potentially start // for this message handler. ASSERT(oob_queue_->IsEmpty()); task_running_ = false; } // The handler may have been deleted by another thread here if it is a native // message handler. // Message handlers either use delete_me or end_callback but not both. ASSERT(!delete_me || !run_end_callback); if (run_end_callback) { ASSERT(end_callback != NULL); end_callback(callback_data); // The handler may have been deleted after this point. } if (delete_me) { delete this; } } void MessageHandler::ClosePort(Dart_Port port) { MonitorLocker ml(&monitor_); if (FLAG_trace_isolates) { OS::PrintErr( "[-] Closing port:\n" "\thandler: %s\n" "\tport: %" Pd64 "\n" "\tports: live(%" Pd ")\n", name(), port, live_ports_); } } void MessageHandler::CloseAllPorts() { MonitorLocker ml(&monitor_); if (FLAG_trace_isolates) { OS::PrintErr( "[-] Closing all ports:\n" "\thandler: %s\n", name()); } queue_->Clear(); oob_queue_->Clear(); } void MessageHandler::RequestDeletion() { ASSERT(OwnedByPortMap()); { MonitorLocker ml(&monitor_); if (task_running_) { // This message handler currently has a task running on the thread pool. delete_me_ = true; return; } } // This message handler has no current task. Delete it. delete this; } void MessageHandler::increment_live_ports() { MonitorLocker ml(&monitor_); #if defined(DEBUG) CheckAccess(); #endif live_ports_++; } void MessageHandler::decrement_live_ports() { MonitorLocker ml(&monitor_); #if defined(DEBUG) CheckAccess(); #endif live_ports_--; } #if !defined(PRODUCT) void MessageHandler::DebugDump() { PortMap::DebugDumpForMessageHandler(this); } void MessageHandler::PausedOnStart(bool paused) { MonitorLocker ml(&monitor_); PausedOnStartLocked(&ml, paused); } void MessageHandler::PausedOnStartLocked(MonitorLocker* ml, bool paused) { if (paused) { ASSERT(!is_paused_on_start_); ASSERT(paused_timestamp_ == -1); paused_timestamp_ = OS::GetCurrentTimeMillis(); // Temporarily release the monitor when calling out to // NotifyPauseOnStart. This avoids a dead lock that can occur // when this message handler tries to post a message while a // message is being posted to it. ml->Exit(); NotifyPauseOnStart(); ml->Enter(); is_paused_on_start_ = true; } else { ASSERT(is_paused_on_start_); ASSERT(paused_timestamp_ != -1); paused_timestamp_ = -1; // Resumed. Clear the resume request of the owning isolate. Isolate* owning_isolate = isolate(); if (owning_isolate != NULL) { owning_isolate->GetAndClearResumeRequest(); } is_paused_on_start_ = false; } } void MessageHandler::PausedOnExit(bool paused) { MonitorLocker ml(&monitor_); PausedOnExitLocked(&ml, paused); } void MessageHandler::PausedOnExitLocked(MonitorLocker* ml, bool paused) { if (paused) { ASSERT(!is_paused_on_exit_); ASSERT(paused_timestamp_ == -1); paused_timestamp_ = OS::GetCurrentTimeMillis(); // Temporarily release the monitor when calling out to // NotifyPauseOnExit. This avoids a dead lock that can // occur when this message handler tries to post a message // while a message is being posted to it. ml->Exit(); NotifyPauseOnExit(); ml->Enter(); is_paused_on_exit_ = true; } else { ASSERT(is_paused_on_exit_); ASSERT(paused_timestamp_ != -1); paused_timestamp_ = -1; // Resumed. Clear the resume request of the owning isolate. Isolate* owning_isolate = isolate(); if (owning_isolate != NULL) { owning_isolate->GetAndClearResumeRequest(); } is_paused_on_exit_ = false; } } #endif // !defined(PRODUCT) MessageHandler::AcquiredQueues::AcquiredQueues(MessageHandler* handler) : handler_(handler), ml_(&handler->monitor_) { ASSERT(handler != NULL); handler_->oob_message_handling_allowed_ = false; } MessageHandler::AcquiredQueues::~AcquiredQueues() { ASSERT(handler_ != NULL); handler_->oob_message_handling_allowed_ = true; } } // namespace dart