Spec-Zone.ru › Haskell 9

System.Mem.Weak

Авторские права (c) Университет Глазго, 2001
Лицензия В стиле BSD (см. файл libraries/base/LICENSE)
Сопровождающий libraries@haskell.org
Стабильность стабильный
Переносимость непереносимый
Безопасный Haskell Доверенный
Язык Haskell2010

Содержание

  • Тип Weak
  • Общий интерфейс
  • Специализированные версии
  • Обработка исключений
  • Точная семантика
  • Примечания по реализации

Описание

В общем случае слабый указатель — это ссылка на объект, за которой сборщик мусора не следует, то есть наличие слабого указателя на объект не влияет на время жизни этого объекта. Слабый указатель можно разыменовать, чтобы узнать, существует ли ещё объект, на который он ссылается, и, если да, получить сам объект.

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

Таким образом, кажется, что таблица мемоизации будет вечно удерживать все свои значения в памяти. Один из способов решить эту проблему — периодически очищать таблицу, удаляя записи, ключи которых больше не существуют.

Слабые указатели в этом модуле поддерживают другой подход, называемый финализацией. Когда ключ, на который ссылается слабый указатель, перестаёт существовать, менеджер памяти запускает финализатор, заданный программистом. Например, в случае таблиц мемоизации финализатор может удалить пару «ключ/значение» из таблицы.

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

Подробное обсуждение слабых указателей см. в статье Stretching the storage manager: weak pointers and stable names in Haskell Саймона Пейтона Джонса, Саймона Марлоу и Конала Эллиотта. Реализацию таблицы мемоизации со слабыми указателями можно найти в пакете stable-memo.

Тип Weak

data Weak v Исходный код

Объект слабого указателя с ключом и значением. Значение имеет тип v.

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

У слабого указателя также может быть финализатор типа IO (); в этом случае финализатор будет запущен не более одного раза — после того, как программа потеряет возможность достичь ключа («ключ умрёт»). Менеджер памяти старается запустить финализатор(ы) вскоре после уничтожения объекта, но своевременный запуск не гарантируется.

Не гарантируется, что финализатор будет запущен в конечном итоге, и при завершении программы не предпринимается попыток запустить оставшиеся финализаторы. Поэтому не следует полагаться на финализаторы при освобождении ресурсов — нужно использовать другие методы (например, обработчики исключений), при необходимости дополняя их финализаторами.

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

Финализатор и связь между ключом и значением существуют независимо от того, сохраняет ли программа ссылку на объект Weak.

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

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

ПРЕДУПРЕЖДЕНИЕ: слабые указатели на обычные непримитивные типы Haskell особенно ненадёжны, поскольку компилятор может оптимизировать или дублировать базовую структуру данных. Поэтому финализатор, назначенный обычному типу Haskell, может запуститься раньше, чем вы ожидали. Это не проблема для кэшей и таблиц мемоизации, где ранняя финализация не причиняет вреда.

Финализаторы можно надёжно использовать для типов, создаваемых явно и имеющих идентичность, например IORef, MVar и TVar. Однако для назначения финализатора одному из этих типов следует использовать специальную операцию, предоставленную для этого типа, например mkWeakIORef, mkWeakMVar и mkWeakTVar соответственно. Эти операции назначают финализатор примитивному объекту внутри обёртки (например, MutVar# в случае IORef), поскольку назначение финализатора самой обёртке не сработает, если компилятор оптимизирует внешнюю обёртку.

Общий интерфейс

mkWeak Исходный код

Аргументы

:: k

ключ

-> v

значение

-> Maybe (IO ())

финализатор

-> IO (Weak v)

возвращает: объект слабого указателя

Создаёт слабый указатель на k со значением v и финализатором.

Это наиболее общий интерфейс для создания слабого указателя.

deRefWeak :: Weak v -> IO (Maybe v) Исходный код

Разыменовывает слабый указатель. Если ключ ещё существует, возвращается Just v (где v — это значение в слабом указателе), в противном случае возвращается Nothing.

Возвращаемое значение deRefWeak зависит от того, когда запустится сборщик мусора, поэтому функция выполняется в монаде IO.

finalize :: Weak v -> IO () Исходный код

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

Специализированные версии

mkWeakPtr :: k -> Maybe (IO ()) -> IO (Weak k) Исходный код

Специализированная версия mkWeak, в которой ключ и значение — один и тот же объект:

mkWeakPtr key finalizer = mkWeak key key finalizer

addFinalizer :: key -> IO () -> IO () Исходный код

Специализированная версия mkWeakPtr, в которой возвращаемый объект Weak просто отбрасывается (однако сборщик мусора запомнит финализатор и всё равно запустит его, когда ключ станет недостижимым).

Примечание: добавление финализатора к ForeignPtr с помощью addFinalizer не сработает; вместо этого используйте специализированную версию addForeignPtrFinalizer. Обсуждение см. в описании типа Weak. .

mkWeakPair :: k -> v -> Maybe (IO ()) -> IO (Weak (k, v)) Исходный код

Специализированная версия mkWeak, в которой значением фактически является пара из ключа и значения, переданных в mkWeakPair:

mkWeakPair key val finalizer = mkWeak key (key,val) finalizer

Преимущество этого подхода в том, что ключ можно получить с помощью deRefWeak наряду со значением.

Обработка исключений

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

setFinalizerExceptionHandler :: (SomeException -> IO ()) -> IO () Исходный код

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

Начиная с: base-4.18.0.0

getFinalizerExceptionHandler :: IO (SomeException -> IO ()) Исходный код

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

Начиная с: base-4.18.0.0

printToHandleFinalizerExceptionHandler :: Handle -> SomeException -> IO () Исходный код

Обработчик исключений при финализации Handle, который выводит ошибку в заданный Handle, но не выбрасывает её повторно.

Начиная с: base-4.18.0.0

Точная семантика

Приведённого выше неформального описания достаточно для простых случаев, но в более сложных ситуациях всё может запутаться. В частности, необходимо точно определить, когда ключ перестаёт существовать, чтобы можно было финализировать все слабые указатели, ссылающиеся на него. Допустим, например, значение одного слабого указателя ссылается на ключ другого. Удерживает ли это ключ в памяти?

Поведение просто:

  • Если слабый указатель (объект) ссылается на недостижимый ключ, он может быть финализирован.
  • Финализация означает: (a) обеспечить, чтобы последующие вызовы deRefWeak возвращали Nothing; и (b) запустить финализатор.

Это поведение зависит от того, что означает достижимость ключа. В общих чертах, объект достижим, если до него можно добраться, следуя обычным указателям от корневого набора, но не следуя слабым указателям. Определим достижимость точнее.

Объект кучи является достижимым, если:

  • Он входит в корневой набор.
  • На него напрямую указывает достижимый объект, не являющийся объектом слабого указателя.
  • Он является объектом слабого указателя, ключ которого достижим.
  • Он является значением или финализатором объекта слабого указателя, ключ которого достижим.

Примечания по реализации

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

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

Если финализатор выбрасывает исключение, оно молча перехватывается без уведомления. Подробности см. в коммите к проблеме 13167. Не рекомендуется писать финализаторы, выбрасывающие исключения.

© The University of Glasgow and others
Licensed under a BSD-style license (see top of the page).
https://downloads.haskell.org/~ghc/9.14.1/docs/libraries/base-4.22.0.0-66f8/System-Mem-Weak.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API