Improve memory coherence management in screenshot code
The existing code worked in practice, but wasn't quite correct in theory and relied on implementation details of other code. It's still somewhat unusual and subtle, but now is correct-in-theory (I believe) and a little better documented. Bug: 16044767 Change-Id: I22b01d6640f0b7beca7cbfc74981795a3218b064
This commit is contained in:
parent
3795c0618f
commit
c61576794e
@ -28,15 +28,25 @@ class Barrier
|
|||||||
public:
|
public:
|
||||||
inline Barrier() : state(CLOSED) { }
|
inline Barrier() : state(CLOSED) { }
|
||||||
inline ~Barrier() { }
|
inline ~Barrier() { }
|
||||||
|
|
||||||
|
// Release any threads waiting at the Barrier.
|
||||||
|
// Provides release semantics: preceding loads and stores will be visible
|
||||||
|
// to other threads before they wake up.
|
||||||
void open() {
|
void open() {
|
||||||
Mutex::Autolock _l(lock);
|
Mutex::Autolock _l(lock);
|
||||||
state = OPENED;
|
state = OPENED;
|
||||||
cv.broadcast();
|
cv.broadcast();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Reset the Barrier, so wait() will block until open() has been called.
|
||||||
void close() {
|
void close() {
|
||||||
Mutex::Autolock _l(lock);
|
Mutex::Autolock _l(lock);
|
||||||
state = CLOSED;
|
state = CLOSED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Wait until the Barrier is OPEN.
|
||||||
|
// Provides acquire semantics: no subsequent loads or stores will occur
|
||||||
|
// until wait() returns.
|
||||||
void wait() const {
|
void wait() const {
|
||||||
Mutex::Autolock _l(lock);
|
Mutex::Autolock _l(lock);
|
||||||
while (state == CLOSED) {
|
while (state == CLOSED) {
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include <dlfcn.h>
|
#include <dlfcn.h>
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
|
#include <stdatomic.h>
|
||||||
|
|
||||||
#include <EGL/egl.h>
|
#include <EGL/egl.h>
|
||||||
|
|
||||||
@ -2681,20 +2682,32 @@ void SurfaceFlinger::repaintEverything() {
|
|||||||
/* The code below is here to handle b/8734824
|
/* The code below is here to handle b/8734824
|
||||||
*
|
*
|
||||||
* We create a IGraphicBufferProducer wrapper that forwards all calls
|
* We create a IGraphicBufferProducer wrapper that forwards all calls
|
||||||
* to the calling binder thread, where they are executed. This allows
|
* from the surfaceflinger thread to the calling binder thread, where they
|
||||||
* the calling thread to be reused (on the other side) and not
|
* are executed. This allows the calling thread in the calling process to be
|
||||||
* depend on having "enough" binder threads to handle the requests.
|
* reused and not depend on having "enough" binder threads to handle the
|
||||||
*
|
* requests.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class GraphicProducerWrapper : public BBinder, public MessageHandler {
|
class GraphicProducerWrapper : public BBinder, public MessageHandler {
|
||||||
|
/* Parts of GraphicProducerWrapper are run on two different threads,
|
||||||
|
* communicating by sending messages via Looper but also by shared member
|
||||||
|
* data. Coherence maintenance is subtle and in places implicit (ugh).
|
||||||
|
*
|
||||||
|
* Don't rely on Looper's sendMessage/handleMessage providing
|
||||||
|
* release/acquire semantics for any data not actually in the Message.
|
||||||
|
* Data going from surfaceflinger to binder threads needs to be
|
||||||
|
* synchronized explicitly.
|
||||||
|
*
|
||||||
|
* Barrier open/wait do provide release/acquire semantics. This provides
|
||||||
|
* implicit synchronization for data coming back from binder to
|
||||||
|
* surfaceflinger threads.
|
||||||
|
*/
|
||||||
|
|
||||||
sp<IGraphicBufferProducer> impl;
|
sp<IGraphicBufferProducer> impl;
|
||||||
sp<Looper> looper;
|
sp<Looper> looper;
|
||||||
status_t result;
|
status_t result;
|
||||||
bool exitPending;
|
bool exitPending;
|
||||||
bool exitRequested;
|
bool exitRequested;
|
||||||
mutable Barrier barrier;
|
Barrier barrier;
|
||||||
volatile int32_t memoryBarrier;
|
|
||||||
uint32_t code;
|
uint32_t code;
|
||||||
Parcel const* data;
|
Parcel const* data;
|
||||||
Parcel* reply;
|
Parcel* reply;
|
||||||
@ -2705,20 +2718,26 @@ class GraphicProducerWrapper : public BBinder, public MessageHandler {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* this is called by our "fake" BpGraphicBufferProducer. We package the
|
* Called on surfaceflinger thread. This is called by our "fake"
|
||||||
* data and reply Parcel and forward them to the calling thread.
|
* BpGraphicBufferProducer. We package the data and reply Parcel and
|
||||||
|
* forward them to the binder thread.
|
||||||
*/
|
*/
|
||||||
virtual status_t transact(uint32_t code,
|
virtual status_t transact(uint32_t code,
|
||||||
const Parcel& data, Parcel* reply, uint32_t /* flags */) {
|
const Parcel& data, Parcel* reply, uint32_t /* flags */) {
|
||||||
this->code = code;
|
this->code = code;
|
||||||
this->data = &data;
|
this->data = &data;
|
||||||
this->reply = reply;
|
this->reply = reply;
|
||||||
android_atomic_acquire_store(0, &memoryBarrier);
|
|
||||||
if (exitPending) {
|
if (exitPending) {
|
||||||
// if we've exited, we run the message synchronously right here
|
// if we've exited, we run the message synchronously right here.
|
||||||
|
// note (JH): as far as I can tell from looking at the code, this
|
||||||
|
// never actually happens. if it does, i'm not sure if it happens
|
||||||
|
// on the surfaceflinger or binder thread.
|
||||||
handleMessage(Message(MSG_API_CALL));
|
handleMessage(Message(MSG_API_CALL));
|
||||||
} else {
|
} else {
|
||||||
barrier.close();
|
barrier.close();
|
||||||
|
// Prevent stores to this->{code, data, reply} from being
|
||||||
|
// reordered later than the construction of Message.
|
||||||
|
atomic_thread_fence(memory_order_release);
|
||||||
looper->sendMessage(this, Message(MSG_API_CALL));
|
looper->sendMessage(this, Message(MSG_API_CALL));
|
||||||
barrier.wait();
|
barrier.wait();
|
||||||
}
|
}
|
||||||
@ -2726,25 +2745,30 @@ class GraphicProducerWrapper : public BBinder, public MessageHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* here we run on the binder calling thread. All we've got to do is
|
* here we run on the binder thread. All we've got to do is
|
||||||
* call the real BpGraphicBufferProducer.
|
* call the real BpGraphicBufferProducer.
|
||||||
*/
|
*/
|
||||||
virtual void handleMessage(const Message& message) {
|
virtual void handleMessage(const Message& message) {
|
||||||
android_atomic_release_load(&memoryBarrier);
|
int what = message.what;
|
||||||
if (message.what == MSG_API_CALL) {
|
// Prevent reads below from happening before the read from Message
|
||||||
|
atomic_thread_fence(memory_order_acquire);
|
||||||
|
if (what == MSG_API_CALL) {
|
||||||
result = impl->asBinder()->transact(code, data[0], reply);
|
result = impl->asBinder()->transact(code, data[0], reply);
|
||||||
barrier.open();
|
barrier.open();
|
||||||
} else if (message.what == MSG_EXIT) {
|
} else if (what == MSG_EXIT) {
|
||||||
exitRequested = true;
|
exitRequested = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
GraphicProducerWrapper(const sp<IGraphicBufferProducer>& impl) :
|
GraphicProducerWrapper(const sp<IGraphicBufferProducer>& impl)
|
||||||
impl(impl), looper(new Looper(true)), result(NO_ERROR),
|
: impl(impl),
|
||||||
exitPending(false), exitRequested(false) {
|
looper(new Looper(true)),
|
||||||
}
|
exitPending(false),
|
||||||
|
exitRequested(false)
|
||||||
|
{}
|
||||||
|
|
||||||
|
// Binder thread
|
||||||
status_t waitForResponse() {
|
status_t waitForResponse() {
|
||||||
do {
|
do {
|
||||||
looper->pollOnce(-1);
|
looper->pollOnce(-1);
|
||||||
@ -2752,9 +2776,13 @@ public:
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Client thread
|
||||||
void exit(status_t result) {
|
void exit(status_t result) {
|
||||||
this->result = result;
|
this->result = result;
|
||||||
exitPending = true;
|
exitPending = true;
|
||||||
|
// Ensure this->result is visible to the binder thread before it
|
||||||
|
// handles the message.
|
||||||
|
atomic_thread_fence(memory_order_release);
|
||||||
looper->sendMessage(this, Message(MSG_EXIT));
|
looper->sendMessage(this, Message(MSG_EXIT));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user