Skip to content

Commit 69eae3c

Browse files
committed
Merge branch 'ash2k/fix-data-race' into 'main'
Fix WebSocket tunnel data race Closes #39215 See merge request https://gitlab.com/gitlab-org/gitlab-runner/-/merge_requests/6237 Merged-by: Mikhail Mazurskiy <mmazurskiy@gitlab.com> Approved-by: Timo Furrer <tfurrer@gitlab.com> Reviewed-by: Timo Furrer <tfurrer@gitlab.com> Reviewed-by: GitLab Duo <gitlab-duo@gitlab.com>
2 parents 6619d60 + 1ab74c1 commit 69eae3c

2 files changed

Lines changed: 24 additions & 11 deletions

File tree

router/client.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,5 +132,8 @@ func (c *Client) getRouterDiscovery(ctx context.Context, config common.RunnerCon
132132
}
133133
c.disco = c.delegate.GetRouterDiscovery(ctx, config)
134134
c.discoExpiresAt = time.Now().Add(discoveryTTL)
135+
if c.disco != nil {
136+
config.Log().Info("Using job router at " + c.disco.ServerURL)
137+
}
135138
return c.disco
136139
}

router/internal/wstunnel/netconn.go

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"fmt"
66
"io"
77
"net"
8+
"sync/atomic"
89
"time"
910

1011
"github.com/gorilla/websocket"
@@ -35,9 +36,10 @@ func NetConn(c *websocket.Conn) net.Conn {
3536
}
3637

3738
type netConn struct {
38-
c *websocket.Conn
39-
reader io.Reader
40-
readEOFed bool
39+
c *websocket.Conn
40+
reader io.Reader
41+
futureWriteDeadline atomic.Pointer[time.Time]
42+
readEOFed bool
4143
}
4244

4345
func (nc *netConn) Close() (retErr error) {
@@ -53,6 +55,16 @@ func (nc *netConn) Close() (retErr error) {
5355
}
5456

5557
func (nc *netConn) Write(p []byte) (int, error) {
58+
old := nc.futureWriteDeadline.Swap(nil)
59+
if old != nil {
60+
// Unsynchronized write deadline field is read in the WriteMessage() call below.
61+
// Hence, it is safe to call SetWriteDeadline() here as it must not be called concurrently
62+
// since that would be a data race.
63+
err := nc.c.SetWriteDeadline(*old)
64+
if err != nil {
65+
return 0, err
66+
}
67+
}
5668
err := nc.c.WriteMessage(websocket.BinaryMessage, p)
5769
if err != nil {
5870
return 0, err
@@ -115,15 +127,13 @@ func (nc *netConn) SetDeadline(t time.Time) error {
115127
}
116128

117129
func (nc *netConn) SetWriteDeadline(t time.Time) error {
118-
// The method below doesn't set the write deadline on the underlying network connection, but it should.
119-
// Hence, we should call the underlying method too to make it possible to abort stuck writes.
120-
err := nc.c.SetWriteDeadline(t)
121-
if err != nil {
122-
return err
123-
}
124-
return nc.c.UnderlyingConn().SetWriteDeadline(t)
130+
// This method must be thread safe - e.g. it is safe to call concurrently to abort a connection.
131+
// We cannot use nc.c.SetWriteDeadline() here directly since it is not thread safe - cannot be called
132+
// concurrently with WriteMessage(). So, we are making our own version with similar functionality.
133+
nc.futureWriteDeadline.Store(&t)
134+
return nc.c.NetConn().SetWriteDeadline(t)
125135
}
126136

127137
func (nc *netConn) SetReadDeadline(t time.Time) error {
128-
return nc.c.UnderlyingConn().SetReadDeadline(t)
138+
return nc.c.NetConn().SetReadDeadline(t)
129139
}

0 commit comments

Comments
 (0)