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