1 //
2 // Thread_POSIX.cpp
3 //
4 // Library: Foundation
5 // Package: Threading
6 // Module: Thread
7 //
8 // Copyright (c) 2004-2007, Applied Informatics Software Engineering GmbH.
9 // and Contributors.
10 //
11 // SPDX-License-Identifier: BSL-1.0
12 //
13
14
15 #include "Poco/Thread_POSIX.h"
16 #include "Poco/Thread.h"
17 #include "Poco/Exception.h"
18 #include "Poco/ErrorHandler.h"
19 #include "Poco/Timespan.h"
20 #include "Poco/Timestamp.h"
21 #include <signal.h>
22 #if defined(__sun) && defined(__SVR4)
23 # if !defined(__EXTENSIONS__)
24 # define __EXTENSIONS__
25 # endif
26 #endif
27 #if POCO_OS == POCO_OS_LINUX || POCO_OS == POCO_OS_ANDROID || POCO_OS == POCO_OS_MAC_OS_X || POCO_OS == POCO_OS_QNX
28 # include <time.h>
29 #endif
30
31 //
32 // Block SIGPIPE in main thread.
33 //
34 #if defined(POCO_OS_FAMILY_UNIX) && !defined(POCO_VXWORKS)
35 namespace
36 {
37 class SignalBlocker
38 {
39 public:
SignalBlocker()40 SignalBlocker()
41 {
42 sigset_t sset;
43 sigemptyset(&sset);
44 sigaddset(&sset, SIGPIPE);
45 pthread_sigmask(SIG_BLOCK, &sset, 0);
46 }
~SignalBlocker()47 ~SignalBlocker()
48 {
49 }
50 };
51
52 static SignalBlocker signalBlocker;
53 }
54 #endif
55
56
57 #if defined(POCO_POSIX_DEBUGGER_THREAD_NAMES)
58
59
60 namespace {
setThreadName(pthread_t thread,const std::string & threadName)61 void setThreadName(pthread_t thread, const std::string& threadName)
62 {
63 #if (POCO_OS == POCO_OS_MAC_OS_X)
64 pthread_setname_np(threadName.c_str()); // __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_2)
65 #else
66 if (pthread_setname_np(thread, threadName.c_str()) == ERANGE && threadName.size() > 15)
67 {
68 std::string truncName(threadName, 0, 7);
69 truncName.append("~");
70 truncName.append(threadName, threadName.size() - 7, 7);
71 pthread_setname_np(thread, truncName.c_str());
72 }
73 #endif
74 }
75 }
76
77
78 #endif
79
80
81 namespace Poco {
82
83
84 ThreadImpl::CurrentThreadHolder ThreadImpl::_currentThreadHolder;
85
86
ThreadImpl()87 ThreadImpl::ThreadImpl():
88 _pData(new ThreadData)
89 {
90 }
91
92
~ThreadImpl()93 ThreadImpl::~ThreadImpl()
94 {
95 if (_pData->started && !_pData->joined)
96 {
97 pthread_detach(_pData->thread);
98 }
99 }
100
101
setPriorityImpl(int prio)102 void ThreadImpl::setPriorityImpl(int prio)
103 {
104 if (prio != _pData->prio)
105 {
106 _pData->prio = prio;
107 _pData->policy = SCHED_OTHER;
108 if (isRunningImpl())
109 {
110 struct sched_param par; struct MyStruct
111 {
112
113 };
114 par.sched_priority = mapPrio(_pData->prio, SCHED_OTHER);
115 if (pthread_setschedparam(_pData->thread, SCHED_OTHER, &par))
116 throw SystemException("cannot set thread priority");
117 }
118 }
119 }
120
121
setOSPriorityImpl(int prio,int policy)122 void ThreadImpl::setOSPriorityImpl(int prio, int policy)
123 {
124 if (prio != _pData->osPrio || policy != _pData->policy)
125 {
126 if (_pData->pRunnableTarget)
127 {
128 struct sched_param par;
129 par.sched_priority = prio;
130 if (pthread_setschedparam(_pData->thread, policy, &par))
131 throw SystemException("cannot set thread priority");
132 }
133 _pData->prio = reverseMapPrio(prio, policy);
134 _pData->osPrio = prio;
135 _pData->policy = policy;
136 }
137 }
138
139
getMinOSPriorityImpl(int policy)140 int ThreadImpl::getMinOSPriorityImpl(int policy)
141 {
142 #if defined(POCO_THREAD_PRIORITY_MIN)
143 return POCO_THREAD_PRIORITY_MIN;
144 #elif defined(__digital__)
145 return PRI_OTHER_MIN;
146 #else
147 return sched_get_priority_min(policy);
148 #endif
149 }
150
151
getMaxOSPriorityImpl(int policy)152 int ThreadImpl::getMaxOSPriorityImpl(int policy)
153 {
154 #if defined(POCO_THREAD_PRIORITY_MAX)
155 return POCO_THREAD_PRIORITY_MAX;
156 #elif defined(__digital__)
157 return PRI_OTHER_MAX;
158 #else
159 return sched_get_priority_max(policy);
160 #endif
161 }
162
163
setStackSizeImpl(int size)164 void ThreadImpl::setStackSizeImpl(int size)
165 {
166 #ifndef PTHREAD_STACK_MIN
167 _pData->stackSize = 0;
168 #else
169 if (size != 0)
170 {
171 #if defined(POCO_OS_FAMILY_BSD)
172 // we must round up to a multiple of the memory page size
173 const int STACK_PAGE_SIZE = 4096;
174 size = ((size + STACK_PAGE_SIZE - 1)/STACK_PAGE_SIZE)*STACK_PAGE_SIZE;
175 #endif
176 if (size < PTHREAD_STACK_MIN)
177 size = PTHREAD_STACK_MIN;
178 }
179 _pData->stackSize = size;
180 #endif
181 }
182
183
startImpl(SharedPtr<Runnable> pTarget)184 void ThreadImpl::startImpl(SharedPtr<Runnable> pTarget)
185 {
186 if (_pData->pRunnableTarget)
187 throw SystemException("thread already running");
188
189 pthread_attr_t attributes;
190 pthread_attr_init(&attributes);
191
192 if (_pData->stackSize != 0)
193 {
194 if (0 != pthread_attr_setstacksize(&attributes, _pData->stackSize))
195 {
196 pthread_attr_destroy(&attributes);
197 throw SystemException("cannot set thread stack size");
198 }
199 }
200
201 _pData->pRunnableTarget = pTarget;
202 if (pthread_create(&_pData->thread, &attributes, runnableEntry, this))
203 {
204 _pData->pRunnableTarget = 0;
205 pthread_attr_destroy(&attributes);
206 throw SystemException("cannot start thread");
207 }
208 _pData->started = true;
209 pthread_attr_destroy(&attributes);
210
211 if (_pData->policy == SCHED_OTHER)
212 {
213 if (_pData->prio != PRIO_NORMAL_IMPL)
214 {
215 struct sched_param par;
216 par.sched_priority = mapPrio(_pData->prio, SCHED_OTHER);
217 if (pthread_setschedparam(_pData->thread, SCHED_OTHER, &par))
218 throw SystemException("cannot set thread priority");
219 }
220 }
221 else
222 {
223 struct sched_param par;
224 par.sched_priority = _pData->osPrio;
225 if (pthread_setschedparam(_pData->thread, _pData->policy, &par))
226 throw SystemException("cannot set thread priority");
227 }
228 }
229
230
joinImpl()231 void ThreadImpl::joinImpl()
232 {
233 if (!_pData->started) return;
234 _pData->done.wait();
235 void* result;
236 if (pthread_join(_pData->thread, &result))
237 throw SystemException("cannot join thread");
238 _pData->joined = true;
239 }
240
241
joinImpl(long milliseconds)242 bool ThreadImpl::joinImpl(long milliseconds)
243 {
244 if (_pData->started && _pData->done.tryWait(milliseconds))
245 {
246 void* result;
247 if (pthread_join(_pData->thread, &result))
248 throw SystemException("cannot join thread");
249 _pData->joined = true;
250 return true;
251 }
252 else if (_pData->started) return false;
253 else return true;
254 }
255
256
currentImpl()257 ThreadImpl* ThreadImpl::currentImpl()
258 {
259 return _currentThreadHolder.get();
260 }
261
262
currentTidImpl()263 ThreadImpl::TIDImpl ThreadImpl::currentTidImpl()
264 {
265 return pthread_self();
266 }
267
268
sleepImpl(long milliseconds)269 void ThreadImpl::sleepImpl(long milliseconds)
270 {
271 #if defined(__digital__)
272 // This is specific to DECThreads
273 struct timespec interval;
274 interval.tv_sec = milliseconds / 1000;
275 interval.tv_nsec = (milliseconds % 1000)*1000000;
276 pthread_delay_np(&interval);
277 #elif POCO_OS == POCO_OS_LINUX || POCO_OS == POCO_OS_ANDROID || POCO_OS == POCO_OS_MAC_OS_X || POCO_OS == POCO_OS_QNX || POCO_OS == POCO_OS_VXWORKS
278 Poco::Timespan remainingTime(1000*Poco::Timespan::TimeDiff(milliseconds));
279 int rc;
280 do
281 {
282 struct timespec ts;
283 ts.tv_sec = (long) remainingTime.totalSeconds();
284 ts.tv_nsec = (long) remainingTime.useconds()*1000;
285 Poco::Timestamp start;
286 rc = ::nanosleep(&ts, 0);
287 if (rc < 0 && errno == EINTR)
288 {
289 Poco::Timestamp end;
290 Poco::Timespan waited = start.elapsed();
291 if (waited < remainingTime)
292 remainingTime -= waited;
293 else
294 remainingTime = 0;
295 }
296 }
297 while (remainingTime > 0 && rc < 0 && errno == EINTR);
298 if (rc < 0 && remainingTime > 0) throw Poco::SystemException("Thread::sleep(): nanosleep() failed");
299 #else
300 Poco::Timespan remainingTime(1000*Poco::Timespan::TimeDiff(milliseconds));
301 int rc;
302 do
303 {
304 struct timeval tv;
305 tv.tv_sec = (long) remainingTime.totalSeconds();
306 tv.tv_usec = (long) remainingTime.useconds();
307 Poco::Timestamp start;
308 rc = ::select(0, NULL, NULL, NULL, &tv);
309 if (rc < 0 && errno == EINTR)
310 {
311 Poco::Timestamp end;
312 Poco::Timespan waited = start.elapsed();
313 if (waited < remainingTime)
314 remainingTime -= waited;
315 else
316 remainingTime = 0;
317 }
318 }
319 while (remainingTime > 0 && rc < 0 && errno == EINTR);
320 if (rc < 0 && remainingTime > 0) throw Poco::SystemException("Thread::sleep(): select() failed");
321 #endif
322 }
323
324
runnableEntry(void * pThread)325 void* ThreadImpl::runnableEntry(void* pThread)
326 {
327 _currentThreadHolder.set(reinterpret_cast<ThreadImpl*>(pThread));
328
329 #if defined(POCO_OS_FAMILY_UNIX)
330 sigset_t sset;
331 sigemptyset(&sset);
332 sigaddset(&sset, SIGQUIT);
333 sigaddset(&sset, SIGTERM);
334 sigaddset(&sset, SIGPIPE);
335 pthread_sigmask(SIG_BLOCK, &sset, 0);
336 #endif
337
338 ThreadImpl* pThreadImpl = reinterpret_cast<ThreadImpl*>(pThread);
339 #if defined(POCO_POSIX_DEBUGGER_THREAD_NAMES)
340 setThreadName(pThreadImpl->_pData->thread, reinterpret_cast<Thread*>(pThread)->getName());
341 #endif
342 AutoPtr<ThreadData> pData = pThreadImpl->_pData;
343 try
344 {
345 pData->pRunnableTarget->run();
346 }
347 catch (Exception& exc)
348 {
349 ErrorHandler::handle(exc);
350 }
351 catch (std::exception& exc)
352 {
353 ErrorHandler::handle(exc);
354 }
355 catch (...)
356 {
357 ErrorHandler::handle();
358 }
359
360 pData->pRunnableTarget = 0;
361 pData->done.set();
362 return 0;
363 }
364
365
mapPrio(int prio,int policy)366 int ThreadImpl::mapPrio(int prio, int policy)
367 {
368 int pmin = getMinOSPriorityImpl(policy);
369 int pmax = getMaxOSPriorityImpl(policy);
370
371 switch (prio)
372 {
373 case PRIO_LOWEST_IMPL:
374 return pmin;
375 case PRIO_LOW_IMPL:
376 return pmin + (pmax - pmin)/4;
377 case PRIO_NORMAL_IMPL:
378 return pmin + (pmax - pmin)/2;
379 case PRIO_HIGH_IMPL:
380 return pmin + 3*(pmax - pmin)/4;
381 case PRIO_HIGHEST_IMPL:
382 return pmax;
383 default:
384 poco_bugcheck_msg("invalid thread priority");
385 }
386 return -1; // just to satisfy compiler - we'll never get here anyway
387 }
388
389
reverseMapPrio(int prio,int policy)390 int ThreadImpl::reverseMapPrio(int prio, int policy)
391 {
392 if (policy == SCHED_OTHER)
393 {
394 int pmin = getMinOSPriorityImpl(policy);
395 int pmax = getMaxOSPriorityImpl(policy);
396 int normal = pmin + (pmax - pmin)/2;
397 if (prio == pmax)
398 return PRIO_HIGHEST_IMPL;
399 if (prio > normal)
400 return PRIO_HIGH_IMPL;
401 else if (prio == normal)
402 return PRIO_NORMAL_IMPL;
403 else if (prio > pmin)
404 return PRIO_LOW_IMPL;
405 else
406 return PRIO_LOWEST_IMPL;
407 }
408 else return PRIO_HIGHEST_IMPL;
409 }
410
411
412 } // namespace Poco
413