replicant-frameworks_native/libs/surfaceflinger_client/SharedBufferStack.cpp
Mathias Agopian 26d244281a libutils Condition are now PRIVATE by default
Condition must be initialized with SHARED for the old behavior, where
they can be used accross processes.

Updated the two places android that require SHARED conditions.

PRIVATE conditions (and mutexes) use more efficient syscalls.

Change-Id: I9a281a4b88206e92ac559c66554e886b9c62db3a
2010-03-19 16:16:43 -07:00

428 lines
12 KiB
C++

/*
* Copyright (C) 2007 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.
*/
#define LOG_TAG "SharedBufferStack"
#include <stdint.h>
#include <sys/types.h>
#include <utils/Debug.h>
#include <utils/Log.h>
#include <utils/threads.h>
#include <private/surfaceflinger/SharedBufferStack.h>
#include <ui/Rect.h>
#include <ui/Region.h>
#define DEBUG_ATOMICS 0
namespace android {
// ----------------------------------------------------------------------------
SharedClient::SharedClient()
: lock(Mutex::SHARED), cv(Condition::SHARED)
{
}
SharedClient::~SharedClient() {
}
// these functions are used by the clients
status_t SharedClient::validate(size_t i) const {
if (uint32_t(i) >= uint32_t(NUM_LAYERS_MAX))
return BAD_INDEX;
return surfaces[i].status;
}
uint32_t SharedClient::getIdentity(size_t token) const {
return uint32_t(surfaces[token].identity);
}
// ----------------------------------------------------------------------------
SharedBufferStack::SharedBufferStack()
{
}
void SharedBufferStack::init(int32_t i)
{
inUse = -1;
status = NO_ERROR;
identity = i;
}
status_t SharedBufferStack::setDirtyRegion(int buffer, const Region& dirty)
{
if (uint32_t(buffer) >= NUM_BUFFER_MAX)
return BAD_INDEX;
// in the current implementation we only send a single rectangle
const Rect bounds(dirty.getBounds());
FlatRegion& reg(dirtyRegion[buffer]);
reg.count = 1;
reg.rects[0] = uint16_t(bounds.left);
reg.rects[1] = uint16_t(bounds.top);
reg.rects[2] = uint16_t(bounds.right);
reg.rects[3] = uint16_t(bounds.bottom);
return NO_ERROR;
}
Region SharedBufferStack::getDirtyRegion(int buffer) const
{
Region res;
if (uint32_t(buffer) >= NUM_BUFFER_MAX)
return res;
const FlatRegion& reg(dirtyRegion[buffer]);
res.set(Rect(reg.rects[0], reg.rects[1], reg.rects[2], reg.rects[3]));
return res;
}
// ----------------------------------------------------------------------------
SharedBufferBase::SharedBufferBase(SharedClient* sharedClient,
int surface, int num, int32_t identity)
: mSharedClient(sharedClient),
mSharedStack(sharedClient->surfaces + surface),
mNumBuffers(num), mIdentity(identity)
{
}
SharedBufferBase::~SharedBufferBase()
{
}
uint32_t SharedBufferBase::getIdentity()
{
SharedBufferStack& stack( *mSharedStack );
return stack.identity;
}
status_t SharedBufferBase::getStatus() const
{
SharedBufferStack& stack( *mSharedStack );
return stack.status;
}
size_t SharedBufferBase::getFrontBuffer() const
{
SharedBufferStack& stack( *mSharedStack );
return size_t( stack.head );
}
String8 SharedBufferBase::dump(char const* prefix) const
{
const size_t SIZE = 1024;
char buffer[SIZE];
String8 result;
SharedBufferStack& stack( *mSharedStack );
int tail = (mNumBuffers + stack.head - stack.available + 1) % mNumBuffers;
snprintf(buffer, SIZE,
"%s[ head=%2d, available=%2d, queued=%2d, tail=%2d ] "
"reallocMask=%08x, inUse=%2d, identity=%d, status=%d\n",
prefix, stack.head, stack.available, stack.queued, tail,
stack.reallocMask, stack.inUse, stack.identity, stack.status);
result.append(buffer);
return result;
}
// ============================================================================
// conditions and updates
// ============================================================================
SharedBufferClient::DequeueCondition::DequeueCondition(
SharedBufferClient* sbc) : ConditionBase(sbc) {
}
bool SharedBufferClient::DequeueCondition::operator()() {
return stack.available > 0;
}
SharedBufferClient::LockCondition::LockCondition(
SharedBufferClient* sbc, int buf) : ConditionBase(sbc), buf(buf) {
}
bool SharedBufferClient::LockCondition::operator()() {
return (buf != stack.head ||
(stack.queued > 0 && stack.inUse != buf));
}
SharedBufferServer::ReallocateCondition::ReallocateCondition(
SharedBufferBase* sbb, int buf) : ConditionBase(sbb), buf(buf) {
}
bool SharedBufferServer::ReallocateCondition::operator()() {
// TODO: we should also check that buf has been dequeued
return (buf != stack.head);
}
// ----------------------------------------------------------------------------
SharedBufferClient::QueueUpdate::QueueUpdate(SharedBufferBase* sbb)
: UpdateBase(sbb) {
}
ssize_t SharedBufferClient::QueueUpdate::operator()() {
android_atomic_inc(&stack.queued);
return NO_ERROR;
}
SharedBufferClient::UndoDequeueUpdate::UndoDequeueUpdate(SharedBufferBase* sbb)
: UpdateBase(sbb) {
}
ssize_t SharedBufferClient::UndoDequeueUpdate::operator()() {
android_atomic_inc(&stack.available);
return NO_ERROR;
}
SharedBufferServer::UnlockUpdate::UnlockUpdate(
SharedBufferBase* sbb, int lockedBuffer)
: UpdateBase(sbb), lockedBuffer(lockedBuffer) {
}
ssize_t SharedBufferServer::UnlockUpdate::operator()() {
if (stack.inUse != lockedBuffer) {
LOGE("unlocking %d, but currently locked buffer is %d",
lockedBuffer, stack.inUse);
return BAD_VALUE;
}
android_atomic_write(-1, &stack.inUse);
return NO_ERROR;
}
SharedBufferServer::RetireUpdate::RetireUpdate(
SharedBufferBase* sbb, int numBuffers)
: UpdateBase(sbb), numBuffers(numBuffers) {
}
ssize_t SharedBufferServer::RetireUpdate::operator()() {
// head is only written in this function, which is single-thread.
int32_t head = stack.head;
// Preventively lock the current buffer before updating queued.
android_atomic_write(head, &stack.inUse);
// Decrement the number of queued buffers
int32_t queued;
do {
queued = stack.queued;
if (queued == 0) {
return NOT_ENOUGH_DATA;
}
} while (android_atomic_cmpxchg(queued, queued-1, &stack.queued));
// update the head pointer
head = ((head+1 >= numBuffers) ? 0 : head+1);
// lock the buffer before advancing head, which automatically unlocks
// the buffer we preventively locked upon entering this function
android_atomic_write(head, &stack.inUse);
// advance head
android_atomic_write(head, &stack.head);
// now that head has moved, we can increment the number of available buffers
android_atomic_inc(&stack.available);
return head;
}
SharedBufferServer::StatusUpdate::StatusUpdate(
SharedBufferBase* sbb, status_t status)
: UpdateBase(sbb), status(status) {
}
ssize_t SharedBufferServer::StatusUpdate::operator()() {
android_atomic_write(status, &stack.status);
return NO_ERROR;
}
// ============================================================================
SharedBufferClient::SharedBufferClient(SharedClient* sharedClient,
int surface, int num, int32_t identity)
: SharedBufferBase(sharedClient, surface, num, identity), tail(0)
{
tail = computeTail();
}
int32_t SharedBufferClient::computeTail() const
{
SharedBufferStack& stack( *mSharedStack );
// we need to make sure we read available and head coherently,
// w.r.t RetireUpdate.
int32_t newTail;
int32_t avail;
int32_t head;
do {
avail = stack.available;
head = stack.head;
} while (stack.available != avail);
newTail = head - avail + 1;
if (newTail < 0) {
newTail += mNumBuffers;
} else if (newTail >= mNumBuffers) {
newTail -= mNumBuffers;
}
return newTail;
}
ssize_t SharedBufferClient::dequeue()
{
SharedBufferStack& stack( *mSharedStack );
if (stack.head == tail && stack.available == 2) {
LOGW("dequeue: tail=%d, head=%d, avail=%d, queued=%d",
tail, stack.head, stack.available, stack.queued);
}
const nsecs_t dequeueTime = systemTime(SYSTEM_TIME_THREAD);
//LOGD("[%d] about to dequeue a buffer",
// mSharedStack->identity);
DequeueCondition condition(this);
status_t err = waitForCondition(condition);
if (err != NO_ERROR)
return ssize_t(err);
// NOTE: 'stack.available' is part of the conditions, however
// decrementing it, never changes any conditions, so we don't need
// to do this as part of an update.
if (android_atomic_dec(&stack.available) == 0) {
LOGW("dequeue probably called from multiple threads!");
}
int dequeued = tail;
tail = ((tail+1 >= mNumBuffers) ? 0 : tail+1);
LOGD_IF(DEBUG_ATOMICS, "dequeued=%d, tail=%d, %s",
dequeued, tail, dump("").string());
mDequeueTime[dequeued] = dequeueTime;
return dequeued;
}
status_t SharedBufferClient::undoDequeue(int buf)
{
UndoDequeueUpdate update(this);
status_t err = updateCondition( update );
if (err == NO_ERROR) {
tail = computeTail();
}
return err;
}
status_t SharedBufferClient::lock(int buf)
{
LockCondition condition(this, buf);
status_t err = waitForCondition(condition);
return err;
}
status_t SharedBufferClient::queue(int buf)
{
QueueUpdate update(this);
status_t err = updateCondition( update );
LOGD_IF(DEBUG_ATOMICS, "queued=%d, %s", buf, dump("").string());
SharedBufferStack& stack( *mSharedStack );
const nsecs_t now = systemTime(SYSTEM_TIME_THREAD);
stack.stats.totalTime = ns2us(now - mDequeueTime[buf]);
return err;
}
bool SharedBufferClient::needNewBuffer(int buffer) const
{
SharedBufferStack& stack( *mSharedStack );
const uint32_t mask = 1<<buffer;
return (android_atomic_and(~mask, &stack.reallocMask) & mask) != 0;
}
status_t SharedBufferClient::setDirtyRegion(int buffer, const Region& reg)
{
SharedBufferStack& stack( *mSharedStack );
return stack.setDirtyRegion(buffer, reg);
}
// ----------------------------------------------------------------------------
SharedBufferServer::SharedBufferServer(SharedClient* sharedClient,
int surface, int num, int32_t identity)
: SharedBufferBase(sharedClient, surface, num, identity)
{
mSharedStack->init(identity);
mSharedStack->head = num-1;
mSharedStack->available = num;
mSharedStack->queued = 0;
mSharedStack->reallocMask = 0;
memset(mSharedStack->dirtyRegion, 0, sizeof(mSharedStack->dirtyRegion));
}
ssize_t SharedBufferServer::retireAndLock()
{
RetireUpdate update(this, mNumBuffers);
ssize_t buf = updateCondition( update );
LOGD_IF(DEBUG_ATOMICS && buf>=0, "retire=%d, %s", int(buf), dump("").string());
return buf;
}
status_t SharedBufferServer::unlock(int buffer)
{
UnlockUpdate update(this, buffer);
status_t err = updateCondition( update );
return err;
}
void SharedBufferServer::setStatus(status_t status)
{
if (status < NO_ERROR) {
StatusUpdate update(this, status);
updateCondition( update );
}
}
status_t SharedBufferServer::reallocate()
{
SharedBufferStack& stack( *mSharedStack );
uint32_t mask = (1<<mNumBuffers)-1;
android_atomic_or(mask, &stack.reallocMask);
return NO_ERROR;
}
int32_t SharedBufferServer::getQueuedCount() const
{
SharedBufferStack& stack( *mSharedStack );
return stack.queued;
}
status_t SharedBufferServer::assertReallocate(int buffer)
{
ReallocateCondition condition(this, buffer);
status_t err = waitForCondition(condition);
return err;
}
Region SharedBufferServer::getDirtyRegion(int buffer) const
{
SharedBufferStack& stack( *mSharedStack );
return stack.getDirtyRegion(buffer);
}
SharedBufferStack::Statistics SharedBufferServer::getStats() const
{
SharedBufferStack& stack( *mSharedStack );
return stack.stats;
}
// ---------------------------------------------------------------------------
}; // namespace android