@@ -48,7 +48,7 @@ RtList: RtList.cpp ../utils/RtList.hpp ../modules/rtmempool.a | |||
# valgrind ./RtList | |||
Utils: Utils.cpp | |||
$(CXX) $^ $(BUILD_CXX_FLAGS) $(ANSI_CXX_FLAGS) -std=c++11 -Wzero-as-null-pointer-constant $(LINK_FLAGS) -ldl -o $@ | |||
$(CXX) $^ $(BUILD_CXX_FLAGS) $(ANSI_CXX_FLAGS) -std=c++11 -Wzero-as-null-pointer-constant $(LINK_FLAGS) -ldl -lpthread -o $@ | |||
# valgrind ./Utils | |||
# -------------------------------------------------------------- | |||
@@ -19,7 +19,9 @@ | |||
#include "CarlaLibUtils.hpp" | |||
#include "CarlaJuceUtils.hpp" | |||
#include "CarlaMutex.hpp" | |||
#include "CarlaString.hpp" | |||
#include "CarlaThread.hpp" | |||
#include <cassert> | |||
#include <cstdlib> | |||
@@ -45,6 +47,41 @@ private: | |||
CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(MyLeakCheckedClass) | |||
}; | |||
class MyThread : public CarlaThread | |||
{ | |||
public: | |||
MyThread(CarlaMutex* const m) | |||
: CarlaThread("myThread"), | |||
fMu(m) | |||
{ | |||
} | |||
protected: | |||
void run() override | |||
{ | |||
carla_stderr("Thread started"); | |||
carla_stderr("Thread lock"); | |||
fMu->lock(); | |||
carla_stderr("Thread sleeping"); | |||
carla_sleep(3); | |||
carla_stderr("Thread unlock"); | |||
fMu->unlock(); | |||
carla_stderr("Thread sleep-waiting for stop"); | |||
while (! shouldExit()) | |||
carla_sleep(1); | |||
carla_stderr("Thread finished"); | |||
} | |||
private: | |||
CarlaMutex* const fMu; | |||
}; | |||
int main() | |||
{ | |||
// misc functions | |||
@@ -185,8 +222,44 @@ int main() | |||
delete b; | |||
} | |||
// Mutex | |||
{ | |||
CarlaMutex m; | |||
assert(! m.wasTryLockCalled()); | |||
assert(m.tryLock()); | |||
assert(m.wasTryLockCalled()); | |||
assert(! m.wasTryLockCalled()); // once | |||
m.unlock(); | |||
m.lock(); | |||
assert(! m.wasTryLockCalled()); | |||
{ CarlaMutex::ScopedUnlocker su(m); } | |||
assert(! m.wasTryLockCalled()); | |||
m.unlock(); | |||
{ CarlaMutex::ScopedLocker sl(m); } | |||
} | |||
// String | |||
{ | |||
CarlaString a, b(2), c("haha"), d((uint)0x999, true), e(0.7f), f(0.9), g('u'); | |||
assert(g == "u"); | |||
} | |||
// Thread | |||
{ | |||
CarlaMutex m; | |||
MyThread t(&m); | |||
carla_stdout("Thread init started"); | |||
t.start(); | |||
carla_stdout("Thread init finished, lock waiting..."); | |||
m.lock(); | |||
carla_stdout("Thread lock wait done"); | |||
m.unlock(); | |||
t.stop(-1); | |||
} | |||
return 0; | |||
@@ -24,25 +24,25 @@ | |||
// library related calls | |||
/* | |||
* Open 'filename' library (must not be NULL). | |||
* May return NULL, in which case "lib_error" has the error. | |||
* Open 'filename' library (must not be null). | |||
* May return null, in which case "lib_error" has the error. | |||
*/ | |||
void* lib_open(const char* const filename); | |||
/* | |||
* Close a previously opened library (must not be NULL). | |||
* Close a previously opened library (must not be null). | |||
* If false is returned,"lib_error" has the error. | |||
*/ | |||
bool lib_close(void* const lib); | |||
/* | |||
* Get a library symbol (must not be NULL). | |||
* May return NULL if the symbol is not found. | |||
* Get a library symbol (must not be null). | |||
* May return null if the symbol is not found. | |||
*/ | |||
void* lib_symbol(void* const lib, const char* const symbol); | |||
/* | |||
* Return the last operation error ('filename' must not be NULL). | |||
* Return the last operation error ('filename' must not be null). | |||
*/ | |||
const char* lib_error(const char* const filename); | |||
@@ -28,54 +28,81 @@ | |||
class CarlaMutex | |||
{ | |||
public: | |||
CarlaMutex() | |||
/* | |||
* Constructor. | |||
*/ | |||
CarlaMutex() noexcept | |||
: fTryLockWasCalled(false) | |||
{ | |||
pthread_mutex_init(&fMutex, nullptr); | |||
pthread_mutexattr_t atts; | |||
pthread_mutexattr_init(&atts); | |||
pthread_mutexattr_settype(&atts, PTHREAD_MUTEX_RECURSIVE); | |||
pthread_mutex_init(&fMutex, &atts); | |||
pthread_mutexattr_destroy (&atts); | |||
} | |||
~CarlaMutex() | |||
/* | |||
* Destructor. | |||
*/ | |||
~CarlaMutex() noexcept | |||
{ | |||
pthread_mutex_destroy(&fMutex); | |||
} | |||
void lock() | |||
/* | |||
* Check if "tryLock()" was called before. | |||
*/ | |||
bool wasTryLockCalled() const noexcept | |||
{ | |||
const bool ret(fTryLockWasCalled); | |||
fTryLockWasCalled = false; | |||
return ret; | |||
} | |||
/* | |||
* Lock the mutex. | |||
*/ | |||
void lock() const noexcept | |||
{ | |||
pthread_mutex_lock(&fMutex); | |||
} | |||
bool tryLock() | |||
/* | |||
* Try to lock the mutex. | |||
* Returns true if successful. | |||
*/ | |||
bool tryLock() const noexcept | |||
{ | |||
fTryLockWasCalled = true; | |||
return (pthread_mutex_trylock(&fMutex) == 0); | |||
} | |||
void unlock(/*const bool resetTryLock*/) | |||
/* | |||
* Unlock the mutex, optionally resetting the tryLock check. | |||
*/ | |||
void unlock(const bool resetTryLock = false) const noexcept | |||
{ | |||
//if (resetTryLock) | |||
// fTryLockWasCalled = false; | |||
if (resetTryLock) | |||
fTryLockWasCalled = false; | |||
pthread_mutex_unlock(&fMutex); | |||
} | |||
bool wasTryLockCalled() | |||
{ | |||
const bool ret(fTryLockWasCalled); | |||
fTryLockWasCalled = false; | |||
return ret; | |||
} | |||
/* | |||
* Helper class to lock&unlock a mutex during a function scope. | |||
*/ | |||
class ScopedLocker | |||
{ | |||
public: | |||
ScopedLocker(CarlaMutex& mutex) | |||
ScopedLocker(CarlaMutex& mutex) noexcept | |||
: fMutex(mutex) | |||
{ | |||
fMutex.lock(); | |||
} | |||
~ScopedLocker() | |||
~ScopedLocker() noexcept | |||
{ | |||
fMutex.unlock(); | |||
} | |||
@@ -87,9 +114,33 @@ public: | |||
CARLA_DECLARE_NON_COPY_CLASS(ScopedLocker) | |||
}; | |||
/* | |||
* Helper class to unlock&lock a mutex during a function scope. | |||
*/ | |||
class ScopedUnlocker | |||
{ | |||
public: | |||
ScopedUnlocker(CarlaMutex& mutex) noexcept | |||
: fMutex(mutex) | |||
{ | |||
fMutex.unlock(); | |||
} | |||
~ScopedUnlocker() noexcept | |||
{ | |||
fMutex.lock(); | |||
} | |||
private: | |||
CarlaMutex& fMutex; | |||
CARLA_PREVENT_HEAP_ALLOCATION | |||
CARLA_DECLARE_NON_COPY_CLASS(ScopedUnlocker) | |||
}; | |||
private: | |||
pthread_mutex_t fMutex; | |||
volatile bool fTryLockWasCalled; | |||
mutable pthread_mutex_t fMutex; // The mutex | |||
mutable volatile bool fTryLockWasCalled; // true if "tryLock()" was called at least once | |||
CARLA_PREVENT_HEAP_ALLOCATION | |||
CARLA_DECLARE_NON_COPY_CLASS(CarlaMutex) | |||
@@ -40,6 +40,19 @@ public: | |||
_dup(nullptr); | |||
} | |||
/* | |||
* Simple character. | |||
*/ | |||
explicit CarlaString(const char c) | |||
{ | |||
char ch[2]; | |||
ch[0] = c; | |||
ch[1] = '\0'; | |||
_init(); | |||
_dup(ch); | |||
} | |||
/* | |||
* Simple char string. | |||
*/ | |||
@@ -618,7 +631,7 @@ private: | |||
* Called whenever the string needs to be allocated. | |||
* | |||
* Notes: | |||
* - Allocates string only if first initiated, or 'strBuf' is not null and new string contents are different | |||
* - Allocates string only if first initiated, or if 'strBuf' is not null and new string contents are different | |||
* - If 'strBuf' is null 'size' must be 0 | |||
*/ | |||
void _dup(const char* const strBuf, const size_t size = 0) | |||
@@ -0,0 +1,198 @@ | |||
/* | |||
* Carla Thread | |||
* Copyright (C) 2013 Filipe Coelho <falktx@falktx.com> | |||
* | |||
* 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 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. | |||
* | |||
* For a full copy of the GNU General Public License see the doc/GPL.txt file. | |||
*/ | |||
#ifndef CARLA_THREAD_HPP_INCLUDED | |||
#define CARLA_THREAD_HPP_INCLUDED | |||
#include "CarlaMutex.hpp" | |||
#include "CarlaString.hpp" | |||
// ----------------------------------------------------------------------- | |||
// CarlaThread class | |||
class CarlaThread | |||
{ | |||
protected: | |||
/* | |||
* Constructor. | |||
*/ | |||
CarlaThread(const char* const threadName = nullptr) | |||
: fName(threadName), | |||
fShouldExit(false) | |||
{ | |||
_init(); | |||
} | |||
/* | |||
* Destructor. | |||
*/ | |||
~CarlaThread() | |||
{ | |||
CARLA_SAFE_ASSERT(! isRunning()); | |||
stop(-1); | |||
} | |||
/* | |||
* Virtual function to be implemented by the subclass. | |||
*/ | |||
virtual void run() = 0; | |||
public: | |||
/* | |||
* Check if the thread is running. | |||
*/ | |||
bool isRunning() const noexcept | |||
{ | |||
#ifdef CARLA_OS_WIN32 | |||
return (fHandle.p != nullptr); | |||
#else | |||
return (fHandle != 0); | |||
#endif | |||
} | |||
/* | |||
* Check if the thread should exit. | |||
*/ | |||
bool shouldExit() const noexcept | |||
{ | |||
return fShouldExit; | |||
} | |||
/* | |||
* Start the thread. | |||
*/ | |||
void start() | |||
{ | |||
CARLA_SAFE_ASSERT_RETURN(! isRunning(),); | |||
const CarlaMutex::ScopedLocker sl(fLock); | |||
fShouldExit = false; | |||
if (pthread_create(const_cast<pthread_t*>(&fHandle), nullptr, _threadEntryPoint, this) == 0) | |||
{ | |||
#if (__GLIBC__ * 1000 + __GLIBC_MINOR__) >= 2012 | |||
if (fName.isNotEmpty()) | |||
pthread_setname_np(fHandle, fName.getBuffer()); | |||
#endif | |||
pthread_detach(fHandle); | |||
// wait for thread to start | |||
fLock.lock(); | |||
} | |||
} | |||
/* | |||
* Stop the thread. | |||
* In the 'timeOutMilliseconds': | |||
* =0 -> no wait | |||
* >0 -> wait timeout value | |||
* <0 -> wait forever | |||
*/ | |||
bool stop(const int timeOutMilliseconds) | |||
{ | |||
const CarlaMutex::ScopedLocker sl(fLock); | |||
if (isRunning()) | |||
{ | |||
signalShouldExit(); | |||
if (timeOutMilliseconds != 0) | |||
{ | |||
// Wait for the thread to stop | |||
int timeOutCheck = (timeOutMilliseconds == 1 || timeOutMilliseconds == -1) ? timeOutMilliseconds : timeOutMilliseconds/2; | |||
while (isRunning()) | |||
{ | |||
carla_msleep(2); | |||
if (timeOutCheck < 0) | |||
continue; | |||
if (timeOutCheck > 0) | |||
timeOutCheck -= 1; | |||
else | |||
break; | |||
} | |||
} | |||
if (isRunning()) | |||
{ | |||
// should never happen! | |||
carla_stderr2("Carla assertion failure: \"! isRunning()\" in file %s, line %i", __FILE__, __LINE__); | |||
pthread_cancel(fHandle); | |||
_init(); | |||
return false; | |||
} | |||
} | |||
return true; | |||
} | |||
/* | |||
* Tell the thread to stop as soon as possible. | |||
*/ | |||
void signalShouldExit() noexcept | |||
{ | |||
fShouldExit = true; | |||
} | |||
private: | |||
CarlaMutex fLock; // Thread lock | |||
const CarlaString fName; // Thread name | |||
volatile pthread_t fHandle; // Handle for this thread | |||
volatile bool fShouldExit; // ... | |||
void _init() noexcept | |||
{ | |||
#ifdef CARLA_OS_WIN32 | |||
fHandle.p = nullptr; | |||
fHandle.x = 0; | |||
#else | |||
fHandle = 0; | |||
#endif | |||
} | |||
static void* _threadEntryPoint(void* userData) | |||
{ | |||
static_cast<CarlaThread*>(userData)->threadEntryPoint(); | |||
return nullptr; | |||
} | |||
void threadEntryPoint() | |||
{ | |||
//if (fName.isNotEmpty()) | |||
// setCurrentThreadName(threadName); | |||
// tell dad we're ready | |||
fLock.unlock(); | |||
run(); | |||
// done | |||
_init(); | |||
} | |||
CARLA_PREVENT_HEAP_ALLOCATION | |||
CARLA_DECLARE_NON_COPY_CLASS(CarlaThread) | |||
}; | |||
// ----------------------------------------------------------------------- | |||
#endif // CARLA_THREAD_HPP_INCLUDED |