C Implementation of Michael-scott non-blocking queue (Lock-free) algorithm

Source: Internet
Author: User
Tags cas

The Michael-scott non-blocking queue algorithm, the Ms-queue algorithm, is 1 9 9 6 years by Maged. M. Michael and M. Scott proposed the most classical algorithms on the concurrent FIFO queue, and much of the current research on concurrent FIFO queues is based on this algorithm. On multi-core processors with shared memory, this compare-and-swap (CAS)-based algorithm is much better performing than previous lock-based algorithms and has been adopted by Java and contracted. Its main feature is to allow multi-threaded concurrent, non-intrusive access to the head and tail of the queue.

The Ms-queue algorithm relies on CAS atomic operations, CAS operations are related to processor architecture, and GCC has already provided built-in CAS-related APIs, see here.

bool __sync_bool_compare_and_swap (Type **ptr, type oldval, type newval, ...); /*  */If return true else return false if return oldval;   }

Along with the CAs API, it also includes an additional set of APIs that are self-increasing, self-reducing, and, or, non-, XOR, or atomic operations.

Type __sync_fetch_and_add (type *ptr, type value, ...);//M+nType __sync_fetch_and_sub (type *ptr, type value, ...);//M-nType __sync_fetch_and_or (type *ptr, type value, ...);//M|nType __sync_fetch_and_and (type *ptr, type value, ...);//M&nType __sync_fetch_and_xor (type *ptr, type value, ...);//m^nType __sync_fetch_and_nand (type *ptr, type value, ...);//(~m) &n/*corresponding pseudo-code*/{tmp= *ptr; *ptr op= value;returntmp;} {tmp= *ptr; *ptr = (~tmp) & value;returntmp }//NAND

There are many benefits of using this set of APIs, such as the self-increment and assignment operations in C/s + + are not atomic operations, and if a multi-threaded program needs to use global counters, the program needs to use locks or mutexes, which can cause some performance bottlenecks for higher concurrency programs. This set of APIs is much more efficient by using this set of API,GCC to guarantee the atomicity of assignment class operations by code at the assembly level, relative to the locks and mutexes involved in operating system calls and application-level synchronization.

Back to the Ms-queue lock-free (lock-free) queue. Although Ms-queue is already famous, but find a ready-made C implementation seems to be really not easy, C + + implementation has been here, is based on boost. Another is the implementation of a research group at Fudan University (here), but mainly for 64-bit machines, CAS primitives directly with the assembly instructions, feel that directly under the 32-bit or arm of the GCC compiler will be problematic. Since the usual project development is based on the GCC compiler or the arm's GCC, I have implemented a ms-queue for 32-bit machines with the GCC built-in CAS API.
Ms_queue.h:

/** * This file defines necessary the data structures to implement a lock-free FIFO * * queue. * * * * which is described in Michael and Scott's excellent paper appeared in PODC * * ': "Simple, Fast, and practical non-blocking and Blocking Conc Urrent Queue * * Algorithms " * * * * * Author:jingcheng Li <[email protected]> * **/#define__gnu_source#include<stdlib.h>#include<stdint.h>#defineCAS __sync_bool_compare_and_swaptypedefintData_type;typedefstructqueue_t queue_t; typedefstructpointer_t pointer_t; typedefstructnode_t node_t;structnode_t;structpointer_t {node_t*ptr;   uint32_t count; };structnode_t {data_type value; pointer_t next;};structqueue_t {pointer_t Head; pointer_t Tail;};voidInitialize (queue_t *q) {node_t*node =NULL; Node=malloc(sizeof(node_t)); Node->next.ptr =NULL; Q->head.ptr = Q->tail.ptr =node;}voidEnqueue (queue_t*Q, data_type value) {node_t*node =NULL;    pointer_t old_tail, tail, next, TMP; Node=malloc(sizeof(node_t)); Node->value =value; Node->next.ptr =NULL;  while(1) {Tail= q->Tail; Old_tail=tail; Next= tail.ptr->Next; /*tail may are changed in CAS after compare but before assign to Q->tail, * So the is incorrect:
if (CAS (uint64_t*) &q->tail, * (uint64_t*) &tail, * (uint64_t*) &old_tail)) This is correct: if (CAS (uint64_t*) &q->tail, * (uint64_t*) &tail, * (uint64_t*) &tail))*/ if(CAS (uint64_t*) &q->tail, * (Constuint64_t*) &tail, * (Constuint64_t*) &tail)) { if(Next.ptr = =NULL) {Tmp.ptr=node; Tmp.count= next.count+1; if(CAS (uint64_t*) &tail.ptr->next, * (Constuint64_t*) &next, * (Constuint64_t*) &tmp)) { Break; } } Else{tmp.ptr=next.ptr; Tmp.count= tail.count+1; CAS ((uint64_t*) &q->tail, * (Constuint64_t*) &tail, * (Constuint64_t*) &tmp); } }} tmp.ptr=node; Tmp.count= tail.count+1; CAS ((uint64_t*) &q->tail, * (Constuint64_t*) &tail, * (Constuint64_t*) &tmp);}intDequeue (queue_t *q, data_type*pvalue) {pointer_t old_head, head, tail, next, TMP; while(1) {Head= q->Head; Old_head=Head; Tail= q->Tail; Next= head.ptr->Next; /*head May was changed in CAS after compare but before assign to Q->head, * so this is incorrect: if (CAS (uint64_t*) &q->head, * (uint64_t*) &head, * (uint64_t*) &old_head)) This is correct: if (CAS (uint64_t*) &q->head, * (uint64_t*) &head, * (uint64_t*) &head))*/ if(CAS (uint64_t*) &q->head, * (Constuint64_t*) &head, * (Constuint64_t*) &head)) { if(Head.ptr = =tail.ptr) {if(Next.ptr = =NULL) { return 0; } tmp.ptr=next.ptr; Tmp.count= tail.count+1; CAS ((uint64_t*) &q->tail, * (Constuint64_t*) &tail, * (Constuint64_t*) &tmp); } Else { if(pvalue) {*pvalue = next.ptr->value; } tmp.ptr=next.ptr; Tmp.count= head.count+1; if(CAS (uint64_t*) &q->head, * (Constuint64_t*) &head, * (Constuint64_t*) &tmp)) { Break; } } } } Free(HEAD.PTR); return 1;}

TEST_QUEUE.C:

#include <stdio.h>#include<assert.h>#include"ms_queue.h"pthread_t a_id[Ten];p thread_t b_id[Ten];queue_t queue;void* Put (void*a) {    inti =0, J; intn = (int) A;  for(j = N10000000; j< (n+1)*10000000; J + +) {Enqueue (&queue, J); } printf ("put thread:%d exit\n", n);}void*Get(void*a) {    intv; intn = (int) A; intCNT =10000000;  while(cnt--)    {         while(0= = Dequeue (&queue, &v) {usleep ( -); }} printf ("get thread:%d exit\n", n);}intMain () {intI, J; Initialize (&queue); ASSERT (NULL!=queue.    HEAD.PTR); ASSERT (NULL!=queue.    TAIL.PTR);  for(i =0; I <Ten; i++) {pthread_create (&A_id[i], NULL, put, i); Pthread_create (&b_id[i], NULL,Get, i); }     for(i =0; I <Ten; i++) {pthread_join (a_id[i], NULL);    Pthread_join (B_id[i], NULL); } assert (0= = Dequeue (&queue, &j));}

C Implementation of the

Michael-scott non-blocking queue (Lock-free) algorithm

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.