Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 : /* This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this
5 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : #include "nsEventQueue.h"
8 : #include "nsAutoPtr.h"
9 : #include "mozilla/Logging.h"
10 : #include "nsThreadUtils.h"
11 : #include "prthread.h"
12 : #include "mozilla/ChaosMode.h"
13 :
14 : using namespace mozilla;
15 :
16 : static LazyLogModule sEventQueueLog("nsEventQueue");
17 : #ifdef LOG
18 : #undef LOG
19 : #endif
20 : #define LOG(args) MOZ_LOG(sEventQueueLog, mozilla::LogLevel::Debug, args)
21 :
22 243 : nsEventQueue::nsEventQueue(mozilla::CondVar& aCondVar, EventQueueType aType)
23 : : mHead(nullptr)
24 : , mTail(nullptr)
25 : , mOffsetHead(0)
26 : , mOffsetTail(0)
27 : , mEventsAvailable(aCondVar)
28 243 : , mType(aType)
29 : {
30 243 : }
31 :
32 58 : nsEventQueue::~nsEventQueue()
33 : {
34 : // It'd be nice to be able to assert that no one else is holding the lock,
35 : // but NSPR doesn't really expose APIs for it.
36 29 : NS_ASSERTION(IsEmpty(),
37 : "Non-empty event queue being destroyed; events being leaked.");
38 :
39 29 : if (mHead) {
40 14 : FreePage(mHead);
41 : }
42 29 : }
43 :
44 : bool
45 14 : nsEventQueue::PeekEvent(nsIRunnable** aEvent, MutexAutoLock& aProofOfLock)
46 : {
47 14 : MOZ_ASSERT(aEvent);
48 14 : *aEvent = nullptr;
49 :
50 14 : if (IsEmpty()) {
51 0 : return false;
52 : }
53 :
54 14 : MOZ_ASSERT(mOffsetHead < EVENTS_PER_PAGE);
55 14 : MOZ_ASSERT_IF(mHead == mTail, mOffsetHead <= mOffsetTail);
56 14 : NS_ADDREF(*aEvent = mHead->mEvents[mOffsetHead]);
57 :
58 14 : MOZ_ASSERT(*aEvent);
59 :
60 14 : return true;
61 : }
62 :
63 : bool
64 7036 : nsEventQueue::GetEvent(bool aMayWait, nsIRunnable** aResult,
65 : MutexAutoLock& aProofOfLock)
66 : {
67 7036 : if (aResult) {
68 2211 : *aResult = nullptr;
69 : }
70 :
71 7036 : while (IsEmpty()) {
72 3919 : if (!aMayWait) {
73 3767 : return false;
74 : }
75 152 : LOG(("EVENTQ(%p): wait begin\n", this));
76 152 : mEventsAvailable.Wait();
77 126 : LOG(("EVENTQ(%p): wait end\n", this));
78 :
79 126 : if (mType == eSharedCondVarQueue) {
80 126 : if (IsEmpty()) {
81 0 : return false;
82 : }
83 126 : break;
84 : }
85 : }
86 :
87 3243 : if (aResult) {
88 1673 : MOZ_ASSERT(mOffsetHead < EVENTS_PER_PAGE);
89 1673 : MOZ_ASSERT_IF(mHead == mTail, mOffsetHead <= mOffsetTail);
90 1673 : *aResult = mHead->mEvents[mOffsetHead++];
91 :
92 1673 : MOZ_ASSERT(*aResult);
93 1673 : MOZ_ASSERT(mOffsetHead <= EVENTS_PER_PAGE);
94 :
95 : // Check if mHead points to empty Page
96 1673 : if (mOffsetHead == EVENTS_PER_PAGE) {
97 3 : Page* dead = mHead;
98 3 : mHead = mHead->mNext;
99 3 : FreePage(dead);
100 3 : mOffsetHead = 0;
101 : }
102 : }
103 :
104 3243 : return true;
105 : }
106 :
107 : void
108 1724 : nsEventQueue::PutEvent(already_AddRefed<nsIRunnable>&& aRunnable,
109 : MutexAutoLock& aProofOfLock)
110 : {
111 1724 : if (!mHead) {
112 84 : mHead = NewPage();
113 84 : MOZ_ASSERT(mHead);
114 :
115 84 : mTail = mHead;
116 84 : mOffsetHead = 0;
117 84 : mOffsetTail = 0;
118 1640 : } else if (mOffsetTail == EVENTS_PER_PAGE) {
119 3 : Page* page = NewPage();
120 3 : MOZ_ASSERT(page);
121 :
122 3 : mTail->mNext = page;
123 3 : mTail = page;
124 3 : mOffsetTail = 0;
125 : }
126 :
127 1724 : nsIRunnable*& queueLocation = mTail->mEvents[mOffsetTail];
128 1724 : MOZ_ASSERT(!queueLocation);
129 1724 : queueLocation = aRunnable.take();
130 1724 : ++mOffsetTail;
131 1724 : LOG(("EVENTQ(%p): notify\n", this));
132 1724 : mEventsAvailable.Notify();
133 1724 : }
134 :
135 : void
136 0 : nsEventQueue::PutEvent(nsIRunnable* aRunnable, MutexAutoLock& aProofOfLock)
137 : {
138 0 : nsCOMPtr<nsIRunnable> event(aRunnable);
139 0 : PutEvent(event.forget(), aProofOfLock);
140 0 : }
141 :
142 : size_t
143 73 : nsEventQueue::Count(MutexAutoLock& aProofOfLock) const
144 : {
145 : // It is obvious count is 0 when the queue is empty.
146 73 : if (!mHead) {
147 2 : return 0;
148 : }
149 :
150 : /* How we count the number of events in the queue:
151 : * 1. Let pageCount(x, y) denote the number of pages excluding the tail page
152 : * where x is the index of head page and y is the index of the tail page.
153 : * 2. Then we have pageCount(x, y) = y - x.
154 : *
155 : * Ex: pageCount(0, 0) = 0 where both head and tail pages point to page 0.
156 : * pageCount(0, 1) = 1 where head points to page 0 and tail points page 1.
157 : *
158 : * 3. number of events = (EVENTS_PER_PAGE * pageCount(x, y))
159 : * - (empty slots in head page) + (non-empty slots in tail page)
160 : * = (EVENTS_PER_PAGE * pageCount(x, y)) - mOffsetHead + mOffsetTail
161 : */
162 :
163 71 : int count = -mOffsetHead;
164 :
165 : // Compute (EVENTS_PER_PAGE * pageCount(x, y))
166 71 : for (Page* page = mHead; page != mTail; page = page->mNext) {
167 0 : count += EVENTS_PER_PAGE;
168 : }
169 :
170 71 : count += mOffsetTail;
171 71 : MOZ_ASSERT(count >= 0);
172 :
173 71 : return count;
174 : }
|