/************
*
* This file is part of the vector graphics language Asymptote
* Copyright (C) 2004 Andy Hammerlindl, John C. Bowman, Tom Prince
* https://asymptote.sourceforge.io
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see .
*
*************/
#include
#include
#include
#include
#if defined(_WIN32)
#include
int _matherr(struct _exception *except)
{
unsigned int cw = 0;
_controlfp_s(&cw, 0, 0);
if((except->type == _SING && !(cw & _EM_ZERODIVIDE)) ||
(except->type == _DOMAIN && !(cw & _EM_INVALID)) ||
(except->type == _OVERFLOW && !(cw & _EM_OVERFLOW))) {
std::cerr << "Floating exception" << std::endl;
raise(SIGFPE);
}
return 0;
}
#else
#include
#endif
#include "common.h"
#include "rendererloader.h"
#ifdef HAVE_LIBGL
#include "glrender.h"
#endif
#ifdef HAVE_LIBVULKAN
#include "vkrender.h"
#endif
#ifdef HAVE_LIBSIGSEGV
#include
#endif
#define GC_PTHREAD_SIGMASK_NEEDED
#ifdef HAVE_LSP
#include "lspserv.h"
#endif
#include "exithandlers.h"
#include "errormsg.h"
#include "fpu.h"
#include "settings.h"
#include "locate.h"
#include "interact.h"
#include "fileio.h"
#ifdef HAVE_LIBFFTW3
#include "fftw++.h"
#endif
#if defined(_WIN32)
#include
#endif
#include "renderBase.h"
pthread_mutex_t main_wait_mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t main_wait_cond = PTHREAD_COND_INITIALIZER;
using namespace settings;
using interact::interactive;
namespace run {
void purge();
}
#ifdef PROFILE
namespace vm {
extern void dumpProfile();
};
#endif
#ifdef HAVE_LIBSIGSEGV
void stackoverflow_handler (int, stackoverflow_context_t)
{
em.runtime(vm::getPos());
cerr << "Stack overflow" << endl;
abort();
}
int sigsegv_handler (void *, int emergency)
{
if(!emergency) return 0; // Really a stack overflow
em.runtime(vm::getPos());
#ifdef HAVE_RENDERER
if(camp::AsyRender::threads)
cerr << "Stack overflow or segmentation fault: rerun with -nothreads"
<< endl;
else
#endif
cerr << "Segmentation fault" << endl;
abort();
}
#endif
void setsignal(void (*handler)(int))
{
#ifdef HAVE_LIBSIGSEGV
char mystack[16384];
stackoverflow_install_handler(&stackoverflow_handler,
mystack,sizeof (mystack));
sigsegv_install_handler(&sigsegv_handler);
#endif
#if !defined(_WIN32)
Signal(SIGBUS,handler);
#endif
Signal(SIGFPE,handler);
}
struct Args
{
int argc;
char **argv;
Args(int argc, char **argv) : argc(argc), argv(argv) {}
};
void *asymain(void *A)
{
setsignal(signalHandler);
Args *args=(Args *) A;
fpu_trap(trap());
#ifdef HAVE_LIBFFTW3
fftwpp::wisdomName=".wisdom";
#endif
#if defined(_WIN32)
// see https://learn.microsoft.com/en-us/windows/win32/api/shellapi/nf-shellapi-shellexecuteexa
if(!SUCCEEDED(
CoInitializeEx(nullptr,
COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE)))
camp::reportError("CoInitializeEx Failed");
#endif
if(interactive) {
Signal(SIGINT,interruptHandler);
#ifdef HAVE_LSP
if (getSetting("lsp")) {
AsymptoteLsp::LspLog log;
auto jsonHandler=std::make_shared();
auto endpoint=std::make_shared(log);
unique_ptr asylsp;
if(getSetting("lspport") != "") {
asylsp=make_unique(
(std::string)getSetting("lsphost").c_str(),
(std::string)getSetting("lspport").c_str(),
jsonHandler, endpoint, log);
} else {
asylsp=make_unique(jsonHandler,
endpoint,
log);
}
asylsp->start();
} else
#endif
processPrompt();
} else if (getSetting("listvariables") && numArgs()==0) {
try {
doUnrestrictedList();
} catch(handled_error const&) {
em.statusError();
}
} else {
int n=numArgs();
if(n == 0) {
int inpipe=intcast(settings::getSetting("inpipe"));
bool hasInpipe=inpipe >= 0;
if(hasInpipe) {
#if !defined(_WIN32)
Signal(SIGHUP,hangup_handler);
#endif
camp::openpipeout();
fprintf(camp::pipeout,"\n");
fflush(camp::pipeout);
}
for(;;) {
processFile("-",true);
try {
setOptions(args->argc,args->argv);
} catch(handled_error const&) {
em.statusError();
}
if(inpipe < 0) break;
}
} else {
for(int ind=0; ind < n; ind++) {
string name=(getArg(ind));
string prefix=stripExt(name);
if(name == prefix+".v3d") {
interact::uptodate=false;
runString("import v3d; defaultfilename=\""+stripDir(prefix)+
"\"; importv3d(\""+name+"\");");
} else
processFile(name,n > 1);
try {
if(ind < n-1)
setOptions(args->argc,args->argv);
} catch(handled_error const&) {
em.statusError();
}
}
}
}
#ifdef PROFILE
vm::dumpProfile();
#endif
#if !defined(_WIN32)
if(getSetting("wait")) {
int status;
while(wait(&status) > 0);
}
#endif
exit(returnCode());
}
int main(int argc, char *argv[])
{
#ifdef HAVE_LIBGSL
unsetenv("GSL_RNG_SEED");
unsetenv("GSL_RNG_TYPE");
#endif
setsignal(signalHandler);
try {
setOptions(argc,argv);
} catch(handled_error const&) {
em.statusError();
}
#ifdef HAVE_RENDERER
// Determine whether threading is needed. On Unix, we only enable the
// render thread when a windowed View is requested; the renderer itself
// (Vulkan/OpenGL library loading) is deferred until initRenderer() is
// called from shipout3().
// On macOS, threads are always required for rendering (even with
// llvmpipe software fallback) to avoid races on AsyRender::View.
#if defined(__APPLE__)
camp::AsyRender::threads = true;
#else
camp::AsyRender::threads = view() ? getSetting("threads") : false;
#endif
#endif
fpu_trap(trap());
Args args(argc,argv);
#ifdef HAVE_RENDERER
#if HAVE_PTHREAD
if(camp::AsyRender::threads) {
pthread_t thread;
try {
#if defined(_WIN32)
auto asymainPtr = [](void* args) -> void* {
#if defined(USEGC)
GC_stack_base gsb {};
GC_get_stack_base(&gsb);
GC_register_my_thread(&gsb);
#endif // defined(USEGC)
auto* ret = asymain(args);
#if defined(USEGC)
GC_unregister_my_thread();
#endif // defined(USEGC)
return reinterpret_cast(ret);
};
#else // defined(_WIN32)
auto* asymainPtr = asymain;
#endif // defined(_WIN32)
if(pthread_create(&thread,NULL,asymainPtr,&args) == 0) {
#if !defined(_WIN32)
sigset_t set;
sigemptyset(&set);
sigaddset(&set, SIGCHLD);
pthread_sigmask(SIG_BLOCK, &set, NULL);
#endif // !defined(_WIN32)
for (;;) {
camp::glrenderWrapper();
if(camp::gl == nullptr) {
pthread_mutex_lock(&main_wait_mutex);
pthread_cond_wait(&main_wait_cond, &main_wait_mutex);
pthread_mutex_unlock(&main_wait_mutex);
}
}
} else {
camp::AsyRender::threads=false;
}
} catch(std::bad_alloc&) {
outOfMemory();
}
}
#endif // HAVE_PTHREAD
#endif // HAVE_RENDERER
asymain(&args);
}