seki at druby.org
October 21, 2017
230

# How to write synchronization mechanisms for Fiber (RubySapporo.beam)

RubySapporo.beam #3 edition.

October 21, 2017

## Transcript

3. ### slide sponsors Ruby @iwadon OSS Gate @ktou dRuby Web (

eBook Store) http:// estore.ohmsha.co.jp/titles/978427406609P @hisashim @m_seki "re: " @awazeki Mastodon Pawoo Ruby @pixiv m_seki @tsuboi 07 smalruby @vestige_

12प೥

37. ### Α͋͘ΔFibonacci [0, 1] [1, 1] [2, 2] [3, 3] [4,

5] [5, 8] [6, 13] [7, 21] [8, 34] [9, 55] ్தͷ஋Λฦ͢ͷ͕೉͍͠ def fibonacci(n) a, b = 1, 1 n.times do |i| p [i, a] a, b = b, a + b end end fibonacci(10)
38. ### FiberͰॻ͖௚͢ API fib = Fiber.new do a, b = 1,

1 while true Fiber.yield(a) a, b = b, a + b end end 10.times do |n| p [n, fib.resume] end def fibonacci(n) a, b = 1, 1 n.times do |i| p [i, a] a, b = b, a + b end end fibonacci(10) FiberΛ࢖͏ͱ్தܦաͷ஋ΛฦͤΔ

40. ### Fiber.new Fiber fib = Fiber.new do a, b = 1,

1 while true Fiber.yield(a) a, b = b, a + b end end 10.times do |n| p [n, fib.resume] end Fiber࡞͚ͬͨͩ
41. ### Fiber#resume Fiber fib = Fiber.new do a, b = 1,

1 while true Fiber.yield(a) a, b = b, a + b end end 10.times do |n| p [n, fib.resume] end FiberʹͳΓ͖ͬͯಈ͘
42. ### Fiber.yield resume Fiber resume fib = Fiber.new do a, b

= 1, 1 while true Fiber.yield(a) a, b = b, a + b end end 10.times do |n| p [n, fib.resume] end resumeʹ໭Δ
43. ### resume, again resume yield fib = Fiber.new do a, b

= 1, 1 while true Fiber.yield(a) a, b = b, a + b end end 10.times do |n| p [n, fib.resume] end

46. ### EnumeratorͰॻ͖௚͢ fib = Fiber.new do a, b = 1, 1

while true Fiber.yield(a) a, b = b, a + b end end 10.times do |n| p [n, fib.resume] end def fibonacci a, b = 1, 1 while true yield(a) a, b = b, a + b end end fib = to_enum(:fibonacci) 10.times do |n| p [n, fib.next] end

49. ### ࣍ͷςʔϚ

do |n| sleep(rand) q.push(n) p [:push, n] end end Thread.new do 10.times do |n| sleep(rand) p [:pop, q.pop] end end.join
51. ### ͜ΕͷFiber൛ Rdv Fiber rendez-vous rdv = Rdv.new Fiber.new do 10.times

do |n| rdv.push(n) p [:push, n] end end.resume Fiber.new do 10.times do |n| p [:pop, rdv.pop] end end.resume

55. ### Rdvͷ࣮૷ initialize push pop def initialize @reader = [] @writer

= [] end def push(it) if @reader.empty? @writer << [it, Fiber.current] return Fiber.yield end @reader.shift.resume(it) end def pop if @writer.empty? @reader << Fiber.current return Fiber.yield end value, fiber = @writer.shift fiber.resume return value end

58. ### initialize @reader pop @writer push Fiber def initialize @reader =

[] @writer = [] end def push(it) if @reader.empty? @writer << [it, Fiber.current] return Fiber.yield end @reader.shift.resume(it) end def pop if @writer.empty? @reader << Fiber.current return Fiber.yield end value, fiber = @writer.shift fiber.resume return value end

@writer = [] end def push(it) if @reader.empty? @writer << [it, Fiber.current] return Fiber.yield end @reader.shift.resume(it) end def pop if @writer.empty? @reader << Fiber.current return Fiber.yield end value, fiber = @writer.shift fiber.resume return value end
60. ### @reader͕͍ͳ͍ Fiber.current yield def initialize @reader = [] @writer =

[] end def push(it) if @reader.empty? @writer << [it, Fiber.current] return Fiber.yield end @reader.shift.resume(it) end def pop if @writer.empty? @reader << Fiber.current return Fiber.yield end value, fiber = @writer.shift fiber.resume return value end

@writer = [] end def push(it) if @reader.empty? @writer << [it, Fiber.current] return Fiber.yield end @reader.shift.resume(it) end def pop if @writer.empty? @reader << Fiber.current return Fiber.yield end value, fiber = @writer.shift fiber.resume return value end
62. ### pop @writer (yield) @writer (resume) def initialize @reader = []

@writer = [] end def push(it) if @reader.empty? @writer << [it, Fiber.current] return Fiber.yield end @reader.shift.resume(it) end def pop if @writer.empty? @reader << Fiber.current return Fiber.yield end value, fiber = @writer.shift fiber.resume return value end pushͱҰॹͩΑʂ Skip!
63. ### @writer͕͍ͳ͍ Fiber.current yield def initialize @reader = [] @writer =

[] end def push(it) if @reader.empty? @writer << [it, Fiber.current] return Fiber.yield end @reader.shift.resume(it) end def pop if @writer.empty? @reader << Fiber.current return Fiber.yield end value, fiber = @writer.shift fiber.resume return value end Skip!
64. ### @writer͕͍Δ @writer resume push Fiber def initialize @reader = []

@writer = [] end def push(it) if @reader.empty? @writer << [it, Fiber.current] return Fiber.yield end @reader.shift.resume(it) end def pop if @writer.empty? @reader << Fiber.current return Fiber.yield end value, fiber = @writer.shift fiber.resume return value end Skip!
65. ### ݕࠪதͷഉଞ੍ޚ Fiber Thread def initialize @reader = [] @writer =

[] end def push(it) if @reader.empty? @writer << [it, Fiber.current] return Fiber.yield end @reader.shift.resume(it) end def pop if @writer.empty? @reader << Fiber.current return Fiber.yield end value, fiber = @writer.shift fiber.resume return value end Thread೴ͷਓ͸υΩυΩͯ͠Δͱࢥ͏

67. ### ͏༷͘͝ࢠ rdv = Rdv.new Fiber.new do 10.times do |n| rdv.push(n)

p [:push, n] end end.resume Fiber.new do 10.times do |n| p [:pop, rdv.pop] end end.resume Fiber.newした  kara-matzを召喚
68. ### ͏༷͘͝ࢠ rdv = Rdv.new Fiber.new do 10.times do |n| rdv.push(n)

p [:push, n] end end.resume Fiber.new do 10.times do |n| p [:pop, rdv.pop] end end.resume kara-matzをresume  kara-matzのターン
69. ### ͏༷͘͝ࢠ rdv = Rdv.new Fiber.new do 10.times do |n| rdv.push(n)

p [:push, n] end end.resume Fiber.new do 10.times do |n| p [:pop, rdv.pop] end end.resume kara-matzのpush！  相手がいない。  自分とデータをメモしyield。ターン終了
70. ### ͏༷͘͝ࢠ rdv = Rdv.new Fiber.new do 10.times do |n| rdv.push(n)

p [:push, n] end end.resume Fiber.new do 10.times do |n| p [:pop, rdv.pop] end end.resume メインのターン  Fiber.new  ichi-matzを召喚
71. ### ͏༷͘͝ࢠ rdv = Rdv.new Fiber.new do 10.times do |n| rdv.push(n)

p [:push, n] end end.resume Fiber.new do 10.times do |n| p [:pop, rdv.pop] end end.resume ichi-matzをresume  ichi-matzのターン
72. ### ͏༷͘͝ࢠ rdv = Rdv.new Fiber.new do 10.times do |n| rdv.push(n)

p [:push, n] end end.resume Fiber.new do 10.times do |n| p [:pop, rdv.pop] end end.resume ichi-matzのpop！  ichi-matzはメモを読んだ  データを手にいれた。kara-matzをresumeしターン終了
73. ### ͏༷͘͝ࢠ rdv = Rdv.new Fiber.new do 10.times do |n| rdv.push(n)

p [:push, n] end end.resume Fiber.new do 10.times do |n| p [:pop, rdv.pop] end end.resume kara-matzのpush！  相手がいない。  自分とデータをメモしyield。ターン終了
74. ### ͏༷͘͝ࢠ rdv = Rdv.new Fiber.new do 10.times do |n| rdv.push(n)

p [:push, n] end end.resume Fiber.new do 10.times do |n| p [:pop, rdv.pop] end end.resume ichi-matzのpop！  ichi-matzはメモを読んだ  データを手にいれた。kara-matzをresumeしターン終了 Skip!

76. ### ͏༷͘͝ࢠ rdv = Rdv.new Fiber.new do 10.times do |n| rdv.push(n)

p [:push, n] end end.resume Fiber.new do 10.times do |n| p [:pop, rdv.pop] end end.resume ichi-matzはしんでしまった  メインのターン  メインはしんでしまった Skip!

83. ### ૉ๿͚ͩͲҰ్ I/O 1 def main_loop loop do select fds.each do

|fd| call_callback end end end def on_accept c = accept loop do read_request(c) do_it write_response(c) end end def read_n(fd, sz) ... ... end
84. ### Thread൛ fd Thread def main_loop loop do select fds.each do

|fd| call_callback end end end def on_accept c = accept Thread.new(c) do |fd| loop do read_request(fd) do_it write_response(fd) end end end def read_n(fd, sz) ... ... end ௥Ճ͸͚ͩ͜͜ ௥Ճ͸͚ͩ͜͜
85. ### Fiber൛ def main_loop loop do select fds.each do |fd| call_callback

end end end def on_accept c = accept Fiber.new do loop do read_request(c) do_it write_response(c) end end.resume end def read_n(fd, sz) ... ... end Swapped with Fiber
86. ### Fiber൛վ I/O I/O def main_loop loop do select fds.each do

|fd| call_callback end end end def on_accept c = accept Fiber.new do loop do read_request(c) do_it write_response(c) end end.resume end def read_n(fd, sz) ... fd.read_nonblock(sz) rescue WaitReadable Fiber.yield retry end
87. ### Block-ish non-block I/O Fiber.yield main_loop resume def main_loop loop do

select fds.each do |fd| call_callback end end end def on_accept c = accept Fiber.new do loop do read_request(c) do_it write_response(c) end end.resume end def read_n(fd, sz) ... fd.read_nonblock(sz) rescue WaitReadable Fiber.yield retry end

90. ### Bartender::Context readable, writable block-ish non-block I/O Context#[]=(event, fd, callback) Πϕϯτछʢ:read,

:writeʣͱfdͷ૊ʹίʔϧόοΫΛొ࿥

wait_readable(fd) retry end def wait_readable(fd); wait_io(:read, fd); end def wait_io(event, fd) self[event, fd] = Fiber.current.method(:resume) Fiber.yield ensure delete(event, fd) end

Skip!

101. ### ଞʹࢼ࡞ͨ͠΋ͷ Fiber Rinda::TupleSpace Fiber WEBrick FD Thread Skip!

@right = IO.pipe @task = Thread.new do begin block.call(*args) ensure left.close end end end def value if @right Bartender.wait_readable(@right) @right.close end @task.value ensure @right = nil end end Skip!

115. ### ϓϩάϥϛϯάErlangΑΓ -module(kvs). -export([start/0, store/2, lookup/1]). start() -> register(kvs, spawn(fun() ->

loop() end)). store(Key, Value) -> rpc({store, Key, Value}). lookup(Key) -> rpc({lookup, Key}). rpc(Q) -> kvs ! {self(), Q}, receive {kvs, Reply} -> Reply end. loop() -> receive {From, {store, Key, Value}} -> put(Key, {ok, Value}), From ! {kvs, true}, loop(); {From, {lookup, Key}} -> From ! {kvs, get(Key)}, loop() end.
116. ### serverͷϝΠϯϧʔϓ -module(kvs). -export([start/0, store/2, lookup/1]). start() -> register(kvs, spawn(fun() ->

loop() end)). store(Key, Value) -> rpc({store, Key, Value}). lookup(Key) -> rpc({lookup, Key}). rpc(Q) -> kvs ! {self(), Q}, receive {kvs, Reply} -> Reply end. loop() -> receive {From, {store, Key, Value}} -> put(Key, {ok, Value}), From ! {kvs, true}, loop(); {From, {lookup, Key}} -> From ! {kvs, get(Key)}, loop() end. server main loop
117. ### client͸ࡶʹϒϩοΫ x -module(kvs). -export([start/0, store/2, lookup/1]). start() -> register(kvs, spawn(fun()

-> loop() end)). store(Key, Value) -> rpc({store, Key, Value}). lookup(Key) -> rpc({lookup, Key}). rpc(Q) -> kvs ! {self(), Q}, receive {kvs, Reply} -> Reply end. loop() -> receive {From, {store, Key, Value}} -> put(Key, {ok, Value}), From ! {kvs, true}, loop(); {From, {lookup, Key}} -> From ! {kvs, get(Key)}, loop() end. client API
118. ### ΋͜͠Εਅࣅͯ͠Δͱ x -module(kvs). -export([start/0, store/2, lookup/1]). start() -> register(kvs, spawn(fun()

-> loop() end)). store(Key, Value) -> rpc({store, Key, Value}). lookup(Key) -> rpc({lookup, Key}). rpc(Q) -> kvs ! {self(), Q}, receive {kvs, Reply} -> Reply end. loop() -> receive {From, {store, Key, Value}} -> put(Key, {ok, Value}), From ! {kvs, true}, loop(); {From, {lookup, Key}} -> From ! {kvs, get(Key)}, loop() end. serverͷओॲཧͰrpcΛ ݺΜͩΒݻ·Δ͔΋...