1 /*
2 * tlibvx.cxx
3 *
4 * Thread library implementation for VxWorks
5 *
6 * Portable Windows Library
7 *
8 * The contents of this file are subject to the Mozilla Public License
9 * Version 1.0 (the "License"); you may not use this file except in
10 * compliance with the License. You may obtain a copy of the License at
11 * http://www.mozilla.org/MPL/
12 *
13 * Software distributed under the License is distributed on an "AS IS"
14 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
15 * the License for the specific language governing rights and limitations
16 * under the License.
17 *
18 * The Original Code is Portable Windows Library.
19 *
20 * The Initial Developer of the Original Code is Equivalence Pty. Ltd.
21 *
22 * Portions are Copyright (c) 1993-1998 Equivalence Pty. Ltd.
23 *
24 * Portions are Copyright (C) 1993 Free Software Foundation, Inc.
25 * All Rights Reserved.
26 *
27 * Contributor(s): ______________________________________.
28 *
29 * $Revision: 27623 $
30 * $Author: rjongbloed $
31 * $Date: 2012-05-14 19:03:37 -0500 (Mon, 14 May 2012) $
32 */
33
34
35 class PProcess;
36 class PSemaphore;
37
38 #include <ptlib.h>
39 #include <ptlib/socket.h>
40 #include <trclib.h>
41 #include <usrlib.h>
42 // Forward to undocumented system call
43 extern "C" void dbgPrintCall(INSTR * callAdrs, int funcAdrs, int nargs,
44 UINT32 * pArgs);
45
46 #define VX_LOWEST_PRIORITY 250
47 #define VX_LOW_PRIORITY 200
48 #define VX_NORMAL_PRIORITY 150
49 #define VX_DISPLAY_PRIORITY 100
50 #define VX_URGENT_DISPLAY_PRIORITY 50
51
52 ///////////////////////////////////////////////////////////////////////////////
53 // Critical Section Implementation
54 // -------------------------------
55 class CCriticalSection {
56 public:
57 CCriticalSection();
58 ~CCriticalSection();
59
60 void Lock();
61 void Unlock();
62
63 private:
64 int intLevel;
65 STATUS taskLocked;
66 bool locked;
67 };
68
CCriticalSection()69 CCriticalSection::CCriticalSection()
70 {
71 intLevel = 0;
72 taskLocked = ERROR;
73 locked = false;
74 }
75
~CCriticalSection()76 CCriticalSection::~CCriticalSection()
77 {
78 // Unlock anyway when someone forgot to call Unlock after Lock
79 if (locked == true)
80 Unlock();
81 }
82
Lock()83 void CCriticalSection::Lock()
84 {
85 if (locked == false) {
86 if (::intContext() == PFalse)
87 taskLocked = ::taskLock();
88 else
89 taskLocked = ERROR;
90 intLevel = ::intLock();
91 locked = true;
92 }
93 }
94
Unlock()95 void CCriticalSection::Unlock()
96 {
97 if (locked == true) {
98 ::intUnlock(intLevel);
99 if (taskLocked == OK)
100 ::taskUnlock();
101 locked = false;
102 }
103 }
104
105 ///////////////////////////////////////////////////////////////////////////////
106 // Threads
107 static int const priorities[] = {
108 VX_LOWEST_PRIORITY,
109 VX_LOW_PRIORITY,
110 VX_NORMAL_PRIORITY,
111 VX_DISPLAY_PRIORITY,
112 VX_URGENT_DISPLAY_PRIORITY
113 };
114
ThreadFunction(void * threadPtr)115 int PThread::ThreadFunction(void *threadPtr)
116 {
117 PAssertNULL(threadPtr);
118 PThread * thread = (PThread *)threadPtr;
119 PProcess & process = PProcess::Current();
120 process.activeThreadMutex.Wait();
121 process.activeThreads.SetAt(thread->PX_threadId, thread);
122 process.activeThreadMutex.Signal();
123 process.SignalTimerChange();
124
125 if (::semTake(thread->syncPoint, WAIT_FOREVER) == OK) {
126 if (::semDelete(thread->syncPoint) == OK)
127 thread->syncPoint = NULL;
128 process.OnThreadStart(*thread);
129 thread->Main();
130 process.OnThreadEnded(*thread);
131 }
132 else
133 printf("::ThreadFunction> ::semTake failed, errno=0x%X\n",errno);
134
135 // And delete from administration
136 process.activeThreadMutex.Wait();
137 process.activeThreads.SetAt(thread->PX_threadId, NULL);
138 process.activeThreadMutex.Signal();
139 thread->PX_threadId = 0;
140
141 return 0;
142 }
143
Trace(PThreadIdentifer threadId)144 void PThread::Trace(PThreadIdentifer threadId)
145 {
146 if (threadId == 0)
147 threadId = PThread::GetCurrentThreadId();
148
149 printf("Task name=%s\n", ::taskName(threadId));
150
151 ::taskRegsShow(threadId);
152 REG_SET regSet;
153 if (::taskRegsGet(threadId, ®Set) != ERROR)
154 ::trcStack(®Set, (FUNCPTR)dbgPrintCall, threadId);
155 else
156 printf("::Terminate> ::taskRegsGet failed, errno=0x%X\n", errno);
157 ::taskShow(0, 2);
158 printf("\n");
159 ::checkStack(0);
160 }
161
PThread()162 PThread::PThread()
163 : autoDelete(false)
164 , PX_threadId(::taskIdSelf())
165 , priority(VX_NORMAL_PRIORITY)
166 , originalStackSize(0)
167 {
168 PAssertOS((PX_threadId != ERROR) && (PX_threadId != 0));
169
170 if (!PProcess::IsInitialised())
171 return;
172
173 autoDelete = true;
174
175 PProcess & process = PProcess::Current();
176
177 process.activeThreadMutex.Wait();
178 process.activeThreads.SetAt(PX_threadId, this);
179 process.activeThreadMutex.Signal();
180
181 process.SignalTimerChange();
182 }
183
PThread(PINDEX stackSize,AutoDeleteFlag deletion,Priority priorityLevel,const PString & name)184 PThread::PThread(PINDEX stackSize,
185 AutoDeleteFlag deletion,
186 Priority priorityLevel,
187 const PString & name
188 )
189 {
190 PAssert(stackSize > 0, PInvalidParameter);
191 autoDelete = (deletion == AutoDeleteThread);
192 originalStackSize = stackSize;
193
194 priority = priorities[priorityLevel];
195
196 syncPoint = ::semMCreate(SEM_Q_FIFO);
197 if (syncPoint != NULL) {
198 if (::semTake(syncPoint, NO_WAIT) == OK) {
199 STATUS taskLocked;
200 taskLocked = ::taskLock();
201 PX_threadId = ::taskSpawn(name, // Name
202 priority, // Priority
203 0, // options
204 stackSize, // stacksize
205 (FUNCPTR)ThreadFunction, // entrypoint
206 (int)this,0,0,0,0,0,0,0,0,0); // arg 1 --- arg 10
207
208 if (PX_threadId != ERROR) {
209 // threads are created suspended
210 Suspend();
211 ::semGive(syncPoint);
212
213 if (taskLocked == OK)
214 ::taskUnlock();
215
216 if (autoDelete) {
217 PProcess & process = PProcess::Current();
218 process.deleteThreadMutex.Wait();
219 process.autoDeleteThreads.Append(this);
220 process.deleteThreadMutex.Signal();
221 }
222 }
223 else {
224 if (taskLocked == OK)
225 ::taskUnlock();
226 printf("::PThread> ::taskSpawn failed, errno=0x%X\n", errno);
227 PX_threadId = 0;
228 ::semDelete(syncPoint);
229 syncPoint = NULL;
230 }
231 }
232 else {
233 printf("::PThread> ::semTake failed, errno=0x%X\n", errno);
234 ::semDelete(syncPoint);
235 syncPoint = NULL;
236 }
237 }
238 }
239
240
~PThread()241 PThread::~PThread()
242 {
243 if (originalStackSize <= 0)
244 return;
245
246 if (!IsTerminated())
247 Terminate();
248 }
249
250
Restart()251 void PThread::Restart()
252 {
253 if (IsTerminated()) {
254 PX_threadId = ::taskSpawn(NULL, // Auto name tn
255 priority, // Priority
256 0, // options
257 originalStackSize, // stacksize
258 (FUNCPTR)ThreadFunction, // entrypoint
259 (int)this,0,0,0,0,0,0,0,0,0); // arg 1 --- arg 10
260
261 if (PX_threadId == ERROR) {
262 printf("::Restart> ::taskSpawn failed, errno=0x%X\n", errno);
263 PX_threadId = 0;
264 }
265 }
266 else
267 printf("::Restart> Cannot restart running thread\n");
268 }
269
Terminate()270 void PThread::Terminate()
271 {
272 if (originalStackSize <= 0)
273 return;
274
275 if (!IsTerminated()) {
276 if (::taskDelete(PX_threadId) == ERROR)
277 printf("::Terminate> ::taskDelete failed, errno=0x%X\n", errno);
278
279 // And delete from administration
280 PProcess & process = PProcess::Current();
281 process.activeThreadMutex.Wait();
282 process.activeThreads.SetAt(PX_threadId, NULL);
283 process.activeThreadMutex.Signal();
284 PX_threadId = 0;
285 }
286 }
287
288
IsTerminated() const289 PBoolean PThread::IsTerminated() const
290 {
291 STATUS stat = ERROR;
292 if (PX_threadId != 0)
293 stat = ::taskIdVerify(PX_threadId);
294
295 return stat == ERROR;
296 }
297
298
WaitForTermination() const299 void PThread::WaitForTermination() const
300 {
301 while (!IsTerminated()) {
302 Current()->Sleep(100);
303 }
304 }
305
306
WaitForTermination(const PTimeInterval & maxWait) const307 PBoolean PThread::WaitForTermination(const PTimeInterval & maxWait) const
308 {
309 if (PX_threadId == 0)
310 return PTrue;
311
312 PTimer timeout = maxWait;
313 while (!IsTerminated()) {
314 if (timeout == 0)
315 return PFalse;
316 Current()->Sleep(100);
317 }
318 return PTrue;
319 }
320
321
Suspend(PBoolean susp)322 void PThread::Suspend(PBoolean susp)
323 {
324 if (!IsTerminated()) {
325 if (susp) {
326 if (::taskSuspend(PX_threadId) == ERROR)
327 printf("::Suspend> Thread doesn't want to suspend, errno=0x%X\n", errno);
328 }
329 else {
330 if (::taskResume(PX_threadId) == ERROR)
331 printf("::Suspend> Thread doesn't want to resume, errno=0x%X\n", errno);
332 }
333 }
334 else
335 printf("::Suspend> Operation on terminated thread\n");
336 }
337
338
Resume()339 void PThread::Resume()
340 {
341 if (!IsTerminated()) {
342 if (::taskResume(PX_threadId) == ERROR)
343 printf("::Resume> Thread doesn't want to resume, errno=0x%X\n", errno);
344 }
345 else
346 printf("::Resume> Operation on terminated thread\n");
347 }
348
349
IsSuspended() const350 PBoolean PThread::IsSuspended() const
351 {
352 PBoolean isSuspended = PFalse;
353 if (!IsTerminated())
354 isSuspended = ::taskIsSuspended(PX_threadId);
355 else
356 printf("::IsSuspended> Operation on terminated thread\n");
357
358 return isSuspended;
359 }
360
SetAutoDelete(AutoDeleteFlag deletion)361 void PThread::SetAutoDelete(AutoDeleteFlag deletion)
362 {
363 PAssert(deletion != AutoDeleteThread || this != &PProcess::Current(), PLogicError);
364 autoDelete = deletion == AutoDeleteThread;
365 }
366
SetPriority(Priority priorityLevel)367 void PThread::SetPriority(Priority priorityLevel)
368 {
369 if (!IsTerminated()) {
370 priority = priorities[priorityLevel];
371 if (::taskPrioritySet(PX_threadId, priority ) == ERROR)
372 printf("::SetPriority> ::taskPrioritySet failed, errno: 0x%X\n", errno);
373 }
374 else
375 printf("::SetPriority> Operation on terminated thread\n");
376 }
377
378
GetPriority() const379 PThread::Priority PThread::GetPriority() const
380 {
381 if (!IsTerminated()) {
382 int prio;
383 if (::taskPriorityGet(PX_threadId, &prio) == OK) {
384 switch (prio) {
385 case VX_LOWEST_PRIORITY :
386 return LowestPriority;
387 case VX_LOW_PRIORITY :
388 return LowPriority;
389 case VX_NORMAL_PRIORITY :
390 return NormalPriority;
391 case VX_DISPLAY_PRIORITY :
392 return HighPriority;
393 case VX_URGENT_DISPLAY_PRIORITY :
394 return HighestPriority;
395 default:
396 printf("::GetPriority> Priority %d not mapped\n", prio);
397 break;
398 }
399 }
400 else
401 printf("::GetPriority> ::taskPriorityGet failed, errno=0x%X\n", errno);
402 }
403 else
404 printf("::GetPriority> Operation on terminated thread\n");
405
406 return LowestPriority;
407 }
408
Yield()409 void PThread::Yield()
410 {
411 ::taskDelay(NO_WAIT);
412 }
413
Sleep(const PTimeInterval & delay)414 void PThread::Sleep( const PTimeInterval & delay ) // Time interval to sleep for in microsec.
415 {
416 ::taskDelay(delay.GetInterval()*sysClkRateGet()/1000);
417 }
418
PXBlockOnChildTerminate(int pid,const PTimeInterval &)419 int PThread::PXBlockOnChildTerminate(int pid, const PTimeInterval & /*timeout*/) // Fix timeout
420 {
421 while (!IsTerminated()) {
422 Current()->Sleep(100);
423 }
424 return PTrue;
425 }
426
PXBlockOnIO(int handle,int type,const PTimeInterval & timeout)427 int PThread::PXBlockOnIO(int handle, int type, const PTimeInterval & timeout)
428 {
429 // make sure we flush the buffer before doing a write
430 fd_set tmp_rfd, tmp_wfd, tmp_efd;
431 fd_set * read_fds = &tmp_rfd;
432 fd_set * write_fds = &tmp_wfd;
433 fd_set * exception_fds = &tmp_efd;
434
435 FD_ZERO (read_fds);
436 FD_ZERO (write_fds);
437 FD_ZERO (exception_fds);
438
439 switch (type) {
440 case PChannel::PXReadBlock:
441 case PChannel::PXAcceptBlock:
442 FD_SET (handle, read_fds);
443 break;
444 case PChannel::PXWriteBlock:
445 FD_SET (handle, write_fds);
446 break;
447 case PChannel::PXConnectBlock:
448 FD_SET (handle, write_fds);
449 FD_SET (handle, exception_fds);
450 break;
451 default:
452 PAssertAlways(PLogicError);
453 return 0;
454 }
455
456 P_timeval tval = timeout;
457 return ::select(handle+1, read_fds, write_fds, exception_fds, tval);
458 }
459
PXAbortBlock() const460 void PThread::PXAbortBlock() const
461 {
462 }
463
464 ///////////////////////////////////////////////////////////////////////////////
465 // PProcess::HouseKeepingThread
466
Construct()467 void PProcess::Construct()
468 {
469 // hard coded value, change this to handle more sockets at once with the select call
470 maxHandles = 1024;
471 houseKeeper=NULL;
472 CommonConstruct();
473 }
474
HouseKeepingThread()475 PProcess::HouseKeepingThread::HouseKeepingThread()
476 : PThread(10000, NoAutoDeleteThread, HighPriority, PString("HKeeping"))
477 {
478 Resume();
479 }
480
Main()481 void PProcess::HouseKeepingThread::Main()
482 {
483 PProcess & process = PProcess::Current();
484
485 while(1) {
486 process.deleteThreadMutex.Wait();
487 for (PINDEX i = 0; i < process.autoDeleteThreads.GetSize(); i++) {
488 PThread * pThread = (PThread *) process.autoDeleteThreads.GetAt(i);
489 if( pThread->IsTerminated() )
490 process.autoDeleteThreads.RemoveAt(i--);
491 }
492 process.deleteThreadMutex.Signal();
493 PTimeInterval nextTimer = process.timers.Process();
494 if (nextTimer != PMaxTimeInterval) {
495 if ( nextTimer.GetInterval() > 10000 )
496 nextTimer = 10000;
497 }
498 breakBlock.Wait( nextTimer );
499 }
500 }
501
SignalTimerChange()502 void PProcess::SignalTimerChange()
503 {
504 if (!PAssert(IsInitialised(), PLogicError) || m_shuttingDown)
505 return false;
506
507 if (houseKeeper == NULL) {
508 // Prevent reentrance before the following assignment is done
509 // Placed after above if-statement due to efficiency, and so
510 // requires an another NULL-test.
511 CCriticalSection section;
512 section.Lock();
513 if (houseKeeper == NULL)
514 houseKeeper = new HouseKeepingThread;
515 section.Unlock();
516 }
517 else
518 houseKeeper->breakBlock.Signal();
519 }
520
521 ///////////////////////////////////////////////////////////////////////////////
522 // PProcess
523
~PProcess()524 PProcess::~PProcess()
525 {
526 PreShutdown();
527
528 Sleep(100); // Give threads time to die a natural death
529
530 delete houseKeeper;
531
532 // OK, if there are any left we get really insistent...
533 activeThreadMutex.Wait();
534 for (PINDEX i = 0; i < activeThreads.GetSize(); i++) {
535 PThread & thread = activeThreads.GetDataAt(i);
536 if (this != &thread && !thread.IsTerminated())
537 thread.Terminate(); // With extreme prejudice
538 }
539 activeThreadMutex.Signal();
540
541 deleteThreadMutex.Wait();
542 autoDeleteThreads.RemoveAll();
543 deleteThreadMutex.Signal();
544
545 delete configFiles;
546
547 PostShutdown();
548 }
549
550 ///////////////////////////////////////////////////////////////////////////////
551 // PSemaphore
552
PSemaphore(SEM_ID anId)553 PSemaphore::PSemaphore(SEM_ID anId)
554 {
555 initialVar = 1;
556 maxCountVar = UINT_MAX;
557 semId = anId;
558 PAssertOS(semId != NULL);
559 }
560
PSemaphore(unsigned initial,unsigned maxCount)561 PSemaphore::PSemaphore(unsigned initial, unsigned maxCount)
562 {
563 if (initial > maxCount)
564 initial = maxCount;
565 initialVar = initial;
566 maxCountVar = maxCount;
567 semId = ::semCCreate(SEM_Q_FIFO, initial);
568 PAssertOS(semId != NULL);
569 }
570
PSemaphore(const PSemaphore & sem)571 PSemaphore::PSemaphore(const PSemaphore & sem)
572 {
573 initialVar = sem.GetInitial();
574 maxCountVar = sem.GetMaxCount();
575
576 if (initialVar > maxCountVar)
577 initialVar = maxCountVar;
578 semId = ::semCCreate(SEM_Q_FIFO, initialVar);
579 PAssertOS(semId != NULL);
580 }
581
~PSemaphore()582 PSemaphore::~PSemaphore()
583 {
584 if (semId != NULL) {
585 if (::semDelete(semId) == OK)
586 semId = NULL;
587 else
588 printf("~PSemaphore> Error delete with ID=0x%X with errno: 0x%X\n",
589 (unsigned int)semId, errno);
590 }
591 }
592
Wait()593 void PSemaphore::Wait()
594 {
595 STATUS result = ::semTake(semId, WAIT_FOREVER);
596 if (result == OK) {
597 CCriticalSection section;
598 section.Lock();
599 initialVar--;
600 section.Unlock();
601 }
602 PAssertOS(result != ERROR);
603 }
604
605
Wait(const PTimeInterval & timeout)606 PBoolean PSemaphore::Wait(const PTimeInterval & timeout)
607 {
608 long wait;
609 if (timeout == PMaxTimeInterval) {
610 wait = WAIT_FOREVER;
611 }
612 else {
613 int ticks = sysClkRateGet();
614 wait = (timeout.GetInterval() * ticks);
615 wait = wait / 1000;
616 wait++; // wait at least one tick
617 }
618 STATUS result = ::semTake(semId, wait);
619 if (result == OK) {
620 CCriticalSection section;
621 section.Lock();
622 initialVar--;
623 section.Unlock();
624 }
625 PAssertOS((result != ERROR) || (errno == S_objLib_OBJ_TIMEOUT));
626 return result != ERROR;
627 }
628
Signal()629 void PSemaphore::Signal()
630 {
631 CCriticalSection section;
632 section.Lock();
633 if (initialVar < maxCountVar) {
634 section.Unlock();
635 STATUS result = ::semGive(semId);
636 section.Lock();
637 if (result == OK)
638 initialVar++;
639 PAssertOS(result != ERROR);
640 }
641 section.Unlock();
642 }
643
WillBlock() const644 PBoolean PSemaphore::WillBlock() const
645 {
646 return initialVar == 0;
647 }
648
649 ///////////////////////////////////////////////////////////////////////////////
650 // PMutex
PMutex()651 PMutex::PMutex()
652 : PSemaphore( ::semMCreate(SEM_Q_FIFO) )
653 {
654 }
655
PMutex(const PMutex & mutex)656 PMutex::PMutex(const PMutex & mutex)
657 : PSemaphore(::semMCreate(SEM_Q_FIFO))
658 {
659 }
660
~PMutex()661 PMutex::~PMutex()
662 {
663 if (semId != NULL) {
664 if (::semDelete(semId) == OK)
665 semId = NULL;
666 else
667 printf("~PMutex> Error delete with ID=0x%X with errno: 0x%X\n",
668 (unsigned int)semId, errno);
669 }
670 }
671
Wait()672 void PMutex::Wait()
673 {
674 STATUS result = ::semTake(semId, WAIT_FOREVER);
675 PAssertOS(result == OK);
676 }
677
Wait(const PTimeInterval & timeout)678 PBoolean PMutex::Wait(const PTimeInterval & timeout)
679 {
680 long wait;
681 if (timeout == PMaxTimeInterval) {
682 wait = WAIT_FOREVER;
683 }
684 else {
685 int ticks = sysClkRateGet();
686 wait = (timeout.GetMilliSeconds() * ticks);
687 wait = wait / 1000;
688 wait++; // wait at least one tick
689 }
690 STATUS result = ::semTake(semId, wait);
691 PAssertOS((result != ERROR) || (errno == S_objLib_OBJ_TIMEOUT));
692 return result != ERROR;
693 }
694
Signal()695 void PMutex::Signal()
696 {
697 ::semGive(semId);
698 }
699
WillBlock() const700 PBoolean PMutex::WillBlock() const
701 {
702 STATUS result = ::semTake(semId, NO_WAIT);
703 PAssertOS((result != ERROR) || (errno == S_objLib_OBJ_UNAVAILABLE));
704 if (result != ERROR)
705 ::semGive(semId);
706 return result == ERROR;
707 }
708
709 ///////////////////////////////////////////////////////////////////////////////
710 // PSyncPoint
711
PSyncPoint()712 PSyncPoint::PSyncPoint()
713 : PSemaphore(0, 1)
714 {
715 }
716
PSyncPoint(const PSyncPoint & syncPoint)717 PSyncPoint::PSyncPoint(const PSyncPoint & syncPoint)
718 : PSemaphore(syncPoint)
719 {
720 }
721
722 // End Of File ///////////////////////////////////////////////////////////////
723