Ensure consistent mutex usage across all access points to shared data structures and clearly document what each mutex protects. Inconsistent locking can lead to race conditions, data corruption, and crashes.
Ensure consistent mutex usage across all access points to shared data structures and clearly document what each mutex protects. Inconsistent locking can lead to race conditions, data corruption, and crashes.
Key principles:
std::lock_guard
over std::scoped_lock
for single mutex scenariosExample of problematic inconsistent locking:
// BAD: Different mutexes protecting the same data
std::mutex mutex;
std::shared_mutex shared_mutex;
std::unordered_map<int32_t, HandlerInfo> functionIdToHandlers;
void setHandler() {
std::lock_guard<std::mutex> lock(mutex); // Uses mutex
functionIdToHandlers[id] = handler;
}
void dispatchHandler() {
std::shared_lock lock(shared_mutex); // Uses shared_mutex!
auto it = functionIdToHandlers.find(id); // Race condition!
}
void processHandler() {
// No lock at all - definitely unsafe!
functionIdToHandlers[id].process();
}
Better approach with consistent protection:
// GOOD: Single mutex with clear scope
std::shared_mutex handlers_mutex; // Clearly named
std::unordered_map<int32_t, HandlerInfo> functionIdToHandlers GUARDED_BY(handlers_mutex);
void setHandler() {
std::lock_guard lock(handlers_mutex);
functionIdToHandlers[id] = handler;
}
void dispatchHandler() {
HandlerInfo handler_copy;
{
std::shared_lock lock(handlers_mutex);
auto it = functionIdToHandlers.find(id);
if (it != functionIdToHandlers.end()) {
handler_copy = it->second; // Copy under lock
}
}
// Use handler_copy safely outside lock
if (handler_copy.isValid()) {
handler_copy.process();
}
}
This prevents race conditions where concurrent modifications can invalidate iterators or cause undefined behavior during map resizing.
Enter the URL of a public GitHub repository