eventbox 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.
@@ -0,0 +1,172 @@
1
+ # frozen-string-literal: true
2
+
3
+ class Eventbox
4
+ # Simple timer services for Eventboxes
5
+ #
6
+ # This module can be included into Eventbox classes to add simple timer functions.
7
+ #
8
+ # class MyBox < Eventbox
9
+ # include Eventbox::Timer
10
+ #
11
+ # async_call def init
12
+ # super # make sure Timer#init is called
13
+ # timer_after(1) do
14
+ # puts "one second elapsed"
15
+ # end
16
+ # end
17
+ # end
18
+ #
19
+ # The main functions are timer_after and timer_every.
20
+ # They schedule asynchronous calls to the given block:
21
+ # timer_after(3) do
22
+ # # executed once after 3 seconds
23
+ # end
24
+ #
25
+ # timer_every(3) do
26
+ # # executed repeatedly every 3 seconds
27
+ # end
28
+ #
29
+ # Both functions return an Alarm object which can be used to cancel the alarm through timer_cancel.
30
+ #
31
+ # timer_after, timer_every and timer_cancel can be used within the event scope, in actions and from external scope.
32
+ #
33
+ # {Timer} always uses one action thread per Eventbox object, regardless of the number of scheduled timers.
34
+ module Timer
35
+ class Reload < RuntimeError
36
+ end
37
+ class InternalError < RuntimeError
38
+ end
39
+
40
+ class Alarm
41
+ # @private
42
+ def initialize(ts, &block)
43
+ @timestamp = ts
44
+ @block = block
45
+ end
46
+
47
+ # @private
48
+ attr_reader :timestamp
49
+ end
50
+
51
+ class OneTimeAlarm < Alarm
52
+ # @private
53
+ def fire_then_repeat?(now=Time.now)
54
+ @block.call
55
+ false
56
+ end
57
+ end
58
+
59
+ class RepeatedAlarm < Alarm
60
+ # @private
61
+ def initialize(ts, every_seconds, &block)
62
+ super(ts, &block)
63
+ @every_seconds = every_seconds
64
+ end
65
+
66
+ # @private
67
+ def fire_then_repeat?(now=Time.now)
68
+ @block.call
69
+ @timestamp = now + @every_seconds
70
+ true
71
+ end
72
+ end
73
+
74
+ extend Boxable
75
+
76
+ # @private
77
+ private async_call def init(*args)
78
+ super
79
+ @timer_alarms = []
80
+ @timer_action = timer_start_worker
81
+ end
82
+
83
+ # @private
84
+ private action def timer_start_worker
85
+ loop do
86
+ begin
87
+ interval = timer_next_timestamp&.-(Time.now)
88
+ Thread.handle_interrupt(Reload => :on_blocking) do
89
+ if interval.nil?
90
+ Kernel.sleep
91
+ elsif interval > 0.0
92
+ Kernel.sleep(interval)
93
+ end
94
+ end
95
+ rescue Reload
96
+ else
97
+ timer_fire
98
+ end
99
+ end
100
+ end
101
+
102
+ # Schedule a one shot alarm
103
+ #
104
+ # Call the given block after half a second:
105
+ # timer_after(0.5) do
106
+ # # executed in 0.5 seconds
107
+ # end
108
+ sync_call def timer_after(seconds, now=Time.now, &block)
109
+ a = OneTimeAlarm.new(now + seconds, &block)
110
+ timer_add_alarm(a)
111
+ a
112
+ end
113
+
114
+ # Schedule a repeated alarm
115
+ #
116
+ # Call the given block in after half a second and then repeatedly every 0.5 seconds:
117
+ # timer_after(0.5) do
118
+ # # executed every 0.5 seconds
119
+ # end
120
+ sync_call def timer_every(seconds, now=Time.now, &block)
121
+ a = RepeatedAlarm.new(now + seconds, seconds, &block)
122
+ timer_add_alarm(a)
123
+ a
124
+ end
125
+
126
+ # Cancel an alarm previously scheduled per timer_after or timer_every
127
+ async_call def timer_cancel(alarm)
128
+ a = @timer_alarms.delete(alarm)
129
+ if a
130
+ timer_check_integrity
131
+ end
132
+ end
133
+
134
+ # @private
135
+ private def timer_add_alarm(alarm)
136
+ i = @timer_alarms.bsearch_index {|t| t.timestamp <= alarm.timestamp }
137
+ if i
138
+ @timer_alarms[i, 0] = alarm
139
+ else
140
+ @timer_alarms << alarm
141
+ @timer_action.raise(Reload) unless @timer_action.current?
142
+ end
143
+ timer_check_integrity
144
+ end
145
+
146
+ # @private
147
+ private def timer_check_integrity
148
+ @timer_alarms.inject(nil) do |min, a|
149
+ raise InternalError, "alarms are not ordered: #{@timer_alarms.inspect}" if min && min < a.timestamp
150
+ a.timestamp
151
+ end
152
+ end
153
+
154
+ # @private
155
+ private sync_call def timer_next_timestamp
156
+ @timer_alarms.last&.timestamp
157
+ end
158
+
159
+ # @private
160
+ private sync_call def timer_fire(now=Time.now)
161
+ while @timer_alarms.last&.timestamp&.<=(now)
162
+ a = @timer_alarms.pop
163
+ if a.fire_then_repeat?(now)
164
+ timer_add_alarm(a)
165
+ end
166
+ timer_check_integrity
167
+ end
168
+ # the method result is irrelevant, but sync_call is necessary to yield the timer blocks
169
+ nil
170
+ end
171
+ end
172
+ end
@@ -0,0 +1,5 @@
1
+ # frozen-string-literal: true
2
+
3
+ class Eventbox
4
+ VERSION = "0.1.0"
5
+ end
metadata ADDED
@@ -0,0 +1,156 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: eventbox
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Lars Kanis
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain:
11
+ - |
12
+ -----BEGIN CERTIFICATE-----
13
+ MIIDPDCCAiSgAwIBAgIBBTANBgkqhkiG9w0BAQsFADBEMQ0wCwYDVQQDDARsYXJz
14
+ MR8wHQYKCZImiZPyLGQBGRYPZ3JlaXotcmVpbnNkb3JmMRIwEAYKCZImiZPyLGQB
15
+ GRYCZGUwHhcNMTgwOTE0MjE0NjIyWhcNMTkwOTE0MjE0NjIyWjBEMQ0wCwYDVQQD
16
+ DARsYXJzMR8wHQYKCZImiZPyLGQBGRYPZ3JlaXotcmVpbnNkb3JmMRIwEAYKCZIm
17
+ iZPyLGQBGRYCZGUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDZb4Uv
18
+ RFJfRu/VEWiy3psh2jinETjiuBrL0NeRFGf8H7iU9+gx/DI/FFhfHGLrDeIskrJx
19
+ YIWDMmEjVO10UUdj7wu4ZhmU++0Cd7Kq9/TyP/shIP3IjqHjVLCnJ3P6f1cl5rxZ
20
+ gqo+d3BAoDrmPk0rtaf6QopwUw9RBiF8V4HqvpiY+ruJotP5UQDP4/lVOKvA8PI9
21
+ P0GmVbFBrbc7Zt5h78N3UyOK0u+nvOC23BvyHXzCtcFsXCoEkt+Wwh0RFqVZdnjM
22
+ LMO2vULHKKHDdX54K/sbVCj9pN9h1aotNzrEyo55zxn0G9PHg/G3P8nMvAXPkUTe
23
+ brhXrfCwWRvOXA4TAgMBAAGjOTA3MAkGA1UdEwQCMAAwCwYDVR0PBAQDAgSwMB0G
24
+ A1UdDgQWBBRAHK81igrXodaDj8a8/BIKsaZrETANBgkqhkiG9w0BAQsFAAOCAQEA
25
+ ZNTM80Pn9caTOcDYCmJi28qV0H8x6k5zeEd8PckEr9gYCFrUtVvtQPBi364PWo82
26
+ Img1GQle3nbhYuiNPRsjfr0uaTgUeA7JXw5OCEtgck2w93KzFISBt+jLyoprAJDh
27
+ uHImSFGzjRwHVi2Kihh1y4VR0ZkofNWaPqhxhxN7JCTVrV8MLqe2ZgHoe1WMGAyT
28
+ mmzdbSP3kmz9L2eshisCKPSi0qKk4VyBhdeeIf+3xMim/DK1gTz44BwHSCtGkaJT
29
+ NOQ0Qq2j8NtFfBHqGHBKaqj/Q0AciwGWIMfXJh3fTsRuMonkEu6TgB6M79HzgqQ8
30
+ sXOGZvvDWUBn5GWztfhdZw==
31
+ -----END CERTIFICATE-----
32
+ date: 2018-12-04 00:00:00.000000000 Z
33
+ dependencies:
34
+ - !ruby/object:Gem::Dependency
35
+ name: bundler
36
+ requirement: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '1.16'
41
+ - - "<"
42
+ - !ruby/object:Gem::Version
43
+ version: '3'
44
+ type: :development
45
+ prerelease: false
46
+ version_requirements: !ruby/object:Gem::Requirement
47
+ requirements:
48
+ - - ">="
49
+ - !ruby/object:Gem::Version
50
+ version: '1.16'
51
+ - - "<"
52
+ - !ruby/object:Gem::Version
53
+ version: '3'
54
+ - !ruby/object:Gem::Dependency
55
+ name: rake
56
+ requirement: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: '10.0'
61
+ type: :development
62
+ prerelease: false
63
+ version_requirements: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: '10.0'
68
+ - !ruby/object:Gem::Dependency
69
+ name: minitest
70
+ requirement: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - "~>"
73
+ - !ruby/object:Gem::Version
74
+ version: '5.0'
75
+ type: :development
76
+ prerelease: false
77
+ version_requirements: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - "~>"
80
+ - !ruby/object:Gem::Version
81
+ version: '5.0'
82
+ - !ruby/object:Gem::Dependency
83
+ name: yard
84
+ requirement: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - "~>"
87
+ - !ruby/object:Gem::Version
88
+ version: '0.9'
89
+ type: :development
90
+ prerelease: false
91
+ version_requirements: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - "~>"
94
+ - !ruby/object:Gem::Version
95
+ version: '0.9'
96
+ description: |-
97
+ {Eventbox} objects are event based and single threaded from the inside but thread-safe and blocking from the outside.
98
+ Eventbox enforces a separation of code for event processing and code running blocking operations.
99
+ Code inside an {Eventbox} object is executed non-concurrently and hence shouldn't do any blocking operations.
100
+ This is similar to the typical JavaScript programming style.
101
+ email:
102
+ - lars@greiz-reinsdorf.de
103
+ executables: []
104
+ extensions: []
105
+ extra_rdoc_files: []
106
+ files:
107
+ - ".appveyor.yml"
108
+ - ".gitignore"
109
+ - ".travis.yml"
110
+ - ".yardopts"
111
+ - CODE_OF_CONDUCT.md
112
+ - Gemfile
113
+ - LICENSE.txt
114
+ - README.md
115
+ - Rakefile
116
+ - bin/console
117
+ - bin/setup
118
+ - docs/downloads.md
119
+ - docs/server.md
120
+ - docs/threadpool.md
121
+ - eventbox.gemspec
122
+ - lib/eventbox.rb
123
+ - lib/eventbox/argument_wrapper.rb
124
+ - lib/eventbox/boxable.rb
125
+ - lib/eventbox/event_loop.rb
126
+ - lib/eventbox/object_registry.rb
127
+ - lib/eventbox/sanitizer.rb
128
+ - lib/eventbox/thread_pool.rb
129
+ - lib/eventbox/timer.rb
130
+ - lib/eventbox/version.rb
131
+ homepage: https://github.com/larskanis/eventbox
132
+ licenses:
133
+ - MIT
134
+ metadata:
135
+ yard.run: yri
136
+ post_install_message:
137
+ rdoc_options: []
138
+ require_paths:
139
+ - lib
140
+ required_ruby_version: !ruby/object:Gem::Requirement
141
+ requirements:
142
+ - - "~>"
143
+ - !ruby/object:Gem::Version
144
+ version: '2.3'
145
+ required_rubygems_version: !ruby/object:Gem::Requirement
146
+ requirements:
147
+ - - ">="
148
+ - !ruby/object:Gem::Version
149
+ version: '0'
150
+ requirements: []
151
+ rubyforge_project:
152
+ rubygems_version: 2.7.7
153
+ signing_key:
154
+ specification_version: 4
155
+ summary: Manage multithreading with the safety of event based programming
156
+ test_files: []
Binary file