Denis Defreyne
April 13, 2019
110

# An introduction to fibers

April 13, 2019

## Transcript

1. Fibers

2. An
introduction
to
Denis Defreyne
Ruby Wine
April 13th, 2019
Fibers

3. 3
1.

4. 3
1. 2.

5. Basics
4

6. 5
f = lambda do
puts "giraffe"
end
f.call

7. 5
f = lambda do
puts "giraffe"
end
f.call
giraffe

8. 6
f = Fiber.new do
puts "giraffe"
end
f.resume

9. 6
f = Fiber.new do
puts "giraffe"
end
f.resume
giraffe

10. Photo by Harshil Gudka on Unsplash

11. 8
f = lambda do
puts "zebra"
end
f.call
zebra

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 ':
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

19. Photo by Arleen wiese on Unsplash

20. 13
f = lambda do
puts "donkey"
end
f.call
donkey

21. 14
f = lambda do
puts "donkey"
end
f.call
f.call

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

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

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

25. 15
f = Fiber.new do
puts "donkey"
end
f.resume
f.resume
donkey
a.rb:6:
in `resume':
(FiberError)

26. Photo by Spencer Watson on Unsplash

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

32. Photo by Austin Elder on Unsplash

33. Image by sandid from Pixabay

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

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

37. 23
f = Fiber.new do
puts 'quokka'
end
f.resume
quokka

38. 24
f = Fiber.new do |a|
puts a
end
f.resume('quokka')

39. 24
f = Fiber.new do |a|
puts a
end
f.resume('quokka')
quokka

40. Image by Tracey Wong from Pixabay

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

45. Image by coolcoolleah from Pixabay

46. Photo by Jonatan Pie on Unsplash

47. 31
f = Fiber.new do
puts 'red panda'
end
f.resume
red panda

48. 32
f = Fiber.new do
'red panda'
end
puts f.resume

49. 32
f = Fiber.new do
'red panda'
end
puts f.resume
red panda

50. Photo by Michael Payne on Unsplash

51. 34
f = Fiber.new do
puts 'maine coon'
end
f.resume
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

55. Photo by Bee Felten-Leidel on Unsplash

56. Photo by Gary Bendig on Unsplash

57. 39
f = Fiber.new { |…| … }
Create a ﬁ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

60. Three
Practical
Applications
40

61. 1.
Generators
41

62. 42
[7, 2, 4].each do |num|
puts num
end
7
2
4

63. 43
class PeopleDirectory
end

64. 44
class PeopleDirectory
KATIE = 'Dr. Katie Bouman'
ANITA = 'Dr. Anita Borg'
end

65. 45
class PeopleDirectory
KATIE = 'Dr. Katie Bouman'
ANITA = 'Dr. Anita Borg'
def each
end
end

66. 46
class PeopleDirectory
KATIE = 'Dr. Katie Bouman'
ANITA = 'Dr. Anita Borg'
def each
yield KATIE
yield ANITA
end
end

67. 47
class PeopleDirectory
KATIE = 'Dr. Katie Bouman'
ANITA = 'Dr. Anita Borg'
def each
yield KATIE
yield ANITA
end
end
PeopleDirectory.new

68. 48
class PeopleDirectory
KATIE = 'Dr. Katie Bouman'
ANITA = 'Dr. Anita Borg'
def each
yield KATIE
yield ANITA
end
end
PeopleDirectory.new.each do |name|
puts name
end

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

70. 50
class LinearCongruentialGenerator

71. 51
class LinearCongruentialGenerator
M = 0x7FFFFFF
A = 1103515245
C = 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

77. 57
class LinearCongruentialGenerator
# …

def each
end
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]

85. 65
M = 0x7FFFFFFF
A = 1103515245
C = 12345

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]

98. 1.
Generators ✓
78

99. 2.
Resumable computation
79

100. 80

101. 81
def compile
end

102. 82
def compile
queue = PriorityQueue.new(pages)
end

103. 83
def compile
queue = PriorityQueue.new(pages)
until queue.empty?
end
end

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

106. 86

107. 86

108. 86

109. 87
def compiled_content_of(page)
@compiled_content[page]
end

110. 88
def compiled_content_of(page)
unless @compiled_content.key?(page)
raise MissingDependencyError.new(page)
end
@compiled_content[page]
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
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.prioritize(error.missing_page)
end
end
end

115. 93

116. 93

117. 93

118. 93

119. 93

120. 93

121. 93

122. 93

123. 94

124. 94

125. 94

126. 94

127. 94

128. 94

129. 95
def fiber_for(page)
@fibers[page] ||=
Fiber.new { compile_page(page) }
end

130. 96
def compile
queue = PriorityQueue.new(pages)
until queue.empty?
page = queue.next
fiber_for(page).resume
end
end

131. 97
def compiled_content_of(page)
unless @compiled_content.key?(page)
Fiber.yield(MissingDependency.new(page))
end
@compiled_content[page]
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)
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.prioritize(result.missing_page)
end
end
end

136. 102
def compiled_content_of(page)
unless @compiled_content.key?(page)
Fiber.yield(MissingDependency.new(page))
end
@compiled_content[page]
end

137. 2.
Resumable computation ✓
103

138. 3.
Non-blocking I/O
104

139. 105

140. 106
urls = [
'https://denis.ws',
'https://denis.ws/cv',
'https://denis.ws/software',
'https://denis.ws/talks',
'https://denis.ws/toolbox',
'https://nanoc.ws',

]

141. 107
Version 1
(sequential)

142. 108
urls.each do |url|
end

143. 109
urls.each do |url|
response = Net::HTTP.get_response(URI.parse(url))
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"
end
end

146. 112
Version 2

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

148. 114
urls.each do |url|
response = Net::HTTP.get_response(URI.parse(url))
if response.code != 200
end
end
end

149. 115
urls.map do |url|
response = Net::HTTP.get_response(URI.parse(url))
if response.code != 200
end
end
end

150. 116
urls.map do |url|
response = Net::HTTP.get_response(URI.parse(url))
if response.code != 200
end
end
end

151. 117
Version 3
(non-blocking IO)

152. This is a low-level implementation.
118
CAUTION I

153. Sockets must never block.
119
CAUTION II

154. 120
make_request_fiber('denis.ws', '/cv/').resume

155. 121
def make_request_fiber(host, path)
end
make_request_fiber('denis.ws', '/cv/').resume

156. 122
def make_request_fiber(host, path)
Fiber.new do
end
end
make_request_fiber('denis.ws', '/cv/').resume

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))
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))
end
end
make_request_fiber('denis.ws', '/cv/').resume

163. 129
def connect_tcp(socket)
end

164. 130
def connect_tcp(socket)
socket.connect_tcp
end

165. 131
def connect_tcp(socket)
case socket.connect_tcp
when :wait_writable
else
end
end

166. 132
def connect_tcp(socket)
case socket.connect_tcp
Fiber.yield
when :wait_writable
else
end
end

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

168. 134
def connect_tcp(socket)
case socket.connect_tcp
Fiber.yield
when :wait_writable
else
end
end

169. 135
make_request_fiber('denis.ws', '/cv/').resume

170. 136
make_request_fiber('denis.ws', '/cv/').resume
# … fiber got suspended; now what?

171. 137
IO.select(
)

172. 138
IO.select(
\$awaiting_writable.keys,
)

173. 139
IO.select(
\$awaiting_writable.keys,
)

174. 140
IO.select(
\$awaiting_writable.keys,
)
end

175. 141
IO.select(
\$awaiting_writable.keys,
)
end

176. 142
IO.select(
\$awaiting_writable.keys,
)
fiber.resume
end

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

178. 144
loop do
IO.select(
\$awaiting_writable.keys,
)
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
Fiber.yield
when :wait_writable
else
end
end

180. 146
def connect_tcp(socket)
case socket.connect_tcp
Fiber.yield
when :wait_writable
else
end
end

181. 147
def connect_tcp(socket)
case socket.connect_tcp
Fiber.yield
when :wait_writable
Fiber.yield
else
end
end

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

183. 149
def connect_tcp(socket)
case socket.connect_tcp
Fiber.yield
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
Fiber.yield
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
Fiber.yield
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))
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))
end
end
make_request_fiber('denis.ws', '/cv/').resume

188. 154
def connect_ssl(socket)
end

189. 155
def connect_ssl(socket)
socket.connect_ssl
end

190. 156
def connect_ssl(socket)
case socket.connect_ssl
end

191. 157
def connect_ssl(socket)
case socket.connect_ssl
when :wait_writable
else
end
end

192. 157
def connect_ssl(socket)
case socket.connect_ssl
when :wait_writable
else
end
end

193. 158
require 'async'
require 'async/http'

194. 159
require 'async'
require 'async/http'
endpoint = Async::HTTP::URLEndpoint.parse('https://denis.ws')

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)
end

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

198. 162
require 'async'
require 'async/http'
endpoint = Async::HTTP::URLEndpoint.parse('https://denis.ws')
client = Async::HTTP::Client.new(endpoint)
end
https://github.com/socketry/async

199. 3.
Non-blocking I/O ✓
163

200. 164
1.

201. 164
1. 2.

202. 164
1. 2. 3.

203. 165
f = Fiber.new { |…| … }
Create a ﬁ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

206. Now you know
everything