Skip to content

Commit 31df3a5

Browse files
authored
Merge pull request #154 from nhooyr/fix-atomic
Fix unaligned 64 bit atomic loads on 32 bit platforms
2 parents 0e00760 + 4b51f4a commit 31df3a5

File tree

4 files changed

+43
-15
lines changed

4 files changed

+43
-15
lines changed

ci/wasm.sh

+2
Original file line numberDiff line numberDiff line change
@@ -26,5 +26,7 @@ GOOS=js GOARCH=wasm go test -exec=wasmbrowsertest ./... -args "$WS_ECHO_SERVER_U
2626

2727
if ! wait "$wsjstestPID"; then
2828
echo "wsjstest exited unsuccessfully"
29+
echo "output:"
30+
cat "$wsjstestOut"
2931
exit 1
3032
fi

conn.go

+8-7
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,7 @@ import (
2020
)
2121

2222
// Conn represents a WebSocket connection.
23-
// All methods may be called concurrently except for Reader, Read
24-
// and SetReadLimit.
23+
// All methods may be called concurrently except for Reader and Read.
2524
//
2625
// You must always read from the connection. Otherwise control
2726
// frames will not be handled. See the docs on Reader and CloseRead.
@@ -56,7 +55,7 @@ type Conn struct {
5655
writeHeaderBuf []byte
5756
writeHeader *header
5857
// read limit for a message in bytes.
59-
msgReadLimit int64
58+
msgReadLimit *atomicInt64
6059

6160
// Used to ensure a previous writer is not used after being closed.
6261
activeWriter atomic.Value
@@ -70,7 +69,7 @@ type Conn struct {
7069
activeReader *messageReader
7170
// readFrameLock is acquired to read from bw.
7271
readFrameLock chan struct{}
73-
readClosed int64
72+
readClosed *atomicInt64
7473
readHeaderBuf []byte
7574
controlPayloadBuf []byte
7675

@@ -90,7 +89,8 @@ type Conn struct {
9089
func (c *Conn) init() {
9190
c.closed = make(chan struct{})
9291

93-
c.msgReadLimit = 32768
92+
c.msgReadLimit = &atomicInt64{}
93+
c.msgReadLimit.Store(32768)
9494

9595
c.writeMsgLock = make(chan struct{}, 1)
9696
c.writeFrameLock = make(chan struct{}, 1)
@@ -105,6 +105,7 @@ func (c *Conn) init() {
105105
c.writeHeaderBuf = makeWriteHeaderBuf()
106106
c.writeHeader = &header{}
107107
c.readHeaderBuf = makeReadHeaderBuf()
108+
c.readClosed = &atomicInt64{}
108109
c.controlPayloadBuf = make([]byte, maxControlFramePayload)
109110

110111
runtime.SetFinalizer(c, func(c *Conn) {
@@ -341,7 +342,7 @@ func (c *Conn) handleControl(ctx context.Context, h header) error {
341342
// See https://github.com/nhooyr/websocket/issues/87#issue-451703332
342343
// Most users should not need this.
343344
func (c *Conn) Reader(ctx context.Context) (MessageType, io.Reader, error) {
344-
if atomic.LoadInt64(&c.readClosed) == 1 {
345+
if c.readClosed.Load() == 1 {
345346
return 0, nil, fmt.Errorf("websocket connection read closed")
346347
}
347348

@@ -391,7 +392,7 @@ func (c *Conn) reader(ctx context.Context) (MessageType, io.Reader, error) {
391392
c.readerMsgHeader = h
392393
c.readerFrameEOF = false
393394
c.readerMaskPos = 0
394-
c.readMsgLeft = c.msgReadLimit
395+
c.readMsgLeft = c.msgReadLimit.Load()
395396

396397
r := &messageReader{
397398
c: c,

conn_common.go

+23-2
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,7 @@ func (c *netConn) SetReadDeadline(t time.Time) error {
178178
// Use this when you do not want to read data messages from the connection anymore but will
179179
// want to write messages to it.
180180
func (c *Conn) CloseRead(ctx context.Context) context.Context {
181-
atomic.StoreInt64(&c.readClosed, 1)
181+
c.readClosed.Store(1)
182182

183183
ctx, cancel := context.WithCancel(ctx)
184184
go func() {
@@ -200,11 +200,32 @@ func (c *Conn) CloseRead(ctx context.Context) context.Context {
200200
//
201201
// When the limit is hit, the connection will be closed with StatusMessageTooBig.
202202
func (c *Conn) SetReadLimit(n int64) {
203-
c.msgReadLimit = n
203+
c.msgReadLimit.Store(n)
204204
}
205205

206206
func (c *Conn) setCloseErr(err error) {
207207
c.closeErrOnce.Do(func() {
208208
c.closeErr = fmt.Errorf("websocket closed: %w", err)
209209
})
210210
}
211+
212+
// See https://github.com/nhooyr/websocket/issues/153
213+
type atomicInt64 struct {
214+
v atomic.Value
215+
}
216+
217+
func (v *atomicInt64) Load() int64 {
218+
i, ok := v.v.Load().(int64)
219+
if !ok {
220+
return 0
221+
}
222+
return i
223+
}
224+
225+
func (v *atomicInt64) Store(i int64) {
226+
v.v.Store(i)
227+
}
228+
229+
func (v *atomicInt64) String() string {
230+
return fmt.Sprint(v.v.Load())
231+
}

websocket_js.go

+10-6
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import (
1010
"reflect"
1111
"runtime"
1212
"sync"
13-
"sync/atomic"
1413
"syscall/js"
1514

1615
"nhooyr.io/websocket/internal/bpool"
@@ -21,9 +20,10 @@ import (
2120
type Conn struct {
2221
ws wsjs.WebSocket
2322

24-
msgReadLimit int64
23+
// read limit for a message in bytes.
24+
msgReadLimit *atomicInt64
2525

26-
readClosed int64
26+
readClosed *atomicInt64
2727
closeOnce sync.Once
2828
closed chan struct{}
2929
closeErrOnce sync.Once
@@ -49,7 +49,11 @@ func (c *Conn) close(err error) {
4949
func (c *Conn) init() {
5050
c.closed = make(chan struct{})
5151
c.readSignal = make(chan struct{}, 1)
52-
c.msgReadLimit = 32768
52+
53+
c.msgReadLimit = &atomicInt64{}
54+
c.msgReadLimit.Store(32768)
55+
56+
c.readClosed = &atomicInt64{}
5357

5458
c.releaseOnClose = c.ws.OnClose(func(e wsjs.CloseEvent) {
5559
cerr := CloseError{
@@ -89,15 +93,15 @@ func (c *Conn) closeWithInternal() {
8993
// Read attempts to read a message from the connection.
9094
// The maximum time spent waiting is bounded by the context.
9195
func (c *Conn) Read(ctx context.Context) (MessageType, []byte, error) {
92-
if atomic.LoadInt64(&c.readClosed) == 1 {
96+
if c.readClosed.Load() == 1 {
9397
return 0, nil, fmt.Errorf("websocket connection read closed")
9498
}
9599

96100
typ, p, err := c.read(ctx)
97101
if err != nil {
98102
return 0, nil, fmt.Errorf("failed to read: %w", err)
99103
}
100-
if int64(len(p)) > c.msgReadLimit {
104+
if int64(len(p)) > c.msgReadLimit.Load() {
101105
c.Close(StatusMessageTooBig, fmt.Sprintf("read limited at %v bytes", c.msgReadLimit))
102106
return 0, nil, c.closeErr
103107
}

0 commit comments

Comments
 (0)