Методы, помеченные как oneway
не блокируются. Для методов, не помеченных как oneway
, вызов метода клиента блокируется до тех пор, пока сервер не завершит выполнение или не вызовет синхронный обратный вызов (в зависимости от того, что наступит раньше). Реализации серверных методов могут вызывать не более одного синхронного обратного вызова; дополнительные вызовы обратного вызова отбрасываются и регистрируются как ошибки. Если метод должен возвращать значения через обратный вызов и не вызывает свой обратный вызов, это регистрируется как ошибка и сообщается клиенту как ошибка транспорта.
Потоки в транзитном режиме
В режиме транзитной передачи большинство вызовов являются синхронными. Однако, чтобы сохранить запланированное поведение, при котором oneway
вызовы не блокируют клиента, для каждого процесса создается поток. Подробности смотрите в обзоре HIDL .
Потоки в связанных HAL
Для обслуживания входящих вызовов RPC (включая асинхронные обратные вызовы от HAL к пользователям HAL) и уведомлений о смерти с каждым процессом, использующим HIDL, связан пул потоков. Если один процесс реализует несколько интерфейсов HIDL и/или обработчиков уведомлений о смерти, его пул потоков используется всеми из них. Когда процесс получает входящий вызов метода от клиента, он выбирает свободный поток из пула потоков и выполняет вызов в этом потоке. Если свободного потока нет, он блокируется до тех пор, пока он не станет доступным.
Если на сервере есть только один поток, вызовы на сервер выполняются по порядку. Сервер с более чем одним потоком может выполнять вызовы не по порядку, даже если у клиента есть только один поток. Однако для данного объекта интерфейса oneway
вызовы гарантированно будут упорядочены (см. Модель потоков сервера ). Для многопоточного сервера, на котором размещено несколько интерфейсов, oneway
вызовы к различным интерфейсам могут обрабатываться одновременно друг с другом или с другими блокирующими вызовами.
Несколько вложенных вызовов отправляются в одном потоке hwbinder. Например, если процесс (A) выполняет синхронный вызов из потока hwbinder в процесс (B), а затем процесс (B) выполняет синхронный обратный вызов в процесс (A), вызов выполняется в исходном потоке hwbinder в (A) который заблокирован при исходном вызове. Эта оптимизация позволяет иметь однопоточный сервер, способный обрабатывать вложенные вызовы, но она не распространяется на случаи, когда вызовы проходят через другую последовательность вызовов IPC. Например, если процесс (B) выполнил вызов связывателя/vndbinder, который вызвал процесс (C), а затем процесс (C) вызывает обратно в (A), он не может быть обслужен в исходном потоке в (A). .
Модель потоков сервера
За исключением транзитного режима, серверные реализации интерфейсов HIDL находятся в другом процессе, чем клиент, и нуждаются в одном или нескольких потоках, ожидающих входящих вызовов методов. Эти потоки представляют собой пул потоков сервера; сервер может решить, сколько потоков он хочет запустить в своем пуле потоков, и может использовать размер пула потоков, равный единице, для сериализации всех вызовов на своих интерфейсах. Если сервер имеет более одного потока в пуле потоков, он может одновременно принимать входящие вызовы на любом из своих интерфейсов (в C++ это означает, что общие данные должны быть тщательно заблокированы).
Односторонние вызовы в один и тот же интерфейс сериализуются. Если многопоточный клиент вызывает method1
и method2
в интерфейсе IFoo
и method3
в интерфейсе IBar
, method1
и method2
всегда сериализуются, но method3
может выполняться параллельно с method1
и method2
.
Один клиентский поток выполнения может вызвать одновременное выполнение на сервере с несколькими потоками двумя способами:
-
oneway
вызовы не блокируются. Если выполняетсяoneway
вызов, а затем вызываетсяoneway
вызов, сервер может выполнитьoneway
вызов иoneway
вызов одновременно. - Методы сервера, которые передают данные обратно с помощью синхронных обратных вызовов, могут разблокировать клиента, как только обратный вызов будет вызван с сервера.
Во втором случае любой код в серверной функции, который выполняется после вызова обратного вызова, может выполняться одновременно, при этом сервер обрабатывает последующие вызовы от клиента. Сюда входит код функции сервера и автоматические деструкторы, которые выполняются в конце функции. Если сервер имеет более одного потока в своем пуле потоков, проблемы параллелизма возникают, даже если вызовы поступают только из одного клиентского потока. (Если для какого-либо HAL, обслуживаемого процессом, требуется несколько потоков, все HAL имеют несколько потоков, поскольку пул потоков является общим для каждого процесса.)
Как только сервер вызывает предоставленный обратный вызов, транспорт может вызвать реализованный обратный вызов на клиенте и разблокировать клиента. Клиент продолжает действовать параллельно с тем, что делает реализация сервера после вызова обратного вызова (который может включать запуск деструкторов). Код в функции сервера после обратного вызова больше не блокирует клиент (пока в пуле потоков сервера достаточно потоков для обработки входящих вызовов), но может выполняться одновременно с будущими вызовами от клиента (если только пул потоков сервера не имеет только один поток ).
Помимо синхронных обратных вызовов, oneway
вызовы от однопоточного клиента могут обрабатываться одновременно сервером с несколькими потоками в пуле потоков, но только если эти oneway
вызовы выполняются на разных интерфейсах. oneway
вызовы на одном и том же интерфейсе всегда сериализуются.
Примечание. Мы настоятельно рекомендуем серверным функциям возвращаться сразу после вызова функции обратного вызова.
Например (в 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 };
Модель потоков клиента
Модель потоков на клиенте различается между неблокирующими вызовами (функции, отмеченные ключевым словом oneway
) и блокирующими вызовами (функции, для которых не указано ключевое слово oneway
).
Блокировать вызовы
При блокировке вызовов клиент блокируется до тех пор, пока не произойдет одно из следующих событий:
- Возникает ошибка транспорта; Объект
Return
содержит состояние ошибки, которое можно получить с помощьюReturn::isOk()
. - Реализация сервера вызывает обратный вызов (если он был).
- Реализация сервера возвращает значение (если не было параметра обратного вызова).
В случае успеха функция обратного вызова, которую клиент передает в качестве аргумента, всегда вызывается сервером до возврата самой функции. Обратный вызов выполняется в том же потоке, в котором выполняется вызов функции, поэтому разработчики должны быть осторожны с удержанием блокировок во время вызовов функций (и вообще избегать их, когда это возможно). Функция без оператора generates
или oneway
ключевого слова по-прежнему блокируется; клиент блокируется до тех пор, пока сервер не вернет объект Return<void>
.
Односторонние звонки
Когда функция помечена oneway
, клиент немедленно возвращается и не ждет, пока сервер завершит вызов функции. На первый взгляд (и в совокупности) это означает, что вызов функции занимает половину времени, поскольку выполняется половина кода, но при написании реализаций, чувствительных к производительности, это имеет некоторые последствия для планирования. Обычно использование одностороннего вызова приводит к тому, что вызывающий объект продолжает планироваться, тогда как использование обычного синхронного вызова приводит к немедленному переходу планировщика от вызывающего процесса к вызываемому процессу. Это оптимизация производительности в связующем. Для служб, где односторонний вызов должен выполняться в целевом процессе с высоким приоритетом, можно изменить политику планирования принимающей службы. В C++ использование метода setMinSchedulerPolicy
libhidltransport
с приоритетами и политиками планировщика, определенными в sched.h
гарантирует, что все вызовы службы выполняются как минимум с установленной политикой и приоритетом планирования.