Before Process (90s) ⥂隊ׁזְًٌٔ ⼿锃㘗وٕثةأؙ ظٝفٔؒٝفذ؍ـ ֿ'JCFSׄׯ - no memory protection - Cooperative multitasking -- non preemptive -- Is this a ... Fiber?
࣮ࡍͷγεςϜ 醱侧ךفٗإأהָⵃ欽ׅ项彁 ر٦ةד圓䧭ׁ ꥫꨄׁًٌٔ瑞馄ִ䗳銲ָ֮ فٗإأֽꥫꨄ׃ג穠㽷כ The Actual System - Assembled by multiple process and the resources that are used by them - Needs to jump out of the isolated memory space - Isolating processes is one thing, but for the others...
ProcessͰॻ͘ͱ͖ͷؾ࣋ͪ קַךفٗإأהוֲחַ׃גⰟ剣׃״ֲ וְֻֽׁו How you feel when you are using process - Try to share with other processes -- Even though it's a pain in the neck
Thread ًٌٔ瑞Ⱏ剣ׅ⸋⻉晛فٗإأ فٔؒٝفذ؍ـז؝ٝذؗأزأ؎حث ⸋⻉ה䒷ֹ䳔ִח鯪ꆀ 㢳ֻך➂חכفٗإأך倯ְְָה䙼ֲ degraded version of process that shares memory space - The system automatically switches to Thread - Bad copy, but lightweight To most people, processes are probably better
Try best to be very careful not to change memories of other threads - Tend to change it whenever you lose focus - Only change the places that are okay to change
Threadʹඞཁͳၗຫ͞ չ⥯ְֻחזה04ח걾זֻג㸜Ⰻ ח⸬桦葺ֻ剅ֽ״պ ה鎉ִ➂ךך娀㐻 04ךؔ٦غ٦قحسָ孡חז➂פ Attitude when using Threads "Secure and efficient coding without OS? No sweat." - threads will be a great advantage for you - for coders who have concerns about OS overheads
Fiber ظٝفٔؒٝفذ؍ـז⸋⻉晛5ISFBE و؎ؙٗأٖحس 5ISFBEⰻח醱侧ך؝ٝذؗأزָ֮ ⴽךⳢ椚ך鷿⚥פأ؎حثׅ קהוך➂חכ5ISFBEך倯ְְָה䙼ֲ A non-preemptive degraded version of Thread - Micro thread - Has multiple context in one Thread - Switches during other jobs To most people, Threads are probably better
FiberͰॻ͘ͱ͖ͷؾ࣋ͪ ֲתְ؝ٝذؗأزأ؎حثך⽃⡘鋅א ֽ״ֲ ـٗحׇׁؙזְ״ֲחׅחכ ׃'JCFS⢪ֲֿהָ湡涸 Attitude when using Threads "Secure and efficient coding without OS? No sweat." - threads will be a great advantage for you - for coders who have concerns about OS overheads
ͳͥFiberͷΛ͢Δͷ͔ 'JCFS⢪ؿٖ٦يٙ٦ָؙׅ׀ְ׃ְ ה耀ְ ׅ׀ְְפ ׅ׀ְְֻ Why Fiber? Fiber used frameworks are cool - from what I heard -- is it hard to do? -- is it really good?
ͳͥFiberͷΛ͢Δͷ͔ ٙةءع9ד(6*הTPDLFUفؚٗٓىؚٝ ָثّحزرٕؗ ךדծ荈ⴓד剅ְגֻז أٖحسד醱侧ךفٗإأה鸐⥋ׅ ך I can socket program with x11 and GUI a little. - So, I decided that I'd like to use it myself -- Something that communicates with multiple processes with one Thread
ͳͥFiberͷΛ͢Δͷ͔ ✮䟝דכ5ISFBE״ꨇ׃ְה䙼 ֽוך鸐ד׃ךדծזׁחֶ ׅⴓֽ׃ְ I suspected that it would be harder than Threads It was. Let me share my experiences with you.
࠷ॳͷςʔϚ &OVNFSBUPSך״ֲז圓鸡ךفؚٗٓي 铣זָծ'JCFSך"1*㷕ןתׅ 'JCFSד剅ֹ湫ׅ &OVNFSUPSד剅ֹ湫ׅ First topic We will use an Enumerator like structure to learn Fiber API - Rewrite with Fiber - Rewrite with Enumerator
Α͋͘ΔFibonacci ٕ٦فד剅ֻה知⽃ [0, 1] [1, 1] [2, 2] [3, 3] [4, 5] [5, 8] [6, 13] [7, 21] [8, 34] [9, 55] Plain Fibonacci - Easy to write with Loop def fibonacci(n) a, b = 1, 1 n.times do |i| p [i, a] a, b = b, a + b end end fibonacci(10)
FiberͰॻ͖͢ ֿ⢪ג"1*铡僇׃תׅ 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) Rewrite with Fiber - Explain API using this
Fiber.new ⴽ'JCFSדװְⳢ椚ـٗحؙד床ׅ תـٗحؙך؝٦سח鹌תׇ 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.new - Pass value with another Fiber to do a different job - The block code will not run yet.
Fiber#resume 'JCFSח؝ٝذؗأزⴖ剏ִ 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 Switch context to the Fiber
Fiber.yield SFTVNF׃'JCFSחⴖ剏ִ 䒷侧ָSFTVNFך⦼חז 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 Switch to fiber.resume The argument becomes the value of the fiber
resume, again ✳䏝湡⟃꣬ךSFTVNFכZJFMEַⱄׅ 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, again From the second time on, resume restarts from yield
resumeͷߦ͖ઌ 'JCFSOFX׃ֽדכ㹋遤ׁזְ SFTVNFד'JCFSח؝ٝذؗأز獳ׅ ♧䏝湡כ⯓걧פ ✳䏝湡⟃꣬כ'JCFSZJFME׃הֿפ ֿכ⡦䏝湡ךSFTVNFַז Where resume jumps to Would not execute just by Fiber new Pass the value to Fiber by resume -- Back to the top on the first round -- Back to Fiber.yield from the second round on --- Which round am I on...?
APIΛઆ໌ͨ͠ SFTVNFהZJFMEך嚊䙀ָהאֹחְֻ 荈歋ֺׅגוֲ׃גְְַַזְ Explained the API -The concept of resume and yield is hard to grep -Don't know what to do, there are so many things we can do with it
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 Rewrite with Enumerator - Nearly identical
Enumerator 単⡲ֿהד罋ִװֻׅז ֶׅׅ ❌ 'JCFSךꨇ׃ׁח䩧⯘אהְֲ䩛䘔ִ ָזְ Enumerator - Easier to understand when put in a frame - Recommended ❌ Can't really get the feel of overcoming the difficulties of Fiber
QueueΛհͨ͠ڠௐ 2VFVF➜׃ג醱侧ך5ISFBEָ鸬䵿ׅ ך5ISFBEכֶ✼ְ濼זְ 欰欵罏ծ嶊顤罏כ醱侧ְג⹛ֻ 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 Sync with Queue - Multiple Threads synchronize through Queue - Each Thread does not know anything about other Threads - Works with multiple producers and consumers
͜ΕͷFiber൛ 3EW➜׃ג醱侧ך'JCFSָ鸬䵿ׅ SFOEF[WPVT 䖉さׇ 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 version - Multiple Fibers work together through Rdv - rendez-vous
Rdvͷ࣮ JOJUJBMJ[F QVTI QPQ 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 Implementation of Rdv
࠷͖ۙͳΠσΟΦϜ וֲװֽֿדֲתֻ剅ֲֽ הִ֮׆SFTVNF 遤ֹ鑥תZJFME My recent favorite idiom Feeling that it will go smoothly with these - just use resume - yield when you're stuck
ͱΓ͋͑ͣresume 'JCFSOFX׃ּׅSFTVNFׅ 5ISFBEOFXך״ֲחּׅ⹛ֹ⳿ׅ 剑ⴱךSFTVNFך暴ⴽז⹛ֹ罋ִזֻ ג״ְ Just use resume for now - Resume right after Fiber.new -- Works right away, just like Thread.new --- Don't have to think about the special behavior on the first round
鋙ִגֲ Yield when you're stuck - Do as much as it can while it can do it - Yield when it has nothing to do -- Let the partner know about yourself (alias fiber.current)
initialize !SFBEFSכQPQ䖉ծ!XSJUFSכQVTI䖉 ך'JCFSًٌׅ 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 @reader holds fibers waiting for pop @writer holds fibers waiting for push
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 - When there is no @reader, it records itself and wait (yield) - Resume when @reader is present (resume)
@reader͕͍ͳ͍ ر٦ةה'JCFSDVSSFOUًٌ׃גZJFME 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 When @reader is not there - Records the data and Fiber.current, then yield
@reader͕͍Δ !SFBEFS♧אSFTVNFׅ QPQד䖉גְ'JCFSָⱄׅ 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 @reader is present - Resume one @reader -- Fiber that was waiting for the pop resumes
׃גر٦ة鵤ׅ 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 When there is no @writer, it records itself and waits (yield) - When @writer is present, it resumes (resume) -- and returns data Skip!
@writer͕͍ͳ͍ 'JCFSDVSSFOUًٌ׃גZJFME 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 is gone Record Fiber.current and yield Skip!
@writer͕͍Δ !XSJUFS♧אSFTVNFׅ QVTIד䖉גְ'JCFSָⱄׅ ַًٌر٦ة鵤ׅ 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 is present - Resume one @writer -- a Fiber that was waiting for push resumes - Returns data value from the note Skip!
ݕࠪதͷഉଞ੍ޚ 'JCFSדכ勴⟝嗚叨ך䱖➭ⵖ䖴ָ♶銲 5ISFBEדכ⽬ꤹ 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 Exclusive control while inspecting - With Fiber, there is no need for exclusive control - Very risky on Threads!!
͏༷͘͝ࢠ 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 How it works. Fiber.new kara-matz is summoned! Fiber.newした kara-matzを召喚
͏༷͘͝ࢠ 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 is resumed It's kara-matz's turn! kara-matzをresume kara-matzのターン
͏༷͘͝ࢠ 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 does push() There are no opponents.. Record data and himself, then yield. kara-matzのpush! 相手がいない。 自分とデータをメモしyield。ターン終了
͏༷͘͝ࢠ 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 Main's turn! Fiber.new ichi-matz is summoned! メインのターン Fiber.new ichi-matzを召喚
͏༷͘͝ࢠ 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 resumed It's ichi-matz's turn! ichi-matzをresume ichi-matzのターン
͏༷͘͝ࢠ 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 does pop! ichi-matz reads the memo takes data. kara-matz resumes the finished. ichi-matzのpop! ichi-matzはメモを読んだ データを手にいれた。kara-matzをresumeしターン終了
͏༷͘͝ࢠ 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 does push() There are no opponents. Record data and himself, then yield. kara-matzのpush! 相手がいない。 自分とデータをメモしyield。ターン終了
͏༷͘͝ࢠ 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しターン終了 ichi-matz does pop! ichi-matz reads the memo takes data. kara-matz resumes the finished. Skip!
͏༷͘͝ࢠ 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 dies. It's main's turn Main dies. ichi-matzはしんでしまった メインのターン メインはしんでしまった Skip!
͜͜ͷ·ͱΊ 'JCFSָず劍ׅך➬穈⡲ 猘ך㥨ֹז؎ر؍ؔي荈䢧׃ הִ֮׆SFTVNF 遤ֹ鑥תZJFME Wrapping up Made a structure of synchronizing fibers Proudly presented my favorite idiom - just use resume - yield when you're stuck
͜͜ͷ·ͱΊ ず劍ך勴⟝嗚叨ח䱖➭ⵖ䖴ְזְ 农럀涸ח5ISFBEח㢌䳔ׁה㔭 5ISFBE⢪גְאָ㹋כ'JCFS ך倯ְְָה䙼ֲ Wrapping up We don't need exclusive control when sync-ing - Don't want it to be implicitly converted into Threads -- It's better off that it was actually Fiber when you thought you were using Threads
࣍ͷςʔϚ TPDLFUך؟٦غ٦فؚٗٓىؚٝ فٗز؝ٕךⳢ椚荈搫ח剅ְֹ׃ծ醱 侧ך䱸竲䪔ְְֽו٦ Next Topic - Socket server programming - Want to code protocol processes smartly and support multiple connections..
͢͢Ί͔ͨ E3VCZ؟٦غ٦ך毟⡂؝٦سד铡僇 稆劰ֽו⚛遤ח⹛ַזְך 5ISFBE欽ְג⚛遤ח⹛ֻך 5ISFBE晛'JCFS晛ח׃ך؎و؎ ث ך何葺晛 Table of Contents: explain with dRuby server pseudo code - Coding is simple, but doesn't move in parallel - Works in parallel with Threads - Code above rewritten to use Fiber (bit awkward) -- Better version using Fiber
ૉ͚ͩͲҰ్ ⽃秪ח♧湫简ח剅ְך *0כـٗحֽؙׅו孡ח׃זְ ؙٓ؎،ٝز׃ַ⹛ַזְ 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 Simple but not parallel - Written straight forward - Ignore I/O blocks - Only works with one client def read_n(fd, sz) ... ... end
Thread൛ GE׀הח5ISFBE饯⹛ׅ ֽֿד⚛遤ח⹛ֻ 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 Thread ver - Start a new thread on every fd - Add 2 columns and it works in parallel def read_n(fd, sz) ... ... end Ճ͚ͩ͜͜ Ճ͚ͩ͜͜
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 Fiber ver - non-preemptive - Not quite parallel, yet def read_n(fd, sz) ... ... end Swapped with Fiber
Fiber൛վ *0ָـٗحؙ׃ֲזהֹծֿ؝ٝ ذؗأزⴖ剏ִ⡲䨌 ـٗحؙ괏ظٝـٗحؙ*0 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 Fiber ver Revisioned - Switch context before it I/O blocks -- Blocking-style non-blocking I/O def read_n(fd, sz) ... fd.read_nonblock(sz) rescue WaitReadable Fiber.yield retry end
Block-ish non-block I/O ـٗحؙ׃ֲזהֹծ'JCFSZJFME׃ג NBJO@MPPQחו ؝٦ٕغحؙדSFTVNFׅ 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 It Fiber.yields back to main_loop before it I/O blocks Resumes on call back def read_n(fd, sz) ... fd.read_nonblock(sz) rescue WaitReadable Fiber.yield retry end
Bartender::Context SFBEBCMF
XSJUBCMFחזֿהח孡בְ ؝٦ٕغحؙ ֿ⢪גCMPDLJTIOPOCMPDL*0 Context#[]=(event, fd, callback) Πϕϯτछʢ:read, :writeʣͱfdͷʹίʔϧόοΫΛొ Bartender::Context Callbacks when readable or writeable Use this to implement block-ish non-block I/O register callbacks for the combination of event types (:read, :write) and fd
_read() SFBE@OPOCMPDLׅկ⢽㢩ך㜥さכ SFBEBCMFחזך䖉גַSFUSZׅ 䖉ג ֿד䖉ׯتً ⴽך'JCFSח؝ٝذؗأزأ؎حثׅ 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 - read_nonblock. On exception, it waits to be readable before it retries. - Waits to be what? -- We need to context switch to another fiber
read_nonblock ּׅ铣ر٦ةָ֮ל铣ד鵤ׅ T[劢弫ך؛٦أ״ֻ֮ ּׅח铣ر٦ةָזְ㜥さחכ⢽㢩 *08BJU3FBEBCMF 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 If there are any readable data, it reads and returns - There are cases when it's below sz Excepts when there is no readable data - IO::WaitReadable
wait_io() ؝٦ٕغحؙה׃גSFTVNF涫ꐮ ׃ג'JCFSZJFME 剑穄涸חً؎ٕٝ٦فח䨱TFMFDUׅ SFTVNFׁהֹSFBEBCMFחזג 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 Register resume as callback Fiber.yield - Goes back to mainloop and selects at the end readable when resumed
tiny_drbͷൈਮ ًحإ٦آ铣ד穈甧ג鿇ⴓדׅ ـٗحؙ孡חׇ׆⽃秪זSFBE⚛ץג ـٗحؙׅ㜥さחכ农럀涸ח؝ٝذؗأ زأ؎حث׃תׅ 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 extracted from tiny_drb - Where it constructs with the read messages - Just lining up reads, blocks are ignored - When blocked, it context switches implicitly.
צҧ͍ ❌ SFBEBCMFזًحإ٦آּׅחⰋ鿇铣 ❌ XSJUBCMFזًحإ٦آּׅחⰋ鿇剅ֽ misinterpretation Can read all messages when it's readable Can write all messages when its writeable
ੲΑ͘ݟͨόά TFMFDU ך֮הծًحإ٦آ铣ד穈甧 גꟼ侧؝٦ٕ 鹼䒀ծꥺ㹱ָ֮הةأؙأ؎حثדֹ ׆ח㔿ת Bugs in the good Ol' days (in C) - Call functions that read & construct messages after select() -- Cannot switch tasks when there is latency and/or incident, and freezes
σϞʢ͋Γ·ͤΜʣ 醱侧ךؙٓ؎،ٝزָ⹛ֻ圫㶨ָTBNQMFח 剅ְג֮תָׅ ⹛ַ׃ג鋅גַזַךד (There is no) DEMO - You can see how multiple clients work in the sample.. -- But you couldn't really *see* it moving
tiny_drbͷ͓͠Ζ͍ͱ͜Ζ CMPDLJTIOPOCMPDL*0 僇爙涸זأ؛آُ٦ָٓזְ ♧אך'JCFSָ醱侧ך➬穈TFMFDU겲ה 3EWהַהֲתֻ⹛ְגְ Interesting stuff about tiny_drb - block-ish nonblock I/O - No written scheduler -- One Fiber is working around well with multiple nodes (select, Rdv, etc.)
Threadͱͷͪ߹Θͤ 5ISFBEWBMVFׅה姺תג׃תֲ *0QJQFךDMPTF⢪ג穄✪鸐濼ׅ 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 Sync Threads with FD - Stops on Thread.value - Notifies stop with IO.pipe close Skip!
Multiplexerͷ·ͱΊ ⽃秪ח♧湫简ח剅ְֹWT⚛遤Ⳣ椚 5ISFBE⤑ⵃ 'JCFSד⚛遤䏝♳־ךꨇ׃ְ CMPDLJTIOPOCMPDL*0⡲ Multiplexer Wrap-up - Simple coding vs parallelism - Threads are useful - Hard to implement perfect parallel with Fiber -- made block-ish non-block I/O
ࠓճͷྫ 农鎸דְֹֻ㼭ְׁ➬穈 ➬穈חさׇג،فٔ؛٦ءّٝ剅 ֽ 如ך״ֲז縐鼘ֽ This example - Was pretty small, small enough to memorize it all -- Can code & adapt according to a particular application - Didn't get stuck in the following traps:
᠘ 'JCFSך⚥ד鹼ְⳢ椚׃גـٗحؙ ㄎ⯓ד5ISFBE⡲גְג'JCFS&SSPS ַךHFNךזַדְֲֲֿֿהװג ַ Traps: - Get blocked inside Fiber by processing something slow in it - Read some place that used a thread and FiberError - Somebody might have already done this in gem somewhere
ͭ·Γ ،فٔ؛٦ءّٝך⢪ֲٓ؎ـٓٔךׅ ׆תד㹋鄲椚鍑׃גזְהعو عوג鋙ִגֶֽל㣐♱㣗 㹋כ'JCFSׄׯזֻגعو The point is - that you need to understand everything about the library or you'll get stuck -- If you know that you'll get stuck, you're okay --- (Actually, it doesn't need to be Fiber to get stuck)
ϋϚΔྫ 'JCFS晛ך5XJUUFSךة؎يٓ؎ٝך铣鴥 ⡲ֲ +40/QBSTFכأزٔ٦يⰋ鿇铣ד 4USJOHח׃ׯֲ̔تً أزٔ٦يַ铣+40/QBSTFזְ Examples of getting stuck - Making a Fiber version of Twitter timeline reading -- JSON.parse reads and puts everything into a string →Won't work - Is there JSON.parse that reads from streams?