stud 0.0.21 → 0.0.22

Sign up to get free protection for your applications and to get access to all the features.
Files changed (4) hide show
  1. checksums.yaml +4 -4
  2. data/lib/stud/interval.rb +80 -8
  3. data/lib/stud/task.rb +27 -3
  4. metadata +4 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 085ea35fd8932fd05459cfac97723d37f1d876c8
4
- data.tar.gz: 63f9bd5fa6a0f601cc226973b9a88c615ecdeacf
3
+ metadata.gz: a6052e9a657a08c734e6ca7326edc7e86e402305
4
+ data.tar.gz: 9c681e29d45145e0df76800b6821d198c3003c57
5
5
  SHA512:
6
- metadata.gz: d64c0a9a164945bb663b59ba800bdbc94da61ffc8cd5703b4440b74d26bc298b30706735fafc469a8513307015c5dc0623a8dca71c9076cc3ae64ae2b2c4666e
7
- data.tar.gz: ef490bdc0feb1520ad1fad2af1d725c90bc7abb342001b885b4d8cee4953c00880a3edd4ef360d94ecfe82bead3220735690c7deddfd49d9b28c75268c00e0ba
6
+ metadata.gz: 3ac208ff1766b0f2827e8239e4739d344bbedff9dd6f9e6d84d4ffba3fd3314e7db6dffda9bea63ee3f61499d1dc1865058837762adb9ad29e9bcdbfb42878f9
7
+ data.tar.gz: 07308bf3b065d429e16802786e4f571321cb31735b784d207619410b63b8d58c0d41ff6d5ee83f327fe602605b74eeb27bcd4aead1cb9d3ab7b9e286f5463fdf
@@ -1,5 +1,8 @@
1
- require "stud/task"
1
+ require "thread"
2
+
2
3
  module Stud
4
+ STUD_STOP_REQUESTED = :stud_stop_requested
5
+
3
6
  # This implementation tries to keep clock more accurately.
4
7
  # Prior implementations still permitted skew, where as this one
5
8
  # will attempt to correct for skew.
@@ -9,30 +12,99 @@ module Stud
9
12
  def self.interval(time, opts = {}, &block)
10
13
  start = Time.now
11
14
  while true
12
- break if Task.interrupted?
13
15
  if opts[:sleep_then_run]
14
16
  start = sleep_for_interval(time, start)
17
+ break if stop?
15
18
  block.call
16
19
  else
17
20
  block.call
18
21
  start = sleep_for_interval(time, start)
22
+ break if stop?
19
23
  end
20
24
  end # loop forever
21
25
  end # def interval
22
26
 
27
+ def interval(time, opts = {}, &block)
28
+ Stud.interval(time, opts, &block)
29
+ end # def interval
30
+
31
+ # stop! instructs interval to stop and exit its execution loop before going to
32
+ # sleep between block executions.
33
+ # NOW the tricky part is: this is typically an operation that will be called
34
+ # from another thread than the thread running the interval loop in which case
35
+ # the target parameter must be set to the Thread object which is running the
36
+ # interval loop.
37
+ # Note that the stop logic is compatible with Stud::Task so if interval is run
38
+ # inside a Stud::Task, calling Stud::Task#stop! will stop the interval the same
39
+ # way as calling stop! on the interval itself.
40
+ # @param target [Thread] the target thread to stop, defaut to Thread.current
41
+ def self.stop!(target = Thread.current)
42
+ # setting/getting threalocal var is thread safe in JRuby
43
+ target[STUD_STOP_REQUESTED] = true
44
+ target.wakeup
45
+ nil
46
+ end
47
+
48
+ # stop? returns true if stop! has been called
49
+ # @param target [Thread] the target thread to check for stop, defaut to Thread.current
50
+ # @return [Boolean] true if the stop! has been called
51
+ def self.stop?(target = Thread.current)
52
+ # setting/getting threalocal var is thread safe in JRuby
53
+ target[STUD_STOP_REQUESTED]
54
+ end
55
+
56
+ class << Stud
57
+ # also support Stud.interrupted? for backward compatibility.
58
+ alias_method :interrupted?, :stop?
59
+ end
60
+
61
+ # stoppable_sleep will try to sleep for the given duration seconds (which may be any number,
62
+ # including a Float with fractional seconds). an optional stop_condition_block can be supplied
63
+ # to verify for sleep interruption if the block returns a truthy value. if not block is supplied
64
+ # it will check for the Stud.stop? condition. this check will be performed at 1s interval
65
+ # by default or you can supply a different stop_condition_interval.
66
+ #
67
+ # note that to achieve this, stoppable_sleep will actually perform a series of incremental sleeps
68
+ # but will try accurately spend the requested duration period in the overall stoppable_sleep method call.
69
+ # in other words this means that the duration supplied will be accurate for the time spent in
70
+ # the stoppable_sleep method not the actual total time spent in the underlying multiple sleep calls.
71
+ #
72
+ # @param duration [Numeric] sleep time in (fractional) seconds
73
+ # @param stop_condition_interval [Numeric] optional interval in (fractional) seconds to perform the sleep interruption verification, default is 1s
74
+ # @param stop_condition_block [Proc] optional sleep interruption code block that must evaluate to a truthy value, default is to use Stud.stop?
75
+ # @return [Numeric] the actual duration in (fractional) seconds spent in stoppable_sleep
76
+ def self.stoppable_sleep(duration, stop_condition_interval = 1.0, &stop_condition_block)
77
+ sleep_start = Time.now
78
+
79
+ # default to using Stud.stop? as the condition block
80
+ stop_condition_block ||= lambda { stop? }
81
+
82
+ while (remaining_duration = (duration - (Time.now - sleep_start))) >= stop_condition_interval
83
+ # sleep X if there is more than X remaining to sleep in relation to the loop start time
84
+ sleep(stop_condition_interval)
85
+
86
+ return(Time.now - sleep_start) if stop_condition_block.call
87
+ end
88
+
89
+ # here we know we have less than 1s reminding to sleep,
90
+ sleep(remaining_duration) if remaining_duration > 0.0
91
+
92
+ Time.now - sleep_start
93
+ end
94
+
95
+ private
96
+
23
97
  def self.sleep_for_interval(time, start)
24
98
  duration = Time.now - start
25
- # Sleep only if the duration was less than the time interval
99
+
100
+ # sleep only if the duration was less than the time interval
26
101
  if duration < time
27
- sleep(time - duration)
102
+ stoppable_sleep(time - duration)
28
103
  start += time
29
104
  else
30
- # Duration exceeded interval time, reset the clock and do not sleep.
105
+ # duration exceeded interval time, reset the clock and do not sleep.
31
106
  start = Time.now
32
107
  end
33
108
  end
34
109
 
35
- def interval(time, opts = {}, &block)
36
- return Stud.interval(time, opts, &block)
37
- end # def interval
38
110
  end # module Stud
@@ -1,7 +1,19 @@
1
1
  require "thread"
2
+ require "stud/interval"
2
3
 
3
4
  module Stud
5
+
6
+ # A Task spawns a thread to execute the given block. execution completion and result retrieval is
7
+ # done using the Task#wait method. A Task is run once and the thread exists upon block completion.
8
+ # A task and its underlying thread are not reusable.
9
+ #
10
+ # Task does not provide a mean to force-interrupt a running task, it only provides the #stop!
11
+ # method to signal the task for a stop request. The task or code block can use the #stop? method
12
+ # to check for a stop request. Note that the #stop! and #stop? methods are thread safe.
4
13
  class Task
14
+ # provide access to the underlying thread if ever needed.
15
+ attr_reader :thread
16
+
5
17
  def initialize(*args, &block)
6
18
  # A queue to receive the result of the block
7
19
  # TODO(sissel): Don't use a queue, just store it in an instance variable.
@@ -17,6 +29,10 @@ module Stud
17
29
  end # thread
18
30
  end # def initialize
19
31
 
32
+ # wait waits for the task thread to complete and return the block return value
33
+ # if the block raises an exception, this exception is propagated in this
34
+ # wait method.
35
+ # @return [Object, Exception] block return value
20
36
  def wait
21
37
  @thread.join
22
38
  reason, result = @queue.pop
@@ -29,12 +45,20 @@ module Stud
29
45
  end
30
46
  end # def wait
31
47
 
48
+ # stop! requests the task to stop. the Thread#wakeup method is also
49
+ # called so that a sleeping task is waked up and has a chance to verify
50
+ # the stop request using the #stop? method. also see Stud.stop!
32
51
  def stop!
33
- Thread.current[:stud_task_interrupted] = true
52
+ Stud.stop!(@thread)
34
53
  end
35
54
 
36
- def self.interrupted?
37
- Thread.current[:stud_task_interrupted]
55
+ # stop? returns true if this task stop! has been called
56
+ # See Stud.stop?
57
+ # @return [Boolean] true if the stop! has been called
58
+ def stop?
59
+ Stud.stop?(@thread)
38
60
  end
61
+ alias_method :interrupted?, :stop?
62
+
39
63
  end # class Task
40
64
  end # module Stud
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: stud
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.21
4
+ version: 0.0.22
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jordan Sissel
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-08-04 00:00:00.000000000 Z
11
+ date: 2015-09-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rspec
@@ -77,8 +77,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
77
77
  version: '0'
78
78
  requirements: []
79
79
  rubyforge_project:
80
- rubygems_version: 2.4.8
80
+ rubygems_version: 2.4.6
81
81
  signing_key:
82
82
  specification_version: 4
83
83
  summary: stud - common code techniques
84
84
  test_files: []
85
+ has_rdoc: