Upgrade to Pro — share decks privately, control downloads, hide ads and more …

How to write synchronization mechanisms for Fi...

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

RubySapporo.beam #3 edition.

seki at druby.org

October 21, 2017
Tweet

More Decks by seki at druby.org

Other Decks in Programming

Transcript

  1. 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_
  2. Α͋͘Δ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)
  3. 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Λ࢖͏ͱ్தܦաͷ஋ΛฦͤΔ
  4. 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࡞͚ͬͨͩ
  5. 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ʹͳΓ͖ͬͯಈ͘
  6. 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ʹ໭Δ
  7. 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
  8. 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
  9. QueueΛհͨ͠ڠௐ Queue Thread Thread q = SizedQueue.new(1) Thread.new do 10.times

    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
  10. ͜Εͷ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
  11. 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
  12. 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
  13. push @reader (yield) @reader (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
  14. @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
  15. @reader͕͍Δ @reader resume pop 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
  16. 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!
  17. @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!
  18. @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!
  19. ݕࠪதͷഉଞ੍ޚ 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೴ͷਓ͸υΩυΩͯ͠Δͱࢥ͏
  20. ͏༷͘͝ࢠ 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を召喚
  21. ͏༷͘͝ࢠ 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のターン

  22. ͏༷͘͝ࢠ 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。ターン終了
  23. ͏༷͘͝ࢠ 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を召喚
  24. ͏༷͘͝ࢠ 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のターン

  25. ͏༷͘͝ࢠ 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しターン終了
  26. ͏༷͘͝ࢠ 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。ターン終了
  27. ͏༷͘͝ࢠ 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!
  28. ͏༷͘͝ࢠ 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!
  29. ૉ๿͚ͩͲҰ్ 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
  30. 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 ௥Ճ͸͚ͩ͜͜ ௥Ճ͸͚ͩ͜͜
  31. 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
  32. 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
  33. 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
  34. _read() read_nonblock readable retry Fiber def _read(fd, sz) return fd.read_nonblock(sz)

    rescue IO::WaitReadable 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
  35. read_nonblock sz IO::WaitReadable def _read(fd, sz) return fd.read_nonblock(sz) rescue IO::WaitReadable

    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
  36. wait_io() resume Fiber.yield select resume readable def _read(fd, sz) return

    fd.read_nonblock(sz) rescue IO::WaitReadable 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
  37. tiny_drbͷൈਮ read def req_drb(reader) ref = load(reader, false) msg =

    load(reader) argc = load(reader) argv = argc.times.collect {load(reader)} block = load(reader, false) [ref, msg, argv] end def load(reader, marshal=true) sz = reader.read(4) sz = sz.unpack('N')[0] data = reader.read(sz) return data unless marshal begin Marshal.load(data) rescue DRb::DRbUnknown.new($!, data) end end
  38. Threadͱͷ଴ͪ߹Θͤ Thread.value IO.pipe close class ThreadTask def initialize(*args, &block) left,

    @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!
  39. ϓϩάϥϛϯά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.
  40. 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
  41. 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
  42. ΋͜͠Εਅࣅͯ͠Δͱ 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Λ ݺΜͩΒݻ·Δ͔΋...
  43. 2छྨͷϝοηʔδΛѻ͏ 1. Receive 4. Do it 2. Send task1 3.

    Receive task1 5. Send receive 2種類のメッセージ
  44. ͋ͱͰ΍ΔϦετ 1. Receive 4. Do it 2. Send task1 3.

    Receive task1 5. Send 処理できないやつをあとでやる