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