Click here to Skip to main content
15,886,725 members
Articles / Programming Languages / C#
Article

Lock Free Queue implementation in C++ and C#

Rate me:
Please Sign up or sign in to vote.
3.09/5 (20 votes)
23 Feb 2008CPOL4 min read 233.9K   5.1K   79   48
Lock Free Queue implementation in C++ and C#

Introduction

This article demonstrates implementation of a "lock free" queue in C# and C++. Lock free queues are typically used in multi-threaded architectures to communicate between threads without fear of data corruption or performance loss due to threads waiting to use shared memory. The goal of the article is to familiarize readers with lock free queues and provide a starting point to writing wait-free production architectures. It should be noted that using lock-free queues is only the beginning - a true lock free architecture use a lock free memory allocator. Implementing lock free memory allocators is beyond the scope of this article however.

Background

Recent developments in CPU architecture has necessitated a change in thinking in high performance software architecture - multithreaded software. Communication between threads in multithreaded architecture has traditionally been accomplished using mutexes, critical sections, and locks. Recent research in algorithms and changes in computer architecture has led to the introduction of "wait free", "lock free", or "non-blocking" data structures. The most popular and possibly the most important is the queue, a First In First Out (FIFO) data structure.

The key to the majority of lock free data structures is an instruction known as Compare and Swap (CAS). The flow chart below describes what Compare and Swap does. For the assembler coders out there the instruction is named CMPXCHG on X86 and Itanium architectures. The special thing about this instruction is that it is atomic- meaning that other threads\processes cannot interrupt until it is finished. Operating Systems use atomic operations to implement sychronization - locks, mutexes, semaphores, and critical sections.

Image 1

My code draws on research by Maged M. Michael and Michael L. Scott on non-blocking and blocking concurrent queue algorithms. In fact, an implementation of their queue is now part of the Java concurrency library. Their paper demonstrates why the queue is "linearizable" and "lock free". An implementation of the code in C is available here in tar.gz format. The idea is that pointers are reference counted and checked for consistency in a loop. The reference count is meant to prevent what is referred to the "ABA problem" - if a process or threads reads a value 'A' then attempts a CAS operation, the CAS operation might succeed incorrectly if a second thread or process changes value 'A' to 'B' and then back again to 'A'. If the "ABA" problem never occurs the code is safe because:

  1. The linked list is always connected.
  2. Nodes are only inserted at the end of the linked list and only inserted to a node that has a NULL next pointer.
  3. Nodes are deleted from the beginning of the list because they are only deleted from the head pointer and head always points to the first element of the list.
  4. Head always points to the first element of the list and only changes it's value atomically.

If CAS or similar instructions are not available I suggest using the STL queue or a similar queue with traditional sychronization primitives. Michael and Scott also present a "two lock" queue data structure.

Using the code

UML Diagram of sourceLockFreeQueue.jpg

Using the code provided with this article is simple.

  • C++ class declaration template< class T > class MSQueue
    • Include the "lockfree.h" file. #include "LockFreeQ.h"
    • Declare C++ queues like this: MSQueue< int > Q;
    • Add items to the queue: Q.enqueue(i);
    • Remove items from the queue: bool bIsQEmpty = Q.dequeue(i); dequeue returns false if the queue is empty and the value of i would be undefined.
  • C# class declaration namespace Lockfreeq { public class MSQueue<t /> {
    • Include the Lock Free Queue DLL: using Lockfreeq;
    • Declare a C# queue: MSQueue< int > Q = new MSQueue< int >();
    • Add items to the queue: Q.enqueue(i);
    • Remove items from the queue: bool bIsQEmpty = Q.dequeue(ref i); dequeue returns false if the queue is empty and the value of i would be undefined.

Points of Interest

Did you know that Valve Software (makers of Half-Life computer game) have switched to a wait free architecture?

History

  • This is my first article! (30.1.2008)
  • Revised 21:00 30.1.2008 (Thanks to the early commenters for spotting those mistakes!)
  • Noted need for memory allocator. 31.1.2008
  • Revision to correct inaccuracy with regards to Java Concurrency library.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Web Developer EMC Corporation
United States United States
Idaho Edokpayi is a Consultant working in EMC's Microsoft Practice specializing in WSS 3.0, MOSS 07, .NET, and ASP.NET technology. He also likes to write C++, Win32/64 API, and DirectX code in his spare time.

Comments and Discussions

 
GeneralMy vote of 1 Pin
m1o210-Sep-14 23:29
m1o210-Sep-14 23:29 
Questionbetter than boost spsc_queue? Pin
Oleg Vazhnev29-Aug-14 9:17
Oleg Vazhnev29-Aug-14 9:17 
QuestionMSQueue crash problem Pin
adolfsuccess6-Jun-14 3:44
adolfsuccess6-Jun-14 3:44 
GeneralMy vote of 1 Pin
Cygon424-Jun-12 22:26
Cygon424-Jun-12 22:26 
GeneralMy vote of 1 Pin
sogard8321-Sep-10 10:03
sogard8321-Sep-10 10:03 
GeneralRe: My vote of 1 Pin
wwwllg24-Apr-12 1:52
wwwllg24-Apr-12 1:52 
GeneralMy vote of 1 Pin
MatthysDT18-Jul-10 23:49
MatthysDT18-Jul-10 23:49 
GeneralMy vote of 1 Pin
smek215-May-10 6:19
smek215-May-10 6:19 
QuestionRe: My vote of 1 Pin
LoaderAll13-Jun-10 23:23
LoaderAll13-Jun-10 23:23 
GeneralMy vote of 1 Pin
Bharat Karia5-Feb-10 7:38
Bharat Karia5-Feb-10 7:38 
GeneralMy vote of 1 Pin
AAO29-Jul-09 18:10
AAO29-Jul-09 18:10 
Generalstress test core Pin
fly_horse8-May-09 2:21
fly_horse8-May-09 2:21 
GeneralC# Test Crashing Pin
irares16-Apr-09 5:11
irares16-Apr-09 5:11 
Generalstress test not working Pin
hagai_sela28-Jul-08 1:08
hagai_sela28-Jul-08 1:08 
GeneralRe: stress test not working Pin
cool_man_from_Russia23-Mar-09 22:25
cool_man_from_Russia23-Mar-09 22:25 
GeneralHello Pin
nidal1020028-Apr-08 6:38
nidal1020028-Apr-08 6:38 
GeneralStill a bug. [modified] Pin
Patrick Twohig7-Apr-08 14:25
Patrick Twohig7-Apr-08 14:25 
Regarding the Michael And Scott lock free queue:

Separating the CAS into separate operations is definitely a bug. In Scott's sample implementation, he shows a CAS function written in assembly that uses a single word compare/swap. In his proof-of-concept code he mocks memory allocation by using a short as his pointer and another short as his counter. His CAS algorithm is implemented as an sc (store/conditional) MIPS instruction written as inlined assembly. This is necessary because the allocator can (and will) recycle memory addresses. If you could assume that the allocator, for the life of the entire queue, will never recycle and address then the pointer tag is not necessary. Realistically, that is not practical. So the pointers are tagged to ensure that if one node was dequeued (and possibly re-enqueued with the same memory address) that it will skip that node. With this implementation, the ABA problem could still occur. Because it's possible to atomically write the pointer to memory, but then its tag lags behind by a few instructions. Not to mention the copying of the objects from scope to scope, also can be potentially racy.

What you need to do is use a double compare/swap which is available on Vista and Xbox360 as InterlockedCompareExchange64. On Xbox360 it's implemented using locked reads/writes and on 32 bit windows is implemented as cmpxchg8 (yes, the infamous F00F bug instruction). The downside to using the InterlockedCompareExchange64 is that you must cast the pointer/tag pair to a LONG64 which is technically undefined behavior. This can cause a failure if the memory isn't aligned on an 8 byte boundary, which is the natural alignment requirement for a long64. I've used a struct pair and a union of a long64, and that seems to do the trick. To rectify the alignment, you'll need to use __declspec( align(8) ) if you don't use a union to ensure that the compiler will align the pair of values properly. Also, if InterlockedCompareExchange64 isn't available, then you've gotta roll your own in assembly. If you do that, it's rather simple but be sure to follow the compiler's ABI.

It is important to also note that alignment is important. On most architectures (such as the PPC based Xbox360) the processor will set an exception flag when unaligned memory is accessed and the process causes a bus error, or in my case the console just locks up, the game quits and everybody stops having fun. On Intel architecture, it's a little different. Intel processors will set the exception flag, but then the exception is handled by replacing the single read/write instructions in the pipeline with a series of instructions to correct the erroneous bus transaction. Once that happens, the cmpxchg8 instruction is no longer atomic and we're still left with a race condition.

modified on Monday, April 7, 2008 9:10 PM

GeneralRe: Still a bug. Pin
Idaho Edokpayi5-May-08 15:36
Idaho Edokpayi5-May-08 15:36 
QuestionIs there still a chance of bug? Pin
dhbid26-Feb-08 22:00
dhbid26-Feb-08 22:00 
AnswerRe: Is there still a chance of bug? Pin
Idaho Edokpayi27-Feb-08 2:35
Idaho Edokpayi27-Feb-08 2:35 
GeneralRe: Is there still a chance of bug? Pin
LanceDiduck18-Apr-08 9:30
LanceDiduck18-Apr-08 9:30 
GeneralJava Concurrency Library Pin
Mike O'Neill21-Feb-08 14:09
Mike O'Neill21-Feb-08 14:09 
GeneralRe: Java Concurrency Library Pin
Idaho Edokpayi23-Feb-08 14:45
Idaho Edokpayi23-Feb-08 14:45 
QuestionCritical Section on the Stack? How Can It Provide Protection/Synchronization? Pin
Mike O'Neill20-Feb-08 15:11
Mike O'Neill20-Feb-08 15:11 
AnswerRe: Critical Section on the Stack? How Can It Provide Protection/Synchronization? Pin
Idaho Edokpayi23-Feb-08 14:48
Idaho Edokpayi23-Feb-08 14:48 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.