Index: mojo/public/platform/dart/dart_handle_watcher.cc |
diff --git a/mojo/public/platform/dart/dart_handle_watcher.cc b/mojo/public/platform/dart/dart_handle_watcher.cc |
deleted file mode 100644 |
index 33b8c61e544185127a04c9de4beb165d68850043..0000000000000000000000000000000000000000 |
--- a/mojo/public/platform/dart/dart_handle_watcher.cc |
+++ /dev/null |
@@ -1,597 +0,0 @@ |
-// Copyright 2015 The Chromium Authors. All rights reserved. |
-// Use of this source code is governed by a BSD-style license that can be |
-// found in the LICENSE file. |
- |
-#include <mojo/result.h> |
-#include <mojo/system/handle.h> |
-#include <mojo/system/message_pipe.h> |
-#include <mojo/system/time.h> |
-#include <mojo/system/wait.h> |
-#include <stdio.h> |
-#include <string.h> |
-#include <sys/time.h> |
- |
-#include <memory> |
-#include <mutex> |
-#include <set> |
-#include <unordered_map> |
-#include <vector> |
- |
-#include "mojo/public/platform/dart/dart_handle_watcher.h" |
- |
-#include "dart/runtime/include/dart_api.h" |
-#include "dart/runtime/include/dart_native_api.h" |
- |
-namespace mojo { |
-namespace dart { |
- |
-#define CONTROL_HANDLE_INDEX 0 |
- |
-static void PostNull(Dart_Port port) { |
- if (port == ILLEGAL_PORT) { |
- return; |
- } |
- Dart_CObject message; |
- message.type = Dart_CObject_kNull; |
- Dart_PostCObject(port, &message); |
-} |
- |
-static void PostSignal(Dart_Port port, int32_t signalled) { |
- if (port == ILLEGAL_PORT) { |
- return; |
- } |
- Dart_PostInteger(port, signalled); |
-} |
- |
-// The internal state of the handle watcher thread. |
-class HandleWatcherThreadState { |
- public: |
- HandleWatcherThreadState(MojoHandle control_pipe_consumer_handle); |
- |
- ~HandleWatcherThreadState(); |
- |
- void Run(); |
- |
- private: |
- struct HandleWatcherTimer { |
- int64_t deadline; |
- Dart_Port port; |
- |
- // Sort on deadline. |
- friend bool operator<(const HandleWatcherTimer& l, |
- const HandleWatcherTimer& r) { |
- return l.deadline < r.deadline; |
- } |
- }; |
- |
- void AddHandle(MojoHandle handle, |
- MojoHandleSignals signals, |
- Dart_Port port); |
- |
- void RemoveHandle(MojoHandle handle); |
- |
- void CloseHandle(MojoHandle handle, Dart_Port port, bool pruning = false); |
- |
- void UpdateTimer(int64_t deadline, Dart_Port port); |
- |
- void Shutdown(); |
- |
- void RemoveHandleAtIndex(intptr_t i); |
- |
- void ProcessControlMessage(); |
- |
- void ProcessTimers(); |
- |
- void ProcessWaitManyResults(MojoResult result, uint32_t result_index); |
- |
- void PruneClosedHandles(bool signals_state_is_valid); |
- |
- void CompleteNextTimer(); |
- |
- bool HasTimers(); |
- |
- int64_t NextTimerDeadline(); |
- |
- int64_t WaitDeadline(); |
- |
- bool shutdown_; |
- |
- MojoHandle control_pipe_consumer_handle_; |
- |
- // All of these vectors are indexed together. |
- std::vector<MojoHandle> wait_many_handles_; |
- std::vector<MojoHandleSignals> wait_many_signals_; |
- std::vector<MojoHandleSignalsState> wait_many_signals_state_; |
- std::vector<Dart_Port> handle_ports_; |
- |
- // Map from MojoHandle -> index into above arrays. |
- std::unordered_map<MojoHandle, intptr_t> handle_to_index_map_; |
- |
- // Set of timers sorted by earliest deadline. |
- std::set<HandleWatcherTimer> timers_; |
- |
- MOJO_DISALLOW_COPY_AND_ASSIGN(HandleWatcherThreadState); |
-}; |
- |
-HandleWatcherThreadState::HandleWatcherThreadState( |
- MojoHandle control_pipe_consumer_handle) |
- : shutdown_(false), |
- control_pipe_consumer_handle_(control_pipe_consumer_handle) { |
- MOJO_CHECK(control_pipe_consumer_handle_ != MOJO_HANDLE_INVALID); |
- // Add the control handle. |
- AddHandle(control_pipe_consumer_handle_, |
- MOJO_HANDLE_SIGNAL_READABLE, |
- ILLEGAL_PORT); |
-} |
- |
-HandleWatcherThreadState::~HandleWatcherThreadState() { |
- if (control_pipe_consumer_handle_ != MOJO_HANDLE_INVALID) { |
- MojoClose(control_pipe_consumer_handle_); |
- control_pipe_consumer_handle_ = MOJO_HANDLE_INVALID; |
- } |
-} |
- |
-void HandleWatcherThreadState::AddHandle(MojoHandle handle, |
- MojoHandleSignals signals, |
- Dart_Port port) { |
- const size_t index = wait_many_handles_.size(); |
- MojoHandleSignalsState signals_state = |
- { MOJO_HANDLE_SIGNAL_NONE, MOJO_HANDLE_SIGNAL_NONE}; |
- |
- auto it = handle_to_index_map_.find(handle); |
- if (it != handle_to_index_map_.end()) { |
- intptr_t index = it->second; |
- // Sanity check. |
- MOJO_CHECK(wait_many_handles_[index] == handle); |
- // We only support 1:1 mapping from handles to ports. |
- if (handle_ports_[index] != port) { |
- MOJO_LOG(ERROR) << "(Dart Handle Watcher) " |
- << "Handle " << handle << " is already bound!"; |
- PostSignal(port, MOJO_HANDLE_SIGNAL_PEER_CLOSED); |
- return; |
- } |
- // Adjust the signals for this handle. |
- wait_many_signals_[index] |= signals; |
- } else { |
- // New handle. |
- wait_many_handles_.push_back(handle); |
- wait_many_signals_.push_back(signals); |
- wait_many_signals_state_.push_back(signals_state); |
- handle_ports_.push_back(port); |
- handle_to_index_map_[handle] = index; |
- } |
- |
- // Sanity check. |
- MOJO_CHECK(wait_many_handles_.size() == handle_to_index_map_.size()); |
-} |
- |
-void HandleWatcherThreadState::RemoveHandle(MojoHandle handle) { |
- auto it = handle_to_index_map_.find(handle); |
- |
- // Removal of a handle for an incoming event can race with the removal of |
- // a handle for an unsubscribe() call on the Dart MojoEventSubscription. |
- // This is not an error, so we ignore attempts to remove a handle that is not |
- // in the map. |
- if (it == handle_to_index_map_.end()) { |
- return; |
- } |
- const intptr_t index = it->second; |
- // We should never be removing the control handle. |
- MOJO_CHECK(index != CONTROL_HANDLE_INDEX); |
- RemoveHandleAtIndex(index); |
-} |
- |
-void HandleWatcherThreadState::CloseHandle(MojoHandle handle, |
- Dart_Port port, |
- bool pruning) { |
- MOJO_CHECK(!pruning || (port == ILLEGAL_PORT)); |
- auto it = handle_to_index_map_.find(handle); |
- if (it == handle_to_index_map_.end()) { |
- // An app isolate may request that the handle watcher close a handle that |
- // has already been pruned. This happens when the app isolate has not yet |
- // received the PEER_CLOSED event. The app isolate will not close the |
- // handle, so we must do so here. |
- MojoClose(handle); |
- if (port != ILLEGAL_PORT) { |
- // Notify that close is done. |
- PostNull(port); |
- } |
- return; |
- } |
- MojoClose(handle); |
- if (port != ILLEGAL_PORT) { |
- // Notify that close is done. |
- PostNull(port); |
- } |
- const intptr_t index = it->second; |
- MOJO_CHECK(index != CONTROL_HANDLE_INDEX); |
- if (pruning) { |
- // If this handle is being pruned, notify the application isolate |
- // by sending PEER_CLOSED; |
- PostSignal(handle_ports_[index], MOJO_HANDLE_SIGNAL_PEER_CLOSED); |
- } |
- // Remove the handle. |
- RemoveHandle(handle); |
-} |
- |
-void HandleWatcherThreadState::UpdateTimer(int64_t deadline, Dart_Port port) { |
- // Scan the timers to see if we have a timer with |port|. |
- auto it = timers_.begin(); |
- while (it != timers_.end()) { |
- if (it->port == port) { |
- break; |
- } |
- it++; |
- } |
- |
- // We found an existing timer with |port|. Remove it. |
- if (it != timers_.end()) { |
- timers_.erase(it); |
- } |
- |
- if (deadline < 0) { |
- // A negative deadline means we should cancel this timer completely. |
- return; |
- } |
- |
- // Create a new timer with the current deadline. |
- HandleWatcherTimer timer; |
- timer.deadline = deadline; |
- timer.port = port; |
- |
- timers_.insert(timer); |
-} |
- |
-void HandleWatcherThreadState::Shutdown() { |
- // Break out of the loop by setting the shutdown_ to true. |
- shutdown_ = true; |
-} |
- |
-void HandleWatcherThreadState::RemoveHandleAtIndex(intptr_t index) { |
- MOJO_CHECK(index != CONTROL_HANDLE_INDEX); |
- const intptr_t last_index = wait_many_handles_.size() - 1; |
- |
- // Remove handle from handle map. |
- handle_to_index_map_.erase(wait_many_handles_[index]); |
- |
- if (index != last_index) { |
- // We should never be overwriting CONTROL_HANDLE_INDEX. |
- |
- MojoHandle handle = wait_many_handles_[last_index]; |
- |
- // Replace |index| with |last_index|. |
- wait_many_handles_[index] = wait_many_handles_[last_index]; |
- wait_many_signals_[index] = wait_many_signals_[last_index]; |
- wait_many_signals_state_[index] = wait_many_signals_state_[last_index]; |
- handle_ports_[index] = handle_ports_[last_index]; |
- |
- // Update handle map. |
- handle_to_index_map_[handle] = index; |
- } |
- |
- wait_many_handles_.pop_back(); |
- wait_many_signals_.pop_back(); |
- wait_many_signals_state_.pop_back(); |
- handle_ports_.pop_back(); |
- MOJO_CHECK(wait_many_handles_.size() >= 1); |
- |
- // Sanity check. |
- MOJO_CHECK(wait_many_handles_.size() == handle_to_index_map_.size()); |
-} |
- |
-void HandleWatcherThreadState::ProcessControlMessage() { |
- HandleWatcherCommand command = HandleWatcherCommand::Empty(); |
- uint32_t num_bytes = sizeof(command); |
- uint32_t num_handles = 0; |
- MojoResult res = MojoReadMessage(control_pipe_consumer_handle_, |
- reinterpret_cast<void*>(&command), |
- &num_bytes, |
- nullptr, |
- &num_handles, |
- 0); |
- // Sanity check that we received the expected amount of data. |
- MOJO_CHECK(res == MOJO_RESULT_OK); |
- MOJO_CHECK(num_bytes == sizeof(command)); |
- MOJO_CHECK(num_handles == 0); |
- switch (command.command()) { |
- case HandleWatcherCommand::kCommandAddHandle: |
- AddHandle(command.handle(), command.signals(), command.port()); |
- break; |
- case HandleWatcherCommand::kCommandRemoveHandle: |
- RemoveHandle(command.handle()); |
- break; |
- case HandleWatcherCommand::kCommandCloseHandle: |
- CloseHandle(command.handle(), command.port()); |
- break; |
- case HandleWatcherCommand::kCommandAddTimer: |
- UpdateTimer(command.deadline(), command.port()); |
- break; |
- case HandleWatcherCommand::kCommandShutdownHandleWatcher: |
- Shutdown(); |
- break; |
- default: |
- MOJO_CHECK(false); |
- break; |
- } |
-} |
- |
-// Dart's Timer class uses MojoCoreNatives.timerMillisecondClock(), which |
-// calls MojoGetTimeTicksNow() and divides by 1000; |
-static int64_t GetDartTimeInMillis() { |
- MojoTimeTicks ticks = MojoGetTimeTicksNow(); |
- return static_cast<int64_t>(ticks) / 1000; |
-} |
- |
-void HandleWatcherThreadState::ProcessTimers() { |
- int64_t now = GetDartTimeInMillis(); |
- while (HasTimers() && now >= NextTimerDeadline()) { |
- CompleteNextTimer(); |
- now = GetDartTimeInMillis(); |
- } |
-} |
- |
-void HandleWatcherThreadState::CompleteNextTimer() { |
- auto it = timers_.begin(); |
- MOJO_CHECK(it != timers_.end()); |
- // Notify that the timer is complete. |
- PostNull(it->port); |
- // Remove it from the timer set. |
- timers_.erase(it); |
-} |
- |
-bool HandleWatcherThreadState::HasTimers() { |
- return !timers_.empty(); |
-} |
- |
-int64_t HandleWatcherThreadState::NextTimerDeadline() { |
- auto it = timers_.begin(); |
- MOJO_CHECK(it != timers_.end()); |
- return it->deadline; |
-} |
- |
-int64_t HandleWatcherThreadState::WaitDeadline() { |
- if (!HasTimers()) { |
- // No pending timers. Wait indefinitely. |
- return MOJO_DEADLINE_INDEFINITE; |
- } |
- int64_t now = GetDartTimeInMillis(); |
- return (NextTimerDeadline() - now) * 1000; |
-} |
- |
-static bool ShouldCloseHandle(MojoHandle handle) { |
- if (handle == MOJO_HANDLE_INVALID) { |
- return false; |
- } |
- // Call wait with a deadline of 0. If the result of this is OK or |
- // DEADLINE_EXCEEDED, the handle is still open. |
- MojoResult result = MojoWait(handle, MOJO_HANDLE_SIGNAL_ALL, 0, NULL); |
- return (result != MOJO_RESULT_OK) && |
- (result != MOJO_RESULT_DEADLINE_EXCEEDED); |
-} |
- |
-void HandleWatcherThreadState::PruneClosedHandles(bool signals_state_is_valid) { |
- std::vector<MojoHandle> closed_handles; |
- const intptr_t num_handles = wait_many_handles_.size(); |
- if (signals_state_is_valid) { |
- // We can rely on |wait_many_signals_state_| having valid data. |
- for (intptr_t i = 0; i < num_handles; i++) { |
- // Check if the handle at index |i| has been closed. |
- MojoHandleSignals satisfied_signals = |
- wait_many_signals_state_[i].satisfied_signals; |
- if ((satisfied_signals & MOJO_HANDLE_SIGNAL_PEER_CLOSED) != 0) { |
- closed_handles.push_back(wait_many_handles_[i]); |
- } |
- } |
- } else { |
- // We can't rely on |wait_many_signals_state_| having valid data. So |
- // we call Wait on each handle and check the status. |
- for (intptr_t i = 0; i < num_handles; i++) { |
- MojoHandle handle = wait_many_handles_[i]; |
- if (ShouldCloseHandle(handle)) { |
- closed_handles.push_back(handle); |
- } |
- } |
- } |
- |
- // Process all closed handles and notify their ports. |
- for (size_t i = 0; i < closed_handles.size(); i++) { |
- MojoHandle handle = closed_handles[i]; |
- CloseHandle(handle, ILLEGAL_PORT, true); |
- } |
-} |
- |
-void HandleWatcherThreadState::ProcessWaitManyResults(MojoResult result, |
- uint32_t result_index) { |
- MOJO_CHECK(result != MOJO_RESULT_DEADLINE_EXCEEDED); |
- if (result != MOJO_RESULT_OK) { |
- // The WaitMany call failed. We need to prune closed handles from our |
- // wait many set and try again. |
- // |
- // If the result is an invalid argument |wait_many_signals_state_| is |
- // meaningless. |
- PruneClosedHandles(result != MOJO_RESULT_INVALID_ARGUMENT); |
- return; |
- } |
- MOJO_CHECK(result == MOJO_RESULT_OK); |
- |
- // Indexes of handles that we are done with. |
- std::vector<intptr_t> to_remove; |
- |
- const intptr_t num_handles = wait_many_handles_.size(); |
- |
- // Loop over all handles except for the control handle. |
- // The order of the looping matters because we call RemoveHandleAtIndex |
- // and need the handle indexes to start at the highest and decrease. |
- for (intptr_t i = num_handles - 1; i > 0; i--) { |
- MojoHandleSignals signals = wait_many_signals_[i]; |
- MojoHandleSignals satisfied_signals = |
- wait_many_signals_state_[i].satisfied_signals; |
- satisfied_signals &= signals; |
- if (satisfied_signals != 0) { |
- // Something happened to this handle. |
- |
- // Notify the port. |
- PostSignal(handle_ports_[i], satisfied_signals); |
- |
- // Now that we have notified the waiting Dart program, remove this handle |
- // from the wait many set until we are requested to add it again. |
- to_remove.push_back(i); |
- } |
- } |
- |
- // Remove any handles we are finished with. |
- const intptr_t num_to_remove = to_remove.size(); |
- for (intptr_t i = 0; i < num_to_remove; i++) { |
- RemoveHandleAtIndex(to_remove[i]); |
- } |
- |
- // Now check for control messages. |
- { |
- MojoHandleSignals signals = wait_many_signals_[CONTROL_HANDLE_INDEX]; |
- MojoHandleSignals satisfied_signals = |
- wait_many_signals_state_[CONTROL_HANDLE_INDEX].satisfied_signals; |
- satisfied_signals &= signals; |
- if (satisfied_signals != 0) { |
- // We have a control message. |
- ProcessControlMessage(); |
- } |
- } |
-} |
- |
-void HandleWatcherThreadState::Run() { |
- while (!shutdown_) { |
- // Process timers. |
- ProcessTimers(); |
- // Wait for the next timer or an event on a handle. |
- uint32_t result_index = -1; |
- uint32_t num_handles = wait_many_handles_.size(); |
- MOJO_CHECK(wait_many_signals_.size() == num_handles); |
- MojoResult result = MojoWaitMany(wait_many_handles_.data(), |
- wait_many_signals_.data(), |
- num_handles, |
- WaitDeadline(), |
- &result_index, |
- wait_many_signals_state_.data()); |
- |
- if (result == MOJO_RESULT_DEADLINE_EXCEEDED) { |
- // Timers are ready. |
- continue; |
- } |
- |
- // Process wait results. |
- ProcessWaitManyResults(result, result_index); |
- } |
- |
- // Close our end of the message pipe. |
- MojoClose(control_pipe_consumer_handle_); |
-} |
- |
-std::unordered_map<MojoHandle, std::thread*> |
- HandleWatcher::handle_watcher_threads_; |
-std::mutex HandleWatcher::handle_watcher_threads_mutex_; |
- |
-// Create a message pipe for communication and spawns a handle watcher thread. |
-MojoHandle HandleWatcher::Start() { |
- MojoCreateMessagePipeOptions options; |
- options.struct_size = sizeof(MojoCreateMessagePipeOptions); |
- options.flags = MOJO_CREATE_MESSAGE_PIPE_OPTIONS_FLAG_NONE; |
- |
- MojoHandle control_pipe_consumer_handle = MOJO_HANDLE_INVALID; |
- MojoHandle control_pipe_producer_handle = MOJO_HANDLE_INVALID; |
- MojoResult res = MojoCreateMessagePipe(&options, |
- &control_pipe_consumer_handle, |
- &control_pipe_producer_handle); |
- if (res != MOJO_RESULT_OK) { |
- return MOJO_HANDLE_INVALID; |
- } |
- |
- // Spawn thread and pass both ends of the pipe to it. |
- std::thread* thread = new std::thread( |
- ThreadMain, control_pipe_consumer_handle); |
- |
- { |
- std::lock_guard<std::mutex> lock(handle_watcher_threads_mutex_); |
- // Record the thread object so that we can join on it during shutdown. |
- MOJO_CHECK(handle_watcher_threads_.find(control_pipe_producer_handle) == |
- handle_watcher_threads_.end()); |
- handle_watcher_threads_[control_pipe_producer_handle] = thread; |
- } |
- |
- // Return producer end of pipe to caller. |
- return control_pipe_producer_handle; |
-} |
- |
-void HandleWatcher::ThreadMain(MojoHandle control_pipe_consumer_handle) { |
- HandleWatcherThreadState state(control_pipe_consumer_handle); |
- |
- // Run the main loop. When this returns the handle watcher has exited. |
- state.Run(); |
-} |
- |
-MojoResult HandleWatcher::SendCommand(MojoHandle control_pipe_producer_handle, |
- const HandleWatcherCommand& command) { |
- return MojoWriteMessage(control_pipe_producer_handle, |
- reinterpret_cast<const void*>(&command), |
- sizeof(command), |
- nullptr, |
- 0, |
- 0); |
-} |
- |
-std::thread* HandleWatcher::RemoveLocked(MojoHandle handle) { |
- std::thread* t; |
- auto mapping = handle_watcher_threads_.find(handle); |
- if (mapping == handle_watcher_threads_.end()) { |
- return nullptr; |
- } |
- t = mapping->second; |
- handle_watcher_threads_.erase(handle); |
- return t; |
-} |
- |
-void HandleWatcher::Stop(MojoHandle control_pipe_producer_handle) { |
- std::thread *t; |
- { |
- std::lock_guard<std::mutex> lock(handle_watcher_threads_mutex_); |
- t = RemoveLocked(control_pipe_producer_handle); |
- } |
- |
- if (t == nullptr) { |
- return; |
- } |
- |
- SendCommand(control_pipe_producer_handle, HandleWatcherCommand::Shutdown()); |
- t->join(); |
- |
- MojoClose(control_pipe_producer_handle); |
- delete t; |
-} |
- |
-void HandleWatcher::StopLocked(MojoHandle handle) { |
- std::thread *t = RemoveLocked(handle); |
- MOJO_CHECK(t != nullptr); |
- |
- SendCommand(handle, HandleWatcherCommand::Shutdown()); |
- t->join(); |
- |
- MojoClose(handle); |
- delete t; |
-} |
- |
-void HandleWatcher::StopAll() { |
- std::lock_guard<std::mutex> lock(handle_watcher_threads_mutex_); |
- |
- std::vector<MojoHandle> control_handles; |
- control_handles.reserve(handle_watcher_threads_.size()); |
- |
- for (const auto& it : handle_watcher_threads_) { |
- control_handles.push_back(it.first); |
- } |
- |
- for (auto it : control_handles) { |
- StopLocked(it); |
- } |
-} |
- |
-} // namespace dart |
-} // namespace mojo |