psched 0.1.0

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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 58946f4a0c8a49837d18022f99a4f98c99cf3117
4
+ data.tar.gz: c1d675d5dba069199d49420407694171855261e1
5
+ SHA512:
6
+ metadata.gz: 47cbf71e0f1f5c47c58980105761bfd0d65dd68a3d9b1fe39f5b54efbbe4ec7e72b10d897e9151bcee797c24319653b916b1a8de07591ac0d8473cb9f2e7c103
7
+ data.tar.gz: a0e0a9d007693cda71f86ae85b8c6280f4fc873fa2f4288520e9382d25eaae0682b532dda4c77a1dbf58824d2b009bceb1d76c3f680172bb56e73fada7b62d69
data/.document ADDED
@@ -0,0 +1,3 @@
1
+ -
2
+ ChangeLog.md
3
+ LICENSE.txt
data/.gitignore ADDED
@@ -0,0 +1,6 @@
1
+ /.bundle
2
+ /.yardoc/
3
+ /Gemfile.lock
4
+ /doc/
5
+ /pkg/
6
+ /vendor/cache/*.gem
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --colour --format documentation
data/.yardopts ADDED
@@ -0,0 +1 @@
1
+ --markup markdown --title "psched Documentation" --protected
data/ChangeLog.md ADDED
@@ -0,0 +1,4 @@
1
+ ### 0.1.0 / 2017-12-13
2
+
3
+ * Initial release:
4
+
data/Gemfile ADDED
@@ -0,0 +1,7 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
4
+
5
+ group :development do
6
+ gem 'kramdown'
7
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,23 @@
1
+ Copyright (c) 2017, Paolo Bosetti
2
+ All rights reserved.
3
+
4
+ Redistribution and use in source and binary forms, with or without modification,
5
+ are permitted provided that the following conditions are met:
6
+
7
+ 1. Redistributions of source code must retain the above copyright notice, this
8
+ list of conditions and the following disclaimer.
9
+
10
+ 2. Redistributions in binary form must reproduce the above copyright notice,
11
+ this list of conditions and the following disclaimer in the documentation
12
+ and/or other materials provided with the distribution.
13
+
14
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
15
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
18
+ ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
19
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
20
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
21
+ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
23
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
data/README.md ADDED
@@ -0,0 +1,44 @@
1
+ # psched
2
+
3
+ - [Homepage](https://rubygems.org/gems/psched)
4
+ - [Documentation](http://rubydoc.info/gems/psched/frames)
5
+ - [Email](mailto:paolo.bosetti at unitn.it)
6
+
7
+ ## Description
8
+
9
+ TODO: Description
10
+
11
+ ## Features
12
+
13
+ TODO: Features
14
+
15
+ ## Examples
16
+
17
+ require 'psched'
18
+ op = PSched::Operation.new(0.5)
19
+ op.start(10) do |i|
20
+ puts "Ping #{i}"
21
+ end
22
+
23
+ Signal.trap("SIGINT") do
24
+ print "Stopping recurring process..."
25
+ op.stop
26
+ puts "done!"
27
+ end
28
+
29
+ sleep(0.2) while op.active? # waits for scheduling to complete
30
+
31
+ ## Requirements
32
+ FFI gem and an OS supporting semaphores.
33
+
34
+ ## Install
35
+
36
+ ```
37
+ $ gem install psched
38
+ ```
39
+
40
+ ## Copyright
41
+
42
+ Copyright (c) 2017 Paolo Bosetti
43
+
44
+ See {file:LICENSE.txt} for details.
data/Rakefile ADDED
@@ -0,0 +1,25 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rubygems'
4
+
5
+ begin
6
+ require 'bundler/setup'
7
+ rescue LoadError => e
8
+ abort e.message
9
+ end
10
+
11
+ require 'rake'
12
+
13
+
14
+ require 'rubygems/tasks'
15
+ Gem::Tasks.new
16
+
17
+ require 'rspec/core/rake_task'
18
+ RSpec::Core::RakeTask.new
19
+
20
+ task :test => :spec
21
+ task :default => :spec
22
+
23
+ require 'yard'
24
+ YARD::Rake::YardocTask.new
25
+ task :doc => :yard
@@ -0,0 +1,4 @@
1
+ module Psched
2
+ # psched version
3
+ VERSION = "0.1.0"
4
+ end
data/lib/psched.rb ADDED
@@ -0,0 +1,192 @@
1
+ #!/usr/bin/env ruby
2
+ # psched.rb
3
+
4
+ # Created by Paolo Bosetti on 2011-04-21.
5
+ # Copyright (c) 2011 University of Trento. All rights reserved.
6
+
7
+ require 'ffi'
8
+ require 'psched/version'
9
+
10
+
11
+ # A module and a class ({PSched::Operation}) for implementing a recurring
12
+ # call as deterministic as possible.
13
+ #
14
+ # See {PSched::Operation} for usage details. Module functions are FFI
15
+ # wrappers and are not intended to be used directly.
16
+ # @author Paolo Bosetti
17
+ # @todo Are there better ways?
18
+ module PSched
19
+ extend FFI::Library
20
+ ffi_lib FFI::Library::LIBC
21
+ # @!method ualarm(useconds, interval)
22
+ # The `ualarm()` function waits a count of useconds before asserting the
23
+ # terminating signal `SIGALRM`. System activity or time used in processing
24
+ # the call may cause a slight delay.
25
+ # If the interval argument is non-zero, the `SIGALRM` signal will be sent to
26
+ # the process every interval microseconds after the timer expires
27
+ # (e.g.,after useconds number of microseconds have passed).
28
+ # @param [Fixnum] useconds initial delay in microseconds
29
+ # @param [Fixnum] interval recurring interval in microseconds
30
+ # @return [Fixnum] the amount of time left on the clock
31
+ attach_function :ualarm, [:uint, :uint], :uint
32
+
33
+ # @!method alarm(seconds)
34
+ # Set a timer to deliver the signal `SIGALRM` to the calling process after
35
+ # the specified number of seconds
36
+ # @param [Fixnum] seconds delay in microseconds
37
+ # @return [Fixnum] the amount of time left on the clock from a previous
38
+ # call to alarm()
39
+ attach_function :alarm, [:uint], :uint
40
+
41
+ enum :which, [
42
+ :prio_process, 0,
43
+ :prio_pgrp,
44
+ :prio_user,
45
+ :prio_darwin_thread,
46
+ :prio_darwin_process
47
+ ]
48
+
49
+ # @!method setpriority(which, who, prio)
50
+ # Set the scheduling priority of the current process
51
+ # @param [Fixnum] which The type of priority, as for the enum `which`
52
+ # @param [Fixnum] who The process, group, user or thread
53
+ # @param [Fixnum] prio The priority level, from -20 to 20
54
+ # @return [0|-1] 0 if no error, -1 if error
55
+ attach_function :setpriority, [:which, :uint, :int], :int
56
+
57
+ # Implements a recurring operation.
58
+ # @example General usage. Note the `sleep` call.
59
+ # op = PSched::Operation.new(0.5)
60
+ # op.start(10) do |i|
61
+ # puts "Ping #{i}"
62
+ # end
63
+ #
64
+ # Signal.trap("SIGINT") do
65
+ # print "Stopping recurring process..."
66
+ # op.stop
67
+ # puts "done!"
68
+ # end
69
+ #
70
+ # sleep(0.2) while op.active? # THIS IS IMPORTANT!!!
71
+ #
72
+ # @author Paolo Bosetti
73
+ class Operation
74
+ # Changes scheduling priority of the current process
75
+ # @param [Fixnum] nice sets the nice level (-20..+20)
76
+ # @todo better return message and error management
77
+ def self.prioritize(nice = -20)
78
+ result = PSched.setpriority(:prio_process,0,nice)
79
+ puts "Setting max priority: #{result == 0 ? 'success' : 'failure (missing sudo?)'}"
80
+ end
81
+
82
+ # @!attribute [rw] strict_timing
83
+ # If true, raise a {RealTimeError} when `TET >= step`
84
+ # (default to `false`)
85
+ attr_accessor :strict_timing
86
+
87
+ # @!attribute [r] tet
88
+ # The Task Execution Time
89
+ # @!attribute [r] step
90
+ # The time step in seconds
91
+ attr_reader :step, :tet
92
+ # Initializer
93
+ # @param [Numeric] step the timestep in seconds
94
+ def initialize(step)
95
+ @step = step
96
+ @active = false
97
+ @lock = false
98
+ @strict_timing = false
99
+ @tet = 0
100
+ end
101
+
102
+ # Sets the time step (in seconds) and reschedule pending alarms.
103
+ # @param [Numeric] secs Timestep in seconds
104
+ def step=(secs)
105
+ @step = secs
106
+ self.schedule
107
+ end
108
+
109
+ # Updates scheduling of pending alarms.
110
+ # @note Usually, there's no need to call this, since {#step=} automatically
111
+ # calls it after having set the +@step+ attribute.
112
+ def schedule
113
+ usecs = @step * 1E6
114
+ PSched::ualarm(usecs, usecs)
115
+ end
116
+
117
+ # Tells id the recurring operation is active or not.
118
+ # @return [Boolean] the status of the recurring alarm operation
119
+ def active?; @active; end
120
+
121
+ # Starts a recurring operation, described by the passed block.
122
+ # If the block returns the symbol :stop, the recurrin operation gets
123
+ # disabled.
124
+ # @param [Fixnum,nil] n_iter the maximum number of iterations.
125
+ # If nil it loops indefinitedly
126
+ # @yieldparam [Fixnum] i the number of elapsed iterations
127
+ # @yieldparam [Numeric] tet the Task Execution Time of previous step
128
+ # @raise [ArgumentError] unless a block is given
129
+ # @raise [RealTimeError] if TET exceeds @step
130
+ def start(n_iter=nil)
131
+ @active = true
132
+ i = 0
133
+ raise ArgumentError, "Need a block!" unless block_given?
134
+ Signal.trap(:ALRM) do
135
+ # If there is still a pending step, raises an error containing
136
+ # information about the CURRENT step
137
+ if @lock then
138
+ if @strict_timing
139
+ @lock = false
140
+ raise RealTimeError.new({:tet => @tet, :step => @step, :i => i, :time => Time.now})
141
+ end
142
+ else
143
+ start = Time.now
144
+ @lock = true
145
+ result = yield(i, @tet)
146
+ i += 1
147
+ self.stop if (n_iter and i >= n_iter)
148
+ self.stop if (result.kind_of? Symbol and result == :stop)
149
+ @tet = Time.now - start
150
+ @lock = false
151
+ end
152
+ end
153
+ self.schedule
154
+ end
155
+
156
+ # Stops the recurring process by resetting alarm and disabling management
157
+ # of `SIGALRM` signal.
158
+ def stop
159
+ PSched::ualarm(0, 0)
160
+ Signal.trap(:ALRM, "DEFAULT")
161
+ @active = false
162
+ @lock = false
163
+ end
164
+ end
165
+ end
166
+
167
+ class RealTimeError < RuntimeError
168
+ attr_reader :status
169
+ def initialize(status)
170
+ @status = status
171
+ end
172
+ def ratio
173
+ (@status[:tet] / @status[:step]) * 100
174
+ end
175
+ end
176
+
177
+ if $0 == __FILE__ then
178
+ PSched::Operation.prioritize
179
+
180
+ op = PSched::Operation.new(0.01)
181
+ op.start(10) do |i|
182
+ puts "Ping #{i}"
183
+ end
184
+
185
+ Signal.trap("SIGINT") do
186
+ print "Stopping recurring process..."
187
+ op.stop
188
+ puts "done!"
189
+ end
190
+
191
+ sleep while op.active?
192
+ end
data/psched.gemspec ADDED
@@ -0,0 +1,38 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ lib = File.expand_path('../lib', __FILE__)
4
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
+ require 'psched/version'
6
+
7
+ Gem::Specification.new do |gem|
8
+ gem.name = "psched"
9
+ gem.version = Psched::VERSION
10
+ gem.summary = %q{Precise scheduling of recurring tasks}
11
+ gem.description = %q{Precise scheduling of recurring tasks using semaphores (not supported on Windows!)}
12
+ gem.license = "0BSD"
13
+ gem.authors = ["Paolo Bosetti"]
14
+ gem.email = "paolo.bosetti@unitn.it"
15
+ gem.homepage = "https://rubygems.org/gems/psched"
16
+
17
+ gem.files = `git ls-files`.split($/)
18
+
19
+ `git submodule --quiet foreach --recursive pwd`.split($/).each do |submodule|
20
+ submodule.sub!("#{Dir.pwd}/",'')
21
+
22
+ Dir.chdir(submodule) do
23
+ `git ls-files`.split($/).map do |subpath|
24
+ gem.files << File.join(submodule,subpath)
25
+ end
26
+ end
27
+ end
28
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
29
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
30
+ gem.require_paths = ['lib']
31
+ gem.add_dependency 'ffi', '~>1.9'
32
+
33
+ gem.add_development_dependency 'bundler', '~> 1.10'
34
+ gem.add_development_dependency 'rake', '~> 10.0'
35
+ gem.add_development_dependency 'rspec', '~> 3.0'
36
+ gem.add_development_dependency 'rubygems-tasks', '~> 0.2'
37
+ gem.add_development_dependency 'yard', '~> 0.8'
38
+ end
@@ -0,0 +1,8 @@
1
+ require 'spec_helper'
2
+ require 'psched'
3
+
4
+ describe Psched do
5
+ it "should have a VERSION constant" do
6
+ expect(subject.const_get('VERSION')).to_not be_empty
7
+ end
8
+ end
@@ -0,0 +1,4 @@
1
+ require 'rspec'
2
+ require 'psched/version'
3
+
4
+ include Psched
metadata ADDED
@@ -0,0 +1,144 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: psched
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Paolo Bosetti
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-12-13 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: ffi
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.9'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.9'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.10'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.10'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '10.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '10.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '3.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '3.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rubygems-tasks
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '0.2'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '0.2'
83
+ - !ruby/object:Gem::Dependency
84
+ name: yard
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '0.8'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '0.8'
97
+ description: Precise scheduling of recurring tasks using semaphores (not supported
98
+ on Windows!)
99
+ email: paolo.bosetti@unitn.it
100
+ executables: []
101
+ extensions: []
102
+ extra_rdoc_files: []
103
+ files:
104
+ - ".document"
105
+ - ".gitignore"
106
+ - ".rspec"
107
+ - ".yardopts"
108
+ - ChangeLog.md
109
+ - Gemfile
110
+ - LICENSE.txt
111
+ - README.md
112
+ - Rakefile
113
+ - lib/psched.rb
114
+ - lib/psched/version.rb
115
+ - psched.gemspec
116
+ - spec/psched_spec.rb
117
+ - spec/spec_helper.rb
118
+ homepage: https://rubygems.org/gems/psched
119
+ licenses:
120
+ - 0BSD
121
+ metadata: {}
122
+ post_install_message:
123
+ rdoc_options: []
124
+ require_paths:
125
+ - lib
126
+ required_ruby_version: !ruby/object:Gem::Requirement
127
+ requirements:
128
+ - - ">="
129
+ - !ruby/object:Gem::Version
130
+ version: '0'
131
+ required_rubygems_version: !ruby/object:Gem::Requirement
132
+ requirements:
133
+ - - ">="
134
+ - !ruby/object:Gem::Version
135
+ version: '0'
136
+ requirements: []
137
+ rubyforge_project:
138
+ rubygems_version: 2.6.13
139
+ signing_key:
140
+ specification_version: 4
141
+ summary: Precise scheduling of recurring tasks
142
+ test_files:
143
+ - spec/psched_spec.rb
144
+ - spec/spec_helper.rb