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();}
281 const Timer timer(timers[
"draw"]);
282 for (
auto i: nativeWindowsToRedraw)
284 nativeWindowsToRedraw.clear();
291 tsDrawNativeWindows_.BlockingCall(
this);
296 #if defined(_PTHREAD_H) && defined(__USE_GNU) && !defined(NDEBUG) 297 pthread_setname_np(pthread_self(),
"minsky thread");
301 unique_ptr<Command> command;
303 const lock_guard<mutex> lock(cmdMutex);
304 if (!minskyCommands.empty())
306 command=std::move(minskyCommands.front());
307 minskyCommands.pop_front();
313 if (reset_flag() && resetDuration<resetPostponedThreshold && resetAt<std::chrono::system_clock::now())
319 const Timer timer(timers[
"minsky.reset"]);
324 {flags&=~reset_needed;}
326 if (!drawLaunched && nativeWindowsToRedraw.size())
327 macOSXLaunchDrawNativeWindows();
331 if (inputBufferExceeded && minskyCommands.empty())
335 inputBufferExceeded=
false;
337 this_thread::sleep_for(chrono::milliseconds(10));
341 if (!running)
return;
344 auto result=doCommand(command->command, command->arguments);
345 if (!running)
return;
346 command->promiseResolver->resolve(result);
348 catch (
const std::exception& ex)
350 if (!running)
return;
351 command->promiseResolver->reject(ex.what());
355 if (!running)
return;
356 command->promiseResolver->reject(
"Unknown exception");
366 auto buttons=Array::New(env);
370 addonMinsky->
userResponse.set_value(fn({String::New(env,addonMinsky->
theMessage), buttons}).As<Number>().Int32Value());
374 bool messageCallbackSet=
false;
378 const Env env = info.Env();
379 if (info.Length()<1 || !info[0].IsFunction())
381 Napi::Error::New(env,
"Callback not provided").ThrowAsJavaScriptException();
383 messageCallbackSet=
true;
384 tsMessageCallback=TypedThreadSafeFunction<void,AddOnMinsky,messageCallback>::New
385 (env,info[0].As<Function>(),
"message",0,2,
nullptr);
393 if (!messageCallbackSet)
return;
395 messageButtons.clear();
397 tsMessageCallback.BlockingCall(const_cast<AddOnMinsky*>(
this));
402 if (messageCallbackSet && bytes>4ULL*1024*1024*1024)
404 theMessage=
"Allocation will use more than 50% of available memory. Do you want to proceed?";
405 messageButtons={
"No",
"Yes"};
407 tsMessageCallback.BlockingCall(const_cast<AddOnMinsky*>(
this));
408 return userResponse.get_future().get()? proceed: abort;
416 fn({Boolean::New(env,*busy)});
419 bool busyCursorCallbackSet=
false;
423 const Env env = info.Env();
424 if (info.Length()<1 || !info[0].IsFunction())
426 Napi::Error::New(env,
"Callback not provided").ThrowAsJavaScriptException();
428 busyCursorCallbackSet=
true;
429 tsBusyCursorCallback=TypedThreadSafeFunction<void,bool,busyCursorCallback>::New
430 (env,info[0].As<Function>(),
"setBusyCursor",0,2,
nullptr);
434 bool busyCursor=
false;
437 if (!busyCursorCallbackSet)
return;
439 tsBusyCursorCallback.BlockingCall(&busyCursor);
442 {doBusyCursor(
true);}
444 {doBusyCursor(
false);}
455 if (!progressCallbackSet)
return;
458 tsProgressCallback.BlockingCall(
this);
460 bool progressCallbackSet=
false;
464 const Env env = info.Env();
465 if (info.Length()<1 || !info[0].IsFunction())
467 Napi::Error::New(env,
"Callback not provided").ThrowAsJavaScriptException();
469 progressCallbackSet=
true;
470 tsProgressCallback=TypedThreadSafeFunction<void,AddOnMinsky,progressCallback>::New
471 (env,info[0].As<Function>(),
"progress",0,2,
nullptr);
482 if (!bookmarkRefreshSet)
return;
483 tsBookmarkRefreshCallback.BlockingCall(
this);
485 bool bookmarkRefreshSet=
false;
489 const Env env = info.Env();
490 if (info.Length()<1 || !info[0].IsFunction())
492 Napi::Error::New(env,
"Callback not provided").ThrowAsJavaScriptException();
494 bookmarkRefreshSet=
true;
495 tsBookmarkRefreshCallback=TypedThreadSafeFunction<void,AddOnMinsky,bookmarkRefreshCallback>::New
496 (env,info[0].As<Function>(),
"refreshBookmark",0,2,
nullptr);
507 if (!resetScrollSet)
return;
508 tsResetScrollCallback.BlockingCall(
this);
510 bool resetScrollSet=
false;
514 const Env env = info.Env();
515 if (info.Length()<1 || !info[0].IsFunction())
517 Napi::Error::New(env,
"Callback not provided").ThrowAsJavaScriptException();
520 tsResetScrollCallback=TypedThreadSafeFunction<void,AddOnMinsky,resetScrollCallback>::New
521 (env,info[0].As<Function>(),
"resetScroll",0,2,
nullptr);
526 string file=(std::filesystem::current_path()/
"savedRavelSession.rvl").
string();
528 autoSaver->killThread();
529 file=autoSaver->fileName;
533 theMessage=
"Out of memory, saving to autosave file: "+file;
534 messageButtons={
"OK"};
536 tsMessageCallback.BlockingCall(const_cast<AddOnMinsky*>(
this));
537 userResponse.get_future().get();
549 LocalMinsky::~LocalMinsky() {}
556 static_cast<minsky::AddOnMinsky&
>(
minsky::minsky()).outOfMemoryHandler();
570 New(env,
"TSResolver", 0, 2,
nullptr);
571 addOnMinsky.tsDrawNativeWindows_=
572 TypedThreadSafeFunction<minsky::AddOnMinsky,void,minsky::AddOnMinsky::tsDrawNativeWindows>::
573 New(env,
"TSDrawNativeWindows",0, 2,&addOnMinsky);
575 DefineAddon(exports, {
589 Value
setMessageCallback(
const Napi::CallbackInfo& info) {
return addOnMinsky.setMessageCallback(info);}
595 *addOnMinsky.progressState.cancel=
true;
596 civita::ITensor::cancel(
true);
597 return info.Env().Null();
601 Value
call(
const Napi::CallbackInfo& info)
603 const Env env = info.Env();
604 if (info.Length() < 1)
606 Napi::TypeError::New(env,
"Needs to be call(endpoint[, arguments])").ThrowAsJavaScriptException();
610 #if defined(_PTHREAD_H) && defined(__USE_GNU) && !defined(NDEBUG) 611 pthread_setname_np(pthread_self(),
"addon thread");
616 json_pack_t arguments(json5_parser::mValue::null);
619 const string jsonArguments=info[1].ToString();
620 if (!jsonArguments.empty())
621 read(jsonArguments, arguments);
623 return addOnMinsky.queueCommand(env,info[0].ToString(),arguments);
625 catch (
const std::exception& ex)
628 Napi::Error::New(env, ex.what()).ThrowAsJavaScriptException();
633 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 macOSXRedraw(RenderNativeWindow &window, const std::shared_ptr< std::lock_guard< std::mutex >> &lock)
void macOSXLaunchDrawNativeWindows()
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)