diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index f14d372..e0f4e9f 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -12,6 +12,7 @@ jobs: continue-on-error: ${{matrix.experimental}} strategy: + fail-fast: false matrix: os: - ubuntu diff --git a/ext/cool.io/watcher.h b/ext/cool.io/watcher.h index b4892f2..f795d28 100644 --- a/ext/cool.io/watcher.h +++ b/ext/cool.io/watcher.h @@ -26,20 +26,15 @@ #define Watcher_Detach(watcher_type, watcher) \ struct Coolio_Watcher *watcher_data; \ - struct Coolio_Loop *loop_data; \ \ watcher_data = Coolio_Watcher_ptr(watcher); \ \ 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; \ + if (watcher_data->enabled) { \ + rb_funcall(watcher, rb_intern("disable"), 0); \ } \ - loop_data = Coolio_Loop_ptr(watcher_data->loop); \ - \ - ev_##watcher_type##_stop(loop_data->ev_loop, &watcher_data->event_types.ev_##watcher_type); \ rb_call_super(0, 0) #define Watcher_Enable(watcher_type, watcher) \ @@ -66,10 +61,10 @@ if(watcher_data->loop == Qnil) \ rb_raise(rb_eRuntimeError, "not attached to a loop"); \ \ - rb_call_super(0, 0); \ - \ - loop_data = Coolio_Loop_ptr(watcher_data->loop); \ - \ - ev_##watcher_type##_stop(loop_data->ev_loop, &watcher_data->event_types.ev_##watcher_type) + if (watcher_data->enabled) { \ + loop_data = Coolio_Loop_ptr(watcher_data->loop); \ + ev_##watcher_type##_stop(loop_data->ev_loop, &watcher_data->event_types.ev_##watcher_type); \ + } \ + rb_call_super(0, 0); #endif diff --git a/spec/detach_race_condition_spec.rb b/spec/detach_race_condition_spec.rb index b22970c..5d11be2 100644 --- a/spec/detach_race_condition_spec.rb +++ b/spec/detach_race_condition_spec.rb @@ -47,4 +47,49 @@ def on_readable end }.not_to raise_error end + + class HttpHandler < Coolio::IO + RESPONSE = "HTTP/1.1 200 OK\r\nContent-Length: 1024\r\nConnection: close\r\n\r\n" + ("X" * 1024) + + def on_connect + end + + def on_read(data) + write(RESPONSE) + end + + def on_write_complete + close + end + end + + # https://github.com/socketry/cool.io/issues/89 + it "does not cause memory leaks" do + port = 18989 + loop = Coolio::Loop.default + + server = Coolio::TCPServer.new('127.0.0.1', port, HttpHandler) + server.attach(loop) + + event_thread = Thread.new { loop.run } + + request = "GET / HTTP/1.1\r\nHost: localhost\r\n\r\n" + + 10.times do |iteration| + begin + sock = TCPSocket.new('127.0.0.1', port) + sock.write(request) + sock.read + sock.close + rescue => e + sleep 0.01 + retry + end + end + + server.close + event_thread.join + + expect(loop.watchers).to be_empty + end end