-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcontext.go
153 lines (125 loc) · 3.03 KB
/
context.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
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
package go_context
import (
"errors"
"reflect"
"sync"
"time"
)
/**
This code takes heavy reference form the video guide by @{Francesc Campoy}
*/
/// The context interface
type Context interface {
Deadline() (deadline time.Time, ok bool)
Done() <-chan struct{}
Err() error
Value(key interface{}) interface{}
}
/**
The Empty Context is used to return different references for TODO() and Background().
Using empty struct causes the simultaneous variables declarations to point to same memory.
*/
type emptyCtx int
func (emptyCtx) Deadline() (deadline time.Time, ok bool) { return }
func (emptyCtx) Done() <-chan struct{} { return nil }
func (emptyCtx) Err() error { return nil }
func (emptyCtx) Value(key interface{}) interface{} { return nil }
var todoContext, backgroundContext = new(emptyCtx), new(emptyCtx)
func TODO() Context { return todoContext }
func Background() Context { return backgroundContext }
/**
A cancelCtx is used to allow a context to be cancelled
*/
type cancelCtx struct {
Context
done chan struct{}
err error
mu sync.Mutex
}
func (ctx *cancelCtx) Done() <-chan struct{} { return ctx.done }
func (ctx *cancelCtx) Err() error {
ctx.mu.Lock()
defer ctx.mu.Unlock()
return ctx.err
}
var Canceled = errors.New("context canceled")
type CancelFunc func()
func WithCancel(parent Context) (Context, CancelFunc) {
ctx := &cancelCtx{
Context: parent,
done: make(chan struct{}),
}
cancel := func() {
ctx.cancel(Canceled)
}
go func() {
select {
case <-parent.Done():
ctx.cancel(parent.Err())
case <-ctx.Done():
}
}()
return ctx, cancel
}
func (ctx *cancelCtx) cancel(err error) {
ctx.mu.Lock()
defer ctx.mu.Unlock()
if ctx.err != nil {
return
}
ctx.err = err
close(ctx.done)
}
/**
A deadlineCtx is a context that is cancelled at provided deadline
*/
type deadlineCtx struct {
*cancelCtx
deadline time.Time
}
func (ctx deadlineCtx) Deadline() (deadline time.Time, ok bool) { return ctx.deadline, true }
var DeadlineExceeded = errors.New("deadline exceeded")
func WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc) {
cctx, cancel := WithCancel(parent)
ctx := deadlineCtx{
cancelCtx: cctx.(*cancelCtx),
deadline: deadline,
}
t := time.AfterFunc(time.Until(deadline), func() {
ctx.cancel(DeadlineExceeded)
})
stop := func() {
t.Stop()
cancel()
}
return ctx, stop
}
func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) {
return WithDeadline(parent, time.Now().Add(timeout))
}
/**
A valueCtx holds additional key, value pair with the context
*/
type valueCtx struct {
Context
key, value interface{}
}
func (ctx valueCtx) Value(key interface{}) interface{} {
if key == ctx.key {
return ctx.value
}
return ctx.Context.Value(key)
}
func WithValue(parent Context, key, value interface{}) Context {
if key == nil {
panic("key is nil")
}
if !reflect.TypeOf(key).Comparable() {
panic("key is not comparable")
}
return valueCtx{
Context: parent,
key: key,
value: value,
}
}