Sane C++ Libraries: Threading (original) (raw)

🟩 Atomic, thread, thread pool, mutex, semaphore, barrier, rw-lock, condition variable

SaneCppThreading.h is a library defining basic primitives for user-space threading and synchronization.

Dependencies

Dependency Graph

Features

Class Description
SC::Thread A native OS thread.
SC::ThreadPool Simple thread pool that executes tasks in a fixed number of worker threads.
SC::Mutex A native OS mutex to synchronize access to shared resources.
SC::RWLock A Read-Write lock that allows multiple concurrent readers but only one writer.
SC::Barrier A synchronization point that blocks threads until the required number of threads have reached it.
SC::Semaphore A semaphore synchronization primitive that maintains a count for resource management.
SC::ConditionVariable A native OS condition variable.
SC::Atomic Atomic variables (only for int and bool for now).
SC::EventObject An automatically reset event object to synchronize two threads.

Status

🟩 Usable
All the main threading primitives are there.
The Atomic header is really only being implemented for a few data types and needs some love to extend and improve it.

Blog

Some relevant blog posts are:

Videos

This is the list of videos that have been recorded showing some of the internal thoughts that have been going into this library:

SC::Thread

A native OS thread.

Example:

Thread thread;

thread.start([](Thread& thread)

{

thread.setThreadName(SC_NATIVE_STR("My Thread"));

Thread::Sleep(1000);

});

thread.join();

thread.detach();

Warning

Thread destructor will assert if SC::Thread::detach() or SC::Thread::join() has not been called.

SC::ThreadPool

Simple thread pool that executes tasks in a fixed number of worker threads.

This class is not copyable / moveable due to it containing Mutex and Condition variable. Additionally, this class does not allocate any memory by itself, and expects the caller to supply SC::ThreadPool::Task objects.

Warning

The caller is responsible of keeping Task address stable until the it will be completed. If it's not already completed the task must still be valid during ThreadPool::destroy or ThreadPool destructor.

Example:

static const size_t wantedThreads = 4;

static const size_t numTasks = 100;

size_t values[numTasks];

for (size_t idx = 0; idx < numTasks; idx++)

{

size_t* value = values + idx;

*value = idx;

{

if (*value % 2)

{

}

*value *= 100;

};

}

bool allGood = true;

for (size_t idx = 0; idx < numTasks; idx++)

{

allGood = allGood && (values[idx] == idx * 100);

}

SC::Mutex

A native OS mutex to synchronize access to shared resources.

Example:

Mutex mutex;

int globalVariable = 0;

Thread thread1;

auto thread1Func = [&](Thread& thread)

{

thread.setThreadName(SC_NATIVE_STR("Thread1"));

mutex.lock();

globalVariable++;

mutex.unlock();

};

Thread thread2;

auto thread2Func = [&](Thread& thread)

{

thread.setThreadName(SC_NATIVE_STR("Signaling2"));

mutex.lock();

globalVariable++;

mutex.unlock();

};

SC::EventObject

An automatically reset event object to synchronize two threads.

Example:

EventObject eventObject;

Thread threadWaiting;

auto waitingFunc = [&](Thread& thread)

{

thread.setThreadName(SC_NATIVE_STR("Thread waiting"));

eventObject.wait();

report.console.printLine("After waiting");

};

Thread threadSignaling;

auto signalingFunc = [&](Thread& thread)

{

thread.setThreadName(SC_NATIVE_STR("Signaling thread"));

report.console.printLine("Signal");

eventObject.signal();

};

SC::Atomic

Atomic variables (only for int and bool for now).

Example:

Atomic test = true;

test.exchange(false);

#define SC_TEST_EXPECT(e)

Records a test expectation (eventually aborting or breaking o n failed test)

Definition Testing.h:159

SC::Semaphore

A semaphore synchronization primitive that maintains a count for resource management.

Example:

constexpr int maxResources = 2;

constexpr int numThreads = 4;

constexpr int operationsPerThread = 3;

Semaphore semaphore(maxResources);

struct Context

{

Semaphore& semaphore;

Mutex counterMutex;

int sharedResource = 0;

} ctx{semaphore, {}};

Thread threads[numThreads];

for (int i = 0; i < numThreads; i++)

{

auto threadFunc = [this, &ctx](Thread& thread)

{

thread.setThreadName(SC_NATIVE_STR("Worker Thread"));

for (int j = 0; j < operationsPerThread; j++)

{

ctx.semaphore.acquire();

ctx.counterMutex.lock();

ctx.sharedResource++;

SC_TEST_EXPECT(ctx.sharedResource <= maxResources);

Thread::Sleep(1);

ctx.sharedResource--;

ctx.counterMutex.unlock();

ctx.semaphore.release();

Thread::Sleep(1);

}

};

}

for (int i = 0; i < numThreads; i++)

{

}

SC::RWLock

A Read-Write lock that allows multiple concurrent readers but only one writer.

Example:

constexpr int numReaders = 3;

constexpr int numIterations = 100;

RWLock rwlock;

int sharedData = 0;

Thread readers[numReaders];

for (int i = 0; i < numReaders; i++)

{

auto readerFunc = [&](Thread& thread)

{

thread.setThreadName(SC_NATIVE_STR("Reader"));

for (int j = 0; j < numIterations; j++)

{

rwlock.lockRead();

volatile int value = sharedData;

(void)value;

rwlock.unlockRead();

Thread::Sleep(1);

}

};

}

Thread writer;

auto writerFunc = [&](Thread& thread)

{

thread.setThreadName(SC_NATIVE_STR("Writer"));

for (int i = 0; i < numIterations; i++)

{

rwlock.lockWrite();

sharedData++;

rwlock.unlockWrite();

Thread::Sleep(1);

}

};

for (int i = 0; i < numReaders; i++)

{

}

SC::Barrier

A synchronization point that blocks threads until the required number of threads have reached it.

Example:

constexpr uint32_t numThreads = 8;

constexpr int incrementsPerThread = 1000;

Thread threads[numThreads];

Barrier barrier(numThreads);

struct Context

{

Barrier& barrier;

Atomic<int32_t> sharedCounter;

} ctx = {barrier, 0};

for (uint32_t i = 0; i < numThreads; i++)

{

auto threadFunc = [this, &ctx](Thread& thread)

{

thread.setThreadName(SC_NATIVE_STR("Barrier"));

for (int j = 0; j < incrementsPerThread; ++j)

{

ctx.sharedCounter++;

}

ctx.barrier.wait();

SC_TEST_EXPECT(ctx.sharedCounter == numThreads * incrementsPerThread);

ctx.barrier.wait();

};

}

for (uint32_t i = 0; i < numThreads; i++)

{

}

SC::ConditionVariable

A native OS condition variable.

Roadmap

🟦 Complete Features:

Statistics

Type Lines Of Code Comments Sum
Headers 407 249 656
Sources 917 168 1085
Sum 1324 417 1741