break thread/mutex implementation stuff out of RageThreads

(not used yet)
This commit is contained in:
Glenn Maynard
2004-06-11 05:12:58 +00:00
parent 95d10d7123
commit 310c18d70c
5 changed files with 671 additions and 0 deletions
+93
View File
@@ -0,0 +1,93 @@
#ifndef THREADS_H
#define THREADS_H
/* This is the low-level implementation; you probably want RageThreads. */
class RageMutex;
class ThreadImpl
{
#if defined(PID_BASED_THREADS)
/* Keep a list of child PIDs, so we can send them SIGKILL. This has an
* added bonus: if this is corrupted, we'll just send signals and they'll
* fail; we won't blow up (unless we're root). */
// int pid;
#endif
#if defined(DARWIN)
// thread_act_t ThreadHandle;
#endif
public:
virtual ~ThreadImpl() { }
virtual void Halt( bool Kill ) = 0;
virtual void Resume() = 0;
/* Get the identifier for this thread. The actual meaning of this is implementation-
* defined, except that each thread has exactly one ID and each ID corresponds to
* one thread. (This means that Win32 thread handles are not acceptable as ThreadIds.) */
virtual uint64_t GetThreadId() const = 0;
virtual uint64_t GetCrashHandle() const { return 0; }
virtual int Wait() = 0;
};
class MutexImpl
{
public:
RageMutex *m_Parent;
MutexImpl( RageMutex *pParent ) { m_Parent = pParent; }
virtual ~MutexImpl() { }
/* Lock the mutex. If mutex timeouts are implemented, and the mutex times out,
* return false and do not lock the mutex. No other failure return is allowed;
* all other errors should fail with an assertion. */
virtual bool Lock() = 0;
/* Unlock the mutex. This must only be called when the mutex is locked; implementations
* may fail with an assertion if the mutex is not locked. */
virtual void Unlock() = 0;
/* Return the thread ID of the thread that has this mutex locked. If no thread
* holds the mutex, return GetInvalidThreadId. */
virtual uint64_t GetLockedByThreadId() const = 0;
};
/* These functions must be implemented by the thread implementation. */
ThreadImpl *MakeThread( int (*fn)(void *), void *data );
ThreadImpl *MakeThisThread();
MutexImpl *MakeMutex( RageMutex *pParent );
uint64_t GetThisThreadId();
/* Since ThreadId is implementation-defined, we can't define a universal invalid
* value. Return the invalid value for this implementation. */
uint64_t GetInvalidThreadId();
#endif
/*
* (c) 2001-2004 Glenn Maynard
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
@@ -0,0 +1,240 @@
#include "global.h"
#include "Threads_Pthreads.h"
#include <sys/time.h>
#if defined(LINUX)
#define PID_BASED_THREADS
#include "archutils/Unix/LinuxThreadHelpers.h"
#endif
#if defined(HAVE_PTHREAD_MUTEX_TIMEDLOCK) && defined(CRASH_HANDLER)
#include "archutils/Unix/Backtrace.h"
#include "archutils/Unix/CrashHandler.h"
#endif
#if defined(DARWIN)
#include <mach/mach_init.h>
#include <mach/thread_act.h>
#endif
void ThreadImpl_Pthreads::Halt( bool Kill )
{
#if defined(PID_BASED_THREADS)
/*
* Send a SIGSTOP to the thread. If we send a SIGKILL, pthreads will
* "helpfully" propagate it to the other threads, and we'll get killed, too.
*
* This isn't ideal, since it can cause the process to background as far as
* the shell is concerned, so the shell prompt can display before the crash
* handler actually displays a message.
*/
SuspendThread( pid );
#elif defined(DARWIN)
thread_suspend( MachThreadHandle );
#endif
}
void ThreadImpl_Pthreads::Resume()
{
#if defined(PID_BASED_THREADS)
/* Send a SIGCONT to the thread. */
ResumeThread( pid );
#elif defined(DARWIN)
thread_resume( MachThreadHandle );
#endif
}
uint64_t ThreadImpl_Pthreads::GetThreadId() const
{
#if defined(PID_BASED_THREADS)
return (uint64_t) pid;
#elif defined(DARWIN)
return MachThreadHandle;
#endif
}
int ThreadImpl_Pthreads::Wait()
{
void *val;
int ret = pthread_join( thread, &val );
if( ret )
RageException::Throw( "pthread_join: %s", strerror(ret) );
return (int) val;
}
static ThreadImpl *MakeThisThread()
{
ThreadImpl_Pthreads *thread = new ThreadImpl_Pthreads;
thread->thread = pthread_self();
#if defined(PID_BASED_THREADS)
thread->pid = GetCurrentThreadId(); /* in LinuxThreadHelpers.cpp */
#endif
#if defined(DARWIN)
MachThreadHandle = mach_thread_self();
#endif
return thread;
}
static void *StartThread( void *pData )
{
ThreadImpl_Pthreads *pThis = (ThreadImpl_Pthreads *) pData;
#if defined(PID_BASED_THREADS)
pThis->pid = GetCurrentThreadId(); /* in LinuxThreadHelpers.cpp */
#endif
#if defined(DARWIN)
MachThreadHandle = mach_thread_self();
#endif
return (void *) pThis->m_pFunc( pThis->m_pData );
}
ThreadImpl *MakeThread( int (*pFunc)(void *pData), void *pData )
{
ThreadImpl_Win32 *thread = new ThreadImpl_Pthreads;
thread->m_pFunc = pFunc;
thread->m_pData = pData;
int ret = pthread_create( &thread->thread, NULL, StartThread, thread );
if( ret )
RageException::Throw( "pthread_create: %s", strerror(ret) );
/* XXX: don't return until StartThread sets pid, etc */
return thread;
}
MutexImpl_Pthreads::MutexImpl_Pthreads( RageMutex *pParent ):
MutexImpl( pParent )
{
pthread_mutex_init( &mutex, NULL );
LockedBy = GetInvalidThreadId();
LockCnt = 0;
}
MutexImpl_Pthreads::~MutexImpl_Pthreads()
{
int ret = pthread_mutex_destroy( &mutex ) == -1;
if( ret )
RageException::Throw( "Error deleting mutex: %s", strerror(ret) );
}
bool MutexImpl_Pthreads::Lock()
{
if( LockedBy == GetCurrentThreadId() )
{
++LockCnt;
return true;
}
#if defined(HAVE_PTHREAD_MUTEX_TIMEDLOCK) && defined(CRASH_HANDLER)
int len = 10; /* seconds */
int tries = 2;
while( tries-- )
{
/* Wait for ten seconds. If it takes longer than that, we're probably deadlocked. */
timeval tv;
gettimeofday( &tv, NULL );
timespec ts;
ts.tv_sec = tv.tv_sec + len;
ts.tv_nsec = tv.tv_usec * 1000;
int ret = pthread_mutex_timedlock( &mutex, &ts );
switch( ret )
{
case 0:
LockedBy = GetCurrentThreadId();
return true;
case ETIMEDOUT:
/* Timed out. Probably deadlocked. Try again one more time, with a smaller
* timeout, just in case we're debugging and happened to stop while waiting
* on the mutex. */
len = 1;
break;
default:
RageException::Throw( "pthread_mutex_timedlock: %s", strerror(ret) );
}
}
return false;
#else
int ret = pthread_mutex_lock( &mutex );
if( ret )
RageException::Throw( "pthread_mutex_lock failed: %s", strerror(ret) );
LockedBy = GetCurrentThreadId();
return true;
#endif
}
void MutexImpl_Pthreads::Unlock()
{
if( LockCnt )
{
--LockCnt;
return;
}
LockedBy = GetInvalidThreadId();
pthread_mutex_unlock( &mutex );
}
uint64_t MutexImpl_Pthreads::GetLockedByThreadId() const
{
return LockedBy;
}
uint64_t GetThisThreadId()
{
#if defined(PID_BASED_THREADS)
return GetCurrentThreadId();
#elif defined(DARWIN)
return (int) mach_thread_self();
#endif
}
uint64_t GetInvalidThreadId()
{
return 0;
}
MutexImpl *MakeMutex( RageMutex *pParent )
{
return new MutexImpl_Pthreads( pParent );
}
/*
* (c) 2001-2004 Glenn Maynard
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
@@ -0,0 +1,74 @@
#ifndef THREADS_WIN32_H
#define THREADS_WIN32_H
#include "Threads.h"
class ThreadImpl_Pthreads: public ThreadImpl
{
public:
// HANDLE ThreadHandle;
// DWORD ThreadId;
pthread_t thread;
#if defined(PID_BASED_THREADS)
/* Keep a list of child PIDs, so we can send them SIGKILL. This has an
* added bonus: if this is corrupted, we'll just send signals and they'll
* fail; we won't blow up (unless we're root). */
int pid;
#endif
#if defined(DARWIN)
thread_act_t MachThreadHandle;
#endif
int (*m_pFunc)( void *pData );
void *m_pData;
void Halt( bool Kill );
void Resume();
uint64_t GetThreadId() const;
int Wait();
};
struct MutexImpl_Pthreads: public MutexImpl
{
uint64_t LockedBy;
volatile int LockCnt;
pthread_mutex_t mutex;
MutexImpl_Pthreads( RageMutex *parent );
~MutexImpl_Pthreads();
bool Lock();
void Unlock();
uint64_t GetLockedByThreadId() const;
};
#endif
/*
* (c) 2001-2004 Glenn Maynard
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
@@ -0,0 +1,201 @@
#include "global.h"
#include "Threads_Win32.h"
#include "RageUtil.h"
#include "RageThreads.h"
void ThreadImpl_Win32::Halt( bool Kill )
{
#ifndef _XBOX
if( Kill )
TerminateThread( ThreadHandle, 0 );
else
#endif
SuspendThread( ThreadHandle );
}
void ThreadImpl_Win32::Resume()
{
ResumeThread( ThreadHandle );
}
uint64_t ThreadImpl_Win32::GetThreadId() const
{
return (uint64_t) ThreadId;
}
uint64_t ThreadImpl_Win32::GetCrashHandle() const
{
return (uint64_t) ThreadHandle;
}
int ThreadImpl_Win32::Wait()
{
WaitForSingleObject( ThreadHandle, INFINITE );
DWORD ret;
GetExitCodeThread( ThreadHandle, &ret );
return ret;
}
static DWORD WINAPI StartThread( LPVOID pData )
{
ThreadImpl_Win32 *pThis = (ThreadImpl_Win32 *) pData;
return (DWORD)pThis->m_pFunc( pThis->m_pData );
}
ThreadImpl *MakeThisThread()
{
ThreadImpl_Win32 *thread = new ThreadImpl_Win32;
const HANDLE CurProc = GetCurrentProcess();
int ret = DuplicateHandle( CurProc, GetCurrentThread(), CurProc,
&thread->ThreadHandle, 0, false, DUPLICATE_SAME_ACCESS );
if( !ret )
{
// LOG->Warn( werr_ssprintf( GetLastError(), "DuplicateHandle(%p, %p) failed",
// CurProc, GetCurrentThread() ) );
thread->ThreadHandle = NULL;
}
thread->ThreadId = GetCurrentThreadId();
return thread;
}
ThreadImpl *MakeThread( int (*pFunc)(void *pData), void *pData )
{
ThreadImpl_Win32 *thread = new ThreadImpl_Win32;
thread->m_pFunc = pFunc;
thread->m_pData = pData;
/* XXX: does ThreadHandle need to be dup'd? */
thread->ThreadHandle = CreateThread( NULL, 0, &StartThread, thread, 0, &thread->ThreadId );
ASSERT_M( thread->ThreadHandle, ssprintf("%s", werr_ssprintf(GetLastError(), "CreateThread")) );
return thread;
}
MutexImpl_Win32::MutexImpl_Win32( RageMutex *pParent ):
MutexImpl( pParent )
{
mutex = CreateMutex( NULL, false, NULL );
ASSERT_M( mutex != NULL, werr_ssprintf(GetLastError(), "CreateMutex") );
LockedBy = GetInvalidThreadId();
LockCnt = 0;
}
MutexImpl_Win32::~MutexImpl_Win32()
{
CloseHandle( mutex );
}
bool MutexImpl_Win32::Lock()
{
if( LockedBy == GetCurrentThreadId() )
{
++LockCnt;
return true;
}
int len = 15000;
int tries = 2;
while( tries-- )
{
/* Wait for fifteen seconds. If it takes longer than that, we're probably deadlocked. */
DWORD ret = WaitForSingleObject( mutex, len );
switch( ret )
{
case WAIT_ABANDONED:
/* The docs aren't particular about what this does, but it should never happen. */
ASSERT( 0 );
break;
case WAIT_OBJECT_0:
LockedBy = GetCurrentThreadId();
return true;
case WAIT_TIMEOUT:
/* Timed out. Probably deadlocked. Try again one more time, with a smaller
* timeout, just in case we're debugging and happened to stop while waiting
* on the mutex. */
len = 1000;
break;
case WAIT_FAILED:
FAIL_M( werr_ssprintf(GetLastError(), "WaitForSingleObject(%s)", this->m_Parent->GetName().c_str()) );
}
}
return false;
}
void MutexImpl_Win32::Unlock()
{
if( LockCnt )
{
--LockCnt;
return;
}
LockedBy = GetInvalidThreadId();
const bool ret = !!ReleaseMutex( mutex );
/* We can't ASSERT here, since this is called from checkpoints, which is
* called from ASSERT. */
if( !ret )
sm_crash( werr_ssprintf( GetLastError(), "ReleaseMutex failed" ) );
}
uint64_t MutexImpl_Win32::GetLockedByThreadId() const
{
return LockedBy;
}
uint64_t GetThisThreadId()
{
return GetCurrentThreadId();
}
uint64_t GetInvalidThreadId()
{
return 0;
}
MutexImpl *MakeMutex( RageMutex *pParent )
{
return new MutexImpl_Win32( pParent );
}
/*
* (c) 2001-2004 Glenn Maynard
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
@@ -0,0 +1,63 @@
#ifndef THREADS_WIN32_H
#define THREADS_WIN32_H
#include "Threads.h"
#include <windows.h>
class ThreadImpl_Win32: public ThreadImpl
{
public:
HANDLE ThreadHandle;
DWORD ThreadId;
int (*m_pFunc)( void *pData );
void *m_pData;
void Halt( bool Kill );
void Resume();
uint64_t GetThreadId() const;
uint64_t GetCrashHandle() const;
int Wait();
};
struct MutexImpl_Win32: public MutexImpl
{
uint64_t LockedBy;
volatile int LockCnt;
HANDLE mutex;
MutexImpl_Win32( RageMutex *parent );
~MutexImpl_Win32();
bool Lock();
void Unlock();
uint64_t GetLockedByThreadId() const;
};
#endif
/*
* (c) 2001-2004 Glenn Maynard
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/