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.
24 #include "win32exception.h"
26 #pragma message( "Please compile using multithreaded run-time libraries" )
28 typedef unsigned (WINAPI
*PBEGINTHREADEX_THREADFUNC
)(LPVOID lpThreadParameter
);
30 #include "PlatformInclude.h"
33 typedef int (*PBEGINTHREADEX_THREADFUNC
)(LPVOID lpThreadParameter
);
37 #include "GraphicContext.h"
38 #include "utils/TimeUtils.h"
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
);
57 //////////////////////////////////////////////////////////////////////
58 // Construction/Destruction
59 //////////////////////////////////////////////////////////////////////
64 // Initialize thread local storage and local thread pointer.
65 pthread_once(&keyOnce
, MakeTlsKeys
);
70 m_bAutoDelete
= false;
71 m_ThreadHandle
= NULL
;
76 m_StopEvent
= CreateEvent(NULL
, TRUE
, TRUE
, NULL
);
81 CThread::CThread(IRunnable
* pRunnable
)
84 // Initialize thread local storage and local thread pointer.
85 pthread_once(&keyOnce
, MakeTlsKeys
);
90 m_bAutoDelete
= false;
91 m_ThreadHandle
= NULL
;
96 m_StopEvent
= CreateEvent(NULL
, TRUE
, TRUE
, NULL
);
98 m_pRunnable
=pRunnable
;
103 if (m_ThreadHandle
!= NULL
)
105 CloseHandle(m_ThreadHandle
);
107 m_ThreadHandle
= NULL
;
110 CloseHandle(m_StopEvent
);
115 // Use pthread-based TLS.
116 #define LOCAL_THREAD ((CThread* )pthread_getspecific(tlsLocalThread))
118 // Use compiler-based TLS.
119 __thread CThread
* pLocalThread
= NULL
;
120 #define LOCAL_THREAD pLocalThread
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
);
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() )
138 int CThread::staticThread(void* data
)
140 DWORD WINAPI
CThread::staticThread(LPVOID
* data
)
143 CThread
* pThread
= (CThread
*)(data
);
145 CLog::Log(LOGERROR
,"%s, sanity failed. thread is NULL.",__FUNCTION__
);
149 CLog::Log(LOGDEBUG
,"thread start, auto delete: %d",pThread
->IsAutoDelete());
152 /* install win32 exception translator */
153 win32_exception::install_handler();
156 pLocalThread
= pThread
;
158 struct sigaction action
;
159 action
.sa_handler
= term_handler
;
160 sigemptyset (&action
.sa_mask
);
162 //sigaction (SIGABRT, &action, NULL);
163 //sigaction (SIGSEGV, &action, NULL);
169 pthread_setspecific(tlsLocalThread
, (void*)pThread
);
174 pThread
->OnStartup();
177 catch (const win32_exception
&e
)
179 e
.writelog(__FUNCTION__
);
180 if( pThread
->IsAutoDelete() )
190 CLog::Log(LOGERROR
, "%s - Unhandled exception caught in thread startup, aborting. auto delete: %d", __FUNCTION__
, pThread
->IsAutoDelete());
191 if( pThread
->IsAutoDelete() )
206 catch (const access_violation
&e
)
208 e
.writelog(__FUNCTION__
);
210 catch (const win32_exception
&e
)
212 e
.writelog(__FUNCTION__
);
217 CLog::Log(LOGERROR
, "%s - Unhandled exception caught in thread process, attemping cleanup in OnExit", __FUNCTION__
);
225 catch (const access_violation
&e
)
227 e
.writelog(__FUNCTION__
);
229 catch (const win32_exception
&e
)
231 e
.writelog(__FUNCTION__
);
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());
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();
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;
266 m_bAutoDelete
= bAutoDelete
;
268 ::ResetEvent(m_StopEvent
);
270 m_ThreadHandle
= (HANDLE
)_beginthreadex(NULL
, stacksize
, (PBEGINTHREADEX_THREADFUNC
)staticThread
, (void*)this, 0, &m_ThreadId
);
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
);
279 bool CThread::IsAutoDelete() const
281 return m_bAutoDelete
;
284 void CThread::StopThread(bool bWait
/*= 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
299 if (m_ThreadHandle
&& m_ThreadHandle
->m_threadValid
)
300 return m_ThreadHandle
->m_hThread
;
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
327 rtn
= SetThreadPriority( m_ThreadHandle
, iPriority
) == TRUE
;
333 void CThread::SetPrioritySched_RR(void)
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.
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
);
349 struct sched_param param
;
350 result
= pthread_getschedparam(ThreadId(), &policy
, ¶m
);
351 // change from default SCHED_OTHER to SCHED_RR
353 result
= pthread_setschedparam(ThreadId(), policy
, ¶m
);
357 int CThread::GetMinPriority(void)
360 //#if defined(__APPLE__)
361 struct sched_param sched
;
364 rtn
= pthread_getschedparam(ThreadId(), &policy
, &sched
);
365 int min
= sched_get_priority_min(policy
);
369 return(THREAD_PRIORITY_LOWEST
);
373 int CThread::GetMaxPriority(void)
376 //#if defined(__APPLE__)
377 struct sched_param sched
;
380 rtn
= pthread_getschedparam(ThreadId(), &policy
, &sched
);
381 int max
= sched_get_priority_max(policy
);
385 return(THREAD_PRIORITY_HIGHEST
);
389 int CThread::GetNormalPriority(void)
392 //#if defined(__APPLE__)
393 struct sched_param sched
;
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) );
402 return(THREAD_PRIORITY_NORMAL
);
407 void CThread::SetName( LPCTSTR szThreadName
)
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
419 info
.dwType
= 0x1000;
420 info
.szName
= szThreadName
;
421 info
.dwThreadID
= m_ThreadId
;
426 RaiseException(MS_VC_EXCEPTION
, 0, sizeof(info
) / sizeof(ULONG_PTR
), (ULONG_PTR
*)&info
);
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;
441 // boost priority of thread we are waiting on to same as caller
442 int callee
= GetThreadPriority(m_ThreadHandle
);
443 int caller
= GetThreadPriority(GetCurrentThread());
445 SetThreadPriority(m_ThreadHandle
, caller
);
447 if (::WaitForSingleObject(m_ThreadHandle
, milliseconds
) != WAIT_TIMEOUT
)
450 // restore thread priority if thread hasn't exited
452 SetThreadPriority(m_ThreadHandle
, callee
);
454 if (!(m_ThreadHandle
->m_threadValid
) || pthread_join(m_ThreadHandle
->m_hThread
, NULL
) == 0)
456 m_ThreadHandle
->m_threadValid
= false;
464 HANDLE
CThread::ThreadHandle()
466 return m_ThreadHandle
;
469 void CThread::Process()
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
;
501 bool CThread::IsCurrentThread() const
503 return IsCurrentThread(ThreadId());
507 ThreadIdentifier
CThread::GetCurrentThreadId()
510 return pthread_self();
512 return ::GetCurrentThreadId();
516 bool CThread::IsCurrentThread(const ThreadIdentifier tid
)
519 return pthread_equal(pthread_self(), tid
);
521 return (::GetCurrentThreadId() == tid
);
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
)
536 if( milliseconds
== INFINITE
)
537 return WAIT_ABANDONED
;
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
);
556 ::Sleep(milliseconds
);