[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:
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -37,6 +37,10 @@
|
||||
|
||||
#include "gbinder_types_p.h"
|
||||
|
||||
struct gbinder_remote_request {
|
||||
GBinderIpcLooperTx* tx;
|
||||
};
|
||||
|
||||
GBinderRemoteRequest*
|
||||
gbinder_remote_request_new(
|
||||
GBinderObjectRegistry* reg,
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
|
||||
Reference in New Issue
Block a user