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 22 LocklessCommandFifo::LocklessCommandFifo() 23 { 24 } 25 26 LocklessCommandFifo::~LocklessCommandFifo() 27 { 28 if (!mInShutdown) { 29 shutdown(); 30 } 31 free(mBuffer); 32 } 33 34 void LocklessCommandFifo::shutdown() 35 { 36 mInShutdown = true; 37 mSignalToWorker.set(); 38 } 39 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 64 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 81 bool LocklessCommandFifo::isEmpty() const 82 { 83 return mPut == mGet; 84 } 85 86 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 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 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 122 void LocklessCommandFifo::flush() 123 { 124 //dumpState("flush 1"); 125 while(mPut != mGet) { 126 mSignalToControl.wait(); 127 } 128 //dumpState("flush 2"); 129 } 130 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 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 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 190 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 195 LocklessCommandFifo::Signal::Signal() 196 { 197 mSet = true; 198 } 199 200 LocklessCommandFifo::Signal::~Signal() 201 { 202 pthread_mutex_destroy(&mMutex); 203 pthread_cond_destroy(&mCondition); 204 } 205 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 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 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