<setjmp.h> Ne İşe Yarar
Son bir hafta, veya biraz daha uzun bir süre boyunca, akademik bir merak neticesinde deflate formatında sıkıştırma/açma
yapan bir programın nasıl yazılabileceğini araştıyordum. Zlib kütüphanesinin içinde, deflate formatını açmaya yarayan
referans niteliğinde yazılmış, puff.c adında bir C
programı var. Bu programı incelerken, içinde setjmp() ve longjmp() fonksiyonlarını gördüm. Kodlarda, bu fonksiyonların
tanımının setjmp.h içerisinde yapıldığı yazıyordu. Zlib kaynak kodları içerisinde bu header dosyasını bir süre aradıktan
sonra, setjmp.h'nin C standardı içinde olduğunu öğrendim. Şimdiye kadar C dilini iyi kullandığını
vehmetmiş biri olarak, hiç duymadığım bir header dosyasıyla karşılaşmak beni heyecanlandırdı. Hemen internetten araştırmaya
koyuldum.
setjmp.h header dosyası, 3 şey tanımlıyor; setjmp(), longjmp() ve jmp_buf. Bu üçü sayesinde, normal fonksiyon çağırma
ve fonksiyondan değer döndürme mekanizmasının dışına çıkıp, programın başka bir yerine zıplayabiliyoruz. Bunu fonksiyon sınırlarını
aşan goto gibi düşünebiliriz. Okuduğum kaynaklarda 2 çeşit kullanımı var. Birincisi, daha üst seviye dillerde bulunan özel durum (exception)
yönetimini gerçekleştirme, ikincisi de coroutine oluşturmak. Bu yazı özel durumlar üzerine kurulu olacak ama, C dilinde coroutine yazma
konusunda da çok heyecanlıyım, ilerleyen günlerde bu konuda da denemeler yapmak istiyorum.
Uygulamaya geçmeden önce, teorik bilgiyle başlamak istiyorum. setjmp makrosu, argüman olarak jmp_buf adında bir struct
alıyor. İleride longjmp tarafından kullanılmak üzere, geçerli yürütme bağlamını (execution context) jmp_buf tipindeki
değişkene kaydediyor. Geçerli yürütme bağlamından kasıt, setjmp makrosu çalıştırıldığı andaki genel amaçlı register'larla
birlikte, Instruction Pointer, Stack Pointer, EFLAGS gibi register'ların değerleri. Bunun detayları haliyle işlemci
mimarisine göre değişebilir. Bu makronun kullanıcısı olarak bizi ilgilendiren, bu makronun bir zıplama noktası kaydettiği.
Bu makronun dönüş değeri, yürütme bağlamını kaydettiği anda 0, longjmp ile dönüldüğü anda ise, longjmp tarafından ayarlanan
değere eşit oluyor. Bunu anlamak için, bir örneğe başvurmakta fayda var.
Yukarıdaki örneği incelerseniz, setjmp kullanımı daha anlaşılır olur diye düşünüyorum. Dikkat etmeniz gereken
en önemli kısım, setjmp makrosundan birden fazla kez dönüldüğü. İlk çağırıldığında, dönüş değeri 0 olacağı için,
if blogu içerisine girilmeyecek, ve program while döngüsüne girecek. while döngüsü sonsuz bir döngü gibi görünse
de, aslında öyle değil. smemset fonksiyonu, özel durumlarda (bunu exception gibi düşünebiliriz) longjmp fonksiyonunu
kullandığı için, sanki setjmp fonksiyonundan yeni dönülüyormuş gibi, main fonksiyonu içindeki if koşuluna zıplayacak.
Ancak bu sefer, setjmp'nin dönüş değeri longjmp tarafından 1 olarak ayarlandığı için, ifin koşulu sağlanacak ve main
fonksiyonu hata mesajı yazdırıp dönecek. Gözünüzü kısıp, kafanızı biraz eğerek bakarsanız, tıpkı daha yüksek seviye dillerdeki
throw/catch veya raise/catch benzeri bir program davranışı ile karşı karşıyayız.
Yukarıdaki kadar küçük bir programda, setjmp ve longjmp kullanımını anlatmak mümkün olsa da, bu ikisinin
faydasını göstermek pek muhtemel görünmüyor. Ancak, fonksiyon çağrı hiyerarşisinin derinliklerinde oluşan özel
durumların, üst seviye bir fonksiyonda merkezi bir şekilde yönetilmesine imkan sağlaması açısından faydalı görünüyor.
Ayrıca, embedded programlama için de, thread benzeri bir program akışı sağlamanın tek yolu bu olabilir. Bu yazıda
söylemek istediklerim bu kadar, ama, setjmp ile coroutine oluşturma konusunda da ileride birşeyler yazmak istiyorum.