Разделяемая память в FPC (Free Pascal)
Источник: http://freepascal.ru/
Проблема
При использовании динамических библиотек приходится соблюдать ограничение на типы данных, которые могут передаваться в параметрах процедур и функций. Если попытаться активно передавать динамические массивы и/или длинные строки (тип ansistring), то рано или поздно возникнет AV - ошибка 216. Кроме того, приходится следить, чтобы процедуры выделения/освобождения динамической памяти вызывались из одного и того же модуля.
При написании сложной многомодульной программы эти ограничения весьма неприятны.
Причины
Дело в том, что Free Pascal для распределения динамической памяти использует не функции операционной системы, а свои собственные, что позволяет значительно ускорить процесс. Однако это приводит к тому, что данные о состоянии памяти, выделенных и свободных блоках и т.д. хранятся в глобальных переменных исполняемого модуля. Таким образом, пул памяти динамической библиотеки отделен от пула памяти вызывающего ее приложения. А длинные строки и динамические массивы как раз и используют (неявно) динамическую память.
Соответственно, чтобы избавиться от нашей проблемы следует заставить менеджер памяти всех исполняемых модулей работать с одними глобальными данными.
Решение
К счастью, Free Pascal позволяет программисту заменить стандартный менеджер памяти любым другим. Для этого используется процедура SetMemoryManager().
Итак, чтобы все модули работали с одним пулом динамической памяти следует выделить один, который за эту память будет отвечать, а в остальных на него сослаться. Конкретно это делается так: пишется библиотека, которая экспортирует функцию, возвращающую ее менеджер памяти; пишется модуль, который в секции инициализации устанавливает этот менеджер памяти в качестве своего; во всех исполняемых файлах большого проекта используется данный модуль. При этом важно, чтобы в первичном файле (том, который начинается с program или library), модуль подключения был первым в списке uses - это нужно для того, чтобы менеджер памяти был установлен до инициализации прочих модулей.
Реализуется это просто:
Библиотека
{$IFDEF WIN32} {$APPTYPE GUI} {$ENDIF WIN32} {$MODE OBJFPC} {$SMARTLINK OFF} library fpMem; procedure GetMemMan (out MemMan : TMemoryManager); stdcall; export; begin GetMemoryManager (MemMan) end; exports GetMemMan name 'GetSharedMemoryManager'; end. Модуль подключения {$MODE OBJFPC} {$SMARTLINK OFF} unit SharedMemory; interface implementation procedure GetMemMan (out MemMan : TMemoryManager); stdcall; external 'fpmem' name 'GetSharedMemoryManager'; var MemMan : TMemoryManager; initialization GetMemMan (MemMan); SetMemoryManager (MemMan) end.
Примечание
Надо заметить, что таким образом мы сделали общим только менеджер динамической памяти. Структура классов, обработка исключений и т.д. по прежнему остаются раздельными.