hakuban 0.5.0 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,259 @@
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 = {}
18
+ @running_handlers = {}
19
+ @event_queue = Queue.new
20
+
21
+ # This callback gets called from a separate thread, with no async reactor running.
22
+ # So, we only use it to forward actions to the main thread.
23
+ @ffi_callback = proc { |descriptor, action|
24
+ @event_queue << Event.new(action: action, descriptor: descriptor)
25
+ }
26
+ @ffi_events = @contract.events
27
+ @ffi_events.callback_register(&@ffi_callback)
28
+
29
+ @async = async_run {
30
+ while event = @event_queue.shift
31
+ case event.action
32
+ when :insert
33
+ raise if @objects[event.descriptor]
34
+ @objects[event.descriptor] = object_class.new(@contract, event.descriptor)
35
+ @event_queue << Event.new(action: :handler_start, descriptor: event.descriptor) if block
36
+ when :change
37
+ raise if not @objects[event.descriptor]
38
+ @objects[event.descriptor].do_change(:change)
39
+ when :remove
40
+ raise if not @objects[event.descriptor]
41
+ @objects[event.descriptor].do_change(:remove)
42
+ @objects.delete(event.descriptor)
43
+ when :handler_start
44
+ if (object = @objects[event.descriptor]) and !object.handler_already_run and !@running_handlers[event.descriptor]
45
+ descriptor_for_lambda = event.descriptor
46
+ @running_handlers[event.descriptor] = async_run {
47
+ object.run(block)
48
+ @event_queue << Event.new(action: :handler_finished, descriptor: descriptor_for_lambda) if @event_queue
49
+ }
50
+ end
51
+ when :handler_finished
52
+ raise if not @running_handlers[event.descriptor]
53
+ @running_handlers.delete(event.descriptor)
54
+ @event_queue << Event.new(action: :handler_start, descriptor: event.descriptor) if @objects[event.descriptor]
55
+ when :drop
56
+ @ffi_events.callback_unregister
57
+ @objects.values.each { |object| object.do_change(:remove) }
58
+ while @running_handlers.size > 0
59
+ event = @event_queue.shift
60
+ @running_handlers.delete(event.descriptor) if event.action == :handler_finished
61
+ end
62
+ @event_queue.clear
63
+ @objects.clear
64
+ @contract.drop
65
+ @contract, @event_queue = nil, nil
66
+ break
67
+ end
68
+ end
69
+ }
70
+ end
71
+
72
+ def objects; @objects; end
73
+ def object; @objects.values.first; end
74
+
75
+ def drop
76
+ drop_nonblock
77
+ async_join(@async)
78
+ end
79
+
80
+ def drop_nonblock
81
+ if @contract
82
+ @event_queue << Event.new(action: :drop)
83
+ end
84
+ end
85
+ end
86
+
87
+
88
+
89
+ class ManagedObject
90
+
91
+ attr_reader :descriptor, :async, :handler_already_run
92
+
93
+
94
+ def initialize(contract, descriptor)
95
+ @contract,@descriptor = contract, descriptor
96
+ @changes = Queue.new
97
+ @handler_already_run = false
98
+ end
99
+
100
+
101
+ def run(handler)
102
+ @handler_already_run = true
103
+ handler.call(self)
104
+ end
105
+
106
+
107
+ def next_event; next_change; end
108
+ def next_change
109
+ loop {
110
+ case @changes.shift
111
+ when :change then return true
112
+ when :remove then return false
113
+ end
114
+ }
115
+ end
116
+
117
+
118
+ def do_change(change)
119
+ @changes.push(change)
120
+ end
121
+
122
+ end
123
+
124
+
125
+
126
+ class ObjectObserve
127
+
128
+ class ObservedObject < ManagedObject
129
+
130
+ def state
131
+ @contract.object_state
132
+ end
133
+
134
+ def data
135
+ @contract.object_state&.data
136
+ end
137
+
138
+ end
139
+
140
+ end
141
+
142
+
143
+ class ObjectExpose
144
+
145
+ class ExposedObject < ManagedObject
146
+
147
+ def initialize(contract, descriptor)
148
+ super(contract, descriptor)
149
+ @assignment = contract.assignment
150
+ end
151
+
152
+ def run(handler)
153
+ super
154
+ @contract.desynchronize(@assignment)
155
+ end
156
+
157
+ def do_change(change)
158
+ @assignment = @contract.assignment
159
+ super(change)
160
+ end
161
+
162
+ def assignement
163
+ @assignment = @contract.assignment()
164
+ end
165
+
166
+ def assigned?
167
+ @contract.assigned?()
168
+ end
169
+
170
+ def set_state(*state, **kwargs)
171
+ kwargs[:assignment] ||= @assignment
172
+ @contract.set_object_state(*state, **kwargs)
173
+ end
174
+
175
+ def state=(state)
176
+ set_state(*state)
177
+ end
178
+
179
+ def set_data(value)
180
+ timestamp = Time.new.to_f
181
+ set_state([1, timestamp.floor, ((timestamp - timestamp.floor)*1000000000).floor, 0],value)
182
+ end
183
+
184
+ def data=(value)
185
+ set_data(value)
186
+ end
187
+
188
+ end
189
+
190
+ end
191
+
192
+
193
+ class TagObserve
194
+
195
+ class ObservedObject < ManagedObject
196
+
197
+ def state
198
+ @contract.object_state(@descriptor)
199
+ end
200
+
201
+ def data
202
+ @contract.object_state(@descriptor)&.data
203
+ end
204
+
205
+ end
206
+
207
+ end
208
+
209
+
210
+ class TagExpose
211
+
212
+ class ExposedObject < ManagedObject
213
+
214
+ def initialize(contract, descriptor)
215
+ super(contract, descriptor)
216
+ @assignment = contract.assignment(@descriptor)
217
+ end
218
+
219
+ def run(handler)
220
+ super
221
+ @contract.desynchronize(@descriptor, @assignment)
222
+ end
223
+
224
+ def do_change(change)
225
+ @assignment = @contract.assignment(@descriptor)
226
+ super(change)
227
+ end
228
+
229
+ def assignement
230
+ @assignment = @contract.assignment(@descriptor)
231
+ end
232
+
233
+ def assigned?
234
+ @contract.assigned?(@descriptor)
235
+ end
236
+
237
+ def set_state(*args, **kwargs)
238
+ kwargs[:assignment] ||= @assignment
239
+ @contract.set_object_state(@descriptor, *args, **kwargs)
240
+ end
241
+
242
+ def state=(state)
243
+ set_state(*state)
244
+ end
245
+
246
+ def set_data(value)
247
+ timestamp = Time.new.to_f
248
+ set_state([1, timestamp.floor, ((timestamp - timestamp.floor)*1000000000).floor, 0],value)
249
+ end
250
+
251
+ def data=(value)
252
+ set_data(value)
253
+ end
254
+
255
+ end
256
+
257
+ end
258
+
259
+ 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.0"
4
+ VERSION = "0.6.0"
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.0
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - yunta
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-06-13 00:00:00.000000000 Z
11
+ date: 2022-01-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rspec
@@ -39,55 +39,54 @@ 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
89
86
  - lib/hakuban/ffi.rb
90
87
  - lib/hakuban/hakuban.rb
88
+ - lib/hakuban/manager.rb
89
+ - lib/hakuban/thread.rb
91
90
  - lib/hakuban/version.rb
92
91
  homepage: https://gitlab.com/yunta/hakuban-ruby
93
92
  licenses:
@@ -99,16 +98,16 @@ require_paths:
99
98
  - lib
100
99
  required_ruby_version: !ruby/object:Gem::Requirement
101
100
  requirements:
102
- - - ">="
101
+ - - "~>"
103
102
  - !ruby/object:Gem::Version
104
- version: 2.4.0
103
+ version: '3.0'
105
104
  required_rubygems_version: !ruby/object:Gem::Requirement
106
105
  requirements:
107
106
  - - ">="
108
107
  - !ruby/object:Gem::Version
109
108
  version: '0'
110
109
  requirements: []
111
- rubygems_version: 3.2.15
110
+ rubygems_version: 3.2.22
112
111
  signing_key:
113
112
  specification_version: 4
114
113
  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