blob: 74b1024deb16176706eaf1c071df7f5af6447855 [file] [log] [blame]
henrike@webrtc.orgf0488722014-05-13 18:00:26 +00001/*
2 * Copyright 2004 The WebRTC Project Authors. All rights reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
11#if defined(WEBRTC_POSIX)
12#include <sys/time.h>
13#endif
14
andresp@webrtc.orgff689be2015-02-12 11:54:26 +000015#include <algorithm>
16
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000017#include "webrtc/base/common.h"
18#include "webrtc/base/logging.h"
19#include "webrtc/base/messagequeue.h"
20#if defined(__native_client__)
21#include "webrtc/base/nullsocketserver.h"
22typedef rtc::NullSocketServer DefaultSocketServer;
23#else
24#include "webrtc/base/physicalsocketserver.h"
25typedef rtc::PhysicalSocketServer DefaultSocketServer;
26#endif
27
28namespace rtc {
29
30const uint32 kMaxMsgLatency = 150; // 150 ms
31
32//------------------------------------------------------------------
33// MessageQueueManager
34
35MessageQueueManager* MessageQueueManager::instance_ = NULL;
36
37MessageQueueManager* MessageQueueManager::Instance() {
38 // Note: This is not thread safe, but it is first called before threads are
39 // spawned.
40 if (!instance_)
41 instance_ = new MessageQueueManager;
42 return instance_;
43}
44
45bool MessageQueueManager::IsInitialized() {
46 return instance_ != NULL;
47}
48
49MessageQueueManager::MessageQueueManager() {
50}
51
52MessageQueueManager::~MessageQueueManager() {
53}
54
55void MessageQueueManager::Add(MessageQueue *message_queue) {
56 return Instance()->AddInternal(message_queue);
57}
58void MessageQueueManager::AddInternal(MessageQueue *message_queue) {
59 // MessageQueueManager methods should be non-reentrant, so we
60 // ASSERT that is the case. If any of these ASSERT, please
61 // contact bpm or jbeda.
62 ASSERT(!crit_.CurrentThreadIsOwner());
63 CritScope cs(&crit_);
64 message_queues_.push_back(message_queue);
65}
66
67void MessageQueueManager::Remove(MessageQueue *message_queue) {
68 // If there isn't a message queue manager instance, then there isn't a queue
69 // to remove.
70 if (!instance_) return;
71 return Instance()->RemoveInternal(message_queue);
72}
73void MessageQueueManager::RemoveInternal(MessageQueue *message_queue) {
74 ASSERT(!crit_.CurrentThreadIsOwner()); // See note above.
75 // If this is the last MessageQueue, destroy the manager as well so that
76 // we don't leak this object at program shutdown. As mentioned above, this is
77 // not thread-safe, but this should only happen at program termination (when
78 // the ThreadManager is destroyed, and threads are no longer active).
79 bool destroy = false;
80 {
81 CritScope cs(&crit_);
82 std::vector<MessageQueue *>::iterator iter;
83 iter = std::find(message_queues_.begin(), message_queues_.end(),
84 message_queue);
85 if (iter != message_queues_.end()) {
86 message_queues_.erase(iter);
87 }
88 destroy = message_queues_.empty();
89 }
90 if (destroy) {
91 instance_ = NULL;
92 delete this;
93 }
94}
95
96void MessageQueueManager::Clear(MessageHandler *handler) {
97 // If there isn't a message queue manager instance, then there aren't any
98 // queues to remove this handler from.
99 if (!instance_) return;
100 return Instance()->ClearInternal(handler);
101}
102void MessageQueueManager::ClearInternal(MessageHandler *handler) {
103 ASSERT(!crit_.CurrentThreadIsOwner()); // See note above.
104 CritScope cs(&crit_);
105 std::vector<MessageQueue *>::iterator iter;
106 for (iter = message_queues_.begin(); iter != message_queues_.end(); iter++)
107 (*iter)->Clear(handler);
108}
109
110//------------------------------------------------------------------
111// MessageQueue
112
113MessageQueue::MessageQueue(SocketServer* ss)
henrike@webrtc.org99b41622014-05-21 20:42:17 +0000114 : ss_(ss), fStop_(false), fPeekKeep_(false),
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000115 dmsgq_next_num_(0) {
116 if (!ss_) {
117 // Currently, MessageQueue holds a socket server, and is the base class for
118 // Thread. It seems like it makes more sense for Thread to hold the socket
119 // server, and provide it to the MessageQueue, since the Thread controls
120 // the I/O model, and MQ is agnostic to those details. Anyway, this causes
121 // messagequeue_unittest to depend on network libraries... yuck.
122 default_ss_.reset(new DefaultSocketServer());
123 ss_ = default_ss_.get();
124 }
125 ss_->SetMessageQueue(this);
henrike@webrtc.org99b41622014-05-21 20:42:17 +0000126 MessageQueueManager::Add(this);
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000127}
128
129MessageQueue::~MessageQueue() {
130 // The signal is done from here to ensure
131 // that it always gets called when the queue
132 // is going away.
133 SignalQueueDestroyed();
henrike@webrtc.org99b41622014-05-21 20:42:17 +0000134 MessageQueueManager::Remove(this);
135 Clear(NULL);
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000136 if (ss_) {
137 ss_->SetMessageQueue(NULL);
138 }
139}
140
141void MessageQueue::set_socketserver(SocketServer* ss) {
142 ss_ = ss ? ss : default_ss_.get();
143 ss_->SetMessageQueue(this);
144}
145
146void MessageQueue::Quit() {
147 fStop_ = true;
148 ss_->WakeUp();
149}
150
151bool MessageQueue::IsQuitting() {
152 return fStop_;
153}
154
155void MessageQueue::Restart() {
156 fStop_ = false;
157}
158
159bool MessageQueue::Peek(Message *pmsg, int cmsWait) {
160 if (fPeekKeep_) {
161 *pmsg = msgPeek_;
162 return true;
163 }
164 if (!Get(pmsg, cmsWait))
165 return false;
166 msgPeek_ = *pmsg;
167 fPeekKeep_ = true;
168 return true;
169}
170
171bool MessageQueue::Get(Message *pmsg, int cmsWait, bool process_io) {
172 // Return and clear peek if present
173 // Always return the peek if it exists so there is Peek/Get symmetry
174
175 if (fPeekKeep_) {
176 *pmsg = msgPeek_;
177 fPeekKeep_ = false;
178 return true;
179 }
180
181 // Get w/wait + timer scan / dispatch + socket / event multiplexer dispatch
182
183 int cmsTotal = cmsWait;
184 int cmsElapsed = 0;
185 uint32 msStart = Time();
186 uint32 msCurrent = msStart;
187 while (true) {
188 // Check for sent messages
189 ReceiveSends();
190
191 // Check for posted events
192 int cmsDelayNext = kForever;
193 bool first_pass = true;
194 while (true) {
195 // All queue operations need to be locked, but nothing else in this loop
196 // (specifically handling disposed message) can happen inside the crit.
197 // Otherwise, disposed MessageHandlers will cause deadlocks.
198 {
199 CritScope cs(&crit_);
200 // On the first pass, check for delayed messages that have been
201 // triggered and calculate the next trigger time.
202 if (first_pass) {
203 first_pass = false;
204 while (!dmsgq_.empty()) {
205 if (TimeIsLater(msCurrent, dmsgq_.top().msTrigger_)) {
206 cmsDelayNext = TimeDiff(dmsgq_.top().msTrigger_, msCurrent);
207 break;
208 }
209 msgq_.push_back(dmsgq_.top().msg_);
210 dmsgq_.pop();
211 }
212 }
213 // Pull a message off the message queue, if available.
214 if (msgq_.empty()) {
215 break;
216 } else {
217 *pmsg = msgq_.front();
218 msgq_.pop_front();
219 }
220 } // crit_ is released here.
221
222 // Log a warning for time-sensitive messages that we're late to deliver.
223 if (pmsg->ts_sensitive) {
224 int32 delay = TimeDiff(msCurrent, pmsg->ts_sensitive);
225 if (delay > 0) {
226 LOG_F(LS_WARNING) << "id: " << pmsg->message_id << " delay: "
227 << (delay + kMaxMsgLatency) << "ms";
228 }
229 }
230 // If this was a dispose message, delete it and skip it.
231 if (MQID_DISPOSE == pmsg->message_id) {
232 ASSERT(NULL == pmsg->phandler);
233 delete pmsg->pdata;
234 *pmsg = Message();
235 continue;
236 }
237 return true;
238 }
239
240 if (fStop_)
241 break;
242
243 // Which is shorter, the delay wait or the asked wait?
244
245 int cmsNext;
246 if (cmsWait == kForever) {
247 cmsNext = cmsDelayNext;
248 } else {
andresp@webrtc.orgff689be2015-02-12 11:54:26 +0000249 cmsNext = std::max(0, cmsTotal - cmsElapsed);
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000250 if ((cmsDelayNext != kForever) && (cmsDelayNext < cmsNext))
251 cmsNext = cmsDelayNext;
252 }
253
254 // Wait and multiplex in the meantime
255 if (!ss_->Wait(cmsNext, process_io))
256 return false;
257
258 // If the specified timeout expired, return
259
260 msCurrent = Time();
261 cmsElapsed = TimeDiff(msCurrent, msStart);
262 if (cmsWait != kForever) {
263 if (cmsElapsed >= cmsWait)
264 return false;
265 }
266 }
267 return false;
268}
269
270void MessageQueue::ReceiveSends() {
271}
272
273void MessageQueue::Post(MessageHandler *phandler, uint32 id,
274 MessageData *pdata, bool time_sensitive) {
275 if (fStop_)
276 return;
277
278 // Keep thread safe
279 // Add the message to the end of the queue
280 // Signal for the multiplexer to return
281
282 CritScope cs(&crit_);
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000283 Message msg;
284 msg.phandler = phandler;
285 msg.message_id = id;
286 msg.pdata = pdata;
287 if (time_sensitive) {
288 msg.ts_sensitive = Time() + kMaxMsgLatency;
289 }
290 msgq_.push_back(msg);
291 ss_->WakeUp();
292}
293
294void MessageQueue::DoDelayPost(int cmsDelay, uint32 tstamp,
295 MessageHandler *phandler, uint32 id, MessageData* pdata) {
296 if (fStop_)
297 return;
298
299 // Keep thread safe
300 // Add to the priority queue. Gets sorted soonest first.
301 // Signal for the multiplexer to return.
302
303 CritScope cs(&crit_);
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000304 Message msg;
305 msg.phandler = phandler;
306 msg.message_id = id;
307 msg.pdata = pdata;
308 DelayedMessage dmsg(cmsDelay, tstamp, dmsgq_next_num_, msg);
309 dmsgq_.push(dmsg);
310 // If this message queue processes 1 message every millisecond for 50 days,
311 // we will wrap this number. Even then, only messages with identical times
312 // will be misordered, and then only briefly. This is probably ok.
313 VERIFY(0 != ++dmsgq_next_num_);
314 ss_->WakeUp();
315}
316
317int MessageQueue::GetDelay() {
318 CritScope cs(&crit_);
319
320 if (!msgq_.empty())
321 return 0;
322
323 if (!dmsgq_.empty()) {
324 int delay = TimeUntil(dmsgq_.top().msTrigger_);
325 if (delay < 0)
326 delay = 0;
327 return delay;
328 }
329
330 return kForever;
331}
332
333void MessageQueue::Clear(MessageHandler *phandler, uint32 id,
334 MessageList* removed) {
335 CritScope cs(&crit_);
336
337 // Remove messages with phandler
338
339 if (fPeekKeep_ && msgPeek_.Match(phandler, id)) {
340 if (removed) {
341 removed->push_back(msgPeek_);
342 } else {
343 delete msgPeek_.pdata;
344 }
345 fPeekKeep_ = false;
346 }
347
348 // Remove from ordered message queue
349
350 for (MessageList::iterator it = msgq_.begin(); it != msgq_.end();) {
351 if (it->Match(phandler, id)) {
352 if (removed) {
353 removed->push_back(*it);
354 } else {
355 delete it->pdata;
356 }
357 it = msgq_.erase(it);
358 } else {
359 ++it;
360 }
361 }
362
363 // Remove from priority queue. Not directly iterable, so use this approach
decurtis@webrtc.org2af30572015-02-21 01:59:50 +0000364
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000365 PriorityQueue::container_type::iterator new_end = dmsgq_.container().begin();
366 for (PriorityQueue::container_type::iterator it = new_end;
367 it != dmsgq_.container().end(); ++it) {
368 if (it->msg_.Match(phandler, id)) {
369 if (removed) {
370 removed->push_back(it->msg_);
371 } else {
372 delete it->msg_.pdata;
373 }
374 } else {
decurtis@webrtc.org2af30572015-02-21 01:59:50 +0000375 *new_end++ = *it;
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000376 }
377 }
378 dmsgq_.container().erase(new_end, dmsgq_.container().end());
379 dmsgq_.reheap();
380}
381
382void MessageQueue::Dispatch(Message *pmsg) {
383 pmsg->phandler->OnMessage(pmsg);
384}
385
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000386} // namespace rtc