tclsci plugged
[scilab.git] / scilab / modules / ast / src / cpp / system_env / threadmanagement.cpp
1 /*
2 *  Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
3 * Copyright (C) 2015 - Scilab Enterprises - Cedric DELAMARRE
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 "threadmanagement.hxx"
14 #include "runner.hxx"
15
16 #ifdef DEBUG_THREAD
17 #include <iostream>
18 #include <iomanip>
19
20 #define PRINT_COL_SIZE 32
21
22 __threadKey ThreadManagement::m_tkMain;
23 __threadKey ThreadManagement::m_tkReadAndExec;
24 __threadKey ThreadManagement::m_tkConsole;
25 #endif // DEBUG_THREAD
26
27 __threadLock ThreadManagement::m_RunnerLock;
28 __threadLock ThreadManagement::m_ParseLock;
29 __threadLock ThreadManagement::m_StoreCommandLock;
30 __threadLock ThreadManagement::m_ScilabReadLock;
31
32 __threadSignal ThreadManagement::m_ConsoleExecDone;
33 __threadSignalLock ThreadManagement::m_ConsoleExecDoneLock;
34
35 __threadSignal ThreadManagement::m_AwakeRunner;
36 __threadSignalLock ThreadManagement::m_AwakeRunnerLock;
37
38 __threadSignal ThreadManagement::m_AvailableRunner;
39 __threadSignalLock ThreadManagement::m_AvailableRunnerLock;
40
41 __threadSignal ThreadManagement::m_StartPending;
42 __threadSignalLock ThreadManagement::m_StartPendingLock;
43
44 __threadSignal ThreadManagement::m_CommandStored;
45 __threadSignalLock ThreadManagement::m_CommandStoredLock;
46
47 __threadSignal ThreadManagement::m_RunMe;
48 __threadSignalLock ThreadManagement::m_RunMeLock;
49
50 bool ThreadManagement::m_AvailableRunnerWasSignalled    = false;
51 bool ThreadManagement::m_ConsoleExecDoneWasSignalled    = false;
52 bool ThreadManagement::m_AwakeRunnerWasSignalled        = false;
53 bool ThreadManagement::m_StartPendingWasSignalled       = false;
54 bool ThreadManagement::m_CommandStoredWasSignalled      = false;
55 bool ThreadManagement::m_RunMeWasSignalled              = false;
56
57 void ThreadManagement::initialize()
58 {
59     __InitLock(&m_RunnerLock);
60     __InitLock(&m_ParseLock);
61     __InitLock(&m_StoreCommandLock);
62     __InitLock(&m_ScilabReadLock);
63
64     __InitSignal(&m_AwakeRunner);
65     __InitSignalLock(&m_AwakeRunnerLock);
66
67     __InitSignal(&m_ConsoleExecDone);
68     __InitSignalLock(&m_ConsoleExecDoneLock);
69
70     __InitSignal(&m_AvailableRunner);
71     __InitSignalLock(&m_AvailableRunnerLock);
72
73     __InitSignal(&m_StartPending);
74     __InitSignalLock(&m_StartPendingLock);
75
76     __InitSignal(&m_CommandStored);
77     __InitSignalLock(&m_CommandStoredLock);
78
79     __InitSignal(&m_RunMe);
80     __InitSignalLock(&m_RunMeLock);
81 }
82
83 /***
84     [Runner Lock]
85     Used when we want to access to the Parser.
86 ***/
87 void ThreadManagement::LockParser(void)
88 {
89 #ifdef DEBUG_THREAD
90     PrintDebug("LockParser");
91 #endif // DEBUG_THREAD
92     __Lock(&m_ParseLock);
93 }
94
95 void ThreadManagement::UnlockParser(void)
96 {
97 #ifdef DEBUG_THREAD
98     PrintDebug("UnlockParser");
99 #endif // DEBUG_THREAD
100     __UnLock(&m_ParseLock);
101 }
102
103 /***
104     [Runner Lock]
105     Used when we want to access to the Store Command.
106 ***/
107 void ThreadManagement::LockStoreCommand(void)
108 {
109 #ifdef DEBUG_THREAD
110     PrintDebug("LockStoreCommand");
111 #endif // DEBUG_THREAD
112     __Lock(&m_StoreCommandLock);
113 }
114
115 void ThreadManagement::UnlockStoreCommand(void)
116 {
117 #ifdef DEBUG_THREAD
118     PrintDebug("UnlockStoreCommand");
119 #endif // DEBUG_THREAD
120     __UnLock(&m_StoreCommandLock);
121 }
122
123 /***
124     [Runner Lock]
125     Used when we want to access to the global Runner.
126 ***/
127 void ThreadManagement::LockRunner(void)
128 {
129 #ifdef DEBUG_THREAD
130     PrintDebug("LockRunner");
131 #endif // DEBUG_THREAD
132     __Lock(&m_RunnerLock);
133 }
134
135 void ThreadManagement::UnlockRunner(void)
136 {
137 #ifdef DEBUG_THREAD
138     PrintDebug("UnlockRunner");
139 #endif // DEBUG_THREAD
140     __UnLock(&m_RunnerLock);
141 }
142
143 /***
144     [ScilabRead Lock]
145     Used to manage scilabRead output wich can be used by Console thread or
146     main thread through mscanf function.
147 ***/
148 void ThreadManagement::LockScilabRead(void)
149 {
150 #ifdef DEBUG_THREAD
151     PrintDebug("LockScilabRead");
152 #endif // DEBUG_THREAD
153     __Lock(&m_ScilabReadLock);
154 }
155
156 void ThreadManagement::UnlockScilabRead(void)
157 {
158 #ifdef DEBUG_THREAD
159     PrintDebug("UnlockScilabRead");
160 #endif // DEBUG_THREAD
161     __UnLock(&m_ScilabReadLock);
162 }
163
164 /***
165     [AvailableRunner Signal]
166
167     Send : The global Runner is available to store a new one.
168     Wait : This happens when the last Runner is not yet in execution.
169
170     This signal can be sent without any threads are waiting for,
171     so we have to perform the Wait for each call to WaitForConsoleExecDoneSignal.
172
173     The loop while is used to avoid spurious wakeup of __Wait.
174 ***/
175 void ThreadManagement::SendAvailableRunnerSignal(void)
176 {
177     __LockSignal(&m_AvailableRunnerLock);
178     m_AvailableRunnerWasSignalled = true;
179 #ifdef DEBUG_THREAD
180     PrintDebug("SendAvailableRunnerSignal");
181 #endif // DEBUG_THREAD
182     __Signal(&m_AvailableRunner);
183     __UnLockSignal(&m_AvailableRunnerLock);
184 }
185
186 void ThreadManagement::WaitForAvailableRunnerSignal(void)
187 {
188     __LockSignal(&m_AvailableRunnerLock);
189     m_AvailableRunnerWasSignalled = false;
190     while (m_AvailableRunnerWasSignalled == false)
191     {
192 #ifdef DEBUG_THREAD
193         PrintDebug("WaitForAvailableRunnerSignal");
194 #endif // DEBUG_THREAD
195         __Wait(&m_AvailableRunner, &m_AvailableRunnerLock);
196     }
197     __UnLockSignal(&m_AvailableRunnerLock);
198 }
199
200 /***
201     [ConsoleExecDone Signal]
202
203     Send : A console command is excuted.
204     Wait : Wait for the last console command ends.
205
206     This signal can be sent without any threads are waiting for,
207     so we have to perform the Wait for each call to WaitForConsoleExecDoneSignal.
208     (in case of "pause", we send this signal in sci_pause and in Runner::launch)
209
210     The loop while is used to avoid spurious wakeup of __Wait.
211 ***/
212 void ThreadManagement::SendConsoleExecDoneSignal(void)
213 {
214 #ifdef DEBUG_THREAD
215     PrintDebug("SendConsoleExecDoneSignal");
216 #endif // DEBUG_THREAD
217     __LockSignal(&m_ConsoleExecDoneLock);
218     m_ConsoleExecDoneWasSignalled = true;
219     __Signal(&m_ConsoleExecDone);
220     __UnLockSignal(&m_ConsoleExecDoneLock);
221 }
222
223 void ThreadManagement::WaitForConsoleExecDoneSignal(void)
224 {
225 # ifdef __DEBUG_SIGNAL
226     std::cout << "WaitForConsoleExecDoneSignal" << std::endl;
227 # endif // __DEBUG_SIGNAL
228     __LockSignal(&m_ConsoleExecDoneLock);
229     ThreadManagement::UnlockStoreCommand();
230     m_ConsoleExecDoneWasSignalled = false;
231     while (m_ConsoleExecDoneWasSignalled == false)
232     {
233 #ifdef DEBUG_THREAD
234         PrintDebug("WaitForConsoleExecDoneSignal");
235 #endif // DEBUG_THREAD
236         __Wait(&m_ConsoleExecDone, &m_ConsoleExecDoneLock);
237     }
238     __UnLockSignal(&m_ConsoleExecDoneLock);
239 }
240
241 /***
242     [AwakeRunner Signal]
243
244     Send : Wakeup the runner when:
245     Wait : Runner is waiting for:
246             - a new prioritary command have to be execute.
247             - a pause is executed, to allow a new console command.
248             - the last execution is made.
249
250     This signal can be sent without any threads are waiting for,
251     so we have to perform the Wait for each call to WaitForAwakeRunnerSignal.
252
253     The loop while is used to avoid spurious wakeup of __Wait.
254 ***/
255
256 void ThreadManagement::SendAwakeRunnerSignal(void)
257 {
258 # ifdef __DEBUG_SIGNAL
259     std::cout << "SendAwakeRunnerSignal" << std::endl;
260 # endif // __DEBUG_SIGNAL
261     __LockSignal(&m_AwakeRunnerLock);
262     m_AwakeRunnerWasSignalled = true;
263 #ifdef DEBUG_THREAD
264     PrintDebug("SendAwakeRunnerSignal");
265 #endif // DEBUG_THREAD
266     __Signal(&m_AwakeRunner);
267     __UnLockSignal(&m_AwakeRunnerLock);
268 }
269
270 void ThreadManagement::WaitForAwakeRunnerSignal(void)
271 {
272 # ifdef __DEBUG_SIGNAL
273     std::cout << "WaitForAwakeRunnerSignal" << std::endl;
274 # endif // __DEBUG_SIGNAL
275     __LockSignal(&m_AwakeRunnerLock);
276     ThreadManagement::UnlockRunner();
277     m_AwakeRunnerWasSignalled = false;
278     while (m_AwakeRunnerWasSignalled == false)
279     {
280 #ifdef DEBUG_THREAD
281         PrintDebug("WaitForAwakeRunnerSignal");
282 #endif // DEBUG_THREAD
283         __Wait(&m_AwakeRunner, &m_AwakeRunnerLock);
284     }
285     __UnLockSignal(&m_AwakeRunnerLock);
286 }
287
288 /***
289     [StartPending Signal]
290
291     This signal is used in case where we have a console thread and a command to execute passed by -f argument.
292     We have to waiting for the "-f" execution before lets users to enter a new command through the console.
293
294     Send : The console thread (scilabReadAndStore) is ready.
295     Wait : The main thread can create the read and exec command thread (scilabReadAndExecCommand).
296
297     To avoid non-expected lost signal, we have to check if the signal was
298     already sent to know if we have to waiting for or not.
299
300     The loop while is used to avoid spurious wakeup of __Wait.
301 ***/
302 void ThreadManagement::SendStartPendingSignal(void)
303 {
304 # ifdef __DEBUG_SIGNAL
305     std::cout << "SendStartPendingSignal" << std::endl;
306 # endif // __DEBUG_SIGNAL
307     __LockSignal(&m_StartPendingLock);
308     m_StartPendingWasSignalled = true;
309 #ifdef DEBUG_THREAD
310     PrintDebug("SendStartPendingSignal");
311 #endif // DEBUG_THREAD
312     __Signal(&m_StartPending);
313     __UnLockSignal(&m_StartPendingLock);
314 }
315
316 void ThreadManagement::WaitForStartPendingSignal(void)
317 {
318 # ifdef __DEBUG_SIGNAL
319     std::cout << "WaitForStartPendingSignal" << std::endl;
320 # endif // __DEBUG_SIGNAL
321     __LockSignal(&m_StartPendingLock);
322     while (m_StartPendingWasSignalled == false)
323     {
324 #ifdef DEBUG_THREAD
325         PrintDebug("WaitForStartPendingSignal");
326 #endif // DEBUG_THREAD
327         __Wait(&m_StartPending, &m_StartPendingLock);
328     }
329     m_StartPendingWasSignalled = false;
330     __UnLockSignal(&m_StartPendingLock);
331 }
332
333 /***
334     [CommandStored Signal]
335
336     Send : A new command is available in the store command.
337     Wait : Wait for a new command.
338
339     To avoid non-expected lost signal, we have to check if the signal was
340     already sent to know if we have to waiting for or not.
341
342     The loop while is used to avoid spurious wakeup of __Wait.
343 ***/
344 void ThreadManagement::SendCommandStoredSignal(void)
345 {
346     __LockSignal(&m_CommandStoredLock);
347     m_CommandStoredWasSignalled = true;
348 #ifdef DEBUG_THREAD
349     PrintDebug("SendCommandStoredSignal");
350 #endif // DEBUG_THREAD
351     __Signal(&m_CommandStored);
352     __UnLockSignal(&m_CommandStoredLock);
353 }
354
355 void ThreadManagement::WaitForCommandStoredSignal(void)
356 {
357     __LockSignal(&m_CommandStoredLock);
358     while (m_CommandStoredWasSignalled == false)
359     {
360 #ifdef DEBUG_THREAD
361         PrintDebug("WaitForCommandStoredSignal");
362 #endif // DEBUG_THREAD
363         __Wait(&m_CommandStored, &m_CommandStoredLock);
364     }
365     m_CommandStoredWasSignalled = false;
366     __UnLockSignal(&m_CommandStoredLock);
367 }
368
369 /***
370     [RunMe Signal]
371
372     Send : A new runner is available for execution.
373     Wait : Wait for an available Runner.
374
375     This signal can be sent without any threads are waiting for,
376     so we have to perform the Wait for each call to WaitForRunMeSignal.
377     (This can happends when an execution is interrupted by an other one.
378      This signal is sent but the main thread is not waiting for.)
379
380     The loop while is used to avoid spurious wakeup of __Wait.
381 ***/
382 void ThreadManagement::SendRunMeSignal(void)
383 {
384     __LockSignal(&m_RunMeLock);
385     m_RunMeWasSignalled = true;
386 #ifdef DEBUG_THREAD
387     PrintDebug("SendRunMeSignal");
388 #endif // DEBUG_THREAD
389     __Signal(&m_RunMe);
390     __UnLockSignal(&m_RunMeLock);
391 }
392
393 void ThreadManagement::WaitForRunMeSignal(void)
394 {
395     __LockSignal(&m_RunMeLock);
396     m_RunMeWasSignalled = false;
397     // Some times, the signal "SendRunMeSignal" can be sent before the main thread is waiting for.
398     // If a Runner is available do not perform this wait.
399     bool bWait = StaticRunner_isRunnerAvailable() == false;
400     while (m_RunMeWasSignalled == false && bWait)
401     {
402 #ifdef DEBUG_THREAD
403         PrintDebug("WaitForRunMeSignal");
404 #endif // DEBUG_THREAD
405         __Wait(&m_RunMe, &m_RunMeLock);
406     }
407     __UnLockSignal(&m_RunMeLock);
408 }
409
410 #ifdef DEBUG_THREAD
411 void ThreadManagement::SetThreadKey(__threadKey tkMain, __threadKey tkReadAndExec, __threadKey tkConsole)
412 {
413     m_tkMain = tkMain;
414     m_tkReadAndExec = tkReadAndExec;
415     m_tkConsole = tkConsole;
416 }
417
418 void ThreadManagement::PrintDebug(const char* pcfunName)
419 {
420     if (__GetCurrentThreadKey() == m_tkConsole)
421     {
422         std::cout.width(2 * PRINT_COL_SIZE);
423         std::cout << " ";
424     }
425
426     if (__GetCurrentThreadKey() == m_tkReadAndExec)
427     {
428         std::cout.width(PRINT_COL_SIZE);
429         std::cout << " ";
430     }
431
432     std::cout << pcfunName << std::endl;
433 }
434
435 void ThreadManagement::PrintDebugHead()
436 {
437     std::cout << std::endl;
438     std::cout.fill('-');
439     std::cout.width(3 * PRINT_COL_SIZE);
440     std::cout << "-";
441
442     std::cout.fill(' ');
443     std::cout << std::endl;
444     std::cout << std::left;
445     std::cout.width(PRINT_COL_SIZE);
446     std::cout << "Main Thread";
447     std::cout.width(PRINT_COL_SIZE);
448     std::cout << "ReadAndExec Thread";
449     std::cout.width(PRINT_COL_SIZE);
450     std::cout << "Console Thread";
451     std::cout << std::endl << std::endl;
452 }
453 #endif // DEBUG_THREAD