Прикладные контексты

Top  Previous  Next

Работа с большинством функций в модуле "Заработная плата" (да и во многих других тоже) требует предварительной инициализации неких структур (открытие таблиц, очистка массивов и т.п.)  Эти действия обычно выполняются при помощи функций-инициализаторов (IntInit, InitWorkingTable, InitServKAU), а очистка и деинициализация - с помощью процедур-финализаторов (ExitProc, DoneWorkingTable, DoneServKAU). Вызовы инициализаторов и финализаторов образуют т.н. "процедурные скобки". Незакрытые процедурные скобки часто приводят к "повисанию" хэндлов таблиц, потере блоков памяти и к тому подобным неприятностям.

Инициализаторы и финализаторы обычно проектируются таким образом, что их повторные вызовы приводят лишь к увеличению (соответственно, к уменьшению) значения внутреннего счетчика вызовов. Таким образом "полезный" код инициализатора отработает только тогда, когда при его вызове значение счетчика перед увеличением равно нулю, финализатора - когда значение счетчика после уменьшения становится равным нулю. Открытие и закрытие процедурных скобок является рутинной работой и требует особого внимания в случаях, когда между последовательными вызовами инициализаторов или финализаторов может произойти какой-либо нефатальный сбой, приводящий к немедленному выходу из процедуры, уже захватившей некоторые контексты при помощи вызовов их инициализаторов, но не успевшей освободить их. Если не предпринять специальных мер, такой незапланированный выход в общем случае приведет к разбалансировке количества вызовов инициализаторов и финализаторов контекстов, что, в свою очередь, повлечет за собой утечку памяти или даже падение приложения.

"Контекстом" называется окружение, необходимое для работы некоторого кода, создаваемое инициализатором и уничтожаемое финализатором . Уникальный идентификатор кода, затребовавшего для себя установки какого-либо контекста, называется сессией этого контекста. Описания инициализированных контекстов и перечни их сессий хранятся в списке контекстов (см. IContextList). Инициализация контекста происходит при создании первой его сессии, финализация - при закрытии последней его сессии. Таким образом, контекст остается открытым до тех пор, пока существует хоть одна его сессия.

С целью уменьшения дублирования кода, упрощения и автоматизации процедуры финализации контекстов их сессии представляются com-интерфейсом (см. IContextSession). Сессия инициализируется в момент добавления прикладным кодом первого нужного ему контекста в список контекстов (см. IContextList.Add). Закрытие сессии и, возможно, финализация ее контекстов происходит в момент, когда внутренний счетчик ссылок на экземпляр класса, реализующего IContextSession, становится равным нулю. Такой подход позволяет прикладному коду закрывать уже открытые контексты простым присваиванием nil переменной-сессии, если эта переменная объявлена как глобальная. Если переменная-сессия объявлена внутри тела процедуры, открывающей сессию с некоторыми контекстами, закрытие сессии произойдет автоматически, в момент выхода потока выполнения за пределы этой процедуры. Другой способ закрыть сессию - явным образом вызвать метод IContextList.Close.

Использование описанного механизма в vip затрудняется невозможностью передачи в/из него ссылок на com-интерфейсы и адресов инициализаторов/финализаторов. Эти проблемы решаются ведением списка экспортированных в vip ссылок на com-интерфейсы и предварительной регистрацией контекстов. Регистрация - это связывание уникального идентификатора строкового типа с точками входа в инициализатор и финализатор контекста (см. RegisterContext). Посредством этих идентификаторов vip может ссылаться на нужные ему контексты.

Сессии контекстов перед экспортом в vip сохраняются в специальном списке (см. IExportedInterfaceList) и преобразуются к типу longint. Список нужен для того, чтобы при экспорте не терялась ссылка на экземпляр класса, представленного этим com-интерфейсом.