cool.io 1.2.1 → 1.2.2

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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 8df378d986336f5aa0c79e3c5e31f80a1571ab0d
4
+ data.tar.gz: 2d9c5aa507a285b4ee129c5a8ccfca8c5f5d8586
5
+ SHA512:
6
+ metadata.gz: f6a74e7fa7dd6bd728835225ecb8481c2e44a44dad2887aaba7d041d835bdcb76ace90ea2fe4aab6bd5db322f3d9ea0c1e81e4eb68da4a3d75273fdcb6781d86
7
+ data.tar.gz: deaffe41d393672ffb44c81766460f8e0736a21294e96acabe998fe01ea36dbdd55ef6b1efadf1e675460ffc0be43376052f9a2f4e3344d36e7c8b37730a3bc5
@@ -4,6 +4,8 @@ rvm:
4
4
  - 1.9.3
5
5
  - 2.0.0
6
6
  - 2.1.0
7
+ - 2.1.1
8
+ - ruby-head
7
9
  - rbx
8
10
 
9
11
  matrix:
data/CHANGES.md CHANGED
@@ -1,3 +1,9 @@
1
+ 1.2.2
2
+ -----
3
+
4
+ * Add timeout option to Loop#run and Loop#run_once. Default by nil
5
+ * Support Ruby 2.2.0
6
+
1
7
  1.2.1
2
8
  -----
3
9
 
data/README.md CHANGED
@@ -1,8 +1,7 @@
1
1
  Cool.io
2
2
  =======
3
3
 
4
- ### NOTE: cool.io is in maintenance mode only and is not being actively developed
5
- ### Please check out [Celluloid::IO](http://github.com/celluloid/celluloid-io) instead!
4
+ ### If you are interested in Celluloid based IO framework, please check out [Celluloid::IO](http://github.com/celluloid/celluloid-io)
6
5
 
7
6
  Cool.io is an event library for Ruby, built on the libev event library which
8
7
  provides a cross-platform interface to high performance system calls . This
@@ -15,10 +14,8 @@ applications.
15
14
 
16
15
  You can include Cool.io in your programs with:
17
16
 
18
- require 'rubygems'
19
17
  require 'cool.io'
20
18
 
21
- Questions? Sign up for the mailing list by emailing: [cool.io@librelist.com](mailto:cool.io@librelist.com)
22
19
 
23
20
  Anatomy
24
21
  -------
@@ -97,7 +94,6 @@ Example Program
97
94
 
98
95
  Cool.io provides a Sinatra-like DSL for authoring event-driven programs:
99
96
 
100
- require 'rubygems'
101
97
  require 'cool.io'
102
98
 
103
99
  ADDR = '127.0.0.1'
@@ -32,6 +32,7 @@ struct Coolio_Event
32
32
  struct Coolio_Loop
33
33
  {
34
34
  struct ev_loop *ev_loop;
35
+ struct ev_timer timer; /* for timeouts */
35
36
 
36
37
  int running;
37
38
  int events_received;
@@ -1,7 +1,9 @@
1
1
  #define EV_STANDALONE /* keeps ev from requiring config.h */
2
+
2
3
  #ifdef _WIN32
3
- # define EV_SELECT_IS_WINSOCKET 1 /* configure libev for windows select */
4
- # define FD_SETSIZE 2048 /* wishful thinking, as msvcrt6 [?] seems to only allow 512 fd's and 256 sockets max */
4
+ #define EV_SELECT_IS_WINSOCKET 1 /* configure libev for windows select */
5
+ #define EV_USE_MONOTONIC 0
6
+ #define EV_USE_REALTIME 0
5
7
  #endif
6
8
 
7
9
  #include "../libev/ev.h"
@@ -8,6 +8,10 @@ if have_func('rb_thread_blocking_region')
8
8
  $defs << '-DHAVE_RB_THREAD_BLOCKING_REGION'
9
9
  end
10
10
 
11
+ if have_func('rb_thread_call_without_gvl')
12
+ $defs << '-DHAVE_RB_THEREAD_CALL_WITHOUT_GVL'
13
+ end
14
+
11
15
  if have_func('rb_thread_alone')
12
16
  $defs << '-DHAVE_RB_THREAD_ALONE'
13
17
  end
@@ -11,14 +11,6 @@
11
11
 
12
12
  #include "cool.io.h"
13
13
 
14
- #if defined(HAVE_RB_THREAD_BLOCKING_REGION)
15
- # define Coolio_Loop_may_block_safely() (1)
16
- #elif defined(HAVE_RB_THREAD_ALONE)
17
- # define Coolio_Loop_may_block_safely() (rb_thread_alone())
18
- #else /* just in case Ruby changes: */
19
- # define Coolio_Loop_may_block_safely() (0)
20
- #endif
21
-
22
14
  static VALUE mCoolio = Qnil;
23
15
  static VALUE cCoolio_Loop = Qnil;
24
16
 
@@ -28,10 +20,10 @@ static void Coolio_Loop_free(struct Coolio_Loop *loop);
28
20
 
29
21
  static VALUE Coolio_Loop_initialize(VALUE self);
30
22
  static VALUE Coolio_Loop_ev_loop_new(VALUE self, VALUE flags);
31
- static VALUE Coolio_Loop_run_once(VALUE self);
23
+ static VALUE Coolio_Loop_run_once(int argc, VALUE *argv, VALUE self);
32
24
  static VALUE Coolio_Loop_run_nonblock(VALUE self);
33
25
 
34
- static void Coolio_Loop_ev_loop_oneshot(struct Coolio_Loop *loop_data);
26
+ static void Coolio_Loop_timeout_callback(struct ev_loop *ev_loop, struct ev_timer *timer, int revents);
35
27
  static void Coolio_Loop_dispatch_events(struct Coolio_Loop *loop_data);
36
28
 
37
29
  #define DEFAULT_EVENTBUF_SIZE 32
@@ -54,7 +46,7 @@ void Init_coolio_loop()
54
46
 
55
47
  rb_define_method(cCoolio_Loop, "initialize", Coolio_Loop_initialize, 0);
56
48
  rb_define_private_method(cCoolio_Loop, "ev_loop_new", Coolio_Loop_ev_loop_new, 1);
57
- rb_define_method(cCoolio_Loop, "run_once", Coolio_Loop_run_once, 0);
49
+ rb_define_method(cCoolio_Loop, "run_once", Coolio_Loop_run_once, -1);
58
50
  rb_define_method(cCoolio_Loop, "run_nonblock", Coolio_Loop_run_nonblock, 0);
59
51
  }
60
52
 
@@ -63,6 +55,7 @@ static VALUE Coolio_Loop_allocate(VALUE klass)
63
55
  struct Coolio_Loop *loop = (struct Coolio_Loop *)xmalloc(sizeof(struct Coolio_Loop));
64
56
 
65
57
  loop->ev_loop = 0;
58
+ ev_init(&loop->timer, Coolio_Loop_timeout_callback);
66
59
  loop->running = 0;
67
60
  loop->events_received = 0;
68
61
  loop->eventbuf_size = DEFAULT_EVENTBUF_SIZE;
@@ -174,79 +167,57 @@ void Coolio_Loop_process_event(VALUE watcher, int revents)
174
167
  loop_data->events_received++;
175
168
  }
176
169
 
170
+ /* Called whenever a timeout fires on the event loop */
171
+ static void Coolio_Loop_timeout_callback(struct ev_loop *ev_loop, struct ev_timer *timer, int revents)
172
+ {
173
+ /* We don't actually need to do anything here, the mere firing of the
174
+ timer is sufficient to interrupt the selector. However, libev still wants a callback */
175
+ }
176
+
177
177
  /**
178
178
  * call-seq:
179
179
  * Coolio::Loop.run_once -> nil
180
180
  *
181
181
  * Run the Coolio::Loop once, blocking until events are received.
182
182
  */
183
- static VALUE Coolio_Loop_run_once(VALUE self)
183
+ static VALUE Coolio_Loop_run_once(int argc, VALUE *argv, VALUE self)
184
184
  {
185
+ VALUE timeout;
185
186
  VALUE nevents;
187
+ struct Coolio_Loop *loop_data;
186
188
 
187
- if (Coolio_Loop_may_block_safely()) {
188
- struct Coolio_Loop *loop_data;
189
-
190
- Data_Get_Struct(self, struct Coolio_Loop, loop_data);
189
+ rb_scan_args(argc, argv, "01", &timeout);
191
190
 
192
- assert(loop_data->ev_loop && !loop_data->events_received);
191
+ if (timeout != Qnil && NUM2DBL(timeout) < 0) {
192
+ rb_raise(rb_eArgError, "time interval must be positive");
193
+ }
193
194
 
194
- Coolio_Loop_ev_loop_oneshot(loop_data);
195
- Coolio_Loop_dispatch_events(loop_data);
195
+ Data_Get_Struct(self, struct Coolio_Loop, loop_data);
196
196
 
197
- nevents = INT2NUM(loop_data->events_received);
198
- loop_data->events_received = 0;
197
+ assert(loop_data->ev_loop && !loop_data->events_received);
199
198
 
199
+ /* Implement the optional timeout (if any) as a ev_timer */
200
+ /* Using the technique written at
201
+ http://pod.tst.eu/http://cvs.schmorp.de/libev/ev.pod#code_ev_timer_code_relative_and_opti,
202
+ the timer is not stopped/started everytime when timeout is specified, instead,
203
+ the timer is stopped when timeout is not specified. */
204
+ if (timeout != Qnil) {
205
+ /* It seems libev is not a fan of timers being zero, so fudge a little */
206
+ loop_data->timer.repeat = NUM2DBL(timeout) + 0.0001;
207
+ ev_timer_again(loop_data->ev_loop, &loop_data->timer);
200
208
  } else {
201
- nevents = Coolio_Loop_run_nonblock(self);
202
- rb_thread_schedule();
209
+ ev_timer_stop(loop_data->ev_loop, &loop_data->timer);
203
210
  }
204
211
 
205
- return nevents;
206
- }
207
-
208
- #ifdef HAVE_RB_THREAD_BLOCKING_REGION
209
- #define HAVE_EV_LOOP_ONESHOT
210
-
211
- static void Coolio_Loop_ev_loop_oneshot(struct Coolio_Loop *loop_data)
212
- {
212
+ /* libev is patched to release the GIL when it makes its system call */
213
213
  RUN_LOOP(loop_data, EVLOOP_ONESHOT);
214
- }
215
- #endif
216
-
217
- /* Ruby 1.8 requires us to periodically run the event loop then defer back to
218
- * the green threads scheduler */
219
- #ifndef HAVE_EV_LOOP_ONESHOT
220
- #define BLOCKING_INTERVAL 0.01 /* Block for 10ms at a time */
221
-
222
- /* Stub for scheduler's ev_timer callback */
223
- static void timer_callback(struct ev_loop *ev_loop, struct ev_timer *timer, int revents)
224
- {
225
- ev_timer_again (ev_loop, timer);
226
- }
227
-
228
- /* Run the event loop, calling rb_thread_schedule every 10ms */
229
- static void Coolio_Loop_ev_loop_oneshot(struct Coolio_Loop *loop_data)
230
- {
231
- struct ev_timer timer;
232
- struct timeval tv;
233
214
 
234
- /* Set up an ev_timer to unblock the loop every 10ms */
235
- ev_timer_init(&timer, timer_callback, BLOCKING_INTERVAL, BLOCKING_INTERVAL);
236
- ev_timer_start(loop_data->ev_loop, &timer);
237
-
238
- /* Loop until we receive events */
239
- while(!loop_data->events_received) {
240
- TRAP_BEG;
241
- RUN_LOOP(loop_data, EVLOOP_ONESHOT);
242
- TRAP_END;
243
-
244
- rb_thread_schedule();
245
- }
215
+ Coolio_Loop_dispatch_events(loop_data);
216
+ nevents = INT2NUM(loop_data->events_received);
217
+ loop_data->events_received = 0;
246
218
 
247
- ev_timer_stop(loop_data->ev_loop, &timer);
219
+ return nevents;
248
220
  }
249
- #endif
250
221
 
251
222
  /**
252
223
  * call-seq:
@@ -3242,7 +3242,7 @@ time_update (EV_P_ ev_tstamp max_block)
3242
3242
  }
3243
3243
 
3244
3244
  /* ########## COOLIO PATCHERY HO! ########## */
3245
- #if defined(HAVE_RB_THREAD_BLOCKING_REGION)
3245
+ #if defined(HAVE_RB_THREAD_BLOCKING_REGION) || defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL)
3246
3246
  static
3247
3247
  VALUE ev_backend_poll(void **args)
3248
3248
  {
@@ -3257,7 +3257,7 @@ int
3257
3257
  ev_run (EV_P_ int flags)
3258
3258
  {
3259
3259
  /* ########## COOLIO PATCHERY HO! ########## */
3260
- #if defined(HAVE_RB_THREAD_BLOCKING_REGION)
3260
+ #if defined(HAVE_RB_THREAD_BLOCKING_REGION) || defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL)
3261
3261
  void *poll_args[2];
3262
3262
  #endif
3263
3263
  /* ######################################## */
@@ -3415,10 +3415,24 @@ Let this be a lesson to the all: CALLBACKS FUCKING BLOW
3415
3415
  #######################################################################
3416
3416
  */
3417
3417
 
3418
- #if defined(HAVE_RB_THREAD_BLOCKING_REGION)
3418
+ /*
3419
+ simulate to rb_thread_call_without_gvl using rb_theread_blocking_region.
3420
+ https://github.com/brianmario/mysql2/blob/master/ext/mysql2/client.h#L8
3421
+ */
3422
+
3423
+ #ifndef HAVE_RB_THREAD_CALL_WITHOUT_GVL
3424
+ #ifdef HAVE_RB_THREAD_BLOCKING_REGION
3425
+
3426
+ #define rb_thread_call_without_gvl(func, data1, ubf, data2) \
3427
+ rb_thread_blocking_region((rb_blocking_function_t *)func, data1, ubf, data2)
3428
+
3429
+ #endif
3430
+ #endif
3431
+
3432
+ #if defined(HAVE_RB_THREAD_BLOCKING_REGION) || defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL)
3419
3433
  poll_args[0] = (void *)loop;
3420
3434
  poll_args[1] = (void *)&waittime;
3421
- rb_thread_blocking_region(ev_backend_poll, (void *)&poll_args, RUBY_UBF_IO, 0);
3435
+ rb_thread_call_without_gvl(ev_backend_poll, (void *)&poll_args, RUBY_UBF_IO, 0);
3422
3436
  #else
3423
3437
  backend_poll (EV_A_ waittime);
3424
3438
  #endif
@@ -88,12 +88,12 @@ module Coolio
88
88
  # event callbacks to watchers until all watchers associated with
89
89
  # the loop have been disabled or detached. The loop may be
90
90
  # explicitly stopped by calling the stop method on the loop object.
91
- def run
91
+ def run(timeout = nil)
92
92
  raise RuntimeError, "no watchers for this loop" if @watchers.empty?
93
93
 
94
94
  @running = true
95
95
  while @running and not @active_watchers.zero?
96
- run_once
96
+ run_once(timeout)
97
97
  end
98
98
  @running = false
99
99
  end
@@ -1,5 +1,5 @@
1
1
  module Coolio
2
- VERSION = "1.2.1"
2
+ VERSION = "1.2.2"
3
3
 
4
4
  def self.version; VERSION; end
5
5
  end
@@ -4,6 +4,13 @@ $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
4
4
  require 'rspec'
5
5
  require 'cool.io'
6
6
 
7
+ def unused_port
8
+ s = TCPServer.open(0)
9
+ port = s.addr[1]
10
+ s.close
11
+ port
12
+ end
13
+
7
14
  RSpec.configure do |c|
8
15
  if RUBY_PLATFORM =~ /mingw|win32/
9
16
  $stderr.puts "Skip some specs on Windows"
@@ -0,0 +1,130 @@
1
+ require File.expand_path('../spec_helper', __FILE__)
2
+
3
+ TIMEOUT = 0.010
4
+ HOST = '127.0.0.1'
5
+ PORT = unused_port
6
+
7
+ def send_data(data)
8
+ io = TCPSocket.new('127.0.0.1', PORT)
9
+ begin
10
+ io.write data
11
+ ensure
12
+ io.close
13
+ end
14
+ end
15
+
16
+ class MyConnection < Coolio::Socket
17
+ attr_accessor :data, :connected, :closed
18
+
19
+ def initialize(io, on_message)
20
+ super(io)
21
+ @on_message = on_message
22
+ end
23
+
24
+ def on_connect
25
+ @connected = true
26
+ end
27
+
28
+ def on_close
29
+ @closed = true
30
+ end
31
+
32
+ def on_read(data)
33
+ @on_message.call(data)
34
+ end
35
+ end
36
+
37
+ @data = ""
38
+ def on_message(data)
39
+ @data = data
40
+ end
41
+
42
+ def test_run(data = nil)
43
+ reactor = Coolio::Loop.new
44
+ server = Cool.io::TCPServer.new(HOST, PORT, MyConnection, method(:on_message))
45
+ reactor.attach(server)
46
+ thread = Thread.new { reactor.run }
47
+ send_data(data) if data
48
+ sleep TIMEOUT
49
+ reactor.stop
50
+ server.detach
51
+ send_data('') # to leave from blocking loop
52
+ thread.join
53
+ @data
54
+ ensure
55
+ server.close
56
+ end
57
+
58
+ def test_run_once(data = nil)
59
+ reactor = Coolio::Loop.new
60
+ server = Cool.io::TCPServer.new(HOST, PORT, MyConnection, method(:on_message))
61
+ reactor.attach(server)
62
+ thread = Thread.new do
63
+ reactor.run_once # on_connect
64
+ reactor.run_once # on_read
65
+ end
66
+ send_data(data) if data
67
+ thread.join
68
+ server.detach
69
+ @data
70
+ ensure
71
+ server.close
72
+ end
73
+
74
+ def test_run_once_timeout(timeout = TIMEOUT)
75
+ reactor = Coolio::Loop.new
76
+ server = Cool.io::TCPServer.new(HOST, PORT, MyConnection, method(:on_message))
77
+ reactor.attach(server)
78
+ running = true
79
+ thread = Thread.new { reactor.run_once(timeout) }
80
+ sleep timeout
81
+ server.detach
82
+ thread.join
83
+ @data
84
+ ensure
85
+ server.close
86
+ end
87
+
88
+ def test_run_timeout(data = nil, timeout = TIMEOUT)
89
+ reactor = Coolio::Loop.new
90
+ server = Cool.io::TCPServer.new(HOST, PORT, MyConnection, method(:on_message))
91
+ reactor.attach(server)
92
+ running = true
93
+ thread = Thread.new do
94
+ while running and reactor.has_active_watchers?
95
+ reactor.run_once(timeout)
96
+ end
97
+ end
98
+ send_data(data) if data
99
+ sleep timeout
100
+ server.detach
101
+ running = false # another send is not required
102
+ thread.join
103
+ @data
104
+ ensure
105
+ server.close
106
+ end
107
+
108
+ describe Coolio::TCPServer do
109
+
110
+ it '#run' do
111
+ test_run("hello").should == "hello"
112
+ end
113
+
114
+ it '#run_once' do
115
+ test_run_once("hello").should == "hello"
116
+ end
117
+
118
+ it '#run_once(timeout)' do
119
+ test_run_once_timeout # should not block
120
+ end
121
+
122
+ it '#run_once(-timeout)' do
123
+ expect { test_run_once_timeout(-0.1) }.to raise_error(ArgumentError)
124
+ end
125
+
126
+ it '#run(timeout)' do
127
+ test_run_timeout("hello").should == "hello"
128
+ end
129
+
130
+ end
metadata CHANGED
@@ -1,8 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cool.io
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.1
5
- prerelease:
4
+ version: 1.2.2
6
5
  platform: ruby
7
6
  authors:
8
7
  - Tony Arcieri
@@ -10,54 +9,48 @@ authors:
10
9
  autorequire:
11
10
  bindir: bin
12
11
  cert_chain: []
13
- date: 2014-02-21 00:00:00.000000000 Z
12
+ date: 2014-04-16 00:00:00.000000000 Z
14
13
  dependencies:
15
14
  - !ruby/object:Gem::Dependency
16
15
  name: rake-compiler
17
16
  requirement: !ruby/object:Gem::Requirement
18
- none: false
19
17
  requirements:
20
- - - ~>
18
+ - - "~>"
21
19
  - !ruby/object:Gem::Version
22
20
  version: 0.8.3
23
21
  type: :development
24
22
  prerelease: false
25
23
  version_requirements: !ruby/object:Gem::Requirement
26
- none: false
27
24
  requirements:
28
- - - ~>
25
+ - - "~>"
29
26
  - !ruby/object:Gem::Version
30
27
  version: 0.8.3
31
28
  - !ruby/object:Gem::Dependency
32
29
  name: rspec
33
30
  requirement: !ruby/object:Gem::Requirement
34
- none: false
35
31
  requirements:
36
- - - ! '>='
32
+ - - ">="
37
33
  - !ruby/object:Gem::Version
38
34
  version: 2.13.0
39
35
  type: :development
40
36
  prerelease: false
41
37
  version_requirements: !ruby/object:Gem::Requirement
42
- none: false
43
38
  requirements:
44
- - - ! '>='
39
+ - - ">="
45
40
  - !ruby/object:Gem::Version
46
41
  version: 2.13.0
47
42
  - !ruby/object:Gem::Dependency
48
43
  name: rdoc
49
44
  requirement: !ruby/object:Gem::Requirement
50
- none: false
51
45
  requirements:
52
- - - ! '>='
46
+ - - ">="
53
47
  - !ruby/object:Gem::Version
54
48
  version: 3.6.0
55
49
  type: :development
56
50
  prerelease: false
57
51
  version_requirements: !ruby/object:Gem::Requirement
58
- none: false
59
52
  requirements:
60
- - - ! '>='
53
+ - - ">="
61
54
  - !ruby/object:Gem::Version
62
55
  version: 3.6.0
63
56
  description: Cool.io provides a high performance event framework for Ruby which uses
@@ -72,9 +65,9 @@ extensions:
72
65
  - ext/iobuffer/extconf.rb
73
66
  extra_rdoc_files: []
74
67
  files:
75
- - .gitignore
76
- - .rspec
77
- - .travis.yml
68
+ - ".gitignore"
69
+ - ".rspec"
70
+ - ".travis.yml"
78
71
  - CHANGES.md
79
72
  - Gemfile
80
73
  - LICENSE
@@ -148,44 +141,39 @@ files:
148
141
  - spec/dns_spec.rb
149
142
  - spec/spec_helper.rb
150
143
  - spec/stat_watcher_spec.rb
144
+ - spec/tcp_server_spec.rb
151
145
  - spec/timer_watcher_spec.rb
152
146
  - spec/unix_listener_spec.rb
153
147
  - spec/unix_server_spec.rb
154
148
  homepage: http://coolio.github.com
155
149
  licenses: []
150
+ metadata: {}
156
151
  post_install_message:
157
152
  rdoc_options: []
158
153
  require_paths:
159
154
  - lib
160
155
  required_ruby_version: !ruby/object:Gem::Requirement
161
- none: false
162
156
  requirements:
163
- - - ! '>='
157
+ - - ">="
164
158
  - !ruby/object:Gem::Version
165
159
  version: '0'
166
- segments:
167
- - 0
168
- hash: 2606014626052723115
169
160
  required_rubygems_version: !ruby/object:Gem::Requirement
170
- none: false
171
161
  requirements:
172
- - - ! '>='
162
+ - - ">="
173
163
  - !ruby/object:Gem::Version
174
164
  version: '0'
175
- segments:
176
- - 0
177
- hash: 2606014626052723115
178
165
  requirements: []
179
166
  rubyforge_project:
180
- rubygems_version: 1.8.23
167
+ rubygems_version: 2.2.2
181
168
  signing_key:
182
- specification_version: 3
169
+ specification_version: 4
183
170
  summary: A cool framework for doing high performance I/O in Ruby
184
171
  test_files:
185
172
  - spec/async_watcher_spec.rb
186
173
  - spec/dns_spec.rb
187
174
  - spec/spec_helper.rb
188
175
  - spec/stat_watcher_spec.rb
176
+ - spec/tcp_server_spec.rb
189
177
  - spec/timer_watcher_spec.rb
190
178
  - spec/unix_listener_spec.rb
191
179
  - spec/unix_server_spec.rb