polyphony 0.36 → 0.38

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +9 -0
  3. data/Gemfile +0 -11
  4. data/Gemfile.lock +1 -3
  5. data/Rakefile +4 -0
  6. data/TODO.md +12 -10
  7. data/docs/index.md +2 -1
  8. data/examples/core/xx-fork-cleanup.rb +22 -0
  9. data/ext/gyro/async.c +27 -13
  10. data/ext/gyro/child.c +29 -15
  11. data/ext/gyro/fiber.c +3 -1
  12. data/ext/gyro/gyro.c +0 -6
  13. data/ext/gyro/gyro.h +6 -0
  14. data/ext/gyro/io.c +24 -9
  15. data/ext/gyro/queue.c +21 -21
  16. data/ext/gyro/selector.c +23 -0
  17. data/ext/gyro/signal.c +24 -9
  18. data/ext/gyro/thread.c +12 -2
  19. data/ext/gyro/timer.c +33 -18
  20. data/lib/polyphony.rb +27 -36
  21. data/lib/polyphony/adapters/fs.rb +1 -4
  22. data/lib/polyphony/adapters/process.rb +29 -25
  23. data/lib/polyphony/adapters/trace.rb +129 -124
  24. data/lib/polyphony/core/channel.rb +36 -36
  25. data/lib/polyphony/core/exceptions.rb +29 -29
  26. data/lib/polyphony/core/global_api.rb +92 -91
  27. data/lib/polyphony/core/resource_pool.rb +84 -84
  28. data/lib/polyphony/core/sync.rb +17 -17
  29. data/lib/polyphony/core/thread_pool.rb +49 -37
  30. data/lib/polyphony/core/throttler.rb +25 -25
  31. data/lib/polyphony/extensions/core.rb +3 -3
  32. data/lib/polyphony/extensions/fiber.rb +269 -267
  33. data/lib/polyphony/extensions/openssl.rb +1 -1
  34. data/lib/polyphony/extensions/socket.rb +2 -1
  35. data/lib/polyphony/extensions/thread.rb +3 -3
  36. data/lib/polyphony/net.rb +71 -67
  37. data/lib/polyphony/version.rb +1 -1
  38. data/polyphony.gemspec +0 -3
  39. data/test/stress.rb +17 -12
  40. data/test/test_thread.rb +1 -0
  41. data/test/test_thread_pool.rb +2 -2
  42. data/test/test_throttler.rb +0 -1
  43. metadata +3 -16
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1baba43b46fa48ea13ec33667390b5b6dafb8323d250f2df5e5ee6930e939680
4
- data.tar.gz: 465c329841226252b1fe7fc3d48904b912323370083b4925d89531e45a4bd34d
3
+ metadata.gz: b952f96f313866e5b61e200093fcb1eb8111b2d48bb2a736e36b62f06772786c
4
+ data.tar.gz: 03ffbc7481d3e20cfd5d50cc0c7e1e28749da15a6c9d65c89e83a0370b61b2d2
5
5
  SHA512:
6
- metadata.gz: d9b863d0231a5ccea65aa015d5a1d3432d0b513be1a60b2d20040bd06ed9d253c6be8391176ceba472300beac8059e27c334aca3bb6bafd51c3dabfd1e105301
7
- data.tar.gz: 29eba3a894084f20d21bab00840cef76a5094f73aee4521947685883dc717811a274ed723deb72433cc0f358715549d79215286aad0ba62d14ee97308ee1415b
6
+ metadata.gz: 8fb34d135a0ab50a3e3ae722dd3f9ae70ba8ee83534c199f98611f5fa273b424fcedab548929a2dedcdaddec73a9aa6b652fa97a924dd5400155cf57a1cdcaf0
7
+ data.tar.gz: caacb6a006f52abf888a9f4a3f1ef2c917a73422abaf1cba7d1ae1b0c7db07a3bc62a5a6b6abbe22e46fd6e679e82d7a048ec2d9ac7ec7dee54453600c688597
data/CHANGELOG.md CHANGED
@@ -1,3 +1,12 @@
1
+ ## 0.38 2020-04-13
2
+
3
+ * Fix post-fork segfault if parent process has multiple threads with active watchers
4
+
5
+ ## 0.37 2020-04-07
6
+
7
+ * Explicitly kill threads on exit to prevent possible segfault
8
+ * Remove Modulation dependency
9
+
1
10
  ## 0.36 2020-03-31
2
11
 
3
12
  * More docs
data/Gemfile CHANGED
@@ -1,14 +1,3 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
3
  gemspec
4
-
5
- # gem "jekyll", "~> 3.8.6"
6
- # gem "jekyll-remote-theme"
7
- # gem "jekyll-seo-tag"
8
- # gem "just-the-docs"
9
-
10
- # # gem "github-pages", group: :jekyll_plugins
11
-
12
- # group :jekyll_plugins do
13
- # gem "jekyll-feed", "~> 0.6"
14
- # end
data/Gemfile.lock CHANGED
@@ -1,8 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- polyphony (0.36)
5
- modulation (~> 1.0)
4
+ polyphony (0.38)
6
5
 
7
6
  GEM
8
7
  remote: https://rubygems.org/
@@ -73,7 +72,6 @@ GEM
73
72
  builder
74
73
  minitest (>= 5.0)
75
74
  ruby-progressbar
76
- modulation (1.0)
77
75
  multi_xml (0.6.0)
78
76
  parallel (1.19.1)
79
77
  parser (2.7.0.2)
data/Rakefile CHANGED
@@ -15,6 +15,10 @@ task :test do
15
15
  exec 'ruby test/run.rb'
16
16
  end
17
17
 
18
+ task :stress_test do
19
+ exec 'ruby test/stress.rb'
20
+ end
21
+
18
22
  task :docs do
19
23
  exec 'RUBYOPT=-W0 jekyll serve -s docs'
20
24
  end
data/TODO.md CHANGED
@@ -32,7 +32,11 @@
32
32
  Fiber.current.add_child_fiber(t.main_fiber)
33
33
  ```
34
34
 
35
- ## 0.36 Some more API work, more docs
35
+
36
+
37
+
38
+
39
+ ## 0.40 Some more API work, more docs
36
40
 
37
41
  - Debugging
38
42
  - Eat your own dogfood: need a good tool to check what's going on when some
@@ -142,17 +146,15 @@
142
146
  - Docs
143
147
  - landing page:
144
148
  - links to the interesting stuff
145
- - concurrency overview
146
- - faq
147
149
  - benchmarks
148
150
  - explain difference between `sleep` and `suspend`
149
- - add explanation about async vs sync
151
+ - add explanation about async vs sync, blocking vs non-blocking
150
152
  - discuss using `snooze` for ensuring responsiveness when executing CPU-bound work
151
153
 
152
154
  - Check why first call to `#sleep` returns too early in tests. Check the
153
155
  sleep behaviour in a spawned thread.
154
156
 
155
- ## 0.37 Sinatra / Sidekiq
157
+ ## 0.41 Sinatra / Sidekiq
156
158
 
157
159
  - sintra app with database access (postgresql)
158
160
 
@@ -162,13 +164,13 @@
162
164
  - test performance
163
165
  - proceed from there
164
166
 
165
- ## 0.38 Testing && Docs
167
+ ## 0.42 Testing && Docs
166
168
 
167
169
  - Pull out redis/postgres code, put into new `polyphony-xxx` gems
168
170
 
169
- ## 0.39 Integration
171
+ ## 0.43 Integration
170
172
 
171
- ## 0.40 Real IO#gets and IO#read
173
+ ## 0.44 Real IO#gets and IO#read
172
174
 
173
175
  - More tests
174
176
  - Implement some basic stuff missing:
@@ -178,11 +180,11 @@
178
180
  - `IO.foreach`
179
181
  - `Process.waitpid`
180
182
 
181
- ## 0.41 Rails
183
+ ## 0.45 Rails
182
184
 
183
185
  - Rails?
184
186
 
185
- ## 0.42 DNS
187
+ ## 0.46 DNS
186
188
 
187
189
  ### DNS client
188
190
 
data/docs/index.md CHANGED
@@ -14,8 +14,9 @@ implements a comprehensive
14
14
  using [libev](https://github.com/enki/libev) as a high-performance event reactor
15
15
  for I/O, timers, and other asynchronous events.
16
16
 
17
- [FAQ](faq){: .btn .btn-green .text-gamma }
18
17
  [Take the tutorial](getting-started/tutorial){: .btn .btn-blue .text-gamma }
18
+ [Main Concepts](main-concepts/concurrency/){: .btn .btn-green .text-gamma }
19
+ [FAQ](faq){: .btn .btn-green .text-gamma }
19
20
  [Source code](https://github.com/digital-fabric/polyphony){: .btn .btn-purple .text-gamma target="_blank" }
20
21
  {: .mt-6 .h-align-center }
21
22
 
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/setup'
4
+ require 'polyphony'
5
+
6
+ Exception.__disable_sanitized_backtrace__ = true
7
+
8
+ t = Thread.new do
9
+ async = Gyro::Async.new
10
+ spin { async.await }
11
+ sleep 100
12
+ end
13
+
14
+ sleep 0.5
15
+
16
+ Polyphony.fork do
17
+ puts "forked #{Process.pid}"
18
+ sleep 1
19
+ puts "done sleeping"
20
+ end
21
+
22
+ sleep 50
data/ext/gyro/async.c CHANGED
@@ -27,11 +27,15 @@ static void Gyro_Async_mark(void *ptr) {
27
27
 
28
28
  static void Gyro_Async_free(void *ptr) {
29
29
  struct Gyro_Async *async = ptr;
30
- if (async->active) {
31
- ev_clear_pending(async->ev_loop, &async->ev_async);
32
- ev_async_stop(async->ev_loop, &async->ev_async);
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);
33
38
  }
34
- xfree(async);
35
39
  }
36
40
 
37
41
  static size_t Gyro_Async_size(const void *ptr) {
@@ -49,7 +53,7 @@ static VALUE Gyro_Async_allocate(VALUE klass) {
49
53
  return TypedData_Wrap_Struct(klass, &Gyro_Async_type, async);
50
54
  }
51
55
 
52
- inline void Gyro_Async_activate(struct Gyro_Async *async) {
56
+ inline void async_activate(struct Gyro_Async *async) {
53
57
  if (async->active) return;
54
58
 
55
59
  async->active = 1;
@@ -60,7 +64,7 @@ inline void Gyro_Async_activate(struct Gyro_Async *async) {
60
64
  ev_async_start(async->ev_loop, &async->ev_async);
61
65
  }
62
66
 
63
- inline void Gyro_Async_deactivate(struct Gyro_Async *async) {
67
+ inline void async_deactivate(struct Gyro_Async *async) {
64
68
  if (!async->active) return;
65
69
 
66
70
  ev_async_stop(async->ev_loop, &async->ev_async);
@@ -76,7 +80,7 @@ void Gyro_Async_callback(struct ev_loop *ev_loop, struct ev_async *ev_async, int
76
80
  struct Gyro_Async *async = (struct Gyro_Async*)ev_async;
77
81
 
78
82
  Fiber_make_runnable(async->fiber, async->value);
79
- Gyro_Async_deactivate(async);
83
+ async_deactivate(async);
80
84
  }
81
85
 
82
86
  #define GetGyro_Async(obj, async) \
@@ -117,22 +121,31 @@ VALUE Gyro_Async_await(VALUE self) {
117
121
  struct Gyro_Async *async;
118
122
  GetGyro_Async(self, async);
119
123
 
120
- Gyro_Async_activate(async);
124
+ async_activate(async);
121
125
  VALUE ret = Gyro_switchpoint();
122
- Gyro_Async_deactivate(async);
126
+ async_deactivate(async);
123
127
 
124
128
  TEST_RESUME_EXCEPTION(ret);
125
129
  RB_GC_GUARD(ret);
126
130
  return ret;
127
131
  }
128
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
+
129
142
  VALUE Gyro_Async_await_no_raise(VALUE self) {
130
143
  struct Gyro_Async *async;
131
144
  GetGyro_Async(self, async);
132
145
 
133
- Gyro_Async_activate(async);
146
+ async_activate(async);
134
147
  VALUE ret = Gyro_switchpoint();
135
- Gyro_Async_deactivate(async);
148
+ async_deactivate(async);
136
149
 
137
150
  RB_GC_GUARD(ret);
138
151
  return ret;
@@ -143,6 +156,7 @@ void Init_Gyro_Async() {
143
156
  rb_define_alloc_func(cGyro_Async, Gyro_Async_allocate);
144
157
 
145
158
  rb_define_method(cGyro_Async, "initialize", Gyro_Async_initialize, 0);
146
- rb_define_method(cGyro_Async, "signal", Gyro_Async_signal, -1);
147
159
  rb_define_method(cGyro_Async, "await", Gyro_Async_await, 0);
148
- }
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
+ }
data/ext/gyro/child.c CHANGED
@@ -24,11 +24,15 @@ static void Gyro_Child_mark(void *ptr) {
24
24
 
25
25
  static void Gyro_Child_free(void *ptr) {
26
26
  struct Gyro_Child *child = ptr;
27
- if (child->active) {
28
- ev_clear_pending(child->ev_loop, &child->ev_child);
29
- ev_child_stop(child->ev_loop, &child->ev_child);
27
+ switch (child->active) {
28
+ case GYRO_WATCHER_POST_FORK:
29
+ return;
30
+ case 1:
31
+ ev_clear_pending(child->ev_loop, &child->ev_child);
32
+ ev_child_stop(child->ev_loop, &child->ev_child);
33
+ default:
34
+ xfree(child);
30
35
  }
31
- xfree(child);
32
36
  }
33
37
 
34
38
  static size_t Gyro_Child_size(const void *ptr) {
@@ -46,7 +50,7 @@ static VALUE Gyro_Child_allocate(VALUE klass) {
46
50
  return TypedData_Wrap_Struct(klass, &Gyro_Child_type, child);
47
51
  }
48
52
 
49
- inline void Gyro_Child_activate(struct Gyro_Child *child) {
53
+ inline void child_activate(struct Gyro_Child *child) {
50
54
  if (child->active) return;
51
55
 
52
56
  child->active = 1;
@@ -57,7 +61,7 @@ inline void Gyro_Child_activate(struct Gyro_Child *child) {
57
61
  ev_child_start(child->ev_loop, &child->ev_child);
58
62
  }
59
63
 
60
- inline void Gyro_Child_deactivate(struct Gyro_Child *child) {
64
+ inline void child_deactivate(struct Gyro_Child *child) {
61
65
  if (!child->active) return;
62
66
 
63
67
  ev_child_stop(child->ev_loop, &child->ev_child);
@@ -82,7 +86,7 @@ void Gyro_Child_callback(struct ev_loop *ev_loop, struct ev_child *ev_child, int
82
86
  VALUE resume_value = Gyro_Child_resume_value(ev_child);
83
87
  Fiber_make_runnable(child->fiber, resume_value);
84
88
 
85
- Gyro_Child_deactivate(child);
89
+ child_deactivate(child);
86
90
  }
87
91
 
88
92
  #define GetGyro_Child(obj, child) \
@@ -93,12 +97,12 @@ static VALUE Gyro_Child_initialize(VALUE self, VALUE pid) {
93
97
 
94
98
  GetGyro_Child(self, child);
95
99
 
96
- child->self = self;
97
- child->fiber = Qnil;
98
- child->selector = Qnil;
99
- child->pid = NUM2INT(pid);
100
- child->active = 0;
101
- child->ev_loop = 0;
100
+ child->self = self;
101
+ child->fiber = Qnil;
102
+ child->selector = Qnil;
103
+ child->pid = NUM2INT(pid);
104
+ child->active = 0;
105
+ child->ev_loop = 0;
102
106
 
103
107
  ev_child_init(&child->ev_child, Gyro_Child_callback, child->pid, 0);
104
108
 
@@ -109,19 +113,29 @@ static VALUE Gyro_Child_await(VALUE self) {
109
113
  struct Gyro_Child *child;
110
114
  GetGyro_Child(self, child);
111
115
 
112
- Gyro_Child_activate(child);
116
+ child_activate(child);
113
117
  VALUE ret = Gyro_switchpoint();
114
- Gyro_Child_deactivate(child);
118
+ child_deactivate(child);
115
119
 
116
120
  TEST_RESUME_EXCEPTION(ret);
117
121
  RB_GC_GUARD(ret);
118
122
  return ret;
119
123
  }
120
124
 
125
+ VALUE Gyro_Child_deactivate_post_fork(VALUE self) {
126
+ struct Gyro_Child *child;
127
+ GetGyro_Child(self, child);
128
+
129
+ if (child->active)
130
+ child->active = GYRO_WATCHER_POST_FORK;
131
+ return self;
132
+ }
133
+
121
134
  void Init_Gyro_Child() {
122
135
  cGyro_Child = rb_define_class_under(mGyro, "Child", rb_cData);
123
136
  rb_define_alloc_func(cGyro_Child, Gyro_Child_allocate);
124
137
 
125
138
  rb_define_method(cGyro_Child, "initialize", Gyro_Child_initialize, 1);
126
139
  rb_define_method(cGyro_Child, "await", Gyro_Child_await, 0);
140
+ rb_define_method(cGyro_Child, "deactivate_post_fork", Gyro_Child_deactivate_post_fork, 0);
127
141
  }
data/ext/gyro/fiber.c CHANGED
@@ -71,7 +71,9 @@ void Fiber_make_runnable(VALUE fiber, VALUE value) {
71
71
  Thread_schedule_fiber(thread, fiber, value);
72
72
  }
73
73
  else {
74
- rb_warn("No thread set for fiber");
74
+ rb_warn("No thread set for fiber (fiber, value, caller):");
75
+ VALUE caller = rb_funcall(rb_cObject, rb_intern("caller"), 0);
76
+ INSPECT(3, fiber, value, caller);
75
77
  }
76
78
  }
77
79
 
data/ext/gyro/gyro.c CHANGED
@@ -44,11 +44,6 @@ VALUE Gyro_snooze(VALUE self) {
44
44
  return ret;
45
45
  }
46
46
 
47
- static VALUE Gyro_post_fork(VALUE self) {
48
- Thread_post_fork(rb_thread_current());
49
- return Qnil;
50
- }
51
-
52
47
  static VALUE Gyro_ref(VALUE self) {
53
48
  return Thread_ref(rb_thread_current());
54
49
  }
@@ -73,7 +68,6 @@ VALUE Gyro_trace(VALUE self, VALUE enabled) {
73
68
  void Init_Gyro() {
74
69
  mGyro = rb_define_module("Gyro");
75
70
 
76
- rb_define_singleton_method(mGyro, "post_fork", Gyro_post_fork, 0);
77
71
  rb_define_singleton_method(mGyro, "ref", Gyro_ref, 0);
78
72
  rb_define_singleton_method(mGyro, "unref", Gyro_unref, 0);
79
73
  rb_define_singleton_method(mGyro, "trace", Gyro_trace, 1);
data/ext/gyro/gyro.h CHANGED
@@ -59,6 +59,12 @@ enum {
59
59
  FIBER_STATE_SCHEDULED = 2
60
60
  };
61
61
 
62
+ // watcher flags
63
+ enum {
64
+ // a watcher's active field will be set to this after fork
65
+ GYRO_WATCHER_POST_FORK = 0xFF
66
+ };
67
+
62
68
  VALUE Fiber_auto_async(VALUE self);
63
69
  VALUE Fiber_auto_io(VALUE self);
64
70
  void Fiber_make_runnable(VALUE fiber, VALUE value);
data/ext/gyro/io.c CHANGED
@@ -34,11 +34,15 @@ static void Gyro_IO_mark(void *ptr) {
34
34
 
35
35
  static void Gyro_IO_free(void *ptr) {
36
36
  struct Gyro_IO *io = ptr;
37
- if (io->active) {
38
- ev_clear_pending(io->ev_loop, &io->ev_io);
39
- ev_io_stop(io->ev_loop, &io->ev_io);
37
+ switch (io->active) {
38
+ case GYRO_WATCHER_POST_FORK:
39
+ return;
40
+ case 1:
41
+ ev_clear_pending(io->ev_loop, &io->ev_io);
42
+ ev_io_stop(io->ev_loop, &io->ev_io);
43
+ default:
44
+ xfree(io);
40
45
  }
41
- xfree(io);
42
46
  }
43
47
 
44
48
  static size_t Gyro_IO_size(const void *ptr) {
@@ -57,7 +61,7 @@ static VALUE Gyro_IO_allocate(VALUE klass) {
57
61
  return TypedData_Wrap_Struct(klass, &Gyro_IO_type, io);
58
62
  }
59
63
 
60
- inline void Gyro_IO_activate(struct Gyro_IO *io) {
64
+ inline void io_activate(struct Gyro_IO *io) {
61
65
  if (io->active) return;
62
66
 
63
67
  io->active = 1;
@@ -68,7 +72,7 @@ inline void Gyro_IO_activate(struct Gyro_IO *io) {
68
72
  ev_io_start(io->ev_loop, &io->ev_io);
69
73
  }
70
74
 
71
- inline void Gyro_IO_deactivate(struct Gyro_IO *io) {
75
+ inline void io_deactivate(struct Gyro_IO *io) {
72
76
  if (!io->active) return;
73
77
 
74
78
  ev_io_stop(io->ev_loop, &io->ev_io);
@@ -83,7 +87,7 @@ void Gyro_IO_callback(struct ev_loop *ev_loop, struct ev_io *ev_io, int revents)
83
87
  struct Gyro_IO *io = (struct Gyro_IO*)ev_io;
84
88
 
85
89
  Fiber_make_runnable(io->fiber, Qnil);
86
- Gyro_IO_deactivate(io);
90
+ io_deactivate(io);
87
91
  }
88
92
 
89
93
  static int Gyro_IO_symbol2event_mask(VALUE sym) {
@@ -143,15 +147,25 @@ VALUE Gyro_IO_await(VALUE self) {
143
147
  struct Gyro_IO *io;
144
148
  GetGyro_IO(self, io);
145
149
 
146
- Gyro_IO_activate(io);
150
+ io_activate(io);
147
151
  VALUE ret = Gyro_switchpoint();
148
- Gyro_IO_deactivate(io);
152
+ io_deactivate(io);
149
153
 
150
154
  TEST_RESUME_EXCEPTION(ret);
151
155
  RB_GC_GUARD(ret);
152
156
  return ret;
153
157
  }
154
158
 
159
+ VALUE Gyro_IO_deactivate_post_fork(VALUE self) {
160
+ struct Gyro_IO *io;
161
+ GetGyro_IO(self, io);
162
+
163
+ if (io->active)
164
+ io->active = GYRO_WATCHER_POST_FORK;
165
+
166
+ return self;
167
+ }
168
+
155
169
  VALUE Gyro_IO_auto_io(int fd, int events) {
156
170
  VALUE watcher = Fiber_auto_io(rb_fiber_current());
157
171
  struct Gyro_IO *io;
@@ -453,6 +467,7 @@ void Init_Gyro_IO() {
453
467
 
454
468
  rb_define_method(cGyro_IO, "initialize", Gyro_IO_initialize, 2);
455
469
  rb_define_method(cGyro_IO, "await", Gyro_IO_await, 0);
470
+ rb_define_method(cGyro_IO, "deactivate_post_fork", Gyro_IO_deactivate_post_fork, 0);
456
471
 
457
472
  VALUE cIO = rb_const_get(rb_cObject, rb_intern("IO"));
458
473
  // rb_define_method(cIO, "gets", IO_gets, -1);