summaryrefslogtreecommitdiffstats
path: root/xpcom/threads/SynchronizedEventQueue.h
blob: e4cf1a62af1b37dc0a849cc89091b7863dc1d555 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef mozilla_SynchronizedEventQueue_h
#define mozilla_SynchronizedEventQueue_h

#include "mozilla/AlreadyAddRefed.h"
#include "mozilla/EventQueue.h"
#include "mozilla/MemoryReporting.h"
#include "mozilla/Mutex.h"
#include "nsIThreadInternal.h"
#include "nsCOMPtr.h"
#include "nsTObserverArray.h"

class nsIEventTarget;
class nsISerialEventTarget;
class nsIThreadObserver;

namespace mozilla {

// A SynchronizedEventQueue is an abstract class for event queues that can be
// used across threads. A SynchronizedEventQueue implementation will typically
// use locks and condition variables to guarantee consistency. The methods of
// SynchronizedEventQueue are split between ThreadTargetSink (which contains
// methods for posting events) and SynchronizedEventQueue (which contains
// methods for getting events). This split allows event targets (specifically
// ThreadEventTarget) to use a narrow interface, since they only need to post
// events.
//
// ThreadEventQueue is the canonical implementation of
// SynchronizedEventQueue. When Quantum DOM is implemented, we will use a
// different synchronized queue on the main thread, SchedulerEventQueue, which
// will handle the cooperative threading model.

class ThreadTargetSink {
 public:
  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ThreadTargetSink)

  virtual bool PutEvent(already_AddRefed<nsIRunnable>&& aEvent,
                        EventQueuePriority aPriority) = 0;

  // After this method is called, no more events can be posted.
  virtual void Disconnect(const MutexAutoLock& aProofOfLock) = 0;

  virtual nsresult RegisterShutdownTask(nsITargetShutdownTask* aTask) = 0;
  virtual nsresult UnregisterShutdownTask(nsITargetShutdownTask* aTask) = 0;

  size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) {
    return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
  }

  // Not const because overrides may need to take a lock
  virtual size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) = 0;

 protected:
  virtual ~ThreadTargetSink() = default;
};

class SynchronizedEventQueue : public ThreadTargetSink {
 public:
  virtual already_AddRefed<nsIRunnable> GetEvent(
      bool aMayWait, mozilla::TimeDuration* aLastEventDelay = nullptr) = 0;
  virtual bool HasPendingEvent() = 0;

  // This method atomically checks if there are pending events and, if there are
  // none, forbids future events from being posted. It returns true if there
  // were no pending events.
  virtual bool ShutdownIfNoPendingEvents() = 0;

  // These methods provide access to an nsIThreadObserver, whose methods are
  // called when posting and processing events. SetObserver should only be
  // called on the thread that processes events. GetObserver can be called from
  // any thread. GetObserverOnThread must be used from the thread that processes
  // events; it does not acquire a lock.
  virtual already_AddRefed<nsIThreadObserver> GetObserver() = 0;
  virtual already_AddRefed<nsIThreadObserver> GetObserverOnThread() = 0;
  virtual void SetObserver(nsIThreadObserver* aObserver) = 0;

  void AddObserver(nsIThreadObserver* aObserver);
  void RemoveObserver(nsIThreadObserver* aObserver);
  const nsTObserverArray<nsCOMPtr<nsIThreadObserver>>& EventObservers();

  size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) override {
    // Normally we'd return
    // mEventObservers.ShallowSizeOfExcludingThis(aMallocSizeOf); However,
    // mEventObservers may be being mutated on another thread, and we don't lock
    // around access, so locking here wouldn't help.  They're small, so
    return 0;
  }

  /**
   * This method causes any events currently enqueued on the thread to be
   * suppressed until PopEventQueue is called, and any event dispatched to this
   * thread's nsIEventTarget will queue as well. Calls to PushEventQueue may be
   * nested and must each be paired with a call to PopEventQueue in order to
   * restore the original state of the thread. The returned nsIEventTarget may
   * be used to push events onto the nested queue. Dispatching will be disabled
   * once the event queue is popped. The thread will only ever process pending
   * events for the innermost event queue. Must only be called on the target
   * thread.
   */
  virtual already_AddRefed<nsISerialEventTarget> PushEventQueue() = 0;

  /**
   * Revert a call to PushEventQueue. When an event queue is popped, any events
   * remaining in the queue are appended to the elder queue. This also causes
   * the nsIEventTarget returned from PushEventQueue to stop dispatching events.
   * Must only be called on the target thread, and with the innermost event
   * queue.
   */
  virtual void PopEventQueue(nsIEventTarget* aTarget) = 0;

  /**
   * Flush the list of shutdown tasks which were previously registered.  After
   * this is called, new shutdown tasks cannot be registered.
   */
  virtual void RunShutdownTasks() = 0;

 protected:
  virtual ~SynchronizedEventQueue() = default;

 private:
  nsTObserverArray<nsCOMPtr<nsIThreadObserver>> mEventObservers;
};

}  // namespace mozilla

#endif  // mozilla_SynchronizedEventQueue_h