Modern applications increasingly rely on multi-threading: background tasks, network processing, rendering pipelines, and data caching often run in parallel. In such environments, shared mutable data structures become a critical source of bugs if not properly synchronized.
With AtomicDictionaryMBS introduced in MBS Xojo Plugins 26.3, we provide a dedicated thread-safe dictionary designed specifically for safe concurrent access in Xojo applications.
Why not just use a Xojo Dictionary?
The standard Dictionary in Xojo is not thread-safe. This means:
-
Concurrent reads and writes can lead to crashes
-
Race conditions can overwrite data unexpectedly
-
Iteration while another thread modifies data can cause exceptions
-
Complex locking must be implemented manually using Mutexes
In practice, this often leads to subtle bugs that are difficult to reproduce, especially in production environments where thread timing differs from debugging scenarios.
Developers typically solve this by wrapping a Dictionary in a Mutex, but this approach is error-prone:
-
Locks may be forgotten for certain access paths
-
Deadlocks can occur if multiple locks are used
-
Fine-grained atomic operations are hard to implement correctly
What AtomicDictionaryMBS improves
AtomicDictionaryMBS encapsulates synchronization internally and provides atomic operations that eliminate common race conditions entirely.
Instead of manually locking around read-modify-write sequences, you use purpose-built methods that guarantee correctness.
Core advantages
1. Built-in thread safety
All operations on AtomicDictionaryMBS are internally synchronized, meaning multiple threads can safely access the same instance without external locks.
2. Atomic operations instead of manual locking
Rather than doing this (unsafe in threaded code):
Var v As Variant = dict.Value("counter")
dict.Value("counter") = v + 1
You do:
a.Increment("counter")
This removes the race condition between read and write.
Key methods and why they matter
CompareAndSet – lock-free style synchronization
Var ok As Boolean = a.CompareAndSet("state", "idle", "running")
This ensures the value is only updated if it still matches the expected state. It prevents:
-
lost updates
-
double processing
-
stale state overwrites
This pattern is widely used in concurrent programming and replaces fragile manual locking logic.
GetAndSet – atomic read-modify-write
Var newValue As Variant = 100
Var oldValue As Variant = a.GetAndSet("queueSize", newValue)
This ensures no other thread can modify the value between reading and updating it.
GetOrSet – safe lazy initialization
Var v As Variant = a.GetOrSet("config", defaultConfig)
This avoids duplicate initialization when multiple threads attempt to create the same entry.
Increment – safe counters without locks
a.Increment("hits")
A common problem in multithreaded apps is updating counters safely. This method guarantees correctness without requiring a mutex.
ExecuteLocked – grouped atomic operations
a.ExecuteLocked(AddressOf UpdateDic)
Sub UpdateDic(dic As AtomicDictionaryMBS)
dic.Value("Hello") = dic.Value("Hello") + 1
End Sub
When multiple operations must be performed as a single atomic unit, this method ensures exclusive access for the duration of the delegate.
SetIfAbsent – safe initialization pattern
a.SetIfAbsent("session", sessionObject)
This ensures a value is only written if it does not already exist, preventing race conditions during initialization.
Iteration safety
The class provides an iterator for For Each loops:
For Each v As Variant In a.Iterator
// safe iteration snapshot behavior
Next
This avoids common crashes that occur when a Dictionary is modified during enumeration.
How AtomicDictionaryMBS differs from manual Mutex usage
| Aspect | Mutex + Dictionary | AtomicDictionaryMBS |
|---|---|---|
| Thread safety | Manual, error-prone | Built-in |
| Atomic operations | Must be implemented manually | Built-in (CompareAndSet, Increment, etc.) |
| Race conditions | Easy to introduce | Eliminated for API operations |
| Code complexity | High | Low |
| Deadlock risk | Possible | Greatly reduced |
Typical use cases
We can use the dictionary for various use cases in our Xojo projects:
-
Shared caches across worker threads
-
Game state synchronization (physics, AI, rendering)
-
Network session and token management
-
Producer/consumer pipelines
-
Event subscription systems
When NOT to use it
A standard Dictionary is still appropriate when:
-
Only one thread accesses the data
-
All access is already protected externally
-
Data is immutable after creation
Using AtomicDictionaryMBS in single-threaded scenarios is safe, but may introduce unnecessary overhead.
Availability
AtomicDictionaryMBS is available in MBS Xojo Plugins 26.3 and newer.
Summary
AtomicDictionaryMBS provides a modern, safe abstraction over shared key-value storage in multi-threaded Xojo applications. It removes the need for manual locking in most cases and replaces fragile concurrency patterns with clear, atomic operations.
The result is simpler code, fewer race conditions, and more reliable applications under real-world concurrent load.