Methods marked as oneway
don't block. For methods not marked as
oneway
, a client's method call blocks until the server has
completed execution or called a synchronous callback (whichever comes first).
Server method implementations can call at most one synchronous callback; extra
callback calls are discarded and logged as errors. If a method is supposed to
return values via callback and doesn't call its callback, this is logged as an
error and reported as a transport error to the client.
Threads in passthrough mode
In passthrough mode, most calls are synchronous. However, to preserve the
intended behavior that oneway
calls don't block the client, a
thread is created for each process. For details, see the
HIDL overview.
Threads in binderized HALs
To serve incoming RPC calls (including asynchronous callbacks from HALs to HAL users) and death notifications, a threadpool is associated with each process that uses HIDL. If a single process implements multiple HIDL interfaces and/or death notification handlers, its threadpool is shared between all of them. When a process receives an incoming method call from a client, it picks a free thread from the threadpool and executes the call on that thread. If no free thread is available, it blocks until one is available.
If the server has only one thread, then calls into the server are completed
in order. A server with more than one thread might complete calls out of order
even if the client has only one thread. However, for a given interface object,
oneway
calls are guaranteed to be ordered (see
Server threading model). For a multi-threaded server that
hosts multiple interfaces, oneway
calls to different interfaces
might be processed concurrently with each other or other blocking calls.
Multiple nested calls are sent on the same hwbinder thread. For instance, if a process (A) makes a synchronous call from a hwbinder thread into process (B), and then process (B) makes a synchronous call back into process (A), the call is executed on the original hwbinder thread in (A) which is blocked on the original call. This optimization makes it possible to have a single threaded server able to handle nested calls, but it doesn't extend to cases where the calls travel through another sequence of IPC calls. For instance, if process (B) had made a binder/vndbinder call which called into a process (C) and then process (C) calls back into (A), it can't be served on the original thread in (A).
Server threading model
Except for passthrough mode, server implementations of HIDL interfaces live in a different process than the client and need one or more threads waiting for incoming method calls. These threads are the server's threadpool; the server can decide how many threads it wants running in its threadpool, and can use a threadpool size of one to serialize all calls on its interfaces. If the server has more than one thread in the threadpool, it can receive concurrent incoming calls on any of its interfaces (in C++, this means that shared data must be carefully locked).
One-way calls into the same interface are serialized. If a multi-threaded
client calls method1
and method2
on interface
IFoo
, and method3
on interface IBar
,
method1
and method2
is always serialized, but
method3
can run in parallel with method1
and
method2
.
A single client thread of execution can cause concurrent execution on a server with multiple threads in two ways:
oneway
calls don't block. If aoneway
call is executed and then a non-oneway
is called, the server can execute theoneway
call and the non-oneway
call simultaneously.- Server methods that pass data back with synchronous callbacks can unblock the client as soon as the callback is called from the server.
For the second way, any code in the server function that executes after the callback is called can execute concurrently, with the server handling subsequent calls from the client. This includes code in the server function and automatic destructors that execute at the end of the function. If the server has more than one thread in its threadpool, concurrency issues arise even if calls are coming in from only one single client thread. (If any HAL served by a process needs multiple threads, all HALs have multiple threads because the threadpool is shared per-process.)
As soon as the server calls the provided callback, the transport can call the implemented callback on the client and unblock the client. The client proceeds in parallel with whatever the server implementation does after it calls the callback (which might include running destructors). Code in the server function after the callback is no longer blocking the client (as long as the server threadpool has enough threads to handle incoming calls), but might be executed concurrently with future calls from the client (unless the server threadpool has only one thread).
In addition to synchronous callbacks, oneway
calls from a
single-threaded client can be handled concurrently by a server with multiple
threads in its threadpool, but only if those oneway
calls are
executed on different interfaces. oneway
calls on the same
interface are always serialized.
Note: We strongly encourage server functions to return as soon as they have called the callback function.
For example (in C++):
Return<void> someMethod(someMethod_cb _cb) { // Do some processing, then call callback with return data hidl_vec<uint32_t> vec = ... _cb(vec); // At this point, the client's callback is called, // and the client resumes execution. ... return Void(); // is basically a no-op };
Client threading model
The threading model on the client differs between non-blocking calls
(functions that are marked with the oneway
keyword) and blocking
calls (functions that don't have the oneway
keyword specified).
Block calls
For blocking calls, the client blocks until one of the following happens:
- Transport error occurs; the
Return
object contains an error state that can be retrieved withReturn::isOk()
. - Server implementation calls the callback (if there was one).
- Server implementation returns a value (if there was no callback parameter).
In case of success, the callback function the client passes as an argument is
always called by the server before the function itself returns. The callback is
executed on the same thread that the function call is made on, so implementers
must be careful with holding locks during function calls (and avoid them
altogether when possible). A function without a generates
statement
or a oneway
keyword is still blocking; the client blocks until the
server returns a Return<void>
object.
One-way calls
When a function is marked oneway
, the client returns immediately
and doesn't wait for the server to complete its function call invocation. At the
surface (and in aggregate), this means the function call takes half the
time because it is executing half the code, but when writing implementations that
are performance sensitive, this has some scheduling implications. Normally,
using a one-way call causes the caller to continue to be scheduled whereas
using a normal synchronous call causes the scheduler to immediately transfer
from the caller to the callee process. This is a performance optimization in
binder. For services where the one-way call must be executed in the target process
with a high priority, the scheduling policy of the receiving service can be
changed. In C++, using libhidltransport
's method
setMinSchedulerPolicy
with the scheduler priorities and policies
defined in sched.h
ensures that all calls into the service run at
least at the set scheduling policy and priority.