Anda di halaman 1dari 40

Effo Design - Lock-free Queue and Ring Buffer

Lock-free Queue and Ring Buffer

Rev 0.5

Author: Pat Huang, Effo Core


Oct 2009

1
Copyright © 2008,2009 The Effo Core project. All rights reserved.
Effo Design - Lock-free Queue and Ring Buffer

Effo Core Project Documentation Copyright & License Notices


Rev 0.3

Copyright (c) 2008,2009 The Effo Core project.


All rights reserved.

This document is a part of the Effo Core project, and is copyrighted by Pat Huang and/or others who
actually wrote it.

Redistribution and use in 'plain source' form (i.e. ASCII text) and 'compiled' forms (HTML, PDF, RTF
and so forth) with or without modification, are permitted provided that the following conditions are
met:

1. Redistributions of 'plain source' form must retain the above copyright notice, this list of
conditions and the following disclaimer as the first lines of this file unmodified.

2. Redistributions in compiled form (transformed to other DTDs, converted to PDF,


PostScript, RTF and other formats) must reproduce the above copyright notice, this list of
conditions and the following disclaimer in the documentation and/or other materials
provided with the distribution.

THIS DOCUMENTATION IS PROVIDED BY THE EFFO CORE PROJECT "AS IS" AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE EFFO CORE PROJECT BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
ANY WAY OUT OF THE USE OF THIS DOCUMENTATION, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

Contact the Effo Core project


<http://effocore.googlecode.com> or <http://code.google.com/p/effocore>

2
Copyright © 2008,2009 The Effo Core project. All rights reserved.
Effo Design - Lock-free Queue and Ring Buffer

Table of Content
Lock-free Queue and Ring Buffer..................................................................................1
1. Document Revision History........................................................................................ 5
2. Documents and Code URL.........................................................................................5
3. What will we Design..................................................................................................5
4. The Effo Project..........................................................................................................6
4.1 Introductions...................................................................................................... 6
4.2 Divide and Rule.................................................................................................7
5. Base LIFO...................................................................................................................7
5.1 Single-linked List...............................................................................................8
5.2 Creator............................................................................................................... 8
5.3 Node..................................................................................................................9
5.4 Head and Push-Front.......................................................................................11
5.5 Front and Pop-Front.........................................................................................13
5.6 Coding Example............................................................................................... 13
5.7 To-do Next.......................................................................................................13
6. Atomic Operations...................................................................................................13
6.1 i++ is not Atomic............................................................................................13
6.2 Get Atomic Primitive........................................................................................14
6.3 Compare with pthread Lock.............................................................................15
6.4 Test Results...................................................................................................... 16
6.5 sync_plc_none and efao Polices......................................................................17
7. sync_queue.............................................................................................................. 20
7.1 Tail, List Head and Queue Head.......................................................................20
7.2 front() and push()............................................................................................21
7.3 pop()................................................................................................................ 22
7.4 Coding Example............................................................................................... 23
8. Base Ringed............................................................................................................. 24
8.1 The Template...................................................................................................24
8.2 The Stuff..........................................................................................................25
8.3 The Snapshot................................................................................................... 26
8.4 The Operations................................................................................................27
8.5 How a Ringed Buffer Works.............................................................................27
8.6 SizeUsed and SizeFree.....................................................................................28
8.7 CanPush() and CanPop()..................................................................................29
8.8 Quick Accessing Host from a Guest.................................................................29
9. Ring Buffer............................................................................................................... 30
9.1 push()..............................................................................................................30
9.2 pop()................................................................................................................ 32
9.3 The Wrapper ring_buffer..................................................................................33
9.4 ring_buffer Code Example...............................................................................34
10. Ring Queue............................................................................................................34
10.1 push()............................................................................................................34

3
Copyright © 2008,2009 The Effo Core project. All rights reserved.
Effo Design - Lock-free Queue and Ring Buffer

10.2 pop()..............................................................................................................35
10.3 The Wrapper ring_queue...............................................................................36
11. Policy-Based Dynamic-size and Fixed-size.............................................................37
11.1 IRingQ<DYNA>..............................................................................................37
11.2 IRingQ<STAT>...............................................................................................38
11.3 IRingBuf<STAT>............................................................................................38
11.4 IRingBuf<DYNA>...........................................................................................39
12. Support, Helps and Suggestions............................................................................39

4
Copyright © 2008,2009 The Effo Core project. All rights reserved.
Effo Design - Lock-free Queue and Ring Buffer

1. Document Revision History

Revision Date Authors Remark


0.1 & 0.2 Aug 2009 and Pat Huang Document created
earlier
0.3 Aug 2009 Pat Huang Moved to the Effo Core from Effo Project.
0.4 Sep 05, 2009 Pat Huang Added section Support, Helps and
Suggestions
0.5 Oct 07, 2009 Pat Huang Document cleanup

2. Documents and Code URL

Please note if you downloaded this document from the Effo Project site(s)
http://code.google.com/p/effo or
http://sourceforge.net/projects/effo
then next time please go to the Effo Core project site to download newer revision(s) of this
document, because the Effo Core project is different from the Effo Project.
The related documents and code could be downloaded from the Effo Core project site
http://effocore.googlecode.com or
http://code.google.com/p/effocore
To be sure always visit above site to get the latest code and documents of the Effo Core project.
Please visit the project for more other references.

3. What will we Design

We’ll design following generic types in this document


Name Type Dynamic or Descriptions
Fixed Size
lifo_list LIFO Dynamic A base type of sync_queue lock-free type; doesn’t
support lock-free itself.
sync_queue FIFO Dynamic A Linked List based FIFO type, also be a type to build
class IRingQ<DYNA> up; either is lock-free or total-
free.
ring_buffer Ring Buffer Fixed A Ring Buffer type, also be a type to build class
IRingBuf<STAT> up; either is lock-free or total-free.
ring_queue FIFO Fixed A Ring FIFO type, also be a type to build class
IRingQ<STAT> up; either is lock-free or total-free.
IRingQ<DYNA> FIFO Dynamic It is sync_queue based and lock-free only.
IRingQ<STAT> FIFO Fixed It is ring_queue based and lock-free only.
IRingBuf<STAT> Ring Buffer Fixed It is ring_buffer based and lock-free only.
IRingBuf<DYNA> Ring Buffer Dynamic A snyc_queue based Ring Buffer type; lock-free only.

Notes:
Lock-free: no lock/mutex needed but atomic operation is required
Doesn’t support lock-free: no atomic operations used in the type implementation
Total-free: neither lock/mutex nor atomic operation used so all operations are not atomically

5
Copyright © 2008,2009 The Effo Core project. All rights reserved.
Effo Design - Lock-free Queue and Ring Buffer

Lock-free only: atomic operations used in the type implementation


Related code:
Name Project Source Code
lifo_list Effo Core codebase/addons/inl/include/cont_inl.h
sync_queue Effo Core codebase/addons/inl/include/cont_inl.h
ring_buffer Effo Core codebase/addons/inl/include/ringed_inl.h
ring_queue Effo Core codebase/addons/inl/include/ringed_inl.h
IRingQ<DYNA> Effo Addon codebase/addons/lockfree/include
IRingQ<STAT> Effo Addon codebase/addons/lockfree/include
IRingBuf<STAT> Effo Addon codebase/addons/lockfree/include
IRingBuf<DYNA> Effo Addon codebase/addons/lockfree/include

See reference document Effo Core References of the Effo Core for Effo header files naming
convention such as *_inl.h or *_i.h etc. The Effo Core References could be download from the Effo
Core project site, see section Document and Code URL.

4. The Effo Project

4.1 Introductions

The Effo Project is an Open Source project targets at C/C++ software system architectural design
and practical library development based on the design.
In fact Effo has other goals as below, and the Effo Framework, Effo Core and Effo Add-ons may
cover some items of them.
1. Effo Kits and Effo Libs/Components, or Effo Add-ons,
Add-ons, providing lots of C/C++ helpers,
libraries and kits to developers for quick applications and modules developing. For
example, multi-threading and network messaging.
2. Effo Framework,
Framework, the development platform on which C/C++ developers only need to
modify or add little code when developing their applications and modules; known as
Effortless Development or Effo Code Samples Driven Development.
Development.
3. Effo Utilities,
Utilities, Makefile auto-generating tool, Code auto-generating tools, release
generating and clean tools, memory test utility, SSO Burst tool, C9B.M. utility and other
useful tools.
4. Effo Kernels,
Kernels, some special-purpose kernels, i.e. Linux kernel variants w/o virtual memory
management, a kernel-only system (means no shell/user space), or kernel supports C++.
5. Effo Tiny, Effo Portable and Effo Compact,
Compact, some special-purpose OS and Linux
distributions, i.e. small Linux, portable and bootable multi-OS (2, 3 or 5) on 500M or 1G or
2G USB flash.
6. Effo Design, Effo Manual and Effo References,
References, not only help user understanding and using
Effo, but also provide generic C/C++ concepts, technique, design and development
references.
7. Effo Research, such as Highly-concurrent Programming, Code Samples Driven
Development, Parallel Computing and Distributed Computing.
8. Others…

Lots of works need to be done. More things to do, concerns of copyright and license become more
too.

6
Copyright © 2008,2009 The Effo Core project. All rights reserved.
Effo Design - Lock-free Queue and Ring Buffer

As you know that one important character of Open Source is reuse. When you reuse something,
troubles come too in some cases. For example, if reuse copyleft source (i.e., GPLed) in the
development of a software, then you’d make the source of the whole software open. This
sometimes prevents developers from reusing excellent free software and Open Source software.

4.2 Divide and Rule

At the beginning of the Effo Project, we made a mistake that there’re 2 project sites; the main site is
on Google Code, http://effo.googlecode.com, and a backup is on SourceForge,
http://effo.sourceforge.net.
So the project need to maintain two SVNs which controls the same source code, and this causes
many problems. You can image that.
Because of the concerns of copyright and license, and the above problem, the Effo Project was then
divided into some projects, that are, Effo Member Projects. The original Effo sites (Effo@GoogleCode
and Effo@SourceForge) were made as the portals of the Effo Project now.
Especially the http://effo.sourceforge.net became the Effo Home page.

Now, Effo@GoogleCode and Effo@SourceForge will not update Effo source code anymore. All source
code was relocated to the Effo member projects listed as below:
1. Effo Core,
Core, http://effocore.googlecode.com, the Core of Effo, MIT license
2. Effo Addon,
Addon, http://effoaddon.googlecode.com, Add-ons for Effo, MIT license
3. Effo NetMsg,
NetMsg, http://effonetmsg.googlecode.com, Network & Messaging, MIT license
4. Effo Devel,
Devel, http://effodevel.googlecode.com, the Effo Framework, or Effo Development
Platform, MIT license
5. Effo GPLed,
GPLed, http://effogpled.googlecode.com, GPL licensed add-ons and applications like
memory test, drivers etc.

One reason that we are doing so:


All developers could use Effo more freely, e.g. you could reuse most Effo source (MIT licensed) in any
place, including proprietary software or commercial software (so many developers will come across such a
situation like "want to reuse Open Source software but my boss doesn't like to deliver the source code to
the customer so I cannot use GPLed code"). Thus also make your software development safe too.
Of course Effo always encourages that all software source should be open to make life easy. Once
Open Source developers fell free, the end users would have much more high quality free software
and Open Source software.
But some developers maybe want to write GPLed code only; s/he also could just go to the Effo
GPLed project to do the job.

5. Base LIFO

You may use fixed-size array based queue, or linked list based queue. Here discuss linked list based
first.

7
Copyright © 2008,2009 The Effo Core project. All rights reserved.
Effo Design - Lock-free Queue and Ring Buffer

Download the design document Effo Design - XLnk, a Generic Linked List in C from the Effo Core
project (see section Document and Code URL of the document) for more Linked List discussion.

5.1 Single-linked List

There’re many kinds of lists, for example, single-linked, doubly linked, circular-linked, and so on.
Single-linked, 3 nodes

1 2 3
Head
Pointer

Above is a single-linked list.


In general for list based LIFO/FIFO, single-linked list is enough, so we start this design document
from single-linked list as shown in above figure.
You might make the list looks like following:

Single-linked, 3 nodes

Head 1 2 3
Pointer

Single-linked LIFO, 3 nodes too

Head 3 2 1
Pointer

5.2 Creator

If write C++, you’ll take advantages of type-strict, namespace and template to design a generic
LIFO:
template
<
class t,
template<class> class node_creator = creator_new
>
class lifo_list
See codebase/addons/inl/include/cont_inl.h of source tree of the Effo Core.
The template template creator should support a create() and a Destroy(), see
codebase/builtin/include/impl/effo_creator.h of source tree of the Effo Core for the examples:
template<class t>
struct creator_new {
static t *create(void)
{
return new t;
}
static void Destroy(t *p)

8
Copyright © 2008,2009 The Effo Core project. All rights reserved.
Effo Design - Lock-free Queue and Ring Buffer

{
delete p;
}
};
This creator uses new() and delete(), and creator_new will be the default creator. By Effo
convention, name of a destroy function will always be Destroy(), not destroy(). Of couse if you like
the later, just make a change on above code.
Optionally, we may use another creator
template<class t>
struct creator_nofail {
static t *create(void)
{
t *ret;
while (NULL == (ret = (t*)malloc
(t*)malloc(sizeof
(sizeof *ret))) {
effo_msleep(1);
}
ret = new(ret) t;
return ret;
}
static void Destroy(t *p)
{
delete p;
}
};
In above code, you’ll note 2 things, malloc() call and new() placement.
It uses malloc() when allocating memory for an object instead of new().
If you try to new() an object or new()[] an array when the load of a system is very heavy and there’s
not enough memory, your program might abort, especially when it was built up as a debug version;
but malloc() won’t.
So use malloc() you’ll get an opportunity to correct something at runtime. Besides malloc(), the
create() won’t return until it get the memory requested.
That’s why I give a name creator_nofail.
new() placement will construct your malloc()ed object.

Selectable-creator is a Policy-Based Design Pattern.


Here is a suggestion
Basically don't use new()/delete() directly, but use creators instead.

5.3 Node

After creator introduction, let’s see the list node now


template
<
class t,

9
Copyright © 2008,2009 The Effo Core project. All rights reserved.
Effo Design - Lock-free Queue and Ring Buffer

template<class> class node_creator = creator_new


>
class lifo_list
{
public:
struct node {
node *next;
t val_;
inline void set(node *link, t &p)
{
next = link;
val_ = p;
}
inline t &val(void) { return val_; }
};

“struct node” is an internal type of the “class lifo_list”, say class-scoped or lifo_list namespace-
scoped.
The node is so simple that it only has a NEXT pointer and a data. With C++ template, it is enough.
In C++ you’ll see lots following code here and there

public:
typedef efusz_t size_type;
typedef node node_type;
typedef node_type* node_pointer;
typedef node_creator<node_type> node_creator_type;

For efusz_t, see the reference document Effo Core References or the design document Effo Design -
XLnk, a Generic Linked List in C of the Effo Core project for details.
To do so, you’d take advantage of namespace-scoped or class-scoped.
For example, here’s a simple class
class List
{
public:
struct node { node *next, *prev; };

You may write some code like
List::node *pNode = pInput->next;

But if someone changed the List
class List
{
public:
struct list_node { list_node *next, *prev; };

10
Copyright © 2008,2009 The Effo Core project. All rights reserved.
Effo Design - Lock-free Queue and Ring Buffer


You should revise your code accordingly.
If the List declared the node type using typedef like
class List
{
public:
struct node { node *next, *prev; };
public:
typedef node node_type;

then you could write the code as following
List::node_type *pNode = pInput->next;

and it will be always correct even if in List the “struct node” was renamed to “struct list_node”.
In List only the typedef line needs to be revised this way
struct list_node { list_node *next, *prev; };
public:
typedef list_node node_type;
In another place one should use typedef to reduce key-striking efforts
namespace Mapping {
map<unsigned long, pair<unsigned short, unsigned char*> > m_oMaps;

map<unsigned long, pair<unsigned short, unsigned char*> >::iterator it =
m_oMaps.find(MY_KEY);
pair<unsigned short, unsigned char*> oVal = it->second;

you’d better revise it as below
namespace Mapping {
typedef unsigned long key;
typedef pair<unsigned short, unsigned char*> value;
typedef map<key, value > mapping;
mapping m_oMaps;

mapping::iterator it = m_oMaps.find(MY_KEY);
value oVal = it->second;

5.4 Head and Push-Front

We have already node_pointer data type for this list, now give the Head

protected:
node_pointer m_head;
The Head is the only data member of the list.

11
Copyright © 2008,2009 The Effo Core project. All rights reserved.
Effo Design - Lock-free Queue and Ring Buffer

For a LIFO, we always insert a node just after the Head to let the new node be the first node of the
list, say the Push-Front operation.

public:
efinline iterator push_front(t &p)
{
node_pointer ret = node_add(m_head, p, true);
if (NULL != ret) {
m_head = ret;
}
return iterator(ret);
}
The efinline is not interesting at all however in some case it is useful.
When EFINLINE was specified to the compiler command by -DEFINLINE, efinline is exactly inline
keyword, otherwise it is nothing.
The result of using Effo EFINLINE option or not is shown as the examples below
Example 1. EFINLINE was defined using -DEFINLINE.
[pat@localhost devel] ll rls/all_applet_s/bin/
-rwxrwxr-x 1 pat pat 677060 2009-06-18 05:04 rls/all_applet_s/bin/effomem86
-rwxrwxr-x 1 pat pat 7833447 2009-06-18 05:09 rls/all_applet_s/bin/effomem86D
-rwxrwxr-x 1 pat pat 677060 2009-06-18 05:04 rls/all_applet_s/bin/getcpuinfo
-rwxrwxr-x 1 pat pat 7833447 2009-06-18 05:09 rls/all_applet_s/bin/getcpuinfoD
-rwxrwxr-x 1 pat pat 677060 2009-06-18 05:04 rls/all_applet_s/bin/getmeminfo
-rwxrwxr-x 1 pat pat 7833447 2009-06-18 05:09 rls/all_applet_s/bin/getmeminfoD
-rwxrwxr-x 1 pat pat 677060 2009-06-18 05:04 rls/all_applet_s/bin/netlooptest
-rwxrwxr-x 1 pat pat 7833447 2009-06-18 05:09 rls/all_applet_s/bin/netlooptestD
...
Example 2. EFINLINE was not defined.
[pat@localhost devel] ll rls/all_applet_s/bin/
-rwxrwxr-x 1 pat pat 468228 2009-06-18 05:12 rls/all_applet_s/bin/effomem86
-rwxrwxr-x 1 pat pat 6562021 2009-06-18 05:13 rls/all_applet_s/bin/effomem86D
-rwxrwxr-x 1 pat pat 468228 2009-06-18 05:12 rls/all_applet_s/bin/getcpuinfo
-rwxrwxr-x 1 pat pat 6562021 2009-06-18 05:13 rls/all_applet_s/bin/getcpuinfoD
-rwxrwxr-x 1 pat pat 468228 2009-06-18 05:12 rls/all_applet_s/bin/getmeminfo
-rwxrwxr-x 1 pat pat 6562021 2009-06-18 05:13 rls/all_applet_s/bin/getmeminfoD
-rwxrwxr-x 1 pat pat 468228 2009-06-18 05:12 rls/all_applet_s/bin/netlooptest
-rwxrwxr-x 1 pat pat 6562021 2009-06-18 05:13 rls/all_applet_s/bin/netlooptestD
...
The examples should make sense that if less inline code, final binary or executables would be
smaller because the compiler would not embed inlined code into caller but make it be a function
instead.
Please note the list will use iterator which is the same as std::list::iterator, so you know how to use
it.

12
Copyright © 2008,2009 The Effo Core project. All rights reserved.
Effo Design - Lock-free Queue and Ring Buffer

5.5 Front and Pop-Front

Pop-Front operation is another operation of LIFO.


It will pop the first node of a list.
efinline void pop_front(void)
{
node_pointer n = m_head;
m_head = m_head->next;
node_destroy(n);
}
This LIFO provides front() operation and it is begin() internally which return an iterator.

5.6 Coding Example

The simple example use the LIFO


typedef longserial_number;
longserial_number;
const serial_number MAX_SN = 1024;
typedef lifo_list<serial_number> q_type;

q_type oQue;
serial_number i;
for (i = 0; i < MAX_SN; i++) {
oQue.push_front(i);
}
for (i = 0; i < MAX_SN / 2; i++) {
serial_number sn = *oQue.front();
oQue.pop_front();
// Output S/N here
}
oQue.clear();

5.7 To-do Next

As you had noted that the LIFO is very simple and it is not lock-free.
We introduced it because later of this document we will design Lock-free FIFO and it will be based
on this simple LIFO. So next we’ll
1. Introduce Atomic Operations, in next section
2. Design a Lock-free FIFO, in next of next

6. Atomic Operations

6.1 i++ is not Atomic

13
Copyright © 2008,2009 The Effo Core project. All rights reserved.
Effo Design - Lock-free Queue and Ring Buffer

CAUTION:
Please keep in mind that none of these lines is guaranteed to be atomic even if on a single CPU
(and one core only) system
pBackup = pStr; // Type of pointer pBackup and pStr is char* pointer
--pDistance; // Type of pointer pDistance is long* pointer
i++; // Type of integer i is int
count = n + 10; // Type of integer count and n is int

6.2 Get Atomic Primitive

Different architectural platforms have different atomic primitive.


Here give the example code of CAS (Compare-and-Swap) for x86/x86_64
static inline bool compare_and_swap(volatile
compare_and_swap(volatile unsigned int *p, unsigned int val_old, unsigned
int val_new)
{
int ret;
/* cmpxchgl */
__asm__ __volatile__("lock; cmpxchgl %3, %0; setz %1"
: "=m"(*p), "=q"(ret)
: "m"(*p), "r" (val_new), "a"(val_old)
: "memory");
return 1 == ret;
}
Note cmpxchgl is for a 32-bit system mainly; if your system is 64-bit, and need to operate an 8-byte
data, use cmpxchgq instead.
Another operation, say Fetch-and-Add, of x86/x86_64 also
static inline unsigned int fetch_and_add(volatile
fetch_and_add(volatile unsigned int *p, unsigned int add)
{
unsigned int ret;
/* xaddl */
__asm__ __volatile__("lock; xaddl %0, %1"
:"=r" (ret), "=m" (*p)
: "0" (add), "m" (*p)
: "memory");
return ret;
}
Also note that xaddl is for a 32-bit system mainly; if operate an 8-byte data in a 64-bit system, use
xaddq instead.
Moreover, xaddb is for one-byte (i.e. unsigned char), xaddw is for a WORD (2-byte, i.e. unsigned
short), and so on.
See codebase/addons/inl/include/atomic_inl.h and codebase/addons/inl/include/impl/atomic_impl.h
of source tree of the Effo Core for more details.

14
Copyright © 2008,2009 The Effo Core project. All rights reserved.
Effo Design - Lock-free Queue and Ring Buffer

If you have an old version of compiler, you’d make these atomic primitive yourself, just like what I
had done.
For example, if your GNU/C version is newer than 4.2.x, then you might use __sync_xxx series built-
in calls.
While note that old version of C++ compilers/libs doesn’t provide std::atomic, so do like what I had
done.

6.3 Compare with pthread Lock

Let’s design some test code to do a test


A. Is count += 1 atomic?

B. How about the cost of an atomic operation?


We will bring 3 threads up, and each thread will perform LOOP (0x01000000 loops) add-one
operations.
typedef unsigned int count_type;
typedef efao<count_type> ao_type;
const count_type LOOP = 0x01000000;
where efao is an Effo Atomic type (See atomic_inl.h).
We’ll operate an unsigned int type COUNT (g_uCount in my code)
extern volatile count_type g_uCount;
this is a global variable, and every thread will increase the COUNT by doing COUNT += 1 together
w/ one of following conditions
Cond. 1: Total-free, Just COUNT += 1, say Free
Cond. 2: Locking a Mutex
Cond. 3: Atomic Operating
but all of the 3 threads should operate the same way, that is, all threads increase COUNT w/
condition 1, or all threads increase w/ condition 2, or all threads w/ condition 3. The conditions
should not be mixed when using.
Here add a mutex
extern pthread_mutex_t g_tLck;
it’s a global variable too, when operating w/ lock, every thread should lock/unlock this mutex.
See reference document Effo Add-ons of the Effo Add-ons project for more threaded details.

Threads loop under condition 1 - Total-free, just adding. Or condition Free:


while (m_uDone++ < LOOP) {
g_uCount += 1;
}
where m_uDone is a per thread local counter.

Threads loop under condition 2 - Locking a Mutex. Or condition Lock:


while (m_uDone++ < LOOP) {
pthread_mutex_lock(&g_tLck);
g_uCount += 1;
pthread_mutex_unlock(&g_tLck);

15
Copyright © 2008,2009 The Effo Core project. All rights reserved.
Effo Design - Lock-free Queue and Ring Buffer

And threads loop under condition 3 - Atomic Operating. Or condition Atomic:


while (m_uDone++ < LOOP) {
ao_type::fetch_and_add
ao_type::fetch_and_add(&g_uCount,
(&g_uCount, 1);
}

6.4 Test Results

Now let’s give the results


[pat@localhost atomic_test]$ time ./test free
Test type: Free
Count 0x12d19ed

real 0m0.203s
user 0m0.166s
sys 0m0.005s
[pat@localhost atomic_test]$ time ./test lock
Test type: Lock
Count 0x3000000

real 0m2.351s
user 0m2.291s
sys 0m0.011s
[pat@localhost atomic_test]$ time ./test atomic
Test type: Atomic
Count 0x3000000

real 0m0.512s
user 0m0.468s
sys 0m0.008s
The time Linux utility was used to measure the execution time, in a Linux use “man time” to get the
details about this utility.
I had run the test on my Linux machine which only has one processor (uni-core), OS FC8/x86 plus
its default kernel.
See FC8/x86 default configuration as below
2.6.23.1-42.fc8
config-2.6.23.1-42.fc8
#
# Automatically generated make config: don't edit
# Linux kernel version: 2.6.23.1-42.fc8
# Tue Oct 30 13:53:02 2007
#
CONFIG_X86_32=y

16
Copyright © 2008,2009 The Effo Core project. All rights reserved.
Effo Design - Lock-free Queue and Ring Buffer

...
CONFIG_SEMAPHORE_SLEEPERS=y
...
CONFIG_X86=y
...
CONFIG_M686=y
...
CONFIG_X86_GENERIC=y
...
CONFIG_X86_CMPXCHG=y
...
CONFIG_X86_XADD=y
...
CONFIG_RWSEM_XCHGADD_ALGORITHM=y
...
CONFIG_SCHED_SMT=y
CONFIG_SCHED_MC=y
# CONFIG_PREEMPT_NONE is not set
CONFIG_PREEMPT_VOLUNTARY=y
# CONFIG_PREEMPT is not set
CONFIG_PREEMPT_BKL=y
...

As expected, the results are:


A. Operation such as increment or decrement upon an integer is not atomic. The result counter
0x3000000 is correct, but 0x12d19ed not (3 threads, 0x01000000 per thread) . Beware if use CAS
instead of Fetch-and-Add you may get wrong result (not 0x3000000).
B. Comparing with pthread-lock/unlock, atomic operation is very very fast.
This test should make sense.
Please keep in mind from now on: use atomic operation instead of lock/unlock operation as could as
possible.

6.5 sync_plc_none and efao Polices

Maybe you have a data that you hope to operate it in some cases atomically, but in some case not.
For example if the data would be operated serially as designed, atomic operations is not necessary
at all. Anyway atomic operating wastes a little of you time although it is very faster than mutexed
operating.
Effo sync_plc_none (See codebase/builtin/include/impl/sync_plc_none.h of the Effo Core) and efao
(See codebase/addons/inl/include/atomic_inl.h of the Effo Core) are for these situations.
template<class t>
struct sync_plc_none {
sync_plc_none(void) { clear(0); }
sync_plc_none(t &p)

17
Copyright © 2008,2009 The Effo Core project. All rights reserved.
Effo Design - Lock-free Queue and Ring Buffer

{
m_t = p;
}
inline void clear(t &p)
{
m_t = p;
}
inline void init(t &p)
{
m_t = p;
}
inline t &load(void)
{
return m_t;
}
/* for CAS of atomic_inl.h. */
inline void CAS(t val_new)
{
m_t = val_new;
}
/* for FetchAndAdd of atomic_inl.h. */
inline t FetchAndAdd(t val_add)
{
m_t += val_add;
return m_t;
}

protected:
t m_t;
};
All member functions are inlined so no performance loss.
Below is efao, Effo Atomic Operations
template<class t>
struct efao {
efao(void) { clear(0); }
efao(t &p)
{
m_t = p;
}
inline void clear(t p)
{
m_t = p;
}
inline void init(t p)

18
Copyright © 2008,2009 The Effo Core project. All rights reserved.
Effo Design - Lock-free Queue and Ring Buffer

{
m_t = p;
}
inline t load(void)
{
return m_t;
}
/* Compare-and-Swap */
inline void CAS(t val_old, t val_new)
{
while (!compare_and_swap(&m_t, val_old, val_new));
}
inline void CAS(t val_new)
{
while (!compare_and_swap(&m_t, m_t, val_new));
}
/*
* Prevent from endless CAS loop.
* Set retry_count = -1 for retrying for ever.
*/
inline bool CAS(t val_old, t val_new, long retry_count)
{
while (!compare_and_swap(&m_t, val_old, val_new)) {
if (retry_count != -1 && 0 == --retry_count) {
return false;
}
}
return true;
}
inline t FetchAndAdd(t val_add)
{
return fetch_and_add(&m_t, val_add);
}

protected:
volatile t m_t;
};
Be sure both sync_plc_none and efao has these members
inline void CAS(t val_new)
inline t FetchAndAdd(t val_add)
because I designed sync_plc_none and efao using Policy-Based Design Pattern so we can specify
which one will be used for our queue, list or buffer in which one or more data could be chosen being
operated atomically or not.

19
Copyright © 2008,2009 The Effo Core project. All rights reserved.
Effo Design - Lock-free Queue and Ring Buffer

7. sync_queue

See codebase/addons/inl/include/cont_inl.h of source tree of the Effo Core.


An Effo sync_queue supports push(), pop(), is_empty() and front() operations of FIFO.
template
<
class t,
/* Atomic operator. */
template<class> class sync_policy = sync_plc_none,
template<class> class node_creator = creator_new
>
class sync_queue
: public lifo_list<t, node_creator>
{
protected:
typedef lifo_list<t, node_creator> base_type;
typedef typename base_type::node_creator_type node_creator_type;
public:
typedef typename base_type::size_type size_type;
typedef typename base_type::node_type node_type;
typedef typename base_type::node_pointer node_pointer;
protected:
typedef typename base_type::iterator iterator;
typedef sync_policy<node_pointer> plc_type;

By default a sync_queue is total-free type by using sync_plc_none<>.
If specify efao<> to the sync_policy instead of sync_plc_none<>, a sync_queue is lock-free.
Again, a sync_queue is based on lifo_list, although it will override all operations almost except
Head, Node (node_type and node_pointer) and Iterator.

7.1 Tail, List Head and Queue Head

Let’s show an example of sync_queue first.


At a specific time of runtime a sync_queue looks like

A Runtime sync_queue Example

Head 0 0 Q 3 4 T
Pointer
Q-Head Last Tail
Q-node Node

In above figure, the Head Pointer is the list Head of lifo_list and it is a pointer only.
The Queue is a segment or sub-list of the whole List. In above figure nodes from “Q” to “T”
compose the Queu, and all nodes from “Head Pointer” to “T” compose the whole List.

20
Copyright © 2008,2009 The Effo Core project. All rights reserved.
Effo Design - Lock-free Queue and Ring Buffer


protected:
node_type m_tail;
plc_type m_sync_qhead;
};
A sync_queue has a Tail Node (m_tail in above code) which performs
The Tail (node “T”) of the List and the Queue
NEXT addresses last node (node “4”) of the List and the Queue

inline node_pointer last(void) { return m_tail.next; }

The only data which might to be operated atomically of a sync_queue is the Q-head (node “Q”), so
the Q-head type should be sync_policy.
Furthermore, the Tail was not dynamically allocated, so in sync_queue namespace-scoped the Tail is
always accessible and its address will not change.
inline iterator end(void) { return iterator(&m_tail); }
Note iterator of the List is not usable and not necessary out of a sync_queue. end() is an internal
call.
When constructing, an empty Q-head need to be pushed into the queue
sync_queue(void)
{
// Adding an empty Q-head node
t o = 0;
base_type::m_head = base_type::node_create(&m_tail, o);
m_sync_qhead.init(base_type::m_head);
m_tail.next = m_sync_qhead.load();
}
After above initializating an empty sync_queue looks like
An Empty sync_queue

Head Q T
Pointer
Q-Head Tail
Node

inline bool is_empty(void)


{
return m_sync_qhead.load()->next == &m_tail;
}
Now Q-head also is the last node of the List and the Queue.

7.2 front() and push()

The first node of the Queue is not the first node of the List, but the node which next to the Q-head
inline t front(void)

21
Copyright © 2008,2009 The Effo Core project. All rights reserved.
Effo Design - Lock-free Queue and Ring Buffer

{
iterator it(m_sync_qhead.load()->next);
return *it;
}
If push a data, a new node will be added to the end of the Queue
00 efinline bool push(t &p)
01 {
02 node_pointer n = this->push_back(p);
03 bool ret = n != &m_tail;
04 /* pop until no 0ed node(s) */
05 while (base_type::m_head != m_sync_qhead.load()) {
06 this->pop_front();
07 }
08 return ret;
09 }
Note in above code push_back() and pop_front() are operations of the List, the Queue is based the
List, so it uses these operations.
Before line 05, the sync_queue may looks like

After push_back, before doing clean

Head 0 0 Q 3 4 5 T
Pointer
Q-Head Last Tail
Q-node Node

In above figure, node “5” is the new one. Nodes 3-5 are our Queue nodes now.
Code lines 05-07 do a clean to erase all aged nodes. After clean the sync_queue will be

After clean

Head Q 3 4 5 T
Pointer
Q-Head Last Tail
Q-node Node

As your homework, think why do clean in push() instead of pop()?.

7.3 pop()

pop() is the simplest one


inline void pop(void)
{
m_sync_qhead.CAS
m_sync_qhead.CAS(m_sync_qhead.load()->next);
(m_sync_qhead.load()->next);
}
But in fact this is the most important part of the sync_queue if you want to make it Lock-free
(sync_policy was specified as efao<>).

22
Copyright © 2008,2009 The Effo Core project. All rights reserved.
Effo Design - Lock-free Queue and Ring Buffer

Because among the List Head, Q-head and Tail, Q-head is the only one which needed to be
operated atomically. Whenever updating the Q-head, atomic operating is a must if require Lock-
free.
Afte pop() the sync_queue is

After pop()

Head 0/Q Q/3 4 5 T


Pointer
Q-Head Last Tail
Q-node Node

The first node of the List is previous Q-head of the Queue, and previous node “3” is the Q-head
now.
Queue nodes are 4-5 because node “3” was popped.

7.4 Coding Example

The simple example use the sync_queue FIFO


typedef longserial_number;
longserial_number;
const serial_number MAX_SN = 1024;
typedef sync_queue<serial_number, efao> q_type;

q_type oQue;

In thread-A doing push()
ThreadLoop_A()
{
serial_number i;
for (i = 0; i < MAX_SN; i++) {
oQue.push(i);
}
}
In thread-B doing pop()
ThreadLoop_B()
{
serial_number i;
for (i = 0; i < MAX_SN;) {
if (!oQue.is_empty()) {
serial_number val = oQue.front();
oQue.pop();
// do something using val here
If (i != val) {
// incorrect
}
i ++;

23
Copyright © 2008,2009 The Effo Core project. All rights reserved.
Effo Design - Lock-free Queue and Ring Buffer

}
}
}
Thread-B loop also could be
ThreadLoop_B()
{
while (m_bRunning) {
if (!oQue.is_empty()) {
serial_number val = oQue.front();
oQue.pop();
// do something using val here
}
}
}
Don’t forget to do a final clean after done
oQue.clear();

8. Base Ringed

See codebase/addons/inl/include/ringed_inl.h of source tree of the Effo Core.


The type Effo ringed<> is used to build ring_buffer and ring_queue up.

8.1 The Template

Let’s look at the Effo Ringed


template
<
class t,
template<class> class sync_policy = sync_plc_none,
template<class> class buf_creator = creator_new,
/* Is Ring Queue? */
bool is_q = false
>
class ringed
{
public:
typedef efusz_t size_type;
typedef t data_type;
typedef data_type* cell_type;
typedef buf_creator<t> buf_creator_type;
typedef ptrdiff_t off_type;
protected:
typedef sync_policy<cell_type> plc_type;

24
Copyright © 2008,2009 The Effo Core project. All rights reserved.
Effo Design - Lock-free Queue and Ring Buffer


From the previous sections of this document, we got known sync_policy, buf_creator already.
Template parameter is_q indicates if the ringed for Ring Buffer or Ring Queue.
In ringed namespace-scoped, we defines 3 things, the Stuff, Snapshot and Operations.
Related to the Stuff, Snapshot and Operations, we named the Ringed class a Host. So the Stuff,
Snapshot and Operations are all Guests of the Host.

8.2 The Stuff

A Stuff is reponsible for storing Tail, SizeUsed and SizeFree etc; it is also used for computing
something like new Head or new Tail of a ringed buffer for the Host.

template<class U, bool isq>
/*
* U for stopping complaint of
* g++ "explicit specificialization in non-namespace scope ..."
*/
struct stuff {
// Queue type, not buffer.
typedef ringed<t, sync_policy, buf_creator, is_q> host_type;
typedef typename host_type::size_type size_type;
typedef typename host_type::cell_type cell_type;

host_type *m_h;
/* A copy of host::m_head. */
cell_type m_pop;
/* A copy of host::m_tail. */
cell_type m_push;
cell_type m_new;
size_type m_used;
size_type m_free;
char m_sz_in;

Note “U” parameter is just used to avoid errors or warning of a compiler, because the Stuff is
defined in the ringed namespace-scoped.
Above default Stuff is for Ring Queeu.
If a Stuff is for Ring Buffer, it will be slightly different from above one (member functions are
different too)

template<class U>
struct stuff<U, false>
false> {
// Buffer type, not queue.
typedef ringed<t, sync_policy, buf_creator, is_q> host_type;
typedef typename host_type::size_type size_type;

25
Copyright © 2008,2009 The Effo Core project. All rights reserved.
Effo Design - Lock-free Queue and Ring Buffer

typedef typename host_type::cell_type cell_type;

host_type *m_h;
/* A copy of host::m_head. */
cell_type m_pop;
/* A copy of host::m_tail. */
cell_type m_push;
cell_type m_new;
size_type m_used;
size_type m_free;
size_type m_left;
size_type m_sz_left;
size_type m_sz_in;

8.3 The Snapshot

A Snapshot is responsible for taking a snapshot of Tail and doing push()/pop() for the Host.

template<class U, bool isq>
/*
* U for stopping complaint of
* g++ "explicit specificialization in non-namespace scope ..."
*/
class snapshot
{
// Queue type, not buffer.
protected:
typedef stuff<U, is_q> stuff_type;
public:
typedef typename stuff_type::host_type host_type;
protected:
typedef typename host_type::size_type size_type;
typedef typename host_type::off_type off_type;
typedef typename host_type::data_type data_type;
typedef typename host_type::cell_type cell_type;
protected:
host_type *m_h;
stuff_type m_t;

This default Snapshot is for Ring Queue.
If a Snapshot is for Ring Buffer, it will be slightly different from above one (member functions are
different too); actually the Stuff it holds is different.

26
Copyright © 2008,2009 The Effo Core project. All rights reserved.
Effo Design - Lock-free Queue and Ring Buffer

template<class U>
class snapshot<U, false>
{
// Buffer type, not queue.
protected:
typedef stuff<U, is_q> stuff_type;

8.4 The Operations

The Operations is responsible for selecting Ring Buffer or Ring Queue and doing corresponding
push() and pop().
template<class U, bool isq>
/*
* U for stopping complaint of
* g++ "explicit specificialization in non-namespace scope ..."
*/
class operations
{
// Queue type, not buffer.
protected:
typedef snapshot<U, is_q> shot_type;
typedef typename shot_type::host_type host_type;
typedef typename host_type::data_type data_type;
typedef typename host_type::cell_type cell_type;
protected:
host_type *m_h;
This default Operations holds a Ring Queue type Snapshot.
If an Operations is for Ring Buffer, it will be slightly different from above one (member functions are
different too).
The held Snapshot is different.

8.5 How a Ringed Buffer Works

For example, an 8-byte buffer looks like:


Offset 01234567
Buffer [0 0 0 0 0 0 0 0]

The buffer size (BufferSize below) is 8.


In this example, the buffer was set to be all-0 state initially, buffer Head and Tail would be set at
offset 0 (Head = 0, Tail = 0) because it is empty.

Main operations of this buffer are


Push/PushBack

27
Copyright © 2008,2009 The Effo Core project. All rights reserved.
Effo Design - Lock-free Queue and Ring Buffer

Pop/PopFront
Discard/DiscardFront (optional)

If we push 4 bytes into it, the bytes will be put at the Tail of this buffer, now the buffer looks like:
Offset 01234567
Buffer [1 1 1 1 0 0 0 0]

Said the corresponding bytes (3:0) would be set to 1 to indicate in use (SizeUsed = 4), and there
are still 4 bytes (7:4) available (SizeFree = 4).
The Head is still at offset 0, but the Tail was set at offset 4 now (Head = 0, Tail = 4).
If you push bytes again, the first new byte would be put at the Tail (offset 4).
So we got
SizeUsed = Tail - Head = 4 - 0 = 4
SizeFree = BufferSize - SizeUsed = 8 - 4 = 4
If we pop 2 bytes from above buffer to make the buffer looks like:
Offset 01234567
Buffer [0 0 1 1 0 0 0 0]

Said the corresponding bytes (1:0) would be reset to 0 to indicate available again
SizeUsed = 2, SizeFree = 6
The head was set at offset 2 now, but buffer Tail is still at offset 4 (Head = 2, Tail = 4).
If you pop bytes again, the first output byte would be from the Head (offset 2).
So we got
SizeUsed = Tail - Head = 4 - 2 = 2
SizeFree = BufferSize - SizeUsed = 8 - 2 = 6
If you want to pop 3 or more bytes from above buffer, it will fail.
What's about push?
We always need to reserve 1 byte for identifying Tail from Head to make the logic simple and clear.
So if you want to push 6 or more bytes, it will fail.

8.6 SizeUsed and SizeFree

After summarize some buffer states:


Offset 01234567
Buffer [1 1 1 1 0 0 0 0] // Tail > Head
[0 1 1 1 1 1 0 0] // Tail > Head too
[1 1 0 0 1 1 1 1] // Head > Tail
[1 1 1 1 1 1 1 0] // full, Tail > Head
[1 1 1 1 0 1 1 1] // another full case, Head > Tail
[0 0 0 0 0 0 0 0] // empty, Head == Tail
we'd like to compute size this way:
protected:
inline void used_and_free(stuff<t,
used_and_free(stuff<t, is_q> *p)
{

28
Copyright © 2008,2009 The Effo Core project. All rights reserved.
Effo Design - Lock-free Queue and Ring Buffer

off_type off = p->m_push - p->m_pop;


if (off > 0) {
p->m_used = off;
p->m_free = m_cnt - off;
} else if (off < 0) {
off = -off;
p->m_free = off;
p->m_used = m_cnt - off;
} else {
p->m_free = m_cnt;
p->m_used = 0;
}
}
This used_and_free() is an internal interface; it is OK for both Ring buffer and Ring Queue.

8.7 CanPush() and CanPop()

CanPush and CanPop are internal members used when doing push() or pop().
static inline bool can_push(stuff<t, is_q> *p)
{
return (size_type)p->m_free > (size_type)p->m_sz_in;
}
An empty cell of the ringed buffer is always reserved, so if SizeFree >= InputLen + 1, then there’re
available cells into which we could push something.
If used cells are more than requested, then the cells could be popped
static inline bool can_pop(stuff<t, is_q> *p)
{
return (size_type)p->m_used >= (size_type)p->m_sz_in;
}

8.8 Quick Accessing Host from a Guest

Following methods are for Guests such as Stuff, Snapshot and Operations internally.
protected:
inline cell_type buf(void)
{
return m_buf;
}
inline cell_type end(void)
{
return m_end;
}
inline const size_type cnt(void)
{

29
Copyright © 2008,2009 The Effo Core project. All rights reserved.
Effo Design - Lock-free Queue and Ring Buffer

return m_cnt;
}
inline cell_type tail(void)
{
return m_sync_tail.load();
}
inline cell_type head(void)
{
return m_sync_head.load();
}
inline plc_type *sync_tail(void)
{
return &m_sync_tail;
}
inline plc_type *sync_head(void)
{
return &m_sync_head;
}

protected:
ops_type m_ops;
cell_type m_buf;
plc_type m_sync_head;
plc_type m_sync_tail;
cell_type m_end;
size_type m_cnt;

};
In above code we also get known that the Head and Tail of a Ring Buffer or Ring Queue should be
operated atomically if Lock-free required

9. Ring Buffer

A Ring Buffer supports these operations


Init(Count), initialize an fixed-size array which has Count cells
Clear(), clear the array
Push(Cells, Count), push data into Count cells
Pop(Cells, Count), pop data from Count cells
Discard(Count), discard Count cells
Since we already have a generic Ringed, we’ll use it to perform above operations.

9.1 push()

30
Copyright © 2008,2009 The Effo Core project. All rights reserved.
Effo Design - Lock-free Queue and Ring Buffer

As mentioned ringed::operations is responsible for push() or pop() for a Ring Buffer or Ring Queue.
Below is for Ring Buffer in the ringed namespace-scoped, ringed::operations
template<class U>
class operations<U, false>
{
// Buffer type, not queue.
protected:
typedef snapshot<U, is_q> shot_type;
typedef typename shot_type::host_type host_type;
typedef typename host_type::size_type size_type;
typedef typename host_type::cell_type cell_type;
protected:
host_type *m_h;
public:
...
inline bool push(const cell_type p, const size_type count)
{
shot_type shot;
shot.host_set(m_h);
shot.take_input(count);
return shot.push(p);
}
...
A Snapshot pushes the data, ringed::snapshot::push():
...
inline bool push(const cell_type p)
{
m_t.compute(m_t.m_push);
if (m_h->can_push(&m_t)) {
if (m_t.broken
(m_t.broken())
()) {
effo_memcpy(m_t.m_push,
effo_memcpy(m_t.m_push, p, m_t.m_left);
effo_memcpy(m_h->buf(), p + m_t.m_left, m_t.m_sz_left);
} else {
effo_memcpy(m_t.m_push, p, m_t.m_sz_in);
}

m_h->sync_tail()->CAS
m_h->sync_tail()->CAS(m_t.m_new);
(m_t.m_new);
return true;
}
return false;
}
...
In above code broken() means before push(), the buffer looks like
Offset 01234567

31
Copyright © 2008,2009 The Effo Core project. All rights reserved.
Effo Design - Lock-free Queue and Ring Buffer

Buffer [0 0 0 0 1 1 1 0]
That is, the input cells (i.e. 3 cells, cell-1, cell-2 and cell 3) need to be split into 2 parts, part-1 (i.e.
cell-1) will be put at the end of the buffer, and another part (part-2, cell-2 and cell-3) will be put at
the begging of the buffer.
So after push() the new buffer will be
Offset 01234567
Buffer [1
[1 1 0 0 1 1 1 1]
The new data at Offset 7, 0 and 1 were just pushed into the buffer.
After moved the data in, the Tail need to be updated atomically if Lock-free required.

See the reference document Effo Core References of the Effo Core and
codebase/addons/inl/include/bytes_inl.h of source tree of the Effo Core for more details about
effo_memcpy().

9.2 pop()

The ringed::operations::pop()
...
inline cell_type pop(cell_type p, const size_type count)
{
shot_type shot;
shot.host_set(m_h);
shot.take_input(count);
return shot.pop(p);
}
...
A Snapshot pops the data, ringed::snapshot::pop():
inline cell_type pop(cell_type p)
{
cell_type ret = NULL;
m_t.compute(m_t.m_pop);
if (m_h->can_pop(&m_t)) {
if (m_t.broken
(m_t.broken())
()) {
effo_memcpy(p, m_t.m_pop, m_t.m_left);
effo_memcpy(p + m_t.m_left, m_h->buf(), m_t.m_sz_left);
} else {
effo_memcpy(p, m_t.m_pop, m_t.m_sz_in);
}
m_h->sync_head()->CAS
m_h->sync_head()->CAS(m_t.m_new);
(m_t.m_new);
ret = p;
}

return ret;
}

32
Copyright © 2008,2009 The Effo Core project. All rights reserved.
Effo Design - Lock-free Queue and Ring Buffer

Here broken() means before pop(), the buffer may be


Offset 01234567
Buffer [1 1 1 0 0 0 1 1]
That is, to pop cells (i.e. 3 cells, cell-6, cell-7 and cell 0) need to be split into 2 parts, part-1 (i.e.
cell-6 and cell-7) is at the end of the buffer, and another part (part-2, cell-0) is at the begging of the
buffer.
So after pop() the new buffer will be
Offset 01234567
Buffer [0
[0 1 1 0 0 0 0 0]
The data at Offset 6, 7 and 0 were just popped from the buffer.
After moved the data out, the Head need to be updated atomically if Lock-free required.

9.3 The Wrapper ring_buffer

For your conveniences, a wrapper Effo ring_buffer is provided, but here suggest using
ringed::operations directly.
template
<
class t,
template<class> class sync_policy = sync_plc_none,
template<class> class buf_creator = creator_new
>
class ring_buffer
{
protected:
typedef ringed<t, sync_policy, buf_creator, false> base_type;
typedef typename base_type::buf_creator_type buf_creator_type;
public:
typedef typename base_type::size_type size_type;
typedef typename base_type::data_type data_type;
typedef typename base_type::cell_type cell_type;

protected:
base_type m_base;

public:
inline bool init(const size_type count)
{
return m_base.init(count);
}
inline void clear(void)
{
m_base.clear();
}

33
Copyright © 2008,2009 The Effo Core project. All rights reserved.
Effo Design - Lock-free Queue and Ring Buffer

inline bool push(const cell_type p, const size_type count)


{
return m_base.ops()->push(p, count);
}
inline cell_type pop(cell_type p, const size_type count)
{
return m_base.ops()->pop(p, count);
}
inline void discard(const size_type count)
{
m_base.ops()->discard(count);
}
};
This also gives an example about how to use ringed and its operations.

9.4 ring_buffer Code Example

See IRingBuf<STAT> in codebase/addons/lockfree/include/lockfree_i.h of source tree of the Effo


Add-ons for references.

10. Ring Queue

A Ring Queue is different from a Ring Buffer, it supports these operations


Init(Count), initialize an fixed-size array which has Count cells
Clear(), clear the array
Push(), push a cell
Pop(), pop a cell
No Front() needed, unlike a sync_queue.
Since we already have a generic Ringed, we’ll use it to perform above operations like described
Ring Buffer.

10.1 push()

As mentioned ringed::operations is responsible for push() or pop() for a Ring Buffer or Ring Queue.
Below is for Ring Queue in the ringed namespace-scoped, ringed::operations
...
template<class U, bool isq>
class operations
{
// Queue type, not buffer.
protected:
typedef snapshot<U, is_q> shot_type;
typedef typename shot_type::host_type host_type;

34
Copyright © 2008,2009 The Effo Core project. All rights reserved.
Effo Design - Lock-free Queue and Ring Buffer

typedef typename host_type::data_type data_type;


typedef typename host_type::cell_type cell_type;
protected:
host_type *m_h;
...
public:
...
inline bool push(data_type &p)
{
shot_type shot;
shot.host_set(m_h);
return shot.push(p);
}
...
By default a ringed::operations is for Ring Queue, not for Ring Buffer.
A Snapshot pushes the data, ringed::snapshot::push():
...
inline bool push(t &p)
{
m_t.compute(m_t.m_push);
if (m_h->can_push(&m_t)) {
*m_h->tail() = p;
m_h->sync_tail()->CAS
m_h->sync_tail()->CAS(m_t.m_new);
(m_t.m_new);
return true;
}
return false;
}
...
It is simple than that of Ring Buffer type Snapshot because a Ring Queue always pushing or
popping just one cell exactly.
After moved the data in, the Tail need to be updated atomically if Lock-free required.

10.2 pop()

The ringed::operations::pop()
...
inline data_type pop(void)
{
shot_type shot;
shot.host_set(m_h);
return shot.pop();
}
...
By default a ringed::operations is for Ring Queue, not for Ring Buffer.

35
Copyright © 2008,2009 The Effo Core project. All rights reserved.
Effo Design - Lock-free Queue and Ring Buffer

A Snapshot pops the data, ringed::snapshot::pop():


...
inline t pop(void)
{
t ret = 0;
m_t.compute(m_t.m_pop);
if (m_h->can_pop(&m_t)) {
ret = *m_h->head();
m_h->sync_head()->CAS
m_h->sync_head()->CAS(m_t.m_new);
(m_t.m_new);
}
return ret;
}
...
It is simple than that of Ring Buffer type Snapshot because a Ring Queue always pushing or
popping just one cell exactly.
After moved the data out, the Head need to be updated atomically if Lock-free required.

10.3 The Wrapper ring_queue

For your conveniences, a wrapper Effo ring_queue is provided, but here suggest using
ringed::operations directly.
template
<
class t,
template<class> class sync_policy = sync_plc_none,
template<class> class buf_creator = creator_new
>
class ring_queue
{
protected:
typedef ringed<t, sync_policy, buf_creator, true> base_type;
typedef typename base_type::buf_creator_type buf_creator_type;
public:
typedef typename base_type::size_type size_type;
typedef typename base_type::data_type data_type;
typedef typename base_type::cell_type cell_type;

protected:
base_type m_base;
public:
inline bool init(const size_type count)
{
return m_base.init(count);
}

36
Copyright © 2008,2009 The Effo Core project. All rights reserved.
Effo Design - Lock-free Queue and Ring Buffer

inline void clear(void)


{
m_base.clear();
}
inline bool push(data_type &p)
{
return m_base.ops()->push(p);
}
inline data_type pop(void)
{
return m_base.ops()->pop();
}
};
Like ring_buffer, this gives an example about how to use ringed and its operations too.

11. Policy-Based Dynamic-size and Fixed-size

In previous sections Ring Buffer was introduced. That Ring Buffer is based on a fixed-size array or
buffer, so its size or buffer length is limited when it get initialized.

In this section, we simply use sync_queue to make a lock-free dynamic-size Ring Buffer, named
IRingBuf<DYNA>.
Before IRingBuf<DYNA>, we give other 3 lock-free queue and ring buffer first, they are
IRingQ<DYNA>, dynamic-size lock-free queue
IRingQ<STAT>, fixed-size lock-free queue
IRingBuf<STAT>, fixed-size lock-free ring buffer
All of them together with IRingBuf<DYNA> were defined on codebase/addons/lockfree of source
tree of the Effo Add-ons.

11.1 IRingQ<DYNA>

An Effo IRingQ<DYNA> uses sync_queue as its internal queue


template <typename T>
class IRingQ<T, true>
{
// Dyna type, not stat.
public:
typedef sync_queue<T, efao, creator_nofail> q_type;
...
It supports following operations
void Init(const size_type uCnt);
void Clear(void);
bool push(T &p);

37
Copyright © 2008,2009 The Effo Core project. All rights reserved.
Effo Design - Lock-free Queue and Ring Buffer

T pop(void);
Understanding them is easy because you’ve gotten sync_queue already.
An IRingQ<DYNA> is not size-limited queue type and it supports lock-free.

11.2 IRingQ<STAT>

An Effo IRingQ<STAT> uses ring_queue as its internal queue


template <typename T, bool bDyna>
class IRingQ
{
// Stat type, not dyna.
public:
typedef ring_queue<T, efao, creator_array_nofail> q_type;
...
It also supports the operations as IRingQ<DYNA>
void Init(const size_type uCnt);
void Clear(void);
bool push(T &p);
T pop(void);
Understanding them is easy too because you’ve gotten ring_queue already.
An IRingQ<STAT> is fixed-size queue type and it supports lock-free.
Note both IRingQ<STAT> and IRIngQ<DYNA> have the same operations and interface, and they
could be used as Policy-Based classes.

11.3 IRingBuf<STAT>

An Effo IRingBuf<STAT> uses ring_buffer as its internal buffer


template<class T, bool bDyna>
class IRingBuf
{
public:
typedef T data_type;
typedef data_type* cell_type;
typedef ring_buffer<data_type, efao, creator_array_nofail> buf_type;
...
It supports following operations
void Init(const size_type uSize);
void Clear(void);
bool push(const cell_type p, const size_type uSz);
cell_type pop(cell_type p, const size_type uSz);
void Discard(const size_type uSz);
Understanding them is easy because you’ve gotten ring_buffer already.
An IRingBuf<STAT> is fixed-size Ring Buffer and it supports lock-free.

38
Copyright © 2008,2009 The Effo Core project. All rights reserved.
Effo Design - Lock-free Queue and Ring Buffer

11.4 IRingBuf<DYNA>

An Effo IRingBuf<DYNA> uses sync_queue as its internal buffer


template<class T>
class IRingBuf<T, true>
{
public:
typedef IRingBuf<T, false> stat_buf;
typedef typename stat_buf::size_type size_type;
typedef T data_type;
typedef data_type* cell_type;
typedef sync_queue<cell_type, efao, creator_nofail> buf_type;
...
It also supports following operations as IRingBuf<STAT>
void Init(const size_type uSize);
void Clear(void);
bool push(const cell_type p, const size_type uSz);
cell_type pop(cell_type p, const size_type uSz);
void Discard(const size_type uSz);
Understanding them is easy too because you’ve gotten sync_queue already.
An IRingBuf<DYNA> is not size-limited Ring Buffer type and it supports lock-free.
Note both IRingBuf<STAT> and IRIngBuf<DYNA> have the same operations and interface, and they
could be used as Policy-Based classes.

12. Support, Helps and Suggestions

Please visit Effo Core Group (a forum)


http://groups.google.com/group/effocore-group
which is for the Effo Core project developers and users.

Here lists all Effo online resources


 Effo Home
http://effo.sourceforge.net
 Effo Project & Effo Group
http://effo.googlecode.com
http://groups.google.com/group/effo-group
 Effo Core Project & Effo Core Group
http://effocore.googlecode.com
http://groups.google.com/group/effocore-group
 Effo Add-ons Project & Effo Add-ons Group
http://effoaddon.googlecode.com
http://groups.google.com/group/effoaddon-group
 Effo NetMsg Project & Effo NetMsg Group

39
Copyright © 2008,2009 The Effo Core project. All rights reserved.
Effo Design - Lock-free Queue and Ring Buffer

http://effonetmsg.googlecode.com
http://groups.google.com/group/effonetmsg-group
 Effo Devel/Framework Project & Effo Devel/Framework Group
http://effodevel.googlecode.com
http://groups.google.com/group/effodevel-group
 Effo GPLed Project & Effo GPLed Group
http://effogpled.googlecode.com
http://groups.google.com/group/effogpled-group

<Nothing Following>

40
Copyright © 2008,2009 The Effo Core project. All rights reserved.

Anda mungkin juga menyukai