@naanlang/naan 1.6.0 → 1.7.1

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.
Files changed (54) hide show
  1. package/LICENSE.md +1 -1
  2. package/README.md +1 -1
  3. package/bin/index.js +2 -2
  4. package/dist/env_web.js +3 -32
  5. package/dist/naan.min.js +10 -10
  6. package/frameworks/browser/sworker.js +23 -23
  7. package/frameworks/browser/terminals.nlg +161 -212
  8. package/frameworks/browser/worker.nlg +78 -30
  9. package/frameworks/browser/workers.nlg +1 -1
  10. package/frameworks/browser/ws_client.nlg +23 -87
  11. package/frameworks/client/apiclient.nlg +91 -58
  12. package/frameworks/client/client.nlg +22 -2
  13. package/frameworks/client/psm_client.nlg +10 -14
  14. package/frameworks/client/relaycon.nlg +1 -1
  15. package/frameworks/common/common.nlg +13 -9
  16. package/frameworks/node/apiserver.nlg +1 -2
  17. package/frameworks/node/node.nlg +1 -0
  18. package/frameworks/node/pty.nlg +21 -21
  19. package/frameworks/node/shell.nlg +26 -3
  20. package/frameworks/node/worker.nlg +166 -84
  21. package/frameworks/node/ws_client.nlg +25 -84
  22. package/frameworks/project/build.nlg +83 -32
  23. package/frameworks/project/projects.nlg +2 -2
  24. package/frameworks/running/debugcom.nlg +20 -3
  25. package/frameworks/running/debugnub.nlg +16 -7
  26. package/frameworks/running/executors.nlg +36 -26
  27. package/frameworks/running/instances.nlg +34 -22
  28. package/frameworks/running/remote_req.nlg +6 -9
  29. package/frameworks/running/remote_res.nlg +2 -2
  30. package/frameworks/running/sourcecode.nlg +3 -2
  31. package/frameworks/running/taskexec.nlg +14 -57
  32. package/frameworks/service/imp.nlg +37 -5
  33. package/frameworks/service/nubnode.nlg +39 -8
  34. package/frameworks/service/nubweb.nlg +28 -10
  35. package/frameworks/storage/file_manager.nlg +3 -2
  36. package/lib/browser/env_web.js +10 -39
  37. package/lib/browser/env_webworker.js +1 -30
  38. package/lib/core/naanlib.js +10 -10
  39. package/lib/env_node.js +5 -22
  40. package/lib/env_nodeworker.js +8 -43
  41. package/lib/node_server_init.nlg +149 -0
  42. package/lib/node_worker.js +36 -0
  43. package/lib/node_worker_init.nlg +43 -0
  44. package/package.json +1 -1
  45. package/plugins/openSearch/openSearch.nlg +3 -2
  46. package/plugins/openSearch/os_dynamo.nlg +3 -2
  47. package/plugins/serviceAws/aws_cloudwatchlogs.nlg +44 -2
  48. package/plugins/serviceAws/aws_dynamo.nlg +144 -63
  49. package/plugins/serviceAws/aws_utils.nlg +6 -11
  50. package/plugins/serviceAws/serviceAws.nlg +3 -2
  51. package/test/test_01_core.nlg +2 -2
  52. package/test/test_02_context.nlg +8 -8
  53. package/test/test_03_jsinterop.nlg +3 -3
  54. package/test/test_07_lingo.nlg +1 -1
@@ -6,7 +6,7 @@
6
6
  *
7
7
  * column positioning: // // !
8
8
  *
9
- * Copyright (c) 2019-2024 by Richard C. Zulch
9
+ * Copyright (c) 2019-2026 by Richard C. Zulch
10
10
  *
11
11
  */
12
12
 
@@ -304,18 +304,26 @@ closure Builder(rules, operation, fs, srcpath, destpath, track, local build, gro
304
304
  //
305
305
  // Apply substitutions from readSubstitutionFile to string, returning a tuple of the resulting
306
306
  // text and the number of substitution patterns actually used from the supplied dictionary.
307
- //
308
- function applySubstitutions(text, subs, local subbed) {
309
- subbed = { }
310
- list(text.replace(RegExp("[$][^$\\s]+[$]", "g"), function(match, local subst) {
311
- subst = subs[match.slice(1,-1)]
312
- if subst {
313
- subbed[match] = true
314
- subst
315
- }
316
- else
317
- match
318
- }), length(subbed))
307
+ // Specify iterative to repeat the substitution on the result until no more substitutions occur.
308
+ //
309
+ function applySubstitutions(text, subs, iterative, local count, subbed) {
310
+ count = 0
311
+ loop {
312
+ subbed = { }
313
+ text = text.replace(RegExp("[$][^$\\s]+[$]", "g"), function(match, local subst) {
314
+ subst = subs[match.slice(1,-1)]
315
+ if subst {
316
+ subbed[match] = true
317
+ subst
318
+ }
319
+ else
320
+ match
321
+ })
322
+ count += length(subbed)
323
+ if length(subbed) == 0 || !iterative
324
+ break
325
+ }
326
+ list(text, count)
319
327
  }
320
328
 
321
329
  //
@@ -426,7 +434,7 @@ closure Builder(rules, operation, fs, srcpath, destpath, track, local build, gro
426
434
  // autoquoted to itself, a symbol. This can be used to detect build time vs. runtime.
427
435
 
428
436
  function parseAndPack1(text, fpath, options, operation,
429
- local util, lib, penv, naansym, dlogsym, pmod, pcomp, output, textout, annerror, annotations, package)
437
+ local util, lib, penv, naansym, dlogsym, pmod, pcomp, ournn, output, textout, annerror, annotations, package)
430
438
  {
431
439
  //
432
440
  // textline
@@ -445,20 +453,39 @@ closure Builder(rules, operation, fs, srcpath, destpath, track, local build, gro
445
453
  lib = Naan.module.modlist().Lib.exports
446
454
  if !util || !lib
447
455
  return (list("Build packager cannot access support functions"))
456
+ //
457
+ // The following crazy rigmarole is about customizing the ParsEnvironment so we can glean
458
+ // enough information to build a package without actually executing the code. To do this we
459
+ // replace the entire Naan.module collection of functions with just build, so the others do
460
+ // nothing. From that we collect the module and component names, plus the manifest. This
461
+ // also sets up a benign "exports" dictionary so module build can "go through the mtions"
462
+ // without modification.
463
+ //
448
464
  penv = lib.ParsEnvironment(Dialect, "build-".concat(operation))
449
465
  penv.evalq(letlocal(App)) // make local copy of App
450
466
  penv.evalq(letlocal(js)) // make local copy of js
451
- naansym = penv.evalq(letlocal(Naan).0) // make local copy of Naan
452
- penv.evalq(assign(naansym,new(Naan))) // make it modifiable
467
+ ournn = new(Naan) // get modifiable Naan
453
468
  pmod = { } // our module object
454
469
  pcomp = { } // our component object
455
- car(naansym).module = {
470
+ ournn.module = {
456
471
  build: function(modname, compname, proc) {
457
- pmod.id = module
472
+ pmod.id = module.id
473
+ pmod.exports = { }
458
474
  pcomp.name = compname
459
475
  call(proc, pmod, pcomp) // obtain manifest
460
476
  }
461
477
  }
478
+ naansym = penv.evalq(function(nn) {
479
+ assign(letlocal(Naan).0,nn)
480
+ `Naan
481
+ }(ournn)) // make Naan modifiable
482
+ penv.evalq(assign(letlocal(module).0, { // make local copy of module
483
+ exports: { }
484
+ }))
485
+ //
486
+ // Here we make a custom debuglog function that identifies the text without color and will
487
+ // write the output to the build log.
488
+ //
462
489
  dlogsym = penv.evalq(letlocal(debuglog).0) // make local copy of debuglog
463
490
  penv.evalq(function \.debuglog args { // redefine debuglog for capture
464
491
  let (arg, spaced) {
@@ -471,8 +498,19 @@ closure Builder(rules, operation, fs, srcpath, destpath, track, local build, gro
471
498
  apply(textline, reverse(spaced))
472
499
  } () })
473
500
  penv.evalq(putproc(car(dlogsym), \.debuglog.proc))
501
+ //
502
+ // The NideBuild symbol is "debug" or "release", etc. or is simply autoquoted as the symbol
503
+ // itself when not building. This is used if the source needs different behavior at build
504
+ // time or run time.
505
+ //
474
506
  penv.eval(append(quote(assign(compress("NideBuild"))), operation)) // NideBuild symbol is operation name string
507
+ //
508
+ // This is the actual meat of the matter, the parsing of the program text.
509
+ //
475
510
  output = penv.parse(text) // parse and evaluate
511
+ //
512
+ // Output processing and error logging
513
+ //
476
514
  textout = ""
477
515
  try {
478
516
  if !pcomp.name
@@ -507,20 +545,30 @@ closure Builder(rules, operation, fs, srcpath, destpath, track, local build, gro
507
545
  }
508
546
  if output.errors.length > 0
509
547
  return (list("errors in ".concat(fpath), false, annotations, textout))
548
+ //
549
+ // Build the package, if not just doing syntax checking
550
+ //
510
551
  if options.naanpack || options.jspack { // actually building a package?
511
552
  if !pcomp.name {
512
- pcomp = penv.evalq(car(compress(NaanBuildComponent))) // fallback for building Module.nlg itself
553
+ //
554
+ // Some components cannot use a build function because they are too early in the
555
+ // boot process, but we still need a component. We look for NaanBuildComponent
556
+ // to define a dictionary with filename, name and manifest fields. For example,
557
+ // see Util:Module.nlg
558
+ //
559
+ pcomp = penv.evalq(car(compress(NaanBuildComponent)))
513
560
  if !pcomp || !fpath.endsWith("/".concat(pcomp.filename))
514
561
  return (list("Build cannot package without component in ".concat(fpath), false, annotations, textout))
515
562
  }
516
- package = penv.evalq(util.pkgSave(compress(pcomp.name), pcomp.manifest))
563
+ package = util.pkgSave(compress(pcomp.name), pcomp.manifest)
564
+ if string(package)
565
+ return (list("Build cannot generate package for ".concat(fpath), false, annotations, textout))
517
566
  }
518
567
  } catch {
519
568
  if true
520
569
  return (list("Packaging exception in ".concat(fpath, ": ", tostring(exception)), false, annotations, textout))
521
570
  } finally {
522
571
  penv.destroy() // remove the namespace
523
- debuglog("parseAndPack1 done", fpath)
524
572
  }
525
573
  if options.naanpack
526
574
  package = (js.w||js.s||js.g).JSON.stringify(package) // doesn't have frameworks/common available
@@ -546,7 +594,7 @@ closure Builder(rules, operation, fs, srcpath, destpath, track, local build, gro
546
594
  build.parallel = ExecutorParallel(track, { // create worker pool
547
595
  basename: "build"
548
596
  initeval: `(false, (parseAndPack1))
549
- })
597
+ })
550
598
  result = build.parallel.rpc(parseAndPack1, list(text, fpath, options, operation))
551
599
  if !result.0 {
552
600
  result = result.1 // unpeel RPC result
@@ -1004,9 +1052,9 @@ closure Builder(rules, operation, fs, srcpath, destpath, track, local build, gro
1004
1052
  }
1005
1053
  else if ((`(error, content) = fs.readFile(entry.readpath, readOptions)) && !error) {
1006
1054
  if entry.license
1007
- `(content) = applySubstitutions(content, licFileDefs)
1055
+ `(content) = applySubstitutions(content, licFileDefs, true)
1008
1056
  if entry.version
1009
- `(content) = applySubstitutions(content, verFileDefs)
1057
+ `(content) = applySubstitutions(content, verFileDefs, true)
1010
1058
  if entry.naanpack
1011
1059
  `(error, content) = parseAndPack(content, entry.readpath, { naanpack: true })
1012
1060
  else if entry.jspack
@@ -1211,17 +1259,20 @@ closure Builder(rules, operation, fs, srcpath, destpath, track, local build, gro
1211
1259
  //
1212
1260
  // Run the current build product by spawning a new worker.
1213
1261
  //
1214
- build.runSpawn = function runSpawn(local error, maintext, executor, vpath, site) {
1262
+ build.runSpawn = function runSpawn(stage,
1263
+ local error, maintext, executor, vpath, site, dirpath) {
1215
1264
  `(error, maintext) = fs.readFile(JSpath.join(build.destpath, build.runway.main))
1216
1265
  if error
1217
1266
  error = Error("Builder: cannot read main file", error)
1218
1267
  else {
1268
+ dirpath = fs.path.resolve("/", fs.rootpath, build.destpath, build.runway.path || "")
1219
1269
  `(error, executor) = track.spawn(build.runway.spawn.0, "${rules.name} ${build.version}", {
1220
1270
  workerID: "Run-${build.version}+${build.buildnum}"
1221
1271
  startup: {
1222
- initcmds: maintext
1223
- dirpath: fs.path.resolve("/", fs.rootpath, build.destpath)
1272
+ maintext: maintext
1273
+ dirpath: dirpath
1224
1274
  }
1275
+ rootPath: dirpath
1225
1276
  })
1226
1277
  if error
1227
1278
  error = Error("Builder: cannot spawn executor instance", error)
@@ -1249,13 +1300,12 @@ closure Builder(rules, operation, fs, srcpath, destpath, track, local build, gro
1249
1300
  //
1250
1301
  // Run the current build product by opening a new window.
1251
1302
  //
1252
- build.runWindow = function runWindow(
1303
+ build.runWindow = function runWindow(stage,
1253
1304
  local title, main, url, features, vpath, error, window, site) {
1254
1305
  if !js.w
1255
1306
  return (list(Error('Run method "window" requires browser host')))
1256
1307
  if !build.runway."v-site"
1257
1308
  return (list(Error('Run method "window" requires v-site specification')))
1258
- title = rules.name.concat("-", build.buildnum)
1259
1309
  main = build.runway.main
1260
1310
  if !main
1261
1311
  main = "index.html"
@@ -1271,6 +1321,7 @@ closure Builder(rules, operation, fs, srcpath, destpath, track, local build, gro
1271
1321
  if error
1272
1322
  return (list(Error("Builder: cannot create virtual site", error)))
1273
1323
  sleep(1) // needed on Windows or it won't open, fiik why
1324
+ title = rules.name.concat("-", stage, "-", operation)
1274
1325
  window = js.w.open(url, title, features)
1275
1326
  list(false, { ok: true }) // we don't know the executor
1276
1327
  }
@@ -1281,13 +1332,13 @@ closure Builder(rules, operation, fs, srcpath, destpath, track, local build, gro
1281
1332
  // Run the current build product, which would be after the make call. Returns a standard result
1282
1333
  // tuple.
1283
1334
  //
1284
- build.run = function run() {
1335
+ build.run = function run(stage) {
1285
1336
  if build.runway.open
1286
1337
  fs.shellOpen(JSpath.join(build.destpath, build.runway.open), build.runway.args)
1287
1338
  else if build.runway.spawn
1288
- build.runSpawn()
1339
+ build.runSpawn(stage)
1289
1340
  else if build.runway.window
1290
- build.runWindow()
1341
+ build.runWindow(stage)
1291
1342
  else
1292
1343
  list(false, { ok: true })
1293
1344
  }
@@ -296,7 +296,7 @@ closure projConnector(projman, projtype, local connector, watch) {
296
296
  *
297
297
  * Nide.proj/
298
298
  * nide_cliser.cfg - JSON definition of the project (see below)
299
- * NaanIDE_version.txt - [default] substitution file defining 1.6.0+1 etc.
299
+ * NaanIDE_version.txt - [default] substitution file defining 1.7.1+1 etc.
300
300
  * NaanIDE_version_builds.txt - [default] contains current build number as decimal string
301
301
  * build/ - [optional] default build output location
302
302
  * NaanIDE.lic - [optional] defines Zulch Laboratories, Inc. and other license info
@@ -646,7 +646,7 @@ closure projConfig(projman, where, projID, local procon, fs, configpath) {
646
646
  if builder.refresh()
647
647
  buildonly = !buildonly // reverse if refresh
648
648
  if !buildonly
649
- `(error) = builder.run()
649
+ `(error) = builder.run(stage)
650
650
  }
651
651
  builder.destroy()
652
652
  procon.builder = false
@@ -19,9 +19,6 @@
19
19
 
20
20
  closure DebugCommand(executor, notify, local decom, excon) {
21
21
  decom = new(object, this)
22
- executor.DebugCmd({
23
- op: "attach"
24
- })
25
22
  decom.executor = executor
26
23
 
27
24
  // debugData
@@ -96,6 +93,24 @@ closure DebugCommand(executor, notify, local decom, excon) {
96
93
  status
97
94
  }
98
95
 
96
+ // query
97
+ // Send a status update back.
98
+ //
99
+ decom.query = function query() {
100
+ decom.executor.DebugCmd({
101
+ op: "query"
102
+ })
103
+ }
104
+
105
+ // attach
106
+ // Attach our GUI to the low-level debugger
107
+ //
108
+ decom.attach = function attach() {
109
+ executor.DebugCmd({
110
+ op: "attach"
111
+ })
112
+ }
113
+
99
114
  //
100
115
  // Run control.
101
116
  //
@@ -138,6 +153,8 @@ closure DebugCommand(executor, notify, local decom, excon) {
138
153
  notify.runState(debugStatus())
139
154
  }
140
155
 
156
+ attach()
157
+
141
158
  // finis
142
159
 
143
160
  decom
@@ -35,6 +35,7 @@ closure DebugNub(local nub, debug, paused) {
35
35
  nub.watches = []
36
36
  nub.stacks = { }
37
37
  nub.usage = { }
38
+ paused = { }
38
39
 
39
40
  // debugDepth
40
41
  // Return the recursive debugging depth as an integer.
@@ -45,6 +46,18 @@ closure DebugNub(local nub, debug, paused) {
45
46
  curdepth = 0
46
47
  curdepth
47
48
  }
49
+
50
+ // sendStatus
51
+ // Send current status to the far end.
52
+ //
53
+ function sendStatus() {
54
+ js.t.ReplyDebugger({
55
+ op: "status"
56
+ depth: debugDepth()
57
+ bpenable: debug.bpenable
58
+ exenable: nub.exenable
59
+ })
60
+ }
48
61
 
49
62
  // onDebugger proc
50
63
  // Process an incoming debugger command.
@@ -52,17 +65,13 @@ closure DebugNub(local nub, debug, paused) {
52
65
  function onDebugger(msg) {
53
66
  if msg.op == "attach" {
54
67
  debug.setCommander(nub)
55
- js.t.ReplyDebugger({
56
- op: "status"
57
- depth: debugDepth()
58
- bpenable: debug.bpenable
59
- exenable: nub.exenable
60
- })
68
+ sendStatus()
61
69
  }
62
70
  else if msg.op == "detach" {
63
71
  debug.setCommander(false)
64
72
  paused.wait = false // false: go back to ndb
65
- }
73
+ } else if msg.op == "query"
74
+ sendStatus()
66
75
  else if msg.op == "pause"
67
76
  debugger()
68
77
  else if msg.op == "resume" || msg.op == "stop"
@@ -5,7 +5,7 @@
5
5
  *
6
6
  * column positioning: // // !
7
7
  *
8
- * Copyright (c) 2020-2024 by Richard C. Zulch
8
+ * Copyright (c) 2020-2026 by Richard C. Zulch
9
9
  *
10
10
  */
11
11
 
@@ -23,13 +23,21 @@ closure ExecutorBase(track, type, name, local exec) {
23
23
  exec.type = type
24
24
  exec.name = name
25
25
  exec.contexts = { }
26
-
26
+
27
27
  // execReset
28
28
  //
29
29
  // Initialize remote communications for a new or restarted instance.
30
30
  //
31
- exec.execReset = function execReset() {
31
+ exec.execReset = function execReset(error, local xid, pending) {
32
+ if !error
33
+ error = Error("execReset")
34
+ for `(xid, pending) in exec.pending {
35
+ pending.signal(list(error))
36
+ exec.pending[xid] = undefined
37
+ }
32
38
  exec.pending = { }
39
+ exec.inst_ready = false
40
+ exec.inst_error = undefined
33
41
  exec.nextSendID = toint(RandomBytes(3)) // keep our sequence code unique for shared servers
34
42
  }
35
43
  execReset()
@@ -52,7 +60,7 @@ closure ExecutorBase(track, type, name, local exec) {
52
60
  pending.signal(Error("execSend failed", error))
53
61
  pending
54
62
  }
55
-
63
+
56
64
  // execReceived
57
65
  //
58
66
  // The specified message was received from the instance, so signal it.
@@ -65,7 +73,7 @@ closure ExecutorBase(track, type, name, local exec) {
65
73
  exec.reverseDispatch = require("./taskexec.nlg").TaskDispatcher(function(data) {
66
74
  data.reverse = true
67
75
  exec.PostMessage(data)
68
- }, exec.contexts[data.contextID].coder)
76
+ }, exec.contexts[data.contextID].coder, "execReceived")
69
77
  data.reverse = false
70
78
  exec.reverseDispatch(data)
71
79
  return
@@ -73,7 +81,7 @@ closure ExecutorBase(track, type, name, local exec) {
73
81
  pending = exec.pending[data.xid]
74
82
  if !pending { // not waiting on message
75
83
  if data.xmsg == "xready"
76
- exec.pending.ready = true
84
+ exec.inst_ready = true
77
85
  if data.xmsg == "xattention"
78
86
  { }
79
87
  return
@@ -81,7 +89,7 @@ closure ExecutorBase(track, type, name, local exec) {
81
89
  exec.pending[data.xid] = undefined
82
90
  pending.signal(list(false, data))
83
91
  }
84
-
92
+
85
93
  // execSetStatus
86
94
  //
87
95
  // Received a status update from the instance. This makes a copy of the specified dictionary for
@@ -93,29 +101,25 @@ closure ExecutorBase(track, type, name, local exec) {
93
101
  exec.execSetStatus = function execSetStatus(status) {
94
102
  exec.xstatus = new(status) // for GC (!)
95
103
  }
96
-
104
+
97
105
  // execFailed
98
106
  //
99
107
  // Communication has failed.
100
108
  //
101
109
  exec.execFailed = closure execFailed(error, local xid, pending) {
102
- exec.pending.error = error
110
+ exec.inst_error = error
103
111
  for `(xid, pending) in exec.pending
104
112
  pending.signal(list(error))
105
113
  }
106
-
114
+
107
115
  // execClosed
108
116
  //
109
- //
110
117
  // Note that the connection has closed, so signal all the waiters.
111
118
  //
112
- exec.execClosed = closure execClosed(error, local xid, pending) {
113
- for `(xid, pending) in exec.pending {
114
- pending.signal(list(error))
115
- pending[xid] = undefined
116
- }
119
+ exec.execClosed = closure execClosed(error) {
120
+ execReset(error)
117
121
  }
118
-
122
+
119
123
  // context
120
124
  //
121
125
  // Return a context object for the instance, or an error, in a result tuple. The optional state
@@ -127,7 +131,7 @@ closure ExecutorBase(track, type, name, local exec) {
127
131
  retries = 0
128
132
  ms = milliseconds()
129
133
  delta = 1
130
- while !exec.pending.ready {
134
+ while !exec.inst_ready {
131
135
  `(error) = exec.PostMessage({
132
136
  xmsg: "xstart"
133
137
  })
@@ -135,8 +139,8 @@ closure ExecutorBase(track, type, name, local exec) {
135
139
  return (Error("exec.context failed", error))
136
140
  if ++retries > 100
137
141
  return (list(Error("timeout creating remote context", type, name)))
138
- if exec.pending.error
139
- return (list(Error("cannot create remote context", exec.pending.error)))
142
+ if exec.inst_error
143
+ return (list(Error("cannot create remote context", exec.inst_error)))
140
144
  sleep(delta)
141
145
  if delta < 100
142
146
  delta *= 2 }
@@ -157,7 +161,7 @@ closure ExecutorBase(track, type, name, local exec) {
157
161
  exec.attention = function attention() {
158
162
  track.attention(exec)
159
163
  }
160
-
164
+
161
165
  // finis
162
166
  exec
163
167
  };
@@ -220,6 +224,8 @@ closure ExecutorContext(exec, state, coder, local context, util) {
220
224
  // an optional tuple of symbols to retrieve after evaluation.
221
225
  //
222
226
  context.eval = closure evalf(expr, put, get, local roots, pkg, data, pending, result) {
227
+ if !exec.inst_ready
228
+ return (list(Error("execution context closed")))
223
229
  if tuple(put)
224
230
  roots = cons(`expr, put)
225
231
  else
@@ -288,6 +294,8 @@ closure ExecutorContext(exec, state, coder, local context, util) {
288
294
  // The procedure must be specified as a symbol or string.
289
295
  //
290
296
  context.rpc = closure rpc(proc, args, local data, pending, result) {
297
+ if !exec.inst_ready
298
+ return (list(Error("execution context closed")))
291
299
  data = {
292
300
  contextID: state.contextID
293
301
  proc: proc
@@ -566,13 +574,13 @@ closure ExecutorParallel(track, options, local maxcount, parallel, state, exqueu
566
574
  return (false) // already destroyed
567
575
  crValue = cancelResult
568
576
  maxcount = 0
569
- state.available = false
570
- parallel.contexts = false
571
577
  while state.workers.length > 0 {
572
578
  worker = state.workers.pop()
573
579
  parallel.contexts[worker] = false
574
580
  worker.destroy()
575
581
  }
582
+ state.available = false
583
+ parallel.contexts = false
576
584
  while exqueue.length > 0 {
577
585
  next = exqueue.pop()
578
586
  next.pending.signal(crValue)
@@ -604,12 +612,14 @@ closure ExecutorTracker(local xtrak) {
604
612
  // Notify the watchers by calling a named procedure
605
613
 
606
614
  closure notify(changes, local watch) {
607
- for watch in xtrak.watchers
608
- call(watch, changes)
615
+ future(function(){
616
+ for watch in xtrak.watchers
617
+ call(watch, changes)
618
+ }, 10)
609
619
  }
610
620
 
611
621
  // add
612
- // Add an execution instance at the specified location.
622
+ // Add an execution instance.
613
623
 
614
624
  xtrak.add = closure add(executor, local instance) {
615
625
  instance = new(nonce)
@@ -41,6 +41,7 @@
41
41
  closure InTop(track, options, local intop, nlregex, str, error) {
42
42
  global(App)
43
43
  intop = ExecutorBase(track, options.type, options.name)
44
+ intop.verbose = false
44
45
  intop.workerID = options.workerID || undefined
45
46
  intop.msgQueue = []
46
47
  nlregex = RegExp("\\n", "g")
@@ -101,7 +102,7 @@ closure InTop(track, options, local intop, nlregex, str, error) {
101
102
  // Received an unknown destination notice from the other side.
102
103
  //
103
104
  intop.impUnknown = function impUnknown(unknownID, sourceID, gateway) {
104
- if !gateway
105
+ if !gateway && intop.verbose
105
106
  debuglog("InTop.impUnknownID:", unknownID, "sourceID:", sourceID)
106
107
  }
107
108
 
@@ -263,8 +264,12 @@ closure InTop(track, options, local intop, nlregex, str, error) {
263
264
  // A terminal was attached.
264
265
  //
265
266
  intop.termAttach = function termAttach(repl, db, notify, local msg) {
267
+ if !repl {
268
+ debuglog("termAttach false repl")
269
+ return (false)
270
+ }
266
271
  if !intop.msgQueue
267
- return (list(Error("already attached")))
272
+ return (intop) // already attached
268
273
  intop.sender(intop.botDestID, {
269
274
  id: "attached",
270
275
  guid: intop.instanceID
@@ -294,7 +299,7 @@ closure InTop(track, options, local intop, nlregex, str, error) {
294
299
  //
295
300
  intop.termDetach = function termDetach() {
296
301
  if intop.msgQueue
297
- return (list(Error("already detached")))
302
+ return // already detached
298
303
  intop.term.SetNotify()
299
304
  intop.msgQueue = []
300
305
  intop.debugger.detach(intop)
@@ -310,19 +315,17 @@ closure InTop(track, options, local intop, nlregex, str, error) {
310
315
 
311
316
  // close
312
317
  //
313
- // Close our access to the worker.
318
+ // Close our access to the worker, but it can be reopened.
314
319
  //
315
320
  intop.close = function close() {
316
- if !intop.instanceID
321
+ if !intop.botDestID
317
322
  return
318
323
  intop.sender(intop.botDestID, {
319
324
  id: "unsubscribe"
320
325
  })
321
- intop.execClosed(Error("instance closed"))
322
- track.delete(intop)
323
- termDetach()
326
+ intop.botDestID = false
324
327
  App.imp.unregister(intop.instanceID)
325
- intop.instanceID = false
328
+ intop.sender = false
326
329
  }
327
330
 
328
331
  // destroy
@@ -333,11 +336,17 @@ closure InTop(track, options, local intop, nlregex, str, error) {
333
336
  if intop.destroyed
334
337
  return
335
338
  intop.destroyed = true
339
+ intop.execClosed(Error("instance closed"))
336
340
  intop.sender(intop.botDestID, {
337
341
  id: "destroy",
338
342
  guid: intop.instanceID
339
343
  })
340
- close()
344
+ track.delete(intop)
345
+ termDetach()
346
+ sleep(10)
347
+ App.imp.unregister(intop.instanceID)
348
+ intop.sender = false
349
+ intop.cleanup() // child classes release resources
341
350
  }
342
351
 
343
352
  // open
@@ -347,24 +356,27 @@ closure InTop(track, options, local intop, nlregex, str, error) {
347
356
  intop.open = closure open(inbotID) {
348
357
  if intop.destroyed
349
358
  return (list(Error("InTop already destroyed")))
350
- if intop.instanceID {
351
- if inbotID != intop.botDestID
352
- return (list(Error("InTop already open")))
353
- } else
354
- intop.instanceID = UUID()
359
+ if intop.botDestID == inbotID
360
+ return (list(false, { reopen: true }))
361
+ if intop.botDestID
362
+ close()
355
363
  intop.botDestID = inbotID
356
- intop.sender = App.imp.register(intop, {
357
- name: options.name
358
- type: options.type
359
- instanceID: intop.instanceID
360
- services: [ "naan-executor" ]
361
- })
364
+ if !intop.instanceID {
365
+ intop.instanceID = App.imp.getUUID("InTop(${options.name}) instanceID")
366
+ track.add(intop)
367
+ }
368
+ if !intop.sender
369
+ intop.sender = App.imp.register(intop, {
370
+ name: options.name
371
+ type: options.type
372
+ instanceID: intop.instanceID
373
+ services: [ "naan-executor" ]
374
+ })
362
375
  `(error) = intop.sender(intop.botDestID, {
363
376
  id: "subscribe"
364
377
  })
365
378
  if error
366
379
  return(list(error))
367
- track.add(intop)
368
380
  list(false, { ok: true })
369
381
  }
370
382