andromeda 0.1 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,48 @@
1
+ module Andromeda
2
+
3
+ module Impl
4
+
5
+ module To_S
6
+
7
+ def to_s(short = false)
8
+ if short
9
+ to_short_s
10
+ else
11
+ super_str = super()
12
+ class_name = self.class.name.split('::')[-1]
13
+ obj_id = object_id.to_s(16)
14
+ "\#<#{class_name}:0x#{obj_id}#{to_s(true)}>"
15
+ end
16
+ end
17
+
18
+ def to_short_s ; '' end
19
+
20
+ def self.short_s(v = value)
21
+ return ":#{v}" if v.is_a?(Symbol)
22
+ return "'#{v}'" if v.is_a?(String)
23
+ return 'nil' unless v
24
+ "#{v}"
25
+ end
26
+
27
+ end
28
+
29
+ # Shared base class of Spot and Impl::PrePlan
30
+ class ConnectorBase
31
+ # @return [Spot] entry.intern nil
32
+ def start ; entry.intern(nil) end
33
+
34
+ # post_to nil, data, tags_in
35
+ #
36
+ # @return [self]
37
+ def post(data, tags_in = {}) ; post_to nil, data, tags_in end
38
+
39
+ alias_method :<<, :post
40
+
41
+ # post_to LocalTrack.instance, data, tags_in
42
+ #
43
+ # @return [self]
44
+ def post_local(data, tags_in = {}) ; post_to Guides::LocalTrack.instance, data, tags_in end
45
+ end
46
+
47
+ end
48
+ end
@@ -0,0 +1,89 @@
1
+ module Andromeda
2
+
3
+ module Impl
4
+
5
+ # Generator for random xorable ids (used for markers)
6
+ class XorId
7
+ include To_S
8
+
9
+ protected
10
+
11
+ def initialize(len, random = true, init_data = nil)
12
+ raise ArgumentError unless len.kind_of?(Fixnum)
13
+ raise ArgumentError unless len >= 0
14
+
15
+ @data = if init_data
16
+ init_data
17
+ else
18
+ if random
19
+ then len.times.map { Id.rnd_byte }
20
+ else len.times.map { 0 } end
21
+ end
22
+ end
23
+
24
+ public
25
+
26
+ def clone_to_copy? ; false end
27
+ def identical_copy ; self end
28
+
29
+ def length ; @data.length end
30
+ def zero? ; each { |b| return false unless b == 0 } ; true end
31
+
32
+ def [](key) ; @data[key] end
33
+
34
+ def same_length?(obj) ; self.length == obj.length end
35
+
36
+ def each ; this = self ; 0.upto(length-1).each { |i| yield this[i] } end
37
+ def each_with_index ; this = self ; 0.upto(length-1).each { |i| yield i, this[i] } end
38
+
39
+ # Compare self to b
40
+ # @param [Id] b
41
+ def ==(b)
42
+ return true if self.equal? b
43
+ return false if b.nil?
44
+ return false unless b.class.equal? self.class
45
+ return false unless same_length? b
46
+ zip_bytes(b) { |i, j| return false if i != j }
47
+ true
48
+ end
49
+
50
+ def hash ; @data.hash end
51
+
52
+ # xor self and b's ids component-wise
53
+ # @param [Array<Fixnum>] b
54
+ # @return [Id]
55
+ def xor(b)
56
+ r = []
57
+ zip_bytes(b) { |i,j| r << (i ^ j) }
58
+ Id.new r.length, false, r
59
+ end
60
+
61
+ def to_short_s
62
+ r = ''
63
+ each { |b| r << Id.two_char_hex_str(b.to_s(16)) }
64
+ r
65
+ end
66
+
67
+ def inspect ; to_s end
68
+
69
+ private
70
+
71
+ def zip_bytes(b)
72
+ a = self
73
+ 0.upto(length-1).each { |i| yield a[i], b[i] }
74
+ end
75
+
76
+ def self.rnd_byte ; Random.rand(256) end
77
+
78
+ def self.two_char_hex_str(s)
79
+ case s.length
80
+ when 0 then '00'
81
+ when 1 then "0#{s}"
82
+ else s
83
+ end
84
+ end
85
+ end
86
+
87
+ end
88
+
89
+ end
@@ -0,0 +1,172 @@
1
+ module Andromeda
2
+
3
+ module Kit
4
+
5
+ module Transf
6
+ attr_accessor :filter
7
+ attr_accessor :mapper
8
+
9
+ def deliver_data(name, meth, key, val, tags_in)
10
+ if signal_name?(name)
11
+ super name, meth, key, val, tags_in
12
+ else
13
+ filter_ = filter
14
+ mapper_ = mapper
15
+ if !(filter_ && filter_.call(val))
16
+ super name, meth, key, (if mapper_ then mapper_.call(val) else val end), tags_in
17
+ end
18
+ end
19
+ end
20
+ end
21
+
22
+ class Tee < Plan
23
+ attr_accessor :level
24
+ attr_accessor :other
25
+ attr_accessor :delay
26
+
27
+ def initialize(config = {})
28
+ config = { nick: config } unless config.is_a? Hash || config.is_a?(Spot)
29
+ config = { other: config } unless config.is_a? Hash
30
+ super config
31
+
32
+ @level ||= :info
33
+ end
34
+
35
+ def initialize_copy(other)
36
+ @level = other.level.identical_copy
37
+ @delay = other.delay.identical_copy
38
+ end
39
+
40
+ def on_enter(key, val)
41
+ log_ = log
42
+ level_ = level
43
+ sleep delay.to_i if delay
44
+ if log_ && level_
45
+ cur_name = current_name
46
+ key_str = Andromeda::Impl::To_S.short_s key
47
+ val_str = Andromeda::Impl::To_S.short_s val
48
+ log_str = "#{to_s}.#{cur_name}(#{key_str}, #{val_str})"
49
+ tags.each_pair { |k, v| log_str << " #{k}=#{Andromeda::Impl::To_S.short_s(v)}" }
50
+ log_str << " tid=0x#{Thread.current.object_id.to_s(16)}"
51
+ log_.send level, log_str
52
+ end
53
+ other_ = other
54
+ other_ << val if other_
55
+ super key, val
56
+ end
57
+ end
58
+
59
+ class Targeting < Plan
60
+ attr_accessor :targets
61
+
62
+ def initialize(config = {})
63
+ super config
64
+ @targets ||= {}
65
+ end
66
+
67
+ def target_values ; t = targets ; t.values rescue t end
68
+ end
69
+
70
+ class Broadc < Targeting
71
+ def on_enter(key, val)
72
+ target_values { |t| intern(t) << val rescue nil }
73
+ end
74
+ end
75
+
76
+ class Switch < Targeting
77
+ def on_enter(key, val)
78
+ (intern(key) rescue exit) << val rescue nil
79
+ end
80
+ end
81
+
82
+ class SinglePlan < Plan
83
+ def init_guide ; Guides::SinglePoolGuide.new end
84
+ end
85
+
86
+ class InlineKeyRouter < SinglePlan
87
+ def key_spot(name, key) ; key end
88
+ end
89
+
90
+ class Gatherer < SinglePlan
91
+ include Transf
92
+ end
93
+
94
+ class Reducer < Gatherer
95
+ attr_accessor :state
96
+ attr_accessor :reducer
97
+
98
+ attr_spot :new_state
99
+
100
+ def on_enter(key, val)
101
+ reducer_ = reducer
102
+
103
+ state_ = state
104
+ new_ = reducer_.call state_, key, val
105
+ unless new_ == state_
106
+ state = new_
107
+ new_state << state if new_state
108
+ end
109
+ end
110
+ end
111
+
112
+ class FileReader < Plan
113
+ attr_reader :path
114
+ attr_reader :mode
115
+
116
+ def initialize(config = {})
117
+ super config
118
+ @mode ||= init_mode
119
+ end
120
+
121
+ def data_tag(name, key, val, tags_in)
122
+ tags_out = super
123
+ tags_out[:first] = val.first rescue 0
124
+ tags_out[:last] = val.last rescue -1
125
+ tags_out
126
+ end
127
+
128
+ def init_mode ; 'r' end
129
+
130
+ protected
131
+
132
+ def on_enter(key, val)
133
+ file = File.open path, mode
134
+ begin
135
+ file.seek tags[:first]
136
+ tags[:last] = file.size - 1 if tags[:last] < 0
137
+ tags[:num] = tags[:last] - tags[:first]
138
+ if block_given? then yield file else super key, val end
139
+ ensure
140
+ file.close
141
+ end
142
+ end
143
+
144
+ end
145
+
146
+ class FileChunker < FileReader
147
+ attr_reader :num_chunks
148
+
149
+ def initialize(config = {})
150
+ super config
151
+ @num_chunks ||= Guides::PoolGuide.num_procs
152
+ end
153
+
154
+ def on_enter(key, val)
155
+ num_c = num_chunks
156
+ super key, val do |f|
157
+ fst = tags[:first]
158
+ lst = tags[:last]
159
+ sz = tags[:num] / num_c rescue 1
160
+ sz = 1 if sz < 0
161
+ while fst <= lst
162
+ nxt = fst + sz
163
+ nxt = lst if nxt > lst
164
+ exit << Range.new(fst, nxt)
165
+ fst = nxt + 1
166
+ end
167
+ end
168
+ end
169
+ end
170
+
171
+ end
172
+ end
@@ -0,0 +1,3 @@
1
+ module Andromeda
2
+ # unimplemented
3
+ end
@@ -0,0 +1,130 @@
1
+ module Andromeda
2
+
3
+ class Plan < Impl::ProtoPlan
4
+
5
+ meth_spot :enter
6
+ attr_spot :errors
7
+
8
+ signal_spot :errors
9
+
10
+ attr_accessor :log
11
+ attr_accessor :marker
12
+ attr_accessor :nick
13
+
14
+ attr_accessor :error_level
15
+
16
+ attr_accessor :trace_enter
17
+ attr_accessor :trace_exit
18
+
19
+
20
+ def initialize(config = {})
21
+ super config
22
+ @trace_enter ||= init_trace_hash :enter
23
+ @trace_exit ||= init_trace_hash :emit
24
+ @error_level ||= :error
25
+ end
26
+
27
+ def initialize_copy(other)
28
+ super other
29
+ @trace_enter = other.trace_enter.identical_copy
30
+ @trace_exit = other.trace_exit.identical_copy
31
+ @error_level = other.error_level.identical_copy
32
+ @nick = other.nick.identical_copy
33
+ end
34
+
35
+ def tap ; yield self end
36
+
37
+ def log ; @log = DefaultLogger.instance end
38
+ def mark ; @mark = Id.zero unless @mark ; @mark end
39
+
40
+ def on_enter(k, v)
41
+ exit_ = exit
42
+ exit_ << v if exit_
43
+ end
44
+
45
+ def to_short_s
46
+ super_ = super()
47
+ nick_ = nick
48
+ if nick_
49
+ then "#{super_} aka: #{Impl::To_S.short_s(nick_)}"
50
+ else super_ end
51
+ end
52
+
53
+ protected
54
+
55
+ def transport_data name, track, meth, key, val, tags_in
56
+ scope = tags_in[:scope]
57
+ enter_level = trace_level trace_enter, name
58
+ exit_level = trace_level trace_exit, name
59
+ details = { name: name, plan: self, track: track, key: key, val: val }
60
+ track.follow(scope) do
61
+ begin
62
+ trace :enter, enter_level, name, details if enter_level
63
+ deliver_data name, meth, key, val, tags_in
64
+ trace :exit, exit_level, name, details if exit_level
65
+ rescue Exception => e
66
+ uncaught_exception name, key, val, e
67
+ end
68
+ end
69
+ end
70
+
71
+ def init_trace_hash(kind) ; {} end
72
+
73
+ def trace_level(h, name) ; if h.is_a?(Symbol) then h else (h[name] rescue nil) end end
74
+
75
+ def trace(kind, level, name, details)
76
+ log.send level, InfoMsg.str("TRACE :#{kind} :#{name}", details) if log
77
+ end
78
+
79
+ def deliver_data(name, meth, k, v, tags_in)
80
+ update_mark
81
+ tags.update tags_in
82
+ meth.call k, v
83
+ end
84
+
85
+ def signal_error(e)
86
+ errors_ = errors
87
+ errors_ << e if errors_
88
+ end
89
+
90
+ def signal_uncaught(e)
91
+ log.send error_level, e rescue nil
92
+ signal_error e
93
+ end
94
+
95
+ def reset_mark(new_mark)
96
+ tags[:mark] = if new_mark then new_mark else Id.zero end
97
+ end
98
+
99
+ def mark ; tags[:mark] end
100
+
101
+ def check_mark
102
+ mark_ = mark
103
+ raise RuntimeError, 'Invalid mark' if mark_ && !mark_.zero?
104
+ end
105
+
106
+ def update_mark
107
+ marker_ = marker
108
+ if marker_ && !marker_.zero?
109
+ mark_ = mark
110
+ if mark_
111
+ then reset_mark mark_.xor(marker_)
112
+ else reset_mark mark_ end
113
+ end
114
+ end
115
+
116
+ def uncaught_exception(name, key, value, e)
117
+ begin
118
+ details = { name: name, key: key, val: value, cause: e }
119
+ info = InfoMsg.str 'Uncaught exception', details
120
+ err = ExecError.new(info)
121
+ err.set_backtrace e.backtrace
122
+ signal_uncaught e
123
+ rescue Exception => e2
124
+ details[:cause] = e2
125
+ log.error InfoMsg.str('Failure handling uncaught exception', details) rescue nil
126
+ end
127
+ end
128
+ end
129
+
130
+ end
@@ -0,0 +1,70 @@
1
+ module Andromeda
2
+
3
+ module Guides
4
+
5
+ class PoolGuide < Guide
6
+ attr_reader :max_procs
7
+ attr_reader :pool_track
8
+
9
+ # @return [Fixnum] number of processors as determined by Facter
10
+ def self.num_procs
11
+ @num_procs = Facter.sp_number_processors.strip.to_i unless defined?(@num_procs)
12
+ @num_procs
13
+ end
14
+
15
+ def initialize(num_procs = nil)
16
+ num_procs = PoolGuide.num_procs unless num_procs
17
+ raise ArgumentError unless num_procs.is_a?(Fixnum)
18
+ raise ArgumentError unless num_procs > 0
19
+ @max_procs = num_procs
20
+ @pool_track = PoolTrack.new ThreadPool.new(@max_procs)
21
+ end
22
+
23
+ def track(spot, label, suggested_track = nil)
24
+ return suggested_track if suggested_track
25
+ return @pool_track
26
+ end
27
+
28
+ def pack(plan, track, was_suggested = false)
29
+ return plan if plan.frozen?
30
+ return plan.identical_copy if was_suggested
31
+ if max_procs > 1 then plan.identical_copy else plan end
32
+ end
33
+ end
34
+
35
+ class PoolTrack
36
+ include DispatchingTrack
37
+
38
+ attr_reader :pool
39
+
40
+ def initialize(pool)
41
+ @pool = pool
42
+ end
43
+
44
+ protected
45
+
46
+ def process(&thunk)
47
+ # DefaultLogger.instance.info ":enter #{pool.inspect}"
48
+ pool.process &thunk
49
+ # DefaultLogger.instance.info ":exit #{pool.inspect}"
50
+ end
51
+ end
52
+
53
+ class SharedPoolGuide < PoolGuide
54
+ include Singleton
55
+
56
+ def initialize
57
+ super PoolGuide.num_procs
58
+ end
59
+ end
60
+
61
+ class SinglePoolGuide < PoolGuide
62
+ def initialize ; super 1 end
63
+ end
64
+
65
+ class SharedSinglePoolGuide < SinglePoolGuide
66
+ include Singleton
67
+ end
68
+
69
+ end
70
+ end