replicant-frameworks_native/libs/ui/SharedBufferStack.cpp
Mathias Agopian e700501d0e fix [2170319] gmail bulk operation checkbox latency on passion
This also fixes [2152536] ANR in browser

When SF is enqueuing buffers faster than SF dequeues them.
The update flag in SF is not counted and under some situations SF will only
dequeue the first buffer. The state at this point is not technically
corrupted, it's valid, but just delayed by one buffer.

In the case of the Browser ANR, because the last enqueued buffer was delayed
the resizing of the current buffer couldn't happen.

The system would always fall back onto its feet if anything -else- in
tried to draw, because the "late" buffer would be picked up then.
2009-10-07 16:44:10 -07:00

425 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/ui/SharedBufferStack.h>
#include <ui/Rect.h>
#include <ui/Region.h>
#define DEBUG_ATOMICS 0
namespace android {
// ----------------------------------------------------------------------------
SharedClient::SharedClient()
: lock(Mutex::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 );
snprintf(buffer, SIZE,
"%s[ head=%2d, available=%2d, queued=%2d ] "
"reallocMask=%08x, inUse=%2d, identity=%d, status=%d\n",
prefix, stack.head, stack.available, stack.queued,
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;
}
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