9ec430adae
A window is created and the browser is about to render into it the very first time, at that point it does an IPC to SF to request a new buffer. Meanwhile, the window manager removes that window from the list and the shared memory block it uses is marked as invalid. However, at that point, another window is created and is given the same index (that just go freed), but a different identity and resets the "invalid" bit in the shared block. When we go back to the buffer allocation code, we're stuck because the surface we're allocating for is gone and we don't detect it's invalid because the invalid bit has been reset. It is not sufficient to check for the invalid bit, I should also check that identities match.
419 lines
12 KiB
C++
419 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;
|
|
}
|
|
|
|
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
|