Denis Defreyne
April 13, 2019
130

# An introduction to fibers

April 13, 2019

## Transcript

Fibers

12. ### 9 f = lambda do puts "zebra" return puts "invisible

zebra" end f.call
13. ### 9 f = lambda do puts "zebra" return puts "invisible

zebra" end f.call zebra
14. ### 10 f = Fiber.new do puts "zebra" return puts "invisible

zebra" end f.resume
15. ### 10 f = Fiber.new do puts "zebra" return puts "invisible

zebra" end f.resume zebra
16. ### 10 f = Fiber.new do puts "zebra" return puts "invisible

zebra" end f.resume zebra sample.rb:3:  in `block in <main>':  unexpected return  (LocalJumpError)
17. ### 11 f = Fiber.new do puts "zebra" Fiber.yield puts "invisible

zebra" end f.resume
18. ### 11 f = Fiber.new do puts "zebra" Fiber.yield puts "invisible

zebra" end f.resume zebra

22. ### 14 f = lambda do puts "donkey" end f.call f.call

donkey donkey

donkey
25. ### 15 f = Fiber.new do puts "donkey" end f.resume f.resume

donkey a.rb:6:  in `resume':  dead fiber called  (FiberError)

27. ### 17 f = lambda do puts "kangaroo" return puts "wallaby"

end f.call  kangaroo
28. ### 18 f = lambda do puts "kangaroo" return puts "wallaby"

end f.call f.call
29. ### 18 f = lambda do puts "kangaroo" return puts "wallaby"

end f.call f.call kangaroo kangaroo
30. ### 19 f = Fiber.new do puts "kangaroo" Fiber.yield puts "wallaby"

end f.resume f.resume
31. ### 19 f = Fiber.new do puts "kangaroo" Fiber.yield puts "wallaby"

end f.resume f.resume kangaroo wallaby

35. ### 22 f = Fiber.new { … } Create a ﬁber

f.resume Start or resume a ﬁber
36. ### 22 f = Fiber.new { … } Create a ﬁber

f.resume Start or resume a ﬁber Fiber.yield Return from a ﬁber

quokka

41. ### 26 f = Fiber.new do |a| puts a    end

f.resume('samoyed')  samoyed
42. ### 27 f = Fiber.new do |a| puts a Fiber.yield puts

'arctic fox' end f.resume('samoyed') f.resume samoyed  arctic fox
43. ### 28 f = Fiber.new do |a| puts a b =

Fiber.yield puts b end f.resume('samoyed') f.resume('arctic fox')
44. ### 28 f = Fiber.new do |a| puts a b =

Fiber.yield puts b end f.resume('samoyed') f.resume('arctic fox') samoyed arctic fox

red panda

red panda

maine coon
52. ### 35 f = Fiber.new do puts 'maine coon' Fiber.yield 'raccoon'

end f.resume puts f.resume maine coon  raccoon
53. ### 36 f = Fiber.new do  Fiber.yield('maine coon')  'raccoon' end puts

f.resume puts f.resume
54. ### 36 f = Fiber.new do  Fiber.yield('maine coon')  'raccoon' end puts

f.resume puts f.resume maine coon raccoon

ﬁber
58. ### 39 f = Fiber.new { |…| … } Create a

ﬁber r = f.resume(…) Start or resume a ﬁber
59. ### 39 f = Fiber.new { |…| … } Create a

ﬁber r = f.resume(…) Start or resume a ﬁber a = Fiber.yield(…) Return from a ﬁber

2 4

64. ### 44 class PeopleDirectory RADIA = 'Dr. Radia Perlman' KATIE =

'Dr. Katie Bouman' ANITA = 'Dr. Anita Borg' end
65. ### 45 class PeopleDirectory RADIA = 'Dr. Radia Perlman' KATIE =

'Dr. Katie Bouman' ANITA = 'Dr. Anita Borg' def each end end
66. ### 46 class PeopleDirectory RADIA = 'Dr. Radia Perlman' KATIE =

'Dr. Katie Bouman' ANITA = 'Dr. Anita Borg' def each yield RADIA yield KATIE yield ANITA end end
67. ### 47 class PeopleDirectory RADIA = 'Dr. Radia Perlman' KATIE =

'Dr. Katie Bouman' ANITA = 'Dr. Anita Borg' def each yield RADIA yield KATIE yield ANITA end end PeopleDirectory.new
68. ### 48 class PeopleDirectory RADIA = 'Dr. Radia Perlman' KATIE =

'Dr. Katie Bouman' ANITA = 'Dr. Anita Borg' def each yield RADIA yield KATIE yield ANITA end end PeopleDirectory.new.each do |name| puts name end
69. ### 49 class PeopleDirectory RADIA = 'Dr. Radia Perlman' KATIE =

'Dr. Katie Bouman' ANITA = 'Dr. Anita Borg' def each yield RADIA yield KATIE yield ANITA end end PeopleDirectory.new.each do |name| puts name end Dr. Radia Perlman Dr. Katie Bouman Dr. Anita Borg

= 12345
72. ### 52 class LinearCongruentialGenerator M = 0x7FFFFFF A = 1103515245 C

= 12345 def initialize(seed) @value = seed end
73. ### 53 class LinearCongruentialGenerator M = 0x7FFFFFF A = 1103515245 C

= 12345 def initialize(seed) @value = seed end def next @value = (A * @value + C) % M end end
74. ### 54 class LinearCongruentialGenerator M = 0x7FFFFFF A = 1103515245 C

= 12345 def initialize(seed) @value = seed end def next @value = (A * @value + C) % M end end rng = LinearCongruentialGenerator.new(123)
75. ### 55 class LinearCongruentialGenerator M = 0x7FFFFFF A = 1103515245 C

= 12345 def initialize(seed) @value = seed end def next @value = (A * @value + C) % M end end rng = LinearCongruentialGenerator.new(123) p rng.next p rng.next p rng.next
76. ### 56 class LinearCongruentialGenerator M = 0x7FFFFFF A = 1103515245 C

= 12345 def initialize(seed) @value = seed end def next @value = (A * @value + C) % M end end rng = LinearCongruentialGenerator.new(123) p rng.next p rng.next p rng.next 440917719 357222964 2107447239

end
78. ### 58 class LinearCongruentialGenerator # …     def each loop

do end end end
79. ### 59 class LinearCongruentialGenerator # …     def each loop

do self.next end end end
80. ### 60 class LinearCongruentialGenerator # …     def each loop

do self.next yield(@value) end end end
81. ### 61 class LinearCongruentialGenerator # … include Enumerable def each loop

do self.next yield(@value) end end end
82. ### 62 class LinearCongruentialGenerator # … include Enumerable def each loop

do self.next yield(@value) end end end    rng = LinearCongruentialGenerator.new(123)
83. ### 63 class LinearCongruentialGenerator # … include Enumerable def each loop

do self.next yield(@value) end end end    rng = LinearCongruentialGenerator.new(123)  p rng.take(3)
84. ### 64 class LinearCongruentialGenerator # … include Enumerable def each loop

do self.next yield(@value) end end end    rng = LinearCongruentialGenerator.new(123)  p rng.take(3) [440917719, 357222964, 2107447239]

86. ### 66 M = 0x7FFFFFFF A = 1103515245 C = 12345

def make_rng(value) end
87. ### 67 M = 0x7FFFFFFF A = 1103515245 C = 12345

def make_rng(value) Fiber.new do end end
88. ### 68 M = 0x7FFFFFFF A = 1103515245 C = 12345

def make_rng(value) Fiber.new do loop do end end end
89. ### 69 M = 0x7FFFFFFF A = 1103515245 C = 12345

def make_rng(value) Fiber.new do loop do value = (A * value + C) % M end end end
90. ### 70 M = 0x7FFFFFFF A = 1103515245 C = 12345

def make_rng(value) Fiber.new do loop do value = (A * value + C) % M Fiber.yield(value) end end end
91. ### 71 M = 0x7FFFFFFF A = 1103515245 C = 12345

def make_rng(value) Fiber.new do loop do value = (A * value + C) % M Fiber.yield(value) end end end    rng = make_rng(123)
92. ### 72 M = 0x7FFFFFFF A = 1103515245 C = 12345

def make_rng(value) Fiber.new do loop do value = (A * value + C) % M Fiber.yield(value) end end end    rng = make_rng(123) p rng.resume  p rng.resume  p rng.resume
93. ### 73 M = 0x7FFFFFFF A = 1103515245 C = 12345

def make_rng(value) Fiber.new do loop do value = (A * value + C) % M Fiber.yield(value) end end end    rng = make_rng(123) p rng.resume  p rng.resume  p rng.resume 440917719 357222964 2107447239
94. ### 74 M = 0x7FFFFFFF A = 1103515245 C = 12345

def make_rng(value) Enumerator.new do |enum| loop do value = (A * value + C) % M enum.yield(value) end end end    rng = make_rng(123) p rng.next  p rng.next  p rng.next 440917719 357222964 2107447239
95. ### 75 M = 0x7FFFFFFF A = 1103515245 C = 12345

def make_rng(value) Enumerator.new do |enum| loop do value = (A * value + C) % M enum.yield(value) end end end    rng = make_rng(123) p rng.next  p rng.next  p rng.next 440917719 357222964 2107447239
96. ### 76 M = 0x7FFFFFFF A = 1103515245 C = 12345

def make_rng(value) Enumerator.new do |enum| loop do value = (A * value + C) % M enum.yield(value) end end end    rng = make_rng(123) p rng.take(2)
97. ### 77 M = 0x7FFFFFFF A = 1103515245 C = 12345

def make_rng(value) Enumerator.new do |enum| loop do value = (A * value + C) % M enum.yield(value) end end end    rng = make_rng(123) p rng.take(2)    [440917719, 357222964]

104. ### 84 def compile queue = PriorityQueue.new(pages) until queue.empty? page =

queue.next end end
105. ### 85 def compile queue = PriorityQueue.new(pages) until queue.empty? page =

queue.next compile_page(page) end end

111. ### 89 def compile queue = PriorityQueue.new(pages) until queue.empty? page =

queue.next    compile_page(page)        end end
112. ### 90 def compile queue = PriorityQueue.new(pages) until queue.empty? page =

queue.next begin compile_page(page) rescue MissingDependencyError => error end end end
113. ### 91 def compile queue = PriorityQueue.new(pages) until queue.empty? page =

queue.next begin compile_page(page) rescue MissingDependencyError => error queue.add(page) end end end
114. ### 92 def compile queue = PriorityQueue.new(pages) until queue.empty? page =

queue.next begin compile_page(page) rescue MissingDependencyError => error queue.add(page) queue.prioritize(error.missing_page) end end end

130. ### 96 def compile queue = PriorityQueue.new(pages) until queue.empty? page =

queue.next fiber_for(page).resume end end

132. ### 98 def compile queue = PriorityQueue.new(pages) until queue.empty? page =

queue.next result = fiber_for(page).resume end end
133. ### 99 def compile queue = PriorityQueue.new(pages) until queue.empty? page =

queue.next result = fiber_for(page).resume if result.is_a?(MissingDependency) end end end
134. ### 100 def compile queue = PriorityQueue.new(pages) until queue.empty? page =

queue.next result = fiber_for(page).resume if result.is_a?(MissingDependency) queue.add(page) end end end
135. ### 101 def compile queue = PriorityQueue.new(pages) until queue.empty? page =

queue.next result = fiber_for(page).resume if result.is_a?(MissingDependency) queue.add(page) queue.prioritize(result.missing_page) end end end

… ]

144. ### 110 urls.each do |url| response = Net::HTTP.get_response(URI.parse(url)) if response.code !=

"200" end end
145. ### 111 urls.each do |url| response = Net::HTTP.get_response(URI.parse(url)) if response.code !=

"200" puts "Bad URL: #{url}" end end

147. ### 113 urls.each do |url| response = Net::HTTP.get_response(URI.parse(url)) if response.code !=

200 puts "Bad URL: #{url}" end end
148. ### 114 urls.each do |url| Thread.new do response = Net::HTTP.get_response(URI.parse(url)) if

response.code != 200 puts "Bad URL: #{url}" end end end
149. ### 115 threads = urls.map do |url| Thread.new do response =

Net::HTTP.get_response(URI.parse(url)) if response.code != 200 puts "Bad URL: #{url}" end end end
150. ### 116 threads = urls.map do |url| Thread.new do response =

Net::HTTP.get_response(URI.parse(url)) if response.code != 200 puts "Bad URL: #{url}" end end end threads.each(&:join)

157. ### 123 def make_request_fiber(host, path) Fiber.new do socket = NonBlockingSSLSocket.new(host, 443)

end end make_request_fiber('denis.ws', '/cv/').resume
158. ### 124 def make_request_fiber(host, path) Fiber.new do socket = NonBlockingSSLSocket.new(host, 443)

connect_tcp(socket) end end make_request_fiber('denis.ws', '/cv/').resume
159. ### 125 def make_request_fiber(host, path) Fiber.new do socket = NonBlockingSSLSocket.new(host, 443)

connect_tcp(socket) connect_ssl(socket) end end make_request_fiber('denis.ws', '/cv/').resume
160. ### 126 def make_request_fiber(host, path) Fiber.new do socket = NonBlockingSSLSocket.new(host, 443)

connect_tcp(socket) connect_ssl(socket) write(socket, build_http_request(host, path)) end end make_request_fiber('denis.ws', '/cv/').resume
161. ### 127 def make_request_fiber(host, path) Fiber.new do socket = NonBlockingSSLSocket.new(host, 443)

connect_tcp(socket) connect_ssl(socket) write(socket, build_http_request(host, path)) puts read(socket, 1024) end end make_request_fiber('denis.ws', '/cv/').resume
162. ### 128 def make_request_fiber(host, path) Fiber.new do socket = NonBlockingSSLSocket.new(host, 443)

connect_tcp(socket) connect_ssl(socket) write(socket, build_http_request(host, path)) puts read(socket, 1024) end end make_request_fiber('denis.ws', '/cv/').resume

end end

else end end
167. ### 133 def connect_tcp(socket) case socket.connect_tcp when :wait_readable Fiber.current Fiber.yield when

:wait_writable else end end
168. ### 134 def connect_tcp(socket) case socket.connect_tcp when :wait_readable \$awaiting_readable[socket] = Fiber.current

Fiber.yield when :wait_writable else end end

|socket| end
175. ### 141 readable, writable = IO.select( \$awaiting_readable.keys, \$awaiting_writable.keys, ) readable.each do

|socket| fiber = \$awaiting_readable.fetch(socket) end
176. ### 142 readable, writable = IO.select( \$awaiting_readable.keys, \$awaiting_writable.keys, ) readable.each do

|socket| fiber = \$awaiting_readable.fetch(socket) fiber.resume end
177. ### 143 readable, writable = IO.select( \$awaiting_readable.keys, \$awaiting_writable.keys, ) readable.each do

|socket| fiber = \$awaiting_readable.fetch(socket) fiber.resume end writable.each do |socket| fiber = \$awaiting_writable.fetch(socket) fiber.resume end
178. ### 144 loop do readable, writable = IO.select( \$awaiting_readable.keys, \$awaiting_writable.keys, )

readable.each do |socket| fiber = \$awaiting_readable.fetch(socket) fiber.resume end writable.each do |socket| fiber = \$awaiting_writable.fetch(socket) fiber.resume end end
179. ### 145 def connect_tcp(socket) case socket.connect_tcp when :wait_readable \$awaiting_readable[socket] = Fiber.current

Fiber.yield when :wait_writable else end end
180. ### 146 def connect_tcp(socket) case socket.connect_tcp when :wait_readable \$awaiting_readable[socket] = Fiber.current

Fiber.yield \$awaiting_readable.delete(socket) when :wait_writable else end end
181. ### 147 def connect_tcp(socket) case socket.connect_tcp when :wait_readable \$awaiting_readable[socket] = Fiber.current

Fiber.yield \$awaiting_readable.delete(socket) when :wait_writable Fiber.yield else end end
182. ### 148 def connect_tcp(socket) case socket.connect_tcp when :wait_readable \$awaiting_readable[socket] = Fiber.current

Fiber.yield \$awaiting_readable.delete(socket) when :wait_writable \$awaiting_writable[socket] = Fiber.current Fiber.yield else end end
183. ### 149 def connect_tcp(socket) case socket.connect_tcp when :wait_readable \$awaiting_readable[socket] = Fiber.current

Fiber.yield \$awaiting_readable.delete(socket) when :wait_writable \$awaiting_writable[socket] = Fiber.current Fiber.yield \$awaiting_writable.delete(socket) else end end
184. ### 150 def connect_tcp(socket) loop do case socket.connect_tcp when :wait_readable \$awaiting_readable[socket]

= Fiber.current Fiber.yield \$awaiting_readable.delete(socket) when :wait_writable \$awaiting_writable[socket] = Fiber.current Fiber.yield \$awaiting_writable.delete(socket) else end end end
185. ### 151 def connect_tcp(socket) loop do case socket.connect_tcp when :wait_readable \$awaiting_readable[socket]

= Fiber.current Fiber.yield \$awaiting_readable.delete(socket) when :wait_writable \$awaiting_writable[socket] = Fiber.current Fiber.yield \$awaiting_writable.delete(socket) else break end end end
186. ### 152 def make_request_fiber(host, path) Fiber.new do socket = NonBlockingSSLSocket.new(host, 443)

connect_tcp(socket) connect_ssl(socket) write(socket, build_http_request(host, path)) puts read(socket, 1024) end end make_request_fiber('denis.ws', '/cv/').resume
187. ### 153 def make_request_fiber(host, path) Fiber.new do socket = NonBlockingSSLSocket.new(host, 443)

connect_tcp(socket) connect_ssl(socket) write(socket, build_http_request(host, path)) puts read(socket, 1024) end end make_request_fiber('denis.ws', '/cv/').resume

end end

end end

195. ### 160 require 'async' require 'async/http' endpoint = Async::HTTP::URLEndpoint.parse('https://denis.ws') client =

Async::HTTP::Client.new(endpoint)
196. ### 161 require 'async' require 'async/http' endpoint = Async::HTTP::URLEndpoint.parse('https://denis.ws') client =

Async::HTTP::Client.new(endpoint) Async do |task| end
197. ### 162 require 'async' require 'async/http' endpoint = Async::HTTP::URLEndpoint.parse('https://denis.ws') client =

Async::HTTP::Client.new(endpoint) Async do |task| task.async { p client.get("/").status } task.async { p client.get("/cv/").status } task.async { p client.get("/software/").status } task.async { p client.get("/talks/").status } task.async { p client.get("/toolbox/").status } end
198. ### 162 require 'async' require 'async/http' endpoint = Async::HTTP::URLEndpoint.parse('https://denis.ws') client =

Async::HTTP::Client.new(endpoint) Async do |task| task.async { p client.get("/").status } task.async { p client.get("/cv/").status } task.async { p client.get("/software/").status } task.async { p client.get("/talks/").status } task.async { p client.get("/toolbox/").status } end https://github.com/socketry/async

ﬁber
204. ### 165 f = Fiber.new { |…| … } Create a

ﬁber r = f.resume(…) Start or resume a ﬁber
205. ### 165 f = Fiber.new { |…| … } Create a

ﬁber r = f.resume(…) Start or resume a ﬁber a = Fiber.yield(…) Return from a ﬁber