threads and monitor

This commit is contained in:
AndreaRigoni
2026-03-25 10:40:13 +00:00
parent 913a1f7b3a
commit 0c8ef7337c
11 changed files with 579 additions and 31 deletions

View File

@@ -23,6 +23,8 @@ set( TESTS
VectorMetaAllocatorTest
PropertyTypesTest
HRPTest
MutexTest
ThreadsTest
)
set(LIBRARIES

View File

@@ -0,0 +1,108 @@
#include "Core/Monitor.h"
#include <iostream>
#include <thread>
#include <vector>
#include <cassert>
using namespace uLib;
void TestBasicLock() {
std::cout << "Testing basic Mutex Lock/Unlock..." << std::endl;
Mutex m;
m.Lock();
m.Unlock();
assert(m.TryLock());
m.Unlock();
std::cout << " Passed." << std::endl;
}
void TestScopedLock() {
std::cout << "Testing Mutex::ScopedLock..." << std::endl;
Mutex m;
{
Mutex::ScopedLock lock(m);
assert(!m.TryLock());
}
assert(m.TryLock());
m.Unlock();
std::cout << " Passed." << std::endl;
}
void TestTimedLock() {
std::cout << "Testing Mutex TryLockFor..." << std::endl;
Mutex m;
m.Lock();
auto start = std::chrono::steady_clock::now();
bool locked = m.TryLockFor(100);
auto end = std::chrono::steady_clock::now();
auto diff = std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
assert(!locked);
assert(diff >= 100);
m.Unlock();
std::cout << " Passed (waited " << diff << "ms)." << std::endl;
}
void TestMacros() {
std::cout << "Testing ULIB_STATIC_LOCK and ULIB_MUTEX_LOCK macros..." << std::endl;
int counter = 0;
auto task = [&]() {
for(int i=0; i<500; ++i) {
ULIB_STATIC_LOCK(-1) {
counter++;
}
}
};
std::vector<std::thread> threads;
for(int i=0; i<4; ++i) threads.emplace_back(task);
for(auto& t : threads) t.join();
assert(counter == 2000);
Mutex m;
int counter2 = 0;
ULIB_MUTEX_LOCK(m, -1) {
counter2++;
}
assert(counter2 == 1);
std::cout << " Passed." << std::endl;
}
void TestMonitor() {
std::cout << "Testing Monitor pattern..." << std::endl;
struct Resource {
int value = 0;
void increment() { value++; }
};
Monitor<Resource> monitor(new Resource());
auto task = [&]() {
for(int i=0; i<1000; ++i) {
monitor.Access([](Resource& r) {
r.increment();
});
}
};
std::vector<std::thread> threads;
for(int i=0; i<5; ++i) threads.emplace_back(task);
for(auto& t : threads) t.join();
int final_value = monitor.Access([](Resource& r) { return r.value; });
assert(final_value == 5000);
std::cout << " Passed (final value: " << final_value << ")." << std::endl;
}
int main() {
TestBasicLock();
TestScopedLock();
TestTimedLock();
TestMacros();
TestMonitor();
std::cout << "All Mutex and Monitor tests passed!" << std::endl;
return 0;
}

View File

@@ -0,0 +1,72 @@
#include "Core/Threads.h"
#include <iostream>
#include <atomic>
#include <cassert>
using namespace uLib;
class MyThread : public Thread {
public:
MyThread() : counter(0) {}
void Run() override {
for (int i = 0; i < 5; ++i) {
counter++;
Thread::Sleep(10);
}
}
std::atomic<int> counter;
};
void TestBasicThread() {
std::cout << "Testing basic Thread lifecycle..." << std::endl;
MyThread t;
assert(!t.IsRunning());
t.Start();
assert(t.IsRunning());
t.Join();
assert(!t.IsRunning());
assert(t.counter == 5);
std::cout << " Passed." << std::endl;
}
void TestThreadDetach() {
std::cout << "Testing Thread Detach..." << std::endl;
std::atomic<bool> done(false);
// Using a lambda or a simple subclass
class DetachedThread : public Thread {
public:
DetachedThread(std::atomic<bool>& d) : m_done(d) {}
void Run() override {
Thread::Sleep(50);
m_done = true;
}
std::atomic<bool>& m_done;
};
{
DetachedThread* t = new DetachedThread(done);
t->Start();
t->Detach();
// The thread object 't' is still alive here,
// but it will be destroyed soon if we delete it.
// For a detached thread using members, we MUST keep it alive.
int wait_count = 0;
while(!done && wait_count < 20) {
Thread::Sleep(10);
wait_count++;
}
delete t;
}
assert(done);
std::cout << " Passed." << std::endl;
}
int main() {
TestBasicThread();
TestThreadDetach();
std::cout << "All Thread tests passed!" << std::endl;
return 0;
}