Статья занудно описывает семантику (ну да, смысл, суть) операций над базами в PalmOS Data Manager. Сие творение было написано как возмущенная реакция на изменения в PalmOS 5.4.9 (начало в этом треде: http://forum.sources.ru/index.php?showtopic=119558 ) . Читать с интонациями писателя Коклюшкина.
В классической 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, которая защищает базу и не позволяет удалять ее. Эта функция не запрещает модифицировать записи базы, но это уже не так принципиально, вряд ли кто-то полезет изменять записи в чужой базе. А вот удалить незащищенную базу можно из любого файл менеджера.
Защита базы напрямую никак не связана с локом записей, ее можно применять и на базе без залоченных записей. Связь между локом и защитой существует на уровне настоятельных рекомендаций
Реализация 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 посмотрели на это безобразие и решили переделать. Все и сразу.
И сделали следующее:
Это кардинальная переделка и плоха и хороша. Плоха тем, что полностью поменяла логику кэша, хороша тем, что сделала его более контролируемым.
Как же надежно защитить свой резидент от превратностей NVFS? Рекомендуется следующий вариант. Он был проверен на программах YAHM и RusHack и показал высокую стабильность:
Инсталляция резидента:
Запрещение резидента: