[gbinder] Added API to block incoming requests. JB#43529

GBinderRemoteRequest can be marked as blocked by calling
gbinder_remote_request_block() and later completed with
gbinder_remote_request_complete()
This commit is contained in:
Slava Monich
2018-12-16 01:53:49 +02:00
parent 4811d51c5d
commit f46448c236
7 changed files with 670 additions and 112 deletions

View File

@@ -67,6 +67,16 @@ gbinder_remote_request_copy_to_local(
GBinderRemoteRequest* req) /* since 1.0.6 */
G_GNUC_WARN_UNUSED_RESULT;
void
gbinder_remote_request_block(
GBinderRemoteRequest* req); /* Since 1.0.20 */
void
gbinder_remote_request_complete(
GBinderRemoteRequest* req,
GBinderLocalReply* reply,
int status); /* Since 1.0.20 */
/* Convenience function to decode requests with just one data item */
gboolean

View File

@@ -68,9 +68,9 @@ struct gbinder_ipc_priv {
GMutex local_objects_mutex;
GHashTable* local_objects;
/* We may need more loopers... But let's start with just one */
GMutex looper_mutex;
GBinderIpcLooper* looper;
GBinderIpcLooper* primary_loopers;
GBinderIpcLooper* blocked_loopers;
};
typedef GObjectClass GBinderIpcClass;
@@ -100,7 +100,7 @@ static pthread_mutex_t gbinder_ipc_mutex = PTHREAD_MUTEX_INITIALIZER;
* When the main thread receives GBinderIpcLooperTx:
*
* 1. Lets the object to process it and produce the response (GBinderOutput).
* 2. Writes one byte to the sending end of the tx pipe.
* 2. Writes one byte (TX_DONE) to the sending end of the tx pipe.
* 3. Unreferences GBinderIpcLooperTx
*
* When tx pipe wakes up the looper:
@@ -111,11 +111,26 @@ static pthread_mutex_t gbinder_ipc_mutex = PTHREAD_MUTEX_INITIALIZER;
* Note that GBinderIpcLooperTx can be deallocated on either looper or
* main thread, depending on whether looper gives up on the transaction
* before it gets processed.
*
* When transaction is blocked by gbinder_remote_request_block() call, it
* gets slightly more complicated. Then the main thread writes TX_BLOCKED
* to the pipe (rather than TX_DONE) and then looper thread spawn another
* looper and keeps waiting for TX_DONE.
*/
#define TX_DONE (0x2a)
#define TX_BLOCKED (0x3b)
typedef struct gbinder_ipc_looper_tx {
typedef enum gbinder_ipc_looper_tx_state {
GBINDER_IPC_LOOPER_TX_SCHEDULED,
GBINDER_IPC_LOOPER_TX_PROCESSING,
GBINDER_IPC_LOOPER_TX_PROCESSED,
GBINDER_IPC_LOOPER_TX_BLOCKING,
GBINDER_IPC_LOOPER_TX_BLOCKED,
GBINDER_IPC_LOOPER_TX_COMPLETE
} GBINDER_IPC_LOOPER_TX_STATE;
struct gbinder_ipc_looper_tx {
/* Reference count */
gint refcount;
/* These are filled by the looper: */
@@ -125,19 +140,23 @@ typedef struct gbinder_ipc_looper_tx {
GBinderLocalObject* obj;
GBinderRemoteRequest* req;
/* And these by the main thread processing the transaction: */
GBINDER_IPC_LOOPER_TX_STATE state;
GBinderLocalReply* reply;
int status;
} GBinderIpcLooperTx;
} /* GBinderIpcLooperTx */;
struct gbinder_ipc_looper {
gint refcount;
GBinderIpcLooper* next;
char* name;
GBinderHandler handler;
GBinderDriver* driver;
GBinderIpc* ipc; /* Not a reference! */
GThread* thread;
GMutex mutex;
GCond start_cond;
gboolean started;
gint exit;
gint started;
int pipefd[2];
int txfd[2];
};
@@ -178,6 +197,11 @@ typedef struct gbinder_ipc_tx_custom {
GBINDER_INLINE_FUNC const char* gbinder_ipc_name(GBinderIpc* self)
{ return gbinder_driver_dev(self->driver); }
static
GBinderIpcLooper*
gbinder_ipc_looper_new(
GBinderIpc* ipc);
/*==========================================================================*
* GBinderIpcLooperTx
*==========================================================================*/
@@ -245,6 +269,91 @@ gbinder_ipc_looper_tx_unref(
return dropped;
}
/*==========================================================================*
* State machine of transaction handling. All this is happening on the event
* thread and therefore doesn't need to be synchronized.
*
* SCHEDULED
* =========
* |
* PROCESSING
* ==========
* |
* --------------------- handler is called ---------------------------------
* |
* +---------------- request doesn't need to be blocked ----------+
* | |
* gbinder_remote_request_block() |
* | |
* BLOCKING -- gbinder_remote_request_complete() --> PROCESSED |
* ======== ========= |
* | | |
* --------------------- handler returns -----------------------------------
* | | |
* BLOCKED COMPLETE <-------+
* ======= ========
* ^
* ... |
* gbinder_remote_request_complete() is called later ----+
*==========================================================================*/
void
gbinder_remote_request_block(
GBinderRemoteRequest* req) /* Since 1.0.20 */
{
if (G_LIKELY(req)) {
GBinderIpcLooperTx* tx = req->tx;
GASSERT(tx);
if (G_LIKELY(tx)) {
GASSERT(tx->state == GBINDER_IPC_LOOPER_TX_PROCESSING);
if (tx->state == GBINDER_IPC_LOOPER_TX_PROCESSING) {
tx->state = GBINDER_IPC_LOOPER_TX_BLOCKING;
}
}
}
}
void
gbinder_remote_request_complete(
GBinderRemoteRequest* req,
GBinderLocalReply* reply,
int status) /* Since 1.0.20 */
{
if (G_LIKELY(req)) {
GBinderIpcLooperTx* tx = req->tx;
GASSERT(tx);
if (G_LIKELY(tx)) {
const guint8 done = TX_DONE;
switch (tx->state) {
case GBINDER_IPC_LOOPER_TX_BLOCKING:
/* Called by the transaction handler */
tx->status = status;
tx->reply = gbinder_local_reply_ref(reply);
tx->state = GBINDER_IPC_LOOPER_TX_PROCESSED;
break;
case GBINDER_IPC_LOOPER_TX_BLOCKED:
/* Really asynchronous completion */
tx->status = status;
tx->reply = gbinder_local_reply_ref(reply);
tx->state = GBINDER_IPC_LOOPER_TX_COMPLETE;
/* Wake up the looper */
(void)write(tx->pipefd[1], &done, sizeof(done));
break;
default:
GWARN("Unexpected state %d in request completion", tx->state);
break;
}
/* Clear the transaction reference */
gbinder_ipc_looper_tx_unref(tx, FALSE);
req->tx = NULL;
}
}
}
/*==========================================================================*
* GBinderIpcLooper
*==========================================================================*/
@@ -255,11 +364,70 @@ gbinder_ipc_looper_tx_handle(
gpointer data)
{
GBinderIpcLooperTx* tx = data;
guint8 done = TX_DONE;
GBinderRemoteRequest* req = tx->req;
GBinderLocalReply* reply;
int status = GBINDER_STATUS_OK;
guint8 done;
/*
* Transaction reference for gbinder_remote_request_block()
* and gbinder_remote_request_complete().
*/
req->tx = gbinder_ipc_looper_tx_ref(tx);
/* See state machine */
GASSERT(tx->state == GBINDER_IPC_LOOPER_TX_SCHEDULED);
tx->state = GBINDER_IPC_LOOPER_TX_PROCESSING;
/* Actually handle the transaction */
tx->reply = gbinder_local_object_handle_transaction(tx->obj, tx->req,
tx->code, tx->flags, &tx->status);
reply = gbinder_local_object_handle_transaction(tx->obj, req,
tx->code, tx->flags, &status);
/* Handle all possible return states */
switch (tx->state) {
case GBINDER_IPC_LOOPER_TX_PROCESSING:
/* Result was returned by the handler */
tx->reply = reply;
tx->status = status;
tx->state = GBINDER_IPC_LOOPER_TX_COMPLETE;
reply = NULL;
break;
case GBINDER_IPC_LOOPER_TX_PROCESSED:
/* Result has been provided to gbinder_remote_request_complete() */
tx->state = GBINDER_IPC_LOOPER_TX_COMPLETE;
break;
case GBINDER_IPC_LOOPER_TX_BLOCKING:
/* Result will be provided to gbinder_remote_request_complete() */
tx->state = GBINDER_IPC_LOOPER_TX_BLOCKED;
break;
default:
break;
}
/* In case handler returns a reply which it wasn't expected to return */
GASSERT(!reply);
gbinder_local_reply_unref(reply);
/* Drop the transaction reference unless blocked */
if (tx->state == GBINDER_IPC_LOOPER_TX_BLOCKED) {
done = TX_BLOCKED;
/*
* From this point on, it's GBinderRemoteRequest who's holding
* reference to GBinderIpcLooperTx, not the other way around and
* not both ways. Even if gbinder_remote_request_complete() never
* gets called, transaction will still be completed when the last
* reference to GBinderRemoteRequest goes away. And if request
* never gets deallocated... oh well.
*/
gbinder_remote_request_unref(tx->req);
tx->req = NULL;
} else {
done = TX_DONE;
if (req->tx) {
gbinder_ipc_looper_tx_unref(req->tx, FALSE);
req->tx = NULL;
}
}
/* And wake up the looper */
(void)write(tx->pipefd[1], &done, sizeof(done));
@@ -274,6 +442,52 @@ gbinder_ipc_looper_tx_done(
gbinder_ipc_looper_tx_unref(data, FALSE);
}
static
gboolean
gbinder_ipc_looper_remove_from_list(
GBinderIpcLooper* looper,
GBinderIpcLooper** list)
{
/* Caller holds looper_mutex */
if (*list) {
if ((*list) == looper) {
(*list) = looper->next;
looper->next = NULL;
return TRUE;
} else {
GBinderIpcLooper* prev = (*list);
while (prev->next) {
if (prev->next == looper) {
prev->next = looper->next;
looper->next = NULL;
return TRUE;
}
prev = prev->next;
}
}
}
return FALSE;
}
static
gboolean
gbinder_ipc_looper_remove_primary(
GBinderIpcLooper* looper)
{
return gbinder_ipc_looper_remove_from_list(looper,
&looper->ipc->priv->primary_loopers);
}
static
gboolean
gbinder_ipc_looper_remove_blocked(
GBinderIpcLooper* looper)
{
return gbinder_ipc_looper_remove_from_list(looper,
&looper->ipc->priv->blocked_loopers);
}
static
GBinderLocalReply*
gbinder_ipc_looper_transact(
@@ -318,11 +532,65 @@ gbinder_ipc_looper_transact(
if ((fds[1].revents & POLLIN) &&
read(fds[1].fd, &done, sizeof(done)) == 1) {
/* Normal completion */
if (done == TX_BLOCKED) {
/*
* We are going to block this looper for potentially
* significant period of time. Start new looper to
* accept normal incoming requests and terminate this
* one when we are done with this transaction.
*
* For the duration of the transaction, this looper is
* moved to the blocked_loopers list.
*/
GBinderIpcPriv* priv = looper->ipc->priv;
/* Lock */
g_mutex_lock(&priv->looper_mutex);
if (gbinder_ipc_looper_remove_primary(looper)) {
GBinderIpcLooper* new_looper;
GDEBUG("Primary looper %s is blocked", looper->name);
looper->next = priv->blocked_loopers;
priv->blocked_loopers = looper;
/* Looper will exit once transaction completes */
g_atomic_int_set(&looper->exit, 1);
/* Create new primary looper to replace this one */
new_looper = gbinder_ipc_looper_new(ipc);
if (new_looper) {
new_looper->next = priv->primary_loopers;
priv->primary_loopers = new_looper;
}
}
g_mutex_unlock(&priv->looper_mutex);
/* Unlock */
/* Block until asynchronous transaction gets completed. */
done = 0;
memset(fds, 0, sizeof(fds));
fds[0].fd = looper->pipefd[0];
fds[0].events = POLLIN | POLLERR | POLLHUP | POLLNVAL;
fds[1].fd = tx->pipefd[0];
fds[1].events = POLLIN | POLLERR | POLLHUP | POLLNVAL;
poll(fds, 2, -1);
if ((fds[1].revents & POLLIN) &&
read(fds[1].fd, &done, sizeof(done)) == 1) {
GASSERT(done == TX_DONE);
}
}
}
if (done) {
GASSERT(done == TX_DONE);
reply = gbinder_local_reply_ref(tx->reply);
status = tx->status;
if (!gbinder_ipc_looper_tx_unref(tx, TRUE)) {
/* gbinder_ipc_looper_tx_free() will close those */
/*
* This wasn't the last references meaning that
* gbinder_ipc_looper_tx_free() will close the
* descriptors and we will have to create a new
* pipe for the next transaction.
*/
looper->txfd[0] = looper->txfd[1] = -1;
}
} else {
@@ -348,6 +616,7 @@ gbinder_ipc_looper_free(
close(looper->txfd[1]);
}
gbinder_driver_unref(looper->driver);
g_free(looper->name);
g_cond_clear(&looper->start_cond);
g_mutex_clear(&looper->mutex);
g_slice_free(GBinderIpcLooper, looper);
@@ -384,11 +653,11 @@ gbinder_ipc_looper_thread(
if (gbinder_driver_enter_looper(driver)) {
struct pollfd pipefd;
int result;
int res;
GDEBUG("Looper %s running", gbinder_driver_dev(driver));
GDEBUG("Looper %s running", looper->name);
g_mutex_lock(&looper->mutex);
looper->started = TRUE;
g_atomic_int_set(&looper->started, TRUE);
g_cond_broadcast(&looper->start_cond);
g_mutex_unlock(&looper->mutex);
@@ -396,31 +665,33 @@ gbinder_ipc_looper_thread(
pipefd.fd = looper->pipefd[0]; /* read end of the pipe */
pipefd.events = POLLIN | POLLERR | POLLHUP | POLLNVAL;
result = gbinder_driver_poll(driver, &pipefd);
while (looper->ipc && ((result & POLLIN) || !result)) {
if (result & POLLIN) {
/* No need to synchronize access to looper->ipc because
res = gbinder_driver_poll(driver, &pipefd);
while (!g_atomic_int_get(&looper->exit) && ((res & POLLIN) || !res)) {
if (res & POLLIN) {
/*
* No need to synchronize access to looper->ipc because
* the other thread would wait until this thread exits
* before setting looper->ipc to NULL */
* before setting looper->ipc to NULL.
*/
GBinderIpc* ipc = gbinder_ipc_ref(looper->ipc);
GBinderObjectRegistry* reg = gbinder_ipc_object_registry(ipc);
/* But that gbinder_driver_read() may unref GBinderIpc */
int ret = gbinder_driver_read(driver, reg, &looper->handler);
/* And this gbinder_ipc_unref() may release the last ref: */
gbinder_ipc_unref(ipc);
/* And at this point looper->ipc may be NULL */
if (ret < 0) {
GDEBUG("Looper %s failed", gbinder_driver_dev(driver));
GDEBUG("Looper %s failed", looper->name);
break;
}
}
if (pipefd.revents) {
/* Any event from this pipe terminates the loop */
GDEBUG("Looper %s is asked to exit",
gbinder_driver_dev(driver));
/* Any event from this pipe terminates the loop */
if (pipefd.revents || g_atomic_int_get(&looper->exit)) {
GDEBUG("Looper %s is requested to exit", looper->name);
break;
}
result = gbinder_driver_poll(driver, &pipefd);
res = gbinder_driver_poll(driver, &pipefd);
}
gbinder_driver_exit_looper(driver);
@@ -432,24 +703,26 @@ gbinder_ipc_looper_thread(
*/
if (looper->ipc) {
GBinderIpcPriv* priv = looper->ipc->priv;
/* Lock */
g_mutex_lock(&priv->looper_mutex);
if (priv->looper == looper) {
if (gbinder_ipc_looper_remove_blocked(looper) ||
gbinder_ipc_looper_remove_primary(looper)) {
/* Spontaneous exit */
priv->looper = NULL;
GDEBUG("Looper %s exits", gbinder_driver_dev(driver));
GDEBUG("Looper %s exits", looper->name);
gbinder_ipc_looper_unref(looper);
} else {
/* Main thread is shutting it down */
GDEBUG("Looper %s done", gbinder_driver_dev(driver));
GDEBUG("Looper %s done", looper->name);
}
g_mutex_unlock(&priv->looper_mutex);
/* Unlock */
} else {
GDEBUG("Looper %s is abandoned", gbinder_driver_dev(driver));
GDEBUG("Looper %s is abandoned", looper->name);
}
} else {
g_mutex_lock(&looper->mutex);
looper->started = TRUE;
g_atomic_int_set(&looper->started, TRUE);
g_cond_broadcast(&looper->start_cond);
g_mutex_unlock(&looper->mutex);
}
@@ -472,20 +745,24 @@ gbinder_ipc_looper_new(
};
GError* error = NULL;
GBinderIpcLooper* looper = g_slice_new0(GBinderIpcLooper);
static gint gbinder_ipc_next_looper_id = 1;
guint id = (guint)g_atomic_int_add(&gbinder_ipc_next_looper_id, 1);
memcpy(looper->pipefd, fd, sizeof(fd));
looper->txfd[0] = looper->txfd[1] = -1;
g_atomic_int_set(&looper->refcount, 1);
g_cond_init(&looper->start_cond);
g_mutex_init(&looper->mutex);
looper->name = g_strdup_printf("%s#%u", gbinder_ipc_name(ipc), id);
looper->handler.f = &handler_functions;
looper->ipc = ipc;
looper->driver = gbinder_driver_ref(ipc->driver);
looper->thread = g_thread_try_new(gbinder_ipc_name(ipc),
looper->thread = g_thread_try_new(looper->name,
gbinder_ipc_looper_thread, looper, &error);
if (looper->thread) {
/* gbinder_ipc_looper_thread() will release this reference: */
gbinder_ipc_looper_ref(looper);
GDEBUG("Starting looper %s", looper->name);
return looper;
} else {
GERR("Failed to create looper thread: %s", GERRMSG(error));
@@ -505,29 +782,29 @@ gbinder_ipc_looper_check(
if (G_LIKELY(self)) {
GBinderIpcPriv* priv = self->priv;
if (!priv->looper) {
if (!priv->primary_loopers) {
GBinderIpcLooper* looper;
/* Lock */
g_mutex_lock(&priv->looper_mutex);
if (!priv->looper) {
GDEBUG("Starting looper %s", gbinder_ipc_name(self));
priv->looper = gbinder_ipc_looper_new(self);
looper = priv->primary_loopers;
if (!looper) {
looper = priv->primary_loopers = gbinder_ipc_looper_new(self);
}
g_mutex_unlock(&priv->looper_mutex);
/* Unlock */
/* We are not ready to accept incoming transactions until
* looper has started. We may need to wait a bit. */
looper = priv->looper;
if (!looper->started) {
if (looper && !g_atomic_int_get(&looper->started)) {
/* Lock */
g_mutex_lock(&looper->mutex);
if (!looper->started) {
if (!g_atomic_int_get(&looper->started)) {
g_cond_wait_until(&looper->start_cond, &looper->mutex,
g_get_monotonic_time() +
GBINDER_IPC_LOOPER_START_TIMEOUT_SEC *
G_TIME_SPAN_SECOND);
GASSERT(looper->started);
GASSERT(g_atomic_int_get(&looper->started));
}
g_mutex_unlock(&looper->mutex);
/* Unlock */
@@ -536,6 +813,54 @@ gbinder_ipc_looper_check(
}
}
static
void
gbinder_ipc_looper_stop(
GBinderIpcLooper* looper)
{
/* Caller checks looper for NULL */
if (looper->thread && looper->thread != g_thread_self()) {
guint8 done = TX_DONE;
GDEBUG("Stopping looper %s", looper->name);
g_atomic_int_set(&looper->exit, TRUE);
if (write(looper->pipefd[1], &done, sizeof(done)) <= 0) {
looper->thread = NULL;
}
}
}
static
GBinderIpcLooper*
gbinder_ipc_looper_stop_all(
GBinderIpcLooper* loopers,
GBinderIpcLooper* list)
{
while (list) {
GBinderIpcLooper* looper = list;
GBinderIpcLooper* next = looper->next;
gbinder_ipc_looper_stop(looper);
looper->next = loopers;
loopers = looper;
list = next;
}
return loopers;
}
static
void
gbinder_ipc_looper_join(
GBinderIpcLooper* looper)
{
/* Caller checks looper for NULL */
if (looper->thread && looper->thread != g_thread_self()) {
g_thread_join(looper->thread);
looper->thread = NULL;
}
looper->ipc = NULL;
}
/*==========================================================================*
* GBinderObjectRegistry
*==========================================================================*/
@@ -1232,7 +1557,7 @@ gbinder_ipc_dispose(
{
GBinderIpc* self = GBINDER_IPC(object);
GBinderIpcPriv* priv = self->priv;
GBinderIpcLooper* looper;
GBinderIpcLooper* loopers = NULL;
GVERBOSE_("%s", self->dev);
/* Lock */
@@ -1253,22 +1578,19 @@ gbinder_ipc_dispose(
/* Lock */
g_mutex_lock(&priv->looper_mutex);
looper = priv->looper;
priv->looper = NULL;
loopers = gbinder_ipc_looper_stop_all(loopers, priv->primary_loopers);
loopers = gbinder_ipc_looper_stop_all(loopers, priv->blocked_loopers);
priv->blocked_loopers = NULL;
priv->primary_loopers = NULL;
g_mutex_unlock(&priv->looper_mutex);
/* Unlock */
if (looper) {
if (looper->thread && looper->thread != g_thread_self()) {
guint8 done = TX_DONE;
while (loopers) {
GBinderIpcLooper* looper = loopers;
GDEBUG("Stopping looper %s", gbinder_ipc_name(looper->ipc));
if (write(looper->pipefd[1], &done, sizeof(done)) > 0) {
g_thread_join(looper->thread);
looper->thread = NULL;
}
}
looper->ipc = NULL;
loopers = looper->next;
looper->next = NULL;
gbinder_ipc_looper_join(looper);
gbinder_ipc_looper_unref(looper);
}

View File

@@ -40,7 +40,10 @@
#include <gutil_macros.h>
struct gbinder_remote_request {
#include <errno.h>
typedef struct gbinder_remote_request_priv {
GBinderRemoteRequest pub;
gint refcount;
pid_t pid;
uid_t euid;
@@ -49,7 +52,11 @@ struct gbinder_remote_request {
char* iface2;
gsize header_size;
GBinderReaderData data;
};
} GBinderRemoteRequestPriv;
GBINDER_INLINE_FUNC GBinderRemoteRequestPriv*
gbinder_remote_request_cast(GBinderRemoteRequest* pub)
{ return G_LIKELY(pub) ? G_CAST(pub,GBinderRemoteRequestPriv,pub) : NULL; }
GBinderRemoteRequest*
gbinder_remote_request_new(
@@ -58,7 +65,7 @@ gbinder_remote_request_new(
pid_t pid,
uid_t euid)
{
GBinderRemoteRequest* self = g_slice_new0(GBinderRemoteRequest);
GBinderRemoteRequestPriv* self = g_slice_new0(GBinderRemoteRequestPriv);
GBinderReaderData* data = &self->data;
g_atomic_int_set(&self->refcount, 1);
@@ -66,13 +73,15 @@ gbinder_remote_request_new(
self->euid = euid;
self->protocol = protocol;
data->reg = gbinder_object_registry_ref(reg);
return self;
return &self->pub;
}
GBinderLocalRequest*
gbinder_remote_request_copy_to_local(
GBinderRemoteRequest* self)
GBinderRemoteRequest* req)
{
GBinderRemoteRequestPriv* self = gbinder_remote_request_cast(req);
if (G_LIKELY(self)) {
GBinderReaderData* d = &self->data;
@@ -84,21 +93,27 @@ gbinder_remote_request_copy_to_local(
static
void
gbinder_remote_request_free(
GBinderRemoteRequest* self)
GBinderRemoteRequestPriv* self)
{
GBinderReaderData* data = &self->data;
GBinderRemoteRequest* req = &self->pub;
GASSERT(!req->tx);
if (req->tx) {
GWARN("Request is dropped without completing the transaction");
gbinder_remote_request_complete(req, NULL, -ECANCELED);
}
gbinder_object_registry_unref(data->reg);
gbinder_buffer_free(data->buffer);
g_free(self->iface2);
g_slice_free(GBinderRemoteRequest, self);
g_slice_free(GBinderRemoteRequestPriv, self);
}
static
inline
void
gbinder_remote_request_init_reader2(
GBinderRemoteRequest* self,
GBinderRemoteRequestPriv* self,
GBinderReader* p)
{
/* The caller has already checked the request for NULL */
@@ -115,10 +130,12 @@ gbinder_remote_request_init_reader2(
void
gbinder_remote_request_set_data(
GBinderRemoteRequest* self,
GBinderRemoteRequest* req,
guint32 txcode,
GBinderBuffer* buffer)
{
GBinderRemoteRequestPriv* self = gbinder_remote_request_cast(req);
if (G_LIKELY(self)) {
GBinderReaderData* data = &self->data;
GBinderReader reader;
@@ -145,26 +162,32 @@ gbinder_remote_request_set_data(
const char*
gbinder_remote_request_interface(
GBinderRemoteRequest* self)
GBinderRemoteRequest* req)
{
GBinderRemoteRequestPriv* self = gbinder_remote_request_cast(req);
return G_LIKELY(self) ? self->iface : NULL;
}
GBinderRemoteRequest*
gbinder_remote_request_ref(
GBinderRemoteRequest* self)
GBinderRemoteRequest* req)
{
GBinderRemoteRequestPriv* self = gbinder_remote_request_cast(req);
if (G_LIKELY(self)) {
GASSERT(self->refcount > 0);
g_atomic_int_inc(&self->refcount);
}
return self;
return req;
}
void
gbinder_remote_request_unref(
GBinderRemoteRequest* self)
GBinderRemoteRequest* req)
{
GBinderRemoteRequestPriv* self = gbinder_remote_request_cast(req);
if (G_LIKELY(self)) {
GASSERT(self->refcount > 0);
if (g_atomic_int_dec_and_test(&self->refcount)) {
@@ -175,9 +198,11 @@ gbinder_remote_request_unref(
void
gbinder_remote_request_init_reader(
GBinderRemoteRequest* self,
GBinderRemoteRequest* req,
GBinderReader* reader)
{
GBinderRemoteRequestPriv* self = gbinder_remote_request_cast(req);
if (G_LIKELY(self)) {
gbinder_remote_request_init_reader2(self, reader);
} else {
@@ -187,15 +212,19 @@ gbinder_remote_request_init_reader(
pid_t
gbinder_remote_request_sender_pid(
GBinderRemoteRequest* self)
GBinderRemoteRequest* req)
{
GBinderRemoteRequestPriv* self = gbinder_remote_request_cast(req);
return G_LIKELY(self) ? self->pid : (uid_t)(-1);
}
uid_t
gbinder_remote_request_sender_euid(
GBinderRemoteRequest* self)
GBinderRemoteRequest* req)
{
GBinderRemoteRequestPriv* self = gbinder_remote_request_cast(req);
return G_LIKELY(self) ? self->euid : (uid_t)(-1);
}
@@ -209,9 +238,11 @@ gbinder_remote_request_read_int32(
gboolean
gbinder_remote_request_read_uint32(
GBinderRemoteRequest* self,
GBinderRemoteRequest* req,
guint32* value)
{
GBinderRemoteRequestPriv* self = gbinder_remote_request_cast(req);
if (G_LIKELY(self)) {
GBinderReader reader;
@@ -231,9 +262,11 @@ gbinder_remote_request_read_int64(
gboolean
gbinder_remote_request_read_uint64(
GBinderRemoteRequest* self,
GBinderRemoteRequest* req,
guint64* value)
{
GBinderRemoteRequestPriv* self = gbinder_remote_request_cast(req);
if (G_LIKELY(self)) {
GBinderReader reader;
@@ -245,8 +278,10 @@ gbinder_remote_request_read_uint64(
const char*
gbinder_remote_request_read_string8(
GBinderRemoteRequest* self)
GBinderRemoteRequest* req)
{
GBinderRemoteRequestPriv* self = gbinder_remote_request_cast(req);
if (G_LIKELY(self)) {
GBinderReader reader;
@@ -258,8 +293,10 @@ gbinder_remote_request_read_string8(
char*
gbinder_remote_request_read_string16(
GBinderRemoteRequest* self)
GBinderRemoteRequest* req)
{
GBinderRemoteRequestPriv* self = gbinder_remote_request_cast(req);
if (G_LIKELY(self)) {
GBinderReader reader;
@@ -271,8 +308,10 @@ gbinder_remote_request_read_string16(
GBinderRemoteObject*
gbinder_remote_request_read_object(
GBinderRemoteRequest* self)
GBinderRemoteRequest* req)
{
GBinderRemoteRequestPriv* self = gbinder_remote_request_cast(req);
if (G_LIKELY(self)) {
GBinderReader reader;

View File

@@ -37,6 +37,10 @@
#include "gbinder_types_p.h"
struct gbinder_remote_request {
GBinderIpcLooperTx* tx;
};
GBinderRemoteRequest*
gbinder_remote_request_new(
GBinderObjectRegistry* reg,

View File

@@ -45,6 +45,7 @@ typedef struct gbinder_object_registry GBinderObjectRegistry;
typedef struct gbinder_output_data GBinderOutputData;
typedef struct gbinder_rpc_protocol GBinderRpcProtocol;
typedef struct gbinder_servicepoll GBinderServicePoll;
typedef struct gbinder_ipc_looper_tx GBinderIpcLooperTx;
#define GBINDER_INLINE_FUNC static inline

View File

@@ -52,6 +52,24 @@
static TestOpt test_opt;
static
gboolean
test_unref_ipc(
gpointer ipc)
{
gbinder_ipc_unref(ipc);
return G_SOURCE_REMOVE;
}
static
void
test_quit_when_destroyed(
gpointer loop,
GObject* obj)
{
test_quit_later((GMainLoop*)loop);
}
/*==========================================================================*
* null
*==========================================================================*/
@@ -606,24 +624,6 @@ test_transact_incoming_proc(
return gbinder_local_object_new_reply(obj);
}
static
gboolean
test_transact_unref_ipc(
gpointer ipc)
{
gbinder_ipc_unref(ipc);
return G_SOURCE_REMOVE;
}
static
void
test_transact_done(
gpointer loop,
GObject* ipc)
{
test_quit_later((GMainLoop*)loop);
}
static
void
test_transact_incoming(
@@ -651,17 +651,17 @@ test_transact_incoming(
/* Now we need to wait until GBinderIpc is destroyed */
GDEBUG("waiting for GBinderIpc to get destroyed");
g_object_weak_ref(G_OBJECT(ipc), test_transact_done, loop);
g_object_weak_ref(G_OBJECT(ipc), test_quit_when_destroyed, loop);
gbinder_local_object_unref(obj);
gbinder_local_request_unref(req);
g_idle_add(test_transact_unref_ipc, ipc);
g_idle_add(test_unref_ipc, ipc);
test_run(&test_opt, loop);
g_main_loop_unref(loop);
}
/*==========================================================================*
* transact_incoming_status
* transact_status_reply
*==========================================================================*/
static
@@ -712,10 +712,184 @@ test_transact_status_reply(
/* Now we need to wait until GBinderIpc is destroyed */
GDEBUG("waiting for GBinderIpc to get destroyed");
g_object_weak_ref(G_OBJECT(ipc), test_transact_done, loop);
g_object_weak_ref(G_OBJECT(ipc), test_quit_when_destroyed, loop);
gbinder_local_object_unref(obj);
gbinder_local_request_unref(req);
g_idle_add(test_transact_unref_ipc, ipc);
g_idle_add(test_unref_ipc, ipc);
test_run(&test_opt, loop);
g_main_loop_unref(loop);
}
/*==========================================================================*
* transact_async
*==========================================================================*/
typedef struct test_transact_async_req {
GBinderLocalObject* obj;
GBinderRemoteRequest* req;
GMainLoop* loop;
} TestTransactAsyncReq;
static
void
test_transact_async_done(
gpointer data)
{
TestTransactAsyncReq* test = data;
gbinder_local_object_unref(test->obj);
gbinder_remote_request_unref(test->req);
test_quit_later(test->loop);
g_free(test);
}
static
gboolean
test_transact_async_reply(
gpointer data)
{
TestTransactAsyncReq* test = data;
GBinderLocalReply* reply = gbinder_local_object_new_reply(test->obj);
gbinder_remote_request_complete(test->req, reply, 0);
gbinder_local_reply_unref(reply);
return G_SOURCE_REMOVE;
}
static
GBinderLocalReply*
test_transact_async_proc(
GBinderLocalObject* obj,
GBinderRemoteRequest* req,
guint code,
guint flags,
int* status,
void* loop)
{
TestTransactAsyncReq* test = g_new(TestTransactAsyncReq, 1);
GVERBOSE_("\"%s\" %u", gbinder_remote_request_interface(req), code);
g_assert(!flags);
g_assert(gbinder_remote_request_sender_pid(req) == getpid());
g_assert(gbinder_remote_request_sender_euid(req) == geteuid());
g_assert(!g_strcmp0(gbinder_remote_request_interface(req), "test"));
g_assert(!g_strcmp0(gbinder_remote_request_read_string8(req), "message"));
g_assert(code == 1);
test->obj = gbinder_local_object_ref(obj);
test->req = gbinder_remote_request_ref(req);
test->loop = (GMainLoop*)loop;
gbinder_remote_request_block(req);
gbinder_remote_request_block(req); /* wrong state; has no effect */
g_idle_add_full(G_PRIORITY_DEFAULT_IDLE, test_transact_async_reply, test,
test_transact_async_done);
return NULL;
}
static
void
test_transact_async(
void)
{
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL);
const GBinderIo* io = gbinder_driver_io(ipc->driver);
const int fd = gbinder_driver_fd(ipc->driver);
const char* dev = gbinder_driver_dev(ipc->driver);
const GBinderRpcProtocol* prot = gbinder_rpc_protocol_for_device(dev);
GMainLoop* loop = g_main_loop_new(NULL, FALSE);
GBinderLocalObject* obj = gbinder_ipc_new_local_object
(ipc, "test", test_transact_async_proc, loop);
GBinderLocalRequest* req = gbinder_local_request_new(io, NULL);
GBinderOutputData* data;
GBinderWriter writer;
gbinder_local_request_init_writer(req, &writer);
prot->write_rpc_header(&writer, "test");
gbinder_writer_append_string8(&writer, "message");
data = gbinder_local_request_data(req);
test_binder_br_transaction(fd, obj, 1, data->bytes);
test_run(&test_opt, loop);
/* Now we need to wait until GBinderIpc is destroyed */
GDEBUG("waiting for GBinderIpc to get destroyed");
g_object_weak_ref(G_OBJECT(ipc), test_quit_when_destroyed, loop);
gbinder_local_object_unref(obj);
gbinder_local_request_unref(req);
g_idle_add(test_unref_ipc, ipc);
test_run(&test_opt, loop);
g_main_loop_unref(loop);
}
/*==========================================================================*
* transact_async_sync
*==========================================================================*/
static
GBinderLocalReply*
test_transact_async_sync_proc(
GBinderLocalObject* obj,
GBinderRemoteRequest* req,
guint code,
guint flags,
int* status,
void* loop)
{
GBinderLocalReply* reply = gbinder_local_object_new_reply(obj);
GVERBOSE_("\"%s\" %u", gbinder_remote_request_interface(req), code);
g_assert(!flags);
g_assert(gbinder_remote_request_sender_pid(req) == getpid());
g_assert(gbinder_remote_request_sender_euid(req) == geteuid());
g_assert(!g_strcmp0(gbinder_remote_request_interface(req), "test"));
g_assert(!g_strcmp0(gbinder_remote_request_read_string8(req), "message"));
g_assert(code == 1);
/* Block and immediately complete the call */
gbinder_remote_request_block(req);
gbinder_remote_request_complete(req, reply, 0);
gbinder_remote_request_complete(req, reply, 0); /* This one is ignored */
gbinder_local_reply_unref(reply);
test_quit_later((GMainLoop*)loop);
return NULL;
}
static
void
test_transact_async_sync(
void)
{
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL);
const GBinderIo* io = gbinder_driver_io(ipc->driver);
const int fd = gbinder_driver_fd(ipc->driver);
const char* dev = gbinder_driver_dev(ipc->driver);
const GBinderRpcProtocol* prot = gbinder_rpc_protocol_for_device(dev);
GMainLoop* loop = g_main_loop_new(NULL, FALSE);
GBinderLocalObject* obj = gbinder_ipc_new_local_object
(ipc, "test", test_transact_async_sync_proc, loop);
GBinderLocalRequest* req = gbinder_local_request_new(io, NULL);
GBinderOutputData* data;
GBinderWriter writer;
gbinder_local_request_init_writer(req, &writer);
prot->write_rpc_header(&writer, "test");
gbinder_writer_append_string8(&writer, "message");
data = gbinder_local_request_data(req);
test_binder_br_transaction(fd, obj, 1, data->bytes);
test_run(&test_opt, loop);
/* Now we need to wait until GBinderIpc is destroyed */
GDEBUG("waiting for GBinderIpc to get destroyed");
g_object_weak_ref(G_OBJECT(ipc), test_quit_when_destroyed, loop);
gbinder_local_object_unref(obj);
gbinder_local_request_unref(req);
g_idle_add(test_unref_ipc, ipc);
test_run(&test_opt, loop);
g_main_loop_unref(loop);
@@ -726,26 +900,28 @@ test_transact_status_reply(
*==========================================================================*/
#define TEST_PREFIX "/ipc/"
#define TEST_(t) TEST_PREFIX t
int main(int argc, char* argv[])
{
g_test_init(&argc, &argv, NULL);
g_test_add_func(TEST_PREFIX "null", test_null);
g_test_add_func(TEST_PREFIX "basic", test_basic);
g_test_add_func(TEST_PREFIX "sync_oneway", test_sync_oneway);
g_test_add_func(TEST_PREFIX "sync_reply_ok", test_sync_reply_ok);
g_test_add_func(TEST_PREFIX "sync_reply_error", test_sync_reply_error);
g_test_add_func(TEST_PREFIX "transact_ok", test_transact_ok);
g_test_add_func(TEST_PREFIX "transact_dead", test_transact_dead);
g_test_add_func(TEST_PREFIX "transact_failed", test_transact_failed);
g_test_add_func(TEST_PREFIX "transact_status", test_transact_status);
g_test_add_func(TEST_PREFIX "transact_custom", test_transact_custom);
g_test_add_func(TEST_PREFIX "transact_custom2", test_transact_custom2);
g_test_add_func(TEST_PREFIX "transact_cancel", test_transact_cancel);
g_test_add_func(TEST_PREFIX "transact_cancel2", test_transact_cancel2);
g_test_add_func(TEST_PREFIX "transact_incoming", test_transact_incoming);
g_test_add_func(TEST_PREFIX "transact_status_reply",
test_transact_status_reply);
g_test_add_func(TEST_("null"), test_null);
g_test_add_func(TEST_("basic"), test_basic);
g_test_add_func(TEST_("sync_oneway"), test_sync_oneway);
g_test_add_func(TEST_("sync_reply_ok"), test_sync_reply_ok);
g_test_add_func(TEST_("sync_reply_error"), test_sync_reply_error);
g_test_add_func(TEST_("transact_ok"), test_transact_ok);
g_test_add_func(TEST_("transact_dead"), test_transact_dead);
g_test_add_func(TEST_("transact_failed"), test_transact_failed);
g_test_add_func(TEST_("transact_status"), test_transact_status);
g_test_add_func(TEST_("transact_custom"), test_transact_custom);
g_test_add_func(TEST_("transact_custom2"), test_transact_custom2);
g_test_add_func(TEST_("transact_cancel"), test_transact_cancel);
g_test_add_func(TEST_("transact_cancel2"), test_transact_cancel2);
g_test_add_func(TEST_("transact_incoming"), test_transact_incoming);
g_test_add_func(TEST_("transact_status_reply"), test_transact_status_reply);
g_test_add_func(TEST_("transact_async"), test_transact_async);
g_test_add_func(TEST_("transact_async_sync"), test_transact_async_sync);
test_init(&test_opt, argc, argv);
return g_test_run();
}

View File

@@ -73,6 +73,8 @@ test_null(
gbinder_remote_request_unref(NULL);
gbinder_remote_request_set_data(NULL, 0, NULL);
gbinder_remote_request_init_reader(NULL, &reader);
gbinder_remote_request_block(NULL);
gbinder_remote_request_complete(NULL, NULL, 0);
g_assert(gbinder_reader_at_end(&reader));
g_assert(!gbinder_remote_request_interface(NULL));
g_assert(!gbinder_remote_request_copy_to_local(NULL));
@@ -100,6 +102,10 @@ test_basic(
GBinderRemoteRequest* req = gbinder_remote_request_new(NULL,
gbinder_rpc_protocol_for_device(NULL), 0, 0);
/* These two calls are wrong but won't cause problems: */
gbinder_remote_request_block(req);
gbinder_remote_request_complete(req, NULL, 0);
gbinder_remote_request_init_reader(req, &reader);
g_assert(gbinder_reader_at_end(&reader));
g_assert(!gbinder_remote_request_interface(req));