123456789_123456789_123456789_123456789_123456789_

Class: Concurrent::LockFreeQueue

Relationships & Source Files
Namespace Children
Classes:
Super Chains via Extension / Inclusion / Inheritance
Class Chain:
Instance Chain:
Inherits: Concurrent::Synchronization::Object
Defined in: lib/concurrent-ruby-edge/concurrent/edge/lock_free_queue.rb

Class Attribute Summary

Class Method Summary

Synchronization::Object - Inherited

.atomic_attribute?, .atomic_attributes,
.attr_atomic

Creates methods for reading and writing to a instance variable with volatile (Java) semantic as .attr_volatile does.

.attr_volatile

Creates methods for reading and writing (as attr_accessor does) to a instance variable with volatile (Java) semantic.

.ensure_safe_initialization_when_final_fields_are_present

For testing purposes, quite slow.

.new

Has to be called by children.

.safe_initialization!, .define_initialize_atomic_fields

Synchronization::AbstractObject - Inherited

Instance Method Summary

Constructor Details

.newLockFreeQueue

[ GitHub ]

  
# File 'lib/concurrent-ruby-edge/concurrent/edge/lock_free_queue.rb', line 27

def initialize
  super()
  dummy_node = Node.new(:dummy, nil)

  self.head = dummy_node
  self.tail = dummy_node
end

Instance Method Details

#compare_and_set_head(expected_head, new_head) ⇒ true, false

Sets the head to new_head if the current head is expected_head

[ GitHub ]

  
# File 'lib/concurrent-ruby-edge/concurrent/edge/lock_free_queue.rb', line 25

attr_atomic :head, :tail

#headObject

Returns:

[ GitHub ]

  
# File 'lib/concurrent-ruby-edge/concurrent/edge/lock_free_queue.rb', line 25

attr_atomic :head, :tail

#head=(new_head) ⇒ Object

Set the head.

Returns:

[ GitHub ]

  
# File 'lib/concurrent-ruby-edge/concurrent/edge/lock_free_queue.rb', line 25

attr_atomic :head, :tail

#pop

[ GitHub ]

  
# File 'lib/concurrent-ruby-edge/concurrent/edge/lock_free_queue.rb', line 65

def pop
  # retry until some value can be returned
  while true
    # the value in @head is just a dummy node that always sits in that position,
    # the real 'head' is in its successor
    current_dummy_node = head
    current_tail_node  = tail

    current_head_node = current_dummy_node.successor

    # if our local head is still consistent with the head node, continue
    # otherwise, start over
    if current_dummy_node == head
      # if either the queue is empty, or falling behind
      if current_dummy_node == current_tail_node
        # if there's nothing after the 'dummy' head node
        if current_head_node.nil?
          # just return nil
          return nil
        else
          # here the head element succeeding head is not nil, but the head and tail are equal
          # so tail is falling behind, update it, then start over
          compare_and_set_tail(current_tail_node, current_head_node)
        end

        # the queue isn't empty
        # if we can set the dummy head to the 'real' head, we're free to return the value in that real head, success
      elsif compare_and_set_head(current_dummy_node, current_head_node)
        # grab the item from the popped node
        item = current_head_node.item

        # return it, success!
        return item
      end
    end
  end
end

#push(item)

[ GitHub ]

  
# File 'lib/concurrent-ruby-edge/concurrent/edge/lock_free_queue.rb', line 35

def push(item)
  # allocate a new node with the item embedded
  new_node = Node.new(item, nil)

  # keep trying until the operation succeeds
  while true
    current_tail_node      = tail
    current_tail_successor = current_tail_node.successor

    # if our stored tail is still the current tail
    if current_tail_node == tail
      # if that tail was really the last node
      if current_tail_successor.nil?
        # if we can update the previous successor of tail to point to this new node
        if current_tail_node.compare_and_set_successor(nil, new_node)
          # then update tail to point to this node as well
          compare_and_set_tail(current_tail_node, new_node)
          # and return
          return true
          # else, start the loop over
        end
      else
        # in this case, the tail ref we had wasn't the real tail
        # so we try to set its successor as the real tail, then start the loop again
        compare_and_set_tail(current_tail_node, current_tail_successor)
      end
    end
  end
end

#size

approximate

[ GitHub ]

  
# File 'lib/concurrent-ruby-edge/concurrent/edge/lock_free_queue.rb', line 104

def size
  successor = head.successor
  count     = 0

  while true
    break if successor.nil?

    current_node = successor
    successor    = current_node.successor
    count        += 1
  end

  count
end

#swap_head(new_head) ⇒ Object

Set the head to new_head and return the old head.

Returns:

[ GitHub ]

  
# File 'lib/concurrent-ruby-edge/concurrent/edge/lock_free_queue.rb', line 25

attr_atomic :head, :tail

#update_head {|Object| ... } ⇒ Object

Updates the head using the block.

Yields:

  • (Object)

    Calculate a new head using given (old) head

Yield Parameters:

Returns:

[ GitHub ]

  
# File 'lib/concurrent-ruby-edge/concurrent/edge/lock_free_queue.rb', line 25

attr_atomic :head, :tail