| // Copyright (c) 2012, 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. | 
 |  | 
 | #ifndef RUNTIME_BIN_EVENTHANDLER_H_ | 
 | #define RUNTIME_BIN_EVENTHANDLER_H_ | 
 |  | 
 | #include "bin/builtin.h" | 
 | #include "bin/dartutils.h" | 
 | #include "bin/isolate_data.h" | 
 |  | 
 | #include "platform/hashmap.h" | 
 | #include "platform/priority_queue.h" | 
 |  | 
 | namespace dart { | 
 | namespace bin { | 
 |  | 
 | // Flags used to provide information and actions to the eventhandler | 
 | // when sending a message about a file descriptor. These flags should | 
 | // be kept in sync with the constants in socket_patch.dart. For more | 
 | // information see the comments in socket_patch.dart | 
 | enum MessageFlags { | 
 |   kInEvent = 0, | 
 |   kOutEvent = 1, | 
 |   kErrorEvent = 2, | 
 |   kCloseEvent = 3, | 
 |   kDestroyedEvent = 4, | 
 |   kCloseCommand = 8, | 
 |   kShutdownReadCommand = 9, | 
 |   kShutdownWriteCommand = 10, | 
 |   kReturnTokenCommand = 11, | 
 |   kSetEventMaskCommand = 12, | 
 |   kListeningSocket = 16, | 
 |   kPipe = 17, | 
 |   kSignalSocket = 18, | 
 | }; | 
 |  | 
 | // clang-format off | 
 | #define COMMAND_MASK ((1 << kCloseCommand) |                                   \ | 
 |                       (1 << kShutdownReadCommand) |                            \ | 
 |                       (1 << kShutdownWriteCommand) |                           \ | 
 |                       (1 << kReturnTokenCommand) |                             \ | 
 |                       (1 << kSetEventMaskCommand)) | 
 | #define EVENT_MASK ((1 << kInEvent) |                                          \ | 
 |                     (1 << kOutEvent) |                                         \ | 
 |                     (1 << kErrorEvent) |                                       \ | 
 |                     (1 << kCloseEvent) |                                       \ | 
 |                     (1 << kDestroyedEvent)) | 
 | #define IS_COMMAND(data, command_bit)                                          \ | 
 |     ((data & COMMAND_MASK) == (1 << command_bit))  // NOLINT | 
 | #define IS_EVENT(data, event_bit)                                              \ | 
 |     ((data & EVENT_MASK) == (1 << event_bit))  // NOLINT | 
 | #define IS_IO_EVENT(data)                                                      \ | 
 |     ((data & (1 << kInEvent | 1 << kOutEvent | 1 << kCloseEvent)) != 0 &&      \ | 
 |      (data & ~(1 << kInEvent | 1 << kOutEvent | 1 << kCloseEvent)) == 0) | 
 | #define IS_LISTENING_SOCKET(data)                                              \ | 
 |     ((data & (1 << kListeningSocket)) != 0)  // NOLINT | 
 | #define IS_SIGNAL_SOCKET(data)                                                 \ | 
 |     ((data & (1 << kSignalSocket)) != 0)  // NOLINT | 
 | #define TOKEN_COUNT(data) (data & ((1 << kCloseCommand) - 1)) | 
 | // clang-format on | 
 |  | 
 | class TimeoutQueue { | 
 |  public: | 
 |   TimeoutQueue() {} | 
 |  | 
 |   ~TimeoutQueue() { | 
 |     while (HasTimeout()) | 
 |       RemoveCurrent(); | 
 |   } | 
 |  | 
 |   bool HasTimeout() const { return !timeouts_.IsEmpty(); } | 
 |  | 
 |   int64_t CurrentTimeout() const { | 
 |     ASSERT(!timeouts_.IsEmpty()); | 
 |     return timeouts_.Minimum().priority; | 
 |   } | 
 |  | 
 |   Dart_Port CurrentPort() const { | 
 |     ASSERT(!timeouts_.IsEmpty()); | 
 |     return timeouts_.Minimum().value; | 
 |   } | 
 |  | 
 |   void RemoveCurrent() { timeouts_.RemoveMinimum(); } | 
 |  | 
 |   void UpdateTimeout(Dart_Port port, int64_t timeout) { | 
 |     if (timeout < 0) { | 
 |       timeouts_.RemoveByValue(port); | 
 |     } else { | 
 |       timeouts_.InsertOrChangePriority(timeout, port); | 
 |     } | 
 |   } | 
 |  | 
 |  private: | 
 |   PriorityQueue<int64_t, Dart_Port> timeouts_; | 
 |  | 
 |   DISALLOW_COPY_AND_ASSIGN(TimeoutQueue); | 
 | }; | 
 |  | 
 | class InterruptMessage { | 
 |  public: | 
 |   intptr_t id; | 
 |   Dart_Port dart_port; | 
 |   int64_t data; | 
 | }; | 
 |  | 
 | static constexpr intptr_t kInterruptMessageSize = sizeof(InterruptMessage); | 
 | static constexpr intptr_t kInfinityTimeout = -1; | 
 | static constexpr intptr_t kTimerId = -1; | 
 | static constexpr intptr_t kShutdownId = -2; | 
 |  | 
 | template <typename T> | 
 | class CircularLinkedList { | 
 |  public: | 
 |   CircularLinkedList() : head_(nullptr) {} | 
 |  | 
 |   typedef void (*ClearFun)(void* value); | 
 |  | 
 |   // Returns true if the list was empty. | 
 |   bool Add(T t) { | 
 |     Entry* e = new Entry(t); | 
 |     if (head_ == nullptr) { | 
 |       // Empty list, make e head, and point to itself. | 
 |       e->next_ = e; | 
 |       e->prev_ = e; | 
 |       head_ = e; | 
 |       return true; | 
 |     } else { | 
 |       // Insert e as the last element in the list. | 
 |       e->prev_ = head_->prev_; | 
 |       e->next_ = head_; | 
 |       e->prev_->next_ = e; | 
 |       head_->prev_ = e; | 
 |       return false; | 
 |     } | 
 |   } | 
 |  | 
 |   void RemoveHead(ClearFun clear = nullptr) { | 
 |     ASSERT(head_ != nullptr); | 
 |  | 
 |     Entry* e = head_; | 
 |     if (e->next_ == e) { | 
 |       head_ = nullptr; | 
 |     } else { | 
 |       e->prev_->next_ = e->next_; | 
 |       e->next_->prev_ = e->prev_; | 
 |       head_ = e->next_; | 
 |     } | 
 |     if (clear != nullptr) { | 
 |       clear(reinterpret_cast<void*>(e->t)); | 
 |     } | 
 |     delete e; | 
 |   } | 
 |  | 
 |   void Remove(T item) { | 
 |     if (head_ == nullptr) { | 
 |       return; | 
 |     } else if (head_ == head_->next_) { | 
 |       if (head_->t == item) { | 
 |         delete head_; | 
 |         head_ = nullptr; | 
 |         return; | 
 |       } | 
 |     } else { | 
 |       Entry* current = head_; | 
 |       do { | 
 |         if (current->t == item) { | 
 |           Entry* next = current->next_; | 
 |           Entry* prev = current->prev_; | 
 |           prev->next_ = next; | 
 |           next->prev_ = prev; | 
 |  | 
 |           if (current == head_) { | 
 |             head_ = head_->next_; | 
 |           } | 
 |  | 
 |           delete current; | 
 |           return; | 
 |         } | 
 |         current = current->next_; | 
 |       } while (current != head_); | 
 |     } | 
 |   } | 
 |  | 
 |   void RemoveAll(ClearFun clear = nullptr) { | 
 |     while (HasHead()) { | 
 |       RemoveHead(clear); | 
 |     } | 
 |   } | 
 |  | 
 |   T head() const { return head_->t; } | 
 |  | 
 |   bool HasHead() const { return head_ != nullptr; } | 
 |  | 
 |   void Rotate() { | 
 |     if (head_ != nullptr) { | 
 |       ASSERT(head_->next_ != nullptr); | 
 |       head_ = head_->next_; | 
 |     } | 
 |   } | 
 |  | 
 |  private: | 
 |   struct Entry { | 
 |     explicit Entry(const T& t) : t(t), next_(nullptr), prev_(nullptr) {} | 
 |     const T t; | 
 |     Entry* next_; | 
 |     Entry* prev_; | 
 |   }; | 
 |  | 
 |   Entry* head_; | 
 |  | 
 |   DISALLOW_COPY_AND_ASSIGN(CircularLinkedList); | 
 | }; | 
 |  | 
 | class DescriptorInfoBase { | 
 |  public: | 
 |   explicit DescriptorInfoBase(intptr_t fd) : fd_(fd) { ASSERT(fd_ != -1); } | 
 |  | 
 |   virtual ~DescriptorInfoBase() {} | 
 |  | 
 |   // The OS descriptor. | 
 |   intptr_t fd() { return fd_; } | 
 |  | 
 |   // Whether this descriptor refers to an underlying listening OS socket. | 
 |   virtual bool IsListeningSocket() const = 0; | 
 |  | 
 |   // Inserts or updates a new Dart_Port which is interested in events specified | 
 |   // in `mask`. | 
 |   virtual void SetPortAndMask(Dart_Port port, intptr_t mask) = 0; | 
 |  | 
 |   // Removes a port from the interested listeners. | 
 |   virtual void RemovePort(Dart_Port port) = 0; | 
 |  | 
 |   // Removes all ports from the interested listeners. | 
 |   virtual void RemoveAllPorts() = 0; | 
 |  | 
 |   // Returns a port to which `events_ready` can be sent to. It will also | 
 |   // decrease the token count by 1 for this port. | 
 |   virtual Dart_Port NextNotifyDartPort(intptr_t events_ready) = 0; | 
 |  | 
 |   // Will post `data` to all known Dart_Ports. It will also decrease the token | 
 |   // count by 1 for all ports. | 
 |   virtual void NotifyAllDartPorts(uintptr_t events) = 0; | 
 |  | 
 |   // Returns `count` tokens for the given port. | 
 |   virtual void ReturnTokens(Dart_Port port, int count) = 0; | 
 |  | 
 |   // Returns the union of event masks of all ports. If a port has a non-positive | 
 |   // token count it's mask is assumed to be 0. | 
 |   virtual intptr_t Mask() = 0; | 
 |  | 
 |   // Closes this descriptor. | 
 |   virtual void Close() = 0; | 
 |  | 
 |  protected: | 
 |   intptr_t fd_; | 
 |  | 
 |  private: | 
 |   DISALLOW_COPY_AND_ASSIGN(DescriptorInfoBase); | 
 | }; | 
 |  | 
 | #if !defined(DART_HOST_OS_WINDOWS) | 
 | // On POSIX systems we use tokens to balance work between isolates listening to | 
 | // the same socket. | 
 | template <intptr_t kInitialAmount> | 
 | class TokenCounter { | 
 |  public: | 
 |   void Return(intptr_t amount) { | 
 |     ASSERT(amount >= 0); | 
 |     tokens_ += amount; | 
 |     ASSERT(tokens_ <= kInitialAmount); | 
 |   } | 
 |  | 
 |   void TakeOne() { tokens_--; } | 
 |  | 
 |   bool IsEmpty() const { return tokens_ <= 0; } | 
 |  | 
 |  private: | 
 |   intptr_t tokens_ = kInitialAmount; | 
 | }; | 
 | #else | 
 | // On Windows we don't use this mechanism. | 
 | template <intptr_t kInitialAmount> | 
 | class TokenCounter { | 
 |  public: | 
 |   void Return(intptr_t amount) { | 
 |     // Do nothing. | 
 |   } | 
 |  | 
 |   void TakeOne() { | 
 |     // Do nothing. | 
 |   } | 
 |  | 
 |   bool IsEmpty() const { return false; } | 
 | }; | 
 |  | 
 | #endif | 
 |  | 
 | // Describes a OS descriptor (e.g. file descriptor on linux or HANDLE on | 
 | // windows) which is connected to a single Dart_Port. | 
 | // | 
 | // Subclasses of this class can be e.g. connected tcp sockets. | 
 | template <typename DI> | 
 | class DescriptorInfoSingleMixin : public DI { | 
 |  public: | 
 |   template <typename... Args> | 
 |   DescriptorInfoSingleMixin(intptr_t fd, Args... args) | 
 |       : DI(fd, args...), port_(0), mask_(0) {} | 
 |  | 
 |   virtual ~DescriptorInfoSingleMixin() {} | 
 |  | 
 |   virtual bool IsListeningSocket() const { return false; } | 
 |  | 
 |   virtual void SetPortAndMask(Dart_Port port, intptr_t mask) { | 
 |     ASSERT(port_ == 0 || port == port_); | 
 |     port_ = port; | 
 |     mask_ = mask; | 
 |   } | 
 |  | 
 |   virtual void RemovePort(Dart_Port port) { | 
 |     // TODO(dart:io): Find out where we call RemovePort() with the invalid | 
 |     // port. Afterwards remove the part in the ASSERT here. | 
 |     ASSERT(port_ == 0 || port_ == port); | 
 |     port_ = 0; | 
 |     mask_ = 0; | 
 |   } | 
 |  | 
 |   virtual void RemoveAllPorts() { | 
 |     port_ = 0; | 
 |     mask_ = 0; | 
 |   } | 
 |  | 
 |   virtual Dart_Port NextNotifyDartPort(intptr_t events_ready) { | 
 |     ASSERT(IS_IO_EVENT(events_ready) || | 
 |            IS_EVENT(events_ready, kDestroyedEvent)); | 
 |     tokens_.TakeOne(); | 
 |     return port_; | 
 |   } | 
 |  | 
 |   virtual void NotifyAllDartPorts(uintptr_t events) { | 
 |     // Unexpected close, asynchronous destroy or error events are the only | 
 |     // ones we broadcast to all listeners. | 
 |     ASSERT(IS_EVENT(events, kCloseEvent) || IS_EVENT(events, kErrorEvent) || | 
 |            IS_EVENT(events, kDestroyedEvent)); | 
 |  | 
 |     if (port_ != 0) { | 
 |       DartUtils::PostInt32(port_, events); | 
 |     } | 
 |     tokens_.TakeOne(); | 
 |   } | 
 |  | 
 |   virtual void ReturnTokens(Dart_Port port, int count) { | 
 |     ASSERT(port_ == port); | 
 |     tokens_.Return(count); | 
 |   } | 
 |  | 
 |   virtual intptr_t Mask() { | 
 |     if (tokens_.IsEmpty()) { | 
 |       return 0; | 
 |     } | 
 |     return mask_; | 
 |   } | 
 |  | 
 |   virtual void Close() { DI::Close(); } | 
 |  | 
 |  private: | 
 |   Dart_Port port_; | 
 |   TokenCounter</*kInitialAmount=*/16> tokens_; | 
 |   intptr_t mask_; | 
 |  | 
 |   DISALLOW_COPY_AND_ASSIGN(DescriptorInfoSingleMixin); | 
 | }; | 
 |  | 
 | // Describes a OS descriptor (e.g. file descriptor on linux or HANDLE on | 
 | // windows) which is connected to multiple Dart_Port's. | 
 | // | 
 | // Subclasses of this class can be e.g. a listening socket which multiple | 
 | // isolates are listening on. | 
 | template <typename DI> | 
 | class DescriptorInfoMultipleMixin : public DI { | 
 |  private: | 
 |   static bool SamePortValue(void* key1, void* key2) { | 
 |     return reinterpret_cast<Dart_Port>(key1) == | 
 |            reinterpret_cast<Dart_Port>(key2); | 
 |   } | 
 |  | 
 |   static uint32_t GetHashmapHashFromPort(Dart_Port port) { | 
 |     return static_cast<uint32_t>(port & 0xFFFFFFFF); | 
 |   } | 
 |  | 
 |   static void* GetHashmapKeyFromPort(Dart_Port port) { | 
 |     return reinterpret_cast<void*>(port); | 
 |   } | 
 |  | 
 |   static bool IsReadingMask(intptr_t mask) { | 
 |     if (mask == (1 << kInEvent)) { | 
 |       return true; | 
 |     } else { | 
 |       ASSERT(mask == 0); | 
 |       return false; | 
 |     } | 
 |   } | 
 |  | 
 |   struct PortEntry { | 
 |     Dart_Port dart_port; | 
 |     intptr_t is_reading; | 
 |     TokenCounter</*kInitialAmount=*/4> tokens; | 
 |  | 
 |     bool IsReady() { return !tokens.IsEmpty() && is_reading != 0; } | 
 |   }; | 
 |  | 
 |  public: | 
 |   template <typename... Args> | 
 |   DescriptorInfoMultipleMixin(intptr_t fd, Args... args) | 
 |       : DI(fd, args...), tokens_map_(&SamePortValue, 4) {} | 
 |  | 
 |   virtual ~DescriptorInfoMultipleMixin() { RemoveAllPorts(); } | 
 |  | 
 |   virtual bool IsListeningSocket() const { return true; } | 
 |  | 
 |   virtual void SetPortAndMask(Dart_Port port, intptr_t mask) { | 
 |     SimpleHashMap::Entry* entry = tokens_map_.Lookup( | 
 |         GetHashmapKeyFromPort(port), GetHashmapHashFromPort(port), true); | 
 |     PortEntry* pentry; | 
 |     if (entry->value == nullptr) { | 
 |       pentry = new PortEntry(); | 
 |       pentry->dart_port = port; | 
 |       pentry->is_reading = IsReadingMask(mask); | 
 |       entry->value = reinterpret_cast<void*>(pentry); | 
 |  | 
 |       if (pentry->IsReady()) { | 
 |         active_readers_.Add(pentry); | 
 |       } | 
 |     } else { | 
 |       pentry = reinterpret_cast<PortEntry*>(entry->value); | 
 |       bool was_ready = pentry->IsReady(); | 
 |       pentry->is_reading = IsReadingMask(mask); | 
 |       bool is_ready = pentry->IsReady(); | 
 |  | 
 |       if (was_ready && !is_ready) { | 
 |         active_readers_.Remove(pentry); | 
 |       } else if (!was_ready && is_ready) { | 
 |         active_readers_.Add(pentry); | 
 |       } | 
 |     } | 
 |  | 
 | #ifdef DEBUG | 
 |     // To ensure that all readers are ready. | 
 |     int ready_count = 0; | 
 |  | 
 |     if (active_readers_.HasHead()) { | 
 |       PortEntry* root = reinterpret_cast<PortEntry*>(active_readers_.head()); | 
 |       PortEntry* current = root; | 
 |       do { | 
 |         ASSERT(current->IsReady()); | 
 |         ready_count++; | 
 |         active_readers_.Rotate(); | 
 |         current = active_readers_.head(); | 
 |       } while (current != root); | 
 |     } | 
 |  | 
 |     for (SimpleHashMap::Entry* entry = tokens_map_.Start(); entry != nullptr; | 
 |          entry = tokens_map_.Next(entry)) { | 
 |       PortEntry* pentry = reinterpret_cast<PortEntry*>(entry->value); | 
 |       if (pentry->IsReady()) { | 
 |         ready_count--; | 
 |       } | 
 |     } | 
 |     // Ensure all ready items are in `active_readers_`. | 
 |     ASSERT(ready_count == 0); | 
 | #endif | 
 |   } | 
 |  | 
 |   virtual void RemovePort(Dart_Port port) { | 
 |     SimpleHashMap::Entry* entry = tokens_map_.Lookup( | 
 |         GetHashmapKeyFromPort(port), GetHashmapHashFromPort(port), false); | 
 |     if (entry != nullptr) { | 
 |       PortEntry* pentry = reinterpret_cast<PortEntry*>(entry->value); | 
 |       if (pentry->IsReady()) { | 
 |         active_readers_.Remove(pentry); | 
 |       } | 
 |       tokens_map_.Remove(GetHashmapKeyFromPort(port), | 
 |                          GetHashmapHashFromPort(port)); | 
 |       delete pentry; | 
 |     } else { | 
 |       // NOTE: This is a listening socket which has been immediately closed. | 
 |       // | 
 |       // If a listening socket is not listened on, the event handler does not | 
 |       // know about it beforehand. So the first time the event handler knows | 
 |       // about it, is when it is supposed to be closed. We therefore do nothing | 
 |       // here. | 
 |       // | 
 |       // But whether to close it, depends on whether other isolates have it open | 
 |       // as well or not. | 
 |     } | 
 |   } | 
 |  | 
 |   virtual void RemoveAllPorts() { | 
 |     for (SimpleHashMap::Entry* entry = tokens_map_.Start(); entry != nullptr; | 
 |          entry = tokens_map_.Next(entry)) { | 
 |       PortEntry* pentry = reinterpret_cast<PortEntry*>(entry->value); | 
 |       entry->value = nullptr; | 
 |       active_readers_.Remove(pentry); | 
 |       delete pentry; | 
 |     } | 
 |     tokens_map_.Clear(); | 
 |     active_readers_.RemoveAll(DeletePortEntry); | 
 |   } | 
 |  | 
 |   virtual Dart_Port NextNotifyDartPort(intptr_t events_ready) { | 
 |     // We're only sending `kInEvents` if there are multiple listeners (which is | 
 |     // listening socktes). | 
 |     ASSERT(IS_EVENT(events_ready, kInEvent) || | 
 |            IS_EVENT(events_ready, kDestroyedEvent)); | 
 |  | 
 |     if (active_readers_.HasHead()) { | 
 |       PortEntry* pentry = reinterpret_cast<PortEntry*>(active_readers_.head()); | 
 |  | 
 |       // Update token count. | 
 |       pentry->tokens.TakeOne(); | 
 |       if (pentry->tokens.IsEmpty()) { | 
 |         active_readers_.RemoveHead(); | 
 |       } else { | 
 |         active_readers_.Rotate(); | 
 |       } | 
 |  | 
 |       return pentry->dart_port; | 
 |     } | 
 |     return 0; | 
 |   } | 
 |  | 
 |   virtual void NotifyAllDartPorts(uintptr_t events) { | 
 |     // Unexpected close, asynchronous destroy or error events are the only | 
 |     // ones we broadcast to all listeners. | 
 |     ASSERT(IS_EVENT(events, kCloseEvent) || IS_EVENT(events, kErrorEvent) || | 
 |            IS_EVENT(events, kDestroyedEvent)); | 
 |  | 
 |     for (SimpleHashMap::Entry* entry = tokens_map_.Start(); entry != nullptr; | 
 |          entry = tokens_map_.Next(entry)) { | 
 |       PortEntry* pentry = reinterpret_cast<PortEntry*>(entry->value); | 
 |       DartUtils::PostInt32(pentry->dart_port, events); | 
 |  | 
 |       // Update token count. | 
 |       bool was_ready = pentry->IsReady(); | 
 |       pentry->tokens.TakeOne(); | 
 |       if (was_ready && pentry->tokens.IsEmpty()) { | 
 |         active_readers_.Remove(pentry); | 
 |       } | 
 |     } | 
 |   } | 
 |  | 
 |   virtual void ReturnTokens(Dart_Port port, int count) { | 
 |     SimpleHashMap::Entry* entry = tokens_map_.Lookup( | 
 |         GetHashmapKeyFromPort(port), GetHashmapHashFromPort(port), false); | 
 |     ASSERT(entry != nullptr); | 
 |  | 
 |     PortEntry* pentry = reinterpret_cast<PortEntry*>(entry->value); | 
 |     bool was_ready = pentry->IsReady(); | 
 |     pentry->tokens.Return(count); | 
 |     bool is_ready = pentry->IsReady(); | 
 |     if (!was_ready && is_ready) { | 
 |       active_readers_.Add(pentry); | 
 |     } | 
 |   } | 
 |  | 
 |   virtual intptr_t Mask() { | 
 |     if (active_readers_.HasHead()) { | 
 |       return 1 << kInEvent; | 
 |     } | 
 |     return 0; | 
 |   } | 
 |  | 
 |   virtual void Close() { DI::Close(); } | 
 |  | 
 |  private: | 
 |   static void DeletePortEntry(void* data) { | 
 |     PortEntry* entry = reinterpret_cast<PortEntry*>(data); | 
 |     delete entry; | 
 |   } | 
 |  | 
 |   // The [Dart_Port]s which are not paused (i.e. are interested in read events, | 
 |   // i.e. `mask == (1 << kInEvent)`) and we have enough tokens to communicate | 
 |   // with them. | 
 |   CircularLinkedList<PortEntry*> active_readers_; | 
 |  | 
 |   // A convenience mapping: | 
 |   //   Dart_Port -> struct PortEntry { dart_port, mask, tokens } | 
 |   SimpleHashMap tokens_map_; | 
 |  | 
 |   DISALLOW_COPY_AND_ASSIGN(DescriptorInfoMultipleMixin); | 
 | }; | 
 |  | 
 | }  // namespace bin | 
 | }  // namespace dart | 
 |  | 
 | // The event handler delegation class is OS specific. | 
 | #if defined(DART_HOST_OS_FUCHSIA) | 
 | #include "bin/eventhandler_fuchsia.h" | 
 | #elif defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) | 
 | #include "bin/eventhandler_linux.h" | 
 | #elif defined(DART_HOST_OS_MACOS) | 
 | #include "bin/eventhandler_macos.h" | 
 | #elif defined(DART_HOST_OS_WINDOWS) | 
 | #include "bin/eventhandler_win.h" | 
 | #else | 
 | #error Unknown target os. | 
 | #endif | 
 |  | 
 | namespace dart { | 
 | namespace bin { | 
 |  | 
 | class EventHandler { | 
 |  public: | 
 |   EventHandler() {} | 
 |   void SendData(intptr_t id, Dart_Port dart_port, int64_t data) { | 
 |     delegate_.SendData(id, dart_port, data); | 
 |   } | 
 |  | 
 |   /** | 
 |    * Signal to main thread that event handler is done. | 
 |    */ | 
 |   void NotifyShutdownDone(); | 
 |  | 
 |   /** | 
 |    * Start the event-handler. | 
 |    */ | 
 |   static void Start(); | 
 |  | 
 |   /** | 
 |    * Stop the event-handler. It's expected that there will be no further calls | 
 |    * to SendData after a call to Stop. | 
 |    */ | 
 |   static void Stop(); | 
 |  | 
 |   static EventHandlerImplementation* delegate(); | 
 |  | 
 |   static void SendFromNative(intptr_t id, Dart_Port port, int64_t data); | 
 |  | 
 |  private: | 
 |   friend class EventHandlerImplementation; | 
 |   EventHandlerImplementation delegate_; | 
 |  | 
 |   DISALLOW_COPY_AND_ASSIGN(EventHandler); | 
 | }; | 
 |  | 
 | }  // namespace bin | 
 | }  // namespace dart | 
 |  | 
 | #endif  // RUNTIME_BIN_EVENTHANDLER_H_ |