Process yang bekerja sama perlu saling memberi tahu ketika salah satunya berhenti secara tidak wajar, dan BEAM menyediakan dua mekanisme untuk itu, yaitu link dan monitor. Topik ini membahas cara membuat keduanya, bagaimana sinyal exit (exit signal) dan pesan :DOWN dihasilkan, peran Process.flag(:trap_exit, true), serta pertimbangan memilih antara keduanya. Hasil akhirnya adalah kemampuan menangkap kegagalan process lain secara deterministik, lengkap dengan alasan berhentinya.
Link: kegagalan yang menyebar dua arah#
spawn_link/1 membuat process baru sekaligus menghubungkannya dengan process pembuat melalui link, yaitu hubungan dua arah antara dua process. Ketika salah satu process yang terhubung berhenti, BEAM memancarkan exit signal kepada process lainnya. Isi sinyal ini berupa alasan (reason) berhentinya, misalnya atom :normal untuk berhenti yang wajar atau tuple berisi exception untuk berhenti akibat error.
Aturan penularannya bergantung pada alasan tersebut. Exit signal dengan alasan :normal diabaikan oleh process penerima, sehingga berhentinya satu pihak tidak menular. Alasan selain :normal menular sepenuhnya: process penerima ikut berhenti dengan alasan yang sama, dan penularan itu berlanjut ke seluruh process yang terhubung dengannya. Fungsi Process.exit/2 mengirim exit signal dengan alasan pilihan pemanggil ke sebuah process, dan alasan tersebut dapat berupa atom apa pun, misalnya :dibunuh.
Perilaku penularan ini berarti satu process yang melempar error dapat menghentikan sebagian besar program tanpa kesempatan mencatat apa pun. Mekanisme trap_exit pada bagian berikutnya mengubah perilaku itu menjadi pesan yang dapat dibaca.
trap_exit: exit menjadi pesan#
Process.flag(:trap_exit, true) mengubah cara sebuah process menerima exit signal. Alih-alih berhenti, process yang memasang bendera ini mengubah setiap exit signal menjadi pesan biasa berbentuk {:EXIT, pid, alasan}, yang masuk ke mailbox seperti pesan lain. Process tersebut tetap hidup dan bebas memutuskan tindak lanjutnya. Skrip berikut memasang bendera itu di process utama, lalu menguji tiga process terhubung yang berhenti dengan alasan berbeda:
# link.exs — exit signal dari process terhubung ditangkap sebagai pesan {:EXIT, pid, alasan}
Process.flag(:trap_exit, true)
# 1: process terhubung yang selesai dengan normal
spawn_link(fn -> :ok end)
receive do
{:EXIT, _pid, alasan} -> IO.puts("process pertama berhenti, alasan: #{inspect(alasan)}")
after
1000 -> IO.puts("waktu habis")
end
# 2: process terhubung yang dibunuh dengan alasan :dibunuh
sasar =
spawn_link(fn ->
receive do
:menunggu_selamanya -> :ok
after
5000 -> :waktu_habis
end
end)
Process.exit(sasar, :dibunuh)
receive do
{:EXIT, _pid, alasan} -> IO.puts("process kedua berhenti, alasan: #{inspect(alasan)}")
after
1000 -> IO.puts("waktu habis")
end
# 3: process terhubung yang melempar error
spawn_link(fn -> raise "runtime error" end)
receive do
{:EXIT, _pid, {%RuntimeError{message: pesan}, _jejak}} ->
IO.puts("process ketiga berhenti, alasan: error runtime: #{pesan}")
after
1000 -> IO.puts("waktu habis")
endprocess pertama berhenti, alasan: :normal
process kedua berhenti, alasan: :dibunuh
process ketiga berhenti, alasan: error runtime: runtime errorTiga bentuk alasan terlihat sekaligus pada contoh di atas. Process pertama selesai tanpa error sehingga alasannya berupa atom :normal. Process kedua dibunuh melalui Process.exit/2 sehingga alasannya berupa atom :dibunuh. Process ketiga berhenti akibat exception, sehingga alasannya berupa tuple dua elemen yang berisi struct exception dan jejak tumpukan (stacktrace), yaitu daftar posisi kode yang sedang berjalan. Jejak tumpukan memuat alamat berkas dan nomor baris yang panjang, sehingga contoh mencocokkannya dengan pola {%RuntimeError{message: pesan}, _jejak} dan hanya mencetak pesan exceptionnya.
Monitor: pengamatan satu arah#
Monitor menyediakan hubungan satu arah yang tidak menularkan kegagalan. spawn_monitor/1 membuat process baru dan memasang monitor atasnya dalam satu langkah, lalu mengembalikan pasangan {pid, ref}. Ketika process yang dimonitor berhenti, dengan alasan apa pun termasuk :normal, process pemonitor menerima pesan {:DOWN, ref, :process, pid, alasan} tanpa harus memasang trap_exit dan tanpa risiko ikut berhenti. Nilai ref adalah referensi (reference) yang unik, sehingga pesan :DOWN dari beberapa monitor dapat dibedakan dengan mencocokkan ^ref.
Process yang sudah ada juga dapat dimonitor melalui Process.monitor/1. Contoh berikut memonitor process pekerja yang selesai secara normal setelah menerima satu pesan:
pekerja = spawn(fn -> receive do :selesai -> :ok after 5000 -> :waktu_habis end end)
ref = Process.monitor(pekerja)
send(pekerja, :selesai)
receive do
{:DOWN, ^ref, :process, _pid, alasan} -> IO.puts("alasan berhenti: #{inspect(alasan)}")
after
1000 -> IO.puts("waktu habis")
endalasan berhenti: :normalSkrip utuh berikut merangkum kedua skenario monitor dalam satu alur, yaitu process yang berhenti normal dan process yang melempar exception. Karena spawn_monitor/1 mengembalikan pid dan referensi sekaligus, pencocokan ^ref memastikan pesan :DOWN yang dibaca benar-benar berasal dari monitor yang baru dibuat:
# monitor.exs — pesan {:DOWN, ref, :process, pid, alasan} tanpa menghubungkan dua process
# 1: monitor process yang berhenti dengan normal
{_pid1, ref1} = spawn_monitor(fn -> :ok end)
receive do
{:DOWN, ^ref1, :process, _pid, alasan} ->
IO.puts("process pertama berhenti, alasan: #{inspect(alasan)}")
after
1000 -> IO.puts("waktu habis")
end
# 2: monitor process yang melempar error
{_pid2, ref2} = spawn_monitor(fn -> raise "runtime error" end)
receive do
{:DOWN, ^ref2, :process, _pid, {%RuntimeError{message: pesan}, _jejak}} ->
IO.puts("process kedua berhenti, alasan: error runtime: #{pesan}")
after
1000 -> IO.puts("waktu habis")
endprocess pertama berhenti, alasan: :normal
process kedua berhenti, alasan: error runtime: runtime errorMemilih antara link dan monitor#
Kedua mekanisme menyampaikan informasi yang sama, yaitu process lain telah berhenti beserta alasannya, tetapi keduanya dirancang untuk kebutuhan yang berbeda. Tabel berikut merangkum perbedaannya:
| Aspek | Link | Monitor |
|---|---|---|
| Arah hubungan | Dua arah antara dua process | Satu arah, dari pemonitor ke yang dimonitor |
| Dampak kegagalan | Menular ke kedua pihak, kecuali trap_exit dipasang | Tidak menular sama sekali |
| Pesan yang diterima | {:EXIT, pid, alasan} saat trap_exit dipasang | {:DOWN, ref, :process, pid, alasan} |
| Fungsi pembuat | spawn_link/1 atau Process.link/1 | spawn_monitor/1 atau Process.monitor/1 |
| Penggunaan lazim | Process yang harus hidup atau mati bersama | Mengamati pekerjaan process dari luar |
Link adalah pilihan yang tepat ketika dua process membentuk satu kesatuan yang kegagalan salah satunya membuat yang lain tidak berguna, misalnya server dan penyimpanannya. Pohon supervision OTP dibangun di atas kombinasi link dan trap_exit, karena supervisor harus mengetahui setiap kali anaknya berhenti. Monitor adalah pilihan yang tepat ketika pengamat tidak terlibat dalam pekerjaan process yang diamati, misalnya menunggu tugas selesai sambil tetap bebas berhenti sendiri. Task, yang dibahas pada modul Inti OTP, mengandalkan monitor untuk keperluan semacam ini, sehingga pemahaman pesan :DOWN menjadi bekal yang langsung terpakai.
Ringkasan#
- Link menghubungkan dua process secara dua arah melalui
spawn_link/1, dan exit signal dengan alasan selain:normalmenularkan kegagalan ke process yang terhubung. Process.flag(:trap_exit, true)mengubah exit signal menjadi pesan{:EXIT, pid, alasan}, dengan alasan berupa atom seperti:normaldan:dibunuhatau tuple exception dan jejak tumpukan.- Monitor mengamati satu process secara satu arah melalui
spawn_monitor/1atauProcess.monitor/1dan selalu menghasilkan pesan{:DOWN, ref, :process, pid, alasan}tanpa menularkan kegagalan. - Link digunakan untuk process yang hidup dan mati bersama, sedangkan monitor digunakan untuk mengamati pekerjaan dari luar.
Referensi#
- Modul Process — Hexdocs Elixir
— dokumentasi
link,monitor,exit, danflag. - Processes — Hexdocs Elixir — panduan resmi tentang process dan link.
- Processes — Erlang Reference Manual — spesifikasi exit signal dan hubungan antar process pada level Erlang.