Clause
← Уся документація

Перекладено з англійського оригіналу · 1e3a39c8-dirty · 2026-10-26 · Читати англійською

Процеси

Виконавець

Один runtime виконує свої процеси на своїх робочих потоках планувальника (робочі потоки, модель виконання). Під час запуску виклик вхідної функції стає першим процесом, головним процесом, і виконавець (executor) працює, доки той не завершиться:

Робочі потоки

Виконавець виконує процеси на RuntimeOptions::schedulers робочих потоках: потоці, що запустив програму, і ще одному потоці на кожен додатковий робочий потік. Програми беруть кількість із --schedulers N (від 1 до 1 024, параметри runtime); типово — один робочий потік на логічний процесор, на якому програма може виконуватися (її маска спорідненості, як активні планувальники OTP), або на кожен CPU із --scheduler-cpus.

Пробудження й завершення роботи не потребують додаткового механізму, бо кожна зміна стану планування процесу відбувається під м'ютексом виконавця:

Прив'язка до CPU

Типово робочі потоки не прив'язані: ОС розміщує їх на будь-якому CPU з маски спорідненості програми, як типове +sbt u в OTP. --scheduler-cpus SET (параметри runtime) прив'язує робочий потік N до N-го CPU набору, циклічно, якщо робочих потоків більше, як +sbt/+sct в OTP; потоки таймерів і вводу-виводу лишаються неприв'язаними.

Тайм-аути receive

Таймери живуть в ієрархічному хешованому колесі таймерів (timer wheel), як їх тримає ERTS (scheduler/timer_wheel):

Виходи

Процес, відмінний від головного, завершується з причиною виходу, як в OTP (detail::exit_reason, process/exits). Сигнали виходу передають її зв'язаним процесам (зв'язки), а повідомлення 'DOWN' — процесам-моніторам (монітори); звіти про помилки показують її.

Як завершується процесПричина виходуЗвіт про помилку
Його перший виклик повертає значенняnormalНемає
exit(Reason) (також normal, kill)ReasonНемає
Помилка (error/1,2,3, badarith, {badmatch, V}, undef, ...){Reason, Stack}Так
Неперехоплений throw(Value){{nocatch, Value}, Stack}Так
Його завершує сигнал виходу (сигнали виходу)Причина сигналу, killed для exit(Pid, kill)Немає

Звіт про помилку пишеться в stderr, коли процес завершується, після скидання стандартного виводу, у форматі типового обробника журналу (logger) OTP:

=ERROR REPORT==== 8-Oct-2026::03:42:15.983000 ===
Error in process <0.8.0> with exit value:
{boom,[{crash_reports,'-main/1-fun-6-',0,[]}]}

Заголовок містить місцевий час; причина розкладається так, як це робить ~p. Головний процес такого звіту не пише: його неперехоплений виняток є винятком програми (виконувані файли).

Повідомлення

Dest ! Msg і erlang:send(Dest, Msg) спершу обчислюють Dest і повертають Msg.

DestЕфект
pid живого процесуMsg копіюється пласко в купу отримувача (копіювання між купами), як OTP копіює повідомлення, а велике — у блок, який отримувач перебирає собі (передавання повідомлень), і додається до його вхідної черги сигналів; для отримувача на іншому робочому потоці — у буфер серед його надходжень, який він забере під час наступної перевірки (буфери надходжень)
pid процесу, що завершивсяНічого; надсилання успішне
Зареєстроване ім'яЯк для його pid; badarg, коли жоден живий процес не має цього імені
{Name, nonode@nohost} із двох атомівЯк для pid, зареєстрованого як Name; нічого, коли такого немає
{Name, Node} для будь-якого іншого вузлаНічого (інших вузлів немає)
Посилання (reference)Як для власника активного псевдоніма; інакше нічого
Будь-що іншеbadarg

Буфери надходжень

Надсилання процесу, що виконується на іншому робочому потоці, взагалі не бере блокувань (створення й знищення процесів):

Receive

receive обирає серед своїх клауз, як case, над повідомленнями поштової скриньки:

Зв'язок (link) з'єднує два процеси в обох напрямках (Signals у кожному контексті: зв'язані pid у порядку створення зв'язків і прапорець trap_exit).

Монітори

Монітор односпрямований: процес-монітор тримає його (за посиланням, у Signals), а процес під монітором зберігає посилання й pid монітора, щоб надіслати повідомлення, коли завершиться.

Зареєстровані імена

Виконавець тримає одну таблицю імен (атомів) до pid, а кожен процес — власне ім'я (Signals::name).

Словник процесу

put/2, get/0,1, get_keys/0,1 і erase/0,1 поводяться як в OTP: put/2 і erase/1 повертають попереднє значення або undefined, erase/0 повертає кожну пару {Key, Value} і спорожнює словник, ключі зіставляються за точною рівністю (1 і 1.0 — два ключі).

Таймери

start_timer/3,4 і send_after/3,4 (builtins/erlang/timers.cpp, scheduler/timers.cpp) взводять таймер у колесі виконавця (тому, яке використовують тайм-аути receive), що надсилає Msg або {timeout, TimerRef, Msg} до pid чи зареєстрованого імені, коли спрацьовує.

Інтроспекція

process_info/1,2 (builtins/erlang/introspection.cpp, scheduler/introspection.cpp) читає інший процес під блокуванням виконавця і копіює його терми (повідомлення, словник) у купу того, хто викликає; процес, що виконується на іншому робочому потоці, читається, щойно його квант закінчиться. Pid без живого процесу дає undefined.

Параметри запуску

Джерела: builtins/erlang/spawning.cpp, scheduler/aliases.cpp.

Псевдоніми

Псевдонім (alias) — це посилання, на яке можна надсилати повідомлення (Alias ! Msg): alias/0,1 (explicit_unalias або reply для одного повідомлення) чи монітор, створений з {alias, explicit_unalias | demonitor | reply_demonitor} у monitor/3 (demonitor закінчується разом із монітором, reply_demonitor також завершує монітор на першому повідомленні). unalias/1 завершує псевдонім того, хто викликає. Надсилання до посилання, яке не є активним псевдонімом або власник якого завершився, відкидається. Псевдонім reply споживається лише тоді, коли його повідомлення доставлено, після будь-якого очікування зайнятого власника. {tag, Tag} у monitor/3 замінює 'DOWN' у повідомленні монітора; монітори портів параметрів не приймають.

Вузли

Програма Clause — це один нерозподілений вузол, nonode@nohost, і відповідає як такий вузол OTP (builtins/erlang/node.cpp): nodes/0 дає [], nodes(this | known) — [nonode@nohost], інші типи — []; nodes/2 дає {nonode@nohost, #{connection_id => undefined, node_type => this}} для запитаних ключів. is_alive/0 дає false, monitor_node/2,3 породжують error:notalive, а disconnect_node/1 повертає ignored.

Сигнали виходу

Кожен сигнал виходу надходить від процесу, що виконується (exit/2, exit_signal/2, link/1), або від завершення процесу. Виконавець обробляє його одразу, коли його ціль не виконується на іншому робочому потоці, інакше — щойно квант цілі закінчився (робочі потоки, scheduler/signals): завершена ціль залишає чергу виконання чи своє очікування і доводиться до завершення (сигнали її зв'язкам надіслано, звіт про помилку записано, контекст звільнено) до того, як вбудована функція надсилання поверне керування. Довгий зв'язаний ланцюг завершується процес за процесом без рекурсії.

Сигнал процесуБез перехоплення виходівЗ перехопленням виходів
exit(Pid, kill), exit_signal(Pid, kill)Завершується з причиною killedЗавершується з причиною killed
Причина normal (від зв'язку або надіслана іншому процесу)НічогоПовідомлення {'EXIT', From, normal}
exit(self(), normal)Завершується з причиною normal (особливість OTP)Повідомлення {'EXIT', Self, normal}
exit_signal(self(), normal)НічогоПовідомлення {'EXIT', Self, normal}
Будь-яка інша причина, включно з kill від зв'язкуЗавершується з цією причиноюПовідомлення {'EXIT', From, Reason}

Порти

Порти описано в портах: порт зв'язаний із процесом, що його відкрив, бере участь у зв'язках, моніторах, сигналах виходу й зареєстрованих іменах, як процес, і спілкується зі своїм під'єднаним процесом повідомленнями.

Призупинення процесів

erlang:suspend_process/1,2 і erlang:resume_process/1 (scheduler/suspension.cpp): кожне призупинення — одне утримання цілі у виконавці, тож призупинений процес знову виконується лише після відновлення всіх призупинень від усіх процесів, що його призупинили; повідомлення й сигнали й далі надходять (сигнал виходу й далі завершує його), а process_info(Pid, status) повідомляє suspended. Завершення процесу, що призупинив інші, відновлює його призупинення. Синхронне призупинення процесу на іншому робочому потоці чекає завершення його кванту; asynchronous повертає керування одразу, {asynchronous, Tag} також надсилає тому, хто викликає, {Tag, suspended}, а unless_suspending відповідає false, якщо той, хто викликає, вже призупинив цей процес. Призупинення чи відновлення самого викликача, завершеного процесу, значення, що не є pid, або хибні параметри дають badarg, як і відновлення процесу, якого викликач не призупиняв.

Вбудовані функції

Вбудована функціяПоведінка
spawn(Fun)badarg, якщо Fun не fun; інакше новий процес викликає Fun(), породжуючи в цьому процесі {badarity, {Fun, []}} для іншої арності
spawn(M, F, Args)badarg, якщо M і F не атоми або Args не правильний список; інакше новий процес викликає M:F(Args...), породжуючи в цьому процесі undef, коли жоден модуль її не експортує і немає вбудованої функції з таким ім'ям
spawn_link(Fun), spawn_link(M, F, Args)Як spawn, і новий процес зв'язується з тим, хто викликає (зв'язки)
is_process_alive(Pid)badarg, якщо Pid не pid; true, доки його процес не завершився
spawn_monitor(Fun), spawn_monitor(M, F, Args)Як spawn, повертаючи {Pid, Ref} нового монітора
link(Pid), unlink(Pid)badarg, якщо Pid не pid (зв'язки)
monitor(process, Item)badarg для іншого типу чи елемента; посилання (монітори); Item — це pid або зареєстроване ім'я
register(Name, Pid), unregister(Name), whereis(Name), registered()Див. зареєстровані імена; Name має бути атомом
demonitor(Ref), demonitor(Ref, Options)badarg, якщо Ref не посилання або Options не правильний список із flush та info
exit(Dest, Reason), exit_signal(Dest, Reason)badarg, якщо Dest не pid і не посилання; true після сигналу виходу
process_flag(trap_exit, Bool), process_flag(min_heap_size, N), process_flag(save_calls, N)Попереднє значення; badarg для іншого прапорця, не булевого значення або від'ємного чи нецілого розміру
erlang:send(Dest, Msg), Dest ! MsgMsg після його надсилання (повідомлення); send/2 не імпортується автоматично
spawn(Node, ...), spawn_link(Node, ...), spawn_monitor(Node, ...) (/2, /4)Цей вузол (nonode@nohost): як без Node. Інший вузол: марний pid неактивного вузла, як в OTP, процес, що одразу завершується з причиною noconnection, тож зв'язок чи монітор повідомляє noconnection; badarg, якщо вузол не атом
erlang:send(Dest, Msg, Options)ok або noconnect для повідомлення іншому вузлу з параметром noconnect; параметри nosuspend, noconnect, priority (звичайне повідомлення)
send_nosuspend(Dest, Msg), send_nosuspend(Dest, Msg, [noconnect])true; false для іншого вузла з noconnect
link(Pid, Options), exit(Dest, Reason, Options), exit_signal(Dest, Reason, Options)Як без параметрів; Options — [] або [priority] (звичайний сигнал)
process_flag(Pid, save_calls, N)Попереднє налаштування іншого процесу (0..10000); зберігається, але Clause не зберігає викликів; інші прапорці — badarg
processes_iterator(), processes_next(Iter)Обхід живих процесів: {Pid, Iter1} або none; процес, запущений під час обходу, може з'явитися
bump_reductions(N)true для невід'ємного цілого числа; Clause не рахує редукцій
erlang:suspend_process(Pid), erlang:suspend_process(Pid, Options), erlang:resume_process(Pid)Див. призупинення процесів
erlang:process_display(Pid, backtrace)true після запису стека викликів живого процесу Pid у стандартний потік помилок, по одному Module:Function/Arity на рядок, починаючи з поточної функції; badarg для іншого типу чи завершеного процесу

Новий процес стає в кінець черги робочого потоку свого батька (робочі потоки); spawn одразу повертає його pid.