d69411e17c86ca833b6ba1a3688037de8acae717
[scilab.git] / scilab / modules / core / src / cpp / runner.cpp
1 /*
2  *  Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
3  *  Copyright (C) 2011-2011 - DIGITEO - Bruno JOFRET
4  *  Copyright (C) 2014-2015 - Scilab Enterprises - Cedric Delamarre
5  *
6  * Copyright (C) 2012 - 2016 - Scilab Enterprises
7  *
8  * This file is hereby licensed under the terms of the GNU GPL v2.0,
9  * pursuant to article 5.3.4 of the CeCILL v.2.1.
10  * This file was originally licensed under the terms of the CeCILL v2.1,
11  * and continues to be available under such terms.
12  * For more information, see the COPYING file which you should have received
13  * along with this program.
14  *
15  */
16
17 #include "runner.hxx"
18 #include "threadmanagement.hxx"
19 #include "configvariable.hxx"
20 #include "debugmanager.hxx"
21
22 extern "C"
23 {
24 #include "HistoryManager.h"
25 #include "BrowseVarManager.h"
26 #include "FileBrowserChDir.h"
27 #include "scicurdir.h"
28 #include "Scierror.h"
29 #include "InitializeJVM.h"
30 }
31
32 std::atomic<Runner*> StaticRunner::m_RunMe(nullptr);
33 std::atomic<Runner*> StaticRunner::m_CurrentRunner(nullptr);
34
35 static bool initialJavaHooks = false;
36
37 void StaticRunner::sendExecDoneSignal()
38 {
39     switch (m_CurrentRunner.load()->getCommandOrigin())
40     {
41         case DEBUGGER :
42         {
43             ThreadManagement::SendDebuggerExecDoneSignal();
44             break;
45         }
46         case CONSOLE :
47         {
48             ThreadManagement::SendConsoleExecDoneSignal();
49             break;
50         }
51         case TCLSCI :
52         case NONE :
53         default : {}
54     }
55 }
56
57 int StaticRunner::launch()
58 {
59     //set execution thread in java
60     if (!initialJavaHooks && getScilabMode() != SCILAB_NWNI)
61     {
62         initialJavaHooks = true;
63         // Execute the initial hooks registered in Scilab.java
64         ExecuteInitialHooks();
65     }
66
67     int iRet = 0;
68
69     // save current runner
70     Runner* pRunSave = m_CurrentRunner.load();
71
72     // get the runner to execute
73     std::unique_ptr<Runner> runMe(getRunner());
74
75     debugger::DebuggerManager* manager = debugger::DebuggerManager::getInstance();
76     manager->resetAborted();
77
78     ConfigVariable::resetExecutionBreak();
79
80     int oldMode = ConfigVariable::getPromptMode();
81     symbol::Context* pCtx = symbol::Context::getInstance();
82     int scope = pCtx->getScopeLevel();
83
84     // a TCL command display nothing
85     int iOldPromptMode = 0;
86     if (runMe->getCommandOrigin() == TCLSCI)
87     {
88         iOldPromptMode = ConfigVariable::getPromptMode();
89         ConfigVariable::setPromptMode(-1);
90     }
91
92     try
93     {
94         int level = ConfigVariable::getRecursionLevel();
95         try
96         {
97             runMe->getProgram()->accept(*(runMe->getVisitor()));
98         }
99         catch (const ast::RecursionException& re)
100         {
101             // management of pause
102             if (ConfigVariable::getPauseLevel())
103             {
104                 ConfigVariable::DecreasePauseLevel();
105                 throw re;
106             }
107
108             //close opened scope during try
109             while (pCtx->getScopeLevel() > scope)
110             {
111                 pCtx->scope_end();
112             }
113
114             //decrease recursion to init value and close where
115             while (ConfigVariable::getRecursionLevel() > level)
116             {
117                 ConfigVariable::where_end();
118                 ConfigVariable::decreaseRecursion();
119             }
120
121             ConfigVariable::resetWhereError();
122             ConfigVariable::setPromptMode(oldMode);
123
124             //print msg about recursion limit and trigger an error
125             wchar_t sz[1024];
126             os_swprintf(sz, 1024, _W("Recursion limit reached (%d).\n").data(), ConfigVariable::getRecursionLimit());
127             throw ast::InternalError(sz);
128         }
129     }
130     catch (const ast::InternalError& se)
131     {
132         if (runMe->getCommandOrigin() == TCLSCI)
133         {
134             ConfigVariable::setPromptMode(iOldPromptMode);
135         }
136
137         std::wostringstream ostr;
138         ConfigVariable::whereErrorToString(ostr);
139         scilabErrorW(ostr.str().c_str());
140         scilabErrorW(se.GetErrorMessage().c_str());
141         ConfigVariable::resetWhereError();
142         iRet = 1;
143     }
144     catch (const ast::InternalAbort& ia)
145     {
146         if (runMe->getCommandOrigin() == TCLSCI)
147         {
148             ConfigVariable::setPromptMode(iOldPromptMode);
149         }
150
151         // management of pause
152         if (ConfigVariable::getPauseLevel())
153         {
154             ConfigVariable::DecreasePauseLevel();
155             // set back the runner wich have been overwritten in StaticRunner::getRunner
156             m_CurrentRunner.store(pRunSave);
157             throw ia;
158         }
159
160         // close all scope before return to console scope
161         symbol::Context* pCtx = symbol::Context::getInstance();
162         while (pCtx->getScopeLevel() > scope)
163         {
164             pCtx->scope_end();
165         }
166
167         // debugger leave with abort state
168         manager->setAborted();
169
170         // send the good signal about the end of execution
171         sendExecDoneSignal();
172
173         // set back the runner wich have been overwritten in StaticRunner::getRunner
174         m_CurrentRunner.store(pRunSave);
175         throw ia;
176     }
177
178     if (runMe->getCommandOrigin() == TCLSCI)
179     {
180         ConfigVariable::setPromptMode(iOldPromptMode);
181     }
182
183     if (getScilabMode() != SCILAB_NWNI && getScilabMode() != SCILAB_API)
184     {
185         char *cwd = NULL;
186         int err = 0;
187
188         UpdateBrowseVar();
189         saveScilabHistoryToFile();
190         cwd = scigetcwd(&err);
191         if (cwd)
192         {
193             FileBrowserChDir(cwd);
194             FREE(cwd);
195         }
196     }
197
198     // reset error state when new prompt occurs
199     ConfigVariable::resetError();
200
201     // send the good signal about the end of execution
202     sendExecDoneSignal();
203
204     //clean debugger step flag if debugger is not interrupted ( end of debug )
205     manager->resetStep();
206
207     // set back the runner wich have been overwritten in StaticRunner::getRunner
208     m_CurrentRunner.store(pRunSave);
209
210     return iRet;
211 }
212
213 void StaticRunner::setRunner(Runner* _RunMe)
214 {
215     m_RunMe = _RunMe;
216 }
217
218 Runner* StaticRunner::getRunner(void)
219 {
220     m_CurrentRunner.store(m_RunMe.exchange(nullptr));
221     ThreadManagement::SendAvailableRunnerSignal();
222     return m_CurrentRunner.load();
223 }
224
225 // return true if a Runner is already set in m_RunMe.
226 bool StaticRunner::isRunnerAvailable(void)
227 {
228     return m_RunMe.load() != nullptr;
229 }
230
231 // return true if a command is running or paused.
232 bool StaticRunner::isRunning(void)
233 {
234     return m_CurrentRunner.load() != nullptr;
235 }
236
237 bool StaticRunner::isInterruptibleCommand()
238 {
239     return m_CurrentRunner.load()->isInterruptible();
240 }
241
242 command_origin_t StaticRunner::getCommandOrigin()
243 {
244     return m_RunMe.load()->getCommandOrigin();
245 }
246
247 void StaticRunner::setCommandOrigin(command_origin_t _origin)
248 {
249     m_CurrentRunner.load()->setCommandOrigin(_origin);
250 }
251
252 void StaticRunner::execAndWait(ast::Exp* _theProgram, ast::RunVisitor *_visitor,
253                                bool /*_isPrioritaryThread*/, bool _isInterruptible, command_origin_t _iCommandOrigin)
254 {
255     if (isRunnerAvailable())
256     {
257         // wait for managenement of last Runner
258         ThreadManagement::WaitForAvailableRunnerSignal();
259     }
260
261     // lock runner to be sure we are waiting for
262     // "AwakeRunner" signal before start execution
263     ThreadManagement::LockRunner();
264     Runner *runMe = new Runner(_theProgram, _visitor, _iCommandOrigin, _isInterruptible);
265     setRunner(runMe);
266
267     ThreadManagement::SendRunMeSignal();
268     ThreadManagement::WaitForAwakeRunnerSignal();
269 }
270
271 bool StaticRunner::exec(ast::Exp* _theProgram, ast::RunVisitor *_visitor)
272 {
273     Runner *runMe = new Runner(_theProgram, _visitor);
274     setRunner(runMe);
275
276     try
277     {
278         launch();
279     }
280     catch (const ast::InternalAbort& /*ia*/)
281     {
282         //catch exit command in .start or .quit
283         return false;
284     }
285
286     return true;
287 }
288
289 void StaticRunner_launch(void)
290 {
291     StaticRunner::launch();
292 }
293
294 int StaticRunner_isRunning(void)
295 {
296     return StaticRunner::isRunning() ? 1 : 0;
297 }
298
299 int StaticRunner_isRunnerAvailable(void)
300 {
301     return StaticRunner::isRunnerAvailable() ? 1 : 0;
302 }
303
304 int StaticRunner_isInterruptibleCommand(void)
305 {
306     return StaticRunner::isInterruptibleCommand() ? 1 : 0;
307 }
308
309 command_origin_t StaticRunner_getCommandOrigin(void)
310 {
311     return StaticRunner::getCommandOrigin();
312 }
313
314 void StaticRunner_setCommandOrigin(command_origin_t _origin)
315 {
316     StaticRunner::setCommandOrigin(_origin);
317 }