Skip to content

Instantly share code, notes, and snippets.

@x-yuri
Last active September 10, 2024 16:04
Show Gist options
  • Save x-yuri/253f76df6287441f64b5eaee418813c0 to your computer and use it in GitHub Desktop.
Save x-yuri/253f76df6287441f64b5eaee418813c0 to your computer and use it in GitHub Desktop.
Breaking Timeout::timeout()

Breaking Timeout::timeout()

a.sh:

docker run --rm -it \
-v "$PWD/timeout.patch:/timeout.patch" \
-v "$PWD/a.rb:/a.rb" \
alpine:3.20.1 sh -euxc '
    apk add ruby ruby-concurrent-ruby patch
    patch -p1 < timeout.patch
    ruby a.rb
'

a.rb:

require 'timeout'
require 'concurrent-ruby'
pool = Concurrent::ThreadPoolExecutor.new(
  max_threads: 50,
)
while true
  begin
  pool.post do
    print '.'
    Timeout::timeout(5) {
      (1..100_000_000).reduce(:+)
    }
  end
  rescue => e
    puts "-- #{e.message}"
  end
  sleep(1)
end

timeout.patch:

--- /usr/lib/ruby/3.3.0/timeout.rb
+++ /usr/lib/ruby/3.3.0/timeout.rb
@@ -55,11 +55,12 @@
   private_constant :CONDVAR, :QUEUE, :QUEUE_MUTEX, :TIMEOUT_THREAD_MUTEX

   class Request
-    attr_reader :deadline
+    attr_reader :deadline, :_start

     def initialize(thread, timeout, exception_class, message)
       @thread = thread
-      @deadline = GET_TIME.call(Process::CLOCK_MONOTONIC) + timeout
+      @_start = GET_TIME.call(Process::CLOCK_MONOTONIC)
+      @deadline = @_start + timeout
       @exception_class = exception_class
       @message = message

@@ -112,7 +113,12 @@
         end

         requests.each do |req|
-          req.interrupt if req.expired?(now)
+          if req.expired?(now)
+            if !req.done? && now - req._start > 5
+              puts "-- expired after #{now - req._start}"
+            end
+            req.interrupt
+          end
         end
         requests.reject!(&:done?)
       end
@@ -185,6 +191,7 @@
       begin
         return yield(sec)
       ensure
+        puts "-- finished"
         request.finished
       end
     end
$ sh a.sh
...
+ ruby a.rb
.....-- expired after 5.34459837898612
-- expired after 5.253567082807422
-- finished
...-- expired after 5.25163783505559
-- expired after 6.865435693413019
-- expired after 5.349646176211536
-- finished
-- finished
-- finished
..-- finished
.-- expired after 5.250308169052005
-- expired after 8.988785319030285
-- expired after 8.080014524050057
.-- expired after 5.352108385413885
.-- finished
.-- finished
.-- finished
-- finished
.-- expired after 5.2506144773215055
-- expired after 11.507156471721828
-- expired after 8.983930067159235
-- expired after 7.671963807195425
-- expired after 6.259166737087071
-- expired after 6.462158016860485
-- finished
.-- finished
..-- expired after 5.353667972609401
.-- finished
.-- finished
.-- expired after 5.568056344054639
-- expired after 5.365871897898614
-- expired after 5.264268446713686
-- finished
.......-- finished
-- finished
.-- finished
..............................-- expired after 5.26423036493361
-- expired after 135.35132688377053
-- expired after 133.73462444357574
-- expired after 131.4107227101922
-- expired after 130.60264235083014
-- expired after 129.69334887433797
-- expired after 128.48101944755763
-- expired after 127.06668251659721
-- expired after 125.95543801039457
-- expired after 124.13710950594395
-- expired after 118.07585388794541
-- expired after 113.73328796494752
-- expired after 111.51159476861358
-- expired after 109.18919527251273
-- expired after 106.76638461183757
-- expired after 104.2427132120356
-- expired after 101.61858779937029
-- expired after 98.8925526086241
-- expired after 96.06468087621033
-- expired after 93.13584002666175
-- expired after 90.10565130785108
-- expired after 86.97459204215556
-- expired after 83.74226874858141
-- expired after 80.40877974405885
-- expired after 76.9741388540715
-- expired after 73.43886508699507
-- expired after 69.8028405457735
-- expired after 66.06549525167793
-- expired after 62.227825090289116
-- expired after 58.28867427445948
-- expired after 54.24853610154241
-- expired after 50.107286545448005
-- expired after 45.86515790876001
-- expired after 41.52191775292158
-- expired after 37.0780393788591
-- expired after 32.53252158034593
-- expired after 27.8859252743423
-- expired after 23.138264525681734
-- expired after 18.289593886584044
-- expired after 13.339680053293705
-- expired after 8.287457170896232
-- finished
.-- finished
.-- finished
..-- finished
.-- finished
.-- finished
.-- finished
.-- finished
.-- finished
.-- finished
.-- finished
.-- finished
.-- finished
.-- finished
.-- finished
.-- finished
.-- finished
.-- finished
.-- finished
.-- finished
.-- finished
.-- finished
.-- finished
.-- finished
.-- finished
.-- finished
.-- finished
.-- expired after 5.55707688536495
-- expired after 5.255160040222108
-- expired after 121.62269569281489
-- expired after 121.01629380509257
-- expired after 119.80299641750753
-- expired after 118.28728046547621
-- expired after 116.46817484870553
-- expired after 114.34875472728163
-- expired after 111.93045178148896
-- expired after 109.20525650959462
-- expired after 106.174814353697
-- expired after 102.84679177589715
-- expired after 99.21311837527901
-- expired after 95.27807685080916
-- expired after 91.03835707250983
-- expired after 86.49613354168832
-- expired after 81.65136142540723
-- expired after 76.50452550593764
-- expired after 71.05460843350738
-- expired after 65.30168993864208
-- expired after 59.25316300243139
-- expired after 52.899687447585166
-- expired after 46.24263026472181
-- expired after 39.28281879238784
-- expired after 32.013337675482035
-- expired after 24.443570415489376
-- expired after 16.56559687666595
-- expired after 8.384128598496318
-- finished
..-- finished
.-- finished
.-- finished
.-- finished
.-- finished
.-- finished
.-- finished
.-- finished
.-- finished
.-- finished
.-- finished
.-- finished
.-- finished
.-- finished
.-- finished
.-- finished
.-- finished
.-- finished
.-- finished
.-- finished
.-- finished
.-- finished
.-- finished
.-- finished
.-- finished
.-- finished
.-- expired after 5.051211640238762
-- expired after 119.8838289231062
-- expired after 119.27751687541604
-- expired after 118.36826601810753
-- expired after 117.15574592445046
-- expired after 115.63964347168803
-- expired after 113.8214066112414
-- expired after 111.69996492844075
-- expired after 109.27556894812733
-- expired after 106.54860797617584
-- expired after 103.5186853967607
-- expired after 100.18569271359593
-- expired after 96.54944463539869
-- expired after 92.60957904532552
-- expired after 88.36697289533913
-- expired after 83.82317813113332
-- expired after 78.97801787871867
-- expired after 73.83003791049123
-- expired after 68.37873449269682
-- expired after 62.623996295034885
-- expired after 56.56519324332476
-- expired after 50.20169021654874
-- expired after 43.536571375094354
-- expired after 36.567623062990606
-- expired after 29.293681268580258
-- expired after 21.71676716953516
-- expired after 13.839074414223433
-- expired after 5.656184485182166
-- finished
.-- finished
.-- finished
..-- finished
.-- finished
.-- finished
.-- finished
.-- finished
.-- finished
.-- finished
.-- finished
.-- finished
.-- finished
.-- finished
.-- finished
.-- finished
.-- finished
.-- finished
.-- finished
.-- finished
.-- finished
.-- finished
.-- finished
.-- finished
.-- finished
.-- finished
.-- finished
.-- expired after 5.553942596539855
-- expired after 5.252297136001289
-- expired after 121.60733005218208
-- expired after 121.0013488046825
-- expired after 119.78919022064656
-- expired after 118.27395635843277
-- expired after 116.45605781208724
-- expired after 114.33566909469664
-- expired after 111.91130980663002
-- expired after 109.18514362815768
-- expired after 106.157590595074
-- expired after 102.82696213107556
-- expired after 99.1940359370783
-- expired after 95.2583811385557
-- expired after 91.02185294311494
-- expired after 86.48003819771111
-- expired after 81.63898331299424
-- expired after 76.49240232259035
-- expired after 71.0440261233598
-- expired after 65.29242160078138
-- expired after 59.23570531792939
-- expired after 52.876851396635175
-- expired after 46.215400505810976
-- expired after 39.25160522013903
-- expired after 31.985599655658007
-- expired after 24.418087415397167
-- expired after 16.5480900965631
-- expired after 8.375697910785675
-- finished
.-- finished
.-- finished
..-- finished
.-- finished
.-- finished
.-- finished
.-- finished
.-- finished
.-- finished
.-- finished
.-- finished
.-- finished
.-- finished
.-- finished
.-- finished
.-- finished
.-- finished
.-- finished
.-- finished
.-- finished
.-- finished
.-- finished
.-- finished
.-- finished
.-- finished
.-- finished
.-- expired after 5.555713854730129
-- expired after 5.253802557475865
-- expired after 121.68442805018276
-- expired after 121.07817130256444
-- expired after 119.86521148402244
-- expired after 118.34989686775953
-- expired after 116.53161072731018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment