1 /*
2  * NucleusThread.cxx
3  *
4  * pwlib's PThread as implemented for Nucleus++
5  *
6  * Copyright (c) 1999 ISDN Communications Ltd
7  *
8  * Author: Chris Wayman Purvis
9  *
10  */
11 
12 #include <ptlib.h>
13 #include <ptlib/sockets.h>
14 #include "net/inc/externs.h"
15 #include "plus/nucleus.h"
16 #include "net/inc/socketd.h"                 /* socket interface structures */
17 #include "net/target.h"
18 #include "net/inc/tcpdefs.h"
19 #include <sys/socket.h>
20 
21 const UNSIGNED pwNUTask::NUTimeSlicesPermitted = 5;
22 
23 // Map from pwlib priority level to Nucleus priority level
24 static int const priorities[] =
25   {
26   66,    // Lowest Priority
27   64,    // Low Priority
28   62,    // Normal Priority
29   60,    // High Priority
30   10     // Highest Priority
31   };
32 
33 
34 #ifdef __NUCLEUS_MNT__
35 #define new PNEW
36 #endif
37 
PThread(PINDEX stackSize,AutoDeleteFlag deletion,Priority PriorityLevel,const PString & ThreadName)38 PThread::PThread(PINDEX stackSize, AutoDeleteFlag deletion,
39                                     Priority PriorityLevel,
40                                    const PString & ThreadName)
41 // Threads start in a singly-suspended state.  That's the way it works.
42   : n_suspendCount(1)
43   {
44   PAssert(stackSize > 0, PInvalidParameter);
45 
46   autoDelete = (deletion == AutoDeleteThread);
47 
48   NucleusTask = new pwNUTask( stackSize,// Stack Size Requested
49                               priorities[PriorityLevel],
50                                     // Map from pwlib level to Nucleus level
51                               this,
52                               ThreadName);    // So it knows what to call back to!
53   STATUS stat = NucleusTask->Information(&NucleusTaskInfo);
54   PAssert(stat == NU_SUCCESS, "Failure to find TaskInfo block");
55   }
56 
~PThread()57 PThread::~PThread()
58   {
59   if (!IsTerminated())
60     {
61     Terminate();
62     }
63   delete NucleusTask;
64   }
65 
Restart()66 void PThread::Restart()
67   {
68   PAssert(IsTerminated(), "Cannot restart running thread");
69 
70   NucleusTask->Reset();
71   PAssertAlways("Reset, but not restarted...");
72   }
73 
Terminate()74 void PThread::Terminate()
75   {
76   PAssert(!IsTerminated(),
77                     "Cannot terminate a thread which is already terminated");
78   STATUS stat = NucleusTask->Terminate();
79   PAssert(stat == NU_SUCCESS, "Invalid Task Pointer on Termination");
80 
81   }
82 
83 #if !P_USE_INLINES
84 
85 // What a pfaff for something that's easy...
86 // Still, at last I've written a function I'm pretty confident will work!
IsTerminated() const87 PBoolean PThread::IsTerminated() const
88   {
89 #ifdef __NUCLEUS_MNT__
90   cout << "q";
91 #else
92   printf("q");
93 #endif
94   STATUS stat = NucleusTaskInfo->Update();
95   PAssert(stat == NU_SUCCESS, "Invalid Task Pointer on Termination Check");
96   return ((NucleusTaskInfo->taskStatus == NU_TERMINATED) || (NucleusTaskInfo->taskStatus == NU_FINISHED));
97   }
98 #endif
99 
WaitForTermination() const100 void PThread::WaitForTermination() const
101   {
102   while (!IsTerminated())
103     {
104     Current()->Sleep(10);
105     }
106   }
107 
WaitForTermination(const PTimeInterval & maxWait) const108 PBoolean PThread::WaitForTermination(const PTimeInterval & maxWait) const
109   {
110   PTimer timeout = maxWait;
111   while (!IsTerminated())
112     {
113     if (timeout == 0)
114       {
115       return PFalse;
116       }
117     Current()->Sleep(10);
118     }
119   return PTrue;
120   }
121 
Suspend(PBoolean susp)122 void PThread::Suspend(PBoolean susp)
123   {
124   STATUS stat;
125   if (susp)
126     {
127     if (++n_suspendCount != 1)
128       {
129       stat = NucleusTask->Suspend();
130       PAssert(stat == NU_SUCCESS, "Invalid Task Pointer on suspend");
131       }
132     }
133   else
134     {
135     Resume();
136     }
137   }
138 
139 #if !P_USE_INLINES
Resume()140 void PThread::Resume()
141   {
142   switch (--n_suspendCount)
143     {
144     case -1:
145       PAssertAlways("Resuming thread that hasn't been suspended!");
146       n_suspendCount = 0;
147       break;
148     case 0:
149         {
150         STATUS  stat = NucleusTask->Resume();
151         PAssert(stat == NU_SUCCESS, "Invalid Task Pointer on resume");
152         }
153       break;
154     default:  // Already handled in switch statement
155       break;
156     }
157   }
158 
IsSuspended() const159 PBoolean PThread::IsSuspended() const
160   {
161   return (n_suspendCount != 0);
162   }
163 
SetPriority(Priority priorityLevel)164 void PThread::SetPriority(Priority priorityLevel)
165   {
166   ((Task *)(NucleusTask))->ChangePriority(priorities[priorityLevel]);
167   }
168 
GetPriority() const169 PThread::Priority PThread::GetPriority() const
170   {
171 //  TaskInfo * NucleusTaskInfo = 0;
172 //  STATUS stat = NucleusTask->Information(&NucleusTaskInfo);
173   NucleusTaskInfo->Update();
174   PAssert(stat == NU_SUCCESS, "Invalid Task Pointer on GetPriority Request");
175 
176   PBoolean FoundIt = PFalse;
177 
178   for ( int i = LowestPriority;
179         i < NumPriorities;
180         ++i)
181     {
182     if (NucleusTaskInfo->priority == priorities[i])
183       {
184       return (PThread::Priority)i;
185       }
186     }
187 
188   PAssertAlways ("PriorityLevel not valid for pwlib task!");
189   return (PThread::Priority)NucleusTaskInfo->priority;
190   }
191 
Current()192 PThread * PThread::Current()
193   {
194   return ((pwNUTask *)Task::Current())->AssociatedPThread;
195   }
196 #endif
197 
Yield()198 void PThread::Yield()
199   {
200   Task::Relinquish();
201   }
202 
Sleep(const PTimeInterval & time)203 void PThread::Sleep(const PTimeInterval & time)
204   {
205     Task::Sleep(((time.GetInterval())/PTimer::Resolution()));
206   }
207 
208 
209 #if 0
210 // Unnecessary as Nucleus has threads!
211 PBoolean PThread::IsNoLongerBlocked()
212   {
213   STATUS stat = NucleusTaskInfo->Update();
214   PAssert(stat == NU_SUCCESS, "Invalid Task Pointer on Blocking Check");
215 
216   PAssert(NucleusTaskInfo->taskStatus != NU_TERMINATED,
217     "Operation on terminated thread");
218 
219 // No longer blocked iff ready to go!
220   return (NucleusTaskInfo->taskStatus) == NU_READY;
221   }
222 #endif
223 
PThread()224 PThread::PThread()
225   : n_suspendCount(1)
226   {
227   autoDelete = PFalse;
228 
229   NucleusTask = new pwNUTask( (UNSIGNED)2048, // Stack Size Requested
230                               (OPTION)priorities[HighPriority],
231                                     // Map from pwlib level to Nucleus level
232                               (PThread *)this,
233                               "PWLIB");// So it knows what to call back to!
234   NucleusTaskInfo = new TaskInfo();
235   }
236 
237 // Stolen out of tlib threads
PXBlockOnIO(int handle,int type,const PTimeInterval & timeout)238 int PThread::PXBlockOnIO(int handle, int type, const PTimeInterval & timeout)
239 {
240   // make sure we flush the buffer before doing a write
241   fd_set tmp_rfd, tmp_wfd, tmp_efd;
242   fd_set * read_fds      = &tmp_rfd;
243   fd_set * write_fds     = &tmp_wfd;
244   fd_set * exception_fds = &tmp_efd;
245 
246   FD_ZERO(read_fds);
247   FD_ZERO(write_fds);
248   FD_ZERO(exception_fds);
249 
250   switch (type) {
251     case PChannel::PXReadBlock:
252     case PChannel::PXAcceptBlock:
253       FD_SET(handle, read_fds);
254       break;
255     case PChannel::PXWriteBlock:
256       FD_SET(handle, write_fds);
257       break;
258     case PChannel::PXConnectBlock:
259       FD_SET(handle, write_fds);
260       FD_SET(handle, exception_fds);
261       break;
262     default:
263       PAssertAlways(PLogicError);
264       return 0;
265   }
266 
267   struct timeval * tptr = NULL;
268   struct timeval   timeout_val;
269   if (timeout != PMaxTimeInterval) {
270     static const PTimeInterval oneDay(0, 0, 0, 0, 1);
271     if (timeout < oneDay) {
272       timeout_val.tv_usec = (timeout.GetMilliSeconds() % 1000) * 1000;
273       timeout_val.tv_sec  = timeout.GetSeconds();
274       tptr                = &timeout_val;
275     }
276   }
277 
278   int retval = ::select(handle+1, read_fds, write_fds, exception_fds, tptr);
279 
280   PProcess::Current().PXCheckSignals();
281   return retval;
282 }
283 
Entry()284 void pwNUTask::Entry()
285 {
286   PProcess & process = PProcess::Current();
287   process.OnThreadStart(*AssociatedPThread);
288   AssociatedPThread->Main();
289   process.OnThreadEnded(*AssociatedPThread);
290 }
291