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>
319 lines
13 KiB
HTML
319 lines
13 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>GroveEngine - IIO Messaging System</title>
|
|
<style>
|
|
body {
|
|
margin: 0;
|
|
padding: 20px;
|
|
background: #1a1a2e;
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
min-height: 100vh;
|
|
font-family: 'Consolas', 'Monaco', monospace;
|
|
}
|
|
|
|
.diagram-container {
|
|
width: 1200px;
|
|
height: 800px;
|
|
background: white;
|
|
border-radius: 10px;
|
|
padding: 40px;
|
|
box-shadow: 0 20px 60px rgba(0,0,0,0.5);
|
|
}
|
|
|
|
svg {
|
|
width: 100%;
|
|
height: 100%;
|
|
}
|
|
|
|
.module-box {
|
|
filter: drop-shadow(3px 3px 5px rgba(0,0,0,0.2));
|
|
}
|
|
|
|
.publisher {
|
|
fill: #e8f5e9;
|
|
stroke: #4caf50;
|
|
stroke-width: 3;
|
|
}
|
|
|
|
.subscriber {
|
|
fill: #e3f2fd;
|
|
stroke: #2196f3;
|
|
stroke-width: 3;
|
|
}
|
|
|
|
.iio-core {
|
|
fill: #fff3e0;
|
|
stroke: #ff9800;
|
|
stroke-width: 3;
|
|
}
|
|
|
|
.message-flow {
|
|
stroke: #4caf50;
|
|
stroke-width: 3;
|
|
fill: none;
|
|
marker-end: url(#arrowhead-msg);
|
|
stroke-dasharray: 8, 4;
|
|
animation: dash 1.5s linear infinite;
|
|
}
|
|
|
|
@keyframes dash {
|
|
to {
|
|
stroke-dashoffset: -12;
|
|
}
|
|
}
|
|
|
|
.subscribe-flow {
|
|
stroke: #2196f3;
|
|
stroke-width: 2;
|
|
fill: none;
|
|
marker-end: url(#arrowhead-sub);
|
|
}
|
|
|
|
.module-name {
|
|
font-size: 14px;
|
|
font-weight: bold;
|
|
fill: #1a1a1a;
|
|
}
|
|
|
|
.module-desc {
|
|
font-size: 10px;
|
|
fill: #666;
|
|
}
|
|
|
|
.topic-text {
|
|
font-size: 11px;
|
|
font-weight: bold;
|
|
fill: #4caf50;
|
|
}
|
|
|
|
.subscribe-text {
|
|
font-size: 10px;
|
|
fill: #2196f3;
|
|
}
|
|
|
|
.main-title {
|
|
font-size: 32px;
|
|
font-weight: bold;
|
|
fill: #1a1a1a;
|
|
}
|
|
|
|
.subtitle {
|
|
font-size: 14px;
|
|
fill: #666;
|
|
}
|
|
|
|
.section-title {
|
|
font-size: 16px;
|
|
font-weight: bold;
|
|
fill: #1a1a1a;
|
|
}
|
|
|
|
.detail-box {
|
|
fill: #f5f5f5;
|
|
stroke: #ddd;
|
|
stroke-width: 2;
|
|
}
|
|
|
|
.detail-text {
|
|
font-size: 10px;
|
|
fill: #555;
|
|
}
|
|
|
|
.highlight-box {
|
|
fill: #ffebee;
|
|
stroke: #d32f2f;
|
|
stroke-width: 2;
|
|
}
|
|
|
|
.code-text {
|
|
font-family: 'Consolas', monospace;
|
|
font-size: 9px;
|
|
fill: #1a1a1a;
|
|
}
|
|
|
|
.metric-value {
|
|
font-size: 20px;
|
|
font-weight: bold;
|
|
fill: #ff9800;
|
|
}
|
|
|
|
.tree-line {
|
|
stroke: #999;
|
|
stroke-width: 1;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="diagram-container">
|
|
<svg viewBox="0 0 1120 720" xmlns="http://www.w3.org/2000/svg">
|
|
<defs>
|
|
<marker id="arrowhead-msg" markerWidth="10" markerHeight="10" refX="9" refY="3" orient="auto">
|
|
<polygon points="0 0, 10 3, 0 6" fill="#4caf50" />
|
|
</marker>
|
|
<marker id="arrowhead-sub" markerWidth="8" markerHeight="8" refX="7" refY="3" orient="auto">
|
|
<polygon points="0 0, 8 3, 0 6" fill="#2196f3" />
|
|
</marker>
|
|
</defs>
|
|
|
|
<!-- Title -->
|
|
<text x="560" y="35" class="main-title" text-anchor="middle">IIO Pub/Sub Messaging System</text>
|
|
<text x="560" y="55" class="subtitle" text-anchor="middle">IntraIOManager • TopicTree Pattern Matching • Zero Module Coupling</text>
|
|
|
|
<!-- Example Scenario Label -->
|
|
<text x="30" y="90" class="section-title">Example: Player Movement Message Flow</text>
|
|
|
|
<!-- Publisher: PlayerModule -->
|
|
<rect x="50" y="110" width="160" height="80" rx="8" class="module-box publisher"/>
|
|
<text x="130" y="135" class="module-name" text-anchor="middle">PlayerModule</text>
|
|
<text x="130" y="150" class="module-desc" text-anchor="middle">Game Logic</text>
|
|
<text x="60" y="170" class="detail-text">io.publish(</text>
|
|
<text x="70" y="182" class="topic-text">"player:position",</text>
|
|
<text x="70" y="194" class="detail-text">{x: 100, y: 200})</text>
|
|
|
|
<!-- Arrow from PlayerModule to IIO -->
|
|
<path d="M 210 150 L 280 230" class="message-flow"/>
|
|
<text x="235" y="185" class="topic-text">publish</text>
|
|
|
|
<!-- IIO Core: IntraIOManager -->
|
|
<rect x="280" y="200" width="280" height="200" rx="8" class="module-box iio-core"/>
|
|
<text x="420" y="225" class="module-name" text-anchor="middle" font-size="16px">IntraIOManager</text>
|
|
<text x="420" y="242" class="module-desc" text-anchor="middle">Message Router + TopicTree</text>
|
|
|
|
<!-- TopicTree visualization -->
|
|
<text x="295" y="265" class="detail-text" font-weight="bold">TopicTree Pattern Matching:</text>
|
|
|
|
<!-- Tree structure -->
|
|
<circle cx="420" cy="285" r="4" fill="#ff9800"/>
|
|
<text x="430" y="290" class="code-text">player:</text>
|
|
|
|
<!-- Branches -->
|
|
<line x1="420" y1="289" x2="360" y2="310" class="tree-line"/>
|
|
<line x1="420" y1="289" x2="420" y2="310" class="tree-line"/>
|
|
<line x1="420" y1="289" x2="480" y2="310" class="tree-line"/>
|
|
|
|
<circle cx="360" cy="310" r="3" fill="#ff9800"/>
|
|
<text x="330" y="315" class="code-text">position</text>
|
|
|
|
<circle cx="420" cy="310" r="3" fill="#ff9800"/>
|
|
<text x="395" y="315" class="code-text">health</text>
|
|
|
|
<circle cx="480" cy="310" r="3" fill="#ff9800"/>
|
|
<text x="455" y="315" class="code-text">score</text>
|
|
|
|
<!-- Subscribers list -->
|
|
<text x="295" y="340" class="detail-text" font-weight="bold">Matched Subscribers:</text>
|
|
|
|
<text x="305" y="357" class="subscribe-text">1. "player:position" → UIModule</text>
|
|
<text x="305" y="370" class="subscribe-text">2. "player:*" → CollisionModule</text>
|
|
<text x="305" y="383" class="subscribe-text">3. "*" → MetricsModule</text>
|
|
|
|
<!-- Performance note -->
|
|
<rect x="290" y="395" width="260" height="35" rx="6" fill="#ffebee" stroke="#d32f2f" stroke-width="1"/>
|
|
<text x="420" y="412" class="detail-text" text-anchor="middle" font-weight="bold" fill="#d32f2f">
|
|
⚡ O(k) matching where k = topic depth
|
|
</text>
|
|
<text x="420" y="424" class="detail-text" text-anchor="middle" fill="#666">
|
|
Sub-millisecond routing • Lock-free design
|
|
</text>
|
|
|
|
<!-- Subscribers receiving messages -->
|
|
|
|
<!-- Subscriber 1: UIModule -->
|
|
<rect x="610" y="110" width="160" height="80" rx="8" class="module-box subscriber"/>
|
|
<text x="690" y="135" class="module-name" text-anchor="middle">UIModule</text>
|
|
<text x="690" y="150" class="module-desc" text-anchor="middle">User Interface</text>
|
|
<text x="620" y="170" class="subscribe-text">subscribed:</text>
|
|
<text x="625" y="182" class="topic-text">"player:position"</text>
|
|
|
|
<path d="M 560 230 L 610 150" class="message-flow"/>
|
|
<text x="575" y="185" class="topic-text">match ✓</text>
|
|
|
|
<!-- Subscriber 2: CollisionModule -->
|
|
<rect x="610" y="220" width="160" height="80" rx="8" class="module-box subscriber"/>
|
|
<text x="690" y="245" class="module-name" text-anchor="middle">CollisionModule</text>
|
|
<text x="690" y="260" class="module-desc" text-anchor="middle">Physics</text>
|
|
<text x="620" y="280" class="subscribe-text">subscribed:</text>
|
|
<text x="625" y="292" class="topic-text">"player:*"</text>
|
|
|
|
<path d="M 560 260 L 610 260" class="message-flow"/>
|
|
<text x="575" y="255" class="topic-text">match ✓</text>
|
|
|
|
<!-- Subscriber 3: MetricsModule -->
|
|
<rect x="610" y="330" width="160" height="80" rx="8" class="module-box subscriber"/>
|
|
<text x="690" y="355" class="module-name" text-anchor="middle">MetricsModule</text>
|
|
<text x="690" y="370" class="module-desc" text-anchor="middle">Analytics</text>
|
|
<text x="620" y="390" class="subscribe-text">subscribed:</text>
|
|
<text x="625" y="402" class="topic-text">"*" (all topics)</text>
|
|
|
|
<path d="M 560 290 L 610 370" class="message-flow"/>
|
|
<text x="575" y="335" class="topic-text">match ✓</text>
|
|
|
|
<!-- Code Example Section -->
|
|
<rect x="50" y="430" width="720" height="260" rx="8" class="detail-box"/>
|
|
<text x="410" y="455" class="section-title" text-anchor="middle">Code Example: Complete Pub/Sub Flow</text>
|
|
|
|
<!-- Publisher code -->
|
|
<text x="65" y="480" class="detail-text" font-weight="bold">1. Publisher (PlayerModule):</text>
|
|
<rect x="65" y="485" width="340" height="90" rx="4" fill="#e8f5e9"/>
|
|
<text x="75" y="502" class="code-text">// Create message data</text>
|
|
<text x="75" y="515" class="code-text">auto data = std::make_unique<JsonDataNode>("position");</text>
|
|
<text x="75" y="528" class="code-text">data->setDouble("x", playerX);</text>
|
|
<text x="75" y="541" class="code-text">data->setDouble("y", playerY);</text>
|
|
<text x="75" y="554" class="code-text">data->setDouble("vx", velocityX);</text>
|
|
<text x="75" y="567" class="code-text" fill="#4caf50" font-weight="bold">io->publish("player:position", std::move(data));</text>
|
|
|
|
<!-- Subscriber code -->
|
|
<text x="420" y="480" class="detail-text" font-weight="bold">2. Subscriber (UIModule):</text>
|
|
<rect x="420" y="485" width="340" height="90" rx="4" fill="#e3f2fd"/>
|
|
<text x="430" y="502" class="code-text">// Subscribe to topic pattern</text>
|
|
<text x="430" y="515" class="code-text" fill="#2196f3" font-weight="bold">io->subscribe("player:position");</text>
|
|
<text x="430" y="528" class="code-text">// In process() loop:</text>
|
|
<text x="430" y="541" class="code-text">while (io->hasMessages()) {</text>
|
|
<text x="440" y="554" class="code-text"> auto msg = io->pullMessage();</text>
|
|
<text x="440" y="567" class="code-text"> updatePlayerUI(msg.data);</text>
|
|
<text x="430" y="580" class="code-text">}</text>
|
|
|
|
<!-- Wildcard patterns -->
|
|
<text x="65" y="600" class="detail-text" font-weight="bold">3. Wildcard Pattern Examples:</text>
|
|
<rect x="65" y="605" width="695" height="75" rx="4" fill="#fff3e0"/>
|
|
|
|
<text x="75" y="622" class="code-text">"player:position" → Exact match only</text>
|
|
<text x="75" y="635" class="code-text">"player:*" → Matches player:position, player:health, player:score</text>
|
|
<text x="75" y="648" class="code-text">"render:*" → Matches render:sprite, render:text, render:clear</text>
|
|
<text x="75" y="661" class="code-text">"*" → Matches ALL topics (use for logging/metrics)</text>
|
|
<text x="75" y="674" class="code-text">"ui:button:*" → Matches ui:button:click, ui:button:hover</text>
|
|
|
|
<!-- Performance Metrics -->
|
|
<rect x="800" y="430" width="270" height="140" rx="8" class="highlight-box"/>
|
|
<text x="935" y="455" class="section-title" text-anchor="middle" fill="#d32f2f">Performance</text>
|
|
|
|
<text x="820" y="480" class="detail-text">Routing time:</text>
|
|
<text x="935" y="505" class="metric-value" text-anchor="middle">< 0.1ms</text>
|
|
|
|
<text x="820" y="525" class="detail-text">Pattern match complexity:</text>
|
|
<text x="935" y="545" class="metric-value" text-anchor="middle">O(k)</text>
|
|
<text x="935" y="560" class="detail-text" text-anchor="middle">k = topic depth (e.g., 2 for "player:pos")</text>
|
|
|
|
<!-- Benefits -->
|
|
<rect x="800" y="585" width="270" height="105" rx="8" fill="#e8f5e9" stroke="#4caf50" stroke-width="2"/>
|
|
<text x="935" y="608" class="section-title" text-anchor="middle" fill="#2e7d32">Benefits</text>
|
|
|
|
<text x="815" y="630" class="detail-text">✓ Zero module coupling</text>
|
|
<text x="815" y="645" class="detail-text">✓ Easy to add/remove modules</text>
|
|
<text x="815" y="660" class="detail-text">✓ Dynamic subscriptions at runtime</text>
|
|
<text x="815" y="675" class="detail-text">✓ Thread-safe message queuing</text>
|
|
|
|
<!-- Footer -->
|
|
<text x="560" y="710" class="subtitle" text-anchor="middle">
|
|
IIO enables complete module decoupling • Add/remove modules without changing existing code
|
|
</text>
|
|
</svg>
|
|
</div>
|
|
</body>
|
|
</html>
|