blob: 10cf81ab677ced93d284058a063d09f89e58db77 [file] [log] [blame]
Andreas Hubere46b7be2009-07-14 16:56:47 -07001/*
2 * Copyright (C) 2009 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#undef __STRICT_ANSI__
18#define __STDINT_LIMITS
19#include <stdint.h>
20
21#define LOG_TAG "TimedEventQueue"
22#include <utils/Log.h>
23
24#include <sys/time.h>
25
26#undef NDEBUG
27#include <assert.h>
28
29#include <media/stagefright/TimedEventQueue.h>
30
31namespace android {
32
33TimedEventQueue::TimedEventQueue()
34 : mRunning(false),
35 mStopped(false) {
36}
37
38TimedEventQueue::~TimedEventQueue() {
39 stop();
40}
41
42void TimedEventQueue::start() {
43 if (mRunning) {
44 return;
45 }
46
47 mStopped = false;
48
49 pthread_attr_t attr;
50 pthread_attr_init(&attr);
51 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
52
53 pthread_create(&mThread, &attr, ThreadWrapper, this);
54
55 pthread_attr_destroy(&attr);
56
57 mRunning = true;
58}
59
60void TimedEventQueue::stop(bool flush) {
61 if (!mRunning) {
62 return;
63 }
64
65 if (flush) {
66 postEventToBack(new StopEvent);
67 } else {
68 postTimedEvent(new StopEvent, INT64_MIN);
69 }
70
71 void *dummy;
72 pthread_join(mThread, &dummy);
73
74 mQueue.clear();
75
76 mRunning = false;
77}
78
79void TimedEventQueue::postEvent(const sp<Event> &event) {
80 // Reserve an earlier timeslot an INT64_MIN to be able to post
81 // the StopEvent to the absolute head of the queue.
82 postTimedEvent(event, INT64_MIN + 1);
83}
84
85void TimedEventQueue::postEventToBack(const sp<Event> &event) {
86 postTimedEvent(event, INT64_MAX);
87}
88
89void TimedEventQueue::postEventWithDelay(
90 const sp<Event> &event, int64_t delay_us) {
91 assert(delay_us >= 0);
92 postTimedEvent(event, getRealTimeUs() + delay_us);
93}
94
95void TimedEventQueue::postTimedEvent(
96 const sp<Event> &event, int64_t realtime_us) {
97 Mutex::Autolock autoLock(mLock);
98
99 List<QueueItem>::iterator it = mQueue.begin();
100 while (it != mQueue.end() && realtime_us >= (*it).realtime_us) {
101 ++it;
102 }
103
104 QueueItem item;
105 item.event = event;
106 item.realtime_us = realtime_us;
107
108 if (it == mQueue.begin()) {
109 mQueueHeadChangedCondition.signal();
110 }
111
112 mQueue.insert(it, item);
113
114 mQueueNotEmptyCondition.signal();
115}
116
117bool TimedEventQueue::cancelEvent(const sp<Event> &event) {
118 Mutex::Autolock autoLock(mLock);
119
120 List<QueueItem>::iterator it = mQueue.begin();
121 while (it != mQueue.end() && (*it).event != event) {
122 ++it;
123 }
124
125 if (it == mQueue.end()) {
126 return false;
127 }
128
129 if (it == mQueue.begin()) {
130 mQueueHeadChangedCondition.signal();
131 }
132
133 mQueue.erase(it);
134
135 return true;
136}
137
138// static
139int64_t TimedEventQueue::getRealTimeUs() {
140 struct timeval tv;
141 gettimeofday(&tv, NULL);
142
143 return (int64_t)tv.tv_sec * 1000000 + tv.tv_usec;
144}
145
146// static
147void *TimedEventQueue::ThreadWrapper(void *me) {
148 static_cast<TimedEventQueue *>(me)->threadEntry();
149
150 return NULL;
151}
152
153void TimedEventQueue::threadEntry() {
154 for (;;) {
155 int64_t now_us;
156 sp<Event> event;
157
158 {
159 Mutex::Autolock autoLock(mLock);
160
161 if (mStopped) {
162 break;
163 }
164
165 while (mQueue.empty()) {
166 mQueueNotEmptyCondition.wait(mLock);
167 }
168
169 List<QueueItem>::iterator it;
170 for (;;) {
171 it = mQueue.begin();
172
173 now_us = getRealTimeUs();
174 int64_t when_us = (*it).realtime_us;
175
176 int64_t delay_us;
177 if (when_us < 0 || when_us == INT64_MAX) {
178 delay_us = 0;
179 } else {
180 delay_us = when_us - now_us;
181 }
182
183 if (delay_us <= 0) {
184 break;
185 }
186
187 status_t err = mQueueHeadChangedCondition.waitRelative(
188 mLock, delay_us * 1000);
189
190 if (err == -ETIMEDOUT) {
191 now_us = getRealTimeUs();
192 break;
193 }
194 }
195
196 event = (*it).event;
197 mQueue.erase(it);
198 }
199
200 // Fire event with the lock NOT held.
201 event->fire(this, now_us);
202 }
203}
204
205} // namespace android
206