pax_global_header00006660000000000000000000000064125656365740014535gustar00rootroot0000000000000052 comment=4b1df4c6cc665389f807365ee340f2acbb30da81 timers-4.1.1/000077500000000000000000000000001256563657400130435ustar00rootroot00000000000000timers-4.1.1/.coveralls.yml000066400000000000000000000000311256563657400156300ustar00rootroot00000000000000service-name: travis-pro timers-4.1.1/.gitignore000066400000000000000000000002321256563657400150300ustar00rootroot00000000000000*.gem *.rbc .bundle .config .yardoc Gemfile.lock InstalledFiles _yardoc coverage doc/ lib/bundler/man pkg rdoc spec/reports test/tmp test/version_tmp tmp timers-4.1.1/.rspec000066400000000000000000000001051256563657400141540ustar00rootroot00000000000000--color --format documentation --backtrace --order random --warnings timers-4.1.1/.travis.yml000066400000000000000000000003631256563657400151560ustar00rootroot00000000000000rvm: - 1.9.3 - 2.0.0 - 2.1.2 - ruby-head - jruby - jruby-head - rbx-2 matrix: fast_finish: true allow_failures: - rvm: ruby-head - rvm: jruby-head - rvm: rbx-2 notifications: irc: "irc.freenode.org#celluloid" timers-4.1.1/AUTHORS.md000066400000000000000000000004541256563657400145150ustar00rootroot00000000000000# The Celluloid timers gem is beamed directly to you from the minds of... - Tony Arcieri - Jeremy Hinegardner - Sean Gregory - Chuck Remes - Utenmiki - Ron Evans - Larry Lv - Bruno Enten - Jesse Cooke - Nicholas Evans - Dimitrij Denissenko - Ryan LeCompte - Samuel G. D. Williamstimers-4.1.1/CHANGES.md000066400000000000000000000027761256563657400144510ustar00rootroot000000000000004.1.1 (2015-08-21) ------------------ * Remove `RubyProf` from Gemfile and a test, due to it providing no substantial benefit while increasing problems building bundles under Rubinius. 4.1.0 (2015-08-16) ------------------ * Addition of `now_and_every` method; fires block immediately, then sets recurring timer. * Includes `now_and_after` method; does the same as above for one-shot timers: essentially a "two-shot" timer. 4.0.1 (2014-09-10) ------------------ * Memory leak fixes * mathn fix for those crazy enough to use it 4.0.0 (2014-07-27) ------------------ * Replace Timers::Timeout with Timers::Wait * Timers::Group#wait_interval now returns nil when no timers, a postive or negative interval which if positive is the amount of time required to wait and if negative, how far in the past the latest timer should have fired * Performance improvements 3.0.1 (2014-06-27) ------------------ * Require 'set' automatically 3.0.0 (2014-06-14) ------------------ * Refactor `Timers` class into `Timers::Group` * Add `Timers::Timeout` class for implementing timeouts * Fix timer fudging * Update to RSpec 3 2.0.0 (2013-12-30) ------------------ * Switch to Hitimes for high precision monotonic counters * Removed Timers#time. Replaced with Timers#current_offset which provides a monotonic time counter. 1.1.0 ----- * Timers#after_milliseconds and #after_ms for waiting in milliseconds 1.0.2 ----- * Handle overdue timers correctly 1.0.1 ----- * Explicitly require Forwardable from stdlib 1.0.0 ----- * Initial release timers-4.1.1/Gemfile000066400000000000000000000001771256563657400143430ustar00rootroot00000000000000source 'https://rubygems.org' gem 'coveralls', :require => false # Specify your gem's dependencies in timers.gemspec gemspec timers-4.1.1/LICENSE000066400000000000000000000022371256563657400140540ustar00rootroot00000000000000Copyright (c) 2012-14 The Celluloid Timers Developers: given in the file AUTHORS.md at https://github.com/celluloid/timers/blob/master/AUTHORS.md MIT License Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. timers-4.1.1/README.md000066400000000000000000000055401256563657400143260ustar00rootroot00000000000000Timers ====== [![Gem Version](https://badge.fury.io/rb/timers.png)](http://rubygems.org/gems/timers) [![Build Status](https://secure.travis-ci.org/celluloid/timers.png?branch=master)](http://travis-ci.org/celluloid/timers) [![Code Climate](https://codeclimate.com/github/celluloid/timers.png)](https://codeclimate.com/github/celluloid/timers) [![Coverage Status](https://coveralls.io/repos/celluloid/timers/badge.png?branch=master)](https://coveralls.io/r/celluloid/timers) Ruby timer collections. Schedule several procs to fire after configurable delays or at periodic intervals. This gem is especially useful when you are faced with an API that accepts a single timeout but you want to run multiple timers on top of it. An example of such a library is [nio4r](https://github.com/celluloid/nio4r), a cross-platform Ruby library for using system calls like epoll and kqueue. Usage ----- Create a new timer group with `Timers::Group.new`: ```ruby require 'timers' timers = Timers::Group.new ``` Schedule a proc to run after 5 seconds with `Timers::Group#after`: ```ruby five_second_timer = timers.after(5) { puts "Take five" } ``` The `five_second_timer` variable is now bound to a Timers::Timer object. To cancel a timer, use `Timers::Timer#cancel` Once you've scheduled a timer, you can wait until the next timer fires with `Timers::Group#wait`: ```ruby # Waits 5 seconds timers.wait # The script will now print "Take five" ``` You can schedule a block to run periodically with `Timers::Group#every`: ```ruby every_five_seconds = timers.every(5) { puts "Another 5 seconds" } loop { timers.wait } ``` You can also schedule a block to run immediately and periodically with `Timers::Group#now_and_every`: ```ruby now_and_every_five_seconds = timers.now_and_every(5) { puts "Now and in another 5 seconds" } loop { timers.wait } ``` If you'd like another method to do the waiting for you, e.g. `Kernel.select`, you can use `Timers::Group#wait_interval` to obtain the amount of time to wait. When a timeout is encountered, you can fire all pending timers with `Timers::Group#fire`: ```ruby loop do interval = timers.wait_interval ready_readers, ready_writers = select readers, writers, nil, interval if ready_readers || ready_writers # Handle IO ... else # Timeout! timers.fire end end ``` You can also pause and continue individual timers, or all timers: ```ruby paused_timer = timers.every(5) { puts "I was paused" } paused_timer.pause 10.times { timers.wait } # will not fire paused timer paused_timer.resume 10.times { timers.wait } # will fire timer timers.pause 10.times { timers.wait } # will not fire any timers timers.resume 10.times { timers.wait } # will fire all timers ``` License ------- Copyright (c) 2014 Celluloid timers project developers (given in the file AUTHORS.md). Distributed under the MIT License. See LICENSE file for further details. timers-4.1.1/Rakefile000066400000000000000000000002021256563657400145020ustar00rootroot00000000000000#!/usr/bin/env rake require "bundler/gem_tasks" require 'rspec/core/rake_task' RSpec::Core::RakeTask.new task :default => :spec timers-4.1.1/lib/000077500000000000000000000000001256563657400136115ustar00rootroot00000000000000timers-4.1.1/lib/timers.rb000066400000000000000000000001101256563657400154310ustar00rootroot00000000000000 require 'timers/version' require 'timers/group' require 'timers/wait' timers-4.1.1/lib/timers/000077500000000000000000000000001256563657400151145ustar00rootroot00000000000000timers-4.1.1/lib/timers/events.rb000066400000000000000000000052721256563657400167530ustar00rootroot00000000000000 require 'forwardable' require 'hitimes' require 'timers/timer' module Timers # Maintains an ordered list of events, which can be cancelled. class Events # Represents a cancellable handle for a specific timer event. class Handle def initialize(time, callback) @time = time @callback = callback end # The absolute time that the handle should be fired at. attr :time # Cancel this timer, O(1). def cancel! # The simplest way to keep track of cancelled status is to nullify the # callback. This should also be optimal for garbage collection. @callback = nil end # Has this timer been cancelled? Cancelled timer's don't fire. def cancelled? @callback.nil? end def > other @time > other.to_f end def to_f @time end # Fire the callback if not cancelled with the given time parameter. def fire(time) if @callback @callback.call(time) end end end def initialize # A sequence of handles, maintained in sorted order, future to present. # @sequence.last is the next event to be fired. @sequence = [] end # Add an event at the given time. def schedule(time, callback) handle = Handle.new(time.to_f, callback) index = bisect_left(@sequence, handle) # Maintain sorted order, O(logN) insertion time. @sequence.insert(index, handle) return handle end # Returns the first non-cancelled handle. def first while handle = @sequence.last if handle.cancelled? @sequence.pop else return handle end end # @sequence.reverse.find { |handle| !handle.cancelled? } end # Returns the number of pending (possibly cancelled) events. def size @sequence.size end # Fire all handles for which Handle#time is less than the given time. def fire(time) pop(time).reverse_each do |handle| handle.fire(time) end end private # Efficiently take k handles for which Handle#time is less than the given # time. def pop(time) index = bisect_left(@sequence, time) @sequence.pop(@sequence.size - index) end # Return the left-most index where to insert item e, in a list a, assuming # a is sorted in descending order. def bisect_left(a, e, l = 0, u = a.length) while l < u m = l + (u-l).div(2) if a[m] > e l = m+1 else u = m end end return l end end end timers-4.1.1/lib/timers/group.rb000066400000000000000000000062721256563657400166040ustar00rootroot00000000000000 require 'set' require 'forwardable' require 'hitimes' require 'timers/timer' require 'timers/events' module Timers class Group include Enumerable extend Forwardable def_delegators :@timers, :each, :empty? def initialize @events = Events.new @timers = Set.new @paused_timers = Set.new @interval = Hitimes::Interval.new @interval.start end # Scheduled events: attr :events # Active timers: attr :timers # Paused timers: attr :paused_timers # Call the given block after the given interval. The first argument will be # the time at which the group was asked to fire timers for. def after(interval, &block) Timer.new(self, interval, false, &block) end # Call the given block immediately, and then after the given interval. The first # argument will be the time at which the group was asked to fire timers for. def now_and_after(interval, &block) block.call after(interval, &block) end # Call the given block periodically at the given interval. The first # argument will be the time at which the group was asked to fire timers for. def every(interval, recur = true, &block) Timer.new(self, interval, recur, &block) end # Call the given block immediately, and then periodically at the given interval. The first # argument will be the time at which the group was asked to fire timers for. def now_and_every(interval, recur = true, &block) block.call every(interval, recur, &block) end # Wait for the next timer and fire it. Can take a block, which should behave # like sleep(n), except that n may be nil (sleep forever) or a negative # number (fire immediately after return). def wait(&block) if block_given? yield wait_interval while interval = wait_interval and interval > 0 yield interval end else while interval = wait_interval and interval > 0 # We cannot assume that sleep will wait for the specified time, it might be +/- a bit. sleep interval end end return fire end # Interval to wait until when the next timer will fire. # - nil: no timers # - -ve: timers expired already # - 0: timers ready to fire # - +ve: timers waiting to fire def wait_interval(offset = self.current_offset) if handle = @events.first return handle.time - Float(offset) end end # Fire all timers that are ready. def fire(offset = self.current_offset) @events.fire(offset) end # Pause all timers. def pause @timers.dup.each do |timer| timer.pause end end # Resume all timers. def resume @paused_timers.dup.each do |timer| timer.resume end end alias_method :continue, :resume # Delay all timers. def delay(seconds) @timers.each do |timer| timer.delay(seconds) end end # Cancel all timers. def cancel @timers.dup.each do |timer| timer.cancel end end # The group's current time. def current_offset @interval.to_f end end end timers-4.1.1/lib/timers/timer.rb000066400000000000000000000060521256563657400165640ustar00rootroot00000000000000 module Timers # An individual timer set to fire a given proc at a given time. A timer is # always connected to a Timer::Group but it would ONLY be in @group.timers # if it also has a @handle specified. Otherwise it is either PAUSED or has # been FIRED and is not recurring. You can manually enter this state by # calling #cancel and resume normal operation by calling #reset. class Timer include Comparable attr_reader :interval, :offset, :recurring def initialize(group, interval, recurring = false, offset = nil, &block) @group = group @interval = interval @recurring = recurring @block = block @offset = offset @handle = nil # If a start offset was supplied, use that, otherwise use the current timers offset. reset(@offset || @group.current_offset) end def paused? @group.paused_timers.include? self end def pause return if paused? @group.timers.delete self @group.paused_timers.add self @handle.cancel! if @handle @handle = nil end def resume return unless paused? @group.paused_timers.delete self # This will add us back to the group: reset end alias_method :continue, :resume # Extend this timer def delay(seconds) @handle.cancel! if @handle @offset += seconds @handle = @group.events.schedule(@offset, self) end # Cancel this timer. Do not call while paused. def cancel return unless @handle @handle.cancel! if @handle @handle = nil # This timer is no longer valid: @group.timers.delete self if @group end # Reset this timer. Do not call while paused. def reset(offset = @group.current_offset) # This logic allows us to minimise the interaction with @group.timers. # A timer with a handle is always registered with the group. if @handle @handle.cancel! else @group.timers << self end @offset = Float(offset) + @interval @handle = @group.events.schedule(@offset, self) end # Fire the block. def fire(offset = @group.current_offset) if recurring == :strict # ... make the next interval strictly the last offset + the interval: reset(@offset) elsif recurring reset(offset) else @offset = offset end @block.call(offset) cancel unless recurring end alias_method :call, :fire # Number of seconds until next fire / since last fire def fires_in @offset - @group.current_offset if @offset end # Inspect a timer def inspect str = "#= 0 str << "fires in #{fires_in} seconds" else str << "fired #{fires_in.abs} seconds ago" end str << ", recurs every #{interval}" if recurring else str << "dead" end str << ">" end end end timers-4.1.1/lib/timers/version.rb000066400000000000000000000000461256563657400171260ustar00rootroot00000000000000module Timers VERSION = "4.1.1" end timers-4.1.1/lib/timers/wait.rb000066400000000000000000000015721256563657400164120ustar00rootroot00000000000000 require 'hitimes' module Timers # An exclusive, monotonic timeout class. class Wait def self.for(duration, &block) if duration timeout = self.new(duration) timeout.while_time_remaining(&block) else while true yield(nil) end end end def initialize(duration) @duration = duration @remaining = true end attr :duration attr :remaining # Yields while time remains for work to be done: def while_time_remaining(&block) @interval = Hitimes::Interval.new @interval.start while time_remaining? yield @remaining end ensure @interval.stop @interval = nil end private def time_remaining? @remaining = (@duration - @interval.duration) return @remaining > 0 end end end timers-4.1.1/spec/000077500000000000000000000000001256563657400137755ustar00rootroot00000000000000timers-4.1.1/spec/cancel_spec.rb000066400000000000000000000015001256563657400165550ustar00rootroot00000000000000 require 'spec_helper' RSpec.describe Timers::Group do it "should be able to cancel twice" do fired = false timer = subject.after(0.1) { fired = true } 2.times do timer.cancel subject.wait end expect(fired).to be false end it "should be possble to reset after cancel" do fired = false timer = subject.after(0.1) { fired = true } timer.cancel subject.wait timer.reset subject.wait expect(fired).to be true end it "should cancel and remove one shot timers after they fire" do x = 0 Timers::Wait.for(2) do |remaining| timer = subject.every(0.2) { x += 1 } subject.after(0.1) { timer.cancel } subject.wait end expect(subject.timers).to be_empty expect(x).to be == 0 end end timers-4.1.1/spec/events_spec.rb000066400000000000000000000021431256563657400166400ustar00rootroot00000000000000 require 'spec_helper' RSpec.describe Timers::Events do it "should register an event" do fired = false callback = proc do |time| fired = true end subject.schedule(0.1, callback) expect(subject.size).to be == 1 subject.fire(0.15) expect(subject.size).to be == 0 expect(fired).to be true end it "should register events in order" do fired = [] times = [0.95, 0.1, 0.3, 0.5, 0.4, 0.2, 0.01, 0.9] times.each do |requested_time| callback = proc do |time| fired << requested_time end subject.schedule(requested_time, callback) end subject.fire(0.5) expect(fired).to be == times.sort.first(6) subject.fire(1.0) expect(fired).to be == times.sort end it "should fire events with the time they were fired at" do fired_at = :not_fired callback = proc do |time| # The time we actually were fired at: fired_at = time end subject.schedule(0.5, callback) subject.fire(1.0) expect(fired_at).to be == 1.0 end end timers-4.1.1/spec/every_spec.rb000066400000000000000000000015461256563657400164740ustar00rootroot00000000000000 require 'spec_helper' RSpec.describe Timers::Group do it "should fire several times" do result = [] subject.every(0.7) { result << :a } subject.every(2.3) { result << :b } subject.every(1.3) { result << :c } subject.every(2.4) { result << :d } Timers::Wait.for(2.5) do |remaining| subject.wait if subject.wait_interval < remaining end expect(result).to be == [:a, :c, :a, :a, :b, :d] end it "should fire immediately and then several times later" do result = [] subject.every(0.7) { result << :a } subject.every(2.3) { result << :b } subject.now_and_every(1.3) { result << :c } subject.now_and_every(2.4) { result << :d } Timers::Wait.for(2.5) do |remaining| subject.wait if subject.wait_interval < remaining end expect(result).to be == [:c, :d, :a, :c, :a, :a, :b, :d] end end timers-4.1.1/spec/group_spec.rb000066400000000000000000000150641256563657400164760ustar00rootroot00000000000000 require 'spec_helper' RSpec.describe Timers::Group do describe "#wait" do it "calls the wait block with nil" do called = false subject.wait do |interval| expect(interval).to be == nil called = true end expect(called).to be true end it "calls the wait block with an interval" do called = false fired = false subject.after(0.1) { fired = true } subject.wait do |interval| expect(interval).to be_within(TIMER_QUANTUM).of(0.1) called = true sleep 0.2 end expect(called).to be true expect(fired).to be true end end it "sleeps until the next timer" do interval = TIMER_QUANTUM * 2 started_at = Time.now fired = false subject.after(interval) { fired = true } subject.wait expect(fired).to be true expect(Time.now - started_at).to be_within(TIMER_QUANTUM).of interval end it "fires instantly when next timer is in the past" do fired = false subject.after(TIMER_QUANTUM) { fired = true } sleep(TIMER_QUANTUM * 2) subject.wait expect(fired).to be true end it "calculates the interval until the next timer should fire" do interval = 0.1 subject.after(interval) expect(subject.wait_interval).to be_within(TIMER_QUANTUM).of interval sleep(interval) expect(subject.wait_interval).to be <= 0 end it "fires timers in the correct order" do result = [] subject.after(TIMER_QUANTUM * 2) { result << :two } subject.after(TIMER_QUANTUM * 3) { result << :three } subject.after(TIMER_QUANTUM * 1) { result << :one } sleep TIMER_QUANTUM * 4 subject.fire expect(result).to eq [:one, :two, :three] end it "raises TypeError if given an invalid time" do expect do subject.after(nil) { nil } end.to raise_exception(TypeError) end describe "recurring timers" do it "continues to fire the timers at each interval" do result = [] subject.every(TIMER_QUANTUM * 2) { result << :foo } sleep TIMER_QUANTUM * 3 subject.fire expect(result).to eq [:foo] sleep TIMER_QUANTUM * 5 subject.fire expect(result).to eq [:foo, :foo] end end it "calculates the proper interval to wait until firing" do interval_ms = 25 subject.after(interval_ms / 1000.0) expect(subject.wait_interval).to be_within(TIMER_QUANTUM).of(interval_ms / 1000.0) end describe "pause and continue timers" do before(:each) do @interval = TIMER_QUANTUM * 2 @fired = false @timer = subject.after(@interval) { @fired = true } @fired2 = false @timer2 = subject.after(@interval) { @fired2 = true } end it "does not fire when paused" do @timer.pause subject.wait expect(@fired).to be false end it "fires when continued after pause" do @timer.pause subject.wait @timer.resume sleep @timer.interval subject.wait expect(@fired).to be true end it "can pause all timers at once" do subject.pause subject.wait expect(@fired).to be false expect(@fired2).to be false end it "can continue all timers at once" do subject.pause subject.wait subject.resume # We need to wait until we are sure both timers will fire, otherwise highly accurate clocks (e.g. JVM) may only fire the first timer, but not the second, because they are actually schedueled at different times. sleep TIMER_QUANTUM * 2 subject.wait expect(@fired).to be true expect(@fired2).to be true end it "can fire the timer directly" do fired = false timer = subject.after( TIMER_QUANTUM * 1 ) { fired = true } timer.pause subject.wait expect(fired).not_to be true timer.resume expect(fired).not_to be true timer.fire expect(fired).to be true end end describe "delay timer" do it "adds appropriate amount of time to timer" do timer = subject.after(10) timer.delay(5) expect(timer.offset - subject.current_offset).to be_within(TIMER_QUANTUM).of(15) end end describe "delay timer collection" do it "delay on set adds appropriate amount of time to all timers" do timer = subject.after(10) timer2 = subject.after(20) subject.delay(5) expect(timer.offset - subject.current_offset).to be_within(TIMER_QUANTUM).of(15) expect(timer2.offset - subject.current_offset).to be_within(TIMER_QUANTUM).of(25) end end describe "on delaying a timer" do it "fires timers in the correct order" do result = [] subject.after(TIMER_QUANTUM * 2) { result << :two } subject.after(TIMER_QUANTUM * 3) { result << :three } first = subject.after(TIMER_QUANTUM * 1) { result << :one } first.delay(TIMER_QUANTUM * 3) sleep TIMER_QUANTUM * 5 subject.fire expect(result).to eq [:two, :three, :one] end end describe "Timer inspection" do it "before firing" do fired = false timer = subject.after(TIMER_QUANTUM * 5) { fired = true } timer.pause expect(fired).not_to be true expect(timer.inspect).to match(/\A#\Z/) end it "after firing" do fired = false timer = subject.after(TIMER_QUANTUM) { fired = true } subject.wait expect(fired).to be true expect(timer.inspect).to match(/\A#\Z/) end it "recurring firing" do result = [] timer = subject.every(TIMER_QUANTUM) { result << :foo } subject.wait expect(result).not_to be_empty expect(timer.inspect).to match(/\A#\Z/) end end describe "fires_in" do let(:interval) { TIMER_QUANTUM * 2 } it "calculates the interval until the next fire if it's recurring" do timer = subject.every(interval) { true } expect(timer.fires_in).to be_within(TIMER_QUANTUM).of(interval) end context "when timer is not recurring" do let!(:timer) { subject.after(interval) { true } } it "calculates the interval until the next fire if it hasn't already fired" do expect(timer.fires_in).to be_within(TIMER_QUANTUM).of(interval) end it "calculates the interval since last fire if already fired" do subject.wait sleep(interval) expect(timer.fires_in).to be_within(TIMER_QUANTUM).of(0 - interval) end end end end timers-4.1.1/spec/performance_spec.rb000066400000000000000000000064771256563657400176530ustar00rootroot00000000000000require 'spec_helper' # Event based timers: # Serviced 31812 events in 2.39075272 seconds, 13306.320832794887 e/s. # Thread ID: 7336700 # Fiber ID: 30106340 # Total: 2.384043 # Sort by: self_time # %self total self wait child calls name # 13.48 0.510 0.321 0.000 0.189 369133 Timers::Events::Handle#<=> # 8.12 0.194 0.194 0.000 0.000 427278 Timers::Events::Handle#to_f # 4.55 0.109 0.109 0.000 0.000 427278 Float#<=> # 4.40 1.857 0.105 0.000 1.752 466376 *Timers::Events#bsearch # 4.30 0.103 0.103 0.000 0.000 402945 Float#to_f # 2.65 0.063 0.063 0.000 0.000 33812 Array#insert # 2.64 1.850 0.063 0.000 1.787 33812 Timers::Events#schedule # 2.40 1.930 0.057 0.000 1.873 33812 Timers::Timer#reset # 1.89 1.894 0.045 0.000 1.849 31812 Timers::Timer#fire # 1.69 1.966 0.040 0.000 1.926 31812 Timers::Events::Handle#fire # 1.35 0.040 0.032 0.000 0.008 33812 Timers::Events::Handle#initialize # 1.29 0.044 0.031 0.000 0.013 44451 Timers::Group#current_offset # SortedSet based timers: # Serviced 32516 events in 66.753277275 seconds, 487.1072288781219 e/s. # Thread ID: 15995640 # Fiber ID: 38731780 # Total: 66.716394 # Sort by: self_time # %self total self wait child calls name # 54.73 49.718 36.513 0.000 13.205 57084873 Timers::Timer#<=> # 23.74 65.559 15.841 0.000 49.718 32534 Array#sort! # 19.79 13.205 13.205 0.000 0.000 57084873 Float#<=> # Max out events performance (on my computer): # Serviced 1142649 events in 11.194903921 seconds, 102068.70405115146 e/s. RSpec.describe Timers::Group do it "runs efficiently" do result = [] range = (1..500) duration = 2.0 total = 0 range.each do |index| offset = index.to_f / range.max total += (duration / offset).floor subject.every(index.to_f / range.max, :strict) { result << index } end subject.wait while result.size < total rate = result.size.to_f / subject.current_offset puts "Serviced #{result.size} events in #{subject.current_offset} seconds, #{rate} e/s." expect(subject.current_offset).to be_within(TIMER_QUANTUM).of(duration) end =begin it "runs efficiently at high volume" do results = [] range = (1..300) groups = (1..20) duration = 101 timers = [] @mutex = Mutex.new start = Time.now groups.each do |gi| timers << Thread.new { result = [] timer = Timers::Group.new total = 0 range.each do |ri| offset = ri.to_f / range.max total += (duration / offset).floor timer.every(ri.to_f / range.max, :strict) { result << ri } end timer.wait while result.size < total @mutex.synchronize { results += result } } end timers.each { |t| t.join } finish = Time.now rate = results.size.to_f / ( runtime = finish - start ) puts "Serviced #{results.size} events in #{runtime} seconds, #{rate} e/s; across #{groups.max} timers." expect(runtime).to be_within(TIMER_QUANTUM).of(duration) end =end end timers-4.1.1/spec/spec_helper.rb000066400000000000000000000012401256563657400166100ustar00rootroot00000000000000require 'coveralls' Coveralls.wear! require 'bundler/setup' require 'timers' # Level of accuracy enforced by tests (50ms) TIMER_QUANTUM = 0.05 RSpec.configure do |config| # Setting this config option `false` removes rspec-core's monkey patching of the # top level methods like `describe`, `shared_examples_for` and `shared_context` # on `main` and `Module`. The methods are always available through the `RSpec` # module like `RSpec.describe` regardless of this setting. # For backwards compatibility this defaults to `true`. # # https://relishapp.com/rspec/rspec-core/v/3-0/docs/configuration/global-namespace-dsl config.expose_dsl_globally = false end timers-4.1.1/spec/strict_spec.rb000066400000000000000000000014561256563657400166520ustar00rootroot00000000000000 require 'spec_helper' RSpec.describe Timers::Group do it "should not diverge too much" do fired = :not_fired_yet count = 0 quantum = 0.01 start_offset = subject.current_offset Timers::Timer.new(subject, quantum, :strict, start_offset) do |offset| fired = offset count += 1 end iterations = 1000 subject.wait while count < iterations # In my testing on the JVM, without the :strict recurring, I noticed 60ms of error here. expect(fired - start_offset).to be_within(quantum).of(iterations * quantum) end it "should only fire once" do fired = :not_fired_yet count = 0 start_offset = subject.current_offset Timers::Timer.new(subject, 0, :strict, start_offset) do |offset| fired = offset count += 1 end subject.wait expect(count).to be == 1 end end timers-4.1.1/spec/timeout_spec.rb000066400000000000000000000011311256563657400170160ustar00rootroot00000000000000 require 'spec_helper' require 'timers/wait' RSpec.describe Timers::Wait do it "repeats until timeout expired" do timeout = Timers::Wait.new(5) count = 0 timeout.while_time_remaining do |remaining| expect(remaining).to be_within(TIMER_QUANTUM).of (timeout.duration - count) count += 1 sleep 1 end expect(count).to eq(5) end it "yields results as soon as possible" do timeout = Timers::Wait.new(5) result = timeout.while_time_remaining do |remaining| break :done end expect(result).to eq(:done) end end timers-4.1.1/timers.gemspec000066400000000000000000000016251256563657400157170ustar00rootroot00000000000000# -*- encoding: utf-8 -*- require File.expand_path('../lib/timers/version', __FILE__) Gem::Specification.new do |gem| gem.authors = ["Tony Arcieri"] gem.email = ["tony.arcieri@gmail.com"] gem.description = "Pure Ruby one-shot and periodic timers" gem.summary = "Schedule procs to run after a certain time, or at periodic intervals, using any API that accepts a timeout" gem.homepage = "https://github.com/celluloid/timers" gem.files = `git ls-files`.split($\) gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) } gem.test_files = gem.files.grep(%r{^(test|spec|features)/}) gem.name = "timers" gem.require_paths = ["lib"] gem.version = Timers::VERSION gem.licenses = ['MIT'] gem.add_runtime_dependency 'hitimes' gem.add_development_dependency 'rake' gem.add_development_dependency 'rspec', '~> 3.0.0' end