blob: f4f5d40cc1577ec070a94d7ea3429b24c7e1c7fe [file] [log] [blame]
Jason Sams326e0dd2009-05-22 14:03:28 -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#include "rsLocklessFifo.h"
18
19using namespace android;
20
Jason Sams326e0dd2009-05-22 14:03:28 -070021
22LocklessCommandFifo::LocklessCommandFifo()
23{
24}
25
26LocklessCommandFifo::~LocklessCommandFifo()
27{
Jason Sams8c0ee652009-08-25 14:49:07 -070028 if (!mInShutdown) {
29 shutdown();
30 }
31 free(mBuffer);
32}
33
34void LocklessCommandFifo::shutdown()
35{
36 mInShutdown = true;
37 mSignalToWorker.set();
Jason Sams326e0dd2009-05-22 14:03:28 -070038}
39
40bool LocklessCommandFifo::init(uint32_t sizeInBytes)
41{
42 // Add room for a buffer reset command
43 mBuffer = static_cast<uint8_t *>(malloc(sizeInBytes + 4));
44 if (!mBuffer) {
45 LOGE("LocklessFifo allocation failure");
46 return false;
47 }
48
Jason Sams732f1c02009-06-18 16:58:42 -070049 if (!mSignalToControl.init() || !mSignalToWorker.init()) {
50 LOGE("Signal setup failed");
Jason Sams326e0dd2009-05-22 14:03:28 -070051 free(mBuffer);
52 return false;
53 }
54
Jason Sams8c0ee652009-08-25 14:49:07 -070055 mInShutdown = false;
Jason Sams326e0dd2009-05-22 14:03:28 -070056 mSize = sizeInBytes;
57 mPut = mBuffer;
58 mGet = mBuffer;
59 mEnd = mBuffer + (sizeInBytes) - 1;
60 dumpState("init");
61 return true;
62}
63
Jason Sams8c0ee652009-08-25 14:49:07 -070064uint32_t LocklessCommandFifo::getFreeSpace() const
Jason Sams326e0dd2009-05-22 14:03:28 -070065{
66 int32_t freeSpace = 0;
67 //dumpState("getFreeSpace");
68
69 if (mPut >= mGet) {
70 freeSpace = mEnd - mPut;
71 } else {
72 freeSpace = mGet - mPut;
73 }
74
75 if (freeSpace < 0) {
76 freeSpace = 0;
77 }
Jason Sams326e0dd2009-05-22 14:03:28 -070078 return freeSpace;
79}
80
81bool LocklessCommandFifo::isEmpty() const
82{
83 return mPut == mGet;
84}
85
86
87void * LocklessCommandFifo::reserve(uint32_t sizeInBytes)
88{
Jason Sams565ac362009-06-03 16:04:54 -070089 // Add space for command header and loop token;
90 sizeInBytes += 8;
Jason Sams326e0dd2009-05-22 14:03:28 -070091
92 //dumpState("reserve");
93 if (getFreeSpace() < sizeInBytes) {
94 makeSpace(sizeInBytes);
95 }
96
97 return mPut + 4;
98}
99
100void LocklessCommandFifo::commit(uint32_t command, uint32_t sizeInBytes)
101{
Jason Sams326e0dd2009-05-22 14:03:28 -0700102 //dumpState("commit 1");
103 reinterpret_cast<uint16_t *>(mPut)[0] = command;
104 reinterpret_cast<uint16_t *>(mPut)[1] = sizeInBytes;
105 mPut += ((sizeInBytes + 3) & ~3) + 4;
106 //dumpState("commit 2");
Jason Sams732f1c02009-06-18 16:58:42 -0700107 mSignalToWorker.set();
Jason Sams326e0dd2009-05-22 14:03:28 -0700108}
109
110void LocklessCommandFifo::commitSync(uint32_t command, uint32_t sizeInBytes)
111{
112 commit(command, sizeInBytes);
113 flush();
114}
115
116void LocklessCommandFifo::flush()
117{
118 //dumpState("flush 1");
119 while(mPut != mGet) {
Jason Sams732f1c02009-06-18 16:58:42 -0700120 mSignalToControl.wait();
Jason Sams326e0dd2009-05-22 14:03:28 -0700121 }
122 //dumpState("flush 2");
123}
124
125const void * LocklessCommandFifo::get(uint32_t *command, uint32_t *bytesData)
126{
127 while(1) {
Jason Sams732f1c02009-06-18 16:58:42 -0700128 //dumpState("get");
Jason Sams8c0ee652009-08-25 14:49:07 -0700129 while(isEmpty() && !mInShutdown) {
Jason Sams732f1c02009-06-18 16:58:42 -0700130 mSignalToControl.set();
131 mSignalToWorker.wait();
Jason Sams326e0dd2009-05-22 14:03:28 -0700132 }
Jason Sams326e0dd2009-05-22 14:03:28 -0700133
134 *command = reinterpret_cast<const uint16_t *>(mGet)[0];
135 *bytesData = reinterpret_cast<const uint16_t *>(mGet)[1];
Jason Sams326e0dd2009-05-22 14:03:28 -0700136 if (*command) {
137 // non-zero command is valid
138 return mGet+4;
139 }
Jason Sams8c0ee652009-08-25 14:49:07 -0700140
Jason Sams326e0dd2009-05-22 14:03:28 -0700141 // zero command means reset to beginning.
142 mGet = mBuffer;
143 }
144}
145
146void LocklessCommandFifo::next()
147{
148 uint32_t bytes = reinterpret_cast<const uint16_t *>(mGet)[1];
149 mGet += ((bytes + 3) & ~3) + 4;
Jason Sams732f1c02009-06-18 16:58:42 -0700150 if (isEmpty()) {
151 mSignalToControl.set();
152 }
Jason Sams326e0dd2009-05-22 14:03:28 -0700153 //dumpState("next");
154}
155
156void LocklessCommandFifo::makeSpace(uint32_t bytes)
157{
Jason Sams565ac362009-06-03 16:04:54 -0700158 //dumpState("make space");
Jason Sams326e0dd2009-05-22 14:03:28 -0700159 if ((mPut+bytes) > mEnd) {
160 // Need to loop regardless of where get is.
Jason Sams565ac362009-06-03 16:04:54 -0700161 while((mGet > mPut) && (mBuffer+4 >= mGet)) {
Jason Samsada7f272009-09-24 14:55:38 -0700162 usleep(100);
Jason Sams326e0dd2009-05-22 14:03:28 -0700163 }
164
165 // Toss in a reset then the normal wait for space will do the rest.
166 reinterpret_cast<uint16_t *>(mPut)[0] = 0;
167 reinterpret_cast<uint16_t *>(mPut)[1] = 0;
Jason Sams565ac362009-06-03 16:04:54 -0700168 mPut = mBuffer;
Jason Sams326e0dd2009-05-22 14:03:28 -0700169 }
170
171 // it will fit here so we just need to wait for space.
172 while(getFreeSpace() < bytes) {
Jason Samsada7f272009-09-24 14:55:38 -0700173 usleep(100);
Jason Sams326e0dd2009-05-22 14:03:28 -0700174 }
Jason Sams8c0ee652009-08-25 14:49:07 -0700175
Jason Sams326e0dd2009-05-22 14:03:28 -0700176}
177
178void LocklessCommandFifo::dumpState(const char *s) const
179{
Jason Sams992a0b72009-06-23 12:22:47 -0700180 LOGV("%s put %p, get %p, buf %p, end %p", s, mPut, mGet, mBuffer, mEnd);
Jason Sams326e0dd2009-05-22 14:03:28 -0700181}
182
Jason Sams732f1c02009-06-18 16:58:42 -0700183LocklessCommandFifo::Signal::Signal()
184{
185 mSet = true;
186}
187
188LocklessCommandFifo::Signal::~Signal()
189{
190 pthread_mutex_destroy(&mMutex);
191 pthread_cond_destroy(&mCondition);
192}
193
194bool LocklessCommandFifo::Signal::init()
195{
196 int status = pthread_mutex_init(&mMutex, NULL);
197 if (status) {
198 LOGE("LocklessFifo mutex init failure");
199 return false;
200 }
201
202 status = pthread_cond_init(&mCondition, NULL);
203 if (status) {
204 LOGE("LocklessFifo condition init failure");
205 pthread_mutex_destroy(&mMutex);
206 return false;
207 }
208
209 return true;
210}
211
212void LocklessCommandFifo::Signal::set()
213{
214 int status;
215
216 status = pthread_mutex_lock(&mMutex);
217 if (status) {
218 LOGE("LocklessCommandFifo: error %i locking for set condition.", status);
219 return;
220 }
221
222 mSet = true;
223
224 status = pthread_cond_signal(&mCondition);
225 if (status) {
226 LOGE("LocklessCommandFifo: error %i on set condition.", status);
227 }
228
229 status = pthread_mutex_unlock(&mMutex);
230 if (status) {
231 LOGE("LocklessCommandFifo: error %i unlocking for set condition.", status);
232 }
233}
234
235void LocklessCommandFifo::Signal::wait()
236{
237 int status;
238
239 status = pthread_mutex_lock(&mMutex);
240 if (status) {
241 LOGE("LocklessCommandFifo: error %i locking for condition.", status);
242 return;
243 }
244
245 if (!mSet) {
246 status = pthread_cond_wait(&mCondition, &mMutex);
247 if (status) {
248 LOGE("LocklessCommandFifo: error %i waiting on condition.", status);
249 }
250 }
251 mSet = false;
252
253 status = pthread_mutex_unlock(&mMutex);
254 if (status) {
255 LOGE("LocklessCommandFifo: error %i unlocking for condition.", status);
256 }
257}
Jason Sams326e0dd2009-05-22 14:03:28 -0700258