Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • World
  • Users
  • Groups
Skins
  • Light
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • Dark
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

  • Default (No Skin)
  • No Skin
Collapse
Code Project
  1. Home
  2. General Programming
  3. C#
  4. interlocked linked list [modified]

interlocked linked list [modified]

Scheduled Pinned Locked Moved C#
data-structuresquestion
8 Posts 3 Posters 0 Views 1 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • M Offline
    M Offline
    mikewinny
    wrote on last edited by
    #1

    nevermind.. i think ill give up for now and just use a modified spinlock! im writing a simple lock-free queue (intended for efficient multithreaded use) and im not sure ive quite figured out the logic behind the use of Interlocked.CompareExchange(). would code like the following produce the correct result of adding a new linked list node to the tail of a list under multithreading conditions?? while (null != Interlocked.CompareExchange(ref _tail.next, newNode, null)) Thread.Sleep(1); _tail = newNode; thanks in advance ;) -- modified at 12:40 Monday 8th January, 2007


    I worship his divine shadow.

    L 1 Reply Last reply
    0
    • M mikewinny

      nevermind.. i think ill give up for now and just use a modified spinlock! im writing a simple lock-free queue (intended for efficient multithreaded use) and im not sure ive quite figured out the logic behind the use of Interlocked.CompareExchange(). would code like the following produce the correct result of adding a new linked list node to the tail of a list under multithreading conditions?? while (null != Interlocked.CompareExchange(ref _tail.next, newNode, null)) Thread.Sleep(1); _tail = newNode; thanks in advance ;) -- modified at 12:40 Monday 8th January, 2007


      I worship his divine shadow.

      L Offline
      L Offline
      Luc Pattyn
      wrote on last edited by
      #2

      Hi, I dont think this is thread-safe: you perform two operations on the list 1. CompareExchange 2. modify tail (_tail=newNode) the combined operations should be atomic. And I dont think you can achieve it with the Interlocked class at all, since inserting or removing a node to/from a linked list involves modifying two references or pointers, which is more than any Interlocked method offers. SO you will need a lock after all. :)

      Luc Pattyn

      M 1 Reply Last reply
      0
      • L Luc Pattyn

        Hi, I dont think this is thread-safe: you perform two operations on the list 1. CompareExchange 2. modify tail (_tail=newNode) the combined operations should be atomic. And I dont think you can achieve it with the Interlocked class at all, since inserting or removing a node to/from a linked list involves modifying two references or pointers, which is more than any Interlocked method offers. SO you will need a lock after all. :)

        Luc Pattyn

        M Offline
        M Offline
        mikewinny
        wrote on last edited by
        #3

        sorry, i dont think u were very convincing, im pretty sure this would work...

        • in neutral state, _tail.next is null; the end of the list.
        • the first thread executes the interlocked operation which succeeds and atomically sets _tail.next to something other than null.
        • this causes the interlocked operation to fail on any other thread, and spin.
        • the final assignment returns _tail.next to null (as a new list node has no next-node) and the list is coherent.

        i think i just answered my own question there but i do get the feeling im missing something. i have more of an issue dequeuing a list node; ascertaining just how the de/enqueue code can interfere with each other and how i can get it to safely execute in parallel.


        I worship his divine shadow. ^

        L 1 Reply Last reply
        0
        • M mikewinny

          sorry, i dont think u were very convincing, im pretty sure this would work...

          • in neutral state, _tail.next is null; the end of the list.
          • the first thread executes the interlocked operation which succeeds and atomically sets _tail.next to something other than null.
          • this causes the interlocked operation to fail on any other thread, and spin.
          • the final assignment returns _tail.next to null (as a new list node has no next-node) and the list is coherent.

          i think i just answered my own question there but i do get the feeling im missing something. i have more of an issue dequeuing a list node; ascertaining just how the de/enqueue code can interfere with each other and how i can get it to safely execute in parallel.


          I worship his divine shadow. ^

          L Offline
          L Offline
          Luc Pattyn
          wrote on last edited by
          #4

          Well, yes and no: Yes, if you have a single-linked list and a _last reference, then you can append a node to it correctly with your code, but that's about it. If there is no _head, and no backward link (prev), then you can not reach any other node. A single-linked list with a head only typically uses the following logic (I use a dummy head node to simplify code: when head.next is the first node (or null), then head itself never changes and never is null):

          void addNode(Node newNode) { // unsafe version
          newNode.next=head.next;
          head.next=newNode;
          }
          Node removeFirstNode() { // unsafe version
          Node firstNode=head.next;
          head.next=firstNode.next; // <<--- modified
          return firstNode;
          }

          the safe versions would be:

          void addNode(Node newNode) { // safe version
          do {
          Node firstNode=head.next;
          newNode.next=firstNode;
          } while (firstNode!=Interlocked.ExchangeCompare(head.next,newNode,firstNode));
          }

          Node removeFirstNode() { // safe version
          do {
          Node firstNode=head.next;
          } while (firstNode!=Interlocked.ExchangeCompare(head.next, firstNode.next, firstNode));
          return firstNode;
          }

          But now there are a lot of functional limitations: there is no access to any other node 1) we do not maintain a tail 2) we do not maintain a backward link so we have what is known as a stack, certainly not a queue. If we want more than stack functionality, we'd better have head+tail+nextlinks+prevlinks; but we must perform multiple stores atomically, and you cannot achieve that with a single Interlocked method. You can of course synthesize a lock yourself, something like:

          while (0!=Interlocked.ExchangeCompare(lockVar, 1, 0)) Thread.Sleep(1);
          // do whatever you want now, even multiple stores
          lockVar=0;
          

          but that is not what you intended, is it ? :) -- modified at 15:10 Monday 8th January, 2007

          Luc Pattyn

          //

          M N 2 Replies Last reply
          0
          • L Luc Pattyn

            Well, yes and no: Yes, if you have a single-linked list and a _last reference, then you can append a node to it correctly with your code, but that's about it. If there is no _head, and no backward link (prev), then you can not reach any other node. A single-linked list with a head only typically uses the following logic (I use a dummy head node to simplify code: when head.next is the first node (or null), then head itself never changes and never is null):

            void addNode(Node newNode) { // unsafe version
            newNode.next=head.next;
            head.next=newNode;
            }
            Node removeFirstNode() { // unsafe version
            Node firstNode=head.next;
            head.next=firstNode.next; // <<--- modified
            return firstNode;
            }

            the safe versions would be:

            void addNode(Node newNode) { // safe version
            do {
            Node firstNode=head.next;
            newNode.next=firstNode;
            } while (firstNode!=Interlocked.ExchangeCompare(head.next,newNode,firstNode));
            }

            Node removeFirstNode() { // safe version
            do {
            Node firstNode=head.next;
            } while (firstNode!=Interlocked.ExchangeCompare(head.next, firstNode.next, firstNode));
            return firstNode;
            }

            But now there are a lot of functional limitations: there is no access to any other node 1) we do not maintain a tail 2) we do not maintain a backward link so we have what is known as a stack, certainly not a queue. If we want more than stack functionality, we'd better have head+tail+nextlinks+prevlinks; but we must perform multiple stores atomically, and you cannot achieve that with a single Interlocked method. You can of course synthesize a lock yourself, something like:

            while (0!=Interlocked.ExchangeCompare(lockVar, 1, 0)) Thread.Sleep(1);
            // do whatever you want now, even multiple stores
            lockVar=0;
            

            but that is not what you intended, is it ? :) -- modified at 15:10 Monday 8th January, 2007

            Luc Pattyn

            //

            M Offline
            M Offline
            mikewinny
            wrote on last edited by
            #5

            yes i see using a dummy node makes things a heck of a lot easier with the interlocked stuff and i had considered it but i didnt think it would make it that easy. the queue does have a head btw, from which items are removed (added to the tail). at first thought i would need to look into whether a circular reference of the head would introduce some other issue. but for simplicity's sake (sister of felicity i believe) im gonna use a good old LinkedList and the sleeplock thing. i usually just lock () { } things but for this i certianly want to keep it lightweight. with a little modification to make it adaptive for an actual multi-processor system (ie. not relinquishing time slice) i think itll be quite scalable. thanks for your input


            I worship his divine shadow. ^

            L 1 Reply Last reply
            0
            • M mikewinny

              yes i see using a dummy node makes things a heck of a lot easier with the interlocked stuff and i had considered it but i didnt think it would make it that easy. the queue does have a head btw, from which items are removed (added to the tail). at first thought i would need to look into whether a circular reference of the head would introduce some other issue. but for simplicity's sake (sister of felicity i believe) im gonna use a good old LinkedList and the sleeplock thing. i usually just lock () { } things but for this i certianly want to keep it lightweight. with a little modification to make it adaptive for an actual multi-processor system (ie. not relinquishing time slice) i think itll be quite scalable. thanks for your input


              I worship his divine shadow. ^

              L Offline
              L Offline
              Luc Pattyn
              wrote on last edited by
              #6

              FYI, I tend to avoid circular linked lists; I typically use either a single-linked list with a dummy head node, or a double-linked list with a dummy head node and a dummy tail node (different from head); reason is you can then walk the list forwards/backwards (without removing nodes) until next/prev equals null. Combining the head and tail dummies would save a node, but that is typically not worth the hassle (unless nodes are really big of course). :)

              Luc Pattyn

              M 1 Reply Last reply
              0
              • L Luc Pattyn

                FYI, I tend to avoid circular linked lists; I typically use either a single-linked list with a dummy head node, or a double-linked list with a dummy head node and a dummy tail node (different from head); reason is you can then walk the list forwards/backwards (without removing nodes) until next/prev equals null. Combining the head and tail dummies would save a node, but that is typically not worth the hassle (unless nodes are really big of course). :)

                Luc Pattyn

                M Offline
                M Offline
                mikewinny
                wrote on last edited by
                #7

                true, and big nodes should be seen by a doctor lest they hinder the GC


                I worship his divine shadow. ^

                1 Reply Last reply
                0
                • L Luc Pattyn

                  Well, yes and no: Yes, if you have a single-linked list and a _last reference, then you can append a node to it correctly with your code, but that's about it. If there is no _head, and no backward link (prev), then you can not reach any other node. A single-linked list with a head only typically uses the following logic (I use a dummy head node to simplify code: when head.next is the first node (or null), then head itself never changes and never is null):

                  void addNode(Node newNode) { // unsafe version
                  newNode.next=head.next;
                  head.next=newNode;
                  }
                  Node removeFirstNode() { // unsafe version
                  Node firstNode=head.next;
                  head.next=firstNode.next; // <<--- modified
                  return firstNode;
                  }

                  the safe versions would be:

                  void addNode(Node newNode) { // safe version
                  do {
                  Node firstNode=head.next;
                  newNode.next=firstNode;
                  } while (firstNode!=Interlocked.ExchangeCompare(head.next,newNode,firstNode));
                  }

                  Node removeFirstNode() { // safe version
                  do {
                  Node firstNode=head.next;
                  } while (firstNode!=Interlocked.ExchangeCompare(head.next, firstNode.next, firstNode));
                  return firstNode;
                  }

                  But now there are a lot of functional limitations: there is no access to any other node 1) we do not maintain a tail 2) we do not maintain a backward link so we have what is known as a stack, certainly not a queue. If we want more than stack functionality, we'd better have head+tail+nextlinks+prevlinks; but we must perform multiple stores atomically, and you cannot achieve that with a single Interlocked method. You can of course synthesize a lock yourself, something like:

                  while (0!=Interlocked.ExchangeCompare(lockVar, 1, 0)) Thread.Sleep(1);
                  // do whatever you want now, even multiple stores
                  lockVar=0;
                  

                  but that is not what you intended, is it ? :) -- modified at 15:10 Monday 8th January, 2007

                  Luc Pattyn

                  //

                  N Offline
                  N Offline
                  nim int3
                  wrote on last edited by
                  #8

                  It not safe code read this http://www.cse.yorku.ca/~ruppert/Mikhail.pdf[^]

                  1 Reply Last reply
                  0
                  Reply
                  • Reply as topic
                  Log in to reply
                  • Oldest to Newest
                  • Newest to Oldest
                  • Most Votes


                  • Login

                  • Don't have an account? Register

                  • Login or register to search.
                  • First post
                    Last post
                  0
                  • Categories
                  • Recent
                  • Tags
                  • Popular
                  • World
                  • Users
                  • Groups