Цепочки моделей

Методы, помеченные как oneway, не блокируют. Для методов, не отмеченных как oneway, вызов метода клиентом блокируется, пока сервер не завершит выполнение или не вызовет синхронный обратный вызов (в зависимости от того, что произойдет раньше). Реализации метода сервера могут вызывать не более одного синхронного обратного вызова. Дополнительные вызовы обратного вызова отбрасываются и регистрируются как ошибки. Если метод должен возвращать значения через обратный вызов, но не вызывает его, это регистрируется как ошибка и сообщается клиенту как ошибка передачи данных.

Цепочки в сквозном режиме

В режиме передачи большинство вызовов синхронные. Однако, чтобы сохранить предполагаемое поведение, при котором вызовы oneway не блокируют клиент, для каждого процесса создается отдельный поток. Подробнее о языке HIDL…

Потоки в HAL с интерфейсом Binder

Для обслуживания входящих вызовов RPC (включая асинхронные обратные вызовы от HAL к пользователям HAL) и уведомлений о завершении работы пул потоков связывается с каждым процессом, использующим HIDL. Если один процесс реализует несколько интерфейсов HIDL и/или обработчиков уведомлений о смерти, его пул потоков используется всеми этими элементами. Когда процесс получает входящий вызов метода от клиента, он выбирает свободный поток из пула потоков и выполняет вызов в этом потоке. Если свободного потока нет, он блокируется до тех пор, пока не появится.

Если у сервера только один поток, вызовы к нему выполняются по порядку. Сервер с несколькими потоками может выполнять вызовы не по порядку, даже если у клиента только один поток. Однако для определенного объекта интерфейса вызовы oneway гарантированно будут упорядочены (см. модель потоков сервера). Для многопоточного сервера, на котором размещено несколько интерфейсов, вызовы oneway к разным интерфейсам могут обрабатываться одновременно друг с другом или другими блокирующими вызовами.

Несколько вложенных вызовов отправляются в одном потоке hwbinder. Например, если процесс А выполняет синхронный вызов из потока hwbinder в процесс Б, а затем процесс Б выполняет синхронный вызов обратно в процесс А, вызов выполняется в исходном потоке hwbinder в процессе А, который заблокирован исходным вызовом. Эта оптимизация позволяет однопоточному серверу обрабатывать вложенные вызовы, но не распространяется на случаи, когда вызовы проходят через другую последовательность вызовов IPC. Например, если процесс Б вызвал процесс В с помощью binder/vndbinder, а затем процесс В вызвал процесс А, то исходный поток в процессе А не может обработать этот вызов.

Модель потоков сервера

За исключением режима сквозной передачи, серверные реализации интерфейсов 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, клиент возвращает результат сразу, не дожидаясь, пока сервер завершит вызов функции. На первый взгляд (и в целом) это означает, что вызов функции занимает вдвое меньше времени, поскольку выполняется вдвое меньше кода, но при написании реализаций, чувствительных к производительности, это имеет некоторые последствия для планирования. Обычно при использовании одностороннего вызова вызывающий абонент продолжает оставаться в расписании, тогда как при использовании обычного синхронного вызова планировщик немедленно переключается с вызывающего абонента на вызываемый процесс. Это оптимизация производительности в binder. Для сервисов, в которых односторонний вызов должен выполняться в целевом процессе с высоким приоритетом, можно изменить правила планирования принимающего сервиса. В C++ использование метода setMinSchedulerPolicy класса libhidltransport с приоритетами и политиками планировщика, определенными в sched.h, гарантирует, что все вызовы сервиса будут выполняться по крайней мере с заданной политикой и приоритетом планирования.