Search Home Members Contacts
About Us
Products
Downloads
Community
Support
Pages: [1]
  Print  
Author Topic: Lockless queue implementation  (Read 1252 times)
Raine
Customers
Community Member
*****
Posts: 1189


« on: July 22, 2008, 07:16:42 AM »

Has anybody got a link to a lockless queue implementation I can learn from?

Thanks in advance Smiley
Logged

Zaknafein
Customers
Community Member
*****
Posts: 2670


WWW
« Reply #1 on: July 22, 2008, 08:36:17 AM »

I'm not familiar with the concept... could you give a bit more detail?
Logged

zaknafein.
>> the instruction limit : my blog & samples repository! <<
shsdevries
Community Member
*
Posts: 19


« Reply #2 on: July 22, 2008, 08:38:15 AM »

Do you mean a window messages queue?
Logged
Raine
Customers
Community Member
*****
Posts: 1189


« Reply #3 on: July 22, 2008, 11:30:14 AM »

A lockless queue is basically a queue you can push into and pop stuff from from different threads without locking mechanisms
Logged

newborn
Customers
Community Member
*****
Posts: 2437


WWW
« Reply #4 on: July 22, 2008, 11:40:27 AM »

I'm not familiar with the concept... could you give a bit more detail?

It's quite simple actually:




Oh, wait, you meant lochness or lockless?
Logged

Zaknafein
Customers
Community Member
*****
Posts: 2670


WWW
« Reply #5 on: July 22, 2008, 12:21:25 PM »

Oh, wait, you meant lochness or lockless?

I lol'd.

A lockless queue is basically a queue you can push into and pop stuff from from different threads without locking mechanisms

So... it's thread-safe AND doesn't lock? Shocked
What I found on lockless queues is that it has to have one reader thread and one writer thread, no more, or it stops being thread-safe. But I found that using Google, no prior knowledge.

Why dyou want to avoid locks so bad? Monitors are the classic solution to threading problems, it should do just fine.
Logged

zaknafein.
>> the instruction limit : my blog & samples repository! <<
Raine
Customers
Community Member
*****
Posts: 1189


« Reply #6 on: July 22, 2008, 12:26:36 PM »

badabum-tschh.

+10 thread hijacking

welcome to level 3
Logged

AriusEso
Customers
Community Member
*****
Posts: 376

Esoteric


« Reply #7 on: July 22, 2008, 12:35:14 PM »

Sorry to interrupt the thread Raine, but what happened to the "C" - I've been meaning to ask you for days now.  Grin
Logged

Raine
Customers
Community Member
*****
Posts: 1189


« Reply #8 on: July 22, 2008, 12:47:49 PM »

Changed my display name; when I registered to the forums, I put the wrong email address twice and couldn't recover the registration info... :S

Lockless queues anyone? I want to know about them just for research purposes. The more I know, the better!
Logged

newborn
Customers
Community Member
*****
Posts: 2437


WWW
« Reply #9 on: July 23, 2008, 11:46:11 AM »

badabum-tschh.

+10 thread hijacking

welcome to level 3

Yah, I know, I still have a long way to go to get to your level huh?
Logged

Raine
Customers
Community Member
*****
Posts: 1189


« Reply #10 on: July 23, 2008, 03:43:47 PM »

ç_ç
Logged

Zaknafein
Customers
Community Member
*****
Posts: 2670


WWW
« Reply #11 on: September 17, 2008, 12:57:12 AM »

So raine, I have hum... reasons to believe that you did manage to find an implementation. Mind sharing it with us? Or sharing the source material?
Logged

zaknafein.
>> the instruction limit : my blog & samples repository! <<
Raine
Customers
Community Member
*****
Posts: 1189


« Reply #12 on: September 17, 2008, 05:23:13 AM »

Code:
// Adityanand Pasumarthi, 2005-2006
// Based on Fober et al. 2003

/// <summary>
/// Node class used by all other data structures
/// </summary>
public class LockFreeQueueNode<T>
        where T: class
{

        #region Public Data Members
public T Data;
public LockFreeQueueNode<T> NextNode;
#endregion

#region Public Constructors
/// <summary>
/// Creates an instance of a Node class with
/// data element as null
/// </summary>
public LockFreeQueueNode()
{
Init(null);
}

/// <summary>
/// Creates an instance of the Node class with
/// data element as the passed in object
/// </summary>
/// <param name="data">Data of the Node instance</param>
public LockFreeQueueNode(T data)
{
Init(data);
}
#endregion

#region Private Methods
private void Init(T data)
{
Data = data;
NextNode = null;
}
#endregion

}

/// <summary>
/// Lock Free Queue
/// </summary>
public class LockFreeQueue<T>
        where T: class
{

#region Private Data Members
/// <summary>
/// Head node of the queue
/// </summary>
private volatile LockFreeQueueNode<T> head;
/// <summary>
/// Head node of the queue
/// </summary>
private volatile LockFreeQueueNode<T> tail;
/// <summary>
/// Count of elements in the queue
/// </summary>
private long count = 0;
#endregion
       
        #region Public Constructors
/// <summary>
/// Creates a new instance of Lock-Free Queue
/// </summary>
public LockFreeQueue()
{
Init(0);
}

/// <summary>
/// Creates a new instance of Lock-Free Queue with n-number of
/// pre-created nodes to hold objects queued on to this instance.
/// </summary>
/// <param name="nodeCount">Number of Nodes to pre-create</param>
public LockFreeQueue(int nodeCount)
{
Init(nodeCount);
}
#endregion

#region Public Methods
/// <summary>
/// Enqueue the given object onto this Queue instance
/// </summary>
/// <param name="data">Object to be enqueued</param>
        public void Enqueue(T data)
        {
            LockFreeQueueNode<T> tempTail = null;
            LockFreeQueueNode<T> tempTailNext = null;
            LockFreeQueueNode<T> newNode = new LockFreeQueueNode<T>(data);
            newNode.Data = data;
            do
            {
                tempTail = tail;
                tempTailNext = tempTail.NextNode;
                if (tempTail == tail)
                {
                    if (tempTailNext == null)
                    {
                        // If the tail node we are referring to is really the last
                        // node in the queue (i.e. its next node is null), then
                        // try to point its next node to our new node
                        //
                        if (Interlocked.CompareExchange(ref tempTail.NextNode, newNode, tempTailNext) == tempTailNext)
                            break;
                    }
                    else
                    {
                        // This condition occurs when we have failed to update
                        // the tail's next node. And the next time we try to update
                        // the next node, the next node is pointing to a new node
                        // updated by other thread. But the other thread has not yet
                        // re-pointed the tail to its new node.
                        // So we try to re-point to the tail node to the next node of the
                        // current tail
                        //
                        Interlocked.CompareExchange(ref tail, tempTailNext, tempTail);
                    }
                }
            } while (true);

            // If we were able to successfully change the next node of the current tail node
            // to point to our new node, then re-point the tail node also to our new node
            //
            Interlocked.CompareExchange(ref tail, newNode, tempTail);
            Interlocked.Increment(ref count);
        }

/// <summary>
/// Dequeue an object from the front of the queue
/// </summary>
/// <param name="empty">true if the queue is empty else false</param>
/// <returns>
/// null if the queue is empty else returns the first element at
/// the front of the queue
/// </returns>
        public T Dequeue(out bool empty)
        {
            T data = null;
            LockFreeQueueNode<T> tempTail = null;
            LockFreeQueueNode<T> tempHead = null;
            LockFreeQueueNode<T> tempHeadNext = null;
           
            empty = false;

            do
            {
                tempHead = head;
                tempTail = tail;
                tempHeadNext = tempHead.NextNode;

                if (tempHead == head)
                {
                    // There may not be any elements in the queue
                    //
                    if (tempHead == tempTail)
                    {
                        if (tempHeadNext == null)
                        {
                            // If the queue is really empty come out of dequeue operation
                            //
                            empty = true;
                            return null;
                        }
                        else
                        {
                            // Some other thread could be in the middle of the
                            // enqueue operation. it could have changed the next node of the tail
                            // to point to the new node.
                            // So let us advance the tail node to point to the next node of the
                            // current tail
                            Interlocked.CompareExchange(ref tail, tempHeadNext, tempTail);
                        }
                    }
                    else
                    {
                        // Move head one element down.
                        // If succeeded Try to get the data from head and
                        // break out of the loop.
                        //
                        data = tempHeadNext.Data;
                        if (Interlocked.CompareExchange(ref head, tempHeadNext, tempHead) == tempHead)
                            break;
                    }
                }
            } while (true);
            Interlocked.Decrement(ref count);
            tempHead.Data = null;
            return data;
        }

/// <summary>
/// Remove all the elements from the queue
/// </summary>
public void Clear()
{
Init(0);
}

/// <summary>
/// Remove all the elements from the queue
/// </summary>
public void Clear(int nodeCount)
{
Init(nodeCount);
}
#endregion

#region Public Properties
/// <summary>
/// Count of elements in the queue
/// </summary>
public long Count
{
get
{
return count;
}
}
#endregion

#region Private Methods
/// <summary>
/// Initialize the queue
/// </summary>
/// <param name="nodeCount">Number of nodes to pre-create</param>
private void Init(int nodeCount)
{
count = 0;
            head = tail = new LockFreeQueueNode<T>();
}
#endregion

}


I don't remember the original name of the class. I can remember finding this implementation over at codeproject.com, the author was P.Adityanand.
Logged

micmanos
Customers
Community Member
*****
Posts: 420

End of this evil world in 4..3..2..


WWW
« Reply #13 on: September 19, 2008, 04:37:24 AM »

I don't know C that much but it looks almost identical to an effort i did to create a class that would create and manage threads for each projectile fired in the game ... asynchronously.

I tried to use 2 separate arrays (One current and one Temporary) and only make available the newest, as well as some fancy checking in between. It does look to me remarkably similar ....... unfortunately, my attempt didn't work that well, throwing me exceptions Sad
Logged

Raine
Customers
Community Member
*****
Posts: 1189


« Reply #14 on: September 19, 2008, 06:34:53 AM »

It's not uncommon to have what I call a "ping pong" cache (buffer, whatever). I think I've been talking about this with JohnB as well during one of our chats. However, such buffers require at least one locking mechanism, since you have to marshal when these caches are swapped. I think so, might as well be wrong since I'm not using such a data structure at the moment.

I don't suggest working with many threads, rather it's good to have a good workloader sharing system and as few threads as possible. Threads are expensive, and there's so much to know to use their power properly it's very easy to get lost. I for one am curious to know what's beneath the tip of this iceberg...
Logged

Pages: [1]
  Print  
 
Jump to:  

Powered by SMF 1.1.3 | SMF © 2006-2007, Simple Machines LLC
Seo4Smf v0.2 © Webmaster's Talks