context: update ValueContext to handle Any value and more Key types, use closures (#11993)
parent
d6a4bce2ef
commit
c151e075e1
|
@ -99,6 +99,10 @@ const (
|
|||
'do_not_remove',
|
||||
]
|
||||
skip_on_windows = [
|
||||
'vlib/context/cancel_test.v',
|
||||
'vlib/context/deadline_test.v',
|
||||
'vlib/context/empty_test.v',
|
||||
'vlib/context/value_test.v',
|
||||
'vlib/orm/orm_test.v',
|
||||
'vlib/v/tests/orm_sub_struct_test.v',
|
||||
'vlib/v/tests/closure_test.v',
|
||||
|
|
|
@ -59,9 +59,9 @@ fn example_with_cancel() {
|
|||
return dst
|
||||
}
|
||||
|
||||
ctx := context.with_cancel(context.background())
|
||||
ctx, cancel := context.with_cancel(context.background())
|
||||
defer {
|
||||
context.cancel(ctx)
|
||||
cancel()
|
||||
}
|
||||
|
||||
ch := gen(ctx)
|
||||
|
@ -87,13 +87,13 @@ const (
|
|||
// function that it should abandon its work as soon as it gets to it.
|
||||
fn example_with_deadline() {
|
||||
dur := time.now().add(short_duration)
|
||||
ctx := context.with_deadline(context.background(), dur)
|
||||
ctx, cancel := context.with_deadline(context.background(), dur)
|
||||
|
||||
defer {
|
||||
// Even though ctx will be expired, it is good practice to call its
|
||||
// cancellation function in any case. Failure to do so may keep the
|
||||
// context and its parent alive longer than necessary.
|
||||
context.cancel(ctx)
|
||||
cancel()
|
||||
}
|
||||
|
||||
ctx_ch := ctx.done()
|
||||
|
@ -122,9 +122,9 @@ const (
|
|||
fn example_with_timeout() {
|
||||
// Pass a context with a timeout to tell a blocking function that it
|
||||
// should abandon its work after the timeout elapses.
|
||||
ctx := context.with_timeout(context.background(), short_duration)
|
||||
ctx, cancel := context.with_timeout(context.background(), short_duration)
|
||||
defer {
|
||||
context.cancel(ctx)
|
||||
cancel()
|
||||
}
|
||||
|
||||
ctx_ch := ctx.done()
|
||||
|
@ -142,25 +142,36 @@ fn example_with_timeout() {
|
|||
```v
|
||||
import context
|
||||
|
||||
type ValueContextKey = string
|
||||
const not_found_value = &Value{
|
||||
val: 'key not found'
|
||||
}
|
||||
|
||||
struct Value {
|
||||
val string
|
||||
}
|
||||
|
||||
// This example demonstrates how a value can be passed to the context
|
||||
// and also how to retrieve it if it exists.
|
||||
fn example_with_value() {
|
||||
f := fn (ctx context.Context, key ValueContextKey) string {
|
||||
f := fn (ctx context.Context, key context.Key) &Value {
|
||||
if value := ctx.value(key) {
|
||||
if !isnil(value) {
|
||||
return *(&string(value))
|
||||
match value {
|
||||
Value {
|
||||
return value
|
||||
}
|
||||
else {}
|
||||
}
|
||||
}
|
||||
return 'key not found'
|
||||
return not_found_value
|
||||
}
|
||||
|
||||
key := ValueContextKey('language')
|
||||
value := 'VAL'
|
||||
ctx := context.with_value(context.background(), key, &value)
|
||||
key := 'language'
|
||||
value := &Value{
|
||||
val: 'VAL'
|
||||
}
|
||||
ctx := context.with_value(context.background(), key, value)
|
||||
|
||||
assert value == f(ctx, key)
|
||||
assert 'key not found' == f(ctx, ValueContextKey('color'))
|
||||
assert not_found_value == f(ctx, 'color')
|
||||
}
|
||||
```
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// This module defines the Context type, which carries deadlines, cancellation signals,
|
||||
// and other request-scoped values across API boundaries and between processes.
|
||||
// Based off: https://github.com/golang/go/tree/master/src/context
|
||||
// Based on: https://github.com/golang/go/tree/master/src/context
|
||||
// Last commit: https://github.com/golang/go/commit/52bf14e0e8bdcd73f1ddfb0c4a1d0200097d3ba2
|
||||
module context
|
||||
|
||||
|
@ -10,7 +10,7 @@ const (
|
|||
background = EmptyContext(0)
|
||||
todo = EmptyContext(1)
|
||||
|
||||
cancel_context_key = 'context.CancelContext'
|
||||
cancel_context_key = Key('context.CancelContext')
|
||||
|
||||
// canceled is the error returned by Context.err when the context is canceled.
|
||||
canceled = error('context canceled')
|
||||
|
@ -20,6 +20,24 @@ const (
|
|||
deadline_exceeded = error('context deadline exceeded')
|
||||
)
|
||||
|
||||
// Key represents the type for the ValueContext key
|
||||
pub type Key = bool
|
||||
| byte
|
||||
| f32
|
||||
| f64
|
||||
| i16
|
||||
| i64
|
||||
| i8
|
||||
| int
|
||||
| string
|
||||
| u16
|
||||
| u32
|
||||
| u64
|
||||
| voidptr
|
||||
|
||||
// Any represents a generic type for the ValueContext
|
||||
pub interface Any {}
|
||||
|
||||
pub interface Context {
|
||||
// deadline returns the time when work done on behalf of this context
|
||||
// should be canceled. deadline returns none when no deadline is
|
||||
|
@ -56,7 +74,7 @@ pub interface Context {
|
|||
// Context.Value. A key can be any type that supports equality;
|
||||
// packages should define keys as an unexported type to avoid
|
||||
// collisions.
|
||||
value(key string) ?voidptr
|
||||
value(key Key) ?Any
|
||||
str() string
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// This module defines the Context type, which carries deadlines, cancellation signals,
|
||||
// and other request-scoped values across API boundaries and between processes.
|
||||
// Based off: https://github.com/golang/go/tree/master/src/context
|
||||
// Based on: https://github.com/golang/go/tree/master/src/context
|
||||
// Last commit: https://github.com/golang/go/commit/52bf14e0e8bdcd73f1ddfb0c4a1d0200097d3ba2
|
||||
module context
|
||||
|
||||
|
@ -8,12 +8,15 @@ import rand
|
|||
import sync
|
||||
import time
|
||||
|
||||
pub type CancelFn = fn ()
|
||||
|
||||
pub interface Canceler {
|
||||
id string
|
||||
cancel(remove_from_parent bool, err IError)
|
||||
done() chan int
|
||||
}
|
||||
|
||||
[deprecated]
|
||||
pub fn cancel(ctx Context) {
|
||||
match mut ctx {
|
||||
CancelContext {
|
||||
|
@ -44,10 +47,12 @@ mut:
|
|||
//
|
||||
// Canceling this context releases resources associated with it, so code should
|
||||
// call cancel as soon as the operations running in this Context complete.
|
||||
pub fn with_cancel(parent Context) Context {
|
||||
pub fn with_cancel(parent Context) (Context, CancelFn) {
|
||||
mut c := new_cancel_context(parent)
|
||||
propagate_cancel(parent, mut c)
|
||||
return Context(c)
|
||||
propagate_cancel(parent, c)
|
||||
return Context(c), fn [mut c] () {
|
||||
c.cancel(true, canceled)
|
||||
}
|
||||
}
|
||||
|
||||
// new_cancel_context returns an initialized CancelContext.
|
||||
|
@ -61,7 +66,7 @@ fn new_cancel_context(parent Context) &CancelContext {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn (ctx CancelContext) deadline() ?time.Time {
|
||||
pub fn (ctx &CancelContext) deadline() ?time.Time {
|
||||
return none
|
||||
}
|
||||
|
||||
|
@ -79,14 +84,14 @@ pub fn (mut ctx CancelContext) err() IError {
|
|||
return err
|
||||
}
|
||||
|
||||
pub fn (ctx CancelContext) value(key string) ?voidptr {
|
||||
pub fn (ctx &CancelContext) value(key Key) ?Any {
|
||||
if key == cancel_context_key {
|
||||
return voidptr(unsafe { &ctx })
|
||||
return ctx
|
||||
}
|
||||
return ctx.context.value(key)
|
||||
}
|
||||
|
||||
pub fn (ctx CancelContext) str() string {
|
||||
pub fn (ctx &CancelContext) str() string {
|
||||
return context_name(ctx.context) + '.with_cancel'
|
||||
}
|
||||
|
||||
|
@ -122,7 +127,7 @@ fn (mut ctx CancelContext) cancel(remove_from_parent bool, err IError) {
|
|||
}
|
||||
}
|
||||
|
||||
fn propagate_cancel(parent Context, mut child Canceler) {
|
||||
fn propagate_cancel(parent Context, child Canceler) {
|
||||
done := parent.done()
|
||||
select {
|
||||
_ := <-done {
|
||||
|
@ -132,19 +137,19 @@ fn propagate_cancel(parent Context, mut child Canceler) {
|
|||
}
|
||||
}
|
||||
mut p := parent_cancel_context(parent) or {
|
||||
go fn (parent Context, mut child Canceler) {
|
||||
go fn (parent Context, child Canceler) {
|
||||
pdone := parent.done()
|
||||
select {
|
||||
_ := <-pdone {
|
||||
child.cancel(false, parent.err())
|
||||
}
|
||||
}
|
||||
}(parent, mut child)
|
||||
}(parent, child)
|
||||
return
|
||||
}
|
||||
|
||||
if p.err is none {
|
||||
p.children[child.id] = *child
|
||||
p.children[child.id] = child
|
||||
} else {
|
||||
// parent has already been canceled
|
||||
child.cancel(false, p.err)
|
||||
|
@ -157,19 +162,20 @@ fn propagate_cancel(parent Context, mut child Canceler) {
|
|||
// parent.done() matches that CancelContext. (If not, the CancelContext
|
||||
// has been wrapped in a custom implementation providing a
|
||||
// different done channel, in which case we should not bypass it.)
|
||||
fn parent_cancel_context(parent Context) ?CancelContext {
|
||||
fn parent_cancel_context(parent Context) ?&CancelContext {
|
||||
done := parent.done()
|
||||
if done.closed {
|
||||
return none
|
||||
}
|
||||
if p_ptr := parent.value(cancel_context_key) {
|
||||
if !isnil(p_ptr) {
|
||||
mut p := &CancelContext(p_ptr)
|
||||
mut p := parent.value(cancel_context_key) ?
|
||||
match mut p {
|
||||
CancelContext {
|
||||
pdone := p.done()
|
||||
if done == pdone {
|
||||
return *p
|
||||
return p
|
||||
}
|
||||
}
|
||||
else {}
|
||||
}
|
||||
return none
|
||||
}
|
||||
|
|
|
@ -30,9 +30,9 @@ fn test_with_cancel() {
|
|||
return dst
|
||||
}
|
||||
|
||||
ctx := context.with_cancel(context.background())
|
||||
ctx, cancel := context.with_cancel(context.background())
|
||||
defer {
|
||||
context.cancel(ctx)
|
||||
cancel()
|
||||
}
|
||||
|
||||
ch := gen(ctx)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// This module defines the Context type, which carries deadlines, cancellation signals,
|
||||
// and other request-scoped values across API boundaries and between processes.
|
||||
// Based off: https://github.com/golang/go/tree/master/src/context
|
||||
// Based on: https://github.com/golang/go/tree/master/src/context
|
||||
// Last commit: https://github.com/golang/go/commit/52bf14e0e8bdcd73f1ddfb0c4a1d0200097d3ba2
|
||||
module context
|
||||
|
||||
|
@ -26,7 +26,7 @@ mut:
|
|||
//
|
||||
// Canceling this context releases resources associated with it, so code should
|
||||
// call cancel as soon as the operations running in this Context complete.
|
||||
pub fn with_deadline(parent Context, d time.Time) Context {
|
||||
pub fn with_deadline(parent Context, d time.Time) (Context, CancelFn) {
|
||||
id := rand.uuid_v4()
|
||||
if cur := parent.deadline() {
|
||||
if cur < d {
|
||||
|
@ -40,11 +40,13 @@ pub fn with_deadline(parent Context, d time.Time) Context {
|
|||
deadline: d
|
||||
id: id
|
||||
}
|
||||
propagate_cancel(parent, mut ctx)
|
||||
propagate_cancel(parent, ctx)
|
||||
dur := d - time.now()
|
||||
if dur.nanoseconds() <= 0 {
|
||||
ctx.cancel(true, deadline_exceeded) // deadline has already passed
|
||||
return Context(ctx)
|
||||
return Context(ctx), fn [mut ctx] () {
|
||||
ctx.cancel(true, canceled)
|
||||
}
|
||||
}
|
||||
|
||||
if ctx.err() is none {
|
||||
|
@ -53,18 +55,20 @@ pub fn with_deadline(parent Context, d time.Time) Context {
|
|||
ctx.cancel(true, deadline_exceeded)
|
||||
}(mut ctx, dur)
|
||||
}
|
||||
return Context(ctx)
|
||||
return Context(ctx), fn [mut ctx] () {
|
||||
ctx.cancel(true, canceled)
|
||||
}
|
||||
}
|
||||
|
||||
// with_timeout returns with_deadline(parent, time.now().add(timeout)).
|
||||
//
|
||||
// Canceling this context releases resources associated with it, so code should
|
||||
// call cancel as soon as the operations running in this Context complete
|
||||
pub fn with_timeout(parent Context, timeout time.Duration) Context {
|
||||
pub fn with_timeout(parent Context, timeout time.Duration) (Context, CancelFn) {
|
||||
return with_deadline(parent, time.now().add(timeout))
|
||||
}
|
||||
|
||||
pub fn (ctx TimerContext) deadline() ?time.Time {
|
||||
pub fn (ctx &TimerContext) deadline() ?time.Time {
|
||||
return ctx.deadline
|
||||
}
|
||||
|
||||
|
@ -76,7 +80,7 @@ pub fn (mut ctx TimerContext) err() IError {
|
|||
return ctx.cancel_ctx.err()
|
||||
}
|
||||
|
||||
pub fn (ctx TimerContext) value(key string) ?voidptr {
|
||||
pub fn (ctx &TimerContext) value(key Key) ?Any {
|
||||
return ctx.cancel_ctx.value(key)
|
||||
}
|
||||
|
||||
|
@ -88,7 +92,7 @@ pub fn (mut ctx TimerContext) cancel(remove_from_parent bool, err IError) {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn (ctx TimerContext) str() string {
|
||||
pub fn (ctx &TimerContext) str() string {
|
||||
return context_name(ctx.cancel_ctx.context) + '.with_deadline(' + ctx.deadline.str() + ' [' +
|
||||
(time.now() - ctx.deadline).str() + '])'
|
||||
}
|
||||
|
|
|
@ -10,13 +10,13 @@ const (
|
|||
// function that it should abandon its work as soon as it gets to it.
|
||||
fn test_with_deadline() {
|
||||
dur := time.now().add(short_duration)
|
||||
ctx := context.with_deadline(context.background(), dur)
|
||||
ctx, cancel := context.with_deadline(context.background(), dur)
|
||||
|
||||
defer {
|
||||
// Even though ctx will be expired, it is good practice to call its
|
||||
// cancellation function in any case. Failure to do so may keep the
|
||||
// context and its parent alive longer than necessary.
|
||||
context.cancel(ctx)
|
||||
cancel()
|
||||
}
|
||||
|
||||
ctx_ch := ctx.done()
|
||||
|
@ -33,9 +33,9 @@ fn test_with_deadline() {
|
|||
fn test_with_timeout() {
|
||||
// Pass a context with a timeout to tell a blocking function that it
|
||||
// should abandon its work after the timeout elapses.
|
||||
ctx := context.with_timeout(context.background(), short_duration)
|
||||
ctx, cancel := context.with_timeout(context.background(), short_duration)
|
||||
defer {
|
||||
context.cancel(ctx)
|
||||
cancel()
|
||||
}
|
||||
|
||||
ctx_ch := ctx.done()
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// This module defines the Context type, which carries deadlines, cancellation signals,
|
||||
// and other request-scoped values across API boundaries and between processes.
|
||||
// Based off: https://github.com/golang/go/tree/master/src/context
|
||||
// Based on: https://github.com/golang/go/tree/master/src/context
|
||||
// Last commit: https://github.com/golang/go/commit/52bf14e0e8bdcd73f1ddfb0c4a1d0200097d3ba2
|
||||
module context
|
||||
|
||||
|
@ -10,11 +10,11 @@ import time
|
|||
// struct{}, since vars of this type must have distinct addresses.
|
||||
pub type EmptyContext = int
|
||||
|
||||
pub fn (ctx EmptyContext) deadline() ?time.Time {
|
||||
pub fn (ctx &EmptyContext) deadline() ?time.Time {
|
||||
return none
|
||||
}
|
||||
|
||||
pub fn (ctx EmptyContext) done() chan int {
|
||||
pub fn (ctx &EmptyContext) done() chan int {
|
||||
ch := chan int{}
|
||||
defer {
|
||||
ch.close()
|
||||
|
@ -22,16 +22,15 @@ pub fn (ctx EmptyContext) done() chan int {
|
|||
return ch
|
||||
}
|
||||
|
||||
pub fn (ctx EmptyContext) err() IError {
|
||||
// TODO: Change this to `none`
|
||||
return none_
|
||||
}
|
||||
|
||||
pub fn (ctx EmptyContext) value(key string) ?voidptr {
|
||||
pub fn (ctx &EmptyContext) err() IError {
|
||||
return none
|
||||
}
|
||||
|
||||
pub fn (ctx EmptyContext) str() string {
|
||||
pub fn (ctx &EmptyContext) value(key Key) ?Any {
|
||||
return none
|
||||
}
|
||||
|
||||
pub fn (ctx &EmptyContext) str() string {
|
||||
if ctx == background {
|
||||
return 'context.Background'
|
||||
}
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
module context
|
||||
|
||||
const none_ = IError(&None{})
|
||||
|
||||
struct None {
|
||||
msg string
|
||||
code int
|
||||
}
|
||||
|
||||
fn (_ None) str() string {
|
||||
return 'none'
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
// This module defines the Context type, which carries deadlines, cancellation signals,
|
||||
// and other request-scoped values across API boundaries and between processes.
|
||||
// Based off: https://github.com/golang/go/tree/master/src/context
|
||||
// Based on: https://github.com/golang/go/tree/master/src/context
|
||||
// Last commit: https://github.com/golang/go/commit/52bf14e0e8bdcd73f1ddfb0c4a1d0200097d3ba2
|
||||
module context
|
||||
|
||||
|
@ -9,8 +9,8 @@ import time
|
|||
// A ValueContext carries a key-value pair. It implements Value for that key and
|
||||
// delegates all other calls to the embedded Context.
|
||||
pub struct ValueContext {
|
||||
key string
|
||||
value voidptr
|
||||
key Key
|
||||
value Any
|
||||
mut:
|
||||
context Context
|
||||
}
|
||||
|
@ -25,7 +25,7 @@ mut:
|
|||
// string or any other built-in type to avoid collisions between
|
||||
// packages using context. Users of with_value should define their own
|
||||
// types for keys
|
||||
pub fn with_value(parent Context, key string, value voidptr) Context {
|
||||
pub fn with_value(parent Context, key Key, value Any) Context {
|
||||
return &ValueContext{
|
||||
context: parent
|
||||
key: key
|
||||
|
@ -33,25 +33,25 @@ pub fn with_value(parent Context, key string, value voidptr) Context {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn (ctx ValueContext) deadline() ?time.Time {
|
||||
pub fn (ctx &ValueContext) deadline() ?time.Time {
|
||||
return ctx.context.deadline()
|
||||
}
|
||||
|
||||
pub fn (ctx ValueContext) done() chan int {
|
||||
pub fn (ctx &ValueContext) done() chan int {
|
||||
return ctx.context.done()
|
||||
}
|
||||
|
||||
pub fn (ctx ValueContext) err() IError {
|
||||
pub fn (ctx &ValueContext) err() IError {
|
||||
return ctx.context.err()
|
||||
}
|
||||
|
||||
pub fn (ctx ValueContext) value(key string) ?voidptr {
|
||||
pub fn (ctx &ValueContext) value(key Key) ?Any {
|
||||
if ctx.key == key {
|
||||
return ctx.value
|
||||
}
|
||||
return ctx.context.value(key)
|
||||
}
|
||||
|
||||
pub fn (ctx ValueContext) str() string {
|
||||
pub fn (ctx &ValueContext) str() string {
|
||||
return context_name(ctx.context) + '.with_value'
|
||||
}
|
||||
|
|
|
@ -1,23 +1,34 @@
|
|||
import context
|
||||
|
||||
type ValueContextKey = string
|
||||
const not_found_value = &Value{
|
||||
val: 'key not found'
|
||||
}
|
||||
|
||||
struct Value {
|
||||
val string
|
||||
}
|
||||
|
||||
// This example demonstrates how a value can be passed to the context
|
||||
// and also how to retrieve it if it exists.
|
||||
fn test_with_value() {
|
||||
f := fn (ctx context.Context, key ValueContextKey) string {
|
||||
f := fn (ctx context.Context, key context.Key) &Value {
|
||||
if value := ctx.value(key) {
|
||||
if !isnil(value) {
|
||||
return *(&string(value))
|
||||
match value {
|
||||
Value {
|
||||
return value
|
||||
}
|
||||
else {}
|
||||
}
|
||||
}
|
||||
return 'key not found'
|
||||
return not_found_value
|
||||
}
|
||||
|
||||
key := ValueContextKey('language')
|
||||
value := 'VAL'
|
||||
ctx := context.with_value(context.background(), key, &value)
|
||||
key := 'language'
|
||||
value := &Value{
|
||||
val: 'VAL'
|
||||
}
|
||||
ctx := context.with_value(context.background(), key, value)
|
||||
|
||||
assert value == f(ctx, key)
|
||||
assert 'key not found' == f(ctx, ValueContextKey('color'))
|
||||
assert not_found_value == f(ctx, 'color')
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue