Class: RandomPort::Pool

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

Overview

Pool of TCP 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 number of ports to acquire using limit. If more acquisition requests arrive, an exception will be raised.

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

Author

Yegor Bugayenko ([email protected])

Copyright

Copyright © 2018-2026 Yegor Bugayenko

License

MIT

Defined Under Namespace

Classes: Timeout

Constant Summary collapse

SINGLETON =

Application-wide singleton pool of ports.

RandomPort::Pool.new

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

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

Constructor.

Parameters:

  • sync (Boolean) (defaults to: true)

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

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

    The maximum number of ports that can be acquired from the pool

  • start (Integer) (defaults to: 1025)

    The first port number to try when acquiring



41
42
43
44
45
46
47
# File 'lib/random-port/pool.rb', line 41

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

Instance Attribute Details

#limitInteger (readonly)

Returns The maximum number of ports that can be acquired from this pool.

Returns:

  • (Integer)

    The maximum number of ports that can be acquired from this pool



35
36
37
# File 'lib/random-port/pool.rb', line 35

def limit
  @limit
end

Instance Method Details

#acquire(total = 1, timeout: 4) {|Integer|Array<Integer>| ... } ⇒ Integer|Array<Integer>

Acquires one or more available TCP ports from the pool.

You can specify the number of ports to acquire. If it’s more than one, an array will be returned. If a block is given, the port(s) will be automatically released after the block execution.

Parameters:

  • total (Integer) (defaults to: 1)

    The number of ports to acquire (default: 1)

  • timeout (Integer) (defaults to: 4)

    The maximum seconds to wait for port availability

Yields:

  • (Integer|Array<Integer>)

    The acquired port(s)

Returns:

  • (Integer|Array<Integer>)

    The acquired port(s) if no block given

Raises:

  • (Timeout)

    If ports cannot be acquired within the timeout period



76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
# File 'lib/random-port/pool.rb', line 76

def acquire(total = 1, timeout: 4)
  start = Time.now
  attempt = 0
  loop do
    if Time.now > start + timeout
      raise \
        Timeout,
        "Can't find a place in the pool of #{@limit} ports " \
        "(#{@ports.size} already occupied) " \
        "for #{total} port(s), after #{attempt} attempts in #{start.ago}"
    end
    attempt += 1
    opts = safe { group(total) }
    if opts.nil?
      @next += 1
    else
      @next = opts.max + 1
    end
    @next = 0 if @next > 65_535
    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

#countInteger Also known as: size

Returns the number of ports currently acquired from the pool.

Returns:

  • (Integer)

    The count of acquired ports



54
55
56
# File 'lib/random-port/pool.rb', line 54

def count
  @ports.count
end

#empty?Boolean

Checks if the pool is empty (no ports are currently acquired).

Returns:

  • (Boolean)

    TRUE if no ports are acquired, FALSE otherwise



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

def empty?
  @ports.empty?
end

#release(port) ⇒ nil

Releases one or more ports back to the pool.

Parameters:

  • port (Integer|Array<Integer>)

    The TCP port number(s) to release

Returns:

  • (nil)


109
110
111
112
113
114
115
116
117
# File 'lib/random-port/pool.rb', line 109

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