fix pause, resume, abort management
[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
77     ConfigVariable::resetExecutionBreak();
78
79     int oldMode = ConfigVariable::getPromptMode();
80     symbol::Context* pCtx = symbol::Context::getInstance();
81     int scope = pCtx->getScopeLevel();
82
83     // a TCL command display nothing
84     int iOldPromptMode = 0;
85     if (runMe->getCommandOrigin() == TCLSCI)
86     {
87         iOldPromptMode = ConfigVariable::getPromptMode();
88         ConfigVariable::setPromptMode(-1);
89     }
90
91     try
92     {
93         int level = ConfigVariable::getRecursionLevel();
94         try
95         {
96             runMe->getProgram()->accept(*(runMe->getVisitor()));
97         }
98         catch (const ast::RecursionException& re)
99         {
100             // management of pause
101             if (ConfigVariable::getPauseLevel())
102             {
103                 ConfigVariable::DecreasePauseLevel();
104                 throw re;
105             }
106
107             //close opened scope during try
108             while (pCtx->getScopeLevel() > scope)
109             {
110                 pCtx->scope_end();
111             }
112
113             //decrease recursion to init value and close where
114             while (ConfigVariable::getRecursionLevel() > level)
115             {
116                 ConfigVariable::where_end();
117                 ConfigVariable::decreaseRecursion();
118             }
119
120             ConfigVariable::resetWhereError();
121             ConfigVariable::setPromptMode(oldMode);
122
123             //print msg about recursion limit and trigger an error
124             wchar_t sz[1024];
125             os_swprintf(sz, 1024, _W("Recursion limit reached (%d).\n").data(), ConfigVariable::getRecursionLimit());
126             throw ast::InternalError(sz);
127         }
128     }
129     catch (const ast::InternalError& se)
130     {
131         if (runMe->getCommandOrigin() == TCLSCI)
132         {
133             ConfigVariable::setPromptMode(iOldPromptMode);
134         }
135
136         std::wostringstream ostr;
137         ConfigVariable::whereErrorToString(ostr);
138         scilabErrorW(ostr.str().c_str());
139         scilabErrorW(se.GetErrorMessage().c_str());
140         ConfigVariable::resetWhereError();
141         iRet = 1;
142     }
143     catch (const ast::InternalAbort& ia)
144     {
145         if (runMe->getCommandOrigin() == TCLSCI)
146         {
147             ConfigVariable::setPromptMode(iOldPromptMode);
148         }
149
150         // management of pause
151         if (ConfigVariable::getPauseLevel())
152         {
153             ConfigVariable::DecreasePauseLevel();
154             // set back the runner wich have been overwritten in StaticRunner::getRunner
155             m_CurrentRunner.store(pRunSave);
156             throw ia;
157         }
158
159         // close all scope before return to console scope
160         symbol::Context* pCtx = symbol::Context::getInstance();
161         while (pCtx->getScopeLevel() > scope)
162         {
163             pCtx->scope_end();
164         }
165
166         // debugger leave with abort state
167         manager->setAborted();
168
169         // send the good signal about the end of execution
170         sendExecDoneSignal();
171
172         // set back the runner wich have been overwritten in StaticRunner::getRunner
173         m_CurrentRunner.store(pRunSave);
174         throw ia;
175     }
176
177     if (runMe->getCommandOrigin() == TCLSCI)
178     {
179         ConfigVariable::setPromptMode(iOldPromptMode);
180     }
181
182     if (getScilabMode() != SCILAB_NWNI && getScilabMode() != SCILAB_API)
183     {
184         char *cwd = NULL;
185         int err = 0;
186
187         UpdateBrowseVar();
188         saveScilabHistoryToFile();
189         cwd = scigetcwd(&err);
190         if (cwd)
191         {
192             FileBrowserChDir(cwd);
193             FREE(cwd);
194         }
195     }
196
197     // reset error state when new prompt occurs
198     ConfigVariable::resetError();
199
200     // send the good signal about the end of execution
201     sendExecDoneSignal();
202
203     //clean debugger step flag if debugger is not interrupted ( end of debug )
204     manager->resetStep();
205
206     // set back the runner wich have been overwritten in StaticRunner::getRunner
207     m_CurrentRunner.store(pRunSave);
208
209     return iRet;
210 }
211
212 void StaticRunner::setRunner(Runner* _RunMe)
213 {
214     m_RunMe = _RunMe;
215 }
216
217 Runner* StaticRunner::getRunner(void)
218 {
219     m_CurrentRunner.store(m_RunMe.exchange(nullptr));
220     ThreadManagement::SendAvailableRunnerSignal();
221     return m_CurrentRunner.load();
222 }
223
224 // return true if a Runner is already set in m_RunMe.
225 bool StaticRunner::isRunnerAvailable(void)
226 {
227     return m_RunMe.load() != nullptr;
228 }
229
230 bool StaticRunner::isInterruptibleCommand()
231 {
232     return m_CurrentRunner.load()->isInterruptible();
233 }
234
235 command_origin_t StaticRunner::getCommandOrigin()
236 {
237     return m_RunMe.load()->getCommandOrigin();
238 }
239
240 void StaticRunner::setCommandOrigin(command_origin_t _origin)
241 {
242     m_CurrentRunner.load()->setCommandOrigin(_origin);
243 }
244
245 void StaticRunner::execAndWait(ast::Exp* _theProgram, ast::RunVisitor *_visitor,
246                                bool /*_isPrioritaryThread*/, bool _isInterruptible, command_origin_t _iCommandOrigin)
247 {
248     if (isRunnerAvailable())
249     {
250         // wait for managenement of last Runner
251         ThreadManagement::WaitForAvailableRunnerSignal();
252     }
253
254     // lock runner to be sure we are waiting for
255     // "AwakeRunner" signal before start execution
256     ThreadManagement::LockRunner();
257     Runner *runMe = new Runner(_theProgram, _visitor, _iCommandOrigin, _isInterruptible);
258     setRunner(runMe);
259
260     ThreadManagement::SendRunMeSignal();
261     ThreadManagement::WaitForAwakeRunnerSignal();
262 }
263
264 bool StaticRunner::exec(ast::Exp* _theProgram, ast::RunVisitor *_visitor)
265 {
266     Runner *runMe = new Runner(_theProgram, _visitor);
267     setRunner(runMe);
268
269     try
270     {
271         launch();
272     }
273     catch (const ast::InternalAbort& /*ia*/)
274     {
275         //catch exit command in .start or .quit
276         return false;
277     }
278
279     return true;
280 }
281
282 void StaticRunner_launch(void)
283 {
284     StaticRunner::launch();
285 }
286
287 int StaticRunner_isRunnerAvailable(void)
288 {
289     return StaticRunner::isRunnerAvailable() ? 1 : 0;
290 }
291
292 int StaticRunner_isInterruptibleCommand(void)
293 {
294     return StaticRunner::isInterruptibleCommand() ? 1 : 0;
295 }
296
297 command_origin_t StaticRunner_getCommandOrigin(void)
298 {
299     return StaticRunner::getCommandOrigin();
300 }
301
302 void StaticRunner_setCommandOrigin(command_origin_t _origin)
303 {
304     StaticRunner::setCommandOrigin(_origin);
305 }