nicoolas25
10/12/2014 - 12:02 AM

A small class that allows to loop over a changing set of values

A small class that allows to loop over a changing set of values

<#3> taking job #0
<#1> taking job #1
<#2> taking job #2
<#0> taking job #3
<#4> taking job #4
<#2> releasing job #2
<#2> taking job #5
<#1> releasing job #1
<#1> taking job #6
<#4> releasing job #4
<#4> taking job #7
<#3> releasing job #0
<#3> taking job #8
<#0> releasing job #3
<#0> taking job #9
<#2> releasing job #5
<#2> taking job #10
<#3> releasing job #8
<#3> taking job #11
<#1> releasing job #6
<#1> taking job #12
<#2> releasing job #10
<#2> taking job #13
<#4> releasing job #7
<#4> taking job #14
<#0> releasing job #9
<#0> taking job #15
<#0> releasing job #15
<#0> taking job #16
<#1> releasing job #12
<#1> taking job #17
<#2> releasing job #13
<#2> taking job #18
<#4> releasing job #14
<#4> taking job #19
<#3> releasing job #11
<#3> taking job #20
<#2> releasing job #18
<#2> taking job #21
<#1> releasing job #17
<#1> taking job #22
<#0> releasing job #16
<#0> taking job #23
<#2> releasing job #21
<#2> taking job #24
<#0> releasing job #23
<#0> taking job #25
<#1> releasing job #22
<#1> taking job #26
<#4> releasing job #19
<#4> taking job #27
<#3> releasing job #20
<#3> taking job #28
<#1> releasing job #26
<#1> taking job #29
<#2> releasing job #24
<#2> taking job #30
<#4> releasing job #27
<#4> taking job #31
<#3> releasing job #28
<#3> taking job #32
<#2> releasing job #30
<#2> taking job #33
<#4> releasing job #31
<#4> taking job #34
<#0> releasing job #25
<#0> taking job #35
<#1> releasing job #29
<#1> taking job #36
<#2> releasing job #33
<#2> taking job #37
<#3> releasing job #32
<#3> taking job #38
<#1> releasing job #36
<#1> taking job #39
<#4> releasing job #34
<#4> taking job #40
<#0> releasing job #35
<#0> taking job #41
<#3> releasing job #38
<#3> taking job #42
<#2> releasing job #37
<#2> taking job #43
<#4> releasing job #40
<#4> taking job #44
<#1> releasing job #39
<#1> taking job #45
<#2> releasing job #43
<#2> taking job #46
<#0> releasing job #41
<#0> taking job #47
<#3> releasing job #42
<#3> taking job #48
<#4> releasing job #44
<#4> taking job #49
<#1> releasing job #45
<#1> taking job #50
<#2> releasing job #46
<#2> taking job #2
<#0> releasing job #47
<#0> taking job #1
<#1> releasing job #50
<#1> taking job #4
<#3> releasing job #48
<#3> taking job #0
<#4> releasing job #49
<#4> taking job #3
<#0> releasing job #1
<#0> taking job #5
<#2> releasing job #2
<#2> taking job #8
<#4> releasing job #3
<#4> taking job #6
<#0> releasing job #5
<#0> taking job #10
<#1> releasing job #4
<#1> taking job #7
<#3> releasing job #0
<#3> taking job #9
<#2> releasing job #8
<#2> taking job #15
<#4> releasing job #6
<#4> taking job #12
<#0> releasing job #10
<#0> taking job #13
<#1> releasing job #7
<#1> taking job #14
<#4> releasing job #12
<#4> taking job #11
<#2> releasing job #15
<#2> taking job #18
<#3> releasing job #9
<#3> taking job #17
<#2> releasing job #18
<#2> taking job #16
<#1> releasing job #14
<#1> taking job #21
<#0> releasing job #13
<#0> taking job #23
<#4> releasing job #11
<#4> taking job #22
<#3> releasing job #17
<#3> taking job #19
<#2> releasing job #16
<#2> taking job #20
<#4> releasing job #22
<#4> taking job #26
<#0> releasing job #23
<#0> taking job #24
<#1> releasing job #21
<#1> taking job #27
<#3> releasing job #19
<#3> taking job #28
<#0> releasing job #24
<#0> taking job #30
<#4> releasing job #26
<#4> taking job #31
<#2> releasing job #20
<#2> taking job #25
<#1> releasing job #27
<#1> taking job #29
<#3> releasing job #28
<#3> taking job #33
<#0> releasing job #30
<#0> taking job #32
<#1> releasing job #29
<#1> taking job #36
<#4> releasing job #31
<#4> taking job #34
<#0> releasing job #32
<#0> taking job #35
<#2> releasing job #25
<#2> taking job #38
<#1> releasing job #36
<#1> taking job #37
<#3> releasing job #33
<#3> taking job #40
<#4> releasing job #34
<#4> taking job #39
<#2> releasing job #38
<#2> taking job #43
<#1> releasing job #37
<#1> taking job #41
<#0> releasing job #35
<#0> taking job #42
<#1> releasing job #41
<#1> taking job #44
<#2> releasing job #43
<#2> taking job #45
<#0> releasing job #42
<#0> taking job #46
<#3> releasing job #40
<#3> taking job #47
<#4> releasing job #39
<#4> taking job #50
<#3> releasing job #47
<#3> taking job #48
<#1> releasing job #44
<#1> taking job #49
<#2> releasing job #45
<#2> taking job #1
<#0> releasing job #46
<#4> releasing job #50
<#0> taking job #2
<#4> taking job #3
<#2> releasing job #1
<#2> taking job #5
<#3> releasing job #48
<#3> taking job #4
<#1> releasing job #49
<#1> taking job #0
<#4> releasing job #3
<#4> taking job #8
<#2> releasing job #5
<#2> taking job #6
<#0> releasing job #2
<#0> taking job #10
<#3> releasing job #4
<#3> taking job #7
<#0> releasing job #10
<#0> taking job #12
<#1> releasing job #0
<#1> taking job #15
<#4> releasing job #8
<#4> taking job #9
<#3> releasing job #7
<#3> taking job #18
<#2> releasing job #6
<#2> taking job #14
<#1> releasing job #15
<#1> taking job #13
<#4> releasing job #9
<#4> taking job #11
<#0> releasing job #12
<#0> taking job #17
<#3> releasing job #18
<#3> taking job #16
<#1> releasing job #13
<#1> taking job #22
<#4> releasing job #11
<#4> taking job #23
<#2> releasing job #14
<#2> taking job #21
<#0> releasing job #17
<#0> taking job #19
<#1> releasing job #22
<#1> taking job #24
<#3> releasing job #16
<#3> taking job #26
<#4> releasing job #23
<#4> taking job #20
<#2> releasing job #21
<#2> taking job #27
<#1> releasing job #24
<#1> taking job #28
<#0> releasing job #19
<#0> taking job #30
<#0> releasing job #30
<#0> taking job #29
<#1> releasing job #28
<#1> taking job #31
<#3> releasing job #26
<#3> taking job #32
<#4> releasing job #20
<#4> taking job #25
<#2> releasing job #27
<#2> taking job #36
<#0> releasing job #29
<#0> taking job #33
<#2> releasing job #36
<#2> taking job #34
<#1> releasing job #31
<#1> taking job #38
<#0> releasing job #33
<#0> taking job #37
<#4> releasing job #25
<#4> taking job #35
<#3> releasing job #32
<#3> taking job #41
<#2> releasing job #34
<#2> taking job #43
<#1> releasing job #38
<#1> taking job #42
<#0> releasing job #37
<#0> taking job #40
<#1> releasing job #42
<#1> taking job #39
<#4> releasing job #35
<#4> taking job #47
<#2> releasing job #43
<#2> taking job #44
<#3> releasing job #41
<#3> taking job #45
<#0> releasing job #40
<#0> taking job #46
<#1> releasing job #39
<#1> taking job #50
<#2> releasing job #44
<#2> taking job #1
<#0> releasing job #46
<#0> taking job #48
<#3> releasing job #45
<#3> taking job #49
<#1> releasing job #50
<#1> taking job #3
<#4> releasing job #47
<#4> taking job #5
<#2> releasing job #1
<#2> taking job #2
<#0> releasing job #48
<#0> taking job #4
<#1> releasing job #3
<#1> taking job #10
<#3> releasing job #49
<#3> taking job #0
<#0> releasing job #4
<#0> taking job #8
<#4> releasing job #5
<#4> taking job #7
<#2> releasing job #2
<#2> taking job #6
<#0> releasing job #8
<#0> taking job #15
<#3> releasing job #0
<#3> taking job #9
<#1> releasing job #10
<#1> taking job #12
<#0> releasing job #15
<#0> taking job #18
<#2> releasing job #6
<#2> taking job #13
<#3> releasing job #9
<#3> taking job #11
<#4> releasing job #7
<#4> taking job #14
<#1> releasing job #12
<#1> taking job #17
<#3> releasing job #11
<#3> taking job #22
<#2> releasing job #13
<#2> taking job #16
<#3> releasing job #22
<#3> taking job #23
<#0> releasing job #18
<#0> taking job #21
<#1> releasing job #17
<#1> taking job #24
<#4> releasing job #14
<#4> taking job #19
<#2> releasing job #16
<#2> taking job #30
<#1> releasing job #24
<#1> taking job #28
<#3> releasing job #23
<#3> taking job #26
<#2> releasing job #30
<#2> taking job #20
<#0> releasing job #21
<#0> taking job #27
<#4> releasing job #19
<#4> taking job #29
<#0> releasing job #27
<#0> taking job #36
<#2> releasing job #20
<#2> taking job #31
<#1> releasing job #28
<#1> taking job #33
<#3> releasing job #26
<#3> taking job #25
<#0> releasing job #36
<#0> taking job #32
<#4> releasing job #29
<#4> taking job #34
<#1> releasing job #33
<#1> taking job #38
<#2> releasing job #31
<#2> taking job #37
<#3> releasing job #25
<#3> taking job #42
<#3> releasing job #42
<#3> taking job #35
<#0> releasing job #32
<#0> taking job #43
<#4> releasing job #34
<#4> taking job #41
<#2> releasing job #37
<#2> taking job #40
<#0> releasing job #43
<#0> taking job #39
<#1> releasing job #38
<#1> taking job #44
<#2> releasing job #40
<#2> taking job #46
<#0> releasing job #39
<#0> taking job #45
<#3> releasing job #35
<#3> taking job #50
<#1> releasing job #44
<#1> taking job #47
<#4> releasing job #41
<#4> taking job #1
<#2> releasing job #46
<#2> taking job #48
<#4> releasing job #1
<#4> taking job #3
<#1> releasing job #47
<#1> taking job #49
<#2> releasing job #48
<#2> taking job #4
<#0> releasing job #45
<#0> taking job #5
<#3> releasing job #50
<#3> taking job #2
<#4> releasing job #3
<#4> taking job #8
<#0> releasing job #5
<#0> taking job #0
<#2> releasing job #4
<#2> taking job #10
<#1> releasing job #49
<#1> taking job #15
<#2> releasing job #10
<#2> taking job #6
<#3> releasing job #2
<#3> taking job #9
<#4> releasing job #8
<#4> taking job #7
<#0> releasing job #0
<#0> taking job #12
<#1> releasing job #15
<#1> taking job #11
<#1> releasing job #11
<#1> taking job #13
<#3> releasing job #9
<#3> taking job #22
<#0> releasing job #12
<#0> taking job #18
<#4> releasing job #7
<#4> taking job #17
<#2> releasing job #6
<#2> taking job #14
<#1> releasing job #13
<#1> taking job #16
<#3> releasing job #22
<#3> taking job #24
<#2> releasing job #14
<#2> taking job #23
<#0> releasing job #18
<#0> taking job #30
<#4> releasing job #17
<#4> taking job #21
<#1> releasing job #16
<#1> taking job #19
<#2> releasing job #23
<#2> taking job #27
<#0> releasing job #30
<#0> taking job #20
<#3> releasing job #24
<#3> taking job #28
<#3> releasing job #28
<#3> taking job #26
<#4> releasing job #21
<#4> taking job #36
<#2> releasing job #27
<#2> taking job #29
<#1> releasing job #19
<#1> taking job #33
<#0> releasing job #20
<#0> taking job #31
Killing worker #0
Killing worker #1
Killing worker #2
Killing worker #3
Killing worker #4
<#0> releasing job #31
<#1> releasing job #33
<#2> releasing job #29
<#3> releasing job #26
<#4> releasing job #36
require_relative 'equiset'

WORKER_COUNT = 5
JOB_COUNT = 10 * WORKER_COUNT
DURATION = 60

# Create and fill the pool with integer jobs
pool = EquiSet.new
(0..JOB_COUNT).each do |i|
  pool.append(i)
end

def do_work(pool, thread_id, random)
  job = pool.take
  if job
    puts "<##{thread_id}> taking job ##{job}"
    sleep_time = 0.5 + random.rand(1.5)
    sleep sleep_time
  else
    puts "<##{thread_id}> no free job, waiting a few seconds..."
    sleep 3
  end
ensure
  if job
    puts "<##{thread_id}> releasing job ##{job}"
    pool.release(job)
  end
end

# Spawn workers
workers = (0...WORKER_COUNT).map do |i|
  Thread.new do
    random = Random.new
    loop do
      do_work(pool, i, random)
    end
  end
end

# Let the program run during DURATION seconds
sleep DURATION

workers.each_with_index do |worker, i|
  puts "Killing worker ##{i}"
  worker.kill
end
require 'set'
require 'thread'

# A thread safe pool of values that can change.
class EquiSet
  def initialize
    @sorted_set = SortedSet.new
    @mutex = Monitor.new
  end

  def include?(value)
    item = Item.new(value)
    @sorted_set.include?(item)
  end

  def append(value)
    safely do
      item = Item.new(value)
      @sorted_set.add(item)
    end
  end

  # Deletes a value in the pool. Returns nil if the
  # value wasn't there and returns the value otherwise.
  def delete(value)
    safely do
      item = Item.new(value)
      item = @sorted_set.delete(item)
      item && item.value
    end
  end

  # Takes the first non-taken item if the pool.
  def take
    safely do
      # If there is a free item, it is at the begining of
      # the set because the way the set is sorted
      # see `Item#<=>`.
      item = @sorted_set.first

      if item && item.free?
        # Deletes the matching item
        @sorted_set.delete(item)

        # Adds a new but non-free item with the same value
        new_item = Item.new(item.value, free: false)
        @sorted_set.add(new_item)

        # Return the value of the matching item
        item.value
      end
    end
  end

  # Release a non-free value if it exists. It append the value
  # at the end of the pool. It returns nil if the value wasn't
  # there anymore and the value itself otherwise.
  def release(value)
    safely do
      if delete(value)
        append(value)
        value
      end
    end
  end

  def safely(&block)
    @mutex.synchronize do
      block.call
    end
  end

  # Define a class that is used by the EquiSet to sort the elements.
  # Each item is tagged with its creation date. The Item provide
  # the adequate methods to be used in a SortedSet: <=>, hash and eql?.
  class Item
    attr_reader :value
    attr_reader :key

    def initialize(value, free: true)
      @value = value
      @key = Time.now
      @free = !!free
    end

    def free?
      @free
    end

    def <=>(other)
      # Same values are the same item
      if other.value == @value
        return 0
      end

      # Free items goes first
      if other.free? != @free
        return @free ? -1 : 1
      end

      # Order by key (timestamp) otherwise
      @key <=> other.key
    end

    def hash
      @value.hash
    end

    def ==(other)
      other.value == @value
    end

    alias eql? ==
  end
end