1
0
mirror of https://github.com/stefanocasazza/ULib.git synced 2025-09-28 19:05:55 +08:00
ULib/include/ulib/thread.h
stefanocasazza 61ad7266fc thread fix
2015-08-27 16:26:09 +02:00

397 lines
9.4 KiB
C++

// ============================================================================
//
// = LIBRARY
// ULib - c++ library
//
// = FILENAME
// thread.h
//
// = AUTHOR
// Stefano Casazza
//
// ============================================================================
#ifndef ULIB_THREAD_H
#define ULIB_THREAD_H
#include <ulib/timeval.h>
#ifdef _MSWINDOWS_
# undef sleep
# undef signal
# define PTHREAD_CREATE_DETACHED 1
#else
# define U_SIGSTOP (SIGRTMIN+5)
# define U_SIGCONT (SIGRTMIN+6)
#endif
class UNotifier;
class U_EXPORT UThread {
public:
// Check for memory error
U_MEMORY_TEST
// Allocator e Deallocator
U_MEMORY_ALLOCATOR
U_MEMORY_DEALLOCATOR
// COSTRUTTORI
UThread(int _detachstate);
virtual ~UThread()
{
U_TRACE_UNREGISTER_OBJECT(0, UThread)
if (tid) close();
}
// SERVICES
#ifdef _MSWINDOWS_
static DWORD getTID();
#else
static pthread_t getTID();
#endif
// Inter Process Communication
static void lock(pthread_mutex_t* pmutex)
{
U_TRACE(1, "UThread::lock(%p)", pmutex)
# ifndef _MSWINDOWS_
(void) U_SYSCALL(pthread_mutex_lock, "%p", pmutex);
# endif
}
static void unlock(pthread_mutex_t* pmutex)
{
U_TRACE(1, "UThread::unlock(%p)", pmutex)
# ifndef _MSWINDOWS_
(void) U_SYSCALL(pthread_mutex_unlock, "%p", pmutex);
# endif
}
#ifndef _MSWINDOWS_
static bool initRwLock(pthread_rwlock_t* prwlock)
{
U_TRACE(1, "UThread::initRwLock(%p)", prwlock)
pthread_rwlockattr_t rwlockattr;
if (U_SYSCALL(pthread_rwlockattr_init, "%p", &rwlockattr) != 0 ||
U_SYSCALL(pthread_rwlockattr_setpshared, "%p,%d", &rwlockattr, PTHREAD_PROCESS_SHARED) != 0 ||
U_SYSCALL(pthread_rwlock_init, "%p,%p", prwlock, &rwlockattr) != 0)
{
U_RETURN(false);
}
U_RETURN(true);
}
static bool initIPC(pthread_mutex_t* mutex, pthread_cond_t* cond);
static void doIPC(pthread_mutex_t* mutex, pthread_cond_t* cond, vPF function, bool wait);
#endif
/**
* When a new thread is created, it does not begin immediate execution. This is because the derived class virtual tables are not properly loaded
* at the time the C++ object is created within the constructor itself, at least in some compiler/system combinations. It can be started directly
* after the constructor completes by calling the start() method
*
* @return false if execution fails
*/
bool start(uint32_t timeoutMS = 0);
/**
* All threads execute by deriving the run method of UThread. This method is called after initial to begin normal operation of the
* thread. If the method terminates, then the thread will also terminate
*/
virtual void run()
{
U_TRACE(0, "UThread::run()")
}
/**
* Check if this thread is detached
*
* @return true if the thread is detached
*/
bool isDetached()
{
U_TRACE(0, "UThread::isDetached()")
U_INTERNAL_DUMP("detachstate = %d", detachstate)
if (detachstate == PTHREAD_CREATE_DETACHED) U_RETURN(true);
U_RETURN(false);
}
void sleep(time_t timeoutMS);
static void nanosleep(time_t timeoutMS)
{
U_TRACE(0, "UThread::nanosleep(%ld)", timeoutMS)
UThread* th = getThread();
if (th)
{
th->sleep(timeoutMS);
return;
}
UTimeVal(timeoutMS / 1000L, (timeoutMS % 1000L) * 1000L).nanosleep();
}
/**
* Yields the current thread's CPU time slice to allow another thread to begin immediate execution
*/
void yield();
/**
* Suspends execution of the selected thread. Pthreads do not normally support suspendable threads, so the behavior is simulated with signals.
* You can't kill or stop just one thread from another process. You can send a signal to a particular thread, but the stop/abort action that
* is taken by the signal affects the whole process. In the earlier implementation of Linux threads, it was possible to stop a single thread
* with SIGSTOP, but this behaviour has now been fixed to conform to the Posix standard (so it stops all threads in the process)
*/
void resume()
{
U_TRACE(0, "UThread::resume()")
# ifndef _MSWINDOWS_
resume(tid);
# ifndef HAVE_PTHREAD_SUSPEND
yield(); // give the signal a time to kick in
# endif
# endif
}
void suspend()
{
U_TRACE(0, "UThread::suspend()")
# ifndef _MSWINDOWS_
suspend(tid);
# ifndef HAVE_PTHREAD_SUSPEND
yield(); // give the signal a time to kick in
# endif
# endif
}
// Cancellation
enum Cancel {
cancelInitial, /* used internally, do not use */
cancelDeferred, /* exit thread on cancellation pointsuch as yield */
cancelImmediate, /* exit befor cancellation */
cancelDisabled, /* ignore cancellation */
cancelManual
};
void setCancel(int mode);
/**
* This is used to help build wrapper functions in libraries
* around system calls that should behave as cancellation points but don't
*
* @return saved cancel type
*/
int enterCancel();
/**
* This is used to restore a cancel block
*
* @param cancel type that was saved
*/
void exitCancel(int cancel);
// A special global function, getThread(), is provided to identify the thread object that represents the current
// execution context you are running under. This is sometimes needed to deliver signals to the correct thread
static UThread* getThread() __pure
{
U_TRACE(1, "UThread::getThread()")
U_INTERNAL_DUMP("first = %p", first)
# ifdef _MSWINDOWS_
DWORD _tid = GetCurrentThreadId();
# else
pthread_t _tid = (pthread_t) U_SYSCALL_NO_PARAM(pthread_self);
# endif
for (UThread* obj = first; obj; obj = obj->next)
{
# ifdef _MSWINDOWS_
if (_tid == obj->tid) U_RETURN_POINTER(obj, UThread);
# else
if (pthread_equal(_tid, obj->tid)) U_RETURN_POINTER(obj, UThread);
# endif
}
U_RETURN_POINTER(0, UThread);
}
static bool isCurrentThread(pthread_t _tid)
{
U_TRACE(1, "UThread::isCurrentThread(%p)", _tid)
U_INTERNAL_ASSERT_POINTER(_tid)
# ifdef _MSWINDOWS_
if (GetCurrentThreadId() == _tid) U_RETURN(true);
# else
if (pthread_equal((pthread_t)U_SYSCALL_NO_PARAM(pthread_self), _tid)) U_RETURN(true);
# endif
U_RETURN(false);
}
#if defined(U_STDCPP_ENABLE) && defined(DEBUG)
const char* dump(bool reset) const;
#endif
protected:
UThread* next;
int detachstate, cancel;
#ifdef _MSWINDOWS_
DWORD tid;
HANDLE cancellation;
#else
pthread_t tid;
pthread_attr_t attr;
int suspendCount;
static pthread_mutex_t mlock;
#endif
static UThread* first;
void close();
#ifdef _MSWINDOWS_
static void lock() {}
static void unlock() {}
static void resume(pthread_t _tid) {}
static void suspend(pthread_t _tid) {}
static unsigned __stdcall execHandler(void* th);
#else
void sigInstall(int signo);
void manageSignal(int signo);
static void lock() { lock(&mlock); }
static void unlock() { unlock(&mlock); }
static void execHandler(UThread* th);
static void sigHandler(int signo)
{
U_TRACE(0, "UThread::sigHandler(%d)", signo)
UThread* th = getThread();
if (th) th->manageSignal(signo);
}
static bool isDetached(pthread_attr_t* pattr)
{
U_TRACE(1, "UThread::isDetached(%p)", pattr)
int state;
(void) U_SYSCALL(pthread_attr_getdetachstate, "%p,%p", pattr, &state);
if (state == PTHREAD_CREATE_DETACHED) U_RETURN(true);
U_RETURN(false);
}
static void stop(pthread_t _tid, pthread_attr_t* pattr)
{
U_TRACE(1, "UThread::stop(%p,%p)", _tid, pattr)
# ifdef HAVE_PTHREAD_CANCEL
(void) U_SYSCALL(pthread_cancel, "%p", _tid);
# endif
if (isDetached(pattr) == false) (void) U_SYSCALL(pthread_join, "%p,%p", _tid, 0);
# ifdef HAVE_PTHREAD_YIELD
else (void) U_SYSCALL_NO_PARAM(pthread_yield);
# endif
}
static void suspend(pthread_t _tid)
{
U_TRACE(1, "UThread::suspend(%p)", _tid)
U_ASSERT_EQUALS(isCurrentThread(_tid), false)
# ifdef HAVE_PTHREAD_SUSPEND
(void) U_SYSCALL(pthread_suspend, "%p", _tid);
# else
(void) U_SYSCALL(pthread_kill, "%p,%d", _tid, U_SIGSTOP);
# endif
}
static void resume(pthread_t _tid)
{
U_TRACE(1, "UThread::resume(%p)", _tid)
U_ASSERT_EQUALS(isCurrentThread(_tid), false)
# ifdef HAVE_PTHREAD_SUSPEND
(void) U_SYSCALL(pthread_resume, "%p", _tid);
# else
(void) U_SYSCALL(pthread_kill, "%p,%d", _tid, U_SIGCONT);
# endif
}
static void threadCleanup(void* th)
{
U_TRACE(0, "UThread::threadCleanup(%p)", th)
U_INTERNAL_ASSERT_POINTER(th)
U_INTERNAL_DUMP("th->tid = %p", ((UThread*)th)->tid)
if (((UThread*)th)->tid) ((UThread*)th)->close();
}
#endif
private:
friend class UNotifier;
#ifdef U_COMPILER_DELETE_MEMBERS
UThread(const UThread&) = delete;
UThread& operator=(const UThread&) = delete;
#else
UThread(const UThread&) {}
UThread& operator=(const UThread&) { return *this; }
#endif
};
class U_EXPORT UThreadPool {
public:
// Check for memory error
U_MEMORY_TEST
// Allocator e Deallocator
U_MEMORY_ALLOCATOR
U_MEMORY_DEALLOCATOR
};
#endif