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 
     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