Skip to content

Instantly share code, notes, and snippets.

@bestie
Last active September 7, 2022 10:22
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bestie/b32cbec68ac4148441a42d02a1697de3 to your computer and use it in GitHub Desktop.
Save bestie/b32cbec68ac4148441a42d02a1697de3 to your computer and use it in GitHub Desktop.
Ruby Thread#stop / #Thread.stop? doesn't work how you might think.
# This does work because a my custom thread status is unambiguous.
#
# However, you may notice that setting the status and sleeping are not atomic.
require "net/http"
responses = []
thread = Thread.new do
Thread.current[:what_i_am_doing] = "starting"
responses << Net::HTTP.get(URI.parse("https://zendesk.com"))
Thread.current[:what_i_am_doing] = "stopping"
sleep # sleep/wakeup seems nicer then stop/wakeup, also reflects value of Thread#status => "sleep"
Thread.current[:what_i_am_doing] = "just woke up"
responses << Net::HTTP.get(URI.parse("https://zendesk.com"))
Thread.current[:what_i_am_doing] = "all done"
end
until thread[:what_i_am_doing] == "stopping" # guaranteed to wait for the request to finish
sleep(0.001)
end
puts "#{responses.size == 1}: Thread has made the first request and stopped"
puts "#{thread.inspect.include?("sleep>")}: Thread is now having a regular sleep"
thread.wakeup
thread.join(_timeout = 5) # Hopefully that's long enough for the request
puts "#{responses.size == 2}: Thread made both requests"
puts "#{thread.inspect.include?("dead")}: Thread should be dead by now"
puts "#{thread[:what_i_am_doing] == "all done"}: Thread never woke up"
# This doesn't always work because Thread#status is ambiguous
require "net/http"
responses = []
thread = Thread.new do
Thread.current[:what_i_am_doing] = "starting"
responses << Net::HTTP.get(URI.parse("https://zendesk.com"))
Thread.current[:what_i_am_doing] = "stopping"
Thread.stop
Thread.current[:what_i_am_doing] = "just woke up"
responses << Net::HTTP.get(URI.parse("https://zendesk.com"))
Thread.current[:what_i_am_doing] = "all done"
end
until thread.stop? # Returns true when blocked on IO
sleep(0.001) # any amount of sleep helps thread switching but using next will be slower
end
thread.wakeup # Too early, the thread hasn't stopped yet
thread.join(_timeout = 5) # Give it as long as you like, it's stopped.
puts "#{responses.size == 1}: Thread only ever makes the first request"
puts "#{thread.inspect.include?("sleep_forever")}: Thread is now sleeping forever in stopped position"
puts "#{thread[:what_i_am_doing] == "stopping"}: Thread never woke up"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment