Методы, помеченные как oneway
, не блокируются. Для методов, не помеченных как oneway
, вызов метода клиента будет блокироваться до тех пор, пока сервер не завершит выполнение или не вызовет синхронный обратный вызов (в зависимости от того, что произойдет раньше). Реализации серверных методов могут вызывать не более одного синхронного обратного вызова; дополнительные вызовы обратного вызова отбрасываются и регистрируются как ошибки. Если метод должен возвращать значения через обратный вызов и не вызывает свой обратный вызов, это регистрируется как ошибка и сообщается клиенту как ошибка транспорта.
Потоки в сквозном режиме
В транзитном режиме большинство вызовов являются синхронными. Однако, чтобы сохранить предполагаемое поведение, при котором oneway
вызовы не блокируют клиент, для каждого процесса создается поток. Подробнее см. в обзоре HIDL .
Потоки в связующих HAL
Для обслуживания входящих вызовов RPC (включая асинхронные обратные вызовы от HAL к пользователям HAL) и уведомлений о смерти с каждым процессом, использующим HIDL, связан пул потоков. Если один процесс реализует несколько интерфейсов HIDL и/или обработчиков уведомлений о смерти, его пул потоков используется всеми ими. Когда процесс получает входящий вызов метода от клиента, он выбирает свободный поток из пула потоков и выполняет вызов в этом потоке. Если свободный поток недоступен, он блокируется до тех пор, пока он не станет доступным.
Если сервер имеет только один поток, то обращения к серверу выполняются по порядку. Сервер с более чем одним потоком может выполнять вызовы не по порядку, даже если у клиента есть только один поток. Однако для данного объекта интерфейса гарантированно упорядочены oneway
вызовы (см. Модель многопоточности сервера ). Для многопоточного сервера с несколькими интерфейсами oneway
вызовы к разным интерфейсам могут обрабатываться одновременно друг с другом или с другими блокирующими вызовами.
Несколько вложенных вызовов будут отправлены в один и тот же поток hwbinder. Например, если процесс (A) выполняет синхронный вызов из потока hwbinder в процесс (B), а затем процесс (B) выполняет синхронный вызов обратно в процесс (A), вызов будет выполнен в исходном потоке hwbinder. в (A), который заблокирован при исходном вызове. Эта оптимизация позволяет иметь однопоточный сервер, способный обрабатывать вложенные вызовы, но не распространяется на случаи, когда вызовы проходят через другую последовательность вызовов IPC. Например, если процесс (B) сделал вызов binder/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
вызовы на одном и том же интерфейсе всегда сериализуются.
Примечание. Мы настоятельно рекомендуем серверным функциям выполнять возврат сразу же после вызова функции обратного вызова.
Например (в С++):
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 will be called, // and the client will resume execution. ... return Void(); // is basically a no-op };
Модель потоков клиентов
Модель потоков на клиенте различается между неблокирующими вызовами (функциями, отмеченными ключевым словом oneway
) и блокирующими вызовами (функциями, для которых не указано ключевое слово oneway
).
Блокировка звонков
При блокировке вызовов клиент блокируется до тех пор, пока не произойдет одно из следующих событий:
- Возникает транспортная ошибка; объект
Return
содержит состояние ошибки, которое можно получить с помощьюReturn::isOk()
. - Реализация сервера вызывает обратный вызов (если он был).
- Реализация сервера возвращает значение (если не было параметра обратного вызова).
В случае успеха функция обратного вызова, которую клиент передает в качестве аргумента, всегда вызывается сервером до возврата самой функции. Обратный вызов выполняется в том же потоке, в котором выполняется вызов функции, поэтому разработчики должны быть осторожны с удержанием блокировок во время вызовов функций (и вообще избегать их, когда это возможно). Функция без оператора generates
или ключевого слова oneway
по-прежнему блокируется; клиент блокируется до тех пор, пока сервер не вернет объект Return<void>
.
Односторонние звонки
Когда функция помечена oneway
, клиент немедленно возвращается и не ждет, пока сервер завершит вызов функции. На поверхности (и в совокупности) это означает, что вызов функции занимает половину времени, потому что он выполняет половину кода, но при написании реализаций, чувствительных к производительности, это имеет некоторые последствия для планирования. Обычно при использовании одностороннего вызова вызывающий процесс продолжает оставаться запланированным, тогда как при использовании обычного синхронного вызова планировщик немедленно переключается с вызывающего процесса на вызываемый. Это оптимизация производительности в связующем. Для служб, где односторонний вызов должен выполняться в целевом процессе с высоким приоритетом, политика планирования принимающей службы может быть изменена. В C++ использование метода libhidltransport
setMinSchedulerPolicy
с приоритетами и политиками планировщика, определенными в sched.h
, гарантирует, что все вызовы службы выполняются, по крайней мере, с заданной политикой планирования и приоритетом.