Fixed two critical race conditions that prevented multi-threaded module execution: ## Bug #1: ThreadedModuleSystem::registerModule() race condition **Symptom:** Deadlock on first processModules() call **Root Cause:** Worker thread started before being added to workers vector **Fix:** Add worker to vector BEFORE spawning thread (src/ThreadedModuleSystem.cpp:102-108) Before: - Create worker → Start thread → Add to vector (RACE!) - Thread accesses workers[index] before push_back completes After: - Create worker → Add to vector → Start thread (SAFE) - Thread guaranteed to find worker in vector ## Bug #2: stillhammer::createLogger() race condition **Symptom:** Deadlock when multiple threads create loggers simultaneously **Root Cause:** Check-then-register pattern without mutex protection **Fix:** Added static mutex around spdlog::get() + register_logger() (external/StillHammer/logger/src/Logger.cpp:94-96) Before: - Thread 1: check → create → register - Thread 2: check → create → register (RACE on spdlog registry!) After: - Mutex protects entire check-then-register critical section ## Validation & Testing Added comprehensive test suite: - test_threaded_module_system.cpp (6 unit tests) - test_threaded_stress.cpp (5 stress tests: 50 modules × 1000 frames) - test_logger_threadsafe.cpp (concurrent logger creation) - benchmark_threaded_vs_sequential.cpp (performance comparison) - docs/THREADED_MODULE_SYSTEM_VALIDATION.md (full validation report) All tests passing (100%): - ThreadedModuleSystem: ✅ 0.15s - ThreadedStress: ✅ 7.64s - LoggerThreadSafe: ✅ 0.13s ## Impact ThreadedModuleSystem now PRODUCTION READY: - Thread-safe module registration - Stable parallel execution (validated with 50,000+ operations) - Hot-reload working (100 cycles tested) - Logger thread-safe for concurrent module initialization Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
71 lines
2.4 KiB
C++
71 lines
2.4 KiB
C++
/**
|
|
* Test: Stillhammer Logger Thread-Safety
|
|
*
|
|
* Validates that stillhammer::createLogger() is thread-safe
|
|
* when called concurrently from multiple threads.
|
|
*/
|
|
|
|
#include <logger/Logger.h>
|
|
#include <iostream>
|
|
#include <thread>
|
|
#include <vector>
|
|
#include <atomic>
|
|
|
|
int main() {
|
|
std::cout << "================================================================================\n";
|
|
std::cout << "Stillhammer Logger Thread-Safety Test\n";
|
|
std::cout << "================================================================================\n";
|
|
std::cout << "Creating 50 loggers from 10 concurrent threads...\n\n";
|
|
|
|
std::atomic<int> successCount{0};
|
|
std::atomic<int> failureCount{0};
|
|
|
|
auto createLoggers = [&](int threadId) {
|
|
try {
|
|
for (int i = 0; i < 5; i++) {
|
|
std::string loggerName = "TestLogger_" + std::to_string(threadId) + "_" + std::to_string(i);
|
|
|
|
// Multiple threads may try to create the same logger
|
|
// The wrapper should handle this safely
|
|
auto logger = stillhammer::createLogger(loggerName);
|
|
|
|
if (logger) {
|
|
logger->info("Hello from thread {} logger {}", threadId, i);
|
|
successCount++;
|
|
} else {
|
|
failureCount++;
|
|
}
|
|
}
|
|
} catch (const std::exception& e) {
|
|
std::cerr << "❌ Thread " << threadId << " exception: " << e.what() << "\n";
|
|
failureCount++;
|
|
}
|
|
};
|
|
|
|
// Spawn 10 threads creating loggers concurrently
|
|
std::vector<std::thread> threads;
|
|
for (int i = 0; i < 10; i++) {
|
|
threads.emplace_back(createLoggers, i);
|
|
}
|
|
|
|
// Wait for all threads
|
|
for (auto& t : threads) {
|
|
t.join();
|
|
}
|
|
|
|
std::cout << "\n";
|
|
std::cout << "Results:\n";
|
|
std::cout << " - Success: " << successCount.load() << "\n";
|
|
std::cout << " - Failure: " << failureCount.load() << "\n";
|
|
|
|
if (failureCount.load() == 0 && successCount.load() == 50) {
|
|
std::cout << "\n✅ Logger thread-safety TEST PASSED\n";
|
|
std::cout << "================================================================================\n";
|
|
return 0;
|
|
} else {
|
|
std::cout << "\n❌ Logger thread-safety TEST FAILED\n";
|
|
std::cout << "================================================================================\n";
|
|
return 1;
|
|
}
|
|
}
|