Home | History | Annotate | Download | only in rs
      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