1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76
|
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...)
}
|