Table of Contents

Введение

Статья занудно описывает семантику (ну да, смысл, суть) операций над базами в PalmOS Data Manager. Сие творение было написано как возмущенная реакция на изменения в PalmOS 5.4.9 (начало в этом треде: http://forum.sources.ru/index.php?showtopic=119558 ) . Читать с интонациями писателя Коклюшкина.

Классический Data Manager

В классической PalmOS Data Manager располагает свои базы в кучах памяти и реализован как надстройка над Memory Manager. Это приводит к тому, что хэндлы записей можно рассматривать как обычные хэндлы блоков в динамической куче, которые можно лочить, данные которых можно читать итд. Различия между хэндлами из динамической памяти и из хранилища следующие:

Чем плохи эти действия? Тем, что они идут в обход Data Manager и не дают ему возможности изменить, например, размер базы при resize записи с помощью MemHandleResize. В принципе изменить размер можно, но где и когда это аукнется - неизвестно.

Какие операции над записями и ресурсами предлагаются программисту?

Записи

У записей в базе существуют два признака: busy и dirty. Операции доступа к записям изменяют состояние этих битов.

операциясмыслдействия с флагом busyдействия с флагом dirtyдействия, если флаг busy выставлен
DmGetRecordВозвращает хэндл записи для модификациивыставляетне трогаетвозвращает ошибку
DmQueryRecordВозвращает хэндл записи для чтенияне изменяетне изменяетне влияет на функцию
DmReleaseRecordОсвобождает записьсбрасываетвыставляет по параметрутребует выставленного флага

Некоторые неверные действия с этими функциями ловится, а некоторые нет.

Ресурсы

Базы ресурсов не содержат признаков записей, поэтому существует только две функции доступа к записям

операциясмысл
DmGet(1)ResourceВозвращает хэндл записи
DmReleaseResourceОсовобождает хэндл записи

Никакой проверки правильного использования пары функций нет.

Локинг записей и ресурсов

Функции DmGet* возвращают хэндлы. Эти хэндлы можно и нужно лочить для работы с данными. Важно понять, что залоченность этих хэндлов абсолютно безразлична Data Manager. Единственное исключение: после любого сброса функция DmInit разлочивает все записи во всех кучах storage. Все остальное время записи могут быть залочены или разлочены. Освобождение записи или закрытие базы на это не влияет.

Лок ресурса часто применяется программами-резидентами для регистрации процедурного аларма или нотификации. Такой код лочит ресурс code#1 и передает указатель в функцию регистрации. Даже после выхода из программы указатель на обработчик останется валидным.

Защита базы

К сожалению лок записи не спасает от удаления базы. В этом случае у залочившей ресурс программы нет никакой возможности обнаружить, что указатель на ресурс больше не валиден. Для предотвращения такой ситуации в API есть функция DmProtectDatabase, которая защищает базу и не позволяет удалять ее. Эта функция не запрещает модифицировать записи базы, но это уже не так принципиально, вряд ли кто-то полезет изменять записи в чужой базе. А вот удалить незащищенную базу можно из любого файл менеджера.

Защита базы напрямую никак не связана с локом записей, ее можно применять и на базе без залоченных записей. Связь между локом и защитой существует на уровне настоятельных рекомендаций

NVFS. Первая попытка

Реализация Data Manager на базе NVFS отличается от классики тем, что базы больше не хранятся в адресуемой памяти. С первого взгляда кажется, что существующее DM API позволяет сделать прозрачную подзагрузку записей в кэш. Не получилось. Почему?

Записи

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

статусописаниеможет ли быть выгружена при очистке кэша
загружена для модификациизапись загружена функцией DmGetRecordне может
загружена для чтениязапись загружена функцией DmQueryRecord или DmRecordInfoможет. остается хэндл, указывающий на блок размера 0
загружена для повторного использованиязапись после вызова DmReleaseRecord с dirty=====falseможет
грязнаязапись после вызова DmReleaseRecord с dirty=====falseне может
сиротазапись после вызова DmQueryRecord и ужатая до блока размером 0не может

Отметим следующие нюансы.

Как мы видим, в целом все хорошо и предсказуемо. По крайней мере пока.

Ресурсы

Переходя к ресурсам, хочется грязно выругаться. API Resource Manager не позволяет программисту выразить свои намерения также явно, как и в Record API. Наличие опциональной DmReleaseRecord не дает никакой информации о том, была ли запись модифицирована или нет.

Как это делает NVFS? Скорее всего этот уровень отслеживает вызовы DmWrite и неявно выставляет внутренний бит dirty для таких ресурсов.

статусописаниеможет ли быть выгружена при очистке кэша
загруженаресурс загружен функцией DmGet(1)Resourceне может
загружена для повторного использованиязапись после вызова DmReleaseResourceможет
грязнаяизмененная запись после вызова DmReleaseResourceне может

Отметим следующие нюансы.

Локинг

Похоже, что залоченные ресурсы и записи кэш не очищает. Что он с ними делает - неизвестно. На следующие вопросы ответ неизвестен:

Эти вопросы подводят к тому, что надежность программы-резидента на системе с NVFS под большим вопросом.

Защита

Факт защищенности базы никак не влияет на логику кэша. Точка.

NVFS в PalmOS 5.4.9

К чему привела вышеописанная логика?

Горе-разработчики NVFS посмотрели на это безобразие и решили переделать. Все и сразу.

И сделали следующее:

Это кардинальная переделка и плоха и хороша. Плоха тем, что полностью поменяла логику кэша, хороша тем, что сделала его более контролируемым.

Заключение

Как же надежно защитить свой резидент от превратностей NVFS? Рекомендуется следующий вариант. Он был проверен на программах YAHM и RusHack и показал высокую стабильность:

Инсталляция резидента:

Запрещение резидента: