Multithreading and concurrency patterns for game servers including synchronization primitives
Implements thread-safe game server architectures using mutexes, read-write locks, and lock-free patterns. Claude uses this when building game servers that need concurrent player updates, network I/O handling, or state synchronization.
/plugin marketplace add pluginagentmarketplace/custom-plugin-server-side-game-dev/plugin install server-side-game-dev-plugin@pluginagentmarketplace-game-serverThis skill inherits all available tools. When active, it can use any tool Claude has access to.
assets/config.yamlassets/threading-patterns.yamlreferences/CONCURRENCY_GUIDE.mdreferences/GUIDE.mdscripts/helper.pyscripts/thread_pool.pyImplement thread-safe game server architectures with proper synchronization.
| Model | Pros | Cons | Use Case |
|---|---|---|---|
| Single-threaded | Simple, predictable | Limited scale | Casual games |
| Thread-per-connection | Simple | High overhead | Small servers |
| Thread pool | Efficient | Complex | Most games |
| Actor model | No locks | Learning curve | Distributed |
std::mutex game_state_mutex;
void updatePlayerPosition(int playerId, Vector3 pos) {
std::lock_guard<std::mutex> lock(game_state_mutex);
players[playerId].position = pos;
}
std::shared_mutex players_rwlock;
// Multiple concurrent readers
Vector3 getPlayerPosition(int playerId) {
std::shared_lock<std::shared_mutex> lock(players_rwlock);
return players[playerId].position;
}
// Exclusive writer
void setPlayerPosition(int playerId, Vector3 pos) {
std::unique_lock<std::shared_mutex> lock(players_rwlock);
players[playerId].position = pos;
}
std::atomic_flag spinlock = ATOMIC_FLAG_INIT;
void criticalSection() {
while (spinlock.test_and_set(std::memory_order_acquire)) {
// Spin - use for very short critical sections only
}
// Critical section
spinlock.clear(std::memory_order_release);
}
// Lock-free player state updates
struct PlayerState {
std::atomic<float> x, y, z;
std::atomic<int> health;
};
// Compare-and-swap for safe updates
bool tryDamagePlayer(std::atomic<int>& health, int damage) {
int current = health.load();
int newHealth = current - damage;
return health.compare_exchange_strong(current, newHealth);
}
// SPSC Lock-free queue
template<typename T, size_t Size>
class SPSCQueue {
std::array<T, Size> buffer;
std::atomic<size_t> head{0};
std::atomic<size_t> tail{0};
public:
bool push(const T& item) {
size_t current_tail = tail.load(std::memory_order_relaxed);
size_t next = (current_tail + 1) % Size;
if (next == head.load(std::memory_order_acquire)) return false;
buffer[current_tail] = item;
tail.store(next, std::memory_order_release);
return true;
}
bool pop(T& item) {
size_t current_head = head.load(std::memory_order_relaxed);
if (current_head == tail.load(std::memory_order_acquire)) return false;
item = buffer[current_head];
head.store((current_head + 1) % Size, std::memory_order_release);
return true;
}
};
class GameThreadPool {
std::vector<std::thread> workers;
std::queue<std::function<void()>> tasks;
std::mutex queue_mutex;
std::condition_variable condition;
std::atomic<bool> stop{false};
public:
explicit GameThreadPool(size_t threads = 0) {
if (threads == 0) {
threads = std::thread::hardware_concurrency();
}
for (size_t i = 0; i < threads; ++i) {
workers.emplace_back([this] {
while (true) {
std::function<void()> task;
{
std::unique_lock<std::mutex> lock(queue_mutex);
condition.wait(lock, [this] {
return stop || !tasks.empty();
});
if (stop && tasks.empty()) return;
task = std::move(tasks.front());
tasks.pop();
}
task();
}
});
}
}
template<typename F>
void enqueue(F&& task) {
{
std::unique_lock<std::mutex> lock(queue_mutex);
tasks.push(std::forward<F>(task));
}
condition.notify_one();
}
~GameThreadPool() {
stop = true;
condition.notify_all();
for (auto& worker : workers) {
worker.join();
}
}
};
class ThreadedGameServer {
GameThreadPool network_pool; // Handle network I/O
GameThreadPool game_pool; // Game logic
std::shared_mutex state_mutex;
GameState state;
public:
void onPacketReceived(Connection* conn, Packet& pkt) {
// Network thread - parse and validate
auto cmd = parseCommand(pkt);
// Queue to game thread
game_pool.enqueue([this, cmd] {
std::unique_lock lock(state_mutex);
state.applyCommand(cmd);
});
}
void broadcastState() {
std::shared_lock lock(state_mutex);
auto snapshot = state.serialize();
// Fan out on network threads
for (auto& conn : connections) {
network_pool.enqueue([conn, snapshot] {
conn->send(snapshot);
});
}
}
};
| Problem | Root Cause | Solution |
|---|---|---|
| Deadlock | Lock ordering violation | Consistent lock hierarchy |
| Priority inversion | Low priority holds lock | Priority inheritance mutex |
| False sharing | Adjacent atomics | Cache line padding |
| Race conditions | Missing synchronization | Atomic operations/locks |
| Thread starvation | Unfair scheduling | Fair lock or work stealing |
| Memory visibility | Missing barriers | Proper memory ordering |
# Check thread count
ps -T -p $(pgrep game-server) | wc -l
# Thread CPU usage
top -H -p $(pgrep game-server)
# Detect deadlocks (Linux)
pstack $(pgrep game-server)
# Valgrind thread checker
valgrind --tool=helgrind ./game-server
# ThreadSanitizer
clang++ -fsanitize=thread -g game-server.cpp
// Runtime deadlock detection
#include <mutex>
#include <thread>
class DeadlockDetector {
std::unordered_map<std::thread::id, std::vector<void*>> held_locks;
std::mutex detector_mutex;
public:
void onLockAcquire(void* lock) {
std::lock_guard<std::mutex> guard(detector_mutex);
held_locks[std::this_thread::get_id()].push_back(lock);
// Check for cycles in lock graph
}
};
#include <gtest/gtest.h>
#include <thread>
#include <vector>
TEST(Threading, ThreadPoolExecutesTasks) {
GameThreadPool pool(4);
std::atomic<int> counter{0};
for (int i = 0; i < 1000; ++i) {
pool.enqueue([&counter] {
counter++;
});
}
std::this_thread::sleep_for(std::chrono::milliseconds(100));
EXPECT_EQ(counter.load(), 1000);
}
TEST(Threading, RWLockAllowsConcurrentReads) {
std::shared_mutex mutex;
std::atomic<int> concurrent_readers{0};
int max_concurrent = 0;
std::vector<std::thread> threads;
for (int i = 0; i < 10; ++i) {
threads.emplace_back([&] {
std::shared_lock lock(mutex);
int current = ++concurrent_readers;
max_concurrent = std::max(max_concurrent, current);
std::this_thread::sleep_for(std::chrono::milliseconds(10));
--concurrent_readers;
});
}
for (auto& t : threads) t.join();
EXPECT_GT(max_concurrent, 1); // Multiple concurrent readers
}
TEST(Threading, AtomicCASWorks) {
std::atomic<int> health{100};
// Simulate concurrent damage
std::vector<std::thread> threads;
for (int i = 0; i < 10; ++i) {
threads.emplace_back([&health] {
int expected = health.load();
while (!health.compare_exchange_weak(expected, expected - 10)) {
// Retry
}
});
}
for (auto& t : threads) t.join();
EXPECT_EQ(health.load(), 0);
}
assets/ - Threading patternsreferences/ - Concurrency guidesThis skill should be used when the user asks to "create a slash command", "add a command", "write a custom command", "define command arguments", "use command frontmatter", "organize commands", "create command with file references", "interactive command", "use AskUserQuestion in command", or needs guidance on slash command structure, YAML frontmatter fields, dynamic arguments, bash execution in commands, user interaction patterns, or command development best practices for Claude Code.
This skill should be used when the user asks to "create an agent", "add an agent", "write a subagent", "agent frontmatter", "when to use description", "agent examples", "agent tools", "agent colors", "autonomous agent", or needs guidance on agent structure, system prompts, triggering conditions, or agent development best practices for Claude Code plugins.
This skill should be used when the user asks to "create a hook", "add a PreToolUse/PostToolUse/Stop hook", "validate tool use", "implement prompt-based hooks", "use ${CLAUDE_PLUGIN_ROOT}", "set up event-driven automation", "block dangerous commands", or mentions hook events (PreToolUse, PostToolUse, Stop, SubagentStop, SessionStart, SessionEnd, UserPromptSubmit, PreCompact, Notification). Provides comprehensive guidance for creating and implementing Claude Code plugin hooks with focus on advanced prompt-based hooks API.