class RandomPort::Pool

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.

Author

Yegor Bugayenko (yegor256@gmail.com)

Copyright

Copyright © 2018 Yegor Bugayenko

License

MIT

Constants

SINGLETON

Application wide pool of ports

Attributes

limit[R]

Public Class Methods

new(sync: false, limit: 65_536) click to toggle source

Ctor.

# File lib/random-port/pool.rb, line 54
def initialize(sync: false, limit: 65_536)
  @ports = []
  @sync = sync
  @monitor = Monitor.new
  @limit = limit
end

Public Instance Methods

acquire(total = 1, timeout: 4) { |opts| ... } click to toggle source

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.

# File lib/random-port/pool.rb, line 82
    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] = i.zero? ? take : take(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?
        opts = opts[0] if total == 1
        return opts unless block_given?
        begin
          return yield opts
        ensure
          release(opts)
        end
      end
    end
count() click to toggle source

How many ports acquired now?

# File lib/random-port/pool.rb, line 65
def count
  @ports.count
end
Also aliased as: size
empty?() click to toggle source

Is it empty?

# File lib/random-port/pool.rb, line 71
def empty?
  @ports.empty?
end
release(port) click to toggle source

Return it/them back to the pool.

# File lib/random-port/pool.rb, line 117
def release(port)
  safe do
    if port.is_a?(Array)
      port.each { |p| @ports.delete(p) }
    else
      @ports.delete(port)
    end
  end
end
size()
Alias for: count

Private Instance Methods

safe() { || ... } click to toggle source
# File lib/random-port/pool.rb, line 136
def safe
  if @sync
    @monitor.synchronize { yield }
  else
    yield
  end
end
take(opt = 0) click to toggle source
# File lib/random-port/pool.rb, line 129
def take(opt = 0)
  server = TCPServer.new('127.0.0.1', opt)
  p = server.addr[1]
  server.close
  p
end