Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions ext/cool.io/loop.c
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,12 @@ void Coolio_Loop_process_event(VALUE watcher, int revents)
/* The Global VM lock isn't held right now, but hopefully
* we can still do this safely */
watcher_data = Coolio_Watcher_ptr(watcher);

if (watcher_data->enabled == 0) {
/* Ignore event because watcher was already detached. */
return;
}

loop_data = Coolio_Loop_ptr(watcher_data->loop);

/* Well, what better place to explain how this all works than
Expand Down
4 changes: 4 additions & 0 deletions ext/cool.io/watcher.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@
if(watcher_data->loop == Qnil) \
rb_raise(rb_eRuntimeError, "not attached to a loop"); \
\
if (watcher_data->enabled == 0) { \
/* Ignore because watcher was already detached. */ \
return Qnil; \
} \
loop_data = Coolio_Loop_ptr(watcher_data->loop); \
\
ev_##watcher_type##_stop(loop_data->ev_loop, &watcher_data->event_types.ev_##watcher_type); \
Expand Down
50 changes: 50 additions & 0 deletions spec/detach_race_condition_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
require 'spec_helper'

describe Cool.io::Loop do
class Victim < Cool.io::IOWatcher
def initialize(io)
super
@io = io
end

def on_readable
begin
@io.read_nonblock(1024)
rescue IO::WaitReadable, EOFError
end
end
end

# https://github.com/socketry/cool.io/issues/87
it "does not raise TypeError when a watcher is detached while an event is pending" do
loop = Cool.io::Loop.default

iterations = 200

expect {
iterations.times do
r_victim, w_victim = IO.pipe
victim_watcher = Victim.new(r_victim)
victim_watcher.attach(loop)

t1 = Thread.new do
sleep 0.01
w_victim.write("dummy\n")
end

t2 = Thread.new do
sleep 0.01
victim_watcher.detach
end

loop.run_once

t1.join
t2.join

r_victim.close
w_victim.close
end
}.not_to raise_error
end
end