aboutsummaryrefslogtreecommitdiff
path: root/v_windows/v/vlib/context
diff options
context:
space:
mode:
authorIndrajith K L2022-12-03 17:00:20 +0530
committerIndrajith K L2022-12-03 17:00:20 +0530
commitf5c4671bfbad96bf346bd7e9a21fc4317b4959df (patch)
tree2764fc62da58f2ba8da7ed341643fc359873142f /v_windows/v/vlib/context
downloadcli-tools-windows-master.tar.gz
cli-tools-windows-master.tar.bz2
cli-tools-windows-master.zip
Adds most of the toolsHEADmaster
Diffstat (limited to 'v_windows/v/vlib/context')
-rw-r--r--v_windows/v/vlib/context/README.md166
-rw-r--r--v_windows/v/vlib/context/_context.v81
-rw-r--r--v_windows/v/vlib/context/cancel.v181
-rw-r--r--v_windows/v/vlib/context/cancel_test.v42
-rw-r--r--v_windows/v/vlib/context/deadline.v94
-rw-r--r--v_windows/v/vlib/context/deadline_test.v48
-rw-r--r--v_windows/v/vlib/context/empty.v42
-rw-r--r--v_windows/v/vlib/context/empty_test.v17
-rw-r--r--v_windows/v/vlib/context/err.v12
-rw-r--r--v_windows/v/vlib/context/value.v57
-rw-r--r--v_windows/v/vlib/context/value_test.v23
11 files changed, 763 insertions, 0 deletions
diff --git a/v_windows/v/vlib/context/README.md b/v_windows/v/vlib/context/README.md
new file mode 100644
index 0000000..7fbf18b
--- /dev/null
+++ b/v_windows/v/vlib/context/README.md
@@ -0,0 +1,166 @@
+# Context
+
+This module defines the Context type, which carries deadlines, cancellation signals,
+and other request-scoped values across API boundaries and between processes.
+
+Incoming requests to a server should create a Context, and outgoing calls to servers
+should accept a Context. The chain of function calls between them must propagate the
+Context, optionally replacing it with a derived Context created using with_cancel,
+with_deadline, with_timeout, or with_value. When a Context is canceled, all Contexts
+derived from it are also canceled.
+
+The with_cancel, with_deadline, and with_timeout functions take a Context (the parent)
+and return a derived Context (the child). Calling the cancel function
+cancels the child and its children, removes the parent's reference to the child,
+and stops any associated timers.
+
+Programs that use Contexts should follow these rules to keep interfaces consistent
+across different modules.
+
+Do not store Contexts inside a struct type; instead, pass a Context explicitly
+to each function that needs it. The Context should be the first parameter,
+typically named ctx, just to make it more consistent.
+
+## Examples
+
+In this section you can see some usage examples for this module
+
+### Context With Cancellation
+
+```v
+import context
+
+// This example demonstrates the use of a cancelable context to prevent a
+// routine leak. By the end of the example function, the routine started
+// by gen will return without leaking.
+fn example_with_cancel() {
+ // gen generates integers in a separate routine and
+ // sends them to the returned channel.
+ // The callers of gen need to cancel the context once
+ // they are done consuming generated integers not to leak
+ // the internal routine started by gen.
+ gen := fn (ctx context.Context) chan int {
+ dst := chan int{}
+ go fn (ctx context.Context, dst chan int) {
+ mut v := 0
+ ch := ctx.done()
+ for {
+ select {
+ _ := <-ch {
+ // returning not to leak the routine
+ return
+ }
+ dst <- v {
+ v++
+ }
+ }
+ }
+ }(ctx, dst)
+ return dst
+ }
+
+ ctx := context.with_cancel(context.background())
+ defer {
+ context.cancel(ctx)
+ }
+
+ ch := gen(ctx)
+ for i in 0 .. 5 {
+ v := <-ch
+ assert i == v
+ }
+}
+```
+
+### Context With Deadline
+
+```v
+import context
+import time
+
+const (
+ // a reasonable duration to block in an example
+ short_duration = 1 * time.millisecond
+)
+
+// This example passes a context with an arbitrary deadline to tell a blocking
+// 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)
+
+ 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)
+ }
+
+ ctx_ch := ctx.done()
+ select {
+ _ := <-ctx_ch {}
+ 1 * time.second {
+ panic('This should not happen')
+ }
+ }
+}
+```
+
+### Context With Timeout
+
+```v
+import context
+import time
+
+const (
+ // a reasonable duration to block in an example
+ short_duration = 1 * time.millisecond
+)
+
+// This example passes a context with a timeout to tell a blocking function that
+// it should abandon its work after the timeout elapses.
+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)
+ defer {
+ context.cancel(ctx)
+ }
+
+ ctx_ch := ctx.done()
+ select {
+ _ := <-ctx_ch {}
+ 1 * time.second {
+ panic('This should not happen')
+ }
+ }
+}
+```
+
+### Context With Value
+
+```v
+import context
+
+type ValueContextKey = 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 {
+ if value := ctx.value(key) {
+ if !isnil(value) {
+ return *(&string(value))
+ }
+ }
+ return 'key not found'
+ }
+
+ key := ValueContextKey('language')
+ value := 'VAL'
+ ctx := context.with_value(context.background(), key, &value)
+
+ assert value == f(ctx, key)
+ assert 'key not found' == f(ctx, ValueContextKey('color'))
+}
+```
diff --git a/v_windows/v/vlib/context/_context.v b/v_windows/v/vlib/context/_context.v
new file mode 100644
index 0000000..5d4e2d1
--- /dev/null
+++ b/v_windows/v/vlib/context/_context.v
@@ -0,0 +1,81 @@
+// 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
+// Last commit: https://github.com/golang/go/commit/52bf14e0e8bdcd73f1ddfb0c4a1d0200097d3ba2
+module context
+
+import time
+
+const (
+ background = EmptyContext(0)
+ todo = EmptyContext(1)
+
+ cancel_context_key = 'context.CancelContext'
+
+ // canceled is the error returned by Context.err when the context is canceled.
+ canceled = error('context canceled')
+
+ // deadline_exceeded is the error returned by Context.err when the context's
+ // deadline passes.
+ deadline_exceeded = error('context deadline exceeded')
+)
+
+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
+ // set. Successive calls to deadline return the same results.
+ deadline() ?time.Time
+ // done returns a channel that's closed when work done on behalf of this
+ // context should be canceled. done may return nil if this context can
+ // never be canceled. Successive calls to done return the same value.
+ // The close of the done channel may happen asynchronously,
+ // after the cancel function returns.
+ //
+ // with_cancel arranges for done to be closed when cancel is called;
+ // with_deadline arranges for done to be closed when the deadline
+ // expires; with_timeout arranges for done to be closed when the timeout
+ // elapses.
+ done() chan int
+ // If done is not yet closed, err returns nil.
+ // If done is closed, err returns a non-nil error explaining why:
+ // canceled if the context was canceled
+ // or deadline_exceeded if the context's deadline passed.
+ // After err returns a non-nil error, successive calls to err return the same error.
+ err() IError
+ // Value returns the value associated with this context for key, or nil
+ // if no value is associated with key. Successive calls to Value with
+ // the same key returns the same result.
+ //
+ // Use context values only for request-scoped data that transits
+ // processes and API boundaries, not for passing optional parameters to
+ // functions.
+ //
+ // A key identifies a specific value in a Context. Functions that wish
+ // to store values in Context typically allocate a key in a global
+ // variable then use that key as the argument to context.with_value and
+ // 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
+ str() string
+}
+
+// background returns an empty Context. It is never canceled, has no
+// values, and has no deadline. It is typically used by the main function,
+// initialization, and tests, and as the top-level Context for incoming
+// requests.
+pub fn background() Context {
+ return context.background
+}
+
+// todo returns an empty Context. Code should use todo when
+// it's unclear which Context to use or it is not yet available (because the
+// surrounding function has not yet been extended to accept a Context
+// parameter).
+pub fn todo() Context {
+ return context.todo
+}
+
+fn context_name(ctx Context) string {
+ return typeof(ctx)
+}
diff --git a/v_windows/v/vlib/context/cancel.v b/v_windows/v/vlib/context/cancel.v
new file mode 100644
index 0000000..1a0ae57
--- /dev/null
+++ b/v_windows/v/vlib/context/cancel.v
@@ -0,0 +1,181 @@
+// 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
+// Last commit: https://github.com/golang/go/commit/52bf14e0e8bdcd73f1ddfb0c4a1d0200097d3ba2
+module context
+
+import rand
+import sync
+import time
+
+pub interface Canceler {
+ id string
+ cancel(remove_from_parent bool, err IError)
+ done() chan int
+}
+
+pub fn cancel(ctx Context) {
+ match mut ctx {
+ CancelContext {
+ ctx.cancel(true, canceled)
+ }
+ TimerContext {
+ ctx.cancel(true, canceled)
+ }
+ else {}
+ }
+}
+
+// A CancelContext can be canceled. When canceled, it also cancels any children
+// that implement Canceler.
+pub struct CancelContext {
+ id string
+mut:
+ context Context
+ mutex &sync.Mutex
+ done chan int
+ children map[string]Canceler
+ err IError
+}
+
+// with_cancel returns a copy of parent with a new done channel. The returned
+// context's done channel is closed when the returned cancel function is called
+// or when the parent context's done channel is closed, whichever happens first.
+//
+// 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 {
+ mut c := new_cancel_context(parent)
+ propagate_cancel(parent, mut c)
+ return Context(c)
+}
+
+// new_cancel_context returns an initialized CancelContext.
+fn new_cancel_context(parent Context) &CancelContext {
+ return &CancelContext{
+ id: rand.uuid_v4()
+ context: parent
+ mutex: sync.new_mutex()
+ done: chan int{cap: 2}
+ err: none
+ }
+}
+
+pub fn (ctx CancelContext) deadline() ?time.Time {
+ return none
+}
+
+pub fn (mut ctx CancelContext) done() chan int {
+ ctx.mutex.@lock()
+ done := ctx.done
+ ctx.mutex.unlock()
+ return done
+}
+
+pub fn (mut ctx CancelContext) err() IError {
+ ctx.mutex.@lock()
+ err := ctx.err
+ ctx.mutex.unlock()
+ return err
+}
+
+pub fn (ctx CancelContext) value(key string) ?voidptr {
+ if key == cancel_context_key {
+ return voidptr(unsafe { &ctx })
+ }
+ return ctx.context.value(key)
+}
+
+pub fn (ctx CancelContext) str() string {
+ return context_name(ctx.context) + '.with_cancel'
+}
+
+fn (mut ctx CancelContext) cancel(remove_from_parent bool, err IError) {
+ if err is none {
+ panic('context: internal error: missing cancel error')
+ }
+
+ ctx.mutex.@lock()
+ if ctx.err !is none {
+ ctx.mutex.unlock()
+ // already canceled
+ return
+ }
+
+ ctx.err = err
+
+ if !ctx.done.closed {
+ ctx.done <- 0
+ ctx.done.close()
+ }
+
+ for _, child in ctx.children {
+ // NOTE: acquiring the child's lock while holding parent's lock.
+ child.cancel(false, err)
+ }
+
+ ctx.children = map[string]Canceler{}
+ ctx.mutex.unlock()
+
+ if remove_from_parent {
+ remove_child(ctx.context, ctx)
+ }
+}
+
+fn propagate_cancel(parent Context, mut child Canceler) {
+ done := parent.done()
+ select {
+ _ := <-done {
+ // parent is already canceled
+ child.cancel(false, parent.err())
+ return
+ }
+ }
+ mut p := parent_cancel_context(parent) or {
+ go fn (parent Context, mut child Canceler) {
+ pdone := parent.done()
+ select {
+ _ := <-pdone {
+ child.cancel(false, parent.err())
+ }
+ }
+ }(parent, mut child)
+ return
+ }
+
+ if p.err is none {
+ p.children[child.id] = *child
+ } else {
+ // parent has already been canceled
+ child.cancel(false, p.err)
+ }
+}
+
+// parent_cancel_context returns the underlying CancelContext for parent.
+// It does this by looking up parent.value(&cancel_context_key) to find
+// the innermost enclosing CancelContext and then checking whether
+// 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 {
+ 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)
+ pdone := p.done()
+ if done == pdone {
+ return *p
+ }
+ }
+ }
+ return none
+}
+
+// remove_child removes a context from its parent.
+fn remove_child(parent Context, child Canceler) {
+ mut p := parent_cancel_context(parent) or { return }
+ p.children.delete(child.id)
+}
diff --git a/v_windows/v/vlib/context/cancel_test.v b/v_windows/v/vlib/context/cancel_test.v
new file mode 100644
index 0000000..6b9fdaa
--- /dev/null
+++ b/v_windows/v/vlib/context/cancel_test.v
@@ -0,0 +1,42 @@
+import context
+
+// This example demonstrates the use of a cancelable context to prevent a
+// routine leak. By the end of the example function, the routine started
+// by gen will return without leaking.
+fn test_with_cancel() {
+ // gen generates integers in a separate routine and
+ // sends them to the returned channel.
+ // The callers of gen need to cancel the context once
+ // they are done consuming generated integers not to leak
+ // the internal routine started by gen.
+ gen := fn (ctx context.Context) chan int {
+ dst := chan int{}
+ go fn (ctx context.Context, dst chan int) {
+ mut v := 0
+ ch := ctx.done()
+ for {
+ select {
+ _ := <-ch {
+ // returning not to leak the routine
+ return
+ }
+ dst <- v {
+ v++
+ }
+ }
+ }
+ }(ctx, dst)
+ return dst
+ }
+
+ ctx := context.with_cancel(context.background())
+ defer {
+ context.cancel(ctx)
+ }
+
+ ch := gen(ctx)
+ for i in 0 .. 5 {
+ v := <-ch
+ assert i == v
+ }
+}
diff --git a/v_windows/v/vlib/context/deadline.v b/v_windows/v/vlib/context/deadline.v
new file mode 100644
index 0000000..43fd056
--- /dev/null
+++ b/v_windows/v/vlib/context/deadline.v
@@ -0,0 +1,94 @@
+// 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
+// Last commit: https://github.com/golang/go/commit/52bf14e0e8bdcd73f1ddfb0c4a1d0200097d3ba2
+module context
+
+import rand
+import time
+
+// A TimerContext carries a timer and a deadline. It embeds a CancelContext to
+// implement done and err. It implements cancel by stopping its timer then
+// delegating to CancelContext.cancel
+pub struct TimerContext {
+ id string
+mut:
+ cancel_ctx CancelContext
+ deadline time.Time
+}
+
+// with_deadline returns a copy of the parent context with the deadline adjusted
+// to be no later than d. If the parent's deadline is already earlier than d,
+// with_deadline(parent, d) is semantically equivalent to parent. The returned
+// context's Done channel is closed when the deadline expires, when the returned
+// cancel function is called, or when the parent context's Done channel is
+// closed, whichever happens first.
+//
+// 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 {
+ id := rand.uuid_v4()
+ if cur := parent.deadline() {
+ if cur < d {
+ // The current deadline is already sooner than the new one.
+ return with_cancel(parent)
+ }
+ }
+ cancel_ctx := new_cancel_context(parent)
+ mut ctx := &TimerContext{
+ cancel_ctx: cancel_ctx
+ deadline: d
+ id: id
+ }
+ propagate_cancel(parent, mut ctx)
+ dur := d - time.now()
+ if dur.nanoseconds() <= 0 {
+ ctx.cancel(true, deadline_exceeded) // deadline has already passed
+ return Context(ctx)
+ }
+
+ if ctx.err() is none {
+ go fn (mut ctx TimerContext, dur time.Duration) {
+ time.sleep(dur)
+ ctx.cancel(true, deadline_exceeded)
+ }(mut ctx, dur)
+ }
+ return Context(ctx)
+}
+
+// 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 {
+ return with_deadline(parent, time.now().add(timeout))
+}
+
+pub fn (ctx TimerContext) deadline() ?time.Time {
+ return ctx.deadline
+}
+
+pub fn (mut ctx TimerContext) done() chan int {
+ return ctx.cancel_ctx.done()
+}
+
+pub fn (mut ctx TimerContext) err() IError {
+ return ctx.cancel_ctx.err()
+}
+
+pub fn (ctx TimerContext) value(key string) ?voidptr {
+ return ctx.cancel_ctx.value(key)
+}
+
+pub fn (mut ctx TimerContext) cancel(remove_from_parent bool, err IError) {
+ ctx.cancel_ctx.cancel(false, err)
+ if remove_from_parent {
+ // Remove this TimerContext from its parent CancelContext's children.
+ remove_child(ctx.cancel_ctx.context, ctx)
+ }
+}
+
+pub fn (ctx TimerContext) str() string {
+ return context_name(ctx.cancel_ctx.context) + '.with_deadline(' + ctx.deadline.str() + ' [' +
+ (time.now() - ctx.deadline).str() + '])'
+}
diff --git a/v_windows/v/vlib/context/deadline_test.v b/v_windows/v/vlib/context/deadline_test.v
new file mode 100644
index 0000000..e4d7280
--- /dev/null
+++ b/v_windows/v/vlib/context/deadline_test.v
@@ -0,0 +1,48 @@
+import context
+import time
+
+const (
+ // a reasonable duration to block in an example
+ short_duration = 1 * time.millisecond
+)
+
+// This example passes a context with an arbitrary deadline to tell a blocking
+// 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)
+
+ 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)
+ }
+
+ ctx_ch := ctx.done()
+ select {
+ _ := <-ctx_ch {}
+ 1 * time.second {
+ panic('This should not happen')
+ }
+ }
+}
+
+// This example passes a context with a timeout to tell a blocking function that
+// it should abandon its work after the timeout elapses.
+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)
+ defer {
+ context.cancel(ctx)
+ }
+
+ ctx_ch := ctx.done()
+ select {
+ _ := <-ctx_ch {}
+ 1 * time.second {
+ panic('This should not happen')
+ }
+ }
+}
diff --git a/v_windows/v/vlib/context/empty.v b/v_windows/v/vlib/context/empty.v
new file mode 100644
index 0000000..335369a
--- /dev/null
+++ b/v_windows/v/vlib/context/empty.v
@@ -0,0 +1,42 @@
+// 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
+// Last commit: https://github.com/golang/go/commit/52bf14e0e8bdcd73f1ddfb0c4a1d0200097d3ba2
+module context
+
+import time
+
+// An EmptyContext is never canceled, has no values, and has no deadline. It is not
+// struct{}, since vars of this type must have distinct addresses.
+pub type EmptyContext = int
+
+pub fn (ctx EmptyContext) deadline() ?time.Time {
+ return none
+}
+
+pub fn (ctx EmptyContext) done() chan int {
+ ch := chan int{}
+ defer {
+ ch.close()
+ }
+ return ch
+}
+
+pub fn (ctx EmptyContext) err() IError {
+ // TODO: Change this to `none`
+ return none_
+}
+
+pub fn (ctx EmptyContext) value(key string) ?voidptr {
+ return none
+}
+
+pub fn (ctx EmptyContext) str() string {
+ if ctx == background {
+ return 'context.Background'
+ }
+ if ctx == todo {
+ return 'context.TODO'
+ }
+ return 'unknown empty Context'
+}
diff --git a/v_windows/v/vlib/context/empty_test.v b/v_windows/v/vlib/context/empty_test.v
new file mode 100644
index 0000000..433d8c8
--- /dev/null
+++ b/v_windows/v/vlib/context/empty_test.v
@@ -0,0 +1,17 @@
+module context
+
+fn test_background() {
+ ctx := background()
+ assert 'context.Background' == ctx.str()
+ if _ := ctx.value('') {
+ panic('This should never happen')
+ }
+}
+
+fn test_todo() {
+ ctx := todo()
+ assert 'context.TODO' == ctx.str()
+ if _ := ctx.value('') {
+ panic('This should never happen')
+ }
+}
diff --git a/v_windows/v/vlib/context/err.v b/v_windows/v/vlib/context/err.v
new file mode 100644
index 0000000..23cfe56
--- /dev/null
+++ b/v_windows/v/vlib/context/err.v
@@ -0,0 +1,12 @@
+module context
+
+const none_ = IError(&None{})
+
+struct None {
+ msg string
+ code int
+}
+
+fn (_ None) str() string {
+ return 'none'
+}
diff --git a/v_windows/v/vlib/context/value.v b/v_windows/v/vlib/context/value.v
new file mode 100644
index 0000000..19a2289
--- /dev/null
+++ b/v_windows/v/vlib/context/value.v
@@ -0,0 +1,57 @@
+// 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
+// Last commit: https://github.com/golang/go/commit/52bf14e0e8bdcd73f1ddfb0c4a1d0200097d3ba2
+module context
+
+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
+mut:
+ context Context
+}
+
+// with_value returns a copy of parent in which the value associated with key is
+// val.
+//
+// Use context Values only for request-scoped data that transits processes and
+// APIs, not for passing optional parameters to functions.
+//
+// The provided key must be comparable and should not be of type
+// 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 {
+ return &ValueContext{
+ context: parent
+ key: key
+ value: value
+ }
+}
+
+pub fn (ctx ValueContext) deadline() ?time.Time {
+ return ctx.context.deadline()
+}
+
+pub fn (ctx ValueContext) done() chan int {
+ return ctx.context.done()
+}
+
+pub fn (ctx ValueContext) err() IError {
+ return ctx.context.err()
+}
+
+pub fn (ctx ValueContext) value(key string) ?voidptr {
+ if ctx.key == key {
+ return ctx.value
+ }
+ return ctx.context.value(key)
+}
+
+pub fn (ctx ValueContext) str() string {
+ return context_name(ctx.context) + '.with_value'
+}
diff --git a/v_windows/v/vlib/context/value_test.v b/v_windows/v/vlib/context/value_test.v
new file mode 100644
index 0000000..a8ed5b5
--- /dev/null
+++ b/v_windows/v/vlib/context/value_test.v
@@ -0,0 +1,23 @@
+import context
+
+type ValueContextKey = 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 {
+ if value := ctx.value(key) {
+ if !isnil(value) {
+ return *(&string(value))
+ }
+ }
+ return 'key not found'
+ }
+
+ key := ValueContextKey('language')
+ value := 'VAL'
+ ctx := context.with_value(context.background(), key, &value)
+
+ assert value == f(ctx, key)
+ assert 'key not found' == f(ctx, ValueContextKey('color'))
+}