changed: update version strings for beta4
[xbmc.git] / xbmc / utils / Thread.cpp
blobabc551fb09f7eb04160da29deaa41c14ebcde19f
1 /*
2 * XBMC Media Center
3 * Copyright (c) 2002 Frodo
4 * Portions Copyright (c) by the authors of ffmpeg and xvid
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 #include "Thread.h"
22 #ifndef _LINUX
23 #include <process.h>
24 #include "win32exception.h"
25 #ifndef _MT
26 #pragma message( "Please compile using multithreaded run-time libraries" )
27 #endif
28 typedef unsigned (WINAPI *PBEGINTHREADEX_THREADFUNC)(LPVOID lpThreadParameter);
29 #else
30 #include "PlatformInclude.h"
31 #include "XHandle.h"
32 #include <signal.h>
33 typedef int (*PBEGINTHREADEX_THREADFUNC)(LPVOID lpThreadParameter);
34 #endif
36 #include "log.h"
37 #include "GraphicContext.h"
38 #include "utils/TimeUtils.h"
40 #ifdef __APPLE__
42 // Use pthread's built-in support for TLS, it's more portable.
44 static pthread_once_t keyOnce = PTHREAD_ONCE_INIT;
45 static pthread_key_t tlsLocalThread = 0;
48 // Called once and only once.
50 static void MakeTlsKeys()
52 pthread_key_create(&tlsLocalThread, NULL);
55 #endif
57 //////////////////////////////////////////////////////////////////////
58 // Construction/Destruction
59 //////////////////////////////////////////////////////////////////////
61 CThread::CThread()
63 #ifdef __APPLE__
64 // Initialize thread local storage and local thread pointer.
65 pthread_once(&keyOnce, MakeTlsKeys);
66 #endif
68 m_bStop = false;
70 m_bAutoDelete = false;
71 m_ThreadHandle = NULL;
72 m_ThreadId = 0;
73 m_iLastTime = 0;
74 m_iLastUsage = 0;
75 m_fLastUsage = 0.0f;
76 m_StopEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
78 m_pRunnable=NULL;
81 CThread::CThread(IRunnable* pRunnable)
83 #ifdef __APPLE__
84 // Initialize thread local storage and local thread pointer.
85 pthread_once(&keyOnce, MakeTlsKeys);
86 #endif
88 m_bStop = false;
90 m_bAutoDelete = false;
91 m_ThreadHandle = NULL;
92 m_ThreadId = 0;
93 m_iLastTime = 0;
94 m_iLastUsage = 0;
95 m_fLastUsage = 0.0f;
96 m_StopEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
98 m_pRunnable=pRunnable;
101 CThread::~CThread()
103 if (m_ThreadHandle != NULL)
105 CloseHandle(m_ThreadHandle);
107 m_ThreadHandle = NULL;
109 if (m_StopEvent)
110 CloseHandle(m_StopEvent);
113 #ifdef _LINUX
114 #ifdef __APPLE__
115 // Use pthread-based TLS.
116 #define LOCAL_THREAD ((CThread* )pthread_getspecific(tlsLocalThread))
117 #else
118 // Use compiler-based TLS.
119 __thread CThread* pLocalThread = NULL;
120 #define LOCAL_THREAD pLocalThread
121 #endif
122 void CThread::term_handler (int signum)
124 CLog::Log(LOGERROR,"thread 0x%lx (%lu) got signal %d. calling OnException and terminating thread abnormally.", pthread_self(), pthread_self(), signum);
125 if (LOCAL_THREAD)
127 LOCAL_THREAD->m_bStop = TRUE;
128 if (LOCAL_THREAD->m_StopEvent)
129 SetEvent(LOCAL_THREAD->m_StopEvent);
131 LOCAL_THREAD->OnException();
132 if( LOCAL_THREAD->IsAutoDelete() )
133 delete LOCAL_THREAD;
136 pthread_exit(NULL);
138 int CThread::staticThread(void* data)
139 #else
140 DWORD WINAPI CThread::staticThread(LPVOID* data)
141 #endif
143 CThread* pThread = (CThread*)(data);
144 if (!pThread) {
145 CLog::Log(LOGERROR,"%s, sanity failed. thread is NULL.",__FUNCTION__);
146 return 1;
149 CLog::Log(LOGDEBUG,"thread start, auto delete: %d",pThread->IsAutoDelete());
151 #ifndef _LINUX
152 /* install win32 exception translator */
153 win32_exception::install_handler();
154 #else
155 #ifndef __APPLE__
156 pLocalThread = pThread;
157 #endif
158 struct sigaction action;
159 action.sa_handler = term_handler;
160 sigemptyset (&action.sa_mask);
161 action.sa_flags = 0;
162 //sigaction (SIGABRT, &action, NULL);
163 //sigaction (SIGSEGV, &action, NULL);
164 #endif
167 #ifdef __APPLE__
168 // Set the TLS.
169 pthread_setspecific(tlsLocalThread, (void*)pThread);
170 #endif
174 pThread->OnStartup();
176 #ifndef _LINUX
177 catch (const win32_exception &e)
179 e.writelog(__FUNCTION__);
180 if( pThread->IsAutoDelete() )
182 delete pThread;
183 _endthreadex(123);
184 return 0;
187 #endif
188 catch(...)
190 CLog::Log(LOGERROR, "%s - Unhandled exception caught in thread startup, aborting. auto delete: %d", __FUNCTION__, pThread->IsAutoDelete());
191 if( pThread->IsAutoDelete() )
193 delete pThread;
194 #ifndef _LINUX
195 _endthreadex(123);
196 #endif
197 return 0;
203 pThread->Process();
205 #ifndef _LINUX
206 catch (const access_violation &e)
208 e.writelog(__FUNCTION__);
210 catch (const win32_exception &e)
212 e.writelog(__FUNCTION__);
214 #endif
215 catch(...)
217 CLog::Log(LOGERROR, "%s - Unhandled exception caught in thread process, attemping cleanup in OnExit", __FUNCTION__);
222 pThread->OnExit();
224 #ifndef _LINUX
225 catch (const access_violation &e)
227 e.writelog(__FUNCTION__);
229 catch (const win32_exception &e)
231 e.writelog(__FUNCTION__);
233 #endif
234 catch(...)
236 CLog::Log(LOGERROR, "%s - Unhandled exception caught in thread exit", __FUNCTION__);
239 if ( pThread->IsAutoDelete() )
241 CLog::Log(LOGDEBUG,"Thread %"PRIu64" terminating (autodelete)", (uint64_t)CThread::GetCurrentThreadId());
242 delete pThread;
243 pThread = NULL;
245 else
246 CLog::Log(LOGDEBUG,"Thread %"PRIu64" terminating", (uint64_t)CThread::GetCurrentThreadId());
248 // DXMERGE - this looks like it might have used to have been useful for something...
249 // g_graphicsContext.DeleteThreadContext();
251 #ifndef _LINUX
252 _endthreadex(123);
253 #endif
254 return 0;
257 void CThread::Create(bool bAutoDelete, unsigned stacksize)
259 if (m_ThreadHandle != NULL)
261 throw 1; //ERROR should not b possible!!!
263 m_iLastTime = CTimeUtils::GetTimeMS() * 10000;
264 m_iLastUsage = 0;
265 m_fLastUsage = 0.0f;
266 m_bAutoDelete = bAutoDelete;
267 m_bStop = false;
268 ::ResetEvent(m_StopEvent);
270 m_ThreadHandle = (HANDLE)_beginthreadex(NULL, stacksize, (PBEGINTHREADEX_THREADFUNC)staticThread, (void*)this, 0, &m_ThreadId);
272 #ifdef _LINUX
273 if (m_ThreadHandle && m_ThreadHandle->m_threadValid && m_bAutoDelete)
274 // FIXME: WinAPI can truncate 64bit pthread ids
275 pthread_detach(m_ThreadHandle->m_hThread);
276 #endif
279 bool CThread::IsAutoDelete() const
281 return m_bAutoDelete;
284 void CThread::StopThread(bool bWait /*= true*/)
286 m_bStop = true;
287 SetEvent(m_StopEvent);
288 if (m_ThreadHandle && bWait)
290 WaitForThreadExit(INFINITE);
291 CloseHandle(m_ThreadHandle);
292 m_ThreadHandle = NULL;
296 ThreadIdentifier CThread::ThreadId() const
298 #ifdef _LINUX
299 if (m_ThreadHandle && m_ThreadHandle->m_threadValid)
300 return m_ThreadHandle->m_hThread;
301 else
302 return 0;
303 #else
304 return m_ThreadId;
305 #endif
309 CThread::operator HANDLE()
311 return m_ThreadHandle;
314 CThread::operator HANDLE() const
316 return m_ThreadHandle;
319 bool CThread::SetPriority(const int iPriority)
320 // Set thread priority
321 // Return true for success
323 bool rtn = false;
325 if (m_ThreadHandle)
327 rtn = SetThreadPriority( m_ThreadHandle, iPriority ) == TRUE;
330 return(rtn);
333 void CThread::SetPrioritySched_RR(void)
335 #ifdef __APPLE__
336 // Changing to SCHED_RR is safe under OSX, you don't need elevated privileges and the
337 // OSX scheduler will monitor SCHED_RR threads and drop to SCHED_OTHER if it detects
338 // the thread running away. OSX automatically does this with the CoreAudio audio
339 // device handler thread.
340 int32_t result;
341 thread_extended_policy_data_t theFixedPolicy;
343 // make thread fixed, set to 'true' for a non-fixed thread
344 theFixedPolicy.timeshare = false;
345 result = thread_policy_set(pthread_mach_thread_np(ThreadId()), THREAD_EXTENDED_POLICY,
346 (thread_policy_t)&theFixedPolicy, THREAD_EXTENDED_POLICY_COUNT);
348 int policy;
349 struct sched_param param;
350 result = pthread_getschedparam(ThreadId(), &policy, &param );
351 // change from default SCHED_OTHER to SCHED_RR
352 policy = SCHED_RR;
353 result = pthread_setschedparam(ThreadId(), policy, &param );
354 #endif
357 int CThread::GetMinPriority(void)
359 #if 0
360 //#if defined(__APPLE__)
361 struct sched_param sched;
362 int rtn, policy;
364 rtn = pthread_getschedparam(ThreadId(), &policy, &sched);
365 int min = sched_get_priority_min(policy);
367 return(min);
368 #else
369 return(THREAD_PRIORITY_LOWEST);
370 #endif
373 int CThread::GetMaxPriority(void)
375 #if 0
376 //#if defined(__APPLE__)
377 struct sched_param sched;
378 int rtn, policy;
380 rtn = pthread_getschedparam(ThreadId(), &policy, &sched);
381 int max = sched_get_priority_max(policy);
383 return(max);
384 #else
385 return(THREAD_PRIORITY_HIGHEST);
386 #endif
389 int CThread::GetNormalPriority(void)
391 #if 0
392 //#if defined(__APPLE__)
393 struct sched_param sched;
394 int rtn, policy;
396 rtn = pthread_getschedparam(ThreadId(), &policy, &sched);
397 int min = sched_get_priority_min(policy);
398 int max = sched_get_priority_max(policy);
400 return( min + ((max-min) / 2) );
401 #else
402 return(THREAD_PRIORITY_NORMAL);
403 #endif
407 void CThread::SetName( LPCTSTR szThreadName )
409 #ifdef _WIN32
410 const unsigned int MS_VC_EXCEPTION = 0x406d1388;
411 struct THREADNAME_INFO
413 DWORD dwType; // must be 0x1000
414 LPCSTR szName; // pointer to name (in same addr space)
415 DWORD dwThreadID; // thread ID (-1 caller thread)
416 DWORD dwFlags; // reserved for future use, most be zero
417 } info;
419 info.dwType = 0x1000;
420 info.szName = szThreadName;
421 info.dwThreadID = m_ThreadId;
422 info.dwFlags = 0;
426 RaiseException(MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(ULONG_PTR), (ULONG_PTR *)&info);
428 catch(...)
431 #endif
434 bool CThread::WaitForThreadExit(unsigned int milliseconds)
435 // Waits for thread to exit, timeout in given number of msec.
436 // Returns true when thread ended
438 if (!m_ThreadHandle) return true;
440 #ifndef _LINUX
441 // boost priority of thread we are waiting on to same as caller
442 int callee = GetThreadPriority(m_ThreadHandle);
443 int caller = GetThreadPriority(GetCurrentThread());
444 if(caller > callee)
445 SetThreadPriority(m_ThreadHandle, caller);
447 if (::WaitForSingleObject(m_ThreadHandle, milliseconds) != WAIT_TIMEOUT)
448 return true;
450 // restore thread priority if thread hasn't exited
451 if(caller > callee)
452 SetThreadPriority(m_ThreadHandle, callee);
453 #else
454 if (!(m_ThreadHandle->m_threadValid) || pthread_join(m_ThreadHandle->m_hThread, NULL) == 0)
456 m_ThreadHandle->m_threadValid = false;
457 return true;
459 #endif
461 return false;
464 HANDLE CThread::ThreadHandle()
466 return m_ThreadHandle;
469 void CThread::Process()
471 if(m_pRunnable)
472 m_pRunnable->Run();
475 float CThread::GetRelativeUsage()
477 unsigned __int64 iTime = CTimeUtils::GetTimeMS();
478 iTime *= 10000; // convert into 100ns tics
480 // only update every 1 second
481 if( iTime < m_iLastTime + 1000*10000 ) return m_fLastUsage;
483 FILETIME CreationTime, ExitTime, UserTime, KernelTime;
484 if( GetThreadTimes( m_ThreadHandle, &CreationTime, &ExitTime, &KernelTime, &UserTime ) )
486 unsigned __int64 iUsage = 0;
487 iUsage += (((unsigned __int64)UserTime.dwHighDateTime) << 32) + ((unsigned __int64)UserTime.dwLowDateTime);
488 iUsage += (((unsigned __int64)KernelTime.dwHighDateTime) << 32) + ((unsigned __int64)KernelTime.dwLowDateTime);
490 if(m_iLastUsage > 0 && m_iLastTime > 0)
491 m_fLastUsage = (float)( iUsage - m_iLastUsage ) / (float)( iTime - m_iLastTime );
493 m_iLastUsage = iUsage;
494 m_iLastTime = iTime;
496 return m_fLastUsage;
498 return 0.0f;
501 bool CThread::IsCurrentThread() const
503 return IsCurrentThread(ThreadId());
507 ThreadIdentifier CThread::GetCurrentThreadId()
509 #ifdef _LINUX
510 return pthread_self();
511 #else
512 return ::GetCurrentThreadId();
513 #endif
516 bool CThread::IsCurrentThread(const ThreadIdentifier tid)
518 #ifdef _LINUX
519 return pthread_equal(pthread_self(), tid);
520 #else
521 return (::GetCurrentThreadId() == tid);
522 #endif
526 DWORD CThread::WaitForSingleObject(HANDLE hHandle, unsigned int milliseconds)
528 if(milliseconds > 10 && IsCurrentThread())
530 HANDLE handles[2] = {hHandle, m_StopEvent};
531 DWORD result = ::WaitForMultipleObjects(2, handles, false, milliseconds);
533 if(result == WAIT_TIMEOUT || result == WAIT_OBJECT_0)
534 return result;
536 if( milliseconds == INFINITE )
537 return WAIT_ABANDONED;
538 else
539 return WAIT_TIMEOUT;
541 else
542 return ::WaitForSingleObject(hHandle, milliseconds);
545 DWORD CThread::WaitForMultipleObjects(DWORD nCount, HANDLE *lpHandles, BOOL bWaitAll, unsigned int milliseconds)
547 // for now not implemented
548 return ::WaitForMultipleObjects(nCount, lpHandles, bWaitAll, milliseconds);
551 void CThread::Sleep(unsigned int milliseconds)
553 if(milliseconds > 10 && IsCurrentThread())
554 ::WaitForSingleObject(m_StopEvent, milliseconds);
555 else
556 ::Sleep(milliseconds);