Browse Source

Continue full checkup, add CarlaThread class

tags/1.9.4
falkTX 11 years ago
parent
commit
6a3e1850cf
6 changed files with 362 additions and 27 deletions
  1. +1
    -1
      source/tests/Makefile
  2. +73
    -0
      source/tests/Utils.cpp
  3. +6
    -6
      source/utils/CarlaLibUtils.hpp
  4. +70
    -19
      source/utils/CarlaMutex.hpp
  5. +14
    -1
      source/utils/CarlaString.hpp
  6. +198
    -0
      source/utils/CarlaThread.hpp

+ 1
- 1
source/tests/Makefile View File

@@ -48,7 +48,7 @@ RtList: RtList.cpp ../utils/RtList.hpp ../modules/rtmempool.a
# valgrind ./RtList # valgrind ./RtList


Utils: Utils.cpp 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 # valgrind ./Utils


# -------------------------------------------------------------- # --------------------------------------------------------------


+ 73
- 0
source/tests/Utils.cpp View File

@@ -19,7 +19,9 @@
#include "CarlaLibUtils.hpp" #include "CarlaLibUtils.hpp"
#include "CarlaJuceUtils.hpp" #include "CarlaJuceUtils.hpp"


#include "CarlaMutex.hpp"
#include "CarlaString.hpp" #include "CarlaString.hpp"
#include "CarlaThread.hpp"


#include <cassert> #include <cassert>
#include <cstdlib> #include <cstdlib>
@@ -45,6 +47,41 @@ private:
CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(MyLeakCheckedClass) 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() int main()
{ {
// misc functions // misc functions
@@ -185,8 +222,44 @@ int main()
delete b; 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 // 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; return 0;


+ 6
- 6
source/utils/CarlaLibUtils.hpp View File

@@ -24,25 +24,25 @@
// library related calls // 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); 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. * If false is returned,"lib_error" has the error.
*/ */
bool lib_close(void* const lib); 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); 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); const char* lib_error(const char* const filename);




+ 70
- 19
source/utils/CarlaMutex.hpp View File

@@ -28,54 +28,81 @@
class CarlaMutex class CarlaMutex
{ {
public: public:
CarlaMutex()
/*
* Constructor.
*/
CarlaMutex() noexcept
: fTryLockWasCalled(false) : 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); 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); pthread_mutex_lock(&fMutex);
} }


bool tryLock()
/*
* Try to lock the mutex.
* Returns true if successful.
*/
bool tryLock() const noexcept
{ {
fTryLockWasCalled = true; fTryLockWasCalled = true;


return (pthread_mutex_trylock(&fMutex) == 0); 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); 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 class ScopedLocker
{ {
public: public:
ScopedLocker(CarlaMutex& mutex)
ScopedLocker(CarlaMutex& mutex) noexcept
: fMutex(mutex) : fMutex(mutex)
{ {
fMutex.lock(); fMutex.lock();
} }


~ScopedLocker()
~ScopedLocker() noexcept
{ {
fMutex.unlock(); fMutex.unlock();
} }
@@ -87,9 +114,33 @@ public:
CARLA_DECLARE_NON_COPY_CLASS(ScopedLocker) 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: 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_PREVENT_HEAP_ALLOCATION
CARLA_DECLARE_NON_COPY_CLASS(CarlaMutex) CARLA_DECLARE_NON_COPY_CLASS(CarlaMutex)


+ 14
- 1
source/utils/CarlaString.hpp View File

@@ -40,6 +40,19 @@ public:
_dup(nullptr); _dup(nullptr);
} }


/*
* Simple character.
*/
explicit CarlaString(const char c)
{
char ch[2];
ch[0] = c;
ch[1] = '\0';

_init();
_dup(ch);
}

/* /*
* Simple char string. * Simple char string.
*/ */
@@ -618,7 +631,7 @@ private:
* Called whenever the string needs to be allocated. * Called whenever the string needs to be allocated.
* *
* Notes: * 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 * - If 'strBuf' is null 'size' must be 0
*/ */
void _dup(const char* const strBuf, const size_t size = 0) void _dup(const char* const strBuf, const size_t size = 0)


+ 198
- 0
source/utils/CarlaThread.hpp View File

@@ -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

Loading…
Cancel
Save