Files
librubberband/src/system/Thread.cpp
2018-03-07 13:10:19 +00:00

664 lines
14 KiB
C++

/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
/*
Rubber Band Library
An audio time-stretching and pitch-shifting library.
Copyright 2007-2018 Particular Programs Ltd.
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. See the file
COPYING included with this distribution for more information.
Alternatively, if you have a valid commercial licence for the
Rubber Band Library obtained by agreement with the copyright
holders, you may redistribute and/or modify it under the terms
described in that licence.
If you wish to distribute code using the Rubber Band Library
under terms other than those of the GNU General Public License,
you must obtain a valid commercial licence before doing so.
*/
#ifndef NO_THREADING
#include "Thread.h"
#include <iostream>
#include <cstdlib>
#ifdef USE_PTHREADS
#include <sys/time.h>
#include <time.h>
#endif
using std::cerr;
using std::endl;
using std::string;
namespace RubberBand
{
#ifdef _WIN32
Thread::Thread() :
m_id(0),
m_extant(false)
{
#ifdef DEBUG_THREAD
cerr << "THREAD DEBUG: Created thread object " << this << endl;
#endif
}
Thread::~Thread()
{
#ifdef DEBUG_THREAD
cerr << "THREAD DEBUG: Destroying thread object " << this << ", id " << m_id << endl;
#endif
if (m_extant) {
WaitForSingleObject(m_id, INFINITE);
}
#ifdef DEBUG_THREAD
cerr << "THREAD DEBUG: Destroyed thread object " << this << endl;
#endif
}
void
Thread::start()
{
m_id = CreateThread(NULL, 0, staticRun, this, 0, 0);
if (!m_id) {
cerr << "ERROR: thread creation failed" << endl;
exit(1);
} else {
#ifdef DEBUG_THREAD
cerr << "THREAD DEBUG: Created thread " << m_id << " for thread object " << this << endl;
#endif
m_extant = true;
}
}
void
Thread::wait()
{
if (m_extant) {
#ifdef DEBUG_THREAD
cerr << "THREAD DEBUG: Waiting on thread " << m_id << " for thread object " << this << endl;
#endif
WaitForSingleObject(m_id, INFINITE);
#ifdef DEBUG_THREAD
cerr << "THREAD DEBUG: Waited on thread " << m_id << " for thread object " << this << endl;
#endif
m_extant = false;
}
}
Thread::Id
Thread::id()
{
return m_id;
}
bool
Thread::threadingAvailable()
{
return true;
}
DWORD
Thread::staticRun(LPVOID arg)
{
Thread *thread = static_cast<Thread *>(arg);
#ifdef DEBUG_THREAD
cerr << "THREAD DEBUG: " << (void *)GetCurrentThreadId() << ": Running thread " << thread->m_id << " for thread object " << thread << endl;
#endif
thread->run();
return 0;
}
Mutex::Mutex()
#ifndef NO_THREAD_CHECKS
:
m_lockedBy(-1)
#endif
{
m_mutex = CreateMutex(NULL, FALSE, NULL);
#ifdef DEBUG_MUTEX
cerr << "MUTEX DEBUG: " << (void *)GetCurrentThreadId() << ": Initialised mutex " << &m_mutex << endl;
#endif
}
Mutex::~Mutex()
{
#ifdef DEBUG_MUTEX
cerr << "MUTEX DEBUG: " << (void *)GetCurrentThreadId() << ": Destroying mutex " << &m_mutex << endl;
#endif
CloseHandle(m_mutex);
}
void
Mutex::lock()
{
#ifndef NO_THREAD_CHECKS
DWORD tid = GetCurrentThreadId();
if (m_lockedBy == tid) {
cerr << "ERROR: Deadlock on mutex " << &m_mutex << endl;
}
#endif
#ifdef DEBUG_MUTEX
cerr << "MUTEX DEBUG: " << (void *)tid << ": Want to lock mutex " << &m_mutex << endl;
#endif
WaitForSingleObject(m_mutex, INFINITE);
#ifndef NO_THREAD_CHECKS
m_lockedBy = tid;
#endif
#ifdef DEBUG_MUTEX
cerr << "MUTEX DEBUG: " << (void *)tid << ": Locked mutex " << &m_mutex << endl;
#endif
}
void
Mutex::unlock()
{
#ifndef NO_THREAD_CHECKS
DWORD tid = GetCurrentThreadId();
if (m_lockedBy != tid) {
cerr << "ERROR: Mutex " << &m_mutex << " not owned by unlocking thread" << endl;
return;
}
#endif
#ifdef DEBUG_MUTEX
cerr << "MUTEX DEBUG: " << (void *)tid << ": Unlocking mutex " << &m_mutex << endl;
#endif
#ifndef NO_THREAD_CHECKS
m_lockedBy = -1;
#endif
ReleaseMutex(m_mutex);
}
bool
Mutex::trylock()
{
#ifndef NO_THREAD_CHECKS
DWORD tid = GetCurrentThreadId();
#endif
DWORD result = WaitForSingleObject(m_mutex, 0);
if (result == WAIT_TIMEOUT || result == WAIT_FAILED) {
#ifdef DEBUG_MUTEX
cerr << "MUTEX DEBUG: " << (void *)tid << ": Mutex " << &m_mutex << " unavailable" << endl;
#endif
return false;
} else {
#ifndef NO_THREAD_CHECKS
m_lockedBy = tid;
#endif
#ifdef DEBUG_MUTEX
cerr << "MUTEX DEBUG: " << (void *)tid << ": Locked mutex " << &m_mutex << " (from trylock)" << endl;
#endif
return true;
}
}
Condition::Condition(string name) :
m_locked(false)
#ifdef DEBUG_CONDITION
, m_name(name)
#endif
{
m_mutex = CreateMutex(NULL, FALSE, NULL);
m_condition = CreateEvent(NULL, FALSE, FALSE, NULL);
#ifdef DEBUG_CONDITION
cerr << "CONDITION DEBUG: " << (void *)GetCurrentThreadId() << ": Initialised condition " << &m_condition << " \"" << m_name << "\"" << endl;
#endif
}
Condition::~Condition()
{
#ifdef DEBUG_CONDITION
cerr << "CONDITION DEBUG: " << (void *)GetCurrentThreadId() << ": Destroying condition " << &m_condition << " \"" << m_name << "\"" << endl;
#endif
if (m_locked) ReleaseMutex(m_mutex);
CloseHandle(m_condition);
CloseHandle(m_mutex);
}
void
Condition::lock()
{
#ifdef DEBUG_CONDITION
cerr << "CONDITION DEBUG: " << (void *)GetCurrentThreadId() << ": Want to lock " << &m_condition << " \"" << m_name << "\"" << endl;
#endif
WaitForSingleObject(m_mutex, INFINITE);
m_locked = true;
#ifdef DEBUG_CONDITION
cerr << "CONDITION DEBUG: " << (void *)GetCurrentThreadId() << ": Locked " << &m_condition << " \"" << m_name << "\"" << endl;
#endif
}
void
Condition::unlock()
{
if (!m_locked) {
#ifdef DEBUG_CONDITION
cerr << "CONDITION DEBUG: " << (void *)GetCurrentThreadId() << ": Not locked " << &m_condition << " \"" << m_name << "\"" << endl;
#endif
return;
}
#ifdef DEBUG_CONDITION
cerr << "CONDITION DEBUG: " << (void *)GetCurrentThreadId() << ": Unlocking " << &m_condition << " \"" << m_name << "\"" << endl;
#endif
m_locked = false;
ReleaseMutex(m_mutex);
}
void
Condition::wait(int us)
{
if (us == 0) {
#ifdef DEBUG_CONDITION
cerr << "CONDITION DEBUG: " << (void *)GetCurrentThreadId() << ": Waiting on " << &m_condition << " \"" << m_name << "\"" << endl;
#endif
SignalObjectAndWait(m_mutex, m_condition, INFINITE, FALSE);
WaitForSingleObject(m_mutex, INFINITE);
} else {
DWORD ms = us / 1000;
if (us > 0 && ms == 0) ms = 1;
#ifdef DEBUG_CONDITION
cerr << "CONDITION DEBUG: " << (void *)GetCurrentThreadId() << ": Timed waiting on " << &m_condition << " \"" << m_name << "\"" << endl;
#endif
SignalObjectAndWait(m_mutex, m_condition, ms, FALSE);
WaitForSingleObject(m_mutex, INFINITE);
}
#ifdef DEBUG_CONDITION
cerr << "CONDITION DEBUG: " << (void *)GetCurrentThreadId() << ": Wait done on " << &m_condition << " \"" << m_name << "\"" << endl;
#endif
m_locked = true;
}
void
Condition::signal()
{
#ifdef DEBUG_CONDITION
cerr << "CONDITION DEBUG: " << (void *)GetCurrentThreadId() << ": Signalling " << &m_condition << " \"" << m_name << "\"" << endl;
#endif
SetEvent(m_condition);
}
#else /* !_WIN32 */
#ifdef USE_PTHREADS
Thread::Thread() :
m_id(0),
m_extant(false)
{
#ifdef DEBUG_THREAD
cerr << "THREAD DEBUG: Created thread object " << this << endl;
#endif
}
Thread::~Thread()
{
#ifdef DEBUG_THREAD
cerr << "THREAD DEBUG: Destroying thread object " << this << ", id " << m_id << endl;
#endif
if (m_extant) {
pthread_join(m_id, 0);
}
#ifdef DEBUG_THREAD
cerr << "THREAD DEBUG: Destroyed thread object " << this << endl;
#endif
}
void
Thread::start()
{
if (pthread_create(&m_id, 0, staticRun, this)) {
cerr << "ERROR: thread creation failed" << endl;
exit(1);
} else {
#ifdef DEBUG_THREAD
cerr << "THREAD DEBUG: Created thread " << m_id << " for thread object " << this << endl;
#endif
m_extant = true;
}
}
void
Thread::wait()
{
if (m_extant) {
#ifdef DEBUG_THREAD
cerr << "THREAD DEBUG: Waiting on thread " << m_id << " for thread object " << this << endl;
#endif
pthread_join(m_id, 0);
#ifdef DEBUG_THREAD
cerr << "THREAD DEBUG: Waited on thread " << m_id << " for thread object " << this << endl;
#endif
m_extant = false;
}
}
Thread::Id
Thread::id()
{
return m_id;
}
bool
Thread::threadingAvailable()
{
return true;
}
void *
Thread::staticRun(void *arg)
{
Thread *thread = static_cast<Thread *>(arg);
#ifdef DEBUG_THREAD
cerr << "THREAD DEBUG: " << (void *)pthread_self() << ": Running thread " << thread->m_id << " for thread object " << thread << endl;
#endif
thread->run();
return 0;
}
Mutex::Mutex()
#ifndef NO_THREAD_CHECKS
:
m_lockedBy(0),
m_locked(false)
#endif
{
pthread_mutex_init(&m_mutex, 0);
#ifdef DEBUG_MUTEX
cerr << "MUTEX DEBUG: " << (void *)pthread_self() << ": Initialised mutex " << &m_mutex << endl;
#endif
}
Mutex::~Mutex()
{
#ifdef DEBUG_MUTEX
cerr << "MUTEX DEBUG: " << (void *)pthread_self() << ": Destroying mutex " << &m_mutex << endl;
#endif
pthread_mutex_destroy(&m_mutex);
}
void
Mutex::lock()
{
#ifndef NO_THREAD_CHECKS
pthread_t tid = pthread_self();
if (m_locked && m_lockedBy == tid) {
cerr << "ERROR: Deadlock on mutex " << &m_mutex << endl;
}
#endif
#ifdef DEBUG_MUTEX
cerr << "MUTEX DEBUG: " << (void *)tid << ": Want to lock mutex " << &m_mutex << endl;
#endif
pthread_mutex_lock(&m_mutex);
#ifndef NO_THREAD_CHECKS
m_lockedBy = tid;
m_locked = true;
#endif
#ifdef DEBUG_MUTEX
cerr << "MUTEX DEBUG: " << (void *)tid << ": Locked mutex " << &m_mutex << endl;
#endif
}
void
Mutex::unlock()
{
#ifndef NO_THREAD_CHECKS
pthread_t tid = pthread_self();
if (!m_locked) {
cerr << "ERROR: Mutex " << &m_mutex << " not locked in unlock" << endl;
return;
} else if (m_lockedBy != tid) {
cerr << "ERROR: Mutex " << &m_mutex << " not owned by unlocking thread" << endl;
return;
}
#endif
#ifdef DEBUG_MUTEX
cerr << "MUTEX DEBUG: " << (void *)tid << ": Unlocking mutex " << &m_mutex << endl;
#endif
#ifndef NO_THREAD_CHECKS
m_locked = false;
#endif
pthread_mutex_unlock(&m_mutex);
}
bool
Mutex::trylock()
{
#ifndef NO_THREAD_CHECKS
pthread_t tid = pthread_self();
#endif
if (pthread_mutex_trylock(&m_mutex)) {
#ifdef DEBUG_MUTEX
cerr << "MUTEX DEBUG: " << (void *)tid << ": Mutex " << &m_mutex << " unavailable" << endl;
#endif
return false;
} else {
#ifndef NO_THREAD_CHECKS
m_lockedBy = tid;
m_locked = true;
#endif
#ifdef DEBUG_MUTEX
cerr << "MUTEX DEBUG: " << (void *)tid << ": Locked mutex " << &m_mutex << " (from trylock)" << endl;
#endif
return true;
}
}
Condition::Condition(string name) :
m_locked(false)
#ifdef DEBUG_CONDITION
, m_name(name)
#endif
{
pthread_mutex_init(&m_mutex, 0);
pthread_cond_init(&m_condition, 0);
#ifdef DEBUG_CONDITION
cerr << "CONDITION DEBUG: " << (void *)pthread_self() << ": Initialised condition " << &m_condition << " \"" << m_name << "\"" << endl;
#endif
}
Condition::~Condition()
{
#ifdef DEBUG_CONDITION
cerr << "CONDITION DEBUG: " << (void *)pthread_self() << ": Destroying condition " << &m_condition << " \"" << m_name << "\"" << endl;
#endif
if (m_locked) pthread_mutex_unlock(&m_mutex);
pthread_cond_destroy(&m_condition);
pthread_mutex_destroy(&m_mutex);
}
void
Condition::lock()
{
#ifdef DEBUG_CONDITION
cerr << "CONDITION DEBUG: " << (void *)pthread_self() << ": Want to lock " << &m_condition << " \"" << m_name << "\"" << endl;
#endif
pthread_mutex_lock(&m_mutex);
m_locked = true;
#ifdef DEBUG_CONDITION
cerr << "CONDITION DEBUG: " << (void *)pthread_self() << ": Locked " << &m_condition << " \"" << m_name << "\"" << endl;
#endif
}
void
Condition::unlock()
{
if (!m_locked) {
#ifdef DEBUG_CONDITION
cerr << "CONDITION DEBUG: " << (void *)pthread_self() << ": Not locked " << &m_condition << " \"" << m_name << "\"" << endl;
#endif
return;
}
#ifdef DEBUG_CONDITION
cerr << "CONDITION DEBUG: " << (void *)pthread_self() << ": Unlocking " << &m_condition << " \"" << m_name << "\"" << endl;
#endif
m_locked = false;
pthread_mutex_unlock(&m_mutex);
}
void
Condition::wait(int us)
{
if (us == 0) {
#ifdef DEBUG_CONDITION
cerr << "CONDITION DEBUG: " << (void *)pthread_self() << ": Waiting on " << &m_condition << " \"" << m_name << "\"" << endl;
#endif
pthread_cond_wait(&m_condition, &m_mutex);
} else {
struct timeval now;
gettimeofday(&now, 0);
now.tv_usec += us;
while (now.tv_usec > 1000000) {
now.tv_usec -= 1000000;
++now.tv_sec;
}
struct timespec timeout;
timeout.tv_sec = now.tv_sec;
timeout.tv_nsec = now.tv_usec * 1000;
#ifdef DEBUG_CONDITION
cerr << "CONDITION DEBUG: " << (void *)pthread_self() << ": Timed waiting on " << &m_condition << " \"" << m_name << "\"" << endl;
#endif
pthread_cond_timedwait(&m_condition, &m_mutex, &timeout);
}
#ifdef DEBUG_CONDITION
cerr << "CONDITION DEBUG: " << (void *)pthread_self() << ": Wait done on " << &m_condition << " \"" << m_name << "\"" << endl;
#endif
m_locked = true;
}
void
Condition::signal()
{
#ifdef DEBUG_CONDITION
cerr << "CONDITION DEBUG: " << (void *)pthread_self() << ": Signalling " << &m_condition << " \"" << m_name << "\"" << endl;
#endif
pthread_cond_signal(&m_condition);
}
#else /* !USE_PTHREADS */
Thread::Thread()
{
}
Thread::~Thread()
{
}
void
Thread::start()
{
abort();
}
void
Thread::wait()
{
abort();
}
Thread::Id
Thread::id()
{
abort();
}
bool
Thread::threadingAvailable()
{
return false;
}
Mutex::Mutex()
{
}
Mutex::~Mutex()
{
}
void
Mutex::lock()
{
abort();
}
void
Mutex::unlock()
{
abort();
}
bool
Mutex::trylock()
{
abort();
}
Condition::Condition(const char *)
{
}
Condition::~Condition()
{
}
void
Condition::lock()
{
abort();
}
void
Condition::wait(int us)
{
abort();
}
void
Condition::signal()
{
abort();
}
#endif /* !USE_PTHREADS */
#endif /* !_WIN32 */
MutexLocker::MutexLocker(Mutex *mutex) :
m_mutex(mutex)
{
if (m_mutex) {
m_mutex->lock();
}
}
MutexLocker::~MutexLocker()
{
if (m_mutex) {
m_mutex->unlock();
}
}
}
#endif