Skip to content

Commit c151e07

Browse files
context: update ValueContext to handle Any value and more Key types, use closures (#11993)
1 parent d6a4bce commit c151e07

File tree

11 files changed

+129
-88
lines changed

11 files changed

+129
-88
lines changed

cmd/tools/vtest-self.v

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,10 @@ const (
9999
'do_not_remove',
100100
]
101101
skip_on_windows = [
102+
'vlib/context/cancel_test.v',
103+
'vlib/context/deadline_test.v',
104+
'vlib/context/empty_test.v',
105+
'vlib/context/value_test.v',
102106
'vlib/orm/orm_test.v',
103107
'vlib/v/tests/orm_sub_struct_test.v',
104108
'vlib/v/tests/closure_test.v',

vlib/context/README.md

Lines changed: 26 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -59,9 +59,9 @@ fn example_with_cancel() {
5959
return dst
6060
}
6161
62-
ctx := context.with_cancel(context.background())
62+
ctx, cancel := context.with_cancel(context.background())
6363
defer {
64-
context.cancel(ctx)
64+
cancel()
6565
}
6666
6767
ch := gen(ctx)
@@ -87,13 +87,13 @@ const (
8787
// function that it should abandon its work as soon as it gets to it.
8888
fn example_with_deadline() {
8989
dur := time.now().add(short_duration)
90-
ctx := context.with_deadline(context.background(), dur)
90+
ctx, cancel := context.with_deadline(context.background(), dur)
9191
9292
defer {
9393
// Even though ctx will be expired, it is good practice to call its
9494
// cancellation function in any case. Failure to do so may keep the
9595
// context and its parent alive longer than necessary.
96-
context.cancel(ctx)
96+
cancel()
9797
}
9898
9999
ctx_ch := ctx.done()
@@ -122,9 +122,9 @@ const (
122122
fn example_with_timeout() {
123123
// Pass a context with a timeout to tell a blocking function that it
124124
// should abandon its work after the timeout elapses.
125-
ctx := context.with_timeout(context.background(), short_duration)
125+
ctx, cancel := context.with_timeout(context.background(), short_duration)
126126
defer {
127-
context.cancel(ctx)
127+
cancel()
128128
}
129129
130130
ctx_ch := ctx.done()
@@ -142,25 +142,36 @@ fn example_with_timeout() {
142142
```v
143143
import context
144144
145-
type ValueContextKey = string
145+
const not_found_value = &Value{
146+
val: 'key not found'
147+
}
148+
149+
struct Value {
150+
val string
151+
}
146152
147153
// This example demonstrates how a value can be passed to the context
148154
// and also how to retrieve it if it exists.
149155
fn example_with_value() {
150-
f := fn (ctx context.Context, key ValueContextKey) string {
156+
f := fn (ctx context.Context, key context.Key) &Value {
151157
if value := ctx.value(key) {
152-
if !isnil(value) {
153-
return *(&string(value))
158+
match value {
159+
Value {
160+
return value
161+
}
162+
else {}
154163
}
155164
}
156-
return 'key not found'
165+
return not_found_value
157166
}
158167
159-
key := ValueContextKey('language')
160-
value := 'VAL'
161-
ctx := context.with_value(context.background(), key, &value)
168+
key := 'language'
169+
value := &Value{
170+
val: 'VAL'
171+
}
172+
ctx := context.with_value(context.background(), key, value)
162173
163174
assert value == f(ctx, key)
164-
assert 'key not found' == f(ctx, ValueContextKey('color'))
175+
assert not_found_value == f(ctx, 'color')
165176
}
166177
```

vlib/context/_context.v

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// This module defines the Context type, which carries deadlines, cancellation signals,
22
// and other request-scoped values across API boundaries and between processes.
3-
// Based off: https://github.com/golang/go/tree/master/src/context
3+
// Based on: https://github.com/golang/go/tree/master/src/context
44
// Last commit: https://github.com/golang/go/commit/52bf14e0e8bdcd73f1ddfb0c4a1d0200097d3ba2
55
module context
66

@@ -10,7 +10,7 @@ const (
1010
background = EmptyContext(0)
1111
todo = EmptyContext(1)
1212

13-
cancel_context_key = 'context.CancelContext'
13+
cancel_context_key = Key('context.CancelContext')
1414

1515
// canceled is the error returned by Context.err when the context is canceled.
1616
canceled = error('context canceled')
@@ -20,6 +20,24 @@ const (
2020
deadline_exceeded = error('context deadline exceeded')
2121
)
2222

23+
// Key represents the type for the ValueContext key
24+
pub type Key = bool
25+
| byte
26+
| f32
27+
| f64
28+
| i16
29+
| i64
30+
| i8
31+
| int
32+
| string
33+
| u16
34+
| u32
35+
| u64
36+
| voidptr
37+
38+
// Any represents a generic type for the ValueContext
39+
pub interface Any {}
40+
2341
pub interface Context {
2442
// deadline returns the time when work done on behalf of this context
2543
// should be canceled. deadline returns none when no deadline is
@@ -56,7 +74,7 @@ pub interface Context {
5674
// Context.Value. A key can be any type that supports equality;
5775
// packages should define keys as an unexported type to avoid
5876
// collisions.
59-
value(key string) ?voidptr
77+
value(key Key) ?Any
6078
str() string
6179
}
6280

vlib/context/cancel.v

Lines changed: 23 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,22 @@
11
// This module defines the Context type, which carries deadlines, cancellation signals,
22
// and other request-scoped values across API boundaries and between processes.
3-
// Based off: https://github.com/golang/go/tree/master/src/context
3+
// Based on: https://github.com/golang/go/tree/master/src/context
44
// Last commit: https://github.com/golang/go/commit/52bf14e0e8bdcd73f1ddfb0c4a1d0200097d3ba2
55
module context
66

77
import rand
88
import sync
99
import time
1010

11+
pub type CancelFn = fn ()
12+
1113
pub interface Canceler {
1214
id string
1315
cancel(remove_from_parent bool, err IError)
1416
done() chan int
1517
}
1618

19+
[deprecated]
1720
pub fn cancel(ctx Context) {
1821
match mut ctx {
1922
CancelContext {
@@ -44,10 +47,12 @@ mut:
4447
//
4548
// Canceling this context releases resources associated with it, so code should
4649
// call cancel as soon as the operations running in this Context complete.
47-
pub fn with_cancel(parent Context) Context {
50+
pub fn with_cancel(parent Context) (Context, CancelFn) {
4851
mut c := new_cancel_context(parent)
49-
propagate_cancel(parent, mut c)
50-
return Context(c)
52+
propagate_cancel(parent, c)
53+
return Context(c), fn [mut c] () {
54+
c.cancel(true, canceled)
55+
}
5156
}
5257

5358
// new_cancel_context returns an initialized CancelContext.
@@ -61,7 +66,7 @@ fn new_cancel_context(parent Context) &CancelContext {
6166
}
6267
}
6368

64-
pub fn (ctx CancelContext) deadline() ?time.Time {
69+
pub fn (ctx &CancelContext) deadline() ?time.Time {
6570
return none
6671
}
6772

@@ -79,14 +84,14 @@ pub fn (mut ctx CancelContext) err() IError {
7984
return err
8085
}
8186

82-
pub fn (ctx CancelContext) value(key string) ?voidptr {
87+
pub fn (ctx &CancelContext) value(key Key) ?Any {
8388
if key == cancel_context_key {
84-
return voidptr(unsafe { &ctx })
89+
return ctx
8590
}
8691
return ctx.context.value(key)
8792
}
8893

89-
pub fn (ctx CancelContext) str() string {
94+
pub fn (ctx &CancelContext) str() string {
9095
return context_name(ctx.context) + '.with_cancel'
9196
}
9297

@@ -122,7 +127,7 @@ fn (mut ctx CancelContext) cancel(remove_from_parent bool, err IError) {
122127
}
123128
}
124129

125-
fn propagate_cancel(parent Context, mut child Canceler) {
130+
fn propagate_cancel(parent Context, child Canceler) {
126131
done := parent.done()
127132
select {
128133
_ := <-done {
@@ -132,19 +137,19 @@ fn propagate_cancel(parent Context, mut child Canceler) {
132137
}
133138
}
134139
mut p := parent_cancel_context(parent) or {
135-
go fn (parent Context, mut child Canceler) {
140+
go fn (parent Context, child Canceler) {
136141
pdone := parent.done()
137142
select {
138143
_ := <-pdone {
139144
child.cancel(false, parent.err())
140145
}
141146
}
142-
}(parent, mut child)
147+
}(parent, child)
143148
return
144149
}
145150

146151
if p.err is none {
147-
p.children[child.id] = *child
152+
p.children[child.id] = child
148153
} else {
149154
// parent has already been canceled
150155
child.cancel(false, p.err)
@@ -157,19 +162,20 @@ fn propagate_cancel(parent Context, mut child Canceler) {
157162
// parent.done() matches that CancelContext. (If not, the CancelContext
158163
// has been wrapped in a custom implementation providing a
159164
// different done channel, in which case we should not bypass it.)
160-
fn parent_cancel_context(parent Context) ?CancelContext {
165+
fn parent_cancel_context(parent Context) ?&CancelContext {
161166
done := parent.done()
162167
if done.closed {
163168
return none
164169
}
165-
if p_ptr := parent.value(cancel_context_key) {
166-
if !isnil(p_ptr) {
167-
mut p := &CancelContext(p_ptr)
170+
mut p := parent.value(cancel_context_key) ?
171+
match mut p {
172+
CancelContext {
168173
pdone := p.done()
169174
if done == pdone {
170-
return *p
175+
return p
171176
}
172177
}
178+
else {}
173179
}
174180
return none
175181
}

vlib/context/cancel_test.v

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,9 @@ fn test_with_cancel() {
3030
return dst
3131
}
3232

33-
ctx := context.with_cancel(context.background())
33+
ctx, cancel := context.with_cancel(context.background())
3434
defer {
35-
context.cancel(ctx)
35+
cancel()
3636
}
3737

3838
ch := gen(ctx)

vlib/context/deadline.v

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// This module defines the Context type, which carries deadlines, cancellation signals,
22
// and other request-scoped values across API boundaries and between processes.
3-
// Based off: https://github.com/golang/go/tree/master/src/context
3+
// Based on: https://github.com/golang/go/tree/master/src/context
44
// Last commit: https://github.com/golang/go/commit/52bf14e0e8bdcd73f1ddfb0c4a1d0200097d3ba2
55
module context
66

@@ -26,7 +26,7 @@ mut:
2626
//
2727
// Canceling this context releases resources associated with it, so code should
2828
// call cancel as soon as the operations running in this Context complete.
29-
pub fn with_deadline(parent Context, d time.Time) Context {
29+
pub fn with_deadline(parent Context, d time.Time) (Context, CancelFn) {
3030
id := rand.uuid_v4()
3131
if cur := parent.deadline() {
3232
if cur < d {
@@ -40,11 +40,13 @@ pub fn with_deadline(parent Context, d time.Time) Context {
4040
deadline: d
4141
id: id
4242
}
43-
propagate_cancel(parent, mut ctx)
43+
propagate_cancel(parent, ctx)
4444
dur := d - time.now()
4545
if dur.nanoseconds() <= 0 {
4646
ctx.cancel(true, deadline_exceeded) // deadline has already passed
47-
return Context(ctx)
47+
return Context(ctx), fn [mut ctx] () {
48+
ctx.cancel(true, canceled)
49+
}
4850
}
4951

5052
if ctx.err() is none {
@@ -53,18 +55,20 @@ pub fn with_deadline(parent Context, d time.Time) Context {
5355
ctx.cancel(true, deadline_exceeded)
5456
}(mut ctx, dur)
5557
}
56-
return Context(ctx)
58+
return Context(ctx), fn [mut ctx] () {
59+
ctx.cancel(true, canceled)
60+
}
5761
}
5862

5963
// with_timeout returns with_deadline(parent, time.now().add(timeout)).
6064
//
6165
// Canceling this context releases resources associated with it, so code should
6266
// call cancel as soon as the operations running in this Context complete
63-
pub fn with_timeout(parent Context, timeout time.Duration) Context {
67+
pub fn with_timeout(parent Context, timeout time.Duration) (Context, CancelFn) {
6468
return with_deadline(parent, time.now().add(timeout))
6569
}
6670

67-
pub fn (ctx TimerContext) deadline() ?time.Time {
71+
pub fn (ctx &TimerContext) deadline() ?time.Time {
6872
return ctx.deadline
6973
}
7074

@@ -76,7 +80,7 @@ pub fn (mut ctx TimerContext) err() IError {
7680
return ctx.cancel_ctx.err()
7781
}
7882

79-
pub fn (ctx TimerContext) value(key string) ?voidptr {
83+
pub fn (ctx &TimerContext) value(key Key) ?Any {
8084
return ctx.cancel_ctx.value(key)
8185
}
8286

@@ -88,7 +92,7 @@ pub fn (mut ctx TimerContext) cancel(remove_from_parent bool, err IError) {
8892
}
8993
}
9094

91-
pub fn (ctx TimerContext) str() string {
95+
pub fn (ctx &TimerContext) str() string {
9296
return context_name(ctx.cancel_ctx.context) + '.with_deadline(' + ctx.deadline.str() + ' [' +
9397
(time.now() - ctx.deadline).str() + '])'
9498
}

vlib/context/deadline_test.v

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,13 @@ const (
1010
// function that it should abandon its work as soon as it gets to it.
1111
fn test_with_deadline() {
1212
dur := time.now().add(short_duration)
13-
ctx := context.with_deadline(context.background(), dur)
13+
ctx, cancel := context.with_deadline(context.background(), dur)
1414

1515
defer {
1616
// Even though ctx will be expired, it is good practice to call its
1717
// cancellation function in any case. Failure to do so may keep the
1818
// context and its parent alive longer than necessary.
19-
context.cancel(ctx)
19+
cancel()
2020
}
2121

2222
ctx_ch := ctx.done()
@@ -33,9 +33,9 @@ fn test_with_deadline() {
3333
fn test_with_timeout() {
3434
// Pass a context with a timeout to tell a blocking function that it
3535
// should abandon its work after the timeout elapses.
36-
ctx := context.with_timeout(context.background(), short_duration)
36+
ctx, cancel := context.with_timeout(context.background(), short_duration)
3737
defer {
38-
context.cancel(ctx)
38+
cancel()
3939
}
4040

4141
ctx_ch := ctx.done()

0 commit comments

Comments
 (0)