@naanlang/naan 1.0.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.
- package/LICENSE.md +21 -0
- package/README.md +46 -0
- package/dist/env_web.js +431 -0
- package/dist/naan.min.js +73 -0
- package/frameworks/browser/browser.nlg +30 -0
- package/frameworks/browser/pouchdb/pouchdb.min.js +7 -0
- package/frameworks/browser/spark-md5/spark-md5.min.js +1 -0
- package/frameworks/browser/sworker.js +192 -0
- package/frameworks/browser/terminals.nlg +787 -0
- package/frameworks/browser/workers.nlg +386 -0
- package/frameworks/client/apiclient.nlg +183 -0
- package/frameworks/client/client.nlg +29 -0
- package/frameworks/client/psm_client.nlg +572 -0
- package/frameworks/common/common.nlg +464 -0
- package/frameworks/common/preferences.nlg +131 -0
- package/frameworks/common/repltools.nlg +100 -0
- package/frameworks/common/watching.nlg +97 -0
- package/frameworks/node/apiserver.nlg +399 -0
- package/frameworks/node/filesystem.nlg +1012 -0
- package/frameworks/node/gitter.nlg +234 -0
- package/frameworks/node/node.nlg +81 -0
- package/frameworks/node/psm_server.nlg +465 -0
- package/frameworks/node/worker.nlg +560 -0
- package/frameworks/project/build.nlg +1229 -0
- package/frameworks/project/jszip.js +30 -0
- package/frameworks/project/jszip.nlg +87 -0
- package/frameworks/project/proj_cliser.nlg +139 -0
- package/frameworks/project/proj_console.nlg +137 -0
- package/frameworks/project/proj_folder.nlg +143 -0
- package/frameworks/project/proj_lambda.nlg +137 -0
- package/frameworks/project/proj_static.nlg +138 -0
- package/frameworks/project/project.nlg +29 -0
- package/frameworks/project/projects.nlg +587 -0
- package/frameworks/project/uglifyjs.js +1 -0
- package/frameworks/project/uglifyjs.nlg +79 -0
- package/frameworks/running/debugcom.nlg +163 -0
- package/frameworks/running/debugnub.nlg +565 -0
- package/frameworks/running/debugutil.nlg +510 -0
- package/frameworks/running/executors.nlg +767 -0
- package/frameworks/running/running.nlg +151 -0
- package/frameworks/running/sourcecode.nlg +346 -0
- package/frameworks/running/taskexec.nlg +196 -0
- package/frameworks/storage/dbt_pouch.nlg +773 -0
- package/frameworks/storage/file_manager.nlg +772 -0
- package/frameworks/storage/psm.nlg +326 -0
- package/frameworks/storage/psm_dbtables.nlg +814 -0
- package/frameworks/storage/resources.nlg +445 -0
- package/frameworks/storage/storage.nlg +52 -0
- package/lib/browser/env_web.js +431 -0
- package/lib/browser/env_webworker.js +146 -0
- package/lib/browser/require.js +1653 -0
- package/lib/core/naanlib.js +73 -0
- package/lib/env_node.js +282 -0
- package/lib/env_nodeworker.js +165 -0
- package/lib/node_repl.js +25 -0
- package/lib/node_repl_init.nlg +52 -0
- package/package.json +13 -0
- package/plugins/gitClient/clientops.nlg +317 -0
- package/plugins/gitClient/gitClient.nlg +63 -0
- package/plugins/gitClient/gitutils.nlg +1331 -0
- package/plugins/gitClient/minimatch.js +945 -0
- package/plugins/gitClient/minimatch_license.txt +15 -0
- package/plugins/gitClient/minimatch_readme.md +208 -0
- package/plugins/nodeLib/nodeLib.nlg +26 -0
- package/plugins/nodeLib/node_cp.nlg +65 -0
- package/plugins/nodeLib/node_fs.nlg +137 -0
- package/plugins/nodeLib/node_https.nlg +172 -0
- package/plugins/nodeLib/node_util.nlg +180 -0
- package/plugins/serviceAws/aws/aws-sdk.min.js +88 -0
- package/plugins/serviceAws/aws/lambda_rest_index.js +122 -0
- package/plugins/serviceAws/aws/lambda_rest_init.nlg +58 -0
- package/plugins/serviceAws/aws/lambda_ws_index.js +498 -0
- package/plugins/serviceAws/aws/lambda_ws_init.nlg +49 -0
- package/plugins/serviceAws/aws_cloudwatchlogs.nlg +122 -0
- package/plugins/serviceAws/aws_cognito.nlg +76 -0
- package/plugins/serviceAws/aws_dynamo.nlg +509 -0
- package/plugins/serviceAws/aws_lambda.nlg +95 -0
- package/plugins/serviceAws/aws_s3.nlg +134 -0
- package/plugins/serviceAws/aws_utils.nlg +86 -0
- package/plugins/serviceAws/dbt_aws.nlg +769 -0
- package/plugins/serviceAws/lambda_naan_control.nlg +175 -0
- package/plugins/serviceAws/psm_aws.nlg +258 -0
- package/plugins/serviceAws/serviceAws.nlg +38 -0
- package/plugins/serviceGitHub/gitops.nlg +522 -0
- package/plugins/serviceGitHub/psm_github.nlg +205 -0
- package/plugins/serviceGitHub/serviceGitHub.nlg +65 -0
- package/plugins/serviceGitLab/gitops.nlg +528 -0
- package/plugins/serviceGitLab/psm_gitlab.nlg +205 -0
- package/plugins/serviceGitLab/serviceGitLab.nlg +65 -0
- package/test/harness.nlg +312 -0
- package/test/node_test.js +33 -0
- package/test/test_01_core.nlg +768 -0
- package/test/test_02_context.nlg +269 -0
- package/test/test_03_jsinterop.nlg +137 -0
- package/test/test_04_numerics.nlg +450 -0
- package/test/test_05_strings.nlg +63 -0
- package/test/test_06_lingoparse.nlg +500 -0
- package/test/test_07_lingo.nlg +623 -0
|
@@ -0,0 +1,767 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* executors.nlg
|
|
3
|
+
*
|
|
4
|
+
* Execution resource tracker.
|
|
5
|
+
*
|
|
6
|
+
* column positioning: // // !
|
|
7
|
+
*
|
|
8
|
+
* Copyright (c) 2020-2021 by Richard C. Zulch
|
|
9
|
+
*
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
/*
|
|
14
|
+
* ExecutorBase
|
|
15
|
+
*
|
|
16
|
+
* A base class for executor instances with the common code.
|
|
17
|
+
*
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
closure ExecutorBase(track, type, name, local exec) {
|
|
21
|
+
exec = new(object, this)
|
|
22
|
+
exec.type = type
|
|
23
|
+
exec.name = name
|
|
24
|
+
|
|
25
|
+
// execReset
|
|
26
|
+
// Initialize remote communications for a new or restarted instance.
|
|
27
|
+
|
|
28
|
+
exec.execReset = function execReset() {
|
|
29
|
+
exec.pending = { }
|
|
30
|
+
exec.nextSendID = toint(RandomBytes(3)) // keep our sequence code unique for shared servers
|
|
31
|
+
exec.nextContextID = 1
|
|
32
|
+
}
|
|
33
|
+
execReset()
|
|
34
|
+
|
|
35
|
+
// execSend
|
|
36
|
+
// Send a message to the running instance and return a nonce to wait on. If data is not a
|
|
37
|
+
// dictionary then it is ignored.
|
|
38
|
+
|
|
39
|
+
exec.execSend = closure execSend(msg, data, local pending) {
|
|
40
|
+
if !dictionary(data)
|
|
41
|
+
data = { }
|
|
42
|
+
data.xmsg = msg
|
|
43
|
+
data.xid = exec.nextSendID++
|
|
44
|
+
pending = new(nonce)
|
|
45
|
+
pending.msgout = data
|
|
46
|
+
exec.pending[data.xid] = pending
|
|
47
|
+
exec.PostMessage(data)
|
|
48
|
+
pending
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// execReceived
|
|
52
|
+
// THe specified message was received from the instance, so signal it.
|
|
53
|
+
|
|
54
|
+
exec.execReceived = closure execReceived(data, local pending) {
|
|
55
|
+
pending = exec.pending[data.xid]
|
|
56
|
+
if !pending {
|
|
57
|
+
if data.xmsg == "xready"
|
|
58
|
+
exec.pending.ready = true
|
|
59
|
+
return
|
|
60
|
+
}
|
|
61
|
+
exec.pending[data.xid] = undefined
|
|
62
|
+
pending.signal(list(false, data))
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// execClosed
|
|
66
|
+
// Note that the connection has closed, so signal all the waiters.
|
|
67
|
+
|
|
68
|
+
exec.execClosed = closure execClosed(error, local xid) {
|
|
69
|
+
for `(xid, pending) in exec.pending {
|
|
70
|
+
pending.signal(list(error))
|
|
71
|
+
pending[xid] = undefined
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// context
|
|
76
|
+
// Return a context object for the instance, or an error, in a result tuple. Currently this does
|
|
77
|
+
// not ever reclaim old contexts on the instance end of things, but it should. ###
|
|
78
|
+
|
|
79
|
+
exec.context = closure context(local context, retries, ms) {
|
|
80
|
+
retries = 0
|
|
81
|
+
ms = milliseconds()
|
|
82
|
+
while !exec.pending.ready {
|
|
83
|
+
exec.PostMessage({
|
|
84
|
+
xmsg: "xstart"
|
|
85
|
+
})
|
|
86
|
+
if ++retries > 100
|
|
87
|
+
return (list(Error("timeout creating remote worker context", type, name)))
|
|
88
|
+
sleep(100) }
|
|
89
|
+
context = ExecutorContext(exec, exec.nextContextID++)
|
|
90
|
+
context.register()
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// attention
|
|
94
|
+
// Focus the UI on this instance because something needs attention.
|
|
95
|
+
|
|
96
|
+
exec.attention = function attention() {
|
|
97
|
+
track.attention(exec)
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// finis
|
|
101
|
+
exec
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
/*
|
|
106
|
+
* ExecutorContext
|
|
107
|
+
*
|
|
108
|
+
* Contexts for remote execution. Remote evaluation requires a namespace context, but there is no
|
|
109
|
+
* easy, general way to specify the context between instances by default. This presents a challenge
|
|
110
|
+
* for accessing different modules, but can be even more difficult when arbitrary namespaces are
|
|
111
|
+
* being created.
|
|
112
|
+
* Execution contexts provide a means for remote evaluation in a specific context, including the
|
|
113
|
+
* ability to track namespace changes as the result of evaluating an expression. They do not override
|
|
114
|
+
* normal namespace protections. The contexts exposed here preserve the active namespace after the
|
|
115
|
+
* expression is evaluated.
|
|
116
|
+
*
|
|
117
|
+
*/
|
|
118
|
+
|
|
119
|
+
closure ExecutorContext(exec, contextID, local context, util) {
|
|
120
|
+
context = new(object, this)
|
|
121
|
+
util = modlist().Util.exports
|
|
122
|
+
|
|
123
|
+
// register
|
|
124
|
+
// Register or re-register our context with the worker.
|
|
125
|
+
|
|
126
|
+
context.register = closure register(local pending, result) {
|
|
127
|
+
pending = exec.execSend("register", {
|
|
128
|
+
contextID: contextID
|
|
129
|
+
})
|
|
130
|
+
result = pending.wait()
|
|
131
|
+
if result.1
|
|
132
|
+
list(false, context)
|
|
133
|
+
else
|
|
134
|
+
result
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// eval
|
|
138
|
+
// Evaluate the specified expression in the current worker context. The optional put argument
|
|
139
|
+
// if non-false is a tuple of symbols that should be transfered to the remote end, while get is
|
|
140
|
+
// an optional tuple of symbols to retrieve after evaluation.
|
|
141
|
+
|
|
142
|
+
context.eval = closure evalf(expr, put, get, local roots, pkg, data, pending, result) {
|
|
143
|
+
if tuple(put)
|
|
144
|
+
roots = cons(`expr, put)
|
|
145
|
+
else
|
|
146
|
+
roots = `(expr)
|
|
147
|
+
pkg = util.pkgSave(`expr, roots, {
|
|
148
|
+
nsignore: true
|
|
149
|
+
comments: true })
|
|
150
|
+
data = {
|
|
151
|
+
contextID: contextID
|
|
152
|
+
expr: pkg }
|
|
153
|
+
if tuple(get)
|
|
154
|
+
data.get = get
|
|
155
|
+
pending = exec.execSend("eval", data)
|
|
156
|
+
result = pending.wait()
|
|
157
|
+
if result.1.error
|
|
158
|
+
list(Error(result.1.error))
|
|
159
|
+
else if result.1.result
|
|
160
|
+
list(false, caar(util.pkgLoad(result.1.result, {
|
|
161
|
+
localroots: false
|
|
162
|
+
intern: false
|
|
163
|
+
execute: false
|
|
164
|
+
})))
|
|
165
|
+
else
|
|
166
|
+
result
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// evalq
|
|
170
|
+
// Evaluate the specified QUOTED expression in the current worker context.
|
|
171
|
+
|
|
172
|
+
context.evalq = macro evalq(expr, put, get) {
|
|
173
|
+
evalf(expr, eval(put), eval(get))
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// rpc
|
|
177
|
+
// Execute a remote procedure call without package translation, to reduce overhead. This will not
|
|
178
|
+
// properly transfer Naan-specific types like symbols, and the results will be native JavaScript.
|
|
179
|
+
// The procedure must be specified as a symbol or string.
|
|
180
|
+
|
|
181
|
+
context.rpc = closure rpc(proc, args, local data, result) {
|
|
182
|
+
data = {
|
|
183
|
+
contextID: contextID
|
|
184
|
+
proc: proc
|
|
185
|
+
args: args }
|
|
186
|
+
pending = exec.execSend("rpc", data)
|
|
187
|
+
result = pending.wait()
|
|
188
|
+
if result.1.error
|
|
189
|
+
list(Error(result.1.error))
|
|
190
|
+
else
|
|
191
|
+
list(false, totuple(result.1.result))
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// finis
|
|
195
|
+
context
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
/*
|
|
200
|
+
* ExecutorParallel
|
|
201
|
+
*
|
|
202
|
+
* Evaluate expressions in parallel. Given a maximum number of workers, this delegates processing
|
|
203
|
+
* to an available worker or enqueues the work to be done on the next available worker, blocking the
|
|
204
|
+
* caller until the work is complete. Destroying this object releases the workers and terminates any
|
|
205
|
+
* pending work with errors.
|
|
206
|
+
* Creating a worker takes 200-300 msec, so this acts like a worker pool and allows them to be
|
|
207
|
+
* saved and reused when needed. At the moment there is no affinity feature, so the actual worker
|
|
208
|
+
* assigned for each task is not selectable.
|
|
209
|
+
* The eval() method can be quite slow because the arguments and result are converted four times
|
|
210
|
+
* through the package manager for each call. Thefore it should only be used for small requests or
|
|
211
|
+
* when Naan semantics must be maintained across the address space transition to the worker.
|
|
212
|
+
* The rpc() method has a very low overhead, but converts everything to JavaScript before making
|
|
213
|
+
* the call. The procedure is specified as a name and must exist as a symbol in the current
|
|
214
|
+
* namespace of the worker. The arguments are specified as a tuple, and will be converted using the
|
|
215
|
+
* normal Naan exporting protocol. So for example, tuples become arrays and symbols become strings.
|
|
216
|
+
* The result will also be in JavaScript types.
|
|
217
|
+
*
|
|
218
|
+
* Allowable options are:
|
|
219
|
+
* {
|
|
220
|
+
* maxcount: <integer> // maximum workers, default 4
|
|
221
|
+
* initeval: `(<expr>, <put>, <get>) // initializes each new worker
|
|
222
|
+
* basename: <string> // worker label for UI, default "batch"
|
|
223
|
+
* }
|
|
224
|
+
*
|
|
225
|
+
*/
|
|
226
|
+
|
|
227
|
+
closure ExecutorParallel(options, local maxcount, basename, parallel, exqueue, crValue) {
|
|
228
|
+
maxcount = options.maxcount
|
|
229
|
+
if !maxcount
|
|
230
|
+
maxcount = 4 // default is 4 workers
|
|
231
|
+
basename = options.basename
|
|
232
|
+
if !string(basename)
|
|
233
|
+
basename = "batch"
|
|
234
|
+
exqueue = [] // waiting for execution
|
|
235
|
+
parallel = new(object, this)
|
|
236
|
+
parallel.workers = [] // array of worker executors
|
|
237
|
+
parallel.contexts = new(weakmap) // map workers to contexts
|
|
238
|
+
parallel.available = [] // array of available workers
|
|
239
|
+
|
|
240
|
+
// acquireWorker
|
|
241
|
+
//
|
|
242
|
+
// Return the next available worker. If no workers are available and we haven't hit the maximum
|
|
243
|
+
// then create an additional worker and return it. If we cannot allocate another worker then this
|
|
244
|
+
// returns false.
|
|
245
|
+
|
|
246
|
+
closure acquireWorker(local workdex, name, worker, context, result) {
|
|
247
|
+
if parallel.available.length > 0
|
|
248
|
+
return (parallel.available.pop())
|
|
249
|
+
workdex = parallel.workers.length
|
|
250
|
+
if workdex >= maxcount
|
|
251
|
+
return (false)
|
|
252
|
+
parallel.workers[workdex] = false // reserve our spot
|
|
253
|
+
name = basename.concat("-", workdex+1)
|
|
254
|
+
worker = App.nide.track.spawn("Local", name, "ExecutorParallel")
|
|
255
|
+
if worker.0 { // ### wasting space on workers list
|
|
256
|
+
debuglog("execParallel: cannot spawn worker:", ErrorString(worker.0))
|
|
257
|
+
return (false)
|
|
258
|
+
}
|
|
259
|
+
worker = worker.1
|
|
260
|
+
context = worker.context()
|
|
261
|
+
if context.0 {
|
|
262
|
+
debuglog("execParallel: cannot create worker context:", ErrorString(context.0))
|
|
263
|
+
worker.destroy()
|
|
264
|
+
return (false)
|
|
265
|
+
}
|
|
266
|
+
context = context.1
|
|
267
|
+
parallel.workers[workdex] = worker
|
|
268
|
+
parallel.contexts[worker] = context
|
|
269
|
+
if tuple(options.initeval) {
|
|
270
|
+
result = apply(context.eval, options.initeval)
|
|
271
|
+
if result.0
|
|
272
|
+
debuglog("execParallel: cannot initialize worker", name, ErrorString(result.0)) }
|
|
273
|
+
return (worker)
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// releaseWorker
|
|
277
|
+
//
|
|
278
|
+
// Release the specified worker to the pool.
|
|
279
|
+
|
|
280
|
+
closure releaseWorker(worker) {
|
|
281
|
+
parallel.available.push(worker)
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// execNext
|
|
285
|
+
//
|
|
286
|
+
// Run the next operation, if any are pending. It's possible that this will acquire a worker and
|
|
287
|
+
// during that process another worker completes and leaves us with nothing to do. In that case we
|
|
288
|
+
// release the worker and do nothing. Another case is where this tries to acquire a worker and
|
|
289
|
+
// cannot at this time, because they are all busy and there is no room for more. This is why we
|
|
290
|
+
// don't remove work from the queue until we already have a worker.
|
|
291
|
+
//
|
|
292
|
+
|
|
293
|
+
closure execNext(local worker, result) {
|
|
294
|
+
if exqueue.length > 0 {
|
|
295
|
+
worker = acquireWorker() // false if none available
|
|
296
|
+
if !worker {
|
|
297
|
+
if maxcount > 0 && parallel.workers.length == 0 // maxcount zero when being destroyed
|
|
298
|
+
throw("execParallel: cannot allocate any workers at all")
|
|
299
|
+
return // we're busy, so try again later
|
|
300
|
+
}
|
|
301
|
+
future(function(local next, context) {
|
|
302
|
+
next = exqueue.splice(0, 1).0 // remove oldest entry, from start
|
|
303
|
+
if !next {
|
|
304
|
+
releaseWorker(worker) // someone else already processed this
|
|
305
|
+
return
|
|
306
|
+
}
|
|
307
|
+
context = parallel.contexts[worker]
|
|
308
|
+
if next.eval
|
|
309
|
+
result = apply(context.eval, next.args)
|
|
310
|
+
else if next.proc
|
|
311
|
+
result = context.rpc(next.proc, next.args)
|
|
312
|
+
else
|
|
313
|
+
debuglog("execNext: exqueue item without task", next.*)
|
|
314
|
+
next.pending.signal(result)
|
|
315
|
+
releaseWorker(worker)
|
|
316
|
+
execNext() // start the next one, if there is one
|
|
317
|
+
}, 1)
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
// eval
|
|
322
|
+
//
|
|
323
|
+
// Evaluate the specified expression in the next available worker, creating workers as required.
|
|
324
|
+
// This blocks the caller until the operation is complete, and returns the result.
|
|
325
|
+
//
|
|
326
|
+
|
|
327
|
+
parallel.eval = closure evalf(expr, put, get, local pending) {
|
|
328
|
+
if !maxcount
|
|
329
|
+
return (crValue) // already destroyed
|
|
330
|
+
pending = new(nonce)
|
|
331
|
+
exqueue.push({
|
|
332
|
+
eval: list(expr, put, get)
|
|
333
|
+
pending: pending
|
|
334
|
+
})
|
|
335
|
+
execNext()
|
|
336
|
+
pending.wait()
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
// evalq
|
|
340
|
+
//
|
|
341
|
+
// Evaluate the specified QUOTED expression in a remote worker.
|
|
342
|
+
//
|
|
343
|
+
|
|
344
|
+
parallel.evalq = macro evalq(expr, put, get) {
|
|
345
|
+
evalf(expr, eval(put), eval(get))
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
// rpc
|
|
349
|
+
//
|
|
350
|
+
// Execute a procedure call in a remote worker.
|
|
351
|
+
//
|
|
352
|
+
|
|
353
|
+
parallel.rpc = closure rpc(proc, args, local pending) {
|
|
354
|
+
if !maxcount
|
|
355
|
+
return (crValue) // already destroyed
|
|
356
|
+
pending = new(nonce)
|
|
357
|
+
exqueue.push({
|
|
358
|
+
proc: proc
|
|
359
|
+
args: args
|
|
360
|
+
pending: pending
|
|
361
|
+
})
|
|
362
|
+
execNext()
|
|
363
|
+
pending.wait()
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
// destroy
|
|
367
|
+
//
|
|
368
|
+
// Destroy the object by destroying the workers and failing pending requests with cancelResult.
|
|
369
|
+
//
|
|
370
|
+
|
|
371
|
+
parallel.destroy = closure destroy(cancelResult, local worker, next) {
|
|
372
|
+
if !maxcount
|
|
373
|
+
return (false) // already destroyed
|
|
374
|
+
crValue = cancelResult
|
|
375
|
+
maxcount = 0
|
|
376
|
+
parallel.available = false
|
|
377
|
+
parallel.contexts = false
|
|
378
|
+
while parallel.workers.length > 0 {
|
|
379
|
+
worker = parallel.workers.pop()
|
|
380
|
+
parallel.contexts[worker] = false
|
|
381
|
+
worker.destroy()
|
|
382
|
+
}
|
|
383
|
+
while exqueue.length > 0 {
|
|
384
|
+
next = exqueue.pop()
|
|
385
|
+
next.pending.signal(crValue)
|
|
386
|
+
}
|
|
387
|
+
true
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
// finis
|
|
391
|
+
|
|
392
|
+
parallel
|
|
393
|
+
};
|
|
394
|
+
|
|
395
|
+
|
|
396
|
+
/*
|
|
397
|
+
* ExecutorTracker
|
|
398
|
+
*
|
|
399
|
+
* Track Naan execution instances.
|
|
400
|
+
*
|
|
401
|
+
*/
|
|
402
|
+
|
|
403
|
+
closure ExecutorTracker(local xtrak) {
|
|
404
|
+
xtrak = new(object, this)
|
|
405
|
+
xtrak.instances = []
|
|
406
|
+
xtrak.watchers = []
|
|
407
|
+
xtrak.makers = {}
|
|
408
|
+
|
|
409
|
+
// notify
|
|
410
|
+
// Notify the watchers by calling a named procedure
|
|
411
|
+
|
|
412
|
+
closure notify(changes, local watch) {
|
|
413
|
+
for watch in xtrak.watchers
|
|
414
|
+
call(watch, changes)
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
// add
|
|
418
|
+
// Add an execution instance at the specified location.
|
|
419
|
+
|
|
420
|
+
xtrak.add = closure add(executor, local instance) {
|
|
421
|
+
instance = new(nonce)
|
|
422
|
+
instance.executor = executor
|
|
423
|
+
xtrak.instances.push(instance)
|
|
424
|
+
notify({ added: [executor] })
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
// delete
|
|
428
|
+
// Delete an execution instance.
|
|
429
|
+
|
|
430
|
+
xtrak.delete = closure delete(executor) {
|
|
431
|
+
xtrak.instances = xtrak.instances.filter(function(item){
|
|
432
|
+
!(item.executor eq executor)
|
|
433
|
+
})
|
|
434
|
+
notify({ deleted: [executor] })
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
// update
|
|
438
|
+
// Notify watchers of an update on an execution instance.
|
|
439
|
+
|
|
440
|
+
xtrak.update = closure update(executor) {
|
|
441
|
+
notify({ updated: [executor] })
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
// attention
|
|
445
|
+
// Notify watchers that our instance wants attention.
|
|
446
|
+
|
|
447
|
+
xtrak.attention = closure attention(executor) {
|
|
448
|
+
notify({ attention: [executor] })
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
// enumlist
|
|
452
|
+
// Enumerate the list of executors.
|
|
453
|
+
|
|
454
|
+
xtrak.enumlist = function enumlist(local results) {
|
|
455
|
+
results = []
|
|
456
|
+
for instance in xtrak.instances
|
|
457
|
+
results.push(instance.executor)
|
|
458
|
+
results
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
// watch
|
|
462
|
+
// Watch for changes on execution instance status.
|
|
463
|
+
|
|
464
|
+
xtrak.watch = function watch(watcher) {
|
|
465
|
+
if xtrak.watchers.indexOf(watcher) < 0
|
|
466
|
+
xtrak.watchers.push(watcher)
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
// unwatch
|
|
470
|
+
// Stop watching for changes on execution instance status.
|
|
471
|
+
|
|
472
|
+
xtrak.unwatch = function unwatch(watcher) {
|
|
473
|
+
xtrak.watchers.filter(function(item){
|
|
474
|
+
!(item eq watcher)
|
|
475
|
+
})
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
// register
|
|
479
|
+
// Register an executor type.
|
|
480
|
+
|
|
481
|
+
xtrak.register = function register(maker, type) {
|
|
482
|
+
xtrak.makers[type] = maker
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
// spawn
|
|
486
|
+
// Spawn a new executor with (type, args), returning a standard (error, result) tuple.
|
|
487
|
+
|
|
488
|
+
xtrak.spawn = closure spawn args {
|
|
489
|
+
function spawnit(type, args) {
|
|
490
|
+
if !xtrak.makers[type]
|
|
491
|
+
list(Error("unknown executor type", type))
|
|
492
|
+
else
|
|
493
|
+
apply(xtrak.makers[type], cons(xtrak, args))
|
|
494
|
+
} (pop(args), args)
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
xtrak.register(execLambda, "Lambda")
|
|
498
|
+
|
|
499
|
+
// finis
|
|
500
|
+
xtrak
|
|
501
|
+
};
|
|
502
|
+
|
|
503
|
+
|
|
504
|
+
/*
|
|
505
|
+
* execLambda
|
|
506
|
+
*
|
|
507
|
+
* Make a Lambda executor connected through web sockets.
|
|
508
|
+
*
|
|
509
|
+
*/
|
|
510
|
+
|
|
511
|
+
closure execLambda(track, url, name, workerID, local target, nlregex, clientID, serverID) {
|
|
512
|
+
target = ExecutorBase(track, "Lambda", name)
|
|
513
|
+
clientID = "DebugWorkers"
|
|
514
|
+
serverID = "Workers"
|
|
515
|
+
nlregex = RegExp("\\n", "g")
|
|
516
|
+
if !workerID
|
|
517
|
+
workerID = "myAwsDebugWorker"
|
|
518
|
+
target.workerID = workerID
|
|
519
|
+
target.msgQueue = []
|
|
520
|
+
|
|
521
|
+
// connect
|
|
522
|
+
// Connect with the host if not already connected.
|
|
523
|
+
function connect() {
|
|
524
|
+
if !target.ws
|
|
525
|
+
target.ws = xnew(js.w.WebSocket, "wss://8h5htw8bfj.execute-api.us-east-1.amazonaws.com/dev")
|
|
526
|
+
|
|
527
|
+
// onopen
|
|
528
|
+
// The WebSocket connection is now open.
|
|
529
|
+
target.ws.onopen = function onopen(e) {
|
|
530
|
+
target.msgCounter = 1
|
|
531
|
+
sendControl("spawn", {
|
|
532
|
+
reset: true
|
|
533
|
+
options: {
|
|
534
|
+
}
|
|
535
|
+
})
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
// onmessage
|
|
539
|
+
// A message was received from the worker via WebSockets.
|
|
540
|
+
|
|
541
|
+
target.ws.onmessage = function onmessage(e, local message, msg) {
|
|
542
|
+
if target.msgQueue
|
|
543
|
+
target.msgQueue.push(e.data)
|
|
544
|
+
else
|
|
545
|
+
target.doMessage(e.data)
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
// onerror
|
|
549
|
+
// An error occurred on the WebSocket connection.
|
|
550
|
+
target.ws.onerror = function onerror(e) {
|
|
551
|
+
target.debugWrite("connection error: ".concat(e.toString()), 3)
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
// onclose
|
|
555
|
+
// The WebSocket connection was closed.
|
|
556
|
+
target.ws.onclose = function onclose(e) {
|
|
557
|
+
target.debugWrite("connection closed: ".concat(e.code, " ", e.reason), 3)
|
|
558
|
+
target.ws = false
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
//
|
|
563
|
+
// doOneMessage
|
|
564
|
+
//
|
|
565
|
+
// Process the specified undecoded message.
|
|
566
|
+
function doOneMessage(message, local msg) {
|
|
567
|
+
if message.respOp == "msgarray" {
|
|
568
|
+
for msg in message.payload
|
|
569
|
+
doOneMessage(msg)
|
|
570
|
+
} else if message.respOp == "msgout" {
|
|
571
|
+
msg = message.payload
|
|
572
|
+
if msg.id == "textout" {
|
|
573
|
+
target.written = true
|
|
574
|
+
target.term.Write(msg.text.replace(nlregex, "\r\n")) }
|
|
575
|
+
else if msg.id == "debugtext" {
|
|
576
|
+
target.written = true
|
|
577
|
+
target.debugWrite(msg.text, msg.level) }
|
|
578
|
+
else if msg.id == "msgout"
|
|
579
|
+
target.listener(msg.data)
|
|
580
|
+
else if msg.id == "started"
|
|
581
|
+
{ }
|
|
582
|
+
else if msg.id == "typed"
|
|
583
|
+
target.term.Write(msg.text.replace(nlregex, "\r\n"))
|
|
584
|
+
else if msg.id == "status"
|
|
585
|
+
target.statusCB(target, msg.status)
|
|
586
|
+
else
|
|
587
|
+
debuglog("unknown AWS worker message:", msg.id)
|
|
588
|
+
} else if message.respOp == "error"
|
|
589
|
+
debuglog("worker controller error:", ErrorString(message.payload[0]))
|
|
590
|
+
else if message.respOp == "linked"
|
|
591
|
+
future(function termLinked(){
|
|
592
|
+
if !target.written
|
|
593
|
+
target.term.WriteLn("(reconnected)")
|
|
594
|
+
}, 1000).run()
|
|
595
|
+
else
|
|
596
|
+
debuglog("unknown AWS response message:", message.respOp)
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
//
|
|
600
|
+
// doMessage
|
|
601
|
+
//
|
|
602
|
+
// Process the specified undecoded message.
|
|
603
|
+
target.doMessage = function doMessage(text, local message) {
|
|
604
|
+
try {
|
|
605
|
+
message = new(js.w.JSON.parse(text))
|
|
606
|
+
if message.connectionId && string(message.message)
|
|
607
|
+
target.debugWrite("AWS response: ".concat(message.message), 3)
|
|
608
|
+
else if message.clientID != clientID || message.serverID != serverID || message.workerID != target.workerID
|
|
609
|
+
debuglog("AWS response doesn't match:", message.clientID, message.serverID, message.workerID)
|
|
610
|
+
else
|
|
611
|
+
doOneMessage(message)
|
|
612
|
+
} catch {
|
|
613
|
+
if true
|
|
614
|
+
return (debuglog("can't decode incoming ws message:", exception))
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
//
|
|
619
|
+
// sendControl
|
|
620
|
+
//
|
|
621
|
+
// Send a message to the worker controller.
|
|
622
|
+
|
|
623
|
+
target.sendControl = function sendControl(workerOp, payload) {
|
|
624
|
+
target.ws.send(js.w.JSON.stringify({
|
|
625
|
+
clientID: clientID
|
|
626
|
+
serverID: serverID
|
|
627
|
+
workerID: target.workerID
|
|
628
|
+
workerOp: workerOp
|
|
629
|
+
payload: payload
|
|
630
|
+
msgCounter: target.msgCounter++
|
|
631
|
+
}))
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
//
|
|
635
|
+
// send
|
|
636
|
+
//
|
|
637
|
+
// Send a message to the worker.
|
|
638
|
+
|
|
639
|
+
target.send = function send(msg) {
|
|
640
|
+
// msg.save = true // save state for this invocation
|
|
641
|
+
sendControl("msgin", msg)
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
//
|
|
645
|
+
// oninterrupt
|
|
646
|
+
//
|
|
647
|
+
|
|
648
|
+
function oninterrupt() {
|
|
649
|
+
send({ // send interrupt to worker
|
|
650
|
+
id: "interrupt"
|
|
651
|
+
})
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
//
|
|
655
|
+
// onreturn
|
|
656
|
+
//
|
|
657
|
+
|
|
658
|
+
function onreturn(local text) {
|
|
659
|
+
text = target.term.TakeText()
|
|
660
|
+
send({ // send user-entered text to worker
|
|
661
|
+
id: "keyline",
|
|
662
|
+
text: text,
|
|
663
|
+
typed: "\x1b[90m\x1b[3m".concat(text, "\x1b[0m\n") // dark-grey, italic
|
|
664
|
+
})
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
//
|
|
668
|
+
// target.PostMessage
|
|
669
|
+
//
|
|
670
|
+
// Post a message to the target.
|
|
671
|
+
//
|
|
672
|
+
|
|
673
|
+
target.PostMessage = function PostMessage(data) {
|
|
674
|
+
target.ws.send({ // send user-entered text to worker
|
|
675
|
+
id: "msgin",
|
|
676
|
+
data: data
|
|
677
|
+
})
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
//
|
|
681
|
+
// target.debugWrite
|
|
682
|
+
//
|
|
683
|
+
// Write a debug message to the repl.
|
|
684
|
+
//
|
|
685
|
+
|
|
686
|
+
target.debugWrite = function(text, level) {
|
|
687
|
+
if (level >= 5)
|
|
688
|
+
target.term.WriteLn(text, target.term.Cyan) // cyan for builtin logging
|
|
689
|
+
else if (level >= 4)
|
|
690
|
+
target.term.WriteLn(text, target.term.Blue) // blue for Naan function logging
|
|
691
|
+
else if (level >= 3)
|
|
692
|
+
target.term.WriteLn(text, target.term.Red) // light red for warnings
|
|
693
|
+
else if (level >= 2)
|
|
694
|
+
target.term.WriteLn(text, target.term.Green) // green for Naan.debug.debuglog logging
|
|
695
|
+
else
|
|
696
|
+
target.term.WriteLn(text, target.term.Red+target.term.Bold) // heavy red for errors
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
//
|
|
700
|
+
// termAttach
|
|
701
|
+
//
|
|
702
|
+
// A terminal was attached to our instance.
|
|
703
|
+
|
|
704
|
+
target.termAttach = function termAttach(repl, db, statusCB, local msg) {
|
|
705
|
+
connect() // ensure we are connected
|
|
706
|
+
target.term = repl
|
|
707
|
+
target.debugger = db
|
|
708
|
+
target.statusCB = statusCB
|
|
709
|
+
if target.termSaved {
|
|
710
|
+
target.term.StateLoad(target.termSaved)
|
|
711
|
+
target.termSaved = false }
|
|
712
|
+
else
|
|
713
|
+
target.term.Reset()
|
|
714
|
+
while target.msgQueue.length > 0 {
|
|
715
|
+
for msg in target.msgQueue.splice(0) // all current messages
|
|
716
|
+
target.doMessage(msg)
|
|
717
|
+
}
|
|
718
|
+
target.msgQueue = false
|
|
719
|
+
target.term.On("interrupt", oninterrupt.proc)
|
|
720
|
+
target.term.On("return", onreturn.proc)
|
|
721
|
+
target.term.Focus()
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
//
|
|
725
|
+
// termDetach
|
|
726
|
+
//
|
|
727
|
+
// The terminal is being deattached from our instance.
|
|
728
|
+
|
|
729
|
+
target.termDetach = function termDetach(repl) {
|
|
730
|
+
target.term.On("interrupt")
|
|
731
|
+
target.term.On("return")
|
|
732
|
+
target.msgQueue = []
|
|
733
|
+
target.termSaved = target.term.StateSave()
|
|
734
|
+
target.statusCB = false
|
|
735
|
+
target.debugger = false
|
|
736
|
+
target.term = false
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
track.add(target)
|
|
740
|
+
connect() // connect right away to start receiving messages
|
|
741
|
+
|
|
742
|
+
// finis
|
|
743
|
+
|
|
744
|
+
list(false, target)
|
|
745
|
+
};
|
|
746
|
+
|
|
747
|
+
|
|
748
|
+
/*
|
|
749
|
+
* execInit
|
|
750
|
+
*
|
|
751
|
+
* Initialize the component.
|
|
752
|
+
*
|
|
753
|
+
*/
|
|
754
|
+
|
|
755
|
+
function execInit(local manifest) {
|
|
756
|
+
|
|
757
|
+
manifest = `(ExecutorBase, ExecutorContext, ExecutorParallel, ExecutorTracker, execLambda, execInit)
|
|
758
|
+
|
|
759
|
+
Naan.module.build(module.id, "executors", function(modobj, compobj) {
|
|
760
|
+
require("./running.nlg")
|
|
761
|
+
compobj.manifest = manifest
|
|
762
|
+
modobj.exports.ExecutorBase = ExecutorBase
|
|
763
|
+
modobj.exports.ExecutorContext = ExecutorContext
|
|
764
|
+
modobj.exports.ExecutorParallel = ExecutorParallel
|
|
765
|
+
modobj.exports.ExecutorTracker = ExecutorTracker
|
|
766
|
+
})
|
|
767
|
+
} ();
|