33 #include <sys/times.h> 59 void reject(
const std::string& error) {
73 if (!promiseResolver)
return;
75 auto result=String::New(env, utf_to_utf<char16_t>(promiseResolver->
result));
77 promiseResolver->
promise.Resolve(result);
79 promiseResolver->
promise.Reject(result);
80 delete promiseResolver;
85 void PromiseResolver::doResolve() {
94 Command(
const Napi::Env& env,
const string& command,
const json_pack_t& arguments):
97 arguments(arguments) {}
106 Times(): counts(0), elapsed(0), user(0), system(0), started(false) {}
118 start_e = times(&tbuf);
119 start_u = tbuf.tms_utime;
120 start_s = tbuf.tms_stime;
126 if (!started)
return;
130 static const double seconds=1.0/CLOCKS_PER_SEC;
131 elapsed+=(clock()-start_e)*seconds;
134 static const double seconds=1.0/sysconf(_SC_CLK_TCK);
136 static const double seconds=1.0/CLK_TCK;
139 elapsed += (times(&tbuf)-start_e)*seconds;
140 user += (tbuf.tms_utime-start_u)*seconds;
141 system += (tbuf.tms_stime-start_s)*seconds;
151 Timer(Times& timer): timer(timer) {timer.start();}
152 ~Timer() {timer.stop();}
155 class Timers:
public map<string, Times>
159 Timers() {overall.start();}
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;
196 atomic<bool> running{
true};
198 bool inputBufferExceeded=
false;
206 if (thread.joinable()) thread.join();
209 Value
queueCommand(Env env,
string command,
const json_pack_t& arguments)
211 static const std::string sync=
".$sync";
212 if (command.ends_with(sync))
214 command.erase(command.size()-sync.length());
216 return String::New(env, utf_to_utf<char16_t>(doCommand(command, arguments)));
218 #if defined(_WIN32) || defined(MAC_OSX_TK) 220 if (command.ends_with(
".renderFrame"))
221 return String::New(env, utf_to_utf<char16_t>(doCommand(command, arguments)));
223 if (minskyCommands.size()>20)
225 if (!inputBufferExceeded) setBusyCursor();
226 inputBufferExceeded=
true;
229 if (inputBufferExceeded) clearBusyCursor();
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();
236 string doCommand(
const string& command,
const json_pack_t& arguments)
239 const Timer timer(timers[command]);
242 if (reset_flag()) requestReset();
244 civita::ITensor::cancel(
false);
246 auto result=write(registry.process(command, arguments)->asBuffer(),json5_parser::raw_utf8);
247 commandHook(command,arguments);
254 const Timer timer(timers[
"draw"]);
255 for (
auto i: nativeWindowsToRedraw)
260 catch (
const std::exception& ex)
263 cerr << ex.what() << endl;
267 nativeWindowsToRedraw.clear();
271 atomic<bool> drawLaunched{
false};
273 {
minsky->macOSXDrawNativeWindows();}
280 const Timer timer(timers[
"draw"]);
281 for (
auto i: nativeWindowsToRedraw)
283 nativeWindowsToRedraw.clear();
290 tsDrawNativeWindows_.BlockingCall(
this);
295 #if defined(_PTHREAD_H) && defined(__USE_GNU) && !defined(NDEBUG) 296 pthread_setname_np(pthread_self(),
"minsky thread");
300 unique_ptr<Command> command;
302 const lock_guard<mutex> lock(cmdMutex);
303 if (!minskyCommands.empty())
305 command=std::move(minskyCommands.front());
306 minskyCommands.pop_front();
312 if (reset_flag() && resetDuration<resetPostponedThreshold && resetAt<std::chrono::system_clock::now())
318 const Timer timer(timers[
"minsky.reset"]);
323 {flags&=~reset_needed;}
325 if (!drawLaunched && nativeWindowsToRedraw.size())
326 macOSXLaunchDrawNativeWindows();
330 if (inputBufferExceeded && minskyCommands.empty())
334 inputBufferExceeded=
false;
336 this_thread::sleep_for(chrono::milliseconds(10));
340 if (!running)
return;
343 auto result=doCommand(command->command, command->arguments);
344 if (!running)
return;
345 command->promiseResolver->resolve(result);
347 catch (
const std::exception& ex)
349 if (!running)
return;
350 command->promiseResolver->reject(ex.what());
354 if (!running)
return;
355 command->promiseResolver->reject(
"Unknown exception");
365 auto buttons=Array::New(env);
369 addonMinsky->
userResponse.set_value(fn({String::New(env,addonMinsky->
theMessage), buttons}).As<Number>().Int32Value());
373 bool messageCallbackSet=
false;
377 const Env env = info.Env();
378 if (info.Length()<1 || !info[0].IsFunction())
380 Napi::Error::New(env,
"Callback not provided").ThrowAsJavaScriptException();
382 messageCallbackSet=
true;
383 tsMessageCallback=TypedThreadSafeFunction<void,AddOnMinsky,messageCallback>::New
384 (env,info[0].As<Function>(),
"message",0,2,
nullptr);
392 if (!messageCallbackSet)
return;
394 messageButtons.clear();
396 tsMessageCallback.BlockingCall(const_cast<AddOnMinsky*>(
this));
401 if (messageCallbackSet && bytes>4ULL*1024*1024*1024)
403 theMessage=
"Allocation will use more than 50% of available memory. Do you want to proceed?";
404 messageButtons={
"No",
"Yes"};
406 tsMessageCallback.BlockingCall(const_cast<AddOnMinsky*>(
this));
407 return userResponse.get_future().get()? proceed: abort;
415 fn({Boolean::New(env,*busy)});
418 bool busyCursorCallbackSet=
false;
422 const Env env = info.Env();
423 if (info.Length()<1 || !info[0].IsFunction())
425 Napi::Error::New(env,
"Callback not provided").ThrowAsJavaScriptException();
427 busyCursorCallbackSet=
true;
428 tsBusyCursorCallback=TypedThreadSafeFunction<void,bool,busyCursorCallback>::New
429 (env,info[0].As<Function>(),
"setBusyCursor",0,2,
nullptr);
433 bool busyCursor=
false;
436 if (!busyCursorCallbackSet)
return;
438 tsBusyCursorCallback.BlockingCall(&busyCursor);
441 {doBusyCursor(
true);}
443 {doBusyCursor(
false);}
454 if (!progressCallbackSet)
return;
457 tsProgressCallback.BlockingCall(
this);
459 bool progressCallbackSet=
false;
463 const Env env = info.Env();
464 if (info.Length()<1 || !info[0].IsFunction())
466 Napi::Error::New(env,
"Callback not provided").ThrowAsJavaScriptException();
468 progressCallbackSet=
true;
469 tsProgressCallback=TypedThreadSafeFunction<void,AddOnMinsky,progressCallback>::New
470 (env,info[0].As<Function>(),
"progress",0,2,
nullptr);
481 if (!bookmarkRefreshSet)
return;
482 tsBookmarkRefreshCallback.BlockingCall(
this);
484 bool bookmarkRefreshSet=
false;
488 const Env env = info.Env();
489 if (info.Length()<1 || !info[0].IsFunction())
491 Napi::Error::New(env,
"Callback not provided").ThrowAsJavaScriptException();
493 bookmarkRefreshSet=
true;
494 tsBookmarkRefreshCallback=TypedThreadSafeFunction<void,AddOnMinsky,bookmarkRefreshCallback>::New
495 (env,info[0].As<Function>(),
"refreshBookmark",0,2,
nullptr);
506 if (!resetScrollSet)
return;
507 tsResetScrollCallback.BlockingCall(
this);
509 bool resetScrollSet=
false;
513 const Env env = info.Env();
514 if (info.Length()<1 || !info[0].IsFunction())
516 Napi::Error::New(env,
"Callback not provided").ThrowAsJavaScriptException();
519 tsResetScrollCallback=TypedThreadSafeFunction<void,AddOnMinsky,resetScrollCallback>::New
520 (env,info[0].As<Function>(),
"resetScroll",0,2,
nullptr);
525 string file=(std::filesystem::current_path()/
"savedRavelSession.rvl").
string();
527 autoSaver->killThread();
528 file=autoSaver->fileName;
532 theMessage=
"Out of memory, saving to autosave file: "+file;
533 messageButtons={
"OK"};
535 tsMessageCallback.BlockingCall(const_cast<AddOnMinsky*>(
this));
536 userResponse.get_future().get();
547 LocalMinsky::LocalMinsky(Minsky&
minsky) {}
548 LocalMinsky::~LocalMinsky() {}
555 static_cast<minsky::AddOnMinsky&
>(
minsky::minsky()).outOfMemoryHandler();
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);
574 DefineAddon(exports, {
588 Value
setMessageCallback(
const Napi::CallbackInfo& info) {
return addOnMinsky.setMessageCallback(info);}
594 *addOnMinsky.progressState.cancel=
true;
595 civita::ITensor::cancel(
true);
596 return info.Env().Null();
600 Value
call(
const Napi::CallbackInfo& info)
602 const Env env = info.Env();
603 if (info.Length() < 1)
605 Napi::TypeError::New(env,
"Needs to be call(endpoint[, arguments])").ThrowAsJavaScriptException();
609 #if defined(_PTHREAD_H) && defined(__USE_GNU) && !defined(NDEBUG) 610 pthread_setname_np(pthread_self(),
"addon thread");
615 json_pack_t arguments(json5_parser::mValue::null);
618 const string jsonArguments=info[1].ToString();
619 if (!jsonArguments.empty())
620 read(jsonArguments, arguments);
622 return addOnMinsky.queueCommand(env,info[0].ToString(),arguments);
624 catch (
const std::exception& ex)
627 Napi::Error::New(env, ex.what()).ThrowAsJavaScriptException();
632 Napi::Error::New(env,
"unknown exception caught").ThrowAsJavaScriptException();
Command(const Napi::Env &env, const string &command, const json_pack_t &arguments)
Value setBusyCursorCallback(const Napi::CallbackInfo &info)
Promise::Deferred promise
Value setBookmarkRefreshCallback(const Napi::CallbackInfo &info)
void doOneEvent(bool idletasksOnly)
checks if any GUI events are waiting, and proces an event if so
void message(const std::string &msg) override
display a message in a popup box on the GUI
void reject(const std::string &error)
static void resetScrollCallback(Napi::Env env, Napi::Function fn, void *, AddOnMinsky *addon)
TypedThreadSafeFunction< void, AddOnMinsky, messageCallback > tsMessageCallback
MemCheckResult
check whether to proceed or abort, given a request to allocate bytes of memory. Implemented in Minsky...
TypedThreadSafeFunction< void, AddOnMinsky, bookmarkRefreshCallback > tsBookmarkRefreshCallback
static void tsDrawNativeWindows(Napi::Env env, Napi::Function, AddOnMinsky *minsky, void *)
minsky::AddOnMinsky addOnMinsky
static void messageCallback(Napi::Env env, Napi::Function fn, void *, AddOnMinsky *addonMinsky)
void resetScroll() override
reset main window scroll bars after model has been panned
PromiseResolver * promiseResolver
TypedThreadSafeFunction< AddOnMinsky, void, tsDrawNativeWindows > tsDrawNativeWindows_
string doCommand(const string &command, const json_pack_t &arguments)
Value setMessageCallback(const Napi::CallbackInfo &info)
void doBusyCursor(bool bc)
void macOSXLaunchDrawNativeWindows()
Creation and access to the minskyTCL_obj object, which has code to record whenever Minsky's state cha...
TypedThreadSafeFunction< void, bool, busyCursorCallback > tsBusyCursorCallback
MinskyAddon(Env env, Napi::Object exports)
void bookmarkRefresh() override
refresh the bookmark menu after changes
static void progressCallback(Napi::Env env, Napi::Function fn, void *, AddOnMinsky *addon)
void progress(const string &title, int x) override
set progress bar, out of 100, labelling the progress bar with title
void resolve(const std::string &result)
Value setProgressCallback(const Napi::CallbackInfo &info)
deque< unique_ptr< Command > > minskyCommands
vector< string > messageButtons
Value setResetScrollCallback(const Napi::CallbackInfo &info)
promise< unsigned > userResponse
static void busyCursorCallback(Napi::Env env, Napi::Function fn, void *, bool *busy)
Value setBusyCursorCallback(const Napi::CallbackInfo &info)
TypedThreadSafeFunction< void, AddOnMinsky, progressCallback > tsProgressCallback
void setBusyCursor() override
set/clear busy cursor in GUI
TypedThreadSafeFunction< void, AddOnMinsky, resetScrollCallback > tsResetScrollCallback
Value queueCommand(Env env, string command, const json_pack_t &arguments)
void macOSXDrawNativeWindows()
Value cancelProgress(const Napi::CallbackInfo &info)
Value setMessageCallback(const Napi::CallbackInfo &info)
Value setBookmarkRefreshCallback(const Napi::CallbackInfo &info)
Value setResetScrollCallback(const Napi::CallbackInfo &info)
Value setProgressCallback(const Napi::CallbackInfo &info)
MemCheckResult checkMemAllocation(std::size_t bytes) const override
Minsky & minsky()
global minsky object
void clearBusyCursor() override
void outOfMemoryHandler()
Value call(const Napi::CallbackInfo &info)
NODE_API_ADDON(MinskyAddon)
PromiseResolver(const Napi::Env &env)
int operator[](const string &)
TypedThreadSafeFunction< void, PromiseResolver, resolvePromise > tsPromiseResolver
void resolvePromise(Napi::Env env, Napi::Function, void *, PromiseResolver *promiseResolver)
static void bookmarkRefreshCallback(Napi::Env env, Napi::Function fn, void *, AddOnMinsky *addon)