polyphony 0.43.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (221) hide show
  1. checksums.yaml +7 -0
  2. data/.gitbook.yaml +4 -0
  3. data/.github/workflows/test.yml +29 -0
  4. data/.gitignore +59 -0
  5. data/.rubocop.yml +175 -0
  6. data/CHANGELOG.md +393 -0
  7. data/Gemfile +3 -0
  8. data/Gemfile.lock +141 -0
  9. data/LICENSE +21 -0
  10. data/README.md +51 -0
  11. data/Rakefile +26 -0
  12. data/TODO.md +201 -0
  13. data/bin/polyphony-debug +87 -0
  14. data/docs/_config.yml +64 -0
  15. data/docs/_includes/head.html +40 -0
  16. data/docs/_includes/title.html +1 -0
  17. data/docs/_sass/custom/custom.scss +10 -0
  18. data/docs/_sass/overrides.scss +0 -0
  19. data/docs/_user-guide/all-about-timers.md +126 -0
  20. data/docs/_user-guide/index.md +9 -0
  21. data/docs/_user-guide/web-server.md +136 -0
  22. data/docs/api-reference/exception.md +27 -0
  23. data/docs/api-reference/fiber.md +425 -0
  24. data/docs/api-reference/index.md +9 -0
  25. data/docs/api-reference/io.md +36 -0
  26. data/docs/api-reference/object.md +99 -0
  27. data/docs/api-reference/polyphony-baseexception.md +33 -0
  28. data/docs/api-reference/polyphony-cancel.md +26 -0
  29. data/docs/api-reference/polyphony-moveon.md +24 -0
  30. data/docs/api-reference/polyphony-net.md +20 -0
  31. data/docs/api-reference/polyphony-process.md +28 -0
  32. data/docs/api-reference/polyphony-resourcepool.md +59 -0
  33. data/docs/api-reference/polyphony-restart.md +18 -0
  34. data/docs/api-reference/polyphony-terminate.md +18 -0
  35. data/docs/api-reference/polyphony-threadpool.md +67 -0
  36. data/docs/api-reference/polyphony-throttler.md +77 -0
  37. data/docs/api-reference/polyphony.md +36 -0
  38. data/docs/api-reference/thread.md +88 -0
  39. data/docs/assets/img/echo-fibers.svg +1 -0
  40. data/docs/assets/img/sleeping-fiber.svg +1 -0
  41. data/docs/faq.md +195 -0
  42. data/docs/favicon.ico +0 -0
  43. data/docs/getting-started/index.md +10 -0
  44. data/docs/getting-started/installing.md +34 -0
  45. data/docs/getting-started/overview.md +486 -0
  46. data/docs/getting-started/tutorial.md +359 -0
  47. data/docs/index.md +94 -0
  48. data/docs/main-concepts/concurrency.md +151 -0
  49. data/docs/main-concepts/design-principles.md +161 -0
  50. data/docs/main-concepts/exception-handling.md +291 -0
  51. data/docs/main-concepts/extending.md +89 -0
  52. data/docs/main-concepts/fiber-scheduling.md +197 -0
  53. data/docs/main-concepts/index.md +9 -0
  54. data/docs/polyphony-logo.png +0 -0
  55. data/examples/adapters/concurrent-ruby.rb +9 -0
  56. data/examples/adapters/pg_client.rb +36 -0
  57. data/examples/adapters/pg_notify.rb +35 -0
  58. data/examples/adapters/pg_pool.rb +43 -0
  59. data/examples/adapters/pg_transaction.rb +31 -0
  60. data/examples/adapters/redis_blpop.rb +12 -0
  61. data/examples/adapters/redis_channels.rb +122 -0
  62. data/examples/adapters/redis_client.rb +19 -0
  63. data/examples/adapters/redis_pubsub.rb +26 -0
  64. data/examples/adapters/redis_pubsub_perf.rb +68 -0
  65. data/examples/core/01-spinning-up-fibers.rb +18 -0
  66. data/examples/core/02-awaiting-fibers.rb +20 -0
  67. data/examples/core/03-interrupting.rb +39 -0
  68. data/examples/core/04-handling-signals.rb +19 -0
  69. data/examples/core/xx-agent.rb +102 -0
  70. data/examples/core/xx-at_exit.rb +29 -0
  71. data/examples/core/xx-caller.rb +12 -0
  72. data/examples/core/xx-channels.rb +45 -0
  73. data/examples/core/xx-daemon.rb +14 -0
  74. data/examples/core/xx-deadlock.rb +8 -0
  75. data/examples/core/xx-deferring-an-operation.rb +14 -0
  76. data/examples/core/xx-erlang-style-genserver.rb +81 -0
  77. data/examples/core/xx-exception-backtrace.rb +40 -0
  78. data/examples/core/xx-fork-cleanup.rb +22 -0
  79. data/examples/core/xx-fork-spin.rb +42 -0
  80. data/examples/core/xx-fork-terminate.rb +27 -0
  81. data/examples/core/xx-forking.rb +24 -0
  82. data/examples/core/xx-move_on.rb +23 -0
  83. data/examples/core/xx-pingpong.rb +18 -0
  84. data/examples/core/xx-queue-async.rb +120 -0
  85. data/examples/core/xx-readpartial.rb +18 -0
  86. data/examples/core/xx-recurrent-timer.rb +12 -0
  87. data/examples/core/xx-resource_delegate.rb +31 -0
  88. data/examples/core/xx-signals.rb +16 -0
  89. data/examples/core/xx-sleep-forever.rb +9 -0
  90. data/examples/core/xx-sleeping.rb +25 -0
  91. data/examples/core/xx-snooze-starve.rb +16 -0
  92. data/examples/core/xx-spin-fork.rb +49 -0
  93. data/examples/core/xx-spin_error_backtrace.rb +33 -0
  94. data/examples/core/xx-state-machine.rb +51 -0
  95. data/examples/core/xx-stop.rb +20 -0
  96. data/examples/core/xx-supervise-process.rb +30 -0
  97. data/examples/core/xx-supervisors.rb +21 -0
  98. data/examples/core/xx-thread-selector-sleep.rb +51 -0
  99. data/examples/core/xx-thread-selector-snooze.rb +46 -0
  100. data/examples/core/xx-thread-sleep.rb +17 -0
  101. data/examples/core/xx-thread-snooze.rb +34 -0
  102. data/examples/core/xx-thread_pool.rb +17 -0
  103. data/examples/core/xx-throttling.rb +18 -0
  104. data/examples/core/xx-timeout.rb +10 -0
  105. data/examples/core/xx-timer-gc.rb +17 -0
  106. data/examples/core/xx-trace.rb +79 -0
  107. data/examples/core/xx-using-a-mutex.rb +21 -0
  108. data/examples/core/xx-worker-thread.rb +30 -0
  109. data/examples/io/tunnel.rb +48 -0
  110. data/examples/io/xx-backticks.rb +11 -0
  111. data/examples/io/xx-echo_client.rb +25 -0
  112. data/examples/io/xx-echo_client_from_stdin.rb +21 -0
  113. data/examples/io/xx-echo_pipe.rb +16 -0
  114. data/examples/io/xx-echo_server.rb +17 -0
  115. data/examples/io/xx-echo_server_with_timeout.rb +34 -0
  116. data/examples/io/xx-echo_stdin.rb +14 -0
  117. data/examples/io/xx-happy-eyeballs.rb +36 -0
  118. data/examples/io/xx-httparty.rb +38 -0
  119. data/examples/io/xx-irb.rb +17 -0
  120. data/examples/io/xx-net-http.rb +15 -0
  121. data/examples/io/xx-open.rb +16 -0
  122. data/examples/io/xx-switch.rb +15 -0
  123. data/examples/io/xx-system.rb +11 -0
  124. data/examples/io/xx-tcpserver.rb +15 -0
  125. data/examples/io/xx-tcpsocket.rb +18 -0
  126. data/examples/io/xx-zip.rb +19 -0
  127. data/examples/performance/fiber_transfer.rb +47 -0
  128. data/examples/performance/fs_read.rb +38 -0
  129. data/examples/performance/mem-usage.rb +56 -0
  130. data/examples/performance/messaging.rb +29 -0
  131. data/examples/performance/multi_snooze.rb +33 -0
  132. data/examples/performance/snooze.rb +39 -0
  133. data/examples/performance/snooze_raw.rb +39 -0
  134. data/examples/performance/thread-vs-fiber/polyphony_mt_server.rb +74 -0
  135. data/examples/performance/thread-vs-fiber/polyphony_server.rb +45 -0
  136. data/examples/performance/thread-vs-fiber/polyphony_server_read_loop.rb +58 -0
  137. data/examples/performance/thread-vs-fiber/threaded_server.rb +27 -0
  138. data/examples/performance/thread-vs-fiber/xx-httparty_multi.rb +36 -0
  139. data/examples/performance/thread-vs-fiber/xx-httparty_threaded.rb +29 -0
  140. data/examples/performance/thread_pool_perf.rb +63 -0
  141. data/examples/performance/xx-array.rb +11 -0
  142. data/examples/performance/xx-fiber-switch.rb +9 -0
  143. data/examples/performance/xx-snooze.rb +15 -0
  144. data/examples/xx-spin.rb +32 -0
  145. data/ext/libev/Changes +548 -0
  146. data/ext/libev/LICENSE +37 -0
  147. data/ext/libev/README +59 -0
  148. data/ext/libev/README.embed +3 -0
  149. data/ext/libev/ev.c +5279 -0
  150. data/ext/libev/ev.h +856 -0
  151. data/ext/libev/ev_epoll.c +296 -0
  152. data/ext/libev/ev_kqueue.c +224 -0
  153. data/ext/libev/ev_linuxaio.c +642 -0
  154. data/ext/libev/ev_poll.c +156 -0
  155. data/ext/libev/ev_port.c +192 -0
  156. data/ext/libev/ev_select.c +316 -0
  157. data/ext/libev/ev_vars.h +215 -0
  158. data/ext/libev/ev_win32.c +162 -0
  159. data/ext/libev/ev_wrap.h +216 -0
  160. data/ext/libev/test_libev_win32.c +123 -0
  161. data/ext/polyphony/extconf.rb +20 -0
  162. data/ext/polyphony/fiber.c +109 -0
  163. data/ext/polyphony/libev.c +2 -0
  164. data/ext/polyphony/libev.h +9 -0
  165. data/ext/polyphony/libev_agent.c +882 -0
  166. data/ext/polyphony/polyphony.c +71 -0
  167. data/ext/polyphony/polyphony.h +97 -0
  168. data/ext/polyphony/polyphony_ext.c +21 -0
  169. data/ext/polyphony/queue.c +168 -0
  170. data/ext/polyphony/ring_buffer.c +96 -0
  171. data/ext/polyphony/ring_buffer.h +28 -0
  172. data/ext/polyphony/thread.c +208 -0
  173. data/ext/polyphony/tracing.c +11 -0
  174. data/lib/polyphony.rb +136 -0
  175. data/lib/polyphony/adapters/fs.rb +19 -0
  176. data/lib/polyphony/adapters/irb.rb +52 -0
  177. data/lib/polyphony/adapters/postgres.rb +110 -0
  178. data/lib/polyphony/adapters/process.rb +33 -0
  179. data/lib/polyphony/adapters/redis.rb +67 -0
  180. data/lib/polyphony/adapters/trace.rb +138 -0
  181. data/lib/polyphony/core/channel.rb +46 -0
  182. data/lib/polyphony/core/exceptions.rb +36 -0
  183. data/lib/polyphony/core/global_api.rb +124 -0
  184. data/lib/polyphony/core/resource_pool.rb +117 -0
  185. data/lib/polyphony/core/sync.rb +21 -0
  186. data/lib/polyphony/core/thread_pool.rb +64 -0
  187. data/lib/polyphony/core/throttler.rb +41 -0
  188. data/lib/polyphony/event.rb +17 -0
  189. data/lib/polyphony/extensions/core.rb +174 -0
  190. data/lib/polyphony/extensions/fiber.rb +379 -0
  191. data/lib/polyphony/extensions/io.rb +221 -0
  192. data/lib/polyphony/extensions/openssl.rb +81 -0
  193. data/lib/polyphony/extensions/socket.rb +150 -0
  194. data/lib/polyphony/extensions/thread.rb +108 -0
  195. data/lib/polyphony/net.rb +77 -0
  196. data/lib/polyphony/version.rb +5 -0
  197. data/polyphony.gemspec +40 -0
  198. data/test/coverage.rb +54 -0
  199. data/test/eg.rb +27 -0
  200. data/test/helper.rb +56 -0
  201. data/test/q.rb +24 -0
  202. data/test/run.rb +5 -0
  203. data/test/stress.rb +25 -0
  204. data/test/test_agent.rb +130 -0
  205. data/test/test_event.rb +59 -0
  206. data/test/test_ext.rb +196 -0
  207. data/test/test_fiber.rb +988 -0
  208. data/test/test_global_api.rb +352 -0
  209. data/test/test_io.rb +249 -0
  210. data/test/test_kernel.rb +57 -0
  211. data/test/test_process_supervision.rb +46 -0
  212. data/test/test_queue.rb +112 -0
  213. data/test/test_resource_pool.rb +138 -0
  214. data/test/test_signal.rb +100 -0
  215. data/test/test_socket.rb +34 -0
  216. data/test/test_supervise.rb +103 -0
  217. data/test/test_thread.rb +170 -0
  218. data/test/test_thread_pool.rb +101 -0
  219. data/test/test_throttler.rb +50 -0
  220. data/test/test_trace.rb +68 -0
  221. metadata +482 -0
@@ -0,0 +1,87 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: utf-8
3
+ # frozen_string_literal: true
4
+
5
+ require 'bundler/setup'
6
+ require 'polyphony'
7
+
8
+ Gyro.trace(true)
9
+
10
+ FILE_CACHE = {}
11
+ @ready = nil
12
+ @last_location = nil
13
+ @mode = :step
14
+ @current_fiber = nil
15
+ @stacks = Hash.new([])
16
+
17
+ def debug_prompt
18
+ loop do
19
+ STDOUT << '(debug) '
20
+ case (cmd = STDIN.gets.chomp)
21
+ when '.q', '.quit'
22
+ exit!
23
+ when 's', 'step'
24
+ @mode = :step
25
+ return
26
+ else
27
+ begin
28
+ result = binding.eval(cmd)
29
+ p result
30
+ rescue Exception => e
31
+ p e
32
+ puts e.backtrace.join("\n")
33
+ end
34
+ end
35
+ end
36
+ end
37
+
38
+ def get_snippet(path, lineno)
39
+ lines = FILE_CACHE[path] ||= IO.read(path).lines
40
+ start_idx = lineno - 5
41
+ stop_idx = lineno + 3
42
+ stop_idx = lines.size - 1 if stop_idx >= lines.size
43
+ start_idx = 0 if start_idx < 0
44
+ (start_idx..stop_idx).map { |idx| [idx + 1, lines[idx]]}
45
+ end
46
+
47
+ def print_snippet(snippet, cur_line)
48
+ snippet.each do |(lineno, line)|
49
+ is_cur = lineno == cur_line
50
+ formatted = format("%s%03d %s", is_cur ? '*=> ' : ' ', lineno, line)
51
+ puts formatted
52
+ end
53
+ end
54
+
55
+ tp = Polyphony::Trace.new(*Polyphony::Trace::STOCK_EVENTS, :fiber_all) do |r|
56
+ unless @ready
57
+ @ready = true if r[:event] == :return
58
+ @current_fiber = r[:fiber]
59
+ next
60
+ end
61
+
62
+ case r[:event]
63
+ when :call, :b_call, :c_call
64
+ @stacks[r[:fiber]] << r
65
+ when :return, :b_return, :c_return
66
+ @stacks[r[:fiber]].pop
67
+ when :line
68
+ case @mode
69
+ when :step
70
+ if r[:location] != @last_location && r[:fiber] == @current_fiber
71
+ @last_location = r[:location]
72
+ puts "in #{r[:location]}"
73
+ puts
74
+ snippet = get_snippet(r[:path], r[:lineno])
75
+ print_snippet(snippet, r[:lineno])
76
+ puts
77
+ debug_prompt
78
+ end
79
+ end
80
+ end
81
+ rescue Exception => e
82
+ p e
83
+ exit!
84
+ end
85
+ tp.enable
86
+
87
+ require File.expand_path(ARGV[0])
@@ -0,0 +1,64 @@
1
+ title: "Polyphony"
2
+ description: Fine-grained concurrency for Ruby
3
+
4
+ plugins:
5
+ - jekyll-remote-theme
6
+
7
+ permalink: pretty
8
+ remote_theme: pmarsceill/just-the-docs
9
+ color_scheme: light
10
+
11
+ search_enabled: true
12
+ search:
13
+ # Split pages into sections that can be searched individually
14
+ # Supports 1 - 6, default: 2
15
+ heading_level: 2
16
+ # Maximum amount of previews per search result
17
+ # Default: 3
18
+ previews: 3
19
+ # Maximum amount of words to display before a matched word in the preview
20
+ # Default: 5
21
+ preview_words_before: 5
22
+ # Maximum amount of words to display after a matched word in the preview
23
+ # Default: 10
24
+ preview_words_after: 10
25
+ # Set the search token separator
26
+ # Default: /[\s\-/]+/
27
+ # Example: enable support for hyphenated search words
28
+ tokenizer_separator: /[\s/]+/
29
+ # Display the relative url in search results
30
+ # Supports true (default) or false
31
+ rel_url: true
32
+ # Enable or disable the search button that appears in the bottom right corner of every page
33
+ # Supports true or false (default)
34
+ button: false
35
+
36
+ aux_links:
37
+ "Polyphony on GitHub":
38
+ - "//github.com/digital-fabric/polyphony"
39
+
40
+ # Makes Aux links open in a new tab. Default is false
41
+ aux_links_new_tab: false
42
+
43
+ # Enable or disable heading anchors
44
+ heading_anchors: true
45
+
46
+ back_to_top: true
47
+ back_to_top_text: "Back to top"
48
+
49
+ footer_content: "Copyright &copy; 2020 Sharon Rosner. Distributed by an <a href=\"https://github.com/digital-fabric/polyphony/tree/master/LICENSE\">MIT license.</a>"
50
+
51
+ # Footer "Edit this page on GitHub" link text
52
+ gh_edit_link: true # show or hide edit this page link
53
+ gh_edit_link_text: "Edit this page on GitHub"
54
+ gh_edit_repository: "https://github.com/digital-fabric/polyphony" # the github URL for your repo
55
+ gh_edit_branch: "master/docs" # the branch that your docs is served from
56
+ gh_edit_view_mode: "tree" # "tree" or "edit" if you want the user to jump into the editor immediately
57
+
58
+ compress_html:
59
+ clippings: all
60
+ comments: all
61
+ endings: all
62
+ startings: []
63
+ blanklines: false
64
+ profile: false
@@ -0,0 +1,40 @@
1
+ <head>
2
+ <meta charset="UTF-8">
3
+ <meta http-equiv="X-UA-Compatible" content="IE=Edge">
4
+
5
+ {% unless site.plugins contains "jekyll-seo-tag" %}
6
+ <title>{{ page.title }} - {{ site.title }}</title>
7
+
8
+ {% if page.description %}
9
+ <meta name="Description" content="{{ page.description }}">
10
+ {% endif %}
11
+ {% endunless %}
12
+
13
+ <link rel="shortcut icon" href="{{ 'polyphony-logo.png' | absolute_url }}" type="image/png">
14
+
15
+ <link rel="stylesheet" href="{{ '/assets/css/just-the-docs-default.css' | absolute_url }}">
16
+
17
+ {% if site.ga_tracking != nil %}
18
+ <script async src="https://www.googletagmanager.com/gtag/js?id={{ site.ga_tracking }}"></script>
19
+ <script>
20
+ window.dataLayer = window.dataLayer || [];
21
+ function gtag(){dataLayer.push(arguments);}
22
+ gtag('js', new Date());
23
+
24
+ gtag('config', '{{ site.ga_tracking }}'{% unless site.ga_tracking_anonymize_ip == nil %}, { 'anonymize_ip': true }{% endunless %});
25
+ </script>
26
+
27
+ {% endif %}
28
+
29
+ {% if site.search_enabled != false %}
30
+ <script type="text/javascript" src="{{ '/assets/js/vendor/lunr.min.js' | absolute_url }}"></script>
31
+ {% endif %}
32
+ <script type="text/javascript" src="{{ '/assets/js/just-the-docs.js' | absolute_url }}"></script>
33
+
34
+ <meta name="viewport" content="width=device-width, initial-scale=1">
35
+
36
+ {% seo %}
37
+
38
+ {% include head_custom.html %}
39
+
40
+ </head>
@@ -0,0 +1 @@
1
+ <img src="{{ 'polyphony-logo.png' | absolute_url }}" style="height: 1.5em; margin-right: 0.5em">Polyphony
@@ -0,0 +1,10 @@
1
+
2
+ h1.logo-title {
3
+ font-size: 42px !important;
4
+ font-weight: bold;
5
+ }
6
+
7
+ h2.logo-title {
8
+ margin-top: 0.25em;
9
+ margin-bottom: 1em;
10
+ }
File without changes
@@ -0,0 +1,126 @@
1
+ ---
2
+ layout: page
3
+ title: All About Timers
4
+ nav_order: 1
5
+ parent: User Guide
6
+ permalink: /user-guide/all-about-timers/
7
+ ---
8
+ # All About Timers
9
+
10
+ Timers form a major part of writing dynamic concurrent programs. They allow
11
+ programmers to create delays and to perform recurring operations with a
12
+ controllable frequency. Crucially, they also enable the implementation of
13
+ timeouts, which are an important aspect of concurrent programming.
14
+
15
+ ## Sleeping
16
+
17
+ Sometimes, your code needs to wait for a certain period of time. For example,
18
+ implementing a retry mechanism for failed HTTP requests might involve waiting
19
+ for a few seconds before retrying. Polyphony patches the `Kernel#sleep` method
20
+ to be fiber-aware, that is to yield control of execution while waiting for a
21
+ timer to elapse.
22
+
23
+ ```ruby
24
+ # This is a naive retry implementation
25
+ def fetch(url)
26
+ fetch_url(url)
27
+ rescue
28
+ sleep 1
29
+ retry
30
+ end
31
+ ```
32
+
33
+ ## Sleeping Forever
34
+
35
+ The `#sleep` method can also be used to sleep forever, if no argument is given:
36
+
37
+ ```ruby
38
+ puts "Go to sleep"
39
+ sleep
40
+ puts "Woke up" # this line will not be executed
41
+ ```
42
+
43
+ The `#sleep` forever call can be used for example in the main fiber when we do
44
+ all our work in other fibers, since once the main fiber terminates the program
45
+ exits.
46
+
47
+ ## Doing Work Later
48
+
49
+ While `#sleep` allows you to block execution of the current fiber, sometimes you
50
+ want to perform some work later, while not blocking the current fiber. This is done simply by spinning off another fiber:
51
+
52
+ ```ruby
53
+ do_some_stuff
54
+ spin do
55
+ sleep 3
56
+ do_stuff_later
57
+ end
58
+ do_some_more_stuff
59
+ ```
60
+
61
+ ## Using timeouts
62
+
63
+ Polyphony provides the following global methods for using timeouts:
64
+
65
+ - `#move_on_after` - used for cancelling an operation after a certain period of time without raising an exception:
66
+
67
+ ```ruby
68
+ move_on_after 1 do
69
+ sleep 60
70
+ end
71
+ ```
72
+
73
+ This method also takes an optional return value argument:
74
+
75
+ ```ruby
76
+ move_on_after 1, with_value: 'bar' do
77
+ sleep 60
78
+ 'foo'
79
+ end #=> 'bar'
80
+ ```
81
+
82
+ - `#cancel_after` - used for cancelling an operation after a certain period of time with a `Cancel` exception:
83
+
84
+ ```ruby
85
+ cancel_after 1 do
86
+ sleep 60
87
+ end #=> raises Cancel
88
+ ```
89
+
90
+ Polyphony also provides a fiber-aware version of the core Ruby `Timeout` API, which may be used directly or indirectly to interrupt blocking operations.
91
+
92
+ ## Using Raw Timers
93
+
94
+ Polyphony implements timers through the `Gyro::Timer` class, which encapsulates
95
+ libev timer watchers. Using `Gyro::Timer` you can create both one-time and
96
+ recurring timers:
97
+
98
+ ```ruby
99
+ # Create a one-time timer
100
+ one_time = Gyro::Timer.new(1, 0)
101
+
102
+ # Create a recurring timer
103
+ recurring = Gyro::Timer.new(0.5, 1.5)
104
+ ```
105
+
106
+ Once your timer is created, you can wait for it using the `#await` method:
107
+
108
+ ```ruby
109
+ def delay(duration)
110
+ timer = Gyro::Timer.new(duration, 0)
111
+ timer.await
112
+ end
113
+ ```
114
+
115
+ Waiting for the timer will *block* the current timer. This means that if you
116
+ want to do other work while waiting for the timer, you need to put it on a
117
+ different fiber:
118
+
119
+ ```ruby
120
+ timer = Gyro::Timer.new(3, 0)
121
+ spin {
122
+ sleep 3
123
+ do_something_else
124
+ }
125
+ do_blocking_operation
126
+ ```
@@ -0,0 +1,9 @@
1
+ ---
2
+ layout: page
3
+ title: User Guide
4
+ has_children: true
5
+ nav_order: 4
6
+ ---
7
+
8
+ # User Guide
9
+ {: .no_toc }
@@ -0,0 +1,136 @@
1
+ ---
2
+ layout: page
3
+ title: Web Server
4
+ nav_order: 5
5
+ parent: User Guide
6
+ permalink: /user-guide/web-server/
7
+ ---
8
+ # Web Server
9
+
10
+ Polyphony's web server functionality offers a powerful and flexible way to
11
+ create Ruby-based web servers and web applications. In addition to supporting
12
+ both HTTP 1 and HTTP 2, it supports seamless Websocket upgrades (and indeed
13
+ arbitrary protocol upgrade), TLS termination, and automatic ALPN-based protocol
14
+ selection. In addition, it includes a Rack adapter for running Rack
15
+ applications. Polyphony's web server offers excellent performance
16
+ characteristics, in terms of throughput, memory consumption and scalability
17
+ (benchmarks are forthcoming).
18
+
19
+ What makes Polyphony's web server design stand out is the fact that incoming
20
+ requests can be processed immediately upon receiving the complete headers,
21
+ without needing to wait for the entire request body to be received. This design
22
+ allows web applications to properly buffer uploads of large files without
23
+ consuming large amounts of RAM, as well as reject requests without waiting for
24
+ the entire request body.
25
+
26
+ ## A basic web server
27
+
28
+ ```ruby
29
+ require 'tipi'
30
+
31
+ Tipi.serve('0.0.0.0', 1234) do |request|
32
+ request.respond("Hello world!\n")
33
+ end
34
+ ```
35
+
36
+ Note that requests are handled using a callback block which takes a single
37
+ argument. The `request` object provides the entire API for responding to the
38
+ client.
39
+
40
+ Each client connection will be handled in a separate fiber, allowing
41
+ concurrent processing of incoming requests.
42
+
43
+ ## HTTP 2 support
44
+
45
+ HTTP 2 support is baked in to the server, which supports both HTTP 2 upgrades
46
+ (for example on a non-encrypted connection) and ALPN-based protocol selection,
47
+ in a completely effortless manner.
48
+
49
+ Since HTTP 2 connections are multiplexed, allowing multiple concurrent requests
50
+ on a single connection, each HTTP 2 stream is handled in a separate fiber.
51
+
52
+ ## TLS termination
53
+
54
+ TLS termination can be handled by passing a `secure_context` option to the
55
+ server:
56
+
57
+ ```ruby
58
+ require 'tipi'
59
+ require 'localhost/authority'
60
+
61
+ authority = Localhost::Authority.fetch
62
+ opts = { secure_context: authority.server_context }
63
+
64
+ Tipi.serve('0.0.0.0', 1234, opts) do |request|
65
+ request.respond("Hello world!\n")
66
+ end
67
+ ```
68
+
69
+ ## Websockets && HTTP upgrades
70
+
71
+ Polyphony's web server makes it really easy to integrate websocket communication
72
+ with normal HTTP processing:
73
+
74
+ ```ruby
75
+ require 'tipi'
76
+ require 'tipi/websocket'
77
+
78
+ ws_handler = Polyphony::Websocket.handler do |ws|
79
+ while (msg = ws.recv)
80
+ ws << "you said: #{msg}"
81
+ end
82
+ end
83
+
84
+ opts = {
85
+ upgrade: { websocket: ws_handler }
86
+ }
87
+
88
+ Tipi.serve('0.0.0.0', 1234, opts) do |request|
89
+ request.respond("Hello world!\n")
90
+ end
91
+ ```
92
+
93
+ Polyphony also supports general-purpose HTTP upgrades using the same mechanism:
94
+
95
+ ```ruby
96
+ require 'tipi'
97
+
98
+ opts = {
99
+ upgrade: {
100
+ echo: ->(conn) {
101
+ while (msg = conn.gets)
102
+ conn << "You said: #{msg}"
103
+ end
104
+ }
105
+ }
106
+ }
107
+
108
+ Tipi.serve('0.0.0.0', 1234, opts) do |request|
109
+ request.respond("Hello world!\n")
110
+ end
111
+ ```
112
+
113
+ ## Sending HTTP responses
114
+
115
+ The response API provides multiple ways of responding, with or without a body,
116
+ and enables streaming (using chunked encoding for HTTP/1.1 connections). Here's
117
+ an example of an SSE response:
118
+
119
+ ```ruby
120
+ require 'tipi'
121
+
122
+ def sse_response(request)
123
+ request.send_headers('Content-Type': 'text/event-stream')
124
+ move_on_after(10) {
125
+ loop {
126
+ request.send_chunk("data: #{Time.now}\n\n")
127
+ sleep 1
128
+ }
129
+ }
130
+ ensure
131
+ request.send_chunk("retry: 0\n\n", done: true)
132
+ end
133
+
134
+ Tipi.serve('0.0.0.0', 1234, &method(:sse_response))
135
+ ```
136
+