eventbox 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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