Ping Ring Contribs


torsdag 23. september 2010 Diverse prosjekter Ping Ring Basic Assembly PHP Haskell Samtidighet

Dette er del 9 i artikkelserien Ping Ring hvor jeg implementerer et og samme program i et utall ulike programmeringsspråk - for å se om det er noe å lære gjennom å gjøre det. Introduksjonen kan du lese her.

Samtidig som jeg startet denne artikkelserien utfordret jeg også folkene på Norsk Freakforum til å implementere den samme oppgaven i språk som jeg selv ikke behersket. Og flere var villige til å bidra. Denne episoden av Ping Ring tar for seg disse implementasjonene.

Programmeringsdelen av Freakforum er et sted hvor man kan gjøre alt fra å diskutere hvorvidt Python eller VB.NET er det beste språket å starte med for ferske utviklere (og det er i alle fall ikke Python), til å løse skoleoppgavene til folk. Jeg tar turen innom av og til, for det hender faktisk det dukker opp interessante ting. Det er mange der som ønsker å lære mer programmering, og da kan det være givende å hjelpe til litt innimellom.

BASIC

Det første bidraget jeg fikk var i PureBasic, en komersiell BASIC-variant som er tilgjengelig på Windows, Linux, MaC OS X og faktisk også Amiga. Løsningen ble laget av Kråkelefse, og er tilgjengelig fra min github repo. Her er et utdrag:

...
51 Procedure PingAlarmThread(ThreadVoid)
52   Shared FriendPort, NextPing, LastPing, AlarmDelay
53   Repeat
54     If LastPing And ElapsedMilliseconds()-LastPing > AlarmDelay
55       PrintN("Weeep! I'm so lonely... No one pinged me for quite some time.")
56       Ping(FriendPort)
57       LastPing = ElapsedMilliseconds()
58     EndIf
59     Delay(1000)
60   ForEver
61 EndProcedure
...

Størrelsesmessig (kodelinjer og bytes) er det en av de lengre implementasjonene, omtrent på nivå med C# og Erlang. Men BASIC skal jo være enkelt å forstå, og løsningen er ganske ryddig og fin - absolutt mye bedre enn Erlang-varianten på det området.

Assembly (galskap)

Neste bidrag er noe ganske anderledes, nemlig en implementasjon i FASM (flat assembler) for x86-64, skrevet av Akhkharu. Det begynte med en versjon på 324 linjer, men etter å ha laget noen "litt fikse macroer" fikk han ned antallet til 199. Her følger et lite utdrag som kanskje gir deg følelsen av hvordan det arter seg å skrive assembler-kode. Hele kilden finner du her.

...
106 surveillance:
107   mov qword [rsp], 5
108   mov qword [rsp + 8], 0
109 .conts: syscall SYS_NANOSLEEP, nocheck, rsp->rdi, rsp->rsi
110   cmp eax, -1
111   je .conts
112
113   syscall SYS_GETTIMEOFDAY, check, rsp->rsi, ~esi
114
115   mov rax, [rsp]
116   mov rdx, [lastPing]
117   add rdx, r14
118   cmp rax, rdx
119   jle surveillance
120
121   call write, timeout&>rdi
122   clone senderStack, sender
123   jmp surveillance
...

Akhkharu postet denne koden med den passende kommentaren: "… og det var nok masochisme for i år."

PHP (er det mulig da?)

Og så til et bidrag jeg ikke trodde skulle komme. |d13m0b har gjort et forsøk på å implementere en Ping Ring i PHP. Han advarer om at koden kan inneholde feil, for den ble skrevet på vorspiel. Men den illustrerer uansett hvordan en dyktig utvikler kan ta et verktøy beregnet for en bestemt oppgave (som å slå inn spiker), og gjøre noe helt annet med den (som å pusse tennene).

Utvikleren forklarer:

Når man skal skrive dette programmet i php oppstår det et par små problemer; PHP har ikke threading. I alle fall ikke threading i den forstand man forbinder med "normale" språk.

Det man er nødt til da er å forke, og det som skjer er at prosessen spawner en ny prosess (som er nøyaktig samme scriptet), og man må sjekke PID for å bestemme hva som er child eller parent og deretter gjøre handling ut i fra dette. Igjennom php kan man aksessere kallet med pcntl_fork. Slik jeg har satt det opp vil det lages 3 prosesser. En for PingAlertProcesser og en for PingListener, og det "opprinnelige" scriptet er parent og holder øye med barna om de dør eller ikke.

Så man har fått "threading" av veien, og så støter man imidlertid på en aldri så liten utfordring til. Hvordan får man delt variabler mellom scriptene? Man kan selvfølgelig serialisere og dumpe til en fil som alle scriptene har tilgang til, men dette er ikke akkurat noen optimal løsning.

Heldigvis har php noe matnyttig man kan bruke; Semaphore. Dette er en wrapper til SysV sine IPC-kall. Vi sitter altså med en modul som kan håndtere minnet mellom uavhengige prosesser (getLastPing og setLastPing)."

|d13m0b har på en måte nesten gjennoppfunnet Erlang-løsningen i PHP :) Den fullstendige kildekoden er på 217 linjer, og er tilgjengelig her. Her er en smakebit fra semafor-koden:

...
172 function hasReceivedPing()
173 {
174   global $handle;
175   global $buffer;
176
177   sem_acquire( $handle );
178   $hasReceivedPing = shm_get_var($buffer, 2 );
179   sem_release( $handle );
180
181   return $hasReceivedPing;
182 }
183
184 function setReceivedPing( $value )
185 {
186   global $handle;
187   global $buffer;
188
189   sem_acquire( $handle );
190   shm_put_var( $buffer, 2, $value );
191   sem_release( $handle );
192 }
...

Bidragsyteren avslutter med følgende kommentar om å implementere Ping Ring eller lignende slik han har gjort:

"Ikke gjør det i php, og ikke gjør det i fylla."

Haskell

Jeg fikk også se en implementasjon i Haskell, og den er tilgjengelig i sin helhet her. Koden inneholder endel unicode, men jeg gjør et forsøk på å rendrere et lite utdrag (krysser fingrene):

...
68 pingSurveillance ∷ State → IO ()
69 pingSurveillance s@State {timeout, sendPing, lastPing} = do
70     threadDelay 5000000
71     t ← readMVar lastPing
72     now ← getClockTimeMS
73     when (now - t > timeout * 10^6) $ do
74         putStrLn $ "ALARM! It has been " ⊕ show ((now - t) `div` 10^6)
75                  ⊕ " seconds since last ping"
76         putMVar sendPing ()
77     pingSurveillance s
...

Akhkharu, som leverte denne løsningen også, innrømmet at han ikke så noen fordel med å implementere Ping Ring i Haskell. Koden er ifølge tallene like lite kompakt som Erlang-versjonen.

I tillegg til de implementasjonene dere har fått sett smakebiter av her inneholder Ping Ring contrib-repositorien på Github også tre ulike Python-implementasjoner. Ta en titt, og se om du blir inspirert til å implementere din egen ring i det språket du liker best. Eller kanskje du skal prøve deg på et språk du aldri har gjort noe i før? Hvorfor ikke?!!

Tidligere i serien: Introduksjon | C# | Ruby | Boo | Erlang | Clojure | Clojure m/Agenter | Python.

Kildekoden fra denne blogposten er tilgjengelig på Github. Der står du fritt til å forgrene løsningen og gjøre egne modifikasjoner om du ønsker det (for å illustrere et poeng eller lignende). Som alt annet på bloggen er koden lisensiert under Creative Commons.


comments powered by Disqus