• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

lightningnetwork / lnd / 12199391122

06 Dec 2024 01:10PM UTC coverage: 49.807% (-9.1%) from 58.933%
12199391122

push

github

web-flow
Merge pull request #9337 from Guayaba221/patch-1

chore: fix typo in ruby.md

100137 of 201051 relevant lines covered (49.81%)

2.07 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/lntest/port/port.go
1
package port
2

3
import (
4
        "fmt"
5
        "net"
6
        "os"
7
        "path/filepath"
8
        "strconv"
9
        "sync"
10
        "time"
11
)
12

13
const (
14
        // ListenerFormat is the format string that is used to generate local
15
        // listener addresses.
16
        ListenerFormat = "127.0.0.1:%d"
17

18
        // defaultNodePort is the start of the range for listening ports of
19
        // harness nodes. Ports are monotonically increasing starting from this
20
        // number and are determined by the results of NextAvailablePort().
21
        defaultNodePort int = 10000
22

23
        // uniquePortFile is the name of the file that is used to store the
24
        // last port that was used by a node. This is used to make sure that
25
        // the same port is not used by multiple nodes at the same time. The
26
        // file is located in the temp directory of a system.
27
        uniquePortFile = "rpctest-port"
28
)
29

30
var (
31
        // portFileMutex is a mutex that is used to make sure that the port file
32
        // is not accessed by multiple goroutines of the same process at the
33
        // same time. This is used in conjunction with the lock file to make
34
        // sure that the port file is not accessed by multiple processes at the
35
        // same time either. So the lock file is to guard between processes and
36
        // the mutex is to guard between goroutines of the same process.
37
        portFileMutex sync.Mutex
38
)
39

40
// NextAvailablePort returns the first port that is available for listening by a
41
// new node, using a lock file to make sure concurrent access for parallel tasks
42
// on the same system don't re-use the same port.
43
func NextAvailablePort() int {
×
44
        portFileMutex.Lock()
×
45
        defer portFileMutex.Unlock()
×
46

×
47
        lockFile := filepath.Join(os.TempDir(), uniquePortFile+".lock")
×
48
        timeout := time.After(time.Second)
×
49

×
50
        var (
×
51
                lockFileHandle *os.File
×
52
                err            error
×
53
        )
×
54
        for {
×
55
                // Attempt to acquire the lock file. If it already exists, wait
×
56
                // for a bit and retry.
×
57
                lockFileHandle, err = os.OpenFile(
×
58
                        lockFile, os.O_CREATE|os.O_EXCL, 0600,
×
59
                )
×
60
                if err == nil {
×
61
                        // Lock acquired.
×
62
                        break
×
63
                }
64

65
                // Wait for a bit and retry.
66
                select {
×
67
                case <-timeout:
×
68
                        panic("timeout waiting for lock file")
×
69
                case <-time.After(10 * time.Millisecond):
×
70
                }
71
        }
72

73
        // Release the lock file when we're done.
74
        defer func() {
×
75
                // Always close file first, Windows won't allow us to remove it
×
76
                // otherwise.
×
77
                _ = lockFileHandle.Close()
×
78
                err := os.Remove(lockFile)
×
79
                if err != nil {
×
80
                        panic(fmt.Errorf("couldn't remove lock file: %w", err))
×
81
                }
82
        }()
83

84
        portFile := filepath.Join(os.TempDir(), uniquePortFile)
×
85
        port, err := os.ReadFile(portFile)
×
86
        if err != nil {
×
87
                if !os.IsNotExist(err) {
×
88
                        panic(fmt.Errorf("error reading port file: %w", err))
×
89
                }
90
                port = []byte(strconv.Itoa(defaultNodePort))
×
91
        }
92

93
        lastPort, err := strconv.Atoi(string(port))
×
94
        if err != nil {
×
95
                panic(fmt.Errorf("error parsing port: %w", err))
×
96
        }
97

98
        // We take the next one.
99
        lastPort++
×
100
        for lastPort < 65535 {
×
101
                // If there are no errors while attempting to listen on this
×
102
                // port, close the socket and return it as available. While it
×
103
                // could be the case that some other process picks up this port
×
104
                // between the time the socket is closed, and it's reopened in
×
105
                // the harness node, in practice in CI servers this seems much
×
106
                // less likely than simply some other process already being
×
107
                // bound at the start of the tests.
×
108
                addr := fmt.Sprintf(ListenerFormat, lastPort)
×
109
                l, err := net.Listen("tcp4", addr)
×
110
                if err == nil {
×
111
                        err := l.Close()
×
112
                        if err == nil {
×
113
                                err := os.WriteFile(
×
114
                                        portFile,
×
115
                                        []byte(strconv.Itoa(lastPort)), 0600,
×
116
                                )
×
117
                                if err != nil {
×
118
                                        panic(fmt.Errorf("error updating "+
×
119
                                                "port file: %w", err))
×
120
                                }
121

122
                                return lastPort
×
123
                        }
124
                }
125
                lastPort++
×
126

×
127
                // Start from the beginning if we reached the end of the port
×
128
                // range. We need to do this because the lock file now is
×
129
                // persistent across runs on the same machine during the same
×
130
                // boot/uptime cycle. So in order to make this work on
×
131
                // developer's machines, we need to reset the port to the
×
132
                // default value when we reach the end of the range.
×
133
                if lastPort == 65535 {
×
134
                        lastPort = defaultNodePort
×
135
                }
×
136
        }
137

138
        // No ports available? Must be a mistake.
139
        panic("no ports available for listening")
×
140
}
141

142
// GenerateSystemUniqueListenerAddresses is a function that returns two
143
// listener addresses with unique ports per system and should be used to
144
// overwrite rpctest's default generator which is prone to use colliding ports.
145
func GenerateSystemUniqueListenerAddresses() (string, string) {
×
146
        port1 := NextAvailablePort()
×
147
        port2 := NextAvailablePort()
×
148
        return fmt.Sprintf(ListenerFormat, port1),
×
149
                fmt.Sprintf(ListenerFormat, port2)
×
150
}
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc