Skip to content

Commit

Permalink
[DO NOT MERGE] GraphicBufferAllocator: stall alloc for async frees
Browse files Browse the repository at this point in the history
This change makes GraphicBufferAllocator::alloc wait for pending async frees to
complete before attempting to allocate a gralloc buffer if there are more than
8 pending async frees.

Bug: 7696861
Change-Id: I1fae86e13edefcaa153b8ce9fd057f335716059e
  • Loading branch information
Jamie Gennis authored and pixelflinger committed Dec 11, 2012
1 parent 72c3f7d commit f53f9c6
Showing 1 changed file with 108 additions and 52 deletions.
160 changes: 108 additions & 52 deletions libs/ui/GraphicBufferAllocator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -90,59 +90,36 @@ void GraphicBufferAllocator::dumpToSystemLog()
ALOGD("%s", s.string());
}

status_t GraphicBufferAllocator::alloc(uint32_t w, uint32_t h, PixelFormat format,
int usage, buffer_handle_t* handle, int32_t* stride)
{
ATRACE_CALL();
// make sure to not allocate a N x 0 or 0 x N buffer, since this is
// allowed from an API stand-point allocate a 1x1 buffer instead.
if (!w || !h)
w = h = 1;
class BufferLiberatorThread : public Thread {
public:

// we have a h/w allocator and h/w buffer is requested
status_t err;

err = mAllocDev->alloc(mAllocDev, w, h, format, usage, handle, stride);
static void queueCaptiveBuffer(buffer_handle_t handle) {
size_t queueSize;
{
Mutex::Autolock lock(sMutex);
if (sThread == NULL) {
sThread = new BufferLiberatorThread;
sThread->run("BufferLiberator");
}

ALOGW_IF(err, "alloc(%u, %u, %d, %08x, ...) failed %d (%s)",
w, h, format, usage, err, strerror(-err));

if (err == NO_ERROR) {
Mutex::Autolock _l(sLock);
KeyedVector<buffer_handle_t, alloc_rec_t>& list(sAllocList);
int bpp = bytesPerPixel(format);
if (bpp < 0) {
// probably a HAL custom format. in any case, we don't know
// what its pixel size is.
bpp = 0;
sThread->mQueue.push_back(handle);
sThread->mQueuedCondition.signal();
queueSize = sThread->mQueue.size();
}
alloc_rec_t rec;
rec.w = w;
rec.h = h;
rec.s = *stride;
rec.format = format;
rec.usage = usage;
rec.size = h * stride[0] * bpp;
list.add(*handle, rec);
}

return err;
}
static void waitForLiberation() {
Mutex::Autolock lock(sMutex);

class BufferLiberatorThread : public Thread {
public:
waitForLiberationLocked();
}

static void queueCaptiveBuffer(buffer_handle_t handle) {
static sp<BufferLiberatorThread> thread(new BufferLiberatorThread);
static bool running = false;
if (!running) {
thread->run("BufferLiberator");
running = true;
}
{
Mutex::Autolock lock(thread->mMutex);
thread->mQueue.push_back(handle);
thread->mCondition.signal();
static void maybeWaitForLiberation() {
Mutex::Autolock lock(sMutex);
if (sThread != NULL) {
if (sThread->mQueue.size() > 8) {
waitForLiberationLocked();
}
}
}

Expand All @@ -152,13 +129,12 @@ class BufferLiberatorThread : public Thread {

virtual bool threadLoop() {
buffer_handle_t handle;
{
Mutex::Autolock lock(mMutex);
{ // Scope for mutex
Mutex::Autolock lock(sMutex);
while (mQueue.isEmpty()) {
mCondition.wait(mMutex);
mQueuedCondition.wait(sMutex);
}
handle = mQueue[0];
mQueue.removeAt(0);
}

status_t err;
Expand All @@ -176,14 +152,94 @@ class BufferLiberatorThread : public Thread {
list.removeItem(handle);
}

{ // Scope for mutex
Mutex::Autolock lock(sMutex);
mQueue.removeAt(0);
mFreedCondition.broadcast();
}

return true;
}

static void waitForLiberationLocked() {
if (sThread == NULL) {
return;
}

const nsecs_t timeout = 500 * 1000 * 1000;
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
nsecs_t timeToStop = now + timeout;
while (!sThread->mQueue.isEmpty() && now < timeToStop) {
sThread->mFreedCondition.waitRelative(sMutex, timeToStop - now);
now = systemTime(SYSTEM_TIME_MONOTONIC);
}

if (!sThread->mQueue.isEmpty()) {
ALOGW("waitForLiberationLocked timed out");
}
}

static Mutex sMutex;
static sp<BufferLiberatorThread> sThread;
Vector<buffer_handle_t> mQueue;
Condition mCondition;
Mutex mMutex;
Condition mQueuedCondition;
Condition mFreedCondition;
};

Mutex BufferLiberatorThread::sMutex;
sp<BufferLiberatorThread> BufferLiberatorThread::sThread;

status_t GraphicBufferAllocator::alloc(uint32_t w, uint32_t h, PixelFormat format,
int usage, buffer_handle_t* handle, int32_t* stride)
{
ATRACE_CALL();
// make sure to not allocate a N x 0 or 0 x N buffer, since this is
// allowed from an API stand-point allocate a 1x1 buffer instead.
if (!w || !h)
w = h = 1;

// we have a h/w allocator and h/w buffer is requested
status_t err;

// If too many async frees are queued up then wait for some of them to
// complete before attempting to allocate more memory. This is exercised
// by the android.opengl.cts.GLSurfaceViewTest CTS test.
BufferLiberatorThread::maybeWaitForLiberation();

err = mAllocDev->alloc(mAllocDev, w, h, format, usage, handle, stride);

if (err != NO_ERROR) {
ALOGW("WOW! gralloc alloc failed, waiting for pending frees!");
BufferLiberatorThread::waitForLiberation();
err = mAllocDev->alloc(mAllocDev, w, h, format, usage, handle, stride);
}

ALOGW_IF(err, "alloc(%u, %u, %d, %08x, ...) failed %d (%s)",
w, h, format, usage, err, strerror(-err));

if (err == NO_ERROR) {
Mutex::Autolock _l(sLock);
KeyedVector<buffer_handle_t, alloc_rec_t>& list(sAllocList);
int bpp = bytesPerPixel(format);
if (bpp < 0) {
// probably a HAL custom format. in any case, we don't know
// what its pixel size is.
bpp = 0;
}
alloc_rec_t rec;
rec.w = w;
rec.h = h;
rec.s = *stride;
rec.format = format;
rec.usage = usage;
rec.size = h * stride[0] * bpp;
list.add(*handle, rec);
}

return err;
}


status_t GraphicBufferAllocator::free(buffer_handle_t handle)
{
BufferLiberatorThread::queueCaptiveBuffer(handle);
Expand Down

0 comments on commit f53f9c6

Please sign in to comment.