thread management fixed.
[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  *
5  *  This file must be used under the terms of the CeCILL.
6  *  This source file is licensed as described in the file COPYING, which
7  *  you should have received as part of this distribution.  The terms
8  *  are also available at
9  *  http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt
10  *
11  */
12
13 #include "runner.hxx"
14
15 __threadLock Runner::m_lock;
16
17 __threadSignal Runner::m_consoleExecDone;
18
19 __threadSignal Runner::m_awakeScilab;
20 __threadSignalLock Runner::m_awakeScilabLock;
21
22 __threadSignal Runner::m_AstPending;
23 __threadSignalLock Runner::m_AstPendingLock;
24
25
26 using namespace ast;
27
28 __threadSignal* getAstPendingSignal(void)
29 {
30     return Runner::getAstPendingSignal();
31 }
32
33 __threadSignal* getConsoleExecDone(void)
34 {
35     return Runner::getConsoleExecDone();
36 }
37
38
39 void Runner::init()
40 {
41     __InitSignal(&m_awakeScilab);
42     __InitSignalLock(&m_awakeScilabLock);
43
44     __InitSignal(&m_consoleExecDone);
45
46     __InitSignal(&m_AstPending);
47     __InitSignalLock(&m_AstPendingLock);
48 }
49
50 void *Runner::launch(void *args)
51 {
52     bool bdoUnlock = false;
53     //try to lock locker ( waiting parent thread register me )
54     __Lock(&m_lock);
55     //just release locker
56     __UnLock(&m_lock);
57
58     __threadKey currentThreadKey = __GetCurrentThreadKey();
59     ThreadId* pThread = ConfigVariable::getThread(currentThreadKey);
60
61     //exec !
62     Runner *me = (Runner *)args;
63
64     try
65     {
66         me->getProgram()->accept(*(me->getVisitor()));
67         //ConfigVariable::clearLastError();
68     }
69     catch (const ast::ScilabException& se)
70     {
71         scilabErrorW(se.GetErrorMessage().c_str());
72     }
73
74     // reset error state when new prompt occurs
75     ConfigVariable::resetError();
76
77     //change thread status
78     if (pThread->getStatus() != ThreadId::Aborted)
79     {
80         pThread->setStatus(ThreadId::Done);
81         bdoUnlock = true;
82     }
83
84     if (pThread->getInterrupt()) // non-prioritary
85     {
86         // Unlock prioritary thread waiting for
87         // non-prioritary thread end this "SeqExp" execution.
88         // This case appear when error is throw or when
89         // non-prioritary execute this last SeqExp.
90         pThread->setInterrupt(false);
91         __Signal(&Runner::m_AstPending);
92     }
93
94     if (pThread->isConsoleCommand())
95     {
96         __Signal(&Runner::m_consoleExecDone);
97     }
98
99     //unregister thread
100     ConfigVariable::deleteThread(currentThreadKey);
101
102     delete me;
103
104     if (bdoUnlock)
105     {
106         UnlockPrompt();
107     }
108
109     return NULL;
110 }
111
112 void Runner::LockPrompt()
113 {
114     __LockSignal(&m_awakeScilabLock);
115     //free locker to release thread
116     __UnLock(&m_lock);
117     __Wait(&m_awakeScilab, &m_awakeScilabLock);
118     __UnLockSignal(&m_awakeScilabLock);
119 }
120
121 void Runner::UnlockPrompt()
122 {
123     __LockSignal(&m_awakeScilabLock);
124     __Signal(&m_awakeScilab);
125     __UnLockSignal(&m_awakeScilabLock);
126 }
127
128
129 void Runner::execAndWait(ast::Exp* _theProgram, ast::ExecVisitor *_visitor,
130                          bool _isPrioritaryThread, bool _isInterruptibleThread, bool _isConsoleCommand)
131 {
132     try
133     {
134         Runner *runMe = new Runner(_theProgram, _visitor);
135         __threadKey threadKey;
136         __threadId threadId;
137
138         //init locker
139         __InitLock(&m_lock);
140         //lock locker
141         __Lock(&m_lock);
142
143         types::ThreadId* pInterruptibleThread = ConfigVariable::getLastRunningThread();
144         if (_isPrioritaryThread)
145         {
146             if (pInterruptibleThread)
147             {
148                 if (pInterruptibleThread->isInterruptible())
149                 {
150                     pInterruptibleThread->setInterrupt(true);
151                     __LockSignal(&m_AstPendingLock);
152                     __Wait(&m_AstPending, &m_AstPendingLock);
153                     __UnLockSignal(&m_AstPendingLock);
154                 }
155                 else
156                 {
157                     __WaitThreadDie(pInterruptibleThread->getThreadId());
158                     pInterruptibleThread = NULL;
159                 }
160             }
161         }
162         else if (pInterruptibleThread)
163         {
164             __WaitThreadDie(pInterruptibleThread->getThreadId());
165             pInterruptibleThread = NULL;
166         }
167
168         //launch thread but is can't really start since locker is locked
169         __CreateThreadWithParams(&threadId, &threadKey, &Runner::launch, runMe);
170         runMe->setThreadId(threadId);
171         runMe->setThreadKey(threadKey);
172
173         //register thread
174         types::ThreadId* pThread = new ThreadId(threadId, threadKey);
175         ConfigVariable::addThread(pThread);
176         pThread->setConsoleCommandFlag(_isConsoleCommand);
177         pThread->setInterruptible(_isInterruptibleThread);
178
179         //free locker to release thread && wait and of thread execution
180         LockPrompt();
181
182         if (pInterruptibleThread && pInterruptibleThread->getInterrupt())
183         {
184             pInterruptibleThread->setInterrupt(false);
185             pInterruptibleThread->resume();
186         }
187
188         types::ThreadId* pExecThread = ConfigVariable::getThread(threadKey);
189         if (pExecThread == NULL)
190         {
191             //call pthread_join to clean stack allocation
192             __WaitThreadDie(threadId);
193         }
194     }
195     catch (const ast::ScilabException& se)
196     {
197         throw se;
198     }
199 }
200
201 void Runner::exec(ast::Exp* _theProgram, ast::ExecVisitor *_visitor)
202 {
203     m_theProgram = _theProgram;
204     m_visitor = _visitor;
205     __CreateThreadWithParams(&m_threadId, &m_threadKey, &Runner::launch, this);
206 }