Class: RandomPort::Pool

Inherits:
Object
  • Object
show all
Defined in:
lib/random-port/pool.rb

Overview

Pool of TPC ports.

Use it like this:

RandomPort::Pool.new.acquire do |port|
  # Use the TCP port. It will be returned back
  # to the pool afterwards.
end

You can specify the maximum amount of ports to acquire, using limit. If more acquiring requests will arrive, an exception will be raised.

The class is thread-safe, by default. You can configure it to be not-thread-safe, using optional sync argument of the constructor, passing FALSE.

Author

Yegor Bugayenko ([email protected])

Copyright

Copyright © 2018-2024 Yegor Bugayenko

License

MIT

Defined Under Namespace

Classes: Timeout

Constant Summary collapse

SINGLETON =

Application wide pool of ports

RandomPort::Pool.new

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(sync: true, limit: 65_536) ⇒ Pool

Ctor.

Parameters:

  • sync (Boolean) (defaults to: true)

    Set it to FALSE if you want this pool to be NOT thread-safe

  • limit (Integer) (defaults to: 65_536)

    Set the maximum number of ports in the pool



57
58
59
60
61
62
63
# File 'lib/random-port/pool.rb', line 57

def initialize(sync: true, limit: 65_536)
  @ports = []
  @sync = sync
  @monitor = Monitor.new
  @limit = limit
  @next = 0
end

Instance Attribute Details

#limitObject (readonly)

Returns the value of attribute limit.



52
53
54
# File 'lib/random-port/pool.rb', line 52

def limit
  @limit
end

Instance Method Details

#acquire(total = 1, timeout: 4) ⇒ Object

Acquire a new random TCP port.

You can specify the number of ports to acquire. If it’s more than one, an array will be returned.

You can specify the amount of seconds to wait until a new port is available.



86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
# File 'lib/random-port/pool.rb', line 86

def acquire(total = 1, timeout: 4)
  start = Time.now
  loop do
    if Time.now > start + timeout
      raise \
        Timeout,
        "Can't find a place in the pool of #{@limit} ports " \
        "for #{total} port(s), in #{format('%.02f', Time.now - start)}s"
    end
    opts = safe do
      next if @ports.count + total > @limit
      opts = Array.new(0, total)
      begin
        (0..(total - 1)).each do |i|
          opts[i] = take(i.zero? ? @next : opts[i - 1] + 1)
        end
      rescue Errno::EADDRINUSE, SocketError
        next
      end
      next if opts.any? { |p| @ports.include?(p) }
      d = total * (total - 1) / 2
      next unless opts.inject(&:+) - (total * opts.min) == d
      @ports += opts
      opts
    end
    next if opts.nil?
    @next = opts.max + 1
    @next = 0 if @next > 65_535
    opts = opts[0] if total == 1
    return opts unless block_given?
    begin
      return yield opts
    ensure
      release(opts)
    end
  end
end

#countObject Also known as: size

How many ports acquired now?



69
70
71
# File 'lib/random-port/pool.rb', line 69

def count
  @ports.count
end

#empty?Boolean

Is it empty?

Returns:

  • (Boolean)


75
76
77
# File 'lib/random-port/pool.rb', line 75

def empty?
  @ports.empty?
end

#release(port) ⇒ Object

Return it/them back to the pool.



125
126
127
128
129
130
131
132
133
# File 'lib/random-port/pool.rb', line 125

def release(port)
  safe do
    if port.is_a?(Array)
      port.each { |p| @ports.delete(p) }
    else
      @ports.delete(port)
    end
  end
end