flor 0.0.1 → 0.9.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.
- data/CHANGELOG.md +13 -0
- data/LICENSE.txt +1 -1
- data/Makefile +66 -0
- data/README.md +57 -0
- data/fail.txt +7 -0
- data/flor.gemspec +12 -9
- data/intercepted.txt +123 -0
- data/lib/flor/colours.rb +140 -0
- data/lib/flor/conf.rb +88 -0
- data/lib/flor/core/executor.rb +473 -0
- data/lib/flor/core/node.rb +397 -0
- data/lib/flor/core/procedure.rb +600 -0
- data/lib/flor/core/texecutor.rb +209 -0
- data/lib/flor/core.rb +93 -0
- data/lib/flor/dollar.rb +248 -0
- data/lib/flor/errors.rb +36 -0
- data/lib/flor/flor.rb +556 -0
- data/lib/flor/log.rb +336 -0
- data/lib/flor/migrations/0001_tables.rb +122 -0
- data/lib/flor/parser.rb +414 -0
- data/lib/flor/pcore/_arr.rb +49 -0
- data/lib/flor/pcore/_atom.rb +43 -0
- data/lib/flor/pcore/_att.rb +160 -0
- data/lib/flor/pcore/_dump.rb +60 -0
- data/lib/flor/pcore/_err.rb +30 -0
- data/lib/flor/pcore/_happly.rb +73 -0
- data/lib/flor/pcore/_obj.rb +65 -0
- data/lib/flor/pcore/_skip.rb +63 -0
- data/lib/flor/pcore/apply.rb +60 -0
- data/lib/flor/pcore/arith.rb +46 -0
- data/lib/flor/pcore/break.rb +71 -0
- data/lib/flor/pcore/cmp.rb +72 -0
- data/lib/flor/pcore/cond.rb +57 -0
- data/lib/flor/pcore/cursor.rb +223 -0
- data/lib/flor/pcore/define.rb +96 -0
- data/lib/flor/pcore/fail.rb +45 -0
- data/lib/flor/pcore/ife.rb +56 -0
- data/lib/flor/pcore/loop.rb +53 -0
- data/lib/flor/pcore/map.rb +75 -0
- data/lib/flor/pcore/match.rb +70 -0
- data/lib/flor/pcore/move.rb +65 -0
- data/lib/flor/pcore/noeval.rb +46 -0
- data/lib/flor/pcore/noret.rb +47 -0
- data/lib/flor/pcore/push.rb +69 -0
- data/lib/flor/pcore/sequence.rb +39 -0
- data/lib/flor/pcore/set.rb +76 -0
- data/lib/flor/pcore/stall.rb +35 -0
- data/lib/flor/pcore/until.rb +122 -0
- data/lib/flor/pcore/val.rb +40 -0
- data/lib/flor/punit/cancel.rb +69 -0
- data/lib/flor/punit/cmap.rb +76 -0
- data/lib/flor/punit/concurrence.rb +149 -0
- data/lib/flor/punit/every.rb +46 -0
- data/lib/flor/punit/on.rb +81 -0
- data/lib/flor/punit/schedule.rb +68 -0
- data/lib/flor/punit/signal.rb +47 -0
- data/lib/flor/punit/sleep.rb +53 -0
- data/lib/flor/punit/task.rb +109 -0
- data/lib/flor/punit/trace.rb +51 -0
- data/lib/flor/punit/trap.rb +100 -0
- data/lib/flor/to_string.rb +81 -0
- data/lib/flor/tools/env.rb +103 -0
- data/lib/flor/tools/repl.rb +231 -0
- data/lib/flor/unit/executor.rb +260 -0
- data/lib/flor/unit/hooker.rb +186 -0
- data/lib/flor/unit/journal.rb +52 -0
- data/lib/flor/unit/loader.rb +181 -0
- data/lib/flor/unit/logger.rb +181 -0
- data/lib/flor/unit/models/execution.rb +105 -0
- data/lib/flor/unit/models/pointer.rb +31 -0
- data/lib/flor/unit/models/timer.rb +52 -0
- data/lib/flor/unit/models/trace.rb +31 -0
- data/lib/flor/unit/models/trap.rb +130 -0
- data/lib/flor/unit/models.rb +106 -0
- data/lib/flor/unit/scheduler.rb +419 -0
- data/lib/flor/unit/storage.rb +633 -0
- data/lib/flor/unit/tasker.rb +191 -0
- data/lib/flor/unit/waiter.rb +146 -0
- data/lib/flor/unit/wlist.rb +77 -0
- data/lib/flor/unit.rb +50 -0
- data/lib/flor.rb +40 -3
- metadata +152 -22
- checksums.yaml +0 -7
- data/Rakefile +0 -52
@@ -0,0 +1,473 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2015-2017, John Mettraux, jmettraux+flor@gmail.com
|
3
|
+
#
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
5
|
+
# of this software and associated documentation files (the "Software"), to deal
|
6
|
+
# in the Software without restriction, including without limitation the rights
|
7
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
8
|
+
# copies of the Software, and to permit persons to whom the Software is
|
9
|
+
# furnished to do so, subject to the following conditions:
|
10
|
+
#
|
11
|
+
# The above copyright notice and this permission notice shall be included in
|
12
|
+
# all copies or substantial portions of the Software.
|
13
|
+
#
|
14
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
15
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
16
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
17
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
18
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
19
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
20
|
+
# THE SOFTWARE.
|
21
|
+
#
|
22
|
+
# Made in Japan.
|
23
|
+
#++
|
24
|
+
|
25
|
+
|
26
|
+
module Flor
|
27
|
+
|
28
|
+
class Executor
|
29
|
+
|
30
|
+
attr_reader :unit
|
31
|
+
attr_reader :execution
|
32
|
+
attr_reader :traps
|
33
|
+
|
34
|
+
def initialize(unit, traps, execution)
|
35
|
+
|
36
|
+
@unit = unit
|
37
|
+
@execution = execution
|
38
|
+
@traps = traps
|
39
|
+
end
|
40
|
+
|
41
|
+
def conf; @unit.conf; end
|
42
|
+
def exid; @execution['exid']; end
|
43
|
+
|
44
|
+
def node(msg_or_nid, node_instance=false)
|
45
|
+
|
46
|
+
return nil unless msg_or_nid
|
47
|
+
|
48
|
+
nid = msg_or_nid
|
49
|
+
msg = msg_or_nid
|
50
|
+
#
|
51
|
+
if nid.is_a?(String)
|
52
|
+
msg = nil
|
53
|
+
else
|
54
|
+
nid = msg['nid']
|
55
|
+
end
|
56
|
+
|
57
|
+
n = @execution['nodes'][nid]
|
58
|
+
|
59
|
+
return nil unless n
|
60
|
+
node_instance ? Flor::Node.new(self, n, msg) : n
|
61
|
+
end
|
62
|
+
|
63
|
+
def counter(key)
|
64
|
+
|
65
|
+
@execution['counters'][key.to_s] || -1
|
66
|
+
end
|
67
|
+
|
68
|
+
def counter_add(key, count)
|
69
|
+
|
70
|
+
k = key.to_s
|
71
|
+
|
72
|
+
@execution['counters'][k] ||= 0
|
73
|
+
@execution['counters'][k] += count
|
74
|
+
end
|
75
|
+
|
76
|
+
def counter_next(key)
|
77
|
+
|
78
|
+
counter_add(key, 1)
|
79
|
+
end
|
80
|
+
|
81
|
+
def trigger_hook(hook, message)
|
82
|
+
|
83
|
+
hook.notify(self, message)
|
84
|
+
end
|
85
|
+
|
86
|
+
def trigger_trap(trap, message)
|
87
|
+
|
88
|
+
del, msgs = trap.trigger(self, message)
|
89
|
+
@traps.delete(trap) if del
|
90
|
+
|
91
|
+
msgs
|
92
|
+
end
|
93
|
+
|
94
|
+
# Given a nid, returns a copy of all the var the node sees
|
95
|
+
#
|
96
|
+
def vars(nid, vs={})
|
97
|
+
|
98
|
+
n = node(nid); return vs unless n
|
99
|
+
|
100
|
+
(n['vars'] || {})
|
101
|
+
.each { |k, v| vs[k] = Flor.dup(v) unless vs.has_key?(k) }
|
102
|
+
|
103
|
+
if @unit.loader && n['parent'] == nil && n['vdomain'] != false
|
104
|
+
|
105
|
+
@unit.loader.variables(n['vdomain'] || Flor.domain(@exid))
|
106
|
+
.each { |k, v| vs[k] = Flor.dup(v) unless vs.has_key?(k) }
|
107
|
+
end
|
108
|
+
|
109
|
+
if cn = n['cnid']; vars(cn, vs); end
|
110
|
+
if pa = n['parent']; vars(pa, vs); end
|
111
|
+
|
112
|
+
vs
|
113
|
+
end
|
114
|
+
|
115
|
+
protected
|
116
|
+
|
117
|
+
def make_node(message)
|
118
|
+
|
119
|
+
nid = message['nid']
|
120
|
+
|
121
|
+
now = Flor.tstamp
|
122
|
+
|
123
|
+
node = {
|
124
|
+
'nid' => nid,
|
125
|
+
'parent' => message['from'],
|
126
|
+
'payload' => message['payload'],
|
127
|
+
'status' => [ { 'status' => nil, 'point' => 'execute', 'ctime' => now } ],
|
128
|
+
'ctime' => now,
|
129
|
+
'mtime' => now }
|
130
|
+
|
131
|
+
%w[ vars vdomain cnid noreply dbg ].each do |k|
|
132
|
+
v = message[k]
|
133
|
+
node[k] = v if v != nil
|
134
|
+
end
|
135
|
+
#
|
136
|
+
# vars: variables
|
137
|
+
# vdomain: variable domain (used in conjuction with the loader)
|
138
|
+
# cnid: closure nid
|
139
|
+
# noreply: this new node has a parent but shouldn't reply to it
|
140
|
+
# dbg: used to debug messages (useful @node['dbg'] when 'receive')
|
141
|
+
|
142
|
+
@execution['nodes'][nid] = node
|
143
|
+
end
|
144
|
+
|
145
|
+
def determine_heat(message)
|
146
|
+
|
147
|
+
nid = message['nid']
|
148
|
+
|
149
|
+
return unless nid
|
150
|
+
|
151
|
+
node =
|
152
|
+
message['point'] == 'execute' ?
|
153
|
+
make_node(message) :
|
154
|
+
@execution['nodes'][nid]
|
155
|
+
|
156
|
+
return unless node
|
157
|
+
|
158
|
+
return if node['heat']
|
159
|
+
|
160
|
+
n = Flor::Node.new(self, node, message)
|
161
|
+
|
162
|
+
mt = message['tree']
|
163
|
+
mt = [ 'noeval', [ 0 ], mt[2] ] \
|
164
|
+
if mt[0] == '_' && Flor.is_array_of_trees?(mt[1])
|
165
|
+
|
166
|
+
nt = n.lookup_tree(nid)
|
167
|
+
|
168
|
+
node['tree'] = mt if mt && (mt != nt)
|
169
|
+
tree = node['tree'] || nt
|
170
|
+
|
171
|
+
t0 = tree[0]
|
172
|
+
t0 = (t0.is_a?(Array) && t0[0] == '_dqs') ? n.expand(t0[1]) : t0
|
173
|
+
|
174
|
+
node['heat0'] = tree[0]
|
175
|
+
node['heat'] = heat = n.deref(t0)
|
176
|
+
|
177
|
+
node['heap'] = heap =
|
178
|
+
if ! heat.is_a?(Array)
|
179
|
+
'_val'
|
180
|
+
elsif tree && tree[1] == []
|
181
|
+
'_val'
|
182
|
+
elsif heat[0] == '_proc'
|
183
|
+
heat[1]['proc']
|
184
|
+
elsif heat[0] == '_func'
|
185
|
+
'apply'
|
186
|
+
elsif heat[0] == '_task'
|
187
|
+
'task'
|
188
|
+
elsif Flor.is_tree_head_tree?(tree)
|
189
|
+
'_happly'
|
190
|
+
else
|
191
|
+
'_val'
|
192
|
+
end
|
193
|
+
|
194
|
+
if heap == 'task' && heat[0] == '_task'
|
195
|
+
#
|
196
|
+
# rewrite `alpha` into `task alpha`
|
197
|
+
|
198
|
+
l = message['tree'][2]
|
199
|
+
|
200
|
+
message['otree'] = Flor.dup(message['tree'])
|
201
|
+
|
202
|
+
message['tree'][0] =
|
203
|
+
'task'
|
204
|
+
message['tree'][1].unshift(
|
205
|
+
[ '_att', [ [ '_sqs', heat[1]['task'], l ] ], l ])
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
def execute(message)
|
210
|
+
|
211
|
+
apply(@execution['nodes'][message['nid']], message)
|
212
|
+
end
|
213
|
+
|
214
|
+
def apply(node, message)
|
215
|
+
|
216
|
+
heap =
|
217
|
+
if node['heat']
|
218
|
+
node['heap']
|
219
|
+
else
|
220
|
+
node['failure'] ? '_err' : nil
|
221
|
+
end
|
222
|
+
|
223
|
+
return ([{
|
224
|
+
'point' => 'receive',
|
225
|
+
'nid' => message['from'], 'from' => message['nid'],
|
226
|
+
'exid' => message['exid'],
|
227
|
+
'payload' => Flor.dupm(message['payload'], 'ret' => node['heat0'])
|
228
|
+
}]) if heap == nil && message['accept_symbol'] == true
|
229
|
+
|
230
|
+
return error_reply(
|
231
|
+
node, message, "don't know how to apply #{node['heat0'].inspect}"
|
232
|
+
) if heap == nil
|
233
|
+
|
234
|
+
heac = Flor::Procedure[heap]
|
235
|
+
fail NameError.new("unknown procedure #{heap.inspect}") unless heac
|
236
|
+
|
237
|
+
head = heac.new(self, node, message)
|
238
|
+
|
239
|
+
#return process(head.rewrite) if head.is_a?(Flor::Macro)
|
240
|
+
return [ head.rewrite ] if head.is_a?(Flor::Macro)
|
241
|
+
|
242
|
+
nid = message['nid']
|
243
|
+
pt = message['point']
|
244
|
+
pt = "do_#{pt}" if pt == 'receive' || pt == 'cancel'
|
245
|
+
|
246
|
+
if pt == 'execute'
|
247
|
+
head.pre_execute
|
248
|
+
pnode = @execution['nodes'][node['parent']]
|
249
|
+
cnodes = pnode && (pnode['cnodes'] ||= [])
|
250
|
+
cnodes << nid if cnodes && ( ! cnodes.include?(nid))
|
251
|
+
end
|
252
|
+
head.send(pt)
|
253
|
+
end
|
254
|
+
|
255
|
+
def remove_node(n)
|
256
|
+
|
257
|
+
return unless n
|
258
|
+
|
259
|
+
n['removed'] = true # or should I use "status" => "removed" ?
|
260
|
+
|
261
|
+
@unit.remove_node(exid, n)
|
262
|
+
# remove timers/waiters for this node, if any
|
263
|
+
|
264
|
+
return if (n['closures'] || []).any?
|
265
|
+
# don't remove the node if it's a closure for some other nodes
|
266
|
+
|
267
|
+
nid = n['nid']
|
268
|
+
|
269
|
+
return if nid == '0'
|
270
|
+
# don't remove if it's the "root" node
|
271
|
+
|
272
|
+
@execution['nodes'].delete(nid)
|
273
|
+
end
|
274
|
+
|
275
|
+
def leave(node, message)
|
276
|
+
|
277
|
+
ts = node && node['tags']
|
278
|
+
return [] unless ts && ts.any?
|
279
|
+
|
280
|
+
[
|
281
|
+
{ 'point' => 'left',
|
282
|
+
'tags' => ts,
|
283
|
+
'exid' => exid,
|
284
|
+
'nid' => node['nid'],
|
285
|
+
'payload' => message['payload'] }
|
286
|
+
]
|
287
|
+
end
|
288
|
+
|
289
|
+
# "receive_terminated_or_ceased"
|
290
|
+
#
|
291
|
+
def receive_toc(message, fnode)
|
292
|
+
|
293
|
+
msg =
|
294
|
+
%w[
|
295
|
+
exid nid from payload
|
296
|
+
].inject({}) { |h, k| h[k] = message[k] if message.has_key?(k); h }
|
297
|
+
|
298
|
+
msg['sm'] = message['m']
|
299
|
+
|
300
|
+
msg['point'] =
|
301
|
+
if message['from'] == '0' || @execution['nodes'].empty? # termination?
|
302
|
+
'terminated'
|
303
|
+
else
|
304
|
+
'ceased'
|
305
|
+
end
|
306
|
+
|
307
|
+
[ msg ]
|
308
|
+
end
|
309
|
+
|
310
|
+
def receive(message)
|
311
|
+
|
312
|
+
from = message['from']
|
313
|
+
fnode = @execution['nodes'][from]
|
314
|
+
|
315
|
+
if fnode && fnode.has_key?('aret')
|
316
|
+
message['payload']['ret'] = fnode['aret']
|
317
|
+
end
|
318
|
+
|
319
|
+
remove_node(fnode)
|
320
|
+
messages = leave(fnode, message)
|
321
|
+
|
322
|
+
nid = message['nid']
|
323
|
+
nid = nil if fnode && fnode['noreply']
|
324
|
+
|
325
|
+
return messages + receive_toc(message, fnode) unless nid
|
326
|
+
|
327
|
+
node = @execution['nodes'][nid]
|
328
|
+
|
329
|
+
return messages unless node
|
330
|
+
|
331
|
+
messages + apply(node, message)
|
332
|
+
end
|
333
|
+
|
334
|
+
def error_reply(node, message, err)
|
335
|
+
|
336
|
+
m = message
|
337
|
+
.select { |k, v| %w[ sm exid nid from payload tree ].include?(k) }
|
338
|
+
|
339
|
+
m['point'] = 'failed'
|
340
|
+
m['fpoint'] = message['point']
|
341
|
+
m['error'] = Flor.to_error(err)
|
342
|
+
|
343
|
+
Flor.detail_msg(self, m, flag: true) if @unit.conf['log_err']
|
344
|
+
|
345
|
+
#if m['error']['msg'].match(/\AToo many open files in system/)
|
346
|
+
# puts "=" * 80 + ' ...'
|
347
|
+
# system(`lsof #{Process.pid}`)
|
348
|
+
# puts "=" * 80 + ' .'
|
349
|
+
#end
|
350
|
+
#
|
351
|
+
# can't seem to provoke that error, so keeping the trap
|
352
|
+
# around but commented out...
|
353
|
+
|
354
|
+
[ m ]
|
355
|
+
end
|
356
|
+
|
357
|
+
def task(message)
|
358
|
+
|
359
|
+
@execution['tasks'][message['nid']] =
|
360
|
+
{ 'tasker' => message['tasker'], 'name' => message['taskname'] }
|
361
|
+
|
362
|
+
@unit.tasker.task(message)
|
363
|
+
end
|
364
|
+
alias detask task
|
365
|
+
|
366
|
+
def return(message)
|
367
|
+
|
368
|
+
@execution['tasks'].delete(message['nid'])
|
369
|
+
|
370
|
+
[
|
371
|
+
{ 'point' => 'receive',
|
372
|
+
'exid' => message['exid'],
|
373
|
+
'nid' => message['nid'],
|
374
|
+
'payload' => message['payload'],
|
375
|
+
'tasker' => message['tasker'] }
|
376
|
+
]
|
377
|
+
end
|
378
|
+
|
379
|
+
def cancel(message)
|
380
|
+
|
381
|
+
if n = @execution['nodes'][message['nid']]
|
382
|
+
apply(n, message)
|
383
|
+
else
|
384
|
+
[] # nothing, node gone
|
385
|
+
end
|
386
|
+
end
|
387
|
+
|
388
|
+
def process(message)
|
389
|
+
|
390
|
+
begin
|
391
|
+
|
392
|
+
message['m'] = counter_next('msgs') # number messages
|
393
|
+
message['pr'] = counter('runs') # "processing run"
|
394
|
+
|
395
|
+
determine_heat(message)
|
396
|
+
|
397
|
+
ms = []
|
398
|
+
ms += @unit.notify(self, message) # pre
|
399
|
+
|
400
|
+
ms += self.send(message['point'].to_sym, message)
|
401
|
+
|
402
|
+
message['payload'] = message.delete('pld') if message.has_key?('pld')
|
403
|
+
message['consumed'] = Flor.tstamp
|
404
|
+
|
405
|
+
ms += @unit.notify(self, message) # post
|
406
|
+
|
407
|
+
ms.each { |m| m['er'] = counter('runs') } # "emitting run"
|
408
|
+
|
409
|
+
rescue => e
|
410
|
+
error_reply(nil, message, e)
|
411
|
+
rescue ScriptError => se
|
412
|
+
error_reply(nil, message, se)
|
413
|
+
end
|
414
|
+
end
|
415
|
+
|
416
|
+
def trap(message)
|
417
|
+
|
418
|
+
exid = message['exid']
|
419
|
+
nid = message['nid']
|
420
|
+
trap = message['trap']
|
421
|
+
|
422
|
+
nd = node(nid)
|
423
|
+
nd['exid'] = exid
|
424
|
+
|
425
|
+
@traps << @unit.trap(nd, trap)
|
426
|
+
|
427
|
+
[]
|
428
|
+
end
|
429
|
+
|
430
|
+
def entered(message); []; end
|
431
|
+
def left(message); []; end
|
432
|
+
|
433
|
+
def ceased(message); []; end
|
434
|
+
|
435
|
+
def terminated(message)
|
436
|
+
|
437
|
+
message['vars'] = @execution['nodes']['0']['vars']
|
438
|
+
# especially useful for debugging
|
439
|
+
|
440
|
+
[]
|
441
|
+
end
|
442
|
+
|
443
|
+
|
444
|
+
def failed(message)
|
445
|
+
|
446
|
+
n = node(message['nid'])
|
447
|
+
|
448
|
+
fail RuntimeError.new(
|
449
|
+
"node #{message['nid']} is gone, cannot flag it as failed"
|
450
|
+
) unless n
|
451
|
+
|
452
|
+
#begin
|
453
|
+
n['failure'] = Flor.dup(message)
|
454
|
+
#rescue; pp message; exit 0; end
|
455
|
+
|
456
|
+
oep = lookup_on_error_parent(message)
|
457
|
+
return oep.trigger_on_error if oep
|
458
|
+
|
459
|
+
Flor.detail_msg(self, message) if @unit.conf['log_err']
|
460
|
+
|
461
|
+
[]
|
462
|
+
end
|
463
|
+
|
464
|
+
def signal(message); []; end
|
465
|
+
|
466
|
+
def lookup_on_error_parent(message)
|
467
|
+
|
468
|
+
nd = Flor::Node.new(self, nil, message).on_error_parent
|
469
|
+
nd ? nd.to_procedure : nil
|
470
|
+
end
|
471
|
+
end
|
472
|
+
end
|
473
|
+
|