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