LinuxSir.cn,穿越时空的Linuxsir!

 找回密码
 注册
搜索
热搜: shell linux mysql
查看: 3066|回复: 0

Non-Blocking Algorithm - FIFO Queue

[复制链接]
发表于 2008-12-5 06:53:32 | 显示全部楼层 |阅读模式
原文: http://blog.chinaunix.net/u/8057/showart_1680274.html

Author: ZC Miao <>

Revision:

- 2008-12-04

   First revision.

=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
  Problem
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-


Provide a non-blocking algorithm that provides a FIFO Queue with the following
interface:

- void InitFIFO(void)

   FIFO queue initialization.

- bool Enqueue(DATA data)

   Push a data into the FIFO queue. Return TRUE if success, return FALSE only if
   the FIFO is full.

- bool Dequeue(DATA* pdata)

   Pull a data from the FIFO queue. Return TRUE if success, return FALSE only if
   the FIFO is empty.


=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
  Link-List Structure
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-


The algorithm introduced here is based on a link-list structure[1]. It's a
single link-list with head and tail pointers. It looks like:


  1.    +-------+     +-------+     +-------+     +-------+
  2.    |       |     |       |     |       |     |       |
  3.    |      -+---->|      -+---->|      -+---->|      -+---->NULL
  4.    |       |     |       |     |       |     |       |
  5.    +---^---+     +-------+     +-------+     +---^---+
  6.        |                                         |
  7.        |                                         |
  8.        |                                         |
  9.       HEAD                                      TAIL
复制代码


New data is pushed to the tail, and old data is pulled from head.


=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
  Dequeue Data, first thought
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-


To dequeue a data, first read the HEAD value, and use CAS to swap the HEAD
pointer to it's next pointer. And the old HEAD value is the data that
dequeued. To avoid ABA problem[3], we should use SLOTID_CAS which described in
the "Fix-size Slots Management"[2].


  1.    +-------+     +-------+     +-------+     +-------+
  2.    |       |     |       |     |       |     |       |
  3.    |      -+---->|      -+---->|      -+---->|      -+----> NULL
  4.    |       |     |       |     |       |     |       |
  5.    +-------+     +---^---+     +-------+     +---^---+
  6.                      |                           |
  7.                      |                           |
  8.                      |                           |
  9.                     HEAD                        TAIL
复制代码



=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
  Enqueue Data
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-


Enqueue operation is more complicated than dequeue operation, because it has to
change two state: the next pointer of the tail slot, and the tail pointer. If we
have MCAS(Multi CAS) or at least Double CAS[4], then the problem is easy to
solve. But since DCAS is not widely available in computer system, so a so-called
cooperative method is introduced firstly by Valois[5].

In cooperative method, enqueue operation first allocates a new slot, and sets
next pointer of tail slot to the new slot. Then set tail pointer to new
slot. All theses set operation use SLOTID_CAS, so the second step can fail, if
someone others enters in, and set the tail pointer. Which leaves the FIFO in a
intermediate state:


  1.    +-------+     +-------+     +-------+     +-------+
  2.    |       |     |       |     |       |     |       |
  3.    |      -+---->|      -+---->|      -+---->|      -+---->NULL
  4.    |       |     |       |     |       |     |       |
  5.    +---^---+     +-------+     +---^---+     +-------+
  6.        |                           |
  7.        |                           |
  8.        |                           |
  9.       HEAD                        TAIL
复制代码


To complete the intermediate state:

1. The next enqueue operation should try to move the TAIL forward, until the
    TAIL at the end, the enqueue operation should not change the TAIL slot next
    pointer.

2. The next dequeue operation should also try to move the TAIL forward when the
    head meets the tail pointer.


=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
  Algorithm
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-


This algorithm uses CAS version of "Fix-size Slots Management"[2].




  1. typedef struct
  2. {
  3.     DATA data;
  4.     SLOTID next;
  5. } Node;

  6. SLOTID head;
  7. SLOTID tail;

  8. void InitFIFO(void)
  9. {
  10.     Node *sentinel;

  11.     /* This algorithm relies on the fix-size slots management */
  12.     InitSlots();

  13.     /* Sentinel slots */
  14.     head = tail = GetSlot();
  15.     sentinel = DecodeSlotID(head);
  16.     sentinel->next = NULL_ID;
  17. }

  18. /*
  19.   Origin Algorithm:
  20.   E01: node = new node()
  21.   E02: node->value = value
  22.   E03: node->next.ptr = NULL
  23.   E04: loop
  24.   E05:   tail = Q->Tail
  25.   E06:   next = tail.ptr->next
  26.   E07:   if tail == Q->Tail
  27.   E08:     if next.ptr == NULL
  28.   E09:       if CAS(&tail.ptr->next, next, <node, next.count+1>)
  29.   E10:         break
  30.   E11:       endif
  31.   E12:     else
  32.   E13:       CAS(&Q->Tail, tail, <next.ptr, tail.count+1>)
  33.   E14:     endif
  34.   E15:   endif
  35.   E16: endloop
  36.   E17: CAS(&Q->Tail, tail, <node, tail.count+1>)
  37. */
  38. bool Enqueue(DATA data)
  39. {
  40.     SLOTID newid;
  41.     Node *newnode;
  42.     SLOTID oldtail;
  43.     Node *oldtailnode;
  44.     SLOTID nextid;
  45.     Node *nextnode;

  46.     /* E01 */
  47.     newid = GetSlot();
  48.     newnode = DecodeSlotID(newid);
  49.     if (newnode == NULL) return FALSE;
  50.     /* E02 */
  51.     newnode->data = data;
  52.     /* E03 */
  53.     newnode->next = NULL_ID;
  54.     /* E04 */
  55.     do {
  56.         /* E05 */
  57.         oldtail = tail;
  58.         oldtailnode = DecodeSlotID(oldtail);
  59.         /* E06 */
  60.         nextid = oldtailnode->next;
  61.         nextnode = DecodeSlotID(nextid);
  62.         /* E07 */
  63.         if (oldtail == tail)
  64.         {
  65.             /* E08 */
  66.             if (nextnode == NULL)
  67.             {
  68.                 /* E09 */
  69.                 if (SLOTID_CAS(&oldtailnode->next, next, newid))
  70.                 {
  71.                     /* E10 */
  72.                     break;
  73.                 /* E11 */
  74.                 }
  75.             }
  76.             /* E12 */
  77.             else
  78.             {
  79.                 /* E13 */
  80.                 SLOTID_CAS(&tail, oldtail, next);
  81.             /* E14 */
  82.             }
  83.         /* E15 */
  84.         }
  85.     /* E16 */
  86.     } while(1);
  87.     /* E17 */
  88.     SLOTID_CAS(&tail, oldtail, newid);

  89.     return TRUE;
  90. }

  91. /*
  92.   Origin Algorithm:
  93.   D01:  loop
  94.   D02:    head = Q->Head
  95.   D03:    tail = Q->Tail
  96.   D04:    next = head->next
  97.   D05:    if head == Q->Head
  98.   D06:      if head.ptr == tail.ptr
  99.   D07:        if next.ptr == NULL
  100.   D08:          return FALSE
  101.   D09:        endif
  102.   D10:        CAS(&Q->Tail, tail, <next.ptr, tail.count+1>)
  103.   D11:      else
  104.   D12:        *pvalue = next.ptr->value
  105.   D13:        if CAS(&Q->Head, head, <next.ptr, head.count+1>)
  106.   D14:          break
  107.   D15:        endif
  108.   D16:      endif
  109.   D17:    endif
  110.   D18:  endloop
  111.   D19:  free(head.ptr)
  112.   D20:  return TRUE
  113. */
  114. bool Dequeue(DATA *pdata)
  115. {
  116.     SLOTID oldhead
  117.     Node *oldheadnode;
  118.     SLOTID oldtail;
  119.     Node *oldtailnode;
  120.     SLOTID next;
  121.     Node *nextnode;

  122.     /* D01 */
  123.     do {
  124.         /* D02 */
  125.         oldhead = head;
  126.         oldheadnode = DecodeSlotID(oldhead);
  127.         /* D03 */
  128.         oldtail = tail;
  129.         oldtailnode = DecodeSlotID(oldtail);
  130.         /* D04 */
  131.         next = oldhead->next;
  132.         nextnode = DecodeSlotID(next);
  133.         /* D05 */
  134.         if (oldhead == head)
  135.         {
  136.             /* D06 */
  137.             if (headnode == tailnode)
  138.             {
  139.                 /* D07 */
  140.                 if (nextnode == NULL)
  141.                 {
  142.                     /* D08 */
  143.                     return FALSE;
  144.                 /* D09 */
  145.                 }
  146.                 /* D10 */
  147.                 SLOTID_CAS(&tail, oldtail, next);
  148.             }
  149.             /* D11 */
  150.             else
  151.             {
  152.                 /* D12 */
  153.                 *pdata = nextnode->data;
  154.                 /* D13 */
  155.                 if (SLOTID_CAS(&head, oldhead, next)
  156.                 {
  157.                     /* D14 */
  158.                     break;
  159.                 /* D15 */
  160.                 }
  161.             /* *D16 /
  162.             }
  163.         /* D17 */
  164.         }
  165.     /* D18 */
  166.     }
  167.     /* D19 */
  168.     PutSlot(oldhead);
  169.     /* D20 */
  170.     return TRUE;
  171. }
复制代码



In enqueue, E13 is the cooperative operation.

In dequeue, D10 is the cooperative operation.


=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
  Array Based Algorithm
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-


Valois also introduced a array based Non-Blocking FIFO algorithm[6], but it's
not feasible on real machine because it requires CAS on unaligned memory region.


=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
  Conclusion
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-


This article introduces a link-listed based Non-Blocking algorithm for FIFO
queue. It's based on the "Fix-size Slots Management"[2], and feasible on the
platforms that support CAS.

==================== Footnote ====================
[1]  Maged M. Michael Michael L. Scott, Simple, Fast, and Practical Non-Blocking
&nbsp;    and Blocking Concurrent Queue Algorithms,, Department of Computer Science
&nbsp;    University of Rochester, 1995.
[2]  ZC Miao, Non-Blocking Algorithm - Fix-size Slots Management, revision
&nbsp;    2008-12-04
[3]  ABA problem from Wikipedia
&nbsp;    http://en.wikipedia.org/wiki/ABA_problem
[4]  Double Compare And Swap from Wikipedia
&nbsp;    http://en.wikipedia.org/wiki/Double_Compare_And_Swap
[5]  J.D. Valois, Lock-Free Data Structures PhD Thesis, Rensselaer Polytechnic
&nbsp;    Institute, Department of Computer Science, 1995.
[6]  J.D. Valois, Implementing Lock-Free Queues
您需要登录后才可以回帖 登录 | 注册

本版积分规则

快速回复 返回顶部 返回列表