# Can Reordering of Release/Acquire Operations Introduce Deadlock?

DevFeed: [Can Reordering of Release/Acquire Operations Introduce Deadlock?](<https://devfeed.tech/articles/can-reordering-of-release-acquire-operations-introduce-deadlock-21011.md>)

Original publisher: [Read original article](<https://preshing.com/20170612/can-reordering-of-release-acquire-operations-introduce-deadlock>)

Author: Jeff Preshing

Published: 2017-06-12T11:34:00Z

Content type: article

Language: en

Sources: [Jeff Preshing](<https://devfeed.tech/sources/jeff-preshing.md>)

Topics: [C++](<https://devfeed.tech/topics/c-plus-plus.md>), [Deadlock](<https://devfeed.tech/topics/deadlock.md>), [Concurrency](<https://devfeed.tech/topics/concurrency.md>), [Concurrent Programming](<https://devfeed.tech/topics/concurrent-programming.md>), [Programming](<https://devfeed.tech/topics/programming.md>), [cpu](<https://devfeed.tech/topics/cpu.md>), [x86](<https://devfeed.tech/topics/x86.md>)

Tags: [architectures](<https://devfeed.tech/tags/architectures.md>), [c-plus-plus](<https://devfeed.tech/tags/c-plus-plus.md>), [code](<https://devfeed.tech/tags/code.md>), [compiler](<https://devfeed.tech/tags/compiler.md>), [cpu](<https://devfeed.tech/tags/cpu.md>), [deadlock](<https://devfeed.tech/tags/deadlock.md>), [implementation](<https://devfeed.tech/tags/implementation.md>), [lock-free](<https://devfeed.tech/tags/lock-free.md>), [memory](<https://devfeed.tech/tags/memory.md>), [synchronization](<https://devfeed.tech/tags/synchronization.md>), [thread](<https://devfeed.tech/tags/thread.md>), [x86](<https://devfeed.tech/tags/x86.md>)

## AI overview

This article examines whether compiler or CPU reordering of C++ release and acquire operations can introduce deadlock when the operations implement spinlocks. It explains the interaction between memory-ordering rules, two threads acquiring locks in opposite orders, and a C++ standard rule concerning the visibility of values assigned by atomic or synchronization operations.

## Source excerpt

I wasn't planning to write about lock-free programming again, but a commenter named Mike recently asked an interesting question on my Acquire and Release Semantics post from 2012. It's a question I wondered about years ago, but could never really reconcile until (possibly) now. A quick recap: A read-acquire operation cannot be reordered, either by the compiler or the CPU, with any read or write operation that follows it in program order. A write-release operation cannot be reordered with any read or write operation that precedes it in program order. Those rules don't prevent the reordering of a write-release followed by a read-acquire. For example, in C++, if A and B are std::atomic<int>, and we write: A.store(1, std::memory_order_release); int b = B.load(std::memory_order_acquire); ...the compiler is free to reorder those statements, as if we had written: int b = B.load(std::memory_order_acquire); A.store(1, std::memory_order_release); And that's fair. Why the heck not? On many architectures, including x86, the CPU could perform this reordering anyway. Well, here's where Mike's question comes in. What if A and B are spinlocks? Let's say that the spinlock is initially 0. To lock it, we repeatedly attempt a compare-and-swap, with acquire semantics, until it changes from 0 to 1. To unlock it, we simply set it back to 0, with release semantics. Now, suppose Thread 1 does the following: // Lock A int expected = 0; while (!A.compare_exchange_weak(expected, 1, std::memory_order_acquire)) { expected = 0; } // Unlock A A.store(0, std::memory_order_release); // Lock B while (!B.compare_exchange_weak(expected, 1, std::memory_order_acquire)) { expected = 0; } // Unlock B B.store(0, std::memory_order_release); Meanwhile, Thread 2 does the following: // Lock B int expected = 0; while (!B.compare_exchange_weak(expected, 1, std::memory_order_acquire)) { expected = 0; } // Lock A while (!A.compare_exchange_weak(expected, 1, std::memory_order_acquire)) { expected = 0; } // Unlock A A.