From ae016eb26409895313c5b3e229749eb801724c8d Mon Sep 17 00:00:00 2001 From: Petr Heinz Date: Wed, 30 Sep 2026 18:31:23 +0200 Subject: [PATCH 1/2] Add failing test: HTTP#close leaves no thread behind The example slept 0.1 s after close and was still flaky on TruffleRuby, because Thread#kill only asks the thread to stop. Without the sleeps it fails on every run: close returns while the threads are still winding down. Co-Authored-By: Claude Fable 5.1 --- spec/logtail/log_devices/http_spec.rb | 2 -- 1 file changed, 2 deletions(-) diff --git a/spec/logtail/log_devices/http_spec.rb b/spec/logtail/log_devices/http_spec.rb index fad540e..9ae0743 100755 --- a/spec/logtail/log_devices/http_spec.rb +++ b/spec/logtail/log_devices/http_spec.rb @@ -57,10 +57,8 @@ http.send(:ensure_flush_threads_are_started) http.close thread = http.instance_variable_get(:@flush_thread) - sleep 0.1 # too fast! expect(thread).to_not be_alive thread = http.instance_variable_get(:@request_outlet_thread) - sleep 0.1 # too fast! expect(thread).to_not be_alive end From 53f627e7adf1aa7f69bb03176c09aa4c68bf9930 Mon Sep 17 00:00:00 2001 From: Petr Heinz Date: Wed, 30 Sep 2026 18:32:01 +0200 Subject: [PATCH 2/2] HTTP#close waits for the threads it kills Thread#kill only asks a thread to stop, so close returned while the flush and request threads were still winding down, which made the close example flaky on TruffleRuby. Joining them makes close return once they are gone, which is what its at_exit caller should get. Co-Authored-By: Claude Fable 5.1 --- lib/logtail/log_devices/http.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/logtail/log_devices/http.rb b/lib/logtail/log_devices/http.rb index 331bf34..48b6edf 100755 --- a/lib/logtail/log_devices/http.rb +++ b/lib/logtail/log_devices/http.rb @@ -119,13 +119,13 @@ def flush # Closes the log device, cleans up, and attempts one last delivery. def close # Kill the flush thread immediately since we are about to flush again. - @flush_thread.kill if @flush_thread + @flush_thread.kill.join if @flush_thread # Flush all remaining messages flush # Kill the request queue thread. Flushing ensures that no requests are pending. - @request_outlet_thread.kill if @request_outlet_thread + @request_outlet_thread.kill.join if @request_outlet_thread end def deliver_one(msg)