Minsky
addon.cc
Go to the documentation of this file.
1 /*
2  @copyright Steve Keen 2021
3  @author Russell Standish
4  This file is part of Minsky.
5 
6  Minsky is free software: you can redistribute it and/or modify it
7  under the terms of the GNU General Public License as published by
8  the Free Software Foundation, either version 3 of the License, or
9  (at your option) any later version.
10 
11  Minsky is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  GNU General Public License for more details.
15 
16  You should have received a copy of the GNU General Public License
17  along with Minsky. If not, see <http://www.gnu.org/licenses/>.
18 */
19 
21 #include <napi.h>
22 #include "RESTMinsky.h"
23 #include "minsky_epilogue.h"
24 
25 #include <exception>
26 #include <atomic>
27 #include <future>
28 #include <filesystem>
29 
30 #ifdef _WIN32
31 #include <time.h>
32 #else
33 #include <sys/times.h>
34 #endif
35 
36 using namespace Napi;
37 using namespace std;
38 using namespace classdesc;
39 using namespace boost::posix_time;
40 
41 namespace
42 {
43 
44  // all NAPI calls involving an Env must be called on Javascript's
45  // thread. In order to pass something back to Javascript's thread,
46  // we need to use a ThreadSafeFunction
48  {
49  Promise::Deferred promise;
50  bool success;
51  string result;
52 
53  void doResolve();
54  void resolve(const std::string& result) {
55  success=true;
56  this->result=result;
57  doResolve();
58  }
59  void reject(const std::string& error) {
60  success=false;
61  result=error;
62  doResolve();
63  }
64 
65  PromiseResolver(const Napi::Env& env):
66  promise(env)
67  {}
68  };
69 
70  // function called back from Javascript eventually
71  void resolvePromise(Napi::Env env, Napi::Function, void*, PromiseResolver* promiseResolver)
72  {
73  if (!promiseResolver) return;
74  // Javascript needs the result returned as UTF-16.
75  auto result=String::New(env, utf_to_utf<char16_t>(promiseResolver->result));
76  if (promiseResolver->success)
77  promiseResolver->promise.Resolve(result);
78  else
79  promiseResolver->promise.Reject(result);
80  delete promiseResolver; // cleans up object allocated in Command::Command() below
81  }
82 
83  TypedThreadSafeFunction<void,PromiseResolver,resolvePromise> tsPromiseResolver;
84 
85  void PromiseResolver::doResolve() {
86  tsPromiseResolver.BlockingCall(this);
87  }
88 
89  struct Command
90  {
92  string command;
93  json_pack_t arguments;
94  Command(const Napi::Env& env, const string& command, const json_pack_t& arguments):
95  promiseResolver(new PromiseResolver(env)), // ownership passed to JS interpreter
96  command(command),
97  arguments(arguments) {}
98  };
99 
100  struct Times
101  {
102  unsigned long counts;
103  double elapsed, user, system;
104  clock_t start_e, start_u, start_s;
105  bool started;
106  Times(): counts(0), elapsed(0), user(0), system(0), started(false) {}
107 
108  void start()
109  {
110  if (started) return;
111  started = true;
112  counts++;
113 
114 #ifdef _WIN32
115  start_e=clock();
116 #else
117  struct tms tbuf;
118  start_e = times(&tbuf);
119  start_u = tbuf.tms_utime;
120  start_s = tbuf.tms_stime;
121 #endif
122  }
123 
124  void stop()
125  {
126  if (!started) return;
127  started = false;
128 
129 #ifdef _WIN32
130  static const double seconds=1.0/CLOCKS_PER_SEC;
131  elapsed+=(clock()-start_e)*seconds;
132 #else
133 #ifdef __linux__
134  static const double seconds=1.0/sysconf(_SC_CLK_TCK);
135 #else
136  static const double seconds=1.0/CLK_TCK;
137 #endif
138  struct tms tbuf;
139  elapsed += (times(&tbuf)-start_e)*seconds;
140  user += (tbuf.tms_utime-start_u)*seconds;
141  system += (tbuf.tms_stime-start_s)*seconds;
142 #endif
143  }
144  };
145 
146 #ifdef TIMERS
147  class Timer
148  {
149  Times& timer;
150  public:
151  Timer(Times& timer): timer(timer) {timer.start();}
152  ~Timer() {timer.stop();}
153  };
154 
155  class Timers: public map<string, Times>
156  {
157  Times overall;
158  public:
159  Timers() {overall.start();}
160  ~Timers() {
161  overall.stop();
162  vector<pair<string,Times>> times(begin(),end());
163  sort(times.begin(),times.end(),[](auto& i, auto& j){return i.second.elapsed>j.second.elapsed;});
164  cout<<setw(40)<<"Times"<<setw(10)<<"Elapsed"<<setw(10)<<"User"<<setw(10)<<"System"<<endl;
165  cout<<setw(40)<<"Overall"<<setw(10)<<overall.elapsed<<setw(10)<<overall.user<<setw(10)<<overall.system<<endl;
166  for (auto& [command,timer]: times)
167  cout<<setw(40)<<command<<setw(10)<<timer.elapsed<<setw(10)<<timer.user<<setw(10)<<timer.system<<endl;
168  }
169  };
170 #else // dummiable
171  struct Timer
172  {
173  Timer(int) {}
174  };
175  struct Timers
176  {
177  int operator[](const string&) {return 0;}
178  };
179 #endif
180 
181 }
182 
183 namespace minsky
184 {
185  namespace
186  {
187  // ensure access to only one global Minsky object at a time,
188  // particular needed for jest tests, which run in parallel
190 
191  struct AddOnMinsky: public RESTMinsky
192  {
193  Timers timers;
194  deque<unique_ptr<Command>> minskyCommands;
195  mutex cmdMutex;
196  atomic<bool> running{true};
197  std::thread thread;
198  bool inputBufferExceeded=false;
199 
200  AddOnMinsky(): thread([this](){run();}) {
201  flags=0;
202  }
203 
205  running=false;
206  if (thread.joinable()) thread.join();
207  }
208 
209  Value queueCommand(Env env, string command, const json_pack_t& arguments)
210  {
211  static const std::string sync=".$sync";
212  if (command.ends_with(sync))
213  {
214  command.erase(command.size()-sync.length());
215  // Javascript needs the result returned as UTF-16.
216  return String::New(env, utf_to_utf<char16_t>(doCommand(command, arguments)));
217  }
218 #if defined(_WIN32) || defined(MAC_OSX_TK)
219  // renderFrame needs to be called synchronously, otherwise inexplicable hangs occur on Windows.
220  if (command.ends_with(".renderFrame"))
221  return String::New(env, utf_to_utf<char16_t>(doCommand(command, arguments)));
222 #endif
223  if (minskyCommands.size()>20)
224  {
225  if (!inputBufferExceeded) setBusyCursor();
226  inputBufferExceeded=true;
227  return env.Null();
228  }
229  if (inputBufferExceeded) clearBusyCursor(); // single shot clear of busy curser
230  inputBufferExceeded=false;
231  const lock_guard<mutex> lock(cmdMutex);
232  minskyCommands.emplace_back(new Command{env,command,arguments});
233  return minskyCommands.back()->promiseResolver->promise.Promise();
234  }
235 
236  string doCommand(const string& command, const json_pack_t& arguments)
237  {
238  const lock_guard<mutex> lock(minskyCmdMutex);
239  const Timer timer(timers[command]);
240 
241  // if reset requested, postpone it
242  if (reset_flag()) requestReset();
243 
244  civita::ITensor::cancel(false);
245  // disable quoting wide characters in UTF-8 strings
246  auto result=write(registry.process(command, arguments)->asBuffer(),json5_parser::raw_utf8);
247  commandHook(command,arguments);
248  return result;
249  }
250 
252  {
253  const lock_guard<mutex> lock(minskyCmdMutex);
254  const Timer timer(timers["draw"]);
255  for (auto i: nativeWindowsToRedraw)
256  try
257  {
258  i->draw();
259  }
260  catch (const std::exception& ex)
261  {
262  /* absorb and log any exceptions, cannot do anything anyway */
263  cerr << ex.what() << endl;
264  break;
265  }
266  catch (...) {break;}
267  nativeWindowsToRedraw.clear();
268  }
269 
270  // arrange for native window drawing to happen on node's main thread, required for MacOSX.
271  atomic<bool> drawLaunched{false};
272  static void tsDrawNativeWindows(Napi::Env env, Napi::Function, AddOnMinsky* minsky, void*)
273  {minsky->macOSXDrawNativeWindows();}
274 
275  TypedThreadSafeFunction<AddOnMinsky,void,tsDrawNativeWindows> tsDrawNativeWindows_;
276 
278  {
279  const lock_guard<mutex> lock(minskyCmdMutex);
280  const Timer timer(timers["draw"]);
281  for (auto i: nativeWindowsToRedraw)
282  i->macOSXRedraw();
283  nativeWindowsToRedraw.clear();
284  drawLaunched=false;
285  }
286 
288  {
289  drawLaunched=true;
290  tsDrawNativeWindows_.BlockingCall(this);
291  }
292 
293  void run()
294  {
295 #if defined(_PTHREAD_H) && defined(__USE_GNU) && !defined(NDEBUG)
296  pthread_setname_np(pthread_self(),"minsky thread");
297 #endif
298  while (running)
299  {
300  unique_ptr<Command> command;
301  {
302  const lock_guard<mutex> lock(cmdMutex);
303  if (!minskyCommands.empty())
304  {
305  command=std::move(minskyCommands.front());
306  minskyCommands.pop_front();
307  }
308  }
309 
310  if (!command) // perform housekeeping
311  {
312  if (reset_flag() && resetDuration<resetPostponedThreshold && resetAt<std::chrono::system_clock::now())
313  try
314  {
315  const lock_guard<mutex> lock(minskyCmdMutex);
316  if (reset_flag()) // check again, in case another thread got there first
317  {
318  const Timer timer(timers["minsky.reset"]);
319  reset();
320  }
321  }
322  catch (...)
323  {flags&=~reset_needed;}
324 #ifdef MAC_OSX_TK
325  if (!drawLaunched && nativeWindowsToRedraw.size())
326  macOSXLaunchDrawNativeWindows();
327 #else
328  drawNativeWindows();
329 #endif
330  if (inputBufferExceeded && minskyCommands.empty())
331  {
332  // clears busy cursor when no commands are being received or processed
333  clearBusyCursor();
334  inputBufferExceeded=false;
335  }
336  this_thread::sleep_for(chrono::milliseconds(10));
337  continue;
338  }
339 
340  if (!running) return;
341  try
342  {
343  auto result=doCommand(command->command, command->arguments);
344  if (!running) return; // prevent crashes on shutdown
345  command->promiseResolver->resolve(result);
346  }
347  catch (const std::exception& ex)
348  {
349  if (!running) return; // prevent crashes on shutdown
350  command->promiseResolver->reject(ex.what());
351  }
352  catch (...)
353  {
354  if (!running) return; // prevent crashes on shutdown
355  command->promiseResolver->reject("Unknown exception");
356  }
357  }
358  }
359 
360  mutable string theMessage;
361  mutable vector<string> messageButtons;
362  mutable promise<unsigned> userResponse;
363  static void messageCallback(Napi::Env env, Napi::Function fn, void*, AddOnMinsky* addonMinsky)
364  {
365  auto buttons=Array::New(env);
366  for (unsigned i=0; i< addonMinsky->messageButtons.size(); ++i)
367  buttons[i]=String::New(env,addonMinsky->messageButtons[i]);
368  try {
369  addonMinsky->userResponse.set_value(fn({String::New(env,addonMinsky->theMessage), buttons}).As<Number>().Int32Value());
370  } catch (...) {/* may fail if multiple messages posted in rapid succession */}
371  }
372 
373  bool messageCallbackSet=false;
374  TypedThreadSafeFunction<void,AddOnMinsky,messageCallback> tsMessageCallback;
375  Value setMessageCallback(const Napi::CallbackInfo& info)
376  {
377  const Env env = info.Env();
378  if (info.Length()<1 || !info[0].IsFunction())
379  {
380  Napi::Error::New(env, "Callback not provided").ThrowAsJavaScriptException();
381  }
382  messageCallbackSet=true;
383  tsMessageCallback=TypedThreadSafeFunction<void,AddOnMinsky,messageCallback>::New
384  (env,info[0].As<Function>(), "message",0,2,nullptr);
385 
386  return env.Null();
387  }
388 
389 
390  void message(const std::string& msg) override
391  {
392  if (!messageCallbackSet) return;
393  theMessage=msg;
394  messageButtons.clear(); // empty buttons imply a single OK button
395  userResponse={}; //reset the promise
396  tsMessageCallback.BlockingCall(const_cast<AddOnMinsky*>(this));
397  }
398 
399  MemCheckResult checkMemAllocation(std::size_t bytes) const override {
400  // Electron restricts heap size to 4GiB, regardless of how much physical memory is present
401  if (messageCallbackSet && bytes>/*physicalMem()*/4ULL*1024*1024*1024)
402  {
403  theMessage="Allocation will use more than 50% of available memory. Do you want to proceed?";
404  messageButtons={"No","Yes"};
405  userResponse={}; //reset the promise
406  tsMessageCallback.BlockingCall(const_cast<AddOnMinsky*>(this));
407  return userResponse.get_future().get()? proceed: abort;
408  }
409  return OK;
410  }
411 
412  // signature of last param must be non-const
413  static void busyCursorCallback(Napi::Env env, Napi::Function fn, void*, bool* busy) // NOLINT
414  {
415  fn({Boolean::New(env,*busy)});
416  }
417 
418  bool busyCursorCallbackSet=false;
419  TypedThreadSafeFunction<void,bool,busyCursorCallback> tsBusyCursorCallback;
420  Value setBusyCursorCallback(const Napi::CallbackInfo& info)
421  {
422  const Env env = info.Env();
423  if (info.Length()<1 || !info[0].IsFunction())
424  {
425  Napi::Error::New(env, "Callback not provided").ThrowAsJavaScriptException();
426  }
427  busyCursorCallbackSet=true;
428  tsBusyCursorCallback=TypedThreadSafeFunction<void,bool,busyCursorCallback>::New
429  (env,info[0].As<Function>(), "setBusyCursor",0,2,nullptr);
430  return env.Null();
431  }
432 
433  bool busyCursor=false;
434  void doBusyCursor(bool bc)
435  {
436  if (!busyCursorCallbackSet) return;
437  busyCursor=bc;
438  tsBusyCursorCallback.BlockingCall(&busyCursor);
439  }
440  void setBusyCursor() override
441  {doBusyCursor(true);}
442  void clearBusyCursor() override
443  {doBusyCursor(false);}
444 
445  static void progressCallback(Napi::Env env, Napi::Function fn, void*, AddOnMinsky* addon)
446  {
447  fn({String::New(env,addon->progressTitle), Number::New(env,addon->progressValue)});
448  }
449 
450  int progressValue=0;
452  void progress(const string& title, int x) override
453  {
454  if (!progressCallbackSet) return;
455  progressValue=x;
456  progressTitle=title;
457  tsProgressCallback.BlockingCall(this);
458  }
459  bool progressCallbackSet=false;
460  TypedThreadSafeFunction<void,AddOnMinsky,progressCallback> tsProgressCallback;
461  Value setProgressCallback(const Napi::CallbackInfo& info)
462  {
463  const Env env = info.Env();
464  if (info.Length()<1 || !info[0].IsFunction())
465  {
466  Napi::Error::New(env, "Callback not provided").ThrowAsJavaScriptException();
467  }
468  progressCallbackSet=true;
469  tsProgressCallback=TypedThreadSafeFunction<void,AddOnMinsky,progressCallback>::New
470  (env,info[0].As<Function>(), "progress",0,2,nullptr);
471  return env.Null();
472  }
473 
474  static void bookmarkRefreshCallback(Napi::Env env, Napi::Function fn, void*, AddOnMinsky* addon)
475  {
476  fn({});
477  }
478 
479  void bookmarkRefresh() override
480  {
481  if (!bookmarkRefreshSet) return;
482  tsBookmarkRefreshCallback.BlockingCall(this);
483  }
484  bool bookmarkRefreshSet=false;
485  TypedThreadSafeFunction<void,AddOnMinsky,bookmarkRefreshCallback> tsBookmarkRefreshCallback;
486  Value setBookmarkRefreshCallback(const Napi::CallbackInfo& info)
487  {
488  const Env env = info.Env();
489  if (info.Length()<1 || !info[0].IsFunction())
490  {
491  Napi::Error::New(env, "Callback not provided").ThrowAsJavaScriptException();
492  }
493  bookmarkRefreshSet=true;
494  tsBookmarkRefreshCallback=TypedThreadSafeFunction<void,AddOnMinsky,bookmarkRefreshCallback>::New
495  (env,info[0].As<Function>(), "refreshBookmark",0,2,nullptr);
496  return env.Null();
497  }
498 
499  static void resetScrollCallback(Napi::Env env, Napi::Function fn, void*, AddOnMinsky* addon)
500  {
501  fn({});
502  }
503 
504  void resetScroll() override
505  {
506  if (!resetScrollSet) return;
507  tsResetScrollCallback.BlockingCall(this);
508  }
509  bool resetScrollSet=false;
510  TypedThreadSafeFunction<void,AddOnMinsky,resetScrollCallback> tsResetScrollCallback;
511  Value setResetScrollCallback(const Napi::CallbackInfo& info)
512  {
513  const Env env = info.Env();
514  if (info.Length()<1 || !info[0].IsFunction())
515  {
516  Napi::Error::New(env, "Callback not provided").ThrowAsJavaScriptException();
517  }
518  resetScrollSet=true;
519  tsResetScrollCallback=TypedThreadSafeFunction<void,AddOnMinsky,resetScrollCallback>::New
520  (env,info[0].As<Function>(), "resetScroll",0,2,nullptr);
521  return env.Null();
522  }
524  {
525  string file=(std::filesystem::current_path()/"savedRavelSession.rvl").string();
526  if (autoSaver) {
527  autoSaver->killThread();
528  file=autoSaver->fileName;
529  }
530  save(file);
531 
532  theMessage="Out of memory, saving to autosave file: "+file;
533  messageButtons={"OK"};
534  userResponse={}; //reset the promise
535  tsMessageCallback.BlockingCall(const_cast<AddOnMinsky*>(this));
536  userResponse.get_future().get();
537  }
538  };
539 
540  Minsky s_minsky; //This object is not really used, needed to avoid a null dereference in minsky()
541  Minsky* l_minsky=&s_minsky; //weak reference to global Minsky object
542  }
543 
544  Minsky& minsky() {return *l_minsky;}
545 
546  // Needed only to solve linkage problems
547  LocalMinsky::LocalMinsky(Minsky& minsky) {}
548  LocalMinsky::~LocalMinsky() {}
549  void doOneEvent(bool idleTasksOnly) {}
550 
551 }
552 
553 void handleSignal(int)
554 {
555  static_cast<minsky::AddOnMinsky&>(minsky::minsky()).outOfMemoryHandler();
556  exit(1); // NOLINT
557 }
558 
559 struct MinskyAddon: public Addon<MinskyAddon>
560 {
561  // Actual global minsky object needs to be declared here, to ensure
562  // threads are started and torn down at the right point in time.
563  minsky::AddOnMinsky addOnMinsky;
564 
565  MinskyAddon(Env env, Napi::Object exports)
566  {
567  minsky::l_minsky=&addOnMinsky; // set the global minsky object
568  tsPromiseResolver=TypedThreadSafeFunction<void,PromiseResolver,resolvePromise>::
569  New(env,"TSResolver", 0, 2, nullptr);
570  addOnMinsky.tsDrawNativeWindows_=
571  TypedThreadSafeFunction<minsky::AddOnMinsky,void,minsky::AddOnMinsky::tsDrawNativeWindows>::
572  New(env,"TSDrawNativeWindows",0, 2,&addOnMinsky);
573 
574  DefineAddon(exports, {
575  InstanceMethod("call", &MinskyAddon::call),
576  InstanceMethod("setMessageCallback", &MinskyAddon::setMessageCallback),
577  InstanceMethod("setBusyCursorCallback", &MinskyAddon::setBusyCursorCallback),
578  InstanceMethod("setProgressCallback", &MinskyAddon::setProgressCallback),
579  InstanceMethod("setBookmarkRefreshCallback", &MinskyAddon::setBookmarkRefreshCallback),
580  InstanceMethod("setResetScrollCallback", &MinskyAddon::setResetScrollCallback),
581  InstanceMethod("cancelProgress", &MinskyAddon::cancelProgress)
582  });
583 #ifndef _WIN32
584  signal(SIGTRAP,handleSignal);
585 #endif
586  }
587 
588  Value setMessageCallback(const Napi::CallbackInfo& info) {return addOnMinsky.setMessageCallback(info);}
589  Value setBusyCursorCallback(const Napi::CallbackInfo& info) {return addOnMinsky.setBusyCursorCallback(info);}
590  Value setProgressCallback(const Napi::CallbackInfo& info) {return addOnMinsky.setProgressCallback(info);}
591  Value setBookmarkRefreshCallback(const Napi::CallbackInfo& info) {return addOnMinsky.setBookmarkRefreshCallback(info);}
592  Value setResetScrollCallback(const Napi::CallbackInfo& info) {return addOnMinsky.setResetScrollCallback(info);}
593  Value cancelProgress(const Napi::CallbackInfo& info) {
594  *addOnMinsky.progressState.cancel=true;
595  civita::ITensor::cancel(true);
596  return info.Env().Null();
597  }
598 
599 
600  Value call(const Napi::CallbackInfo& info)
601  {
602  const Env env = info.Env();
603  if (info.Length() < 1)
604  {
605  Napi::TypeError::New(env, "Needs to be call(endpoint[, arguments])").ThrowAsJavaScriptException();
606  return env.Null();
607  }
608 
609 #if defined(_PTHREAD_H) && defined(__USE_GNU) && !defined(NDEBUG)
610  pthread_setname_np(pthread_self(),"addon thread");
611 #endif
612 
613  try
614  {
615  json_pack_t arguments(json5_parser::mValue::null);
616  if (info.Length()>1)
617  {
618  const string jsonArguments=info[1].ToString();
619  if (!jsonArguments.empty())
620  read(jsonArguments, arguments);
621  }
622  return addOnMinsky.queueCommand(env,info[0].ToString(),arguments);
623  }
624  catch (const std::exception& ex)
625  {
626  // throw C++ exception as Javascript exception
627  Napi::Error::New(env, ex.what()).ThrowAsJavaScriptException();
628  return env.Null();
629  }
630  catch (...)
631  {
632  Napi::Error::New(env, "unknown exception caught").ThrowAsJavaScriptException();
633  return env.Null();
634  }
635  }
636 };
637 
Command(const Napi::Env &env, const string &command, const json_pack_t &arguments)
Definition: addon.cc:94
Value setBusyCursorCallback(const Napi::CallbackInfo &info)
Definition: addon.cc:589
Value setBookmarkRefreshCallback(const Napi::CallbackInfo &info)
Definition: addon.cc:486
void doOneEvent(bool idletasksOnly)
checks if any GUI events are waiting, and proces an event if so
Definition: tclmain.cc:161
void message(const std::string &msg) override
display a message in a popup box on the GUI
Definition: addon.cc:390
void reject(const std::string &error)
Definition: addon.cc:59
reset
Definition: minsky.tcl:1325
static void resetScrollCallback(Napi::Env env, Napi::Function fn, void *, AddOnMinsky *addon)
Definition: addon.cc:499
TypedThreadSafeFunction< void, AddOnMinsky, messageCallback > tsMessageCallback
Definition: addon.cc:374
minsky::Minsky minsky
Definition: pyminsky.cc:28
MemCheckResult
check whether to proceed or abort, given a request to allocate bytes of memory. Implemented in Minsky...
Definition: minsky.h:463
STL namespace.
TypedThreadSafeFunction< void, AddOnMinsky, bookmarkRefreshCallback > tsBookmarkRefreshCallback
Definition: addon.cc:485
static void tsDrawNativeWindows(Napi::Env env, Napi::Function, AddOnMinsky *minsky, void *)
Definition: addon.cc:272
minsky::AddOnMinsky addOnMinsky
Definition: addon.cc:563
static void messageCallback(Napi::Env env, Napi::Function fn, void *, AddOnMinsky *addonMinsky)
Definition: addon.cc:363
void resetScroll() override
reset main window scroll bars after model has been panned
Definition: addon.cc:504
PromiseResolver * promiseResolver
Definition: addon.cc:91
TypedThreadSafeFunction< AddOnMinsky, void, tsDrawNativeWindows > tsDrawNativeWindows_
Definition: addon.cc:275
string doCommand(const string &command, const json_pack_t &arguments)
Definition: addon.cc:236
Value setMessageCallback(const Napi::CallbackInfo &info)
Definition: addon.cc:588
Creation and access to the minskyTCL_obj object, which has code to record whenever Minsky&#39;s state cha...
Definition: constMap.h:22
TypedThreadSafeFunction< void, bool, busyCursorCallback > tsBusyCursorCallback
Definition: addon.cc:419
MinskyAddon(Env env, Napi::Object exports)
Definition: addon.cc:565
void bookmarkRefresh() override
refresh the bookmark menu after changes
Definition: addon.cc:479
static void progressCallback(Napi::Env env, Napi::Function fn, void *, AddOnMinsky *addon)
Definition: addon.cc:445
void progress(const string &title, int x) override
set progress bar, out of 100, labelling the progress bar with title
Definition: addon.cc:452
void resolve(const std::string &result)
Definition: addon.cc:54
Value setProgressCallback(const Napi::CallbackInfo &info)
Definition: addon.cc:461
deque< unique_ptr< Command > > minskyCommands
Definition: addon.cc:194
Value setResetScrollCallback(const Napi::CallbackInfo &info)
Definition: addon.cc:592
static void busyCursorCallback(Napi::Env env, Napi::Function fn, void *, bool *busy)
Definition: addon.cc:413
Value setBusyCursorCallback(const Napi::CallbackInfo &info)
Definition: addon.cc:420
TypedThreadSafeFunction< void, AddOnMinsky, progressCallback > tsProgressCallback
Definition: addon.cc:460
void setBusyCursor() override
set/clear busy cursor in GUI
Definition: addon.cc:440
TypedThreadSafeFunction< void, AddOnMinsky, resetScrollCallback > tsResetScrollCallback
Definition: addon.cc:510
Value queueCommand(Env env, string command, const json_pack_t &arguments)
Definition: addon.cc:209
Value cancelProgress(const Napi::CallbackInfo &info)
Definition: addon.cc:593
Value setMessageCallback(const Napi::CallbackInfo &info)
Definition: addon.cc:375
exit
Definition: minsky.tcl:1757
Value setBookmarkRefreshCallback(const Napi::CallbackInfo &info)
Definition: addon.cc:591
Value setResetScrollCallback(const Napi::CallbackInfo &info)
Definition: addon.cc:511
Value setProgressCallback(const Napi::CallbackInfo &info)
Definition: addon.cc:590
MemCheckResult checkMemAllocation(std::size_t bytes) const override
Definition: addon.cc:399
Minsky & minsky()
global minsky object
Definition: minskyTCL.cc:51
Value call(const Napi::CallbackInfo &info)
Definition: addon.cc:600
save
Definition: minsky.tcl:1469
NODE_API_ADDON(MinskyAddon)
int operator[](const string &)
Definition: addon.cc:177
TypedThreadSafeFunction< void, PromiseResolver, resolvePromise > tsPromiseResolver
Definition: addon.cc:83
void resolvePromise(Napi::Env env, Napi::Function, void *, PromiseResolver *promiseResolver)
Definition: addon.cc:71
static void bookmarkRefreshCallback(Napi::Env env, Napi::Function fn, void *, AddOnMinsky *addon)
Definition: addon.cc:474
void handleSignal(int)
Definition: addon.cc:553