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