libbpg/x265/source/common/threading.h
2015-10-27 11:46:00 +01:00

491 lines
12 KiB
C++

/*****************************************************************************
* Copyright (C) 2013 x265 project
*
* Authors: Steve Borho <steve@borho.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111, USA.
*
* This program is also available under a commercial proprietary license.
* For more information, contact us at license @ x265.com
*****************************************************************************/
#ifndef X265_THREADING_H
#define X265_THREADING_H
#include "common.h"
#include "x265.h"
#ifdef _WIN32
#include <windows.h>
#include "winxp.h" // XP workarounds for CONDITION_VARIABLE and ATOMIC_OR
#else
#include <pthread.h>
#include <semaphore.h>
#include <errno.h>
#include <fcntl.h>
#endif
#if MACOS
#include <sys/param.h>
#include <sys/sysctl.h>
#endif
#if NO_ATOMICS
#include <sys/time.h>
#include <unistd.h>
namespace X265_NS {
// x265 private namespace
int no_atomic_or(int* ptr, int mask);
int no_atomic_and(int* ptr, int mask);
int no_atomic_inc(int* ptr);
int no_atomic_dec(int* ptr);
int no_atomic_add(int* ptr, int val);
}
#define CLZ(id, x) id = (unsigned long)__builtin_clz(x) ^ 31
#define CTZ(id, x) id = (unsigned long)__builtin_ctz(x)
#define ATOMIC_OR(ptr, mask) no_atomic_or((int*)ptr, mask)
#define ATOMIC_AND(ptr, mask) no_atomic_and((int*)ptr, mask)
#define ATOMIC_INC(ptr) no_atomic_inc((int*)ptr)
#define ATOMIC_DEC(ptr) no_atomic_dec((int*)ptr)
#define ATOMIC_ADD(ptr, val) no_atomic_add((int*)ptr, val)
#define GIVE_UP_TIME() usleep(0)
#elif __GNUC__ /* GCCs builtin atomics */
#include <sys/time.h>
#include <unistd.h>
#define CLZ(id, x) id = (unsigned long)__builtin_clz(x) ^ 31
#define CTZ(id, x) id = (unsigned long)__builtin_ctz(x)
#define ATOMIC_OR(ptr, mask) __sync_fetch_and_or(ptr, mask)
#define ATOMIC_AND(ptr, mask) __sync_fetch_and_and(ptr, mask)
#define ATOMIC_INC(ptr) __sync_add_and_fetch((volatile int32_t*)ptr, 1)
#define ATOMIC_DEC(ptr) __sync_add_and_fetch((volatile int32_t*)ptr, -1)
#define ATOMIC_ADD(ptr, val) __sync_fetch_and_add((volatile int32_t*)ptr, val)
#define GIVE_UP_TIME() usleep(0)
#elif defined(_MSC_VER) /* Windows atomic intrinsics */
#include <intrin.h>
#define CLZ(id, x) _BitScanReverse(&id, x)
#define CTZ(id, x) _BitScanForward(&id, x)
#define ATOMIC_INC(ptr) InterlockedIncrement((volatile LONG*)ptr)
#define ATOMIC_DEC(ptr) InterlockedDecrement((volatile LONG*)ptr)
#define ATOMIC_ADD(ptr, val) InterlockedExchangeAdd((volatile LONG*)ptr, val)
#define ATOMIC_OR(ptr, mask) _InterlockedOr((volatile LONG*)ptr, (LONG)mask)
#define ATOMIC_AND(ptr, mask) _InterlockedAnd((volatile LONG*)ptr, (LONG)mask)
#define GIVE_UP_TIME() Sleep(0)
#endif // ifdef __GNUC__
namespace X265_NS {
// x265 private namespace
#ifdef _WIN32
typedef HANDLE ThreadHandle;
class Lock
{
public:
Lock()
{
InitializeCriticalSection(&this->handle);
}
~Lock()
{
DeleteCriticalSection(&this->handle);
}
void acquire()
{
EnterCriticalSection(&this->handle);
}
void release()
{
LeaveCriticalSection(&this->handle);
}
protected:
CRITICAL_SECTION handle;
};
class Event
{
public:
Event()
{
this->handle = CreateEvent(NULL, FALSE, FALSE, NULL);
}
~Event()
{
CloseHandle(this->handle);
}
void wait()
{
WaitForSingleObject(this->handle, INFINITE);
}
bool timedWait(uint32_t milliseconds)
{
/* returns true if the wait timed out */
return WaitForSingleObject(this->handle, milliseconds) == WAIT_TIMEOUT;
}
void trigger()
{
SetEvent(this->handle);
}
protected:
HANDLE handle;
};
/* This class is intended for use in signaling state changes safely between CPU
* cores. One thread should be a writer and multiple threads may be readers. The
* mutex's main purpose is to serve as a memory fence to ensure writes made by
* the writer thread are visible prior to readers seeing the m_val change. Its
* secondary purpose is for use with the condition variable for blocking waits */
class ThreadSafeInteger
{
public:
ThreadSafeInteger()
{
m_val = 0;
InitializeCriticalSection(&m_cs);
InitializeConditionVariable(&m_cv);
}
~ThreadSafeInteger()
{
DeleteCriticalSection(&m_cs);
XP_CONDITION_VAR_FREE(&m_cv);
}
int waitForChange(int prev)
{
EnterCriticalSection(&m_cs);
if (m_val == prev)
SleepConditionVariableCS(&m_cv, &m_cs, INFINITE);
LeaveCriticalSection(&m_cs);
return m_val;
}
int get()
{
EnterCriticalSection(&m_cs);
int ret = m_val;
LeaveCriticalSection(&m_cs);
return ret;
}
void set(int newval)
{
EnterCriticalSection(&m_cs);
m_val = newval;
WakeAllConditionVariable(&m_cv);
LeaveCriticalSection(&m_cs);
}
void poke(void)
{
/* awaken all waiting threads, but make no change */
EnterCriticalSection(&m_cs);
WakeAllConditionVariable(&m_cv);
LeaveCriticalSection(&m_cs);
}
void incr()
{
EnterCriticalSection(&m_cs);
m_val++;
WakeAllConditionVariable(&m_cv);
LeaveCriticalSection(&m_cs);
}
protected:
CRITICAL_SECTION m_cs;
CONDITION_VARIABLE m_cv;
int m_val;
};
#else /* POSIX / pthreads */
typedef pthread_t ThreadHandle;
class Lock
{
public:
Lock()
{
pthread_mutex_init(&this->handle, NULL);
}
~Lock()
{
pthread_mutex_destroy(&this->handle);
}
void acquire()
{
pthread_mutex_lock(&this->handle);
}
void release()
{
pthread_mutex_unlock(&this->handle);
}
protected:
pthread_mutex_t handle;
};
class Event
{
public:
Event()
{
m_counter = 0;
if (pthread_mutex_init(&m_mutex, NULL) ||
pthread_cond_init(&m_cond, NULL))
{
x265_log(NULL, X265_LOG_ERROR, "fatal: unable to initialize conditional variable\n");
}
}
~Event()
{
pthread_cond_destroy(&m_cond);
pthread_mutex_destroy(&m_mutex);
}
void wait()
{
pthread_mutex_lock(&m_mutex);
/* blocking wait on conditional variable, mutex is atomically released
* while blocked. When condition is signaled, mutex is re-acquired */
while (!m_counter)
pthread_cond_wait(&m_cond, &m_mutex);
m_counter--;
pthread_mutex_unlock(&m_mutex);
}
bool timedWait(uint32_t waitms)
{
bool bTimedOut = false;
pthread_mutex_lock(&m_mutex);
if (!m_counter)
{
struct timeval tv;
struct timespec ts;
gettimeofday(&tv, NULL);
/* convert current time from (sec, usec) to (sec, nsec) */
ts.tv_sec = tv.tv_sec;
ts.tv_nsec = tv.tv_usec * 1000;
ts.tv_nsec += 1000 * 1000 * (waitms % 1000); /* add ms to tv_nsec */
ts.tv_sec += ts.tv_nsec / (1000 * 1000 * 1000); /* overflow tv_nsec */
ts.tv_nsec %= (1000 * 1000 * 1000); /* clamp tv_nsec */
ts.tv_sec += waitms / 1000; /* add seconds */
/* blocking wait on conditional variable, mutex is atomically released
* while blocked. When condition is signaled, mutex is re-acquired.
* ts is absolute time to stop waiting */
bTimedOut = pthread_cond_timedwait(&m_cond, &m_mutex, &ts) == ETIMEDOUT;
}
if (m_counter > 0)
{
m_counter--;
bTimedOut = false;
}
pthread_mutex_unlock(&m_mutex);
return bTimedOut;
}
void trigger()
{
pthread_mutex_lock(&m_mutex);
if (m_counter < UINT_MAX)
m_counter++;
/* Signal a single blocking thread */
pthread_cond_signal(&m_cond);
pthread_mutex_unlock(&m_mutex);
}
protected:
pthread_mutex_t m_mutex;
pthread_cond_t m_cond;
uint32_t m_counter;
};
/* This class is intended for use in signaling state changes safely between CPU
* cores. One thread should be a writer and multiple threads may be readers. The
* mutex's main purpose is to serve as a memory fence to ensure writes made by
* the writer thread are visible prior to readers seeing the m_val change. Its
* secondary purpose is for use with the condition variable for blocking waits */
class ThreadSafeInteger
{
public:
ThreadSafeInteger()
{
m_val = 0;
if (pthread_mutex_init(&m_mutex, NULL) ||
pthread_cond_init(&m_cond, NULL))
{
x265_log(NULL, X265_LOG_ERROR, "fatal: unable to initialize conditional variable\n");
}
}
~ThreadSafeInteger()
{
pthread_cond_destroy(&m_cond);
pthread_mutex_destroy(&m_mutex);
}
int waitForChange(int prev)
{
pthread_mutex_lock(&m_mutex);
if (m_val == prev)
pthread_cond_wait(&m_cond, &m_mutex);
pthread_mutex_unlock(&m_mutex);
return m_val;
}
int get()
{
pthread_mutex_lock(&m_mutex);
int ret = m_val;
pthread_mutex_unlock(&m_mutex);
return ret;
}
void set(int newval)
{
pthread_mutex_lock(&m_mutex);
m_val = newval;
pthread_cond_broadcast(&m_cond);
pthread_mutex_unlock(&m_mutex);
}
void poke(void)
{
/* awaken all waiting threads, but make no change */
pthread_mutex_lock(&m_mutex);
pthread_cond_broadcast(&m_cond);
pthread_mutex_unlock(&m_mutex);
}
void incr()
{
pthread_mutex_lock(&m_mutex);
m_val++;
pthread_cond_broadcast(&m_cond);
pthread_mutex_unlock(&m_mutex);
}
protected:
pthread_mutex_t m_mutex;
pthread_cond_t m_cond;
int m_val;
};
#endif // ifdef _WIN32
class ScopedLock
{
public:
ScopedLock(Lock &instance) : inst(instance)
{
this->inst.acquire();
}
~ScopedLock()
{
this->inst.release();
}
protected:
// do not allow assignments
ScopedLock &operator =(const ScopedLock &);
Lock &inst;
};
// Utility class which adds elapsed time of the scope of the object into the
// accumulator provided to the constructor
struct ScopedElapsedTime
{
ScopedElapsedTime(int64_t& accum) : accumlatedTime(accum) { startTime = x265_mdate(); }
~ScopedElapsedTime() { accumlatedTime += x265_mdate() - startTime; }
protected:
int64_t startTime;
int64_t& accumlatedTime;
// do not allow assignments
ScopedElapsedTime &operator =(const ScopedElapsedTime &);
};
//< Simplistic portable thread class. Shutdown signalling left to derived class
class Thread
{
private:
ThreadHandle thread;
public:
Thread();
virtual ~Thread();
//< Derived class must implement ThreadMain.
virtual void threadMain() = 0;
//< Returns true if thread was successfully created
bool start();
void stop();
};
} // end namespace X265_NS
#endif // ifndef X265_THREADING_H