polyphony 0.38 → 0.43

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 (112) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/test.yml +11 -2
  3. data/.gitignore +2 -2
  4. data/.rubocop.yml +30 -0
  5. data/CHANGELOG.md +25 -2
  6. data/Gemfile.lock +15 -12
  7. data/README.md +2 -1
  8. data/Rakefile +3 -3
  9. data/TODO.md +27 -97
  10. data/docs/_config.yml +56 -7
  11. data/docs/_sass/custom/custom.scss +0 -30
  12. data/docs/_sass/overrides.scss +0 -46
  13. data/docs/{user-guide → _user-guide}/all-about-timers.md +0 -0
  14. data/docs/_user-guide/index.md +9 -0
  15. data/docs/{user-guide → _user-guide}/web-server.md +0 -0
  16. data/docs/api-reference/fiber.md +2 -2
  17. data/docs/api-reference/index.md +9 -0
  18. data/docs/api-reference/polyphony-process.md +1 -1
  19. data/docs/api-reference/thread.md +1 -1
  20. data/docs/faq.md +21 -11
  21. data/docs/getting-started/index.md +10 -0
  22. data/docs/getting-started/installing.md +2 -6
  23. data/docs/getting-started/overview.md +486 -0
  24. data/docs/getting-started/tutorial.md +27 -19
  25. data/docs/index.md +1 -1
  26. data/docs/main-concepts/concurrency.md +0 -5
  27. data/docs/main-concepts/design-principles.md +69 -21
  28. data/docs/main-concepts/extending.md +1 -1
  29. data/docs/main-concepts/index.md +9 -0
  30. data/examples/core/01-spinning-up-fibers.rb +1 -0
  31. data/examples/core/03-interrupting.rb +4 -1
  32. data/examples/core/04-handling-signals.rb +19 -0
  33. data/examples/core/xx-agent.rb +102 -0
  34. data/examples/core/xx-sleeping.rb +14 -6
  35. data/examples/io/tunnel.rb +48 -0
  36. data/examples/io/xx-irb.rb +1 -1
  37. data/examples/performance/thread-vs-fiber/polyphony_mt_server.rb +7 -6
  38. data/examples/performance/thread-vs-fiber/polyphony_server.rb +13 -36
  39. data/examples/performance/thread-vs-fiber/polyphony_server_read_loop.rb +58 -0
  40. data/examples/performance/xx-array.rb +11 -0
  41. data/examples/performance/xx-fiber-switch.rb +9 -0
  42. data/examples/performance/xx-snooze.rb +15 -0
  43. data/ext/{gyro → polyphony}/extconf.rb +2 -2
  44. data/ext/{gyro → polyphony}/fiber.c +17 -23
  45. data/ext/{gyro → polyphony}/libev.c +0 -0
  46. data/ext/{gyro → polyphony}/libev.h +0 -0
  47. data/ext/polyphony/libev_agent.c +718 -0
  48. data/ext/polyphony/libev_queue.c +216 -0
  49. data/ext/{gyro/gyro.c → polyphony/polyphony.c} +16 -40
  50. data/ext/{gyro/gyro.h → polyphony/polyphony.h} +19 -39
  51. data/ext/polyphony/polyphony_ext.c +23 -0
  52. data/ext/{gyro → polyphony}/socket.c +21 -18
  53. data/ext/polyphony/thread.c +206 -0
  54. data/ext/{gyro → polyphony}/tracing.c +1 -1
  55. data/lib/polyphony.rb +19 -14
  56. data/lib/polyphony/adapters/irb.rb +1 -1
  57. data/lib/polyphony/adapters/postgres.rb +6 -5
  58. data/lib/polyphony/adapters/process.rb +5 -5
  59. data/lib/polyphony/adapters/trace.rb +28 -28
  60. data/lib/polyphony/core/channel.rb +3 -3
  61. data/lib/polyphony/core/exceptions.rb +1 -1
  62. data/lib/polyphony/core/global_api.rb +13 -11
  63. data/lib/polyphony/core/resource_pool.rb +3 -3
  64. data/lib/polyphony/core/sync.rb +2 -2
  65. data/lib/polyphony/core/thread_pool.rb +6 -6
  66. data/lib/polyphony/core/throttler.rb +13 -6
  67. data/lib/polyphony/event.rb +27 -0
  68. data/lib/polyphony/extensions/core.rb +22 -14
  69. data/lib/polyphony/extensions/fiber.rb +4 -4
  70. data/lib/polyphony/extensions/io.rb +59 -25
  71. data/lib/polyphony/extensions/openssl.rb +36 -16
  72. data/lib/polyphony/extensions/socket.rb +27 -9
  73. data/lib/polyphony/extensions/thread.rb +16 -9
  74. data/lib/polyphony/net.rb +9 -9
  75. data/lib/polyphony/version.rb +1 -1
  76. data/polyphony.gemspec +4 -4
  77. data/test/helper.rb +14 -1
  78. data/test/test_agent.rb +124 -0
  79. data/test/{test_async.rb → test_event.rb} +15 -7
  80. data/test/test_ext.rb +25 -4
  81. data/test/test_fiber.rb +19 -10
  82. data/test/test_global_api.rb +4 -4
  83. data/test/test_io.rb +46 -24
  84. data/test/test_queue.rb +74 -0
  85. data/test/test_signal.rb +3 -40
  86. data/test/test_socket.rb +34 -0
  87. data/test/test_thread.rb +37 -16
  88. data/test/test_trace.rb +6 -5
  89. metadata +40 -43
  90. data/docs/_includes/nav.html +0 -51
  91. data/docs/_includes/prevnext.html +0 -17
  92. data/docs/_layouts/default.html +0 -106
  93. data/docs/api-reference.md +0 -11
  94. data/docs/api-reference/gyro-async.md +0 -57
  95. data/docs/api-reference/gyro-child.md +0 -29
  96. data/docs/api-reference/gyro-queue.md +0 -44
  97. data/docs/api-reference/gyro-timer.md +0 -51
  98. data/docs/api-reference/gyro.md +0 -25
  99. data/docs/getting-started.md +0 -10
  100. data/docs/main-concepts.md +0 -10
  101. data/docs/user-guide.md +0 -10
  102. data/examples/core/forever_sleep.rb +0 -19
  103. data/ext/gyro/async.c +0 -162
  104. data/ext/gyro/child.c +0 -141
  105. data/ext/gyro/gyro_ext.c +0 -33
  106. data/ext/gyro/io.c +0 -489
  107. data/ext/gyro/queue.c +0 -142
  108. data/ext/gyro/selector.c +0 -228
  109. data/ext/gyro/signal.c +0 -133
  110. data/ext/gyro/thread.c +0 -308
  111. data/ext/gyro/timer.c +0 -149
  112. data/test/test_timer.rb +0 -56
@@ -1,17 +0,0 @@
1
- {%- for node in site.html_pages -%}
2
- {%- if page.prev_title == node.title -%}
3
- {%- assign prev_url = node.url -%}
4
- {%- endif -%}
5
- {%- if page.next_title == node.title -%}
6
- {%- assign next_url = node.url -%}
7
- {%- endif -%}
8
- {%- endfor -%}
9
- <div id="prevnext">
10
- {%- if prev_url -%}
11
- <span class="prev"><a href="{{prev_url | relative_url}}">☜ {{page.prev_title}}</a></span>
12
- {%- endif -%}
13
- {%- if next_url -%}
14
- <span class="next"><a href="{{next_url | relative_url}}">{{page.next_title}} ☞</a></span>
15
- {%- endif -%}
16
- <span class="clear">&nbsp;</span>
17
- </div>
@@ -1,106 +0,0 @@
1
- ---
2
- layout: table_wrappers
3
- ---
4
-
5
- <!DOCTYPE html>
6
-
7
- <html lang="{{ site.lang | default: "en-US" }}">
8
- {% include head.html %}
9
- <body>
10
- <svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
11
- <symbol id="link" viewBox="0 0 16 16">
12
- <title>Link</title>
13
- <path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path>
14
- </symbol>
15
- </svg>
16
-
17
- <div class="page-wrap">
18
- <div class="side-bar">
19
- <div class="site-header">
20
- <a href="{{ site.url }}{{ site.baseurl }}" class="site-title lh-tight">{% include title.html %}</a>
21
- <button class="menu-button fs-3 js-main-nav-trigger" data-text-toggle="Hide" type="button">Menu</button>
22
- </div>
23
-
24
- <div class="navigation main-nav js-main-nav">
25
- {% include nav.html %}
26
- </div>
27
- <footer class="site-footer">
28
- <p class="text-small text-grey-dk-000 mb-4">This site uses <a href="https://github.com/pmarsceill/just-the-docs">Just the Docs</a>, a documentation theme for Jekyll.</p>
29
- </footer>
30
- </div>
31
- <div class="main-content-wrap js-main-content" tabindex="0">
32
- <div class="main-content">
33
- <div class="page-header js-page-header">
34
- {% if site.search_enabled != false %}
35
- <div class="search">
36
- <div class="search-input-wrap">
37
- <input type="text" class="js-search-input search-input" tabindex="0" placeholder="Search {{ site.title }}" aria-label="Search {{ site.title }}" autocomplete="off">
38
- <svg width="14" height="14" viewBox="0 0 28 28" xmlns="http://www.w3.org/2000/svg" class="search-icon"><title>Search</title><g fill-rule="nonzero"><path d="M17.332 20.735c-5.537 0-10-4.6-10-10.247 0-5.646 4.463-10.247 10-10.247 5.536 0 10 4.601 10 10.247s-4.464 10.247-10 10.247zm0-4c3.3 0 6-2.783 6-6.247 0-3.463-2.7-6.247-6-6.247s-6 2.784-6 6.247c0 3.464 2.7 6.247 6 6.247z"/><path d="M11.672 13.791L.192 25.271 3.02 28.1 14.5 16.62z"/></g></svg>
39
- </div>
40
- <div class="js-search-results search-results-wrap"></div>
41
- </div>
42
- {% endif %}
43
- {% if site.aux_links != nil %}
44
- <ul class="list-style-none text-small aux-nav">
45
- {% for link in site.aux_links %}
46
- <li class="d-inline-block my-0{% unless forloop.last %} mr-2{% endunless %}"><a href="{{ link.last }}">{{ link.first }}</a></li>
47
- {% endfor %}
48
- </ul>
49
- {% endif %}
50
- </div>
51
- <div class="page">
52
- {% unless page.url == "/" %}
53
- {% if page.parent %}
54
- <nav class="breadcrumb-nav">
55
- <ol class="breadcrumb-nav-list">
56
- {% if page.grand_parent %}
57
- <li class="breadcrumb-nav-list-item"><a href="{{ first_level_url }}">{{ page.grand_parent }}</a></li>
58
- <li class="breadcrumb-nav-list-item"><a href="{{ second_level_url }}">{{ page.parent }}</a></li>
59
- {% else %}
60
- <li class="breadcrumb-nav-list-item"><a href="{{ first_level_url }}">{{ page.parent }}</a></li>
61
- {% endif %}
62
- <li class="breadcrumb-nav-list-item"><span>{{ page.title }}</span></li>
63
- </ol>
64
- </nav>
65
- {% endif %}
66
- {% endunless %}
67
- <div id="main-content" class="page-content" role="main">
68
- {% if site.heading_anchors != false %}
69
- {% include vendor/anchor_headings.html html=content beforeHeading="true" anchorBody="<svg viewBox=\"0 0 16 16\" aria-hidden=\"true\"><use xlink:href=\"#link\"></use></svg>" anchorClass="anchor-heading" %}
70
- {% else %}
71
- {{ content }}
72
- {% endif %}
73
-
74
- {% if page.prev_title or page.next_title %}
75
- {% include prevnext.html %}
76
- {% endif %}
77
-
78
- {% if page.has_children == true and page.has_toc != false %}
79
- <hr>
80
- <h2 class="text-delta">Table of contents</h2>
81
- {% assign children_list = site.pages | sort:"nav_order" %}
82
- <ul>
83
- {% for child in children_list %}
84
- {% if child.parent == page.title and child.title != page.title %}
85
- <li>
86
- <a href="{{ child.url | absolute_url }}">{{ child.title }}</a>{% if child.summary %} - {{ child.summary }}{% endif %}
87
- </li>
88
- {% endif %}
89
- {% endfor %}
90
- </ul>
91
- {% endif %}
92
-
93
- {% if site.footer_content != nil %}
94
- <hr>
95
- <footer role="contentinfo">
96
- <p class="text-small text-grey-dk-000 mb-0">{{ site.footer_content }}</p>
97
- </footer>
98
- {% endif %}
99
-
100
- </div>
101
- </div>
102
- </div>
103
- </div>
104
-
105
- </body>
106
- </html>
@@ -1,11 +0,0 @@
1
- ---
2
- layout: page
3
- title: API Reference
4
- description: Lorem ipsum
5
- has_children: true
6
- alphabetical_order: true
7
- section: true
8
- has_toc: false
9
- nav_order: 5
10
- section_link: /api-reference/exception
11
- ---
@@ -1,57 +0,0 @@
1
- ---
2
- layout: page
3
- title: Gyro::Async
4
- parent: API Reference
5
- permalink: /api-reference/gyro-async/
6
- ---
7
- # Gyro::Async
8
-
9
- `Gyro::Async` encapsulates a libev [async
10
- watcher](http://pod.tst.eu/http://cvs.schmorp.de/libev/ev.pod#code_ev_async_code_how_to_wake_up_an),
11
- allowing thread-safe synchronisation and signalling. `Gyro::Async` watchers are
12
- used both directly and indirectly in Polyphony to implement
13
- [queues](../gyro-queue/), await fibers and threads, and auxiliary features such
14
- as [thread pools](../polyphony-threadpool/).
15
-
16
- A `Gyro::Async` watcher instance is shared across two or more fibers (across one
17
- or more threads), where one fiber waits to be signalled by calling
18
- `Gyro::Async#await`, and one or more other fibers do the signalling by calling
19
- `Gyro::Async#signal`:
20
-
21
- ```ruby
22
- async = Gyro::Async.new
23
- spin do
24
- sleep 1
25
- async.signal
26
- end
27
-
28
- async.await
29
- ```
30
-
31
- The signalling of async watchers is compressed, which means that multiple
32
- invocations of `Gyro::Async#signal` before the event loop can continue will
33
- result the watcher being signalled just a single time.
34
-
35
- In addition to signalling, the async watcher can also be used to transfer an
36
- arbitrary value to the awaitng fiber. See `#signal` for an example.
37
-
38
- ## Instance methods
39
-
40
- ### #await → object
41
-
42
- Blocks the current thread until the watcher is signalled.
43
-
44
- ### #initialize
45
-
46
- Initializes the watcher instance.
47
-
48
- ### #signal(value = nil) → async
49
-
50
- Signals the watcher, causing the fiber awaiting the watcher to become runnable
51
- and be eventually resumed with the given value.
52
-
53
- ```ruby
54
- async = Gyro::Async.new
55
- spin { async.signal('foo') }
56
- async.await #=> 'foo'
57
- ```
@@ -1,29 +0,0 @@
1
- ---
2
- layout: page
3
- title: Gyro::Child
4
- parent: API Reference
5
- permalink: /api-reference/gyro-child/
6
- ---
7
- # Gyro::Child
8
-
9
- `Gyro::Child` encapsulates a libev [child
10
- watcher](http://pod.tst.eu/http://cvs.schmorp.de/libev/ev.pod#code_ev_child_code_watch_out_for_pro),
11
- used for waiting for a child process to terminate. A `Gyro::Child` watcher
12
- instance can be used for low-level control of child processes, instead of using
13
- more high-level APIs such `Process.wait` etc.
14
-
15
- ## Instance methods
16
-
17
- ### #await → [pid, exitcode]
18
-
19
- Blocks the current thread until the watcher is signalled. The return value is an
20
- array containing the child's pid and the exit code.
21
-
22
- ```ruby
23
- pid = Polyphony.fork { sleep 1 }
24
- Gyro::Child.new(pid).await #=> [pid, 0]
25
- ```
26
-
27
- ### #initialize(pid)
28
-
29
- Initializes the watcher instance with the given pid
@@ -1,44 +0,0 @@
1
- ---
2
- layout: page
3
- title: Gyro::Queue
4
- parent: API Reference
5
- permalink: /api-reference/gyro-queue/
6
- ---
7
- # Gyro::Queue
8
-
9
- `Gyro::Queue` implements a polyphonic (fiber-aware) queue that can store 0 or
10
- more items of any data types. Adding an item to the queue never blocks.
11
- Retrieving an item from the queue will block if the queue is empty.
12
- `Gyro::Queue` is both fiber-safe and thread-safe. This means multiple fibers
13
- from multiple threads can concurrently interact with the same queue.
14
- `Gyro::Queue` is used pervasively across the Polyphony code base for
15
- synchronisation and fiber control.
16
-
17
- ## Instance methods
18
-
19
- ### #&lt;&lt;(object) → queue<br>#push(object) → queue
20
-
21
- Adds an item to the queue.
22
-
23
- ### #clear → queue
24
-
25
- Removes all items currently in the queue.
26
-
27
- ### #empty? → true or false
28
-
29
- Returns true if the queue is empty. Otherwise returns false.
30
-
31
- ### #initialize
32
-
33
- Initializes an empty queue.
34
-
35
- ### #shift → object<br>#pop → object
36
-
37
- Retrieves an item from the queue. If the queue is empty, `#shift` blocks until
38
- an item is added to the queue or until interrupted. Multiple fibers calling
39
- `#shift` are served in a first-ordered first-served manner.
40
-
41
- ### #shift_each → [*object]<br>#shift_each({ block }) → queue
42
-
43
- Removes and returns all items currently in the queue. If a block is given, it
44
- will be invoked for each item.
@@ -1,51 +0,0 @@
1
- ---
2
- layout: page
3
- title: Gyro::Timer
4
- parent: API Reference
5
- permalink: /api-reference/gyro-timer/
6
- ---
7
- # Gyro::Timer
8
-
9
- `Gyro::Timer` encapsulates a libev [timer
10
- watcher](http://pod.tst.eu/http://cvs.schmorp.de/libev/ev.pod#code_ev_timer_code_relative_and_opti),
11
- allowing waiting a certain amount of time before proceeding with an operation.
12
- Watchers can be either one-time timers or recurring timers. The Polyphony API
13
- provides various APIs that use timer watchers for timeouts, throttled
14
- operations, and sleeping.
15
-
16
- ## Instance methods
17
-
18
- ### #await → object
19
-
20
- Blocks the current thread until the timer has elapsed. For recurrent timers,
21
- `#await` will block until the next timer period has elapsed, as specified by the
22
- `repeat` argument given to `#initialize`.
23
-
24
- ### #initialize(after, repeat)
25
-
26
- Initializes the watcher instance. The `after` argument gives the time duration
27
- in seconds before the timer has elapsed. The `repeat` argument gives the time
28
- period for recurring timers, or `0` for non-recurring timers.
29
-
30
- ### #stop
31
-
32
- Stops an active recurring timer. Recurring timers stay active (from the point of
33
- view of the event loop) even after the timer period has elapsed. Calling `#stop`
34
- marks the timer as inactive and cleans up associated resources. This should
35
- normally be done inside an `ensure` block:
36
-
37
- ```ruby
38
- def repeat(period)
39
- timer = Gyro::Timer.new(period, period)
40
- loop do
41
- timer.await
42
- yield
43
- end
44
- ensure
45
- timer.stop
46
- end
47
-
48
- repeat(10) { puts Time.now }
49
- ```
50
-
51
- There's no need to call `#stop` for non-recurring timers.
@@ -1,25 +0,0 @@
1
- ---
2
- layout: page
3
- title: Gyro
4
- parent: API Reference
5
- permalink: /api-reference/gyro/
6
- ---
7
- # Gyro
8
-
9
- `Gyro` is the subsystem in charge of the low-level functionality in Polyphony.
10
- It contains all of the different event watcher classes, as well as other
11
- low-level constructs such as `Gyro::Queue`, a fiber-aware queue implementation,
12
- used pervasively across the Polyphony code base.
13
-
14
- While most Polyphony-based applications do not normally need to interact
15
- directly with the `Gyro` classes, more advanced applications and libraries may
16
- use those classes to enhance Polyphony and create custom concurrency patterns.
17
-
18
- ## Classes
19
-
20
- - [`Gyro::Async`](../gyro-async/) - async event watcher
21
- - [`Gyro::Child`](../gyro-child/) - child process event watcher
22
- - [`Gyro::IO`](../gyro-io/) - IO event watcher
23
- - [`Gyro::Queue`](../gyro-queue/) - fiber-aware queue
24
- - [`Gyro::Signal`](../gyro-signal/) - signal event watcher
25
- - [`Gyro::Timer`](../gyro-timer/) - timer event watcher
@@ -1,10 +0,0 @@
1
- ---
2
- layout: page
3
- title: Getting Started
4
- description: Lorem ipsum
5
- has_children: true
6
- section: true
7
- has_toc: false
8
- nav_order: 2
9
- section_link: /getting-started/installing
10
- ---
@@ -1,10 +0,0 @@
1
- ---
2
- layout: page
3
- title: Main Concepts
4
- description: Lorem ipsum
5
- has_children: true
6
- section: true
7
- has_toc: false
8
- nav_order: 3
9
- section_link: /main-concepts/concurrency
10
- ---
@@ -1,10 +0,0 @@
1
- ---
2
- layout: page
3
- title: User Guide
4
- description: Lorem ipsum
5
- has_children: true
6
- section: true
7
- has_toc: false
8
- nav_order: 4
9
- section_link: /user-guide/all-about-timers
10
- ---
@@ -1,19 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'bundler/setup'
4
- require 'polyphony'
5
-
6
- trap('TERM') do
7
- # do nothing
8
- end
9
-
10
- trap('INT') do
11
- # do nothing
12
- end
13
-
14
- puts "go to sleep"
15
- begin
16
- sleep
17
- ensure
18
- puts "done sleeping"
19
- end
@@ -1,162 +0,0 @@
1
- #include "gyro.h"
2
-
3
- struct Gyro_Async {
4
- struct ev_async ev_async;
5
- struct ev_loop *ev_loop;
6
- int active;
7
- VALUE self;
8
- VALUE fiber;
9
- VALUE value;
10
- VALUE selector;
11
- };
12
-
13
- VALUE cGyro_Async = Qnil;
14
-
15
- static void Gyro_Async_mark(void *ptr) {
16
- struct Gyro_Async *async = ptr;
17
- if (async->fiber != Qnil) {
18
- rb_gc_mark(async->fiber);
19
- }
20
- if (async->value != Qnil) {
21
- rb_gc_mark(async->value);
22
- }
23
- if (async->selector != Qnil) {
24
- rb_gc_mark(async->selector);
25
- }
26
- }
27
-
28
- static void Gyro_Async_free(void *ptr) {
29
- struct Gyro_Async *async = ptr;
30
- switch (async->active) {
31
- case GYRO_WATCHER_POST_FORK:
32
- return;
33
- case 1:
34
- ev_clear_pending(async->ev_loop, &async->ev_async);
35
- ev_async_stop(async->ev_loop, &async->ev_async);
36
- default:
37
- xfree(async);
38
- }
39
- }
40
-
41
- static size_t Gyro_Async_size(const void *ptr) {
42
- return sizeof(struct Gyro_Async);
43
- }
44
-
45
- static const rb_data_type_t Gyro_Async_type = {
46
- "Gyro_Async",
47
- {Gyro_Async_mark, Gyro_Async_free, Gyro_Async_size,},
48
- 0, 0, 0
49
- };
50
-
51
- static VALUE Gyro_Async_allocate(VALUE klass) {
52
- struct Gyro_Async *async = ALLOC(struct Gyro_Async);
53
- return TypedData_Wrap_Struct(klass, &Gyro_Async_type, async);
54
- }
55
-
56
- inline void async_activate(struct Gyro_Async *async) {
57
- if (async->active) return;
58
-
59
- async->active = 1;
60
- async->fiber = rb_fiber_current();
61
- async->selector = Thread_current_event_selector();
62
- async->ev_loop = Gyro_Selector_ev_loop(async->selector);
63
- Gyro_Selector_add_active_watcher(async->selector, async->self);
64
- ev_async_start(async->ev_loop, &async->ev_async);
65
- }
66
-
67
- inline void async_deactivate(struct Gyro_Async *async) {
68
- if (!async->active) return;
69
-
70
- ev_async_stop(async->ev_loop, &async->ev_async);
71
- Gyro_Selector_remove_active_watcher(async->selector, async->self);
72
- async->active = 0;
73
- async->ev_loop = 0;
74
- async->selector = Qnil;
75
- async->fiber = Qnil;
76
- async->value = Qnil;
77
- }
78
-
79
- void Gyro_Async_callback(struct ev_loop *ev_loop, struct ev_async *ev_async, int revents) {
80
- struct Gyro_Async *async = (struct Gyro_Async*)ev_async;
81
-
82
- Fiber_make_runnable(async->fiber, async->value);
83
- async_deactivate(async);
84
- }
85
-
86
- #define GetGyro_Async(obj, async) \
87
- TypedData_Get_Struct((obj), struct Gyro_Async, &Gyro_Async_type, (async))
88
-
89
- static VALUE Gyro_Async_initialize(VALUE self) {
90
- struct Gyro_Async *async;
91
- GetGyro_Async(self, async);
92
-
93
- async->self = self;
94
- async->fiber = Qnil;
95
- async->value = Qnil;
96
- async->selector = Qnil;
97
- async->active = 0;
98
- async->ev_loop = 0;
99
-
100
- ev_async_init(&async->ev_async, Gyro_Async_callback);
101
-
102
- return Qnil;
103
- }
104
-
105
- static VALUE Gyro_Async_signal(int argc, VALUE *argv, VALUE self) {
106
- struct Gyro_Async *async;
107
- GetGyro_Async(self, async);
108
-
109
- if (!async->active) {
110
- // printf("signal called before await\n");
111
- return Qnil;
112
- }
113
-
114
- async->value = (argc == 1) ? argv[0] : Qnil;
115
- ev_async_send(async->ev_loop, &async->ev_async);
116
-
117
- return Qnil;
118
- }
119
-
120
- VALUE Gyro_Async_await(VALUE self) {
121
- struct Gyro_Async *async;
122
- GetGyro_Async(self, async);
123
-
124
- async_activate(async);
125
- VALUE ret = Gyro_switchpoint();
126
- async_deactivate(async);
127
-
128
- TEST_RESUME_EXCEPTION(ret);
129
- RB_GC_GUARD(ret);
130
- return ret;
131
- }
132
-
133
- VALUE Gyro_Async_deactivate_post_fork(VALUE self) {
134
- struct Gyro_Async *async;
135
- GetGyro_Async(self, async);
136
-
137
- if (async->active)
138
- async->active = GYRO_WATCHER_POST_FORK;
139
- return self;
140
- }
141
-
142
- VALUE Gyro_Async_await_no_raise(VALUE self) {
143
- struct Gyro_Async *async;
144
- GetGyro_Async(self, async);
145
-
146
- async_activate(async);
147
- VALUE ret = Gyro_switchpoint();
148
- async_deactivate(async);
149
-
150
- RB_GC_GUARD(ret);
151
- return ret;
152
- }
153
-
154
- void Init_Gyro_Async() {
155
- cGyro_Async = rb_define_class_under(mGyro, "Async", rb_cData);
156
- rb_define_alloc_func(cGyro_Async, Gyro_Async_allocate);
157
-
158
- rb_define_method(cGyro_Async, "initialize", Gyro_Async_initialize, 0);
159
- rb_define_method(cGyro_Async, "await", Gyro_Async_await, 0);
160
- rb_define_method(cGyro_Async, "deactivate_post_fork", Gyro_Async_deactivate_post_fork, 0);
161
- rb_define_method(cGyro_Async, "signal", Gyro_Async_signal, -1);
162
- }