#!/usr/bin/ruby# The Santa Claus problem# wzieto w Allen B. Downey "The Little Book of Semaphores" ## Santa Claus spi w swojej pracowni na Polnocnym Biegunie i # moze zostac obudzony tylko jezeli:# (1) Wsystkie 9 reniferow wroca z wakacji# (2) Elfy maja problemy z produkowaniem zabawek# Zeby pozwolic Sant'ie sie wyspac elfy moga obudzic# go tylko jezeli trzy elfa maja problemy. Kiedy# Santa rozwiaze problemy tych 3 elfow i jezeli jacys# inny elfy maja problemy, to musza poczekac az tamci# 3 wroca. Co wiecej jezeli Santa budzac sie zobaczy# jednoczesnie 9 reniferow oraz 3 elfy, to zajmuje sie# reniferami, bo wazne jest dostarczyc prezenty na czas# (renifery maja zwyczaj wracac w ostatniej chwili).# Wracajace renifery czekaja az uzbiera sie ich 9# i tylko pozniej zostaja umieszczone w sanie. Zakladamy# ze jest dowolna ilosc elfow ale tylko 9 reniferow# Semafor licznikowy zaimplementowany uzywajac wait i signal# zrodlo: http://www.imasy.or.jp/~fukumoto/ruby/semaphore.rbclass CountingSemaphore def initialize(initvalue = 0) @counter = initvalue @waiting_list = [] end def wait Thread.critical = true if (@counter -= 1) < 0 @waiting_list.push(Thread.current) Thread.stop end self ensure Thread.critical = false end def signal Thread.critical = true begin if (@counter += 1) <= 0 t = @waiting_list.shift t.wakeup if t end rescue ThreadError retry end self ensure Thread.critical = false end alias down wait alias up signal alias P wait alias V signal def exclusive wait yield ensure signal end alias synchronize exclusiveendSemaphore = CountingSemaphoreelves = 0 # licznik elfowreindeer = 0 # licznik reniferowsanta_sem = Semaphore.new # Santa czeka az 9 reniferow go zasygnalizuje# zmienne globalne musza sie zaczynac od $, inaczej nie sa widoczne w funkcjach# reszta zmiennych nie jest globalna, ale uzywa sie tylko w blokach (do..end, for..end i podobnych)$reindeer_sem = Semaphore.new # renifery czekaja az Santa kaze im isc do sani elf_tex = Semaphore.new(1) # elfy uzywaja tego semaforu zeby nie pozwolic wejsc wiecej niz 3 elfom$help_sem = Semaphore.new # elfy jak dostaja pomocy blokuja sie tutaj, Santa jak im pomoze puszcza ichrequire 'thread'mutex = Mutex.new # chroni dostep do elves i reindeer# renifer jedzie na wakacje # sleep pobiera czas w sekundach ale jako float# rand zwraca wartos 0..1, wiec wakacje trwaja od 0 do 2 sekunddef reindeer_vacation sleep(2.0*rand)end# elf spia i nie potrzebuja pomocydef elf_sleep sleep(2.0*rand)end# Santa wstaje i przygotowuje sanie, puszczajac wszystkich reniferow# renifery jada i rozdaja prezenty (po tej funkcji zakladamy ze juz rozdali)def prepare_sleigh sleep(2.0*rand) for i in 1..9 $reindeer_sem.signal endend# renifery czekaja az Santa ich umiesci w saniedef get_hitched $reindeer_sem.waitend# Santa pomaga trzem elfom i puszcza ichdef help_elves sleep(2.0*rand) $help_sem.signal $help_sem.signal $help_sem.signalend# Elfy czekaja az Santa im pomozedef get_help $help_sem.waitend# Watek Santysanta_thread = Thread.new do while true puts "Santa spi" santa_sem.wait puts "Ktos obudzil Sante" mutex.synchronize do if reindeer == 9 puts "Jest 9 reniferow Santa idzie ich umiescic w sanie" reindeer = 0 prepare_sleigh puts "Santa umiescil 9 reniferow w sanie" elsif elves == 3 puts "Santa idzie pomoc 3 elfom z zabawkami" help_elves puts "Santa skonczyl pomagac elfom z zabawkami" end end endend# Watki reniferowreindeer_threads = []for i in 1..9 reindeer_threads << Thread.new(i) do |nr| while true puts "renifer #{nr} jedzie na wakacje" reindeer_vacation puts "renifer #{nr} przyjechal z wakacje i czeka az Santa go umiesci w sanie" mutex.synchronize do reindeer += 1 if reindeer == 9 santa_sem.signal end end get_hitched end endend# Watki elfownelves = 3elves_threads = []for i in 1..nelves elves_threads << Thread.new(i) do |nr| while true puts "elf #{nr} chce pomocy z zabawkami" elf_tex.wait mutex.synchronize do elves += 1 if elves == 3 puts "jest trzech elfow budzimy Sante" santa_sem.signal else elf_tex.signal end end get_help mutex.synchronize do elves -= 1 if elves == 0 elf_tex.signal end end elf_sleep end endend# W Ruby jesli glowny watek nie ma nic do roboty to nie czeka na inne # watki i umiera, wiec musimy czekac az sie skonczy watek Santy # (a nie skonczy sie nigdy), wiec program tez sie nigdy nie skonczy# chyba ze wcisniemy Ctrl-Csanta_thread.join