System.Mem.Weak
| Copyright | (c) Университет Глазго 2001 |
|---|---|
| License | BSD-стиль (см. файл libraries/base/LICENSE) |
| Maintainer | libraries@haskell.org |
| Stability | экспериментальная |
| Portability | непортабельная |
| Safe Haskell | Надёжная |
| Language | Haskell2010 |
Описание
В общих чертах, слабая ссылка — это ссылка на объект, за которой сборщик мусора не следит — то есть существование слабой ссылки на объект никак не влияет на время жизни этого объекта. Слабая ссылка может быть разыменована, чтобы узнать, жив ли объект, на который она ссылается, и если жив, то вернуть сам объект.
Слабые ссылки особенно полезны для кэшей и таблиц мемоизации. Чтобы создать таблицу мемоизации, нужно построить структуру данных, отображающую аргумент функции (ключ) на её результат (значение). Когда вы применяете функцию к новому аргументу, вы сначала проверяете, существует ли пара «ключ/значение» в таблице мемоизации. Важный момент: сама таблица мемоизации не должна сохранять ключ и значение живыми. Поэтому таблица должна содержать слабую ссылку на ключ, а не обычную ссылку. Ссылка на значение не должна быть слабой, потому что единственная ссылка на значение может исходить именно от таблицы мемоизации.
Таким образом, похоже, что таблица мемоизации будет сохранять все свои значения живыми вечно. Один из способов решения этой проблемы — периодически очищать таблицу, удаляя записи, ключи которых умерли.
Слабые ссылки в этой библиотеке поддерживают другой подход, называемый *финализацией*. Когда ключ, на который указывает слабая ссылка, умирает, менеджер памяти организует выполнение указанного программистом финализатора. Например, в случае таблиц мемоизации финализатор может удалить пару «ключ/значение» из таблицы мемоизации.
Ещё одна сложность с таблицей мемоизации заключается в том, что значение пары «ключ/значение» может само содержать ссылку на ключ. Таким образом, таблица мемоизации сохраняет значение живым, что сохраняет ключ живым, даже если других ссылок на ключ нет, и оба должны умереть. Слабые ссылки в этой библиотеке предоставляют небольшое обобщение базовой идеи слабой ссылки, при котором каждая слабая ссылка фактически содержит и ключ, и значение.
Тип Weak
Объект слабой ссылки с ключом и значением. Значение имеет тип v.
Слабая ссылка выражает отношение между двумя объектами: *ключом* и *значением*: если сборщик мусора считает ключ живым, то значение также живое. Ссылка из значения на ключ *не* сохраняет ключ живым.
Слабая ссылка также может иметь финализатор типа IO (); если он есть, то финализатор будет запущен не более одного раза, в момент, когда ключ станет недоступным для программы («мёртвым»). Менеджер памяти пытается запустить финализатор(ы) объекта вскоре после смерти объекта, но быстродействие не гарантируется.
Не гарантируется, что финализатор в конечном итоге будет запущен, и не предпринимается попыток выполнить оставшиеся финализаторы при завершении программы. Поэтому на финализаторах не следует полагаться для освобождения ресурсов — следует использовать другие методы (например, обработчики исключений), возможно, в дополнение к финализаторам.
Ссылки из финализатора на ключ обрабатываются так же, как ссылки из значения на ключ: они не сохраняют ключ живым. Таким образом, финализатор может возродить ключ, возможно, сохранив его в той же структуре данных.
Финализатор и отношение между ключом и значением существуют независимо от того, сохраняет ли программа ссылку на объект Weak или нет.
Может быть несколько слабых ссылок с одним и тем же ключом. В этом случае финализаторы каждой из этих слабых ссылок будут выполнены в произвольном порядке, или, возможно, одновременно, когда ключ умрёт. Если программист указывает финализатор, предполагающий, что он имеет единственную ссылку на объект (например, файл, который он хочет закрыть), то программист должен убедиться, что существует только один такой финализатор.
Если нет других потоков для выполнения, система выполнения проверит наличие готовых финализаторов, прежде чем объявить систему заблокированной.
ПРЕДУПРЕЖДЕНИЕ: слабые ссылки на обычные не-примитивные типы Haskell особенно хрупкие, потому что компилятор свободен оптимизировать или дублировать лежащую в основе структуру данных. Поэтому попытка размещения финализатора на обычном типе Haskell может привести к тому, что финализатор выполнится раньше, чем ожидалось. Это не проблема для кэшей и таблиц мемоизации, где ранняя финализация не вредна.
Финализаторы *могут* использоваться надёжно для типов, которые создаются явно и имеют идентичность, таких как IORef и MVar. Однако, чтобы разместить финализатор на одном из этих типов, вы должны использовать специальную операцию, предусмотренную для этого типа, например, mkWeakIORef и addMVarFinalizer соответственно (неравномерность — случайность). Эти операции прикрепляют финализатор к примитивному объекту внутри ящика (например, MutVar# в случае IORef), потому что прикрепление финализатора к самому ящику терпит неудачу, когда внешний ящик оптимизируется компилятором.
Общий интерфейс
Аргументы
| :: k | ключ |
| -> v | значение |
| -> Maybe (IO ()) | финализатор |
| -> IO (Weak v) | Возвращаемое значение: объект слабой ссылки |
Создаёт слабую ссылку на k, со значением v и финализатором.
Это наиболее общий интерфейс для создания слабой ссылки.
deRefWeak :: Weak v -> IO (Maybe v) Source
Разыменовывает слабую ссылку. Если ключ всё ещё жив, то возвращается Just v (где v — *значение* в слабой ссылке), иначе возвращается Nothing.
Возвращаемое значение deRefWeak зависит от того, когда выполняется сборщик мусора, поэтому оно находится в монаде IO.
finalize :: Weak v -> IO () Source
Вызывает немедленное выполнение финализатора, связанного со слабой ссылкой.
Специализированные версии
mkWeakPtr :: k -> Maybe (IO ()) -> IO (Weak k) Source
Специализированная версия mkWeak, где ключ и значение — один и тот же объект:
mkWeakPtr key finalizer = mkWeak key key finalizer
addFinalizer :: key -> IO () -> IO () Source
Специализированная версия mkWeakPtr, где объект Weak возвращаемый объект просто отбрасывается (однако финализатор будет запомнен сборщиком мусора и всё равно будет выполнен, когда ключ станет недоступным).
Примечание: добавление финализатора к ForeignPtr с помощью addFinalizer не сработает; используйте специализированную версию addForeignPtrFinalizer вместо этого. Для обсуждения см. тип Weak. .
mkWeakPair :: k -> v -> Maybe (IO ()) -> IO (Weak (k, v)) Source
Специализированная версия mkWeak где значение — фактически пара ключа и значения, переданная mkWeakPair:
mkWeakPair key val finalizer = mkWeak key (key,val) finalizer
Преимущество этого в том, что ключ можно получить с помощью deRefWeak в дополнение к значению.
Точное семантическое описание
Вышеупомянутое неформальное описание подходит для простых случаев, но ситуация может усложняться. В частности, необходимо чётко указать, когда ключ умирает, чтобы любые слабые ссылки, которые на него ссылаются, могли быть финализированы. Например, предположим, что значение одной слабой ссылки ссылается на ключ другой… это сохраняет ли ключ живым?
Поведение просто таково:
- Если слабая ссылка (объект) ссылается на *недоступный* ключ, она может быть финализирована.
- Финализация означает (a) указать, что последующие вызовы
deRefWeakвернутNothing; и (b) выполнить финализатор.
Это поведение зависит от того, что означает «доступный» ключ. Неформально, что-то доступно, если к нему можно добраться, пройдя по обычным ссылкам от корневого набора, но не пройдя по слабым ссылкам. Мы определяем доступность более точно следующим образом.
Объект кучи *доступен*, если:
- Он является членом *корневого набора*.
- Он непосредственно указывается доступным объектом, отличным от объекта слабой ссылки.
- Это объект слабой ссылки, ключ которой доступен.
- Это значение или финализатор объекта слабой ссылки, ключ которого доступен.
© The University of Glasgow and others
Licensed under a BSD-style license (see top of the page).
https://downloads.haskell.org/~ghc/7.10.3/docs/html/libraries/base-4.8.2.0/System-Mem-Weak.html