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 #include "utils/Timers.h" 19 #include "utils/StopWatch.h" 20 21 using namespace android; 22 using namespace android::renderscript; 23 24 LocklessCommandFifo::LocklessCommandFifo() : mBuffer(0), mInitialized(false) { 25 mTimeoutCallback = NULL; 26 mTimeoutCallbackData = NULL; 27 mTimeoutWait = 0; 28 } 29 30 LocklessCommandFifo::~LocklessCommandFifo() { 31 if (!mInShutdown && mInitialized) { 32 shutdown(); 33 } 34 if (mBuffer) { 35 free(mBuffer); 36 } 37 } 38 39 void LocklessCommandFifo::shutdown() { 40 mInShutdown = true; 41 mSignalToWorker.set(); 42 } 43 44 bool LocklessCommandFifo::init(uint32_t sizeInBytes) { 45 // Add room for a buffer reset command 46 mBuffer = static_cast<uint8_t *>(malloc(sizeInBytes + 4)); 47 if (!mBuffer) { 48 LOGE("LocklessFifo allocation failure"); 49 return false; 50 } 51 52 if (!mSignalToControl.init() || !mSignalToWorker.init()) { 53 LOGE("Signal setup failed"); 54 free(mBuffer); 55 return false; 56 } 57 58 mInShutdown = false; 59 mSize = sizeInBytes; 60 mPut = mBuffer; 61 mGet = mBuffer; 62 mEnd = mBuffer + (sizeInBytes) - 1; 63 //dumpState("init"); 64 mInitialized = true; 65 return true; 66 } 67 68 uint32_t LocklessCommandFifo::getFreeSpace() const { 69 int32_t freeSpace = 0; 70 //dumpState("getFreeSpace"); 71 72 if (mPut >= mGet) { 73 freeSpace = mEnd - mPut; 74 } else { 75 freeSpace = mGet - mPut; 76 } 77 78 if (freeSpace < 0) { 79 freeSpace = 0; 80 } 81 return freeSpace; 82 } 83 84 bool LocklessCommandFifo::isEmpty() const { 85 uint32_t p = android_atomic_acquire_load((int32_t *)&mPut); 86 return ((uint8_t *)p) == mGet; 87 } 88 89 90 void * LocklessCommandFifo::reserve(uint32_t sizeInBytes) { 91 // Add space for command header and loop token; 92 sizeInBytes += 8; 93 94 //dumpState("reserve"); 95 if (getFreeSpace() < sizeInBytes) { 96 makeSpace(sizeInBytes); 97 } 98 99 return mPut + 4; 100 } 101 102 void LocklessCommandFifo::commit(uint32_t command, uint32_t sizeInBytes) { 103 if (mInShutdown) { 104 return; 105 } 106 //dumpState("commit 1"); 107 reinterpret_cast<uint16_t *>(mPut)[0] = command; 108 reinterpret_cast<uint16_t *>(mPut)[1] = sizeInBytes; 109 110 int32_t s = ((sizeInBytes + 3) & ~3) + 4; 111 android_atomic_add(s, (int32_t *)&mPut); 112 //dumpState("commit 2"); 113 mSignalToWorker.set(); 114 } 115 116 void LocklessCommandFifo::commitSync(uint32_t command, uint32_t sizeInBytes) { 117 if (mInShutdown) { 118 return; 119 } 120 121 //char buf[1024]; 122 //sprintf(buf, "RenderScript LocklessCommandFifo::commitSync %p %i %i", this, command, sizeInBytes); 123 //StopWatch compileTimer(buf); 124 commit(command, sizeInBytes); 125 flush(); 126 } 127 128 void LocklessCommandFifo::flush() { 129 //dumpState("flush 1"); 130 while (mPut != mGet) { 131 while (!mSignalToControl.wait(mTimeoutWait)) { 132 if (mTimeoutCallback) { 133 mTimeoutCallback(mTimeoutCallbackData); 134 } 135 } 136 } 137 //dumpState("flush 2"); 138 } 139 140 void LocklessCommandFifo::setTimoutCallback(void (*cbk)(void *), void *data, uint64_t timeout) { 141 mTimeoutCallback = cbk; 142 mTimeoutCallbackData = data; 143 mTimeoutWait = timeout; 144 } 145 146 bool LocklessCommandFifo::wait(uint64_t timeout) { 147 while (isEmpty() && !mInShutdown) { 148 mSignalToControl.set(); 149 return mSignalToWorker.wait(timeout); 150 } 151 return true; 152 } 153 154 const void * LocklessCommandFifo::get(uint32_t *command, uint32_t *bytesData, uint64_t timeout) { 155 while (1) { 156 //dumpState("get"); 157 wait(timeout); 158 159 if (isEmpty() || mInShutdown) { 160 *command = 0; 161 *bytesData = 0; 162 return NULL; 163 } 164 165 *command = reinterpret_cast<const uint16_t *>(mGet)[0]; 166 *bytesData = reinterpret_cast<const uint16_t *>(mGet)[1]; 167 if (*command) { 168 // non-zero command is valid 169 return mGet+4; 170 } 171 172 // zero command means reset to beginning. 173 mGet = mBuffer; 174 } 175 } 176 177 void LocklessCommandFifo::next() { 178 uint32_t bytes = reinterpret_cast<const uint16_t *>(mGet)[1]; 179 180 android_atomic_add(((bytes + 3) & ~3) + 4, (int32_t *)&mGet); 181 //mGet += ((bytes + 3) & ~3) + 4; 182 if (isEmpty()) { 183 mSignalToControl.set(); 184 } 185 //dumpState("next"); 186 } 187 188 bool LocklessCommandFifo::makeSpaceNonBlocking(uint32_t bytes) { 189 //dumpState("make space non-blocking"); 190 if ((mPut+bytes) > mEnd) { 191 // Need to loop regardless of where get is. 192 if ((mGet > mPut) || (mBuffer+4 >= mGet)) { 193 return false; 194 } 195 196 // Toss in a reset then the normal wait for space will do the rest. 197 reinterpret_cast<uint16_t *>(mPut)[0] = 0; 198 reinterpret_cast<uint16_t *>(mPut)[1] = 0; 199 mPut = mBuffer; 200 mSignalToWorker.set(); 201 } 202 203 // it will fit here so we just need to wait for space. 204 if (getFreeSpace() < bytes) { 205 return false; 206 } 207 208 return true; 209 } 210 211 void LocklessCommandFifo::makeSpace(uint32_t bytes) { 212 //dumpState("make space"); 213 if ((mPut+bytes) > mEnd) { 214 // Need to loop regardless of where get is. 215 while ((mGet > mPut) || (mBuffer+4 >= mGet)) { 216 usleep(100); 217 } 218 219 // Toss in a reset then the normal wait for space will do the rest. 220 reinterpret_cast<uint16_t *>(mPut)[0] = 0; 221 reinterpret_cast<uint16_t *>(mPut)[1] = 0; 222 mPut = mBuffer; 223 mSignalToWorker.set(); 224 } 225 226 // it will fit here so we just need to wait for space. 227 while (getFreeSpace() < bytes) { 228 usleep(100); 229 } 230 231 } 232 233 void LocklessCommandFifo::dumpState(const char *s) const { 234 LOGV("%s %p put %p, get %p, buf %p, end %p", s, this, mPut, mGet, mBuffer, mEnd); 235 } 236 237 void LocklessCommandFifo::printDebugData() const { 238 dumpState("printing fifo debug"); 239 const uint32_t *pptr = (const uint32_t *)mGet; 240 pptr -= 8 * 4; 241 if (mGet < mBuffer) { 242 pptr = (const uint32_t *)mBuffer; 243 } 244 245 246 for (int ct=0; ct < 16; ct++) { 247 LOGV("fifo %p = 0x%08x 0x%08x 0x%08x 0x%08x", pptr, pptr[0], pptr[1], pptr[2], pptr[3]); 248 pptr += 4; 249 } 250 251 } 252