blob: ce69a603f353d68c54c0bfd6c54f35ff99fae7b2 [file] [log] [blame]
/*
* Copyright (C) 2009 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "rsLocklessFifo.h"
#include "utils/Timers.h"
#include "utils/StopWatch.h"
using namespace android;
using namespace android::renderscript;
LocklessCommandFifo::LocklessCommandFifo() : mBuffer(0), mInitialized(false) {
mTimeoutCallback = NULL;
mTimeoutCallbackData = NULL;
mTimeoutWait = 0;
}
LocklessCommandFifo::~LocklessCommandFifo() {
if (!mInShutdown && mInitialized) {
shutdown();
}
if (mBuffer) {
free(mBuffer);
}
}
void LocklessCommandFifo::shutdown() {
mInShutdown = true;
mSignalToWorker.set();
}
bool LocklessCommandFifo::init(uint32_t sizeInBytes) {
// Add room for a buffer reset command
mBuffer = static_cast<uint8_t *>(malloc(sizeInBytes + 4));
if (!mBuffer) {
LOGE("LocklessFifo allocation failure");
return false;
}
if (!mSignalToControl.init() || !mSignalToWorker.init()) {
LOGE("Signal setup failed");
free(mBuffer);
return false;
}
mInShutdown = false;
mSize = sizeInBytes;
mPut = mBuffer;
mGet = mBuffer;
mEnd = mBuffer + (sizeInBytes) - 1;
//dumpState("init");
mInitialized = true;
return true;
}
uint32_t LocklessCommandFifo::getFreeSpace() const {
int32_t freeSpace = 0;
//dumpState("getFreeSpace");
if (mPut >= mGet) {
freeSpace = mEnd - mPut;
} else {
freeSpace = mGet - mPut;
}
if (freeSpace < 0) {
freeSpace = 0;
}
return freeSpace;
}
bool LocklessCommandFifo::isEmpty() const {
uint32_t p = android_atomic_acquire_load((int32_t *)&mPut);
return ((uint8_t *)p) == mGet;
}
void * LocklessCommandFifo::reserve(uint32_t sizeInBytes) {
// Add space for command header and loop token;
sizeInBytes += 8;
//dumpState("reserve");
if (getFreeSpace() < sizeInBytes) {
makeSpace(sizeInBytes);
}
return mPut + 4;
}
void LocklessCommandFifo::commit(uint32_t command, uint32_t sizeInBytes) {
if (mInShutdown) {
return;
}
//dumpState("commit 1");
reinterpret_cast<uint16_t *>(mPut)[0] = command;
reinterpret_cast<uint16_t *>(mPut)[1] = sizeInBytes;
int32_t s = ((sizeInBytes + 3) & ~3) + 4;
android_atomic_add(s, (int32_t *)&mPut);
//dumpState("commit 2");
mSignalToWorker.set();
}
void LocklessCommandFifo::commitSync(uint32_t command, uint32_t sizeInBytes) {
if (mInShutdown) {
return;
}
//char buf[1024];
//sprintf(buf, "RenderScript LocklessCommandFifo::commitSync %p %i %i", this, command, sizeInBytes);
//StopWatch compileTimer(buf);
commit(command, sizeInBytes);
flush();
}
void LocklessCommandFifo::flush() {
//dumpState("flush 1");
while (mPut != mGet) {
while (!mSignalToControl.wait(mTimeoutWait)) {
if (mTimeoutCallback) {
mTimeoutCallback(mTimeoutCallbackData);
}
}
}
//dumpState("flush 2");
}
void LocklessCommandFifo::setTimoutCallback(void (*cbk)(void *), void *data, uint64_t timeout) {
mTimeoutCallback = cbk;
mTimeoutCallbackData = data;
mTimeoutWait = timeout;
}
bool LocklessCommandFifo::wait(uint64_t timeout) {
while (isEmpty() && !mInShutdown) {
mSignalToControl.set();
return mSignalToWorker.wait(timeout);
}
return true;
}
const void * LocklessCommandFifo::get(uint32_t *command, uint32_t *bytesData, uint64_t timeout) {
while (1) {
//dumpState("get");
wait(timeout);
if (isEmpty() || mInShutdown) {
*command = 0;
*bytesData = 0;
return NULL;
}
*command = reinterpret_cast<const uint16_t *>(mGet)[0];
*bytesData = reinterpret_cast<const uint16_t *>(mGet)[1];
if (*command) {
// non-zero command is valid
return mGet+4;
}
// zero command means reset to beginning.
mGet = mBuffer;
}
}
void LocklessCommandFifo::next() {
uint32_t bytes = reinterpret_cast<const uint16_t *>(mGet)[1];
android_atomic_add(((bytes + 3) & ~3) + 4, (int32_t *)&mGet);
//mGet += ((bytes + 3) & ~3) + 4;
if (isEmpty()) {
mSignalToControl.set();
}
//dumpState("next");
}
bool LocklessCommandFifo::makeSpaceNonBlocking(uint32_t bytes) {
//dumpState("make space non-blocking");
if ((mPut+bytes) > mEnd) {
// Need to loop regardless of where get is.
if ((mGet > mPut) || (mBuffer+4 >= mGet)) {
return false;
}
// Toss in a reset then the normal wait for space will do the rest.
reinterpret_cast<uint16_t *>(mPut)[0] = 0;
reinterpret_cast<uint16_t *>(mPut)[1] = 0;
mPut = mBuffer;
mSignalToWorker.set();
}
// it will fit here so we just need to wait for space.
if (getFreeSpace() < bytes) {
return false;
}
return true;
}
void LocklessCommandFifo::makeSpace(uint32_t bytes) {
//dumpState("make space");
if ((mPut+bytes) > mEnd) {
// Need to loop regardless of where get is.
while ((mGet > mPut) || (mBuffer+4 >= mGet)) {
usleep(100);
}
// Toss in a reset then the normal wait for space will do the rest.
reinterpret_cast<uint16_t *>(mPut)[0] = 0;
reinterpret_cast<uint16_t *>(mPut)[1] = 0;
mPut = mBuffer;
mSignalToWorker.set();
}
// it will fit here so we just need to wait for space.
while (getFreeSpace() < bytes) {
usleep(100);
}
}
void LocklessCommandFifo::dumpState(const char *s) const {
ALOGV("%s %p put %p, get %p, buf %p, end %p", s, this, mPut, mGet, mBuffer, mEnd);
}
void LocklessCommandFifo::printDebugData() const {
dumpState("printing fifo debug");
const uint32_t *pptr = (const uint32_t *)mGet;
pptr -= 8 * 4;
if (mGet < mBuffer) {
pptr = (const uint32_t *)mBuffer;
}
for (int ct=0; ct < 16; ct++) {
ALOGV("fifo %p = 0x%08x 0x%08x 0x%08x 0x%08x", pptr, pptr[0], pptr[1], pptr[2], pptr[3]);
pptr += 4;
}
}