Setiap proc di Odin menerima satu nilai yang tidak pernah ditulis pada daftar parameternya, yaitu context yang memuat alokator beserta fasilitas pelengkapnya. Topik ini membahas isi struct Context, cara context diwariskan dari pemanggil ke proc yang dipanggil, cara mengganti alokator untuk satu lingkup tertentu, serta cara memulihkan context setelah lingkup selesai. Setelah topik ini, pengiriman alokator antar proc tidak lagi dilakukan melalui parameter, melainkan melalui sistem context Odin.
Isi struct Context dan warisan context pada proc#
Struct Context pada paket base:runtime adalah wadah bagi fasilitas yang dibutuhkan hampir semua proc. Field terpentingnya adalah allocator, yaitu nilai bertipe Allocator yang menyediakan dan melepaskan memori, dan temp_allocator, yaitu alokator kedua untuk memori sementara. Tabel berikut merangkum field yang akan sering disentuh sepanjang modul ini.
| Field | Tipe | Peran |
|---|---|---|
allocator | Allocator | Sumber memori utama untuk make, new, dan pustaka core |
temp_allocator | Allocator | Penyimpanan scratch, yakni area kerja sementara berumur sangat pendek |
logger | Logger | Tujuan pesan log dari pustaka yang melapor |
user_ptr, user_index | rawptr, int | Ruang bebas milik aplikasi |
Warisan context bekerja pada tingkat pemanggilan proc. Proc yang dipanggil menerima salinan context pemanggilnya, sehingga make di dalam proc bantu memakai alokator yang sedang dipasang oleh pemanggil pada saat itu. Perubahan terhadap context di dalam proc hanya berlaku di proc itu karena yang diterima adalah salinan nilai. Contoh berikut menunjukkan bahwa perubahan context.user_index di proc bantu tidak merambat kembali ke main.
// tidak-dijalankan
ubah_context :: proc() {
context.user_index = 42
}
main :: proc() {
context.user_index = 7
ubah_context()
// context.user_index di main tetap bernilai 7
}Alur context dari main ke proc bantu#
Diagram berikut menggambarkan alur context ketika main memasang alokator arena lalu memanggil proc bantu. Alokasi di proc bantu mendarat pada arena milik pemanggil, dan pemulihan context mengembalikan alokator ke nilai semula.
Program berikut membuktikan alur itu dengan angka. Proc simpan_pembacaan memanggil make tanpa parameter alokator apa pun, tetapi alokasinya tetap mendarat di arena karena context pemanggil ikut mengalir. Offset arena naik dari 0 menjadi 16, yaitu ukuran empat nilai f32.
package main
import "base:runtime"
import "core:fmt"
import "core:mem"
// proc bantu tidak menerima parameter alokator apa pun
// allocator mengalir masuk bersama context pemanggilnya
simpan_pembacaan :: proc(nilai: f32) {
rekaman := make([]f32, 4)
rekaman[0] = nilai
}
main :: proc() {
fmt.println("alokator bawaan adalah heap runtime:", context.allocator.procedure == runtime.default_allocator_proc)
arena: mem.Arena
mem.arena_init(&arena, make([]byte, mem.Kilobyte))
// simpan context sebagai salinan untuk pemulihan
ctx_awal := context
context.allocator = mem.arena_allocator(&arena)
fmt.println("offset arena sebelum proc bantu berjalan:", arena.offset)
simpan_pembacaan(28.4)
fmt.println("offset arena sesudah proc bantu berjalan:", arena.offset)
// pulihkan context agar alokasi berikutnya kembali ke alokator semula
context = ctx_awal
fmt.println("offset arena tidak berubah lagi:", arena.offset)
}# sumberkan lingkungan sandbox jalur odin bila belum tersedia
[ -f /home/kal/my-project/tools/env.sh ] && source /home/kal/my-project/tools/env.sh
# jalankan program dari dalam folder topik
odin run main.odin -filealokator bawaan adalah heap runtime: true
offset arena sebelum proc bantu berjalan: 0
offset arena sesudah proc bantu berjalan: 16
offset arena tidak berubah lagi: 16Angka pada keluaran menggambarkan mekanismenya secara lengkap. Nilai 16 adalah hasil alokasi empat elemen f32, yaitu 4 byte per elemen, dan nilainya muncul karena make di proc bantu membaca alokator dari salinan context yang dibawa saat pemanggilan. Offset tidak berubah lagi setelah pemulihan karena salinan context milik proc bantu dibuang ketika proc itu selesai, sehingga alokasi setelah lingkup selesai kembali ke alokator semula. Panggilan fmt.println pada bentuk ini tidak mengalokasikan memori dari context.allocator, sehingga satu-satunya perubahan offset berasal dari make di proc bantu.
Mengganti alokator untuk satu lingkup#
Penggantian alokator mengikuti pola tiga langkah yang selalu muncul dalam bentuk yang sama. Pola ini memanfaatkan sifat context sebagai nilai biasa yang dapat disalin.
- Simpan context aktif ke variabel lokal, misalnya
ctx_awal := context, sebagai salinan pemulihan. - Pasang alokator baru pada lingkup yang diinginkan, misalnya
context.allocator = mem.arena_allocator(&arena), lalu jalankan pekerjaan yang seluruh alokasinya ditujukan ke alokator baru. - Pulihkan context dari salinan dengan
context = ctx_awalsetelah pekerjaan selesai.
Pemulihan bisa diserahkan pada defer yang sudah dibahas pada topik defer, sehingga penulisan polanya menjadi satu baris: defer context = ctx_awal tepat setelah menyimpan salinan. Odin juga menyediakan mem.context_from_allocator yang mengembalikan salinan context dengan alokator baru, berguna ketika lingkup baru akan dikirim sebagai argumen ke proc lain.
Mengapa Odin mengalirkan context secara implisit#
Desain ini menghilangkan satu masalah berulang pada bahasa tanpa pengumpul sampah, yaitu menyertakan parameter alokator pada proc mana pun yang mengalokasikan memori. Tanpa sistem context, tanda tangan proc menjadi panjang karena setiap pemanggil harus menyalurkan alokator dari atas ke bawah, dan penambahan satu parameter baru menular ke seluruh rantai pemanggilan. Pada sistem context, alokator diubah satu kali pada batas lingkup, lalu semua proc di dalamnya mewarisinya tanpa perubahan tanda tangan. Proc pustaka core memakai mekanisme yang sama: parameter alokatornya memiliki nilai bawaan context.allocator, sehingga pemanggilan tanpa argumen tambahan sudah benar, sedangkan pemanggilan dengan argumen eksplisit tetap mungkin ketika satu pemanggilan memang butuh alokator berbeda. Proc yang menandai dirinya contextless adalah pengecualian, karena proc semacam itu sengaja tidak menerima context dan hanya boleh memanggil proc contextless pula.
Field user_ptr dan user_index melengkapi desain ini dengan ruang bebas untuk keadaan aplikasi, misalnya pointer ke buffer data yang sedang diproses, sehingga keadaan semacam itu juga tidak perlu variabel global. Karena context disalin per pemanggilan, keadaan yang ditaruh pada kedua field itu aman dari penulisan silang antar proc, dan setiap thread membawa context beserta penyimpanan temp-nya sendiri.
Ringkasan#
- Struct
Contextmemuatallocatordantemp_allocatorserta logger dan ruang milik aplikasi, dan setiap proc menerima salinan context pemanggilnya. makedi dalam proc bantu memakai alokator pemanggil, sehingga alokator tidak perlu dikirim melalui parameter.- Pola penggantian alokator: simpan
ctx_awal := context, pasang alokator baru, lalu pulihkan dengandefer context = ctx_awal. - Perubahan context di dalam proc tidak merambat kembali ke pemanggil karena yang diterima adalah salinan nilai.
Referensi#
- Overview — Odin Documentation — Panduan resmi dengan bagian Implicit context system dan Allocators.
- Paket core:mem — Referensi pustaka Odin
— Dokumentasi paket memori dan alokator, termasuk
Arenadancontext_from_allocator.