Skip to content

Commit 97dd44e

Browse files
neildgopherbot
authored andcommitted
http2, internal/gate: move Gate type to an internal package
For reuse in internal/http3. Change-Id: I186d7219194a07c100aa8bd049e007232de2d3d9 Reviewed-on: https://go-review.googlesource.com/c/net/+/641497 Auto-Submit: Damien Neil <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]> Reviewed-by: Jonathan Amsterdam <[email protected]>
1 parent 35e1007 commit 97dd44e

File tree

3 files changed

+112
-35
lines changed

3 files changed

+112
-35
lines changed

http2/clientconn_test.go

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

2222
"golang.org/x/net/http2/hpack"
23+
"golang.org/x/net/internal/gate"
2324
)
2425

2526
// TestTestClientConn demonstrates usage of testClientConn.
@@ -206,7 +207,7 @@ func (tc *testClientConn) closeWrite() {
206207
// testRequestBody is a Request.Body for use in tests.
207208
type testRequestBody struct {
208209
tc *testClientConn
209-
gate gate
210+
gate gate.Gate
210211

211212
// At most one of buf or bytes can be set at any given time:
212213
buf bytes.Buffer // specific bytes to read from the body
@@ -218,18 +219,18 @@ type testRequestBody struct {
218219
func (tc *testClientConn) newRequestBody() *testRequestBody {
219220
b := &testRequestBody{
220221
tc: tc,
221-
gate: newGate(),
222+
gate: gate.New(false),
222223
}
223224
return b
224225
}
225226

226227
func (b *testRequestBody) unlock() {
227-
b.gate.unlock(b.buf.Len() > 0 || b.bytes > 0 || b.err != nil)
228+
b.gate.Unlock(b.buf.Len() > 0 || b.bytes > 0 || b.err != nil)
228229
}
229230

230231
// Read is called by the ClientConn to read from a request body.
231232
func (b *testRequestBody) Read(p []byte) (n int, _ error) {
232-
if err := b.gate.waitAndLock(context.Background()); err != nil {
233+
if err := b.gate.WaitAndLock(context.Background()); err != nil {
233234
return 0, err
234235
}
235236
defer b.unlock()
@@ -258,7 +259,7 @@ func (b *testRequestBody) Close() error {
258259
// writeBytes adds n arbitrary bytes to the body.
259260
func (b *testRequestBody) writeBytes(n int) {
260261
defer b.tc.sync()
261-
b.gate.lock()
262+
b.gate.Lock()
262263
defer b.unlock()
263264
b.bytes += n
264265
b.checkWrite()
@@ -268,7 +269,7 @@ func (b *testRequestBody) writeBytes(n int) {
268269
// Write adds bytes to the body.
269270
func (b *testRequestBody) Write(p []byte) (int, error) {
270271
defer b.tc.sync()
271-
b.gate.lock()
272+
b.gate.Lock()
272273
defer b.unlock()
273274
n, err := b.buf.Write(p)
274275
b.checkWrite()
@@ -287,7 +288,7 @@ func (b *testRequestBody) checkWrite() {
287288
// closeWithError sets an error which will be returned by Read.
288289
func (b *testRequestBody) closeWithError(err error) {
289290
defer b.tc.sync()
290-
b.gate.lock()
291+
b.gate.Lock()
291292
defer b.unlock()
292293
b.err = err
293294
}
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,37 @@
11
// Copyright 2024 The Go Authors. All rights reserved.
22
// Use of this source code is governed by a BSD-style
33
// license that can be found in the LICENSE file.
4-
package http2
4+
5+
// Package gate contains an alternative condition variable.
6+
package gate
57

68
import "context"
79

8-
// An gate is a monitor (mutex + condition variable) with one bit of state.
10+
// A Gate is a monitor (mutex + condition variable) with one bit of state.
911
//
1012
// The condition may be either set or unset.
1113
// Lock operations may be unconditional, or wait for the condition to be set.
1214
// Unlock operations record the new state of the condition.
13-
type gate struct {
15+
type Gate struct {
1416
// When unlocked, exactly one of set or unset contains a value.
1517
// When locked, neither chan contains a value.
1618
set chan struct{}
1719
unset chan struct{}
1820
}
1921

20-
// newGate returns a new, unlocked gate with the condition unset.
21-
func newGate() gate {
22-
g := newLockedGate()
23-
g.unlock(false)
24-
return g
25-
}
26-
27-
// newLockedGate returns a new, locked gate.
28-
func newLockedGate() gate {
29-
return gate{
22+
// New returns a new, unlocked gate.
23+
func New(set bool) Gate {
24+
g := Gate{
3025
set: make(chan struct{}, 1),
3126
unset: make(chan struct{}, 1),
3227
}
28+
g.Unlock(set)
29+
return g
3330
}
3431

35-
// lock acquires the gate unconditionally.
32+
// Lock acquires the gate unconditionally.
3633
// It reports whether the condition is set.
37-
func (g *gate) lock() (set bool) {
34+
func (g *Gate) Lock() (set bool) {
3835
select {
3936
case <-g.set:
4037
return true
@@ -43,9 +40,9 @@ func (g *gate) lock() (set bool) {
4340
}
4441
}
4542

46-
// waitAndLock waits until the condition is set before acquiring the gate.
47-
// If the context expires, waitAndLock returns an error and does not acquire the gate.
48-
func (g *gate) waitAndLock(ctx context.Context) error {
43+
// WaitAndLock waits until the condition is set before acquiring the gate.
44+
// If the context expires, WaitAndLock returns an error and does not acquire the gate.
45+
func (g *Gate) WaitAndLock(ctx context.Context) error {
4946
select {
5047
case <-g.set:
5148
return nil
@@ -59,8 +56,8 @@ func (g *gate) waitAndLock(ctx context.Context) error {
5956
}
6057
}
6158

62-
// lockIfSet acquires the gate if and only if the condition is set.
63-
func (g *gate) lockIfSet() (acquired bool) {
59+
// LockIfSet acquires the gate if and only if the condition is set.
60+
func (g *Gate) LockIfSet() (acquired bool) {
6461
select {
6562
case <-g.set:
6663
return true
@@ -69,17 +66,11 @@ func (g *gate) lockIfSet() (acquired bool) {
6966
}
7067
}
7168

72-
// unlock sets the condition and releases the gate.
73-
func (g *gate) unlock(set bool) {
69+
// Unlock sets the condition and releases the gate.
70+
func (g *Gate) Unlock(set bool) {
7471
if set {
7572
g.set <- struct{}{}
7673
} else {
7774
g.unset <- struct{}{}
7875
}
7976
}
80-
81-
// unlockFunc sets the condition to the result of f and releases the gate.
82-
// Useful in defers.
83-
func (g *gate) unlockFunc(f func() bool) {
84-
g.unlock(f())
85-
}

internal/gate/gate_test.go

+85
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
// Copyright 2024 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
package gate_test
6+
7+
import (
8+
"context"
9+
"testing"
10+
"time"
11+
12+
"golang.org/x/net/internal/gate"
13+
)
14+
15+
func TestGateLockAndUnlock(t *testing.T) {
16+
g := gate.New(false)
17+
if set := g.Lock(); set {
18+
t.Errorf("g.Lock of never-locked gate: true, want false")
19+
}
20+
unlockedc := make(chan struct{})
21+
donec := make(chan struct{})
22+
go func() {
23+
defer close(donec)
24+
if set := g.Lock(); !set {
25+
t.Errorf("g.Lock of set gate: false, want true")
26+
}
27+
select {
28+
case <-unlockedc:
29+
default:
30+
t.Errorf("g.Lock succeeded while gate was held")
31+
}
32+
g.Unlock(false)
33+
}()
34+
time.Sleep(1 * time.Millisecond)
35+
close(unlockedc)
36+
g.Unlock(true)
37+
<-donec
38+
if set := g.Lock(); set {
39+
t.Errorf("g.Lock of unset gate: true, want false")
40+
}
41+
}
42+
43+
func TestGateWaitAndLock(t *testing.T) {
44+
g := gate.New(false)
45+
// WaitAndLock is canceled.
46+
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Millisecond)
47+
defer cancel()
48+
if err := g.WaitAndLock(ctx); err != context.DeadlineExceeded {
49+
t.Fatalf("g.WaitAndLock = %v, want context.DeadlineExceeded", err)
50+
}
51+
// WaitAndLock succeeds.
52+
set := false
53+
go func() {
54+
time.Sleep(1 * time.Millisecond)
55+
g.Lock()
56+
set = true
57+
g.Unlock(true)
58+
}()
59+
if err := g.WaitAndLock(context.Background()); err != nil {
60+
t.Fatalf("g.WaitAndLock = %v, want nil", err)
61+
}
62+
if !set {
63+
t.Fatalf("g.WaitAndLock returned before gate was set")
64+
}
65+
g.Unlock(true)
66+
// WaitAndLock succeeds when the gate is set and the context is canceled.
67+
if err := g.WaitAndLock(ctx); err != nil {
68+
t.Fatalf("g.WaitAndLock = %v, want nil", err)
69+
}
70+
}
71+
72+
func TestGateLockIfSet(t *testing.T) {
73+
g := gate.New(false)
74+
if locked := g.LockIfSet(); locked {
75+
t.Fatalf("g.LockIfSet of unset gate = %v, want false", locked)
76+
}
77+
g.Lock()
78+
if locked := g.LockIfSet(); locked {
79+
t.Fatalf("g.LockIfSet of locked gate = %v, want false", locked)
80+
}
81+
g.Unlock(true)
82+
if locked := g.LockIfSet(); !locked {
83+
t.Fatalf("g.LockIfSet of set gate = %v, want true", locked)
84+
}
85+
}

0 commit comments

Comments
 (0)