stud 0.0.21 → 0.0.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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: