hakuban 0.5.2 → 0.6.2

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,275 @@
1
+ require 'hakuban/hakuban'
2
+ require 'ostruct'
3
+
4
+ #TODO: prevent usage of old object?
5
+ #TODO: destructors
6
+
7
+ module Hakuban
8
+
9
+ class ObjectManager
10
+
11
+ attr_reader :contract
12
+
13
+ class Event < OpenStruct; end
14
+
15
+ def initialize(contract, object_class, block)
16
+ @contract = contract
17
+ @objects_mutex = Mutex.new
18
+ @objects = {}
19
+ @running_handlers = {}
20
+ @event_queue = Queue.new
21
+
22
+ # This callback gets called from a separate thread, with no async reactor running.
23
+ # So, we only use it to forward actions to the main thread.
24
+ @ffi_callback = proc { |descriptor, action|
25
+ @event_queue << Event.new(action: action, descriptor: descriptor)
26
+ }
27
+ @ffi_events = @contract.new_callback_event_queue
28
+ @ffi_events.callback_register(&@ffi_callback)
29
+
30
+ @async = async_run {
31
+ while @event_queue and event = @event_queue.shift
32
+ @objects_mutex.synchronize {
33
+ case event.action
34
+ when :insert
35
+ raise if @objects[event.descriptor]
36
+ @objects[event.descriptor] = object_class.new(@contract, event.descriptor)
37
+ @event_queue << Event.new(action: :handler_start, descriptor: event.descriptor) if block
38
+ when :change
39
+ raise if not @objects[event.descriptor]
40
+ @objects[event.descriptor].do_change(:change)
41
+ when :remove
42
+ raise if not @objects[event.descriptor]
43
+ @objects[event.descriptor].do_change(:remove)
44
+ @objects.delete(event.descriptor)
45
+ when :handler_start
46
+ if (object = @objects[event.descriptor]) and !object.handler_already_run and !@running_handlers[event.descriptor]
47
+ descriptor_for_lambda = event.descriptor
48
+ @running_handlers[event.descriptor] = async_run {
49
+ object.run(block)
50
+ @event_queue << Event.new(action: :handler_finished, descriptor: descriptor_for_lambda) if @event_queue
51
+ }
52
+ end
53
+ when :handler_finished
54
+ raise if not @running_handlers[event.descriptor]
55
+ @running_handlers.delete(event.descriptor)
56
+ @event_queue << Event.new(action: :handler_start, descriptor: event.descriptor) if @objects[event.descriptor]
57
+ when :drop
58
+ @ffi_events.callback_unregister
59
+ @objects.values.each { |object| object.do_change(:remove) }
60
+ while @running_handlers.size > 0
61
+ event = @event_queue.shift
62
+ @running_handlers.delete(event.descriptor) if event.action == :handler_finished
63
+ end
64
+ @event_queue.clear
65
+ @objects.clear
66
+ @contract.drop
67
+ @contract, @event_queue = nil, nil
68
+ break
69
+ end
70
+ }
71
+ end
72
+ }
73
+ end
74
+
75
+
76
+ def objects
77
+ @objects_mutex.synchronize {
78
+ @objects.dup
79
+ }
80
+ end
81
+
82
+
83
+ def object
84
+ @objects_mutex.synchronize {
85
+ @objects.values.first
86
+ }
87
+ end
88
+
89
+
90
+ def drop
91
+ drop_nonblock
92
+ async_join(@async)
93
+ end
94
+
95
+
96
+ def drop_nonblock
97
+ if @contract
98
+ @event_queue << Event.new(action: :drop)
99
+ end
100
+ end
101
+ end
102
+
103
+
104
+
105
+ class ManagedObject
106
+
107
+ attr_reader :descriptor, :async, :handler_already_run
108
+
109
+
110
+ def initialize(contract, descriptor)
111
+ @contract,@descriptor = contract, descriptor
112
+ @changes = Queue.new
113
+ @handler_already_run = false
114
+ end
115
+
116
+
117
+ def run(handler)
118
+ @handler_already_run = true
119
+ handler.call(self)
120
+ end
121
+
122
+
123
+ def next_event; next_change; end
124
+ def next_change
125
+ loop {
126
+ case @changes.shift
127
+ when :change then return true
128
+ when :remove then return false
129
+ end
130
+ }
131
+ end
132
+
133
+
134
+ def do_change(change)
135
+ @changes.push(change)
136
+ end
137
+
138
+ end
139
+
140
+
141
+
142
+ class ObjectObserve
143
+
144
+ class ObservedObject < ManagedObject
145
+
146
+ def state
147
+ @contract.object_state
148
+ end
149
+
150
+ def data
151
+ @contract.object_state&.data
152
+ end
153
+
154
+ end
155
+
156
+ end
157
+
158
+
159
+ class ObjectExpose
160
+
161
+ class ExposedObject < ManagedObject
162
+
163
+ def initialize(contract, descriptor)
164
+ super(contract, descriptor)
165
+ @assignment = contract.assignment
166
+ end
167
+
168
+ def run(handler)
169
+ super
170
+ @contract.desynchronize(@assignment)
171
+ end
172
+
173
+ def do_change(change)
174
+ @assignment = @contract.assignment
175
+ super(change)
176
+ end
177
+
178
+ def assignement
179
+ @assignment = @contract.assignment()
180
+ end
181
+
182
+ def assigned?
183
+ @contract.assigned?()
184
+ end
185
+
186
+ def set_state(*state, **kwargs)
187
+ kwargs[:assignment] ||= @assignment
188
+ @contract.set_object_state(*state, **kwargs)
189
+ end
190
+
191
+ def state=(state)
192
+ set_state(*state)
193
+ end
194
+
195
+ def set_data(value)
196
+ timestamp = Time.new.to_f
197
+ set_state([1, timestamp.floor, ((timestamp - timestamp.floor)*1000000000).floor, 0],value)
198
+ end
199
+
200
+ def data=(value)
201
+ set_data(value)
202
+ end
203
+
204
+ end
205
+
206
+ end
207
+
208
+
209
+ class TagObserve
210
+
211
+ class ObservedObject < ManagedObject
212
+
213
+ def state
214
+ @contract.object_state(@descriptor)
215
+ end
216
+
217
+ def data
218
+ @contract.object_state(@descriptor)&.data
219
+ end
220
+
221
+ end
222
+
223
+ end
224
+
225
+
226
+ class TagExpose
227
+
228
+ class ExposedObject < ManagedObject
229
+
230
+ def initialize(contract, descriptor)
231
+ super(contract, descriptor)
232
+ @assignment = contract.assignment(@descriptor)
233
+ end
234
+
235
+ def run(handler)
236
+ super
237
+ @contract.desynchronize(@descriptor, @assignment)
238
+ end
239
+
240
+ def do_change(change)
241
+ @assignment = @contract.assignment(@descriptor)
242
+ super(change)
243
+ end
244
+
245
+ def assignement
246
+ @assignment = @contract.assignment(@descriptor)
247
+ end
248
+
249
+ def assigned?
250
+ @contract.assigned?(@descriptor)
251
+ end
252
+
253
+ def set_state(*args, **kwargs)
254
+ kwargs[:assignment] ||= @assignment
255
+ @contract.set_object_state(@descriptor, *args, **kwargs)
256
+ end
257
+
258
+ def state=(state)
259
+ set_state(*state)
260
+ end
261
+
262
+ def set_data(value)
263
+ timestamp = Time.new.to_f
264
+ set_state([1, timestamp.floor, ((timestamp - timestamp.floor)*1000000000).floor, 0],value)
265
+ end
266
+
267
+ def data=(value)
268
+ set_data(value)
269
+ end
270
+
271
+ end
272
+
273
+ end
274
+
275
+ end
@@ -0,0 +1,44 @@
1
+ require 'hakuban'
2
+ require 'hakuban/manager'
3
+
4
+ module Hakuban
5
+
6
+ class ThreadObjectManager < ObjectManager
7
+ def async_run
8
+ Thread.new { yield }
9
+ end
10
+
11
+ def async_join(thread)
12
+ thread.join
13
+ end
14
+ end
15
+
16
+
17
+ class ObjectObserve
18
+ def thread(&block)
19
+ ThreadObjectManager.new(self, ObservedObject, block)
20
+ end
21
+ end
22
+
23
+
24
+ class ObjectExpose
25
+ def thread(&block)
26
+ ThreadObjectManager.new(self, ExposedObject, block)
27
+ end
28
+ end
29
+
30
+
31
+ class TagObserve
32
+ def thread(&block)
33
+ ThreadObjectManager.new(self, ObservedObject, block)
34
+ end
35
+ end
36
+
37
+
38
+ class TagExpose
39
+ def thread(&block)
40
+ ThreadObjectManager.new(self, ExposedObject, block)
41
+ end
42
+ end
43
+
44
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Hakuban
4
- VERSION = "0.5.2"
4
+ VERSION = "0.6.2"
5
5
  end
data/lib/hakuban.rb CHANGED
@@ -2,6 +2,9 @@
2
2
 
3
3
  require_relative "hakuban/version"
4
4
  require_relative "hakuban/hakuban"
5
+ require_relative "hakuban/engine"
6
+ #require_relative "hakuban/async"
7
+ #require_relative "hakuban/thread"
5
8
 
6
9
  module Hakuban
7
10
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hakuban
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.2
4
+ version: 0.6.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - yunta
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-07-11 00:00:00.000000000 Z
11
+ date: 2022-02-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rspec
@@ -39,55 +39,55 @@ dependencies:
39
39
  - !ruby/object:Gem::Version
40
40
  version: '1.15'
41
41
  - !ruby/object:Gem::Dependency
42
- name: msgpack
42
+ name: json
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: '1.4'
47
+ version: '2.5'
48
48
  type: :runtime
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: '1.4'
54
+ version: '2.5'
55
55
  - !ruby/object:Gem::Dependency
56
- name: json
56
+ name: slop
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
59
  - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: '2.5'
61
+ version: '4.9'
62
62
  type: :runtime
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
- version: '2.5'
68
+ version: '4.9'
69
69
  description: Ruby binding for convenient data-object sharing library - Hakuban.
70
70
  email:
71
71
  - maciej.blomberg@mikoton.com
72
- executables: []
72
+ executables:
73
+ - hakuban-observer
74
+ - hakuban-thread-engine
73
75
  extensions: []
74
76
  extra_rdoc_files: []
75
77
  files:
76
- - ".gitignore"
77
- - ".rspec"
78
- - Gemfile
79
- - LICENSE.txt
78
+ - MIT-LICENSE
80
79
  - README.md
81
80
  - Rakefile
82
- - bin/console
83
- - bin/setup
84
- - examples/all-top-managed.rb
85
- - examples/all-top-simple.rb
86
- - examples/cpu-usage.rb
87
- - hakuban.gemspec
81
+ - bin/hakuban-observer
82
+ - bin/hakuban-thread-engine
88
83
  - lib/hakuban.rb
84
+ - lib/hakuban/async.rb
85
+ - lib/hakuban/engine.rb
86
+ - lib/hakuban/event-queue.rb
89
87
  - lib/hakuban/ffi.rb
90
88
  - lib/hakuban/hakuban.rb
89
+ - lib/hakuban/manager.rb
90
+ - lib/hakuban/thread.rb
91
91
  - lib/hakuban/version.rb
92
92
  homepage: https://gitlab.com/yunta/hakuban-ruby
93
93
  licenses:
@@ -99,16 +99,16 @@ require_paths:
99
99
  - lib
100
100
  required_ruby_version: !ruby/object:Gem::Requirement
101
101
  requirements:
102
- - - ">="
102
+ - - "~>"
103
103
  - !ruby/object:Gem::Version
104
- version: 2.4.0
104
+ version: '3.0'
105
105
  required_rubygems_version: !ruby/object:Gem::Requirement
106
106
  requirements:
107
107
  - - ">="
108
108
  - !ruby/object:Gem::Version
109
109
  version: '0'
110
110
  requirements: []
111
- rubygems_version: 3.2.15
111
+ rubygems_version: 3.2.22
112
112
  signing_key:
113
113
  specification_version: 4
114
114
  summary: Ruby binding for Hakuban library
data/.gitignore DELETED
@@ -1,12 +0,0 @@
1
- /.bundle/
2
- /.yardoc
3
- /_yardoc/
4
- /coverage/
5
- /doc/
6
- /pkg/
7
- /spec/reports/
8
- /tmp/
9
- /vendor/
10
-
11
- # rspec failure tracking
12
- .rspec_status
data/.rspec DELETED
@@ -1,3 +0,0 @@
1
- --format documentation
2
- --color
3
- --require spec_helper
data/Gemfile DELETED
@@ -1,6 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- source "https://rubygems.org"
4
-
5
- gemspec
6
-
data/LICENSE.txt DELETED
@@ -1,21 +0,0 @@
1
- The MIT License (MIT)
2
-
3
- Copyright (c) 2021 yunta
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in
13
- all copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
- THE SOFTWARE.
data/bin/console DELETED
@@ -1,15 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # frozen_string_literal: true
3
-
4
- require "bundler/setup"
5
- require "hakuban"
6
-
7
- # You can add fixtures and/or initialization code here to make experimenting
8
- # with your gem easier. You can also use a different console, if you like.
9
-
10
- # (If you use this, don't forget to add pry to your Gemfile!)
11
- # require "pry"
12
- # Pry.start
13
-
14
- require "irb"
15
- IRB.start(__FILE__)
data/bin/setup DELETED
@@ -1,8 +0,0 @@
1
- #!/usr/bin/env bash
2
- set -euo pipefail
3
- IFS=$'\n\t'
4
- set -vx
5
-
6
- bundle install
7
-
8
- # Do any other automated setup that you need to do here
@@ -1,46 +0,0 @@
1
- require 'hakuban'
2
- require_relative './cpu-usage.rb'
3
-
4
-
5
- class Utilization < Hakuban::ObjectManager::ManagedObject
6
- include Hakuban::ObjectManager::ObservedObject
7
- include Hakuban::ObjectManager::ExposedObject
8
-
9
- attr_reader :id
10
-
11
- def initialize(contract, descriptor)
12
- super
13
- @id = descriptor.json["id"]
14
- end
15
- end
16
-
17
-
18
- id = "#{$$}@#{`hostname`.strip}"
19
-
20
- hakuban = Hakuban::LocalNode.new
21
- connector = Hakuban::WebsocketConnector.new("ws://localhost:3001").start(hakuban)
22
- all_utilizations = hakuban.tag("utilizations").observe.manage(Utilization)
23
- my_utilization = hakuban.object(["utilizations"],{id: id}).expose.manage(Utilization)
24
-
25
- previous_cpu_usage_state = nil
26
- loop {
27
- previous_cpu_usage_state, cpu_usage = CPU::current_usage(previous_cpu_usage_state)
28
- my_utilization.data = [cpu_usage]
29
-
30
- utilizations = all_utilizations.objects.values.sort_by(&:id).map { |utilization|
31
- [utilization.id, utilization.data&.first]
32
- }.select { |id, utilization| utilization }
33
- next sleep 1 if utilizations.empty?
34
-
35
- widest_id_width = utilizations.transpose[0].map(&:size).max
36
- utilizations.each.with_index { |(id, utilization), i|
37
- bars = utilization.map { |percentage| " ▁▂▃▄▅▆▇█"[percentage*9/100] }.join("")
38
- bg_color = [0,0,180 + (i % 2)*64]
39
- fg_color = [255,255,0]
40
- puts "%s -> \e[48;2;%i;%i;%im\e[38;2;%i;%i;%im%s\e[49m\e[39m"%([id.ljust(widest_id_width)]+bg_color+fg_color+[bars])
41
- }
42
-
43
- sleep 1
44
- #TODO: make this more friendly, save state, etc.
45
- print "\e[2J\e[f"
46
- }
@@ -1,26 +0,0 @@
1
- require 'hakuban'
2
- require_relative './cpu-usage.rb'
3
-
4
-
5
- id = "#{$$}@#{`hostname`.strip}"
6
-
7
- hakuban = Hakuban::LocalNode.new
8
- connector = Hakuban::WebsocketConnector.new("ws://localhost:3001").start(hakuban)
9
- all_utilizations = hakuban.tag("utilizations").observe
10
- my_utilization = hakuban.object(["utilizations"],{id: id}).expose
11
-
12
- previous_cpu_usage_state = nil
13
-
14
- loop {
15
- previous_cpu_usage_state, cpu_usage = CPU::current_usage(previous_cpu_usage_state)
16
- my_utilization.set_object_state([Time.new.to_f],[],[cpu_usage]) if cpu_usage
17
-
18
- all_utilizations.object_descriptors.map { |descriptor|
19
- if data = all_utilizations.object_state(descriptor).data
20
- puts "#{descriptor.json["id"]} -> #{data.first}"
21
- end
22
- }
23
-
24
- sleep 1
25
- puts
26
- }
@@ -1,16 +0,0 @@
1
- module CPU
2
-
3
- def self.current_usage(previous_cpu_usage_state)
4
- current_uptime = open("/proc/uptime") { |file| file.read.split(" ")[0].to_f }
5
- current_cpu_ticks = open("/proc/stat") { |file|
6
- file.read.split("\n").select { |line| line.start_with?(/cpu[0-9]+/) }.map { |line| line.split(" ")[4].to_f }
7
- }
8
- cpu_usage = previous_cpu_usage_state&.dig(0)&.zip(current_cpu_ticks)&.map { |old_ticks, new_ticks|
9
- [0,(100 - (new_ticks - old_ticks) / (current_uptime - previous_cpu_usage_state[1]))].max.floor
10
- }
11
- [[current_cpu_ticks, current_uptime], cpu_usage]
12
- end
13
-
14
- end
15
-
16
-
data/hakuban.gemspec DELETED
@@ -1,36 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "lib/hakuban/version"
4
-
5
- Gem::Specification.new do |spec|
6
- spec.name = "hakuban"
7
- spec.version = Hakuban::VERSION
8
- spec.authors = ["yunta"]
9
- spec.email = ["maciej.blomberg@mikoton.com"]
10
-
11
- spec.summary = "Ruby binding for Hakuban library"
12
- spec.description = "Ruby binding for convenient data-object sharing library - Hakuban."
13
- spec.homepage = "https://gitlab.com/yunta/hakuban-ruby"
14
- spec.license = "MIT"
15
- spec.required_ruby_version = Gem::Requirement.new(">= 2.4.0")
16
-
17
- #spec.metadata["allowed_push_host"] = "TODO: Set to 'http://mygemserver.com'"
18
-
19
- #spec.metadata["homepage_uri"] = spec.homepage
20
- #spec.metadata["source_code_uri"] = "https://gitlab.com/yunta/hakuban-ruby"
21
- #spec.metadata["changelog_uri"] = "TODO: Put your gem's CHANGELOG.md URL here."
22
-
23
- # Specify which files should be added to the gem when it is released.
24
- # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
25
- spec.files = Dir.chdir(File.expand_path(__dir__)) do
26
- `git ls-files -z`.split("\x0").reject { |f| f.match(%r{\A(?:test|spec|features)/}) }
27
- end
28
- #spec.bindir = "exe"
29
- #spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
30
- spec.require_paths = ["lib"]
31
-
32
- spec.add_development_dependency "rspec", "~>3.10"
33
- spec.add_dependency "ffi", "~>1.15"
34
- spec.add_dependency "msgpack", "~>1.4"
35
- spec.add_dependency "json", "~>2.5"
36
- end