Skip to content

Commit f62c12b

Browse files
committed
Добавлена статья про освобождение ресурсов
1 parent 02b88c1 commit f62c12b

File tree

3 files changed

+116
-0
lines changed

3 files changed

+116
-0
lines changed

.vscode/settings.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
"ocamlformat",
88
"ocamlportal",
99
"opam",
10+
"RAII",
1011
"Rpmfile",
1112
"Stdlib",
1213
"struct",

docs/.vitepress/config.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ export default defineConfig({
3535
{ text: "Performance", link: "/performance" },
3636
{ text: "Публикация пакета", link: "/publish-package" },
3737
{ text: "Без Stdlib", link: "/without-stdlib" },
38+
{ text: "Освобождение ресурсов", link: "/dispose-resources" },
39+
3840
],
3941
},
4042
{

docs/recipes/dispose-resources.md

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
# Освобождение ресурсов
2+
3+
Процесс освобождения ресурсов является распространённой задачей: закрыть файловый дескриптор,
4+
закрыть соединение и т.д..
5+
6+
При ручном освобождении ресурсов (она же функция `close`) могут
7+
возникнуть множество проблем — это можно забыть сделать или закрыть слишком рано.
8+
9+
10+
:::details Пример ручного закрытия файла
11+
```ocaml
12+
let read_first_line_from_file filename =
13+
let file = open_in filename in
14+
let line = input_line file in
15+
close_in file;
16+
line
17+
```
18+
:::
19+
20+
> [!INFO] Как это в других языках?
21+
>
22+
> В языке вроде C++ или Rust для это существует нативная поддержка механизма [RAII],
23+
> но он не сильно подходит средам с автоматическим управлением памятью (т.е [GC]).
24+
25+
Далее приведены идиомы и возможные варианты как это делать правильно на OCaml.
26+
27+
## Функция открытия — также функция закрытия
28+
29+
> [!NOTE] Смотрите также
30+
> - [Channels: Безопасная работа](./channels.md#безопасная-работа)
31+
> - [Switches](#switches) — далее
32+
33+
> [!NOTE] Пример
34+
> Абстрактный пример того, как реализуется идиоматичная функции автоматического освобождения
35+
> некоторого ресурса.
36+
> ```ocaml
37+
> let initialize _ = (* ... *)
38+
> let dispose _ = (* ... *)
39+
>
40+
> let with_initialize _ f =
41+
> let resource = initialize _ in
42+
> Fun.protect
43+
> (fun () -> f resource)
44+
> ~finally:(fun () -> dispose resource)
45+
> ```
46+
47+
Данный шаблон вы можете встретить как в стандартной библиотеке, так и в множестве других,
48+
ибо он позволяет правильно завершить код даже в случае исключения внутри callback-функции.
49+
50+
## Освобождение при уничтожении объекта
51+
52+
Используя интерфейс сборщика мусора (модуль [Gc](https://ocaml.org/manual/api/Gc.html)) мы можем установить функцию,
53+
что должна быть выполнена во время освобождения объекта сборщика мусора.
54+
55+
Работает исключительно на heap-allocated объектах!
56+
57+
> [!NOTE] Материалы
58+
> Для этого смотрите функции [`finalise`](https://ocaml.org/manual/5.2/api/Gc.html#VALfinalise) и
59+
> [`alarm`](https://ocaml.org/manual/5.2/api/Gc.html#TYPEalarm).
60+
61+
> [!IMPORTANT] Стоит понимать
62+
> Освобождения ресурса произойдёт только тогда, когда OCaml решит очистить объект,
63+
> а это может не произойти вообще. Смотрите [Memory management](./performance.md#memory-management).
64+
65+
> [!TIP] Библиотека Lwt
66+
> В пакете `lwt.unix` (часть [Lwt](../libraries/concurrency/lwt.md)) есть модуль `Lwt_gc` и функция `finalise_or_exit`, которая гарантирует, что
67+
> при завершении программы ресурс будет освобождён (будет вызвана функция, в которой произойдёт освобождение).
68+
>
69+
> :::details Пример
70+
> ```ocaml
71+
> let main =
72+
> let res = String.make 10 'x' in
73+
> Lwt_gc.finalise_or_exit (Lwt_io.printlf "free '%s'") res;
74+
> Lwt.return_unit
75+
> ```
76+
> :::
77+
78+
## Switches
79+
80+
**Switch** (свитч) — область видимости, к которой привязываются ресурсы, и по завершению которой ресурсы должны будут быть освобождены.
81+
82+
Эдакая прокаченная версия [with-функций](#функция-открытия--также-функция-закрытия) и [finalise из Gc](#освобождение-при-уничтожении-объекта).
83+
84+
Этот паттерн можно особенно ярко встретить в библиотеке [Eio](../libraries/concurrency/eio.md).
85+
Он там повсеместен и без него нельзя ничего сделать, так как ресурс должен быть к чему-то привязан.
86+
87+
> [!NOTE] Пример использования Eio.Switch
88+
> ```ocaml
89+
> let run_client ~net ~addr =
90+
> Switch.run ~name:"client" @@ fun sw ->
91+
> traceln "Client: connecting to server";
92+
> let flow = Eio.Net.connect ~sw net addr in
93+
> (* Read all data until end-of-stream (shutdown): *)
94+
> traceln "Client: received %S" (Eio.Flow.read_all flow)
95+
> ```
96+
97+
> [!NOTE] Пример использования Lwt_switch
98+
> В [Lwt] тоже можно найти свитчи как доп. абстракции — модуль [Lwt_switch].
99+
> ```ocaml
100+
> let main =
101+
> Lwt_switch.with_switch @@ fun sw ->
102+
> let* file = Lwt_io.open_file "some-file" ~mode:Output in
103+
> Lwt_switch.add_hook (Some sw) (fun () -> Lwt_io.close file);
104+
> Lwt.return_unit
105+
> ```
106+
107+
108+
109+
[RAII]: https://ru.wikipedia.org/wiki/%D0%9F%D0%BE%D0%BB%D1%83%D1%87%D0%B5%D0%BD%D0%B8%D0%B5_%D1%80%D0%B5%D1%81%D1%83%D1%80%D1%81%D0%B0_%D0%B5%D1%81%D1%82%D1%8C_%D0%B8%D0%BD%D0%B8%D1%86%D0%B8%D0%B0%D0%BB%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D1%8F
110+
[GC]: https://ru.wikipedia.org/wiki/%D0%A1%D0%B1%D0%BE%D1%80%D0%BA%D0%B0_%D0%BC%D1%83%D1%81%D0%BE%D1%80%D0%B0
111+
112+
[Lwt]: ../libraries/concurrency/lwt.md
113+
[Lwt_switch]: https://ocsigen.org/lwt/latest/api/Lwt_switch

0 commit comments

Comments
 (0)