• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 
19 using namespace android;
20 
21 
LocklessCommandFifo()22 LocklessCommandFifo::LocklessCommandFifo()
23 {
24 }
25 
~LocklessCommandFifo()26 LocklessCommandFifo::~LocklessCommandFifo()
27 {
28     if (!mInShutdown) {
29         shutdown();
30     }
31     free(mBuffer);
32 }
33 
shutdown()34 void LocklessCommandFifo::shutdown()
35 {
36     mInShutdown = true;
37     mSignalToWorker.set();
38 }
39 
init(uint32_t sizeInBytes)40 bool 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 
49     if (!mSignalToControl.init() || !mSignalToWorker.init()) {
50         LOGE("Signal setup failed");
51         free(mBuffer);
52         return false;
53     }
54 
55     mInShutdown = false;
56     mSize = sizeInBytes;
57     mPut = mBuffer;
58     mGet = mBuffer;
59     mEnd = mBuffer + (sizeInBytes) - 1;
60     //dumpState("init");
61     return true;
62 }
63 
getFreeSpace() const64 uint32_t LocklessCommandFifo::getFreeSpace() const
65 {
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     }
78     return freeSpace;
79 }
80 
isEmpty() const81 bool LocklessCommandFifo::isEmpty() const
82 {
83     return mPut == mGet;
84 }
85 
86 
reserve(uint32_t sizeInBytes)87 void * LocklessCommandFifo::reserve(uint32_t sizeInBytes)
88 {
89     // Add space for command header and loop token;
90     sizeInBytes += 8;
91 
92     //dumpState("reserve");
93     if (getFreeSpace() < sizeInBytes) {
94         makeSpace(sizeInBytes);
95     }
96 
97     return mPut + 4;
98 }
99 
commit(uint32_t command,uint32_t sizeInBytes)100 void LocklessCommandFifo::commit(uint32_t command, uint32_t sizeInBytes)
101 {
102     if (mInShutdown) {
103         return;
104     }
105     //dumpState("commit 1");
106     reinterpret_cast<uint16_t *>(mPut)[0] = command;
107     reinterpret_cast<uint16_t *>(mPut)[1] = sizeInBytes;
108     mPut += ((sizeInBytes + 3) & ~3) + 4;
109     //dumpState("commit 2");
110     mSignalToWorker.set();
111 }
112 
commitSync(uint32_t command,uint32_t sizeInBytes)113 void LocklessCommandFifo::commitSync(uint32_t command, uint32_t sizeInBytes)
114 {
115     if (mInShutdown) {
116         return;
117     }
118     commit(command, sizeInBytes);
119     flush();
120 }
121 
flush()122 void LocklessCommandFifo::flush()
123 {
124     //dumpState("flush 1");
125     while(mPut != mGet) {
126         mSignalToControl.wait();
127     }
128     //dumpState("flush 2");
129 }
130 
get(uint32_t * command,uint32_t * bytesData)131 const void * LocklessCommandFifo::get(uint32_t *command, uint32_t *bytesData)
132 {
133     while(1) {
134         //dumpState("get");
135         while(isEmpty() && !mInShutdown) {
136             mSignalToControl.set();
137             mSignalToWorker.wait();
138         }
139 
140         if (mInShutdown) {
141             *command = 0;
142             *bytesData = 0;
143             return 0;
144         }
145 
146         *command = reinterpret_cast<const uint16_t *>(mGet)[0];
147         *bytesData = reinterpret_cast<const uint16_t *>(mGet)[1];
148         if (*command) {
149             // non-zero command is valid
150             return mGet+4;
151         }
152 
153         // zero command means reset to beginning.
154         mGet = mBuffer;
155     }
156 }
157 
next()158 void LocklessCommandFifo::next()
159 {
160     uint32_t bytes = reinterpret_cast<const uint16_t *>(mGet)[1];
161     mGet += ((bytes + 3) & ~3) + 4;
162     if (isEmpty()) {
163         mSignalToControl.set();
164     }
165     //dumpState("next");
166 }
167 
makeSpace(uint32_t bytes)168 void LocklessCommandFifo::makeSpace(uint32_t bytes)
169 {
170     //dumpState("make space");
171     if ((mPut+bytes) > mEnd) {
172         // Need to loop regardless of where get is.
173         while((mGet > mPut) && (mBuffer+4 >= mGet)) {
174             usleep(100);
175         }
176 
177         // Toss in a reset then the normal wait for space will do the rest.
178         reinterpret_cast<uint16_t *>(mPut)[0] = 0;
179         reinterpret_cast<uint16_t *>(mPut)[1] = 0;
180         mPut = mBuffer;
181     }
182 
183     // it will fit here so we just need to wait for space.
184     while(getFreeSpace() < bytes) {
185         usleep(100);
186     }
187 
188 }
189 
dumpState(const char * s) const190 void LocklessCommandFifo::dumpState(const char *s) const
191 {
192     LOGV("%s  put %p, get %p,  buf %p,  end %p", s, mPut, mGet, mBuffer, mEnd);
193 }
194 
Signal()195 LocklessCommandFifo::Signal::Signal()
196 {
197     mSet = true;
198 }
199 
~Signal()200 LocklessCommandFifo::Signal::~Signal()
201 {
202     pthread_mutex_destroy(&mMutex);
203     pthread_cond_destroy(&mCondition);
204 }
205 
init()206 bool LocklessCommandFifo::Signal::init()
207 {
208     int status = pthread_mutex_init(&mMutex, NULL);
209     if (status) {
210         LOGE("LocklessFifo mutex init failure");
211         return false;
212     }
213 
214     status = pthread_cond_init(&mCondition, NULL);
215     if (status) {
216         LOGE("LocklessFifo condition init failure");
217         pthread_mutex_destroy(&mMutex);
218         return false;
219     }
220 
221     return true;
222 }
223 
set()224 void LocklessCommandFifo::Signal::set()
225 {
226     int status;
227 
228     status = pthread_mutex_lock(&mMutex);
229     if (status) {
230         LOGE("LocklessCommandFifo: error %i locking for set condition.", status);
231         return;
232     }
233 
234     mSet = true;
235 
236     status = pthread_cond_signal(&mCondition);
237     if (status) {
238         LOGE("LocklessCommandFifo: error %i on set condition.", status);
239     }
240 
241     status = pthread_mutex_unlock(&mMutex);
242     if (status) {
243         LOGE("LocklessCommandFifo: error %i unlocking for set condition.", status);
244     }
245 }
246 
wait()247 void LocklessCommandFifo::Signal::wait()
248 {
249     int status;
250 
251     status = pthread_mutex_lock(&mMutex);
252     if (status) {
253         LOGE("LocklessCommandFifo: error %i locking for condition.", status);
254         return;
255     }
256 
257     if (!mSet) {
258         status = pthread_cond_wait(&mCondition, &mMutex);
259         if (status) {
260             LOGE("LocklessCommandFifo: error %i waiting on condition.", status);
261         }
262     }
263     mSet = false;
264 
265     status = pthread_mutex_unlock(&mMutex);
266     if (status) {
267         LOGE("LocklessCommandFifo: error %i unlocking for condition.", status);
268     }
269 }
270 
271