event_subscriber.h 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. // Copyright (c) 2017 GitHub, Inc.
  2. // Use of this source code is governed by the MIT license that can be
  3. // found in the LICENSE file.
  4. #ifndef ATOM_BROWSER_API_EVENT_SUBSCRIBER_H_
  5. #define ATOM_BROWSER_API_EVENT_SUBSCRIBER_H_
  6. #include <map>
  7. #include <string>
  8. #include "atom/common/api/event_emitter_caller.h"
  9. #include "base/synchronization/lock.h"
  10. #include "content/public/browser/browser_thread.h"
  11. #include "native_mate/arguments.h"
  12. namespace mate {
  13. namespace internal {
  14. class EventSubscriberBase {
  15. public:
  16. EventSubscriberBase(v8::Isolate* isolate, v8::Local<v8::Object> emitter);
  17. virtual ~EventSubscriberBase();
  18. virtual void EventEmitted(const std::string& event_name,
  19. mate::Arguments* args) = 0;
  20. protected:
  21. void On(const std::string& event_name);
  22. void Off(const std::string& event_name);
  23. void RemoveAllListeners();
  24. private:
  25. std::map<std::string, v8::Global<v8::Value>>::iterator RemoveListener(
  26. std::map<std::string, v8::Global<v8::Value>>::iterator it);
  27. v8::Isolate* isolate_;
  28. v8::Global<v8::Object> emitter_;
  29. std::map<std::string, v8::Global<v8::Value>> js_handlers_;
  30. DISALLOW_COPY_AND_ASSIGN(EventSubscriberBase);
  31. };
  32. } // namespace internal
  33. template <typename HandlerType>
  34. class EventSubscriber : internal::EventSubscriberBase {
  35. public:
  36. using EventCallback = void (HandlerType::*)(mate::Arguments* args);
  37. // Alias to unique_ptr with deleter.
  38. using unique_ptr = std::unique_ptr<EventSubscriber<HandlerType>,
  39. void (*)(EventSubscriber<HandlerType>*)>;
  40. // EventSubscriber should only be created/deleted in the main thread since it
  41. // communicates with the V8 engine. This smart pointer makes it simpler to
  42. // bind the lifetime of EventSubscriber with a class whose lifetime is managed
  43. // by a non-UI thread.
  44. class SafePtr : public unique_ptr {
  45. public:
  46. SafePtr() : SafePtr(nullptr) {}
  47. explicit SafePtr(EventSubscriber<HandlerType>* ptr)
  48. : unique_ptr(ptr, Deleter) {}
  49. private:
  50. // Custom deleter that schedules destructor invocation to the main thread.
  51. static void Deleter(EventSubscriber<HandlerType>* ptr) {
  52. DCHECK(
  53. !::content::BrowserThread::CurrentlyOn(::content::BrowserThread::UI));
  54. DCHECK(ptr);
  55. // Acquire handler lock and reset handler_ to ensure that any new events
  56. // emitted will be ignored after this function returns
  57. base::AutoLock auto_lock(ptr->handler_lock_);
  58. ptr->handler_ = nullptr;
  59. content::BrowserThread::PostTask(
  60. content::BrowserThread::UI, FROM_HERE,
  61. base::BindOnce(
  62. [](EventSubscriber<HandlerType>* subscriber) {
  63. {
  64. // It is possible that this function will execute in the UI
  65. // thread before the outer function has returned and destroyed
  66. // its auto_lock. We need to acquire the lock before deleting
  67. // or risk a crash.
  68. base::AutoLock auto_lock(subscriber->handler_lock_);
  69. }
  70. delete subscriber;
  71. },
  72. ptr));
  73. }
  74. };
  75. EventSubscriber(HandlerType* handler,
  76. v8::Isolate* isolate,
  77. v8::Local<v8::Object> emitter)
  78. : EventSubscriberBase(isolate, emitter), handler_(handler) {
  79. DCHECK_CURRENTLY_ON(::content::BrowserThread::UI);
  80. }
  81. void On(const std::string& event, EventCallback callback) {
  82. DCHECK_CURRENTLY_ON(::content::BrowserThread::UI);
  83. EventSubscriberBase::On(event);
  84. callbacks_.insert(std::make_pair(event, callback));
  85. }
  86. void Off(const std::string& event) {
  87. DCHECK_CURRENTLY_ON(::content::BrowserThread::UI);
  88. EventSubscriberBase::Off(event);
  89. DCHECK(callbacks_.find(event) != callbacks_.end());
  90. callbacks_.erase(callbacks_.find(event));
  91. }
  92. void RemoveAllListeners() {
  93. DCHECK_CURRENTLY_ON(::content::BrowserThread::UI);
  94. EventSubscriberBase::RemoveAllListeners();
  95. callbacks_.clear();
  96. }
  97. private:
  98. void EventEmitted(const std::string& event_name,
  99. mate::Arguments* args) override {
  100. DCHECK_CURRENTLY_ON(::content::BrowserThread::UI);
  101. base::AutoLock auto_lock(handler_lock_);
  102. if (!handler_) {
  103. // handler_ was probably destroyed by another thread and we should not
  104. // access it.
  105. return;
  106. }
  107. auto it = callbacks_.find(event_name);
  108. if (it != callbacks_.end()) {
  109. auto method = it->second;
  110. (handler_->*method)(args);
  111. }
  112. }
  113. HandlerType* handler_;
  114. base::Lock handler_lock_;
  115. std::map<std::string, EventCallback> callbacks_;
  116. };
  117. } // namespace mate
  118. #endif // ATOM_BROWSER_API_EVENT_SUBSCRIBER_H_