package testutil
|
|
import (
|
"container/list"
|
"fmt"
|
"net"
|
"os"
|
"sync"
|
"time"
|
)
|
|
var (
|
bindLock sync.Mutex
|
freeIPs *list.List
|
condNotEmpty *sync.Cond
|
)
|
|
const bindLockPort = 10101
|
|
func init() {
|
freeIPs = list.New()
|
condNotEmpty = sync.NewCond(&bindLock)
|
for octet := byte(10); octet < 255; octet++ {
|
result := net.IPv4(127, 0, 0, octet)
|
freeIPs.PushBack(result)
|
}
|
}
|
|
func returnIP(ip net.IP) {
|
bindLock.Lock()
|
defer bindLock.Unlock()
|
freeIPs.PushBack(ip)
|
condNotEmpty.Broadcast()
|
}
|
|
func getBindAddr() net.IP {
|
bindLock.Lock()
|
defer bindLock.Unlock()
|
|
for freeIPs.Len() == 0 {
|
condNotEmpty.Wait()
|
}
|
|
elem := freeIPs.Front()
|
freeIPs.Remove(elem)
|
result := elem.Value.(net.IP)
|
|
return result
|
}
|
|
func TakeIP() (ip net.IP, returnFn func()) {
|
for attempts := 0; ; attempts++ {
|
ip = getBindAddr()
|
|
addr := &net.TCPAddr{IP: ip, Port: bindLockPort}
|
|
ln, err := net.ListenTCP("tcp4", addr)
|
if err != nil {
|
returnIP(ip)
|
continue
|
}
|
|
if attempts > 3 {
|
logf("took %s after %d attempts", ip, attempts)
|
}
|
return ip, func() {
|
ln.Close()
|
time.Sleep(50 * time.Millisecond) // let the kernel cool down
|
returnIP(ip)
|
}
|
}
|
}
|
|
func logf(format string, a ...interface{}) {
|
fmt.Fprintf(os.Stdout, "testutil: "+format+"\n", a...)
|
}
|