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, &regSet) != ERROR)
154     ::trcStack(&regSet, (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