Ioを使っていていつも思うのが、どうやってタイマーを実装するか。という問題
たとえば、3秒毎に特定の処理をしたくなった時、こんなコードを書いてしまいたくなる
1
2
3
4
5
6
7
| loop (
proc1
proc2
proc3
:
wait(3)
)
|
一見動いてしまうけど
でも、これは実はダメで loop でブロックされてしまうし、 wait
時にスレッドがブロックされてしまう
もうかれこれ3年くらい前に実装したものが↓
/lang/io/Timer – CodeRepos::Share – Trac
まぁ、なんとなく動くんだけど、System sleep
や wait
は全体がブロックされてしまう問題があり
同時並行性に問題があった
(そもそも 0.01 sec で sleep してるあたりが問題すぎる)
ということで、これをどうにかして解決したいなぁ。と3年ぶりに思い至り
Addon の Concurrency Thread でどうにか出来るんじゃないか。ということで、 non-blocking な実装を考えてみた
実装としては、親スレッドで 時間を受け取る Server を立ててしまって、子スレッドを生成して
その子スレッド内で sleep
すれば親スレッドをブロックさせないという仕掛け
親スレッドはこんな感じに作っておいて
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
| Timer Interval Loop := Server clone do (
setPort(11002)
listener := Map clone
addListener := method(id, callback,
listener atPut(id, callback)
)
Handler := Object clone do (
handle := method(socket, server,
while(socket isOpen,
if(socket read) then (
data := socket readBuffer
id := data beforeSeq(":")
timestamp := data afterSeq(":")
if(server listener hasKey(id)) then (
callback := server listener at(id)
callback @call(id, timestamp asNumber)
)
)
socket readBuffer empty
)
socket close
)
)
handleSocket := method(socket, Handler clone @handle(socket, self))
)
|
子スレッドはソケットで書き込んであげている
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
| thread = Thread createThread("""
id := "#{id}"
host := "localhost"
port := #{Timer Interval Loop port}
interval := #{interval}
socket := Socket clone
socket setHost(host)
socket setPort(port)
socket connect
if (socket isOpen) then (
"client connected" println
) else (
Exception raise("connection failed")
)
timestamp := Date now asNumberString
msg := "\#\{id\}:\#\{timestamp\}"
loop(
timestamp := Date now asNumberString
socket write(msg interpolate)
wait(interval)
)
""" interpolate)
|
まぁ、時間でブロックされずにコールバックをとれる javascript の setTimeout
と setInterval
がすごいなぁ。ってのとそれをどうやって io で作ろうかなと思った奮闘記