/
chan.go
95 lines (78 loc) · 1.91 KB
/
chan.go
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
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
// Copyright (C) 2021 Storj Labs, Inc.
// See LICENSE for copying information.
package drpcsignal
import (
"sync"
"sync/atomic"
)
var closed = make(chan struct{})
func init() { close(closed) }
// Chan is a lazily allocated chan struct{} that avoids allocating if
// it is closed before being used for anything.
type Chan struct {
done uint32
mu sync.Mutex
ch chan struct{}
}
func (c *Chan) do(f func()) bool {
return atomic.LoadUint32(&c.done) == 0 && c.doSlow(f)
}
func (c *Chan) doSlow(f func()) bool {
c.mu.Lock()
defer c.mu.Unlock()
if c.done == 0 {
defer atomic.StoreUint32(&c.done, 1)
f()
return true
}
return false
}
// setFresh sets the channel to a freshly allocated one.
func (c *Chan) setFresh() {
c.ch = make(chan struct{})
}
// setClosed sets the channel to an already closed one.
func (c *Chan) setClosed() {
c.ch = closed
}
// Close tries to set the channel to an already closed one if
// a fresh one has not already been set, and closes the fresh
// one otherwise.
func (c *Chan) Close() {
if !c.do(c.setClosed) {
close(c.ch)
}
}
// Make sets the channel to a freshly allocated channel with the
// provided capacity. It is a no-op if called after any other
// methods.
func (c *Chan) Make(cap uint) {
c.do(func() { c.ch = make(chan struct{}, cap) })
}
// Get returns the channel, allocating if necessary.
func (c *Chan) Get() chan struct{} {
c.do(c.setFresh)
return c.ch
}
// Send sends a value on the channel, allocating if necessary.
func (c *Chan) Send() {
c.do(c.setFresh)
c.ch <- struct{}{}
}
// Recv receives a value on the channel, allocating if necessary.
func (c *Chan) Recv() {
c.do(c.setFresh)
<-c.ch
}
// Full returns true if the channel is currently full. The information
// is immediately invalid in the sense that a send could always block.
func (c *Chan) Full() bool {
c.do(c.setFresh)
select {
case c.ch <- struct{}{}:
<-c.ch
return false
default:
return true
}
}