
Golang atomic operations allow for safe and efficient updates to shared variables in concurrent programs. This is crucial for maintaining data consistency and preventing data races.
In Golang, atomic operations are implemented using the `sync/atomic` package, which provides a set of functions for performing atomic operations on integers and pointers.
One key aspect of Golang atomic operations is that they can be used with both pointers and integers, making them versatile for a wide range of use cases.
To ensure thread-safety, Golang atomic operations use a combination of hardware and software techniques to prevent concurrent modifications to shared variables.
Atomic Operations
Atomic operations are a fundamental concept in concurrency, allowing safe and uninterrupted updates to shared variables. They are designed to be executed as single, indivisible units, ensuring that certain operations on shared variables are performed atomically.
Atomic operations are lower-level synchronization primitives that provide a safe way to access and modify shared variables. They can be used to implement simple, frequent updates to individual variables like counters or flags.
In Go, atomic operations can be used to perform bitwise AND operations, decrement values, and execute compare-and-swap operations. For example, the `AndInt32` function atomically performs a bitwise AND operation on a pointer using a provided bitmask.
Here are some examples of atomic operations in Go:
- Swap: used to atomically swap the value of a variable with a new value.
- CompareAndSwapUintptr: executes the compare-and-swap operation for a uintptr value.
- Dec: atomically decrements the wrapped uintptr and returns the new value.
These atomic operations are useful for tracking the number of concurrent users in a web application, as well as for implementing other concurrent access scenarios.
Int32
The Int32 type is an atomic wrapper around int32, which means it provides atomic operations for integers. This is useful for concurrent programming.
You can use the Int32 type to perform atomic operations such as adding a value, storing a value, and swapping a value. For example, you can use the AddInt32 function to atomically add a delta to an address and return the new value.
Here are some key features of the Int32 type:
- AddInt32: Atomically adds delta to *addr and returns the new value.
- LoadInt32: Atomically loads *addr.
- StoreInt32: Atomically stores val into *addr.
- SwapInt32: Atomically stores new into *addr and returns the previous *addr value.
The Int32 type also has a more ergonomic and less error-prone alternative, Int32, which provides similar operations with less chance of errors. For example, you can use Int32.Add instead of AddInt32.
In addition, the Int32 type has a MarshalJSON method that encodes the wrapped int32 into JSON. This is useful for serializing the value.
The Int32 type is a useful tool for concurrent programming, and its atomic operations make it a great choice for situations where multiple goroutines need to access the same data.
Uintptr
A Uintptr is an atomic uintptr, and its zero value is zero.
You can use the Uintptr type to perform atomic operations on a uintptr value. For example, you can use the AddUintptr function to atomically add a delta to a uintptr address and return the new value.
The AddUintptr function is not the only option, as the Uintptr type has its own Add method that is more ergonomic and less error-prone. This method atomically adds to the wrapped uint64 and returns the new value.
The Uintptr type also has a Dec method that atomically decrements the wrapped uintptr and returns the new value. This method is part of the Uintptr type, not a separate function like AddUintptr.
Broaden your view: Golang Method
The Uintptr type is not the only atomic type, as there is also a Uint64 type that provides similar functionality. For example, you can use the AndUint64 function to atomically perform a bitwise AND operation on a uintptr address using a bitmask and return the old value.
The Uint64 type has its own And method that is more ergonomic and less error-prone than the AndUint64 function. This method is particularly useful on 32-bit platforms, where the AndUint64 function may not work correctly.
In general, it's a good idea to use the more ergonomic and less error-prone methods provided by the Uintptr and Uint64 types, such as Uintptr.Add or Uint64.And, rather than using the separate functions like AddUintptr or AndUint64.
Compare Uint64
You can use the CompareAndSwapUint64 function to execute a compare-and-swap operation for a uint64 value.
This operation checks if the current value is equal to an expected value and, if it is, sets it to a new value.

The CompareAndSwapUint64 function returns a boolean indicating if the swap was successful.
It's a crucial operation that's used in many concurrent programming scenarios.
Consider using the more ergonomic and less error-prone Uint64.CompareAndSwap instead, particularly if you target 32-bit platforms.
You can also use the CompareAndSwapUint64 function as part of a larger atomic operation, such as swapping the value of a Uint64 atomically.
This can be useful when you need to update a value and then perform some additional operation based on the result.
The CompareAndSwapUint64 function is a powerful tool for working with uint64 values in a concurrent environment.
By using it correctly, you can write more efficient and reliable code.
Compare and Swap
Compare and Swap is a crucial operation in Go's atomic package. It checks if the current value is equal to an expected value and, if it is, sets it to a new value.
The atomic package provides several CompareAndSwap functions for different types, such as CompareAndSwapUint32, CompareAndSwapUint64, and CompareAndSwapInt32. These functions are less error-prone and more ergonomic than their counterparts.
Consider using the more ergonomic and less error-prone Uint32.CompareAndSwap, Uint64.CompareAndSwap, and Int32.CompareAndSwap instead of their raw counterparts. This is particularly important if you target 32-bit platforms.
See what others are reading: Golang Pkg
CompareAnd
CompareAnd is a crucial operation that checks if the current value is equal to an expected value and, if it is, sets it to a new value. It returns a boolean indicating if the swap was successful.
CompareAndSwapUint32 and CompareAndSwapUint64 are two specific functions that execute the compare-and-swap operation for uint32 and uint64 values respectively. However, it's recommended to use Uint32.CompareAndSwap and Uint64.CompareAndSwap instead, as they are more ergonomic and less error-prone.
The CompareAndSwap operation is not limited to integers, it can also be used for boolean values. The (*Bool) CompareAndSwap function executes the compare-and-swap operation for the boolean value x.
CAS is a crucial operation that checks if the current value is equal to an expected value and, if it is, sets it to a new value. It's essential to note that all calls to CompareAndSwap for a given Value must use values of the same concrete type.
The CompareAndSwap operation can be used for various types, including int32, int64, and Value. However, it's recommended to use the more ergonomic and less error-prone functions, such as Int32.CompareAndSwap and Int64.CompareAndSwap, especially when targeting 32-bit platforms.
The CompareAndSwap operation can also be used for Error values. The (*Error) CompareAndSwap function atomically stores the given error and returns the old value.
CompareAndPointer
CompareAndSwapPointer is a function that executes the compare-and-swap operation for a unsafe.Pointer value. It's recommended to use the more ergonomic and less error-prone Pointer.CompareAndSwap instead.
The CompareAndSwapPointer function is designed for working with unsafe.Pointer values, which are not as safe as regular pointers. This is because unsafe.Pointer values can point to any memory location, not just valid Go objects.
Pointer.CompareAndSwap is a more modern and safer alternative to CompareAndSwapPointer. It's designed to be easier to use and less prone to errors, making it a better choice for most use cases.
Load and Store
Load and Store operations are the building blocks of atomicity in Go. They allow you to update shared variables in a thread-safe manner.
The atomic.Load operation reads a value from a shared variable without blocking other goroutines from accessing it. This is crucial when dealing with shared data.
Here's an example of using atomic.Load to read a shared variable: `x := atomic.LoadInt32(&sharedVariable)`. This operation ensures that the value is read without interference from other threads.
In contrast, atomic.Store updates a shared variable with a new value. This operation also allows for thread-safe updates. For instance, `atomic.StoreInt32(&sharedVariable, 10)` stores the value 10 in the shared variable.
Atomic operations like Load and Store are essential for maintaining data consistency in concurrent programs. They provide a way to update shared variables without worrying about data corruption or other concurrency-related issues.
Bitwise Operations
The Go language provides atomic bitwise operations, which are useful for updating shared variables in a thread-safe way.
The AndInt32 and AndUint32 functions perform atomic bitwise AND operations on a pointer to an integer or unsigned integer, respectively, using a provided bitmask and return the old value.
When you need to update a shared variable, consider using the more ergonomic and less error-prone Int32.And or Uint32.And functions instead of the atomic bitwise operations.
The OrInt32, OrUint32, OrUint64, and OrUintptr functions perform atomic bitwise OR operations on a pointer to an integer, unsigned integer, unsigned 64-bit integer, or unsigned pointer, respectively, using a provided bitmask and return the old value.
Remember to use the more ergonomic and less error-prone Int32.Or, Uint32.Or, Uint64.Or, or Uintptr.Or functions when possible.
Pointer

A Pointer is an atomic pointer of type *T, and the zero value is a nil *T.
The Pointer type is used in conjunction with atomic operations like LoadPointer, StorePointer, and SwapPointer.
These operations, LoadPointer, StorePointer, and SwapPointer, are atomic and can be used to safely access and modify shared memory.
LoadPointer atomically loads the value at the address pointed to by addr.
StorePointer atomically stores the value val into the address pointed to by addr.
SwapPointer atomically stores the value new into the address pointed to by addr and returns the previous value at that address.
The more ergonomic and less error-prone alternatives to these operations are Pointer.Load, Pointer.Store, and Pointer.Swap.
And Int32 In
The bitwise AND operation is a fundamental part of programming, and Go's Int32 type has a convenient function for it: AndInt32.
AndInt32 atomically performs a bitwise AND operation on *addr using the bitmask provided as mask and returns the old value.

If you're working with Int32 values, you'll appreciate the atomicity of AndInt32, which ensures thread safety.
Consider using the more ergonomic and less error-prone Int32.And instead, as it's the recommended alternative.
In practice, AndInt32 is useful when you need to update a value based on a specific mask, and you want to retain the original value for logging or other purposes.
AndInt32 is available since Go 1.23.0.
OrUint32
OrUint32 is a function that atomically performs a bitwise OR operation on *addr using the bitmask provided as mask and returns the old value.
You can use OrUint32 for atomic operations, but keep in mind that it's recommended to use the more ergonomic and less error-prone Uint32.Or instead.
The OrUint32 function is part of the ingo package, which is a Go library for atomic operations. It's available in version 1.23.0 and later.
OrUint32 is similar to other bitwise operation functions like AndUint32, OrUint64, OrInt32, and OrUintptr, which all perform atomic operations on different types of data.

If you're working with Uint32 values, you can use OrUint32 to perform bitwise OR operations in a thread-safe way.
The OrUint32 function is useful for scenarios where you need to update a Uint32 value atomically, such as in a concurrent environment.
You can use OrUint32 in combination with other atomic operations, like Load and Store, to create more complex operations.
Package Overview
The atomic package in Go provides low-level atomic memory primitives that can be used to modify values without worrying about race conditions. It's a powerful tool for concurrent programming.
The package offers atomic operations on integers and pointers, which are performed without interruption. This ensures that operations before the atomic operation are observed before it, and operations after the atomic operation are observed after.
Here are some key features of the atomic package:
- Atomic Operations: These functions allow operations on integers and pointers to be performed atomically.
- Memory Ordering Guarantees: Atomic operations ensure proper memory ordering, guaranteeing that operations are performed in the order they are specified.
Overview
The atomic package is a powerful tool for concurrent programming in Go. It provides low-level atomic memory primitives that ensure operations are performed without fear of race conditions.

Atomic operations are the core of the package, allowing you to perform operations on integers and pointers atomically. For example, atomic.AddInt64 adds an integer to an int64 value atomically.
Memory ordering guarantees are also a key feature of the atomic package. This ensures that operations before an atomic operation are guaranteed to be observed before the atomic operation, and operations after the atomic operation are observed after.
The atomic package is a wrapper around numerics to enforce atomic access, making it a simple and efficient solution for concurrent programming.
Here are some key benefits of using the atomic package:
- Atomicity: Ensures that operations are performed without interference from other goroutines.
- Concurrency Safety: Allows multiple goroutines to safely access shared variables.
- Efficiency: Faster than mutex locking for simple read or write operations.
Source Files
When you're working with packages, you'll often need to access the source files that make them up. These files are the heart of the package, containing all the code and data that define its functionality.
The source files for a package are typically stored in a directory with the same name as the package. This is where you'll find all the individual files that make up the package, including Python modules, data files, and other resources.

A package can have multiple directories, each containing its own set of source files. This helps keep related files organized and makes it easier to find what you need.
The package directory structure is usually defined in a file called `__init__.py`. This file tells Python what files and directories should be included in the package.
Example Use Cases
Atomic operations in GoLang are incredibly useful for simple and low-level updates on primitive data types such as Integers and Pointers. This is because they provide thread-safe updates, eliminating the need for locks.
The atomic.Add* and atomic.Sub* functions are used to atomically increment or decrement the value of shared variables. For example, atomic.AddInt64 is used to safely increment a counter without race conditions.
Here are some examples of atomic operations in GoLang:
These atomic operations are faster and more efficient than traditional locking mechanisms, making them a great choice for simple updates on primitive data types.
Usage

The sync/atomic library is a powerful tool, but it can be easy to forget which variables need to be accessed atomically.
go.uber.org/atomic is a wrapper around the standard library that provides a safer and more convenient API. It preserves all the functionality of the standard library, so you can rely on it to get the job done.
The go.uber.org/atomic API is well-documented, so be sure to check it out for a complete specification of all the available functions and types.
Example 2: Safe Boolean Flag
In a concurrent setup, using atomic operations can help ensure that shared variables are updated safely. This is especially important when dealing with boolean flags that need to be set or checked by multiple goroutines.
A flag variable, like the one used in Example 2, can be an integer used as a boolean flag to signal between goroutines. This is a common pattern in concurrent programming.
The atomic.StoreInt32 function is used to set the flag variable, while the atomic.LoadInt32 function is used to check if the flag has been set. This ensures that the flag is updated atomically, preventing race conditions.
Here's a breakdown of the key components involved in this pattern:
- Flag Variable: An integer used as a boolean flag to signal between goroutines.
- WaitGroup: Used to wait for the completion of goroutines.
The first goroutine sets the flag after a delay, while the second goroutine waits for the flag to be set. This ensures that the second goroutine only proceeds once the flag has been signaled by the first goroutine.
Atomic read/write operations, like atomic.StoreInt32 and atomic.LoadInt32, are used to safely update the flag variable. This pattern ensures that the second goroutine waits until the first goroutine signals (by setting the flag).
Concurrency and Safety
Concurrency is a core part of the Go programming language, facilitated by goroutines that can be thought of as lightweight threads managed by the Go runtime.
To ensure concurrency safety, atomic operations are crucial to prevent data inconsistency and race conditions. An atomic operation either completes fully or doesn’t happen at all, leaving no intermediate state visible to other goroutines or threads.
Multiple goroutines accessing a shared variable at the same time can lead to data inconsistency, a situation known as Race Conditions. This highlights the importance of atomic operations in keeping the program consistent in case of multi-threading or concurrent programs.
Atomic operations are used for simple operations such as increment, decrement, store, load, compare & swap, and mutexes are used for complex operations. Functions like AddInt32, LoadUint64, and StorePointer provide atomic addition, load, and store operations on integers and pointers.
Here are some key features of atomic operations in Go:
- Atomic Operations: Functions like AddInt32, LoadUint64, StorePointer, etc., provide atomic addition, load, and store operations on integers and pointers.
- Memory Order Guarantees: Ensures that operations are performed in the order they are specified, which is crucial in concurrent programming.
- Efficiency: Atomic operations are faster than mutex locking, especially for simple read or write operations.
The atomic.AddInt32 function ensures that the increment operation on the counter is atomic, preventing race conditions. This allows multiple goroutines to safely increment the counter without interfering with each other.
Version History
The Go atomic package has a rich history that dates back to its initial release in Go 1.1. It was written by a single contributor, Rob Pike.
The package was first introduced to provide a simple and efficient way to update shared variables in a concurrent environment. This was a significant improvement over the existing methods, which were often prone to data corruption and other concurrency issues.
One notable update to the package was the addition of the `Load` function, which allows you to read the current value of an atomic variable. This was a significant addition, as it enables developers to inspect the state of an atomic variable without blocking its use by other goroutines.
1.1.0

In version 1.1.0, the CompareAndSwap function, also known as CAS, was introduced for float64 values. This function is an atomic compare-and-swap operation.
The CAS function was added to provide a way to atomically swap float64 values. It's an important addition for developers working with concurrent programming.
NewError creates a new Error, which is an atomic type-safe wrapper for error values. This is a useful tool for developers who need to handle errors in a thread-safe manner.
The CompareAndSwap function is deprecated, which means it's still available but might be removed in future versions.
1.10.0
In version 1.10.0, we don't have any specific information mentioned in the article sections. However, we can look at the versions that were mentioned to get an idea of what's new and improved.
The first version mentioned is 1.9.0, which introduced atomic operations for float64 values with the Swap function. This allows for safe and efficient swapping of float64 values.
The Swap function for Uintptr and UnsafePointer types was introduced in version 1.8.0, enabling atomic swaps for these types as well. Additionally, the Inc function was introduced for Uintptr values, allowing for atomic increments.
It's worth noting that some functions, such as CAS for time.Duration values, have been deprecated in favor of newer atomic operations. This highlights the importance of keeping up with the latest version and using the most modern and efficient atomic operations available.
Time Inv 1.9.0
Time Inv 1.9.0 brought a significant update to the Store function, which now stores passed time.Time atomically. This means that the Store function is now thread-safe.
The Store function's new atomic behavior makes it a reliable choice for storing time.Time values in concurrent environments.
Functions and Types
In Go, atomic operations provide a way to safely access shared variables in concurrent programs. The CompareAndSwap function is an example of this, specifically designed for time.Duration values.
This function allows for an atomic compare-and-swap operation, which is a fundamental building block for more complex concurrency primitives.
The Go atomic package offers a range of functions to help you manage shared variables, making it easier to write efficient and correct concurrent code.
Functions

The functions in Go's atomic package are designed to be efficient and safe for concurrent programming.
CompareAndSwap is an atomic compare-and-swap for time.Duration values, which is a useful feature for managing time-sensitive operations.
You can use the AddUint32 function to atomically add a delta to a pointer, and it returns the new value.
To subtract a signed positive constant value c from x, you can use the expression AddUint32(&x, ^uint32(c-1)).
The atomic package provides a range of functions for working with atomic values, including CompareAndSwapUint32 and SwapUint32.
These functions are designed to be more ergonomic and less error-prone than their raw equivalents.
You can use the Uint32 type as an atomic wrapper around a uint32 value, which provides a convenient way to work with atomic integers.
The Uint32 type also has a MarshalJSON method that encodes the wrapped uint32 into JSON.
The atomic package also provides functions for working with 64-bit integers, including AddUint64 and Uint64.
To decrement a Uint64 value, you can use the expression AddUint64(&x, ^uint64(0)).
The Uint64 type is an atomic wrapper around a uint64 value, and it provides a convenient way to work with atomic integers.
The Uint64 type also has a MarshalJSON method that encodes the wrapped uint64 into JSON.
Return

When you need to return a value from a function, it's essential to consider the type of value you're working with. For example, if you're working with an Int64, you'll want to use the Swap function to atomically swap the wrapped int64 and return the old value.
The Swap function is a powerful tool that ensures thread safety by allowing you to swap values without blocking other threads. This is particularly useful when working with large datasets or in high-traffic applications.
In some cases, you may need to swap a uint32 value. In this case, you can use the (*Uint32) Swap function, which works similarly to the Int64 Swap function. It atomically swaps the wrapped uint32 and returns the old value.
It's worth noting that the Int64 Swap function has some specific requirements. For instance, the Int64 must not be copied after first use. This is an important consideration to keep in mind when working with this function.
Best Practices and Tips
Atomic operations are low-level, so ensure they're really what you need before using them. This will save you from unnecessary complexity and potential issues down the line.
Beware of False Sharing, as it can become a bottleneck when multiple goroutines modify different variables stored close to each other in memory. This is a common pitfall in concurrent programming, so be mindful of your memory layout.
Testing is crucial when working with concurrent code, so always test thoroughly to ensure it behaves as expected under different conditions. This will help you catch any issues early on and prevent headaches later.
Here are some key takeaways to keep in mind:
Deprecation and Removal
The CAS function for time.Duration values has been deprecated since Go version 1.3.2.
You should use CompareAndSwap instead of CAS for float64 values, as it's been the recommended alternative since Go version 1.1.0.
Note that CompareAndSwap handles NaN values correctly, unlike the inbuilt operators, and allows a stored NaN to compare equal to a passed in NaN.
(*Float32) CAS Deprecated
The (*Float32) CAS has been deprecated since version 1.10.0.
It's been replaced by CompareAndSwap, which handles NaN values more correctly.
The CAS function had a major issue with NaN values, causing CompareAndSwap loops to block forever.
This is because NaN != NaN using Go's inbuilt operators, but CompareAndSwap allows a stored NaN to compare equal to a passed in NaN.
CompareAndSwap is an atomic compare-and-swap for float32 values, providing a more reliable alternative to CAS.
It's been available since version 1.10.0, making it a viable replacement for CAS.
Remove
Removing functionality is a crucial part of the deprecation process, and it's essential to do it correctly to avoid breaking existing code.
The Add function in the Int32 package is a good example of how removal can be done atomically, adding to the wrapped int32 and returning the new value.
In a similar fashion, removing a function should be done in a way that doesn't break existing code, and it's essential to provide a clear path for users to migrate to the new functionality.

The Int32 package's Add function returns the new value after adding, which is a key consideration when removing functionality to ensure that users can still access the old functionality in some way.
This approach helps to minimize the impact of removal on existing code and provides a clear migration path for users to upgrade to the new functionality.
Featured Images: pexels.com


