Наши партнеры

UnixForum





Библиотека сайта rus-linux.net

Фреймворк Telepathy

Глава 20 из книги "Архитектура приложений с открытым исходным кодом", том 1.

Оригинал: Telepathy
Автор: Danielle Madeley
Перевод: Н.Ромоданов

20.4. Роль привязок к языкам программирования

Поскольку фреймворк Telepathy является интерфейсом API для D-Bus, им можно управлять с помощью любого языка программирования, в котором поддерживается работа с шиной D-Bus. Для Telepathy не требуются привязки к языкам программирования, но привязками можно пользоваться с целью удобства в работе.

Языковые привязки можно разделить на две группы: низкоуровневые привязки, в которых есть код, сгенерированный с использованием спецификаций, констант, названий методов, и т.д., и высокоуровневые привязки, написанные программистами для того, чтобы облегчить другим программистам возможность что-либо делать с использованием фреймворка Telepathy. Примерами высокоуровневых привязок являются привязки к GLib и к Qt4. Примерами низкоуровневых привязок являются привязки к языку Python и привязки к языку C для библиотеки libtelepathy, входящей в состав фреймворка; впрочем, привязки к GLib и к Qt4 также относятся к низкоуровневым привязкам.

20.4.1. Асинхронное программирование

В языковых привязках все обращения к методам, с помощью которые осуществляются запросы к шине D-Bus, являются асинхронными: выполняется запрос, а ответ осуществляется через функцию обратного вызова (через callback). Это необходимо, поскольку сама шина D-Bus является асинхронной.

Как и в большинстве случаев программирования сетевых и пользовательских интерфейсов, программирование в шине D-Bus требует использовать цикл обработки событий с тем, чтобы выполнять диспетчеризацию обратных вызовов для входящих сигналов и результатов вызовов методов. Шина D-Bus хорошо интегрируется с главным циклом GLib, используемых в инструментальных наборах GTK+ и Qt.

В языковых привязках шины D-Bus (например, для dbus-glib) предоставляет псевдо-синхронный интерфейс API, в котором главный цикл блокируется до тех пор, пока метод не вернет результат. Когда-то это можно было делать с помощью привязок к API telepathy-glib. К сожалению, использование псевдо-синхронного интерфейса API привело к возникновению проблем и, в конце концов, эта возможность была убрана из telepathy-glib.

Почему не работают псевдо-синхронные обращения к шине D-Bus

Псевдо-синхронный интерфейс, предоставляемый dbus-glib и другими привязками D-Bus, реализован с использованием техники вида «запрос и блокирование». Когда устанавлевается блокировка, только для сокета D-Bus выполняется опрос новых событиях ввода-вывода, а все сообщения шины D-Bus, которые не являются ответом на запрос, помещаются в очередь для последующей обработки.

Это ведет к нескольким серьезным и неизбежным проблемам:

  • Клиентское приложение, которое делает вызов, блокируется в ожидании ответа на запрос. Оно (а также его пользовательский интерфейс, если он имеется), полностью перестанет реагировать. Если запрос требует обращения к сети, то на это уйдет определенное время. Если вызываемый метод будет заблокирован, то тот, кто его вызвал, перестанет реагировать до тех пор, пока не наступит таймаут.

    Использование потоков в данном случае также не будет решением, поскольку это всего лишь еще один способ сделать ваши вызовы асинхронным. Вместо этого вы можете с тем же самым успехом делать асинхронные вызовы, а ответы получать с помощью уже существующего цикла обработки событий.

  • Порядок сообщений может быть изменен. Те сообщения, которые были получены до ожидаемого ответа, будут добавлены в очередь и доставлены в клиентское приложение только после получения ответа.

    Это приводит к проблемам, когда сигнал, оповещающий об изменении состояния (например, что объекта, который должен быть уничтожен), будет получен уже после того, как вызов метода для этого объекта завершится ошибкой (он завершится исключением вида \code{UnknownMethod}). В данной ситуации сложно решить, какое сообщение об ошибке показыать пользователю. В случае же, когда мы сначала получаем сигнал, мы можем отменить приостановленные вызовы методов D-Bus, или проигнорировать их ответы.

  • Два процесса, делающие псевдо-блокирующие вызовы друг-друга, могут попасть в состояние взаимной блокировки (deadloc), причем каждый в своей очереди будет ожидать ответа от другого процесса. Такой сценарий может произойти с процессами, которые оба являются сервисами D-Bus (например, клиентскими программами Telepathy). Диспетчер каналов вызывает методы в этих клиентских приложениях с тем, чтобы назначить им каналы, но клиентские приложения вызывают методы в диспетчере каналов с тем, чтобы запросить открытие новых каналов (или, аналогично, вызывают методы менеджера аккаунтов, который является частью того же самого процесса).

Вызовы методов в первых привязках Telepathy, сгенерированных для языка C, просто пользовались функцией обратного вызова для typedef. Ваша функция обратного вызова просто должна реализовывать сигнатуру того же самого типа.

typedef void (*tp_conn_get_self_handle_reply) (
    DBusGProxy *proxy,
    guint handle,
    GError *error,
    gpointer userdata
);

Эта идея проста, и работает для языка C, поэтому она продолжала использоваться в следующем поколении привязок.

В последние годы был разработан способ, позволяющий использовать такие скриптовые языки, как, например, Javascript и Python, и язык, похожий на язык C# и называющийся Vala, так, чтобы интерфейсами API, базирующимися на Glib/GObject, стало можно пользоваться через инструментальное средство, называемое GObject-Introspection. К сожалению, очень сложно сделать так, чтобы привязать использование обратных вызовов этих типов к другим языкам программирования. Поэтому новые привязки создавались такимим, чтобы они могли использовать возможности асинхронных обратных вызовов, предоставляемыми этими языками и библиотекой GLib.

20.4.2. Готовность объектов

В простом интерфейсе API для шины D-Bus, таком как низкоуровневые привязки фреймворка Telepathy, вы можете начать создавать вызовы методов или принимать сигналы для объекта просто с помощью создания для него прокси-объекта. Это столь же просто, как передать путь к объекту и имя интерфейса и произвести запуск.

Однако в высокоуровневом API фреймворка Telepathy нам нужно, чтобы в прокси-объектах наших объектов было известно, какие есть интерфейсы, нам нужно, чтобы можно было получать описание базовых свойств для данного типа объектов (например, тип канала, цель использования канала и кто был инициатором его открытия), а также нам необходимо определять и отслеживать состояние или статус объекта (например, статус соединения).

Таким образом, для всех прокси-объектов существует понятие готовности. С помощью вызова метода в прокси-объекте, вы можете асинхронно подготовить информацию о состоянии этого объекта и, при этом вы будете уведомлены, когда информаци о состоянии объекта будет подготовлена, а это будет означать, что объект готов к использованию.

Поскольку не все клиентские программы реализованы и не все из них могут нас интересовать, все возможности данного объекта, подготавливаемые для объекта определенного типа, выделяются в ряд допустимых возможностей. В каждом объекте реализуется базовая возможность, в которой будет подготовлена наиболее важная информация об объекте (например, его свойство Interfaces и его начальное состояние), плюс ряд необязательных возможностей для дополнительного состояния, которое может включать в себя дополнительные свойства или возможность отслеживания состояние объекта. Конкретными примерами дополнительных возможностей, которые вы можете подготавливать на различных прокси, являются информация о контакте, свойства объекта, геолокационная информация, состояния чата (например, «Пользователь печатает сообщение...») и аватары пользователей.

Например, в прокси объектах соединений обычно имеется:

  • базовая возможность (core feature), которая позволяют получать статус интерфейса и соединения,
  • возможность получения информации о классах запрашиваемых каналов и поддерживаемых контактах и
  • возможность установки соединение и получения сигнала о его готовности, когда оно будет установлено.

Программист запрашивает объект, который нужно будет подготовить, предоставляя список возможностей, которые ему интересны, и функцию обратного вызова, которая будет вызвана, когда эти возможности станут готовыми. Если все возможности уже подготовлены, то функция обратного вызова может вызываться сразу, в ином случае функция обратного вызова будет вызвана тогда, когда все указанные возможности будут найдены.


Продолжение статьи: Устойчивость к ошибкам.