polyphony 0.13 → 0.14

Sign up to get free protection for your applications and to get access to all the features.
Files changed (135) hide show
  1. checksums.yaml +4 -4
  2. data/.gitbook.yaml +5 -0
  3. data/.gitignore +55 -0
  4. data/.rubocop.yml +49 -0
  5. data/CHANGELOG.md +13 -2
  6. data/Gemfile +3 -0
  7. data/Gemfile.lock +31 -0
  8. data/LICENSE +21 -0
  9. data/README.md +35 -18
  10. data/Rakefile +20 -0
  11. data/TODO.md +49 -0
  12. data/docs/getting-started/getting-started.md +10 -0
  13. data/docs/getting-started/tutorial.md +2 -0
  14. data/docs/summary.md +9 -0
  15. data/examples/core/cancel.rb +10 -0
  16. data/examples/core/channel_echo.rb +43 -0
  17. data/examples/core/enumerator.rb +14 -0
  18. data/examples/core/fork.rb +22 -0
  19. data/examples/core/genserver.rb +74 -0
  20. data/examples/core/lock.rb +20 -0
  21. data/examples/core/move_on.rb +11 -0
  22. data/examples/core/move_on_twice.rb +17 -0
  23. data/examples/core/move_on_with_ensure.rb +17 -0
  24. data/examples/core/multiple_async.rb +17 -0
  25. data/examples/core/nested_async.rb +18 -0
  26. data/examples/core/nested_cancel.rb +41 -0
  27. data/examples/core/nested_multiple_async.rb +19 -0
  28. data/examples/core/next_tick.rb +13 -0
  29. data/examples/core/pulse.rb +13 -0
  30. data/examples/core/resource.rb +29 -0
  31. data/examples/core/resource_cancel.rb +34 -0
  32. data/examples/core/resource_delegate.rb +32 -0
  33. data/examples/core/sleep.rb +9 -0
  34. data/examples/core/sleep2.rb +13 -0
  35. data/examples/core/spawn.rb +15 -0
  36. data/examples/core/spawn_cancel.rb +19 -0
  37. data/examples/core/spawn_error.rb +28 -0
  38. data/examples/core/supervisor.rb +22 -0
  39. data/examples/core/supervisor_with_cancel_scope.rb +24 -0
  40. data/examples/core/supervisor_with_error.rb +23 -0
  41. data/examples/core/supervisor_with_manual_move_on.rb +25 -0
  42. data/examples/core/thread.rb +30 -0
  43. data/examples/core/thread_cancel.rb +30 -0
  44. data/examples/core/thread_pool.rb +60 -0
  45. data/examples/core/throttle.rb +17 -0
  46. data/examples/fs/read.rb +37 -0
  47. data/examples/interfaces/pg_client.rb +38 -0
  48. data/examples/interfaces/pg_pool.rb +37 -0
  49. data/examples/interfaces/pg_query.rb +32 -0
  50. data/examples/interfaces/redis_channels.rb +119 -0
  51. data/examples/interfaces/redis_client.rb +21 -0
  52. data/examples/interfaces/redis_pubsub.rb +26 -0
  53. data/examples/interfaces/redis_pubsub_perf.rb +65 -0
  54. data/examples/io/config.ru +3 -0
  55. data/examples/io/echo_client.rb +22 -0
  56. data/examples/io/echo_server.rb +14 -0
  57. data/examples/io/echo_server_with_timeout.rb +33 -0
  58. data/examples/io/echo_stdin.rb +15 -0
  59. data/examples/io/happy_eyeballs.rb +32 -0
  60. data/examples/io/http_client.rb +19 -0
  61. data/examples/io/http_server.js +24 -0
  62. data/examples/io/http_server.rb +16 -0
  63. data/examples/io/http_server_forked.rb +27 -0
  64. data/examples/io/http_server_throttled.rb +16 -0
  65. data/examples/io/http_ws_server.rb +42 -0
  66. data/examples/io/https_client.rb +17 -0
  67. data/examples/io/https_server.rb +23 -0
  68. data/examples/io/https_wss_server.rb +46 -0
  69. data/examples/io/rack_server.rb +19 -0
  70. data/examples/io/rack_server_https.rb +24 -0
  71. data/examples/io/rack_server_https_forked.rb +32 -0
  72. data/examples/io/websocket_server.rb +33 -0
  73. data/examples/io/ws_page.html +34 -0
  74. data/examples/io/wss_page.html +34 -0
  75. data/examples/performance/perf_multi_snooze.rb +21 -0
  76. data/examples/performance/perf_snooze.rb +30 -0
  77. data/examples/performance/thread-vs-fiber/polyphony_server.rb +63 -0
  78. data/examples/performance/thread-vs-fiber/threaded_server.rb +27 -0
  79. data/examples/streams/lines.rb +27 -0
  80. data/examples/streams/stdio.rb +18 -0
  81. data/ext/ev/async.c +168 -0
  82. data/ext/ev/child.c +169 -0
  83. data/ext/ev/ev.h +32 -0
  84. data/ext/ev/ev_ext.c +20 -0
  85. data/ext/ev/ev_module.c +222 -0
  86. data/ext/ev/io.c +405 -0
  87. data/ext/ev/libev.h +9 -0
  88. data/ext/ev/signal.c +119 -0
  89. data/ext/ev/timer.c +197 -0
  90. data/ext/libev/Changes +513 -0
  91. data/ext/libev/LICENSE +37 -0
  92. data/ext/libev/README +58 -0
  93. data/ext/libev/README.embed +3 -0
  94. data/ext/libev/ev.c +5214 -0
  95. data/ext/libev/ev.h +849 -0
  96. data/ext/libev/ev_epoll.c +285 -0
  97. data/ext/libev/ev_kqueue.c +218 -0
  98. data/ext/libev/ev_poll.c +151 -0
  99. data/ext/libev/ev_port.c +189 -0
  100. data/ext/libev/ev_select.c +316 -0
  101. data/ext/libev/ev_vars.h +204 -0
  102. data/ext/libev/ev_win32.c +162 -0
  103. data/ext/libev/ev_wrap.h +200 -0
  104. data/ext/libev/test_libev_win32.c +123 -0
  105. data/lib/polyphony.rb +7 -2
  106. data/lib/polyphony/core.rb +1 -1
  107. data/lib/polyphony/core/{coroutine.rb → coprocess.rb} +10 -10
  108. data/lib/polyphony/core/exceptions.rb +5 -5
  109. data/lib/polyphony/core/supervisor.rb +16 -16
  110. data/lib/polyphony/core/thread.rb +1 -1
  111. data/lib/polyphony/extensions/io.rb +43 -42
  112. data/lib/polyphony/extensions/kernel.rb +10 -34
  113. data/lib/polyphony/extensions/postgres.rb +3 -2
  114. data/lib/polyphony/extensions/redis.rb +1 -1
  115. data/lib/polyphony/extensions/socket.rb +8 -4
  116. data/lib/polyphony/extensions/ssl.rb +0 -54
  117. data/lib/polyphony/http/agent.rb +4 -10
  118. data/lib/polyphony/http/http1.rb +25 -25
  119. data/lib/polyphony/http/http1_request.rb +38 -26
  120. data/lib/polyphony/http/http2.rb +4 -5
  121. data/lib/polyphony/http/http2_request.rb +12 -18
  122. data/lib/polyphony/http/rack.rb +1 -3
  123. data/lib/polyphony/http/server.rb +9 -9
  124. data/lib/polyphony/net.rb +2 -2
  125. data/lib/polyphony/resource_pool.rb +5 -1
  126. data/lib/polyphony/version.rb +1 -1
  127. data/lib/polyphony/websocket.rb +52 -0
  128. data/polyphony.gemspec +31 -0
  129. data/test/test_coprocess.rb +131 -0
  130. data/test/test_core.rb +274 -0
  131. data/test/test_ev.rb +117 -0
  132. data/test/test_io.rb +38 -0
  133. metadata +113 -7
  134. data/lib/polyphony/core/async.rb +0 -36
  135. data/lib/polyphony/net_old.rb +0 -299
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f95ac584e1a686e3ff45ec50878bce9cc3ea0457f2894e0839e69c79621e3ab9
4
- data.tar.gz: 189eb3effd86a54ab16eec64cd4159ce851fdbd249a6b227c02ecfedfd35c987
3
+ metadata.gz: 78b1d2bf93b1562b6ef1010acdf4a30993d6fd8f40e001ab13a00606e0ed2137
4
+ data.tar.gz: ea0f410c483ee06da41e1acdd342f9fccadf87c51bbca258571d8f5d2c6048a5
5
5
  SHA512:
6
- metadata.gz: e935934c596936a1fcce5ecf5f87cb288245755bd4e08f9dce6fde16cffd486134f66bc209774418b1942b00c13e015f6bfa6c55026e1233fe7a4533aa3411b6
7
- data.tar.gz: a95b472caf17fa3c5a1f7afb8757e891e445a9afe7770131054a1db49c4a4529afa85acc723e1b621f4ae1200de4bfaef42a6a72b8002decbba4324be21ea3d5
6
+ metadata.gz: 70af3ec736856b611bd1d4242f6430761b83382f88239a31a1a7e4124fbe6804153e08d248c03c61510c272f9511cdf7f6f4ada277eab2cbd6809f2e41daaa39
7
+ data.tar.gz: 678dad06dab6f6dbbccc23e39ac45e8045b4fdf0b2ea29959fd34e243d9d0f19f624ef80a8022f7dde4790e080302f59b06e65ec3ca26c9fe5efc4a70856f2c2
@@ -0,0 +1,5 @@
1
+ root: ./docs/
2
+ structure:
3
+ readme: ../README.md
4
+ summary: ./summary.md
5
+ getting-started: ./getting-started.md
@@ -0,0 +1,55 @@
1
+ *.gem
2
+ *.rbc
3
+ /.config
4
+ /coverage/
5
+ /InstalledFiles
6
+ /pkg/
7
+ /spec/reports/
8
+ /spec/examples.txt
9
+ /test/tmp/
10
+ /test/version_tmp/
11
+ /tmp/
12
+
13
+ # Used by dotenv library to load environment variables.
14
+ # .env
15
+
16
+ ## Specific to RubyMotion:
17
+ .dat*
18
+ .repl_history
19
+ build/
20
+ *.bridgesupport
21
+ build-iPhoneOS/
22
+ build-iPhoneSimulator/
23
+
24
+ ## Specific to RubyMotion (use of CocoaPods):
25
+ #
26
+ # We recommend against adding the Pods directory to your .gitignore. However
27
+ # you should judge for yourself, the pros and cons are mentioned at:
28
+ # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
29
+ #
30
+ # vendor/Pods/
31
+
32
+ ## Documentation cache and generated files:
33
+ /.yardoc/
34
+ /_yardoc/
35
+ /doc/
36
+ /rdoc/
37
+
38
+ ## Environment normalization:
39
+ /.bundle/
40
+ /vendor/bundle
41
+ /lib/bundler/man/
42
+
43
+ # for a library or gem, you might want to ignore these files since the code is
44
+ # intended to run in multiple environments; otherwise, check them in:
45
+ # Gemfile.lock
46
+ # .ruby-version
47
+ # .ruby-gemset
48
+
49
+ # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
50
+ .rvmrc
51
+
52
+ test.rb
53
+ .vscode
54
+
55
+ lib/ev_ext.bundle
@@ -0,0 +1,49 @@
1
+ AllCops:
2
+ TargetRubyVersion: 2.5
3
+ RubyInterpreters:
4
+ - ruby
5
+ Exclude:
6
+ - '**/*.gemspec'
7
+ - 'test/**/*.rb'
8
+ - 'examples/**/*.rb'
9
+ - 'Gemfile*'
10
+
11
+ Style/LambdaCall:
12
+ Enabled: false
13
+ # Style/ModuleFunction:
14
+ # Enabled: false
15
+ # Style/RegexpLiteral:
16
+ # Enabled: false
17
+
18
+ # Naming/MemoizedInstanceVariableName:
19
+ # Enabled: false
20
+
21
+ Style/Alias:
22
+ EnforcedStyle: prefer_alias_method
23
+
24
+ Style/SpecialGlobalVars:
25
+ Enabled: false
26
+
27
+ Style/ClassAndModuleChildren:
28
+ Enabled: false
29
+
30
+ Metrics/AbcSize:
31
+ Enabled: false
32
+
33
+ Style/MixinUsage:
34
+ Enabled: false
35
+
36
+ Style/MultilineBlockChain:
37
+ Enabled: false
38
+
39
+ Lint/RescueException:
40
+ Enabled: false
41
+
42
+ Lint/InheritException:
43
+ Enabled: false
44
+
45
+ Style/NumericPredicate:
46
+ Enabled: false
47
+
48
+ Style/TrivialAccessors:
49
+ Enabled: false
@@ -1,3 +1,14 @@
1
+ 0.14 2019-05-17
2
+ ---------------
3
+
4
+ * Use chunked encoding in HTTP 1 response
5
+ * Rewrite IO#read, #readpartial, #write in C (about 30% performance improvement)
6
+ * Add method delegation to `ResourcePool`
7
+ * Optimize PG::Connection#async_exec
8
+ * Fix Coprocess#cancel!
9
+ * Preliminary support for websocket (see `examples/io/http_ws_server.rb`)
10
+ * Rename `Coroutine` to `Coprocess`
11
+
1
12
  0.13 2019-01-05
2
13
  ---------------
3
14
 
@@ -18,8 +29,8 @@
18
29
 
19
30
  * Move reactor loop to secondary fiber, allow blocking operations on main
20
31
  fiber.
21
- * Example implementation of erlang-style generic server pattern (implement
22
- async API to a coroutine)
32
+ * Example implementation of erlang-style generic server pattern (implement async
33
+ API to a coroutine)
23
34
  * Implement coroutine mailboxes, Coroutine#<<, Coroutine#receive, Kernel.receive
24
35
  for message passing
25
36
  * Add Coroutine.current for getting current coroutine
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
@@ -0,0 +1,31 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ polyphony (0.14)
5
+ http-2 (= 0.10.0)
6
+ http_parser.rb (= 0.6.0)
7
+ modulation (= 0.23)
8
+
9
+ GEM
10
+ remote: https://rubygems.org/
11
+ specs:
12
+ http-2 (0.10.0)
13
+ http_parser.rb (0.6.0)
14
+ localhost (1.1.4)
15
+ minitest (5.11.3)
16
+ modulation (0.23)
17
+ rake (12.3.2)
18
+ rake-compiler (1.0.5)
19
+ rake
20
+
21
+ PLATFORMS
22
+ ruby
23
+
24
+ DEPENDENCIES
25
+ localhost (= 1.1.4)
26
+ minitest (= 5.11.3)
27
+ polyphony!
28
+ rake-compiler (= 1.0.5)
29
+
30
+ BUNDLED WITH
31
+ 1.17.2
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2018 Sharon Rosner
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md CHANGED
@@ -19,7 +19,7 @@ supports Linux and MacOS only. This software is currently at the alpha stage.
19
19
  Polyphony is a library for building concurrent applications in Ruby. Polyphony
20
20
  harnesses the power of
21
21
  [Ruby fibers](https://ruby-doc.org/core-2.5.1/Fiber.html) to provide a
22
- cooperative, sequential coroutine-based concurrency model. Under the hood,
22
+ cooperative, sequential coprocess-based concurrency model. Under the hood,
23
23
  Polyphony uses [libev](https://github.com/enki/libev) as a high-performance event
24
24
  reactor that provides timers, I/O watchers and other asynchronous event
25
25
  primitives.
@@ -31,16 +31,18 @@ takes care of context-switching automatically whenever a blocking call like
31
31
 
32
32
  ## Features
33
33
 
34
+ - **Full-blown, integrated, high-performance HTTP 1 / HTTP 2 / WebSocket server
35
+ with TLS/SSL termination and automatic ALPN protocol selection**.
34
36
  - Co-operative scheduling of concurrent tasks using Ruby fibers.
35
37
  - High-performance event reactor for handling I/O events and timers.
36
38
  - Natural, sequential programming style that makes it easy to reason about
37
39
  concurrent code.
38
- - Higher-order constructs for controlling the execution of concurrent code:
40
+ - Abstractions and constructs for controlling the execution of concurrent code:
39
41
  coprocesses, supervisors, cancel scopes, throttling, resource pools etc.
40
42
  - Code can use native networking classes and libraries, growing support for
41
43
  third-party gems such as `pg` and `redis`.
42
- - Comprehensive HTTP 1.0 / 1.1 / 2 client and server APIs.
43
- - Excellent performance and scalability characteristics, in terms of both
44
+ - HTTP 1 / HTTP 2 client
45
+ - Competitive performance and scalability characteristics, in terms of both
44
46
  throughput and memory consumption.
45
47
 
46
48
  ## Prior Art
@@ -89,12 +91,12 @@ end
89
91
  In the above example, both `sleep` calls will be executed concurrently, and thus
90
92
  the program will take approximately only 1 second to execute. Note the lack of
91
93
  any boilerplate relating to concurrency. Each `spawn` block starts a
92
- *coroutine*, and is executed in sequential manner.
94
+ *coprocess*, and is executed in sequential manner.
93
95
 
94
- > **Coroutines - the basic unit of concurrency**: In Polyphony, concurrent
95
- > operations take place inside coroutines. A `Coroutine` is executed on top of a
96
- > `Fiber`, which allows it to be suspended whenever a blocking operation is
97
- > called, and resumed once that operation has been completed. Coroutines offer
96
+ > **Coprocesses - the basic unit of concurrency**: In Polyphony, concurrent
97
+ > operations take place inside coprocesses. A `Coprocess` is executed on top of
98
+ > a `Fiber`, which allows it to be suspended whenever a blocking operation is
99
+ > called, and resumed once that operation has been completed. Coprocesses offer
98
100
  > significant advantages over threads - they consume only about 10KB, switching
99
101
  > between them is much faster than switching threads, and literally millions of
100
102
  > them can be spawned without affecting performance*. Besides, Ruby does not yet
@@ -113,7 +115,7 @@ require 'polyphony'
113
115
 
114
116
  server = TCPServer.open(1234)
115
117
  while client = server.accept
116
- # spawn starts a new coroutine on a separate fiber
118
+ # spawn starts a new coprocess on a separate fiber
117
119
  spawn {
118
120
  while data = client.read rescue nil
119
121
  client.write(data)
@@ -128,10 +130,10 @@ This example demonstrates several features of Polyphony:
128
130
  server. The result of `server.accept` is also a native `TCPSocket` object.
129
131
  There are no wrapper classes being used.
130
132
  - The only hint of the code being concurrent is the use of `Kernel#spawn`,
131
- which starts a new coroutine on a dedicated fiber. This allows serving
133
+ which starts a new coprocess on a dedicated fiber. This allows serving
132
134
  multiple clients at once. Whenever a blocking call is issued, such as
133
135
  `#accept` or `#read`, execution is *yielded* to the event loop, which will
134
- resume only those coroutines which are ready to be resumed.
136
+ resume only those coprocesses which are ready to be resumed.
135
137
  - Exception handling is done using the normal Ruby constructs `raise`, `rescue`
136
138
  and `ensure`. Exceptions never go unhandled (as might be the case with Ruby
137
139
  threads), and must be dealt with explicitly. An unhandled exception will cause
@@ -224,9 +226,9 @@ In order to facilitate writing concurrent code, Polyphony provides additional
224
226
  constructs that make it easier to spawn concurrent tasks and to control them.
225
227
 
226
228
  `CancelScope` - an abstraction used to cancel the execution of one or more
227
- coroutines or supervisors. It usually works by defining a timeout for the
229
+ coprocesses or supervisors. It usually works by defining a timeout for the
228
230
  completion of a task. Any blocking operation can be cancelled, including
229
- a coroutine or a supervisor. The developer may choose to cancel with or without
231
+ a coprocess or a supervisor. The developer may choose to cancel with or without
230
232
  an exception with `cancel` or `move_on`, respectively. Cancel scopes are
231
233
  typically started using `Kernel.cancel_after` and `Kernel.move_on`:
232
234
 
@@ -261,9 +263,24 @@ Pool = Polyphony::ResourcePool.new(limit: 5) {
261
263
  }
262
264
  ```
263
265
 
264
- `Supervisor` - a class used to control one or more `Coroutine`s. It can be used
265
- to start, stop and restart multiple coroutines. A supervisor can also be
266
- used for awaiting the completion of multiple coroutines. It is usually started
266
+ You can also call arbitrary methods on the resource pool, which will be
267
+ delegated to the resource using `#method_missing`:
268
+
269
+ ```ruby
270
+ # up to 5 concurrent connections
271
+ Pool = Polyphony::ResourcePool.new(limit: 5) {
272
+ # the block sets up the resource
273
+ PG.connect(...)
274
+ }
275
+
276
+ 1000.times {
277
+ spawn { p Pool.query('select 1') }
278
+ }
279
+ ```
280
+
281
+ `Supervisor` - a class used to control one or more `Coprocess`s. It can be used
282
+ to start, stop and restart multiple coprocesses. A supervisor can also be
283
+ used for awaiting the completion of multiple coprocesses. It is usually started
267
284
  using `Kernel.supervise`:
268
285
 
269
286
  ```ruby
@@ -289,7 +306,7 @@ result = Polyphony::ThreadPool.process { long_running_process }
289
306
  `Throttler` - a mechanism for throttling an arbitrary task, such as sending of
290
307
  emails, or crawling a website. A throttler is normally created using
291
308
  `Kernel.throttle`, and can even be used to throttle operations across multiple
292
- coroutines:
309
+ coprocesses:
293
310
 
294
311
  ```ruby
295
312
  server = Net.tcp_listen(1234)
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rake/clean"
5
+
6
+ # frozen_string_literal: true
7
+
8
+ require "rake/extensiontask"
9
+ Rake::ExtensionTask.new("ev_ext") do |ext|
10
+ ext.ext_dir = "ext/ev"
11
+ end
12
+
13
+ task :default => [:compile, :test]
14
+ task :test do
15
+ Dir.glob('./test/test_*.rb').each { |file| require(file) }
16
+ end
17
+
18
+ # task default: %w[compile]# spec rubocop]
19
+
20
+ CLEAN.include "**/*.o", "**/*.so", "**/*.bundle", "**/*.jar", "pkg", "tmp"
data/TODO.md ADDED
@@ -0,0 +1,49 @@
1
+ ## Testing
2
+
3
+ - test IO
4
+ - test TCP server / client
5
+ - test thread / thread_pool modules
6
+
7
+ ## UDP socket
8
+
9
+ ```ruby
10
+ socket = UDPSocket.new
11
+ socket.bind("127.0.0.1", 1234)
12
+
13
+ socket.send "message-to-self", 0, "127.0.0.1", 1234
14
+ p socket.recvfrom(10)
15
+ #=> ["message-to", ["AF_INET", 4913, "localhost", "127.0.0.1"]]
16
+ ```
17
+
18
+ ## DNS client
19
+
20
+ ```ruby
21
+ ip_address = DNS.lookup('google.com', 'A')
22
+ ```
23
+
24
+ Prior art:
25
+
26
+ - https://github.com/alexdalitz/dnsruby
27
+ - https://github.com/eventmachine/eventmachine/blob/master/lib/em/resolver.rb
28
+ - https://github.com/gmodarelli/em-resolv-replace/blob/master/lib/em-dns-resolver.rb
29
+ - https://github.com/socketry/async-dns
30
+
31
+ ### DNS server
32
+
33
+ ```ruby
34
+ Server = import('../../lib/polyphony/dns/server')
35
+
36
+ server = Server.new do |transaction|
37
+ puts "got query from #{transaction.info[:client_ip_address]}"
38
+ transaction.questions.each do |q|
39
+ respond(transaction, q[:domain], q[:resource_class])
40
+ end
41
+ end
42
+
43
+ server.listen(port: 5300)
44
+ puts "listening on port 5300"
45
+ ```
46
+
47
+ Prior art:
48
+
49
+ - https://github.com/socketry/async-dns
@@ -0,0 +1,10 @@
1
+ # Installing
2
+
3
+ ## Installing
4
+
5
+ ```bash
6
+ $ gem install polyphony
7
+ ```
8
+
9
+ ## Tutorial
10
+
@@ -0,0 +1,2 @@
1
+ # Tutorial
2
+
@@ -0,0 +1,9 @@
1
+ # Table of contents
2
+
3
+ * [Introduction](../README.md)
4
+
5
+ ## Getting Started
6
+
7
+ * [Installing](getting-started/getting-started.md)
8
+ * [Tutorial](getting-started/tutorial.md)
9
+
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'modulation'
4
+
5
+ Polyphony = import('../../lib/polyphony')
6
+
7
+ puts "going to sleep..."
8
+ cancel_after(1) do
9
+ sleep(60)
10
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'modulation'
4
+
5
+ Polyphony = import('../../lib/polyphony')
6
+
7
+ def echo(rchan, wchan)
8
+ puts "start echoer"
9
+ while msg = rchan.receive
10
+ wchan << "you said: #{msg}"
11
+ end
12
+ ensure
13
+ puts "echoer stopped"
14
+ end
15
+
16
+ chan1, chan2 = Polyphony::Channel.new, Polyphony::Channel.new
17
+
18
+ echoer = spawn { echo(chan1, chan2) }
19
+
20
+ spawn do
21
+ puts "start receiver"
22
+ while msg = chan2.receive
23
+ puts msg
24
+ $main.resume if msg =~ /world/
25
+ end
26
+ ensure
27
+ puts "receiver stopped"
28
+ end
29
+
30
+ $main = spawn do
31
+ t0 = Time.now
32
+ puts "send hello"
33
+ chan1 << "hello"
34
+ puts "send world"
35
+ chan1 << "world"
36
+
37
+ suspend
38
+
39
+ puts "closing channels"
40
+ chan1.close
41
+ chan2.close
42
+ puts "done #{Time.now - t0}"
43
+ end