C++
lock-free programming
concurrent data structures
multithreading
hash maps

Is it possible to implement lock free map in C

Data Structures & Algorithms practice on Codemia

Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.

Practice algorithms

Introduction

Yes, a lock-free map is possible in C++, but the honest answer needs a qualifier: a fully general lock-free map is hard to implement correctly. Simple insert-and-read designs are feasible, while deletion, resizing, and memory reclamation are the parts that turn the problem into advanced concurrent systems work.

Start with the Meaning of Lock-Free

Lock-free does not mean "uses atomics" or "never waits." It means the data structure guarantees overall system progress, so even if one thread stalls, some other thread can still finish an operation in a bounded number of steps.

That is a stronger claim than ordinary thread safety. A map is especially demanding because it has to coordinate key lookup, insertion, collision handling, and often deletion. Those operations are much harder to keep lock-free than something narrow like a single-producer queue.

A Small Insert-Only Map Is Achievable

The easiest realistic example is a fixed-capacity hash table that supports concurrent insert and find, but not deletion or resizing. That is still useful as a teaching model because it shows where atomics help and where the real complexity begins.

cpp
1#include <atomic>
2#include <cstddef>
3#include <iostream>
4#include <optional>
5#include <vector>
6
7struct Slot {
8    std::atomic<int> key;
9    std::atomic<int> value;
10
11    Slot() : key(-1), value(0) {}
12};
13
14class LockFreeIntMap {
15public:
16    explicit LockFreeIntMap(std::size_t capacity) : slots_(capacity) {}
17
18    bool insert(int k, int v) {
19        std::size_t start = static_cast<std::size_t>(k) % slots_.size();
20
21        for (std::size_t i = 0; i < slots_.size(); ++i) {
22            Slot& slot = slots_[(start + i) % slots_.size()];
23            int empty = -1;
24
25            if (slot.key.compare_exchange_strong(empty, k, std::memory_order_acq_rel)) {
26                slot.value.store(v, std::memory_order_release);
27                return true;
28            }
29
30            if (empty == k) {
31                slot.value.store(v, std::memory_order_release);
32                return true;
33            }
34        }
35
36        return false;
37    }
38
39    std::optional<int> find(int k) const {
40        std::size_t start = static_cast<std::size_t>(k) % slots_.size();
41
42        for (std::size_t i = 0; i < slots_.size(); ++i) {
43            const Slot& slot = slots_[(start + i) % slots_.size()];
44            int current = slot.key.load(std::memory_order_acquire);
45
46            if (current == k) {
47                return slot.value.load(std::memory_order_acquire);
48            }
49
50            if (current == -1) {
51                return std::nullopt;
52            }
53        }
54
55        return std::nullopt;
56    }
57
58private:
59    std::vector<Slot> slots_;
60};
61
62int main() {
63    LockFreeIntMap map(16);
64    map.insert(10, 100);
65    map.insert(26, 260);
66
67    if (auto value = map.find(26)) {
68        std::cout << *value << '\n';
69    }
70}

This program is intentionally limited. It proves that map-like lock-free behavior is possible, but it does not solve the whole problem space.

Why Production Lock-Free Maps Are Hard

A real map usually needs removal. Once removal enters the design, memory reclamation becomes the central difficulty. One thread may still be reading a node while another thread decides that node can be freed. Avoiding use-after-free bugs requires schemes such as hazard pointers, epoch-based reclamation, or related techniques.

Resizing is another major obstacle. A map that grows under contention has to migrate entries while readers and writers continue to operate. Doing that without a global lock is possible, but it requires careful versioning and a design that tolerates multiple table generations existing at once.

This is why experienced engineers often split the question into two parts:

  • Is a limited lock-free map possible? Yes.
  • Is a full-featured, production-grade one easy to write from scratch? No.

Choose Complexity Only When It Pays Off

A sharded hash map with fine-grained locks is often easier to verify and fast enough for real workloads. Lock-free code can reduce contention in the right environment, but it also increases implementation complexity, testing cost, and debugging difficulty.

If you truly need a production-quality concurrent map, start with a proven library or a published design instead of inventing one from scratch. In concurrent programming, correctness work usually dominates coding work.

Common Pitfalls

The biggest mistake is assuming atomics alone make a container lock-free and correct. They only give you low-level synchronization tools, not a finished algorithm.

Another common mistake is ignoring memory reclamation. A map that inserts and looks up correctly can still be unsafe the moment deletion is added.

Resizing is also easy to underestimate. Many toy examples appear correct until the table needs to grow under concurrent load.

Summary

  • A lock-free map in C++ is possible, especially in a restricted insert-only form.
  • Lock-free is a progress guarantee, not just a synonym for atomic operations.
  • Deletion, reclamation, and resizing are the hard parts of a real implementation.
  • Fine-grained locking is often simpler and good enough in production.
  • For serious use, prefer proven designs or libraries over an improvised implementation.

Related reading
Course
Intermediate
27 lessons
15 hours
DSA Fundamentals

Master algorithmic patterns and data structures through hands-on LeetCode-style problems - from arrays and hashing to dynamic programming and advanced graphs.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Data Structures & Algorithms practice on Codemia

Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.

Practice algorithms

All Rights Reserved.