diff options
Diffstat (limited to 'v_windows/v/old/vlib/context')
| -rw-r--r-- | v_windows/v/old/vlib/context/README.md | 166 | ||||
| -rw-r--r-- | v_windows/v/old/vlib/context/_context.v | 81 | ||||
| -rw-r--r-- | v_windows/v/old/vlib/context/cancel.v | 181 | ||||
| -rw-r--r-- | v_windows/v/old/vlib/context/cancel_test.v | 42 | ||||
| -rw-r--r-- | v_windows/v/old/vlib/context/deadline.v | 94 | ||||
| -rw-r--r-- | v_windows/v/old/vlib/context/deadline_test.v | 48 | ||||
| -rw-r--r-- | v_windows/v/old/vlib/context/empty.v | 42 | ||||
| -rw-r--r-- | v_windows/v/old/vlib/context/empty_test.v | 17 | ||||
| -rw-r--r-- | v_windows/v/old/vlib/context/err.v | 12 | ||||
| -rw-r--r-- | v_windows/v/old/vlib/context/value.v | 57 | ||||
| -rw-r--r-- | v_windows/v/old/vlib/context/value_test.v | 23 | 
11 files changed, 763 insertions, 0 deletions
diff --git a/v_windows/v/old/vlib/context/README.md b/v_windows/v/old/vlib/context/README.md new file mode 100644 index 0000000..7fbf18b --- /dev/null +++ b/v_windows/v/old/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/old/vlib/context/_context.v b/v_windows/v/old/vlib/context/_context.v new file mode 100644 index 0000000..5d4e2d1 --- /dev/null +++ b/v_windows/v/old/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/old/vlib/context/cancel.v b/v_windows/v/old/vlib/context/cancel.v new file mode 100644 index 0000000..2972b17 --- /dev/null +++ b/v_windows/v/old/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/old/vlib/context/cancel_test.v b/v_windows/v/old/vlib/context/cancel_test.v new file mode 100644 index 0000000..6b9fdaa --- /dev/null +++ b/v_windows/v/old/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/old/vlib/context/deadline.v b/v_windows/v/old/vlib/context/deadline.v new file mode 100644 index 0000000..43fd056 --- /dev/null +++ b/v_windows/v/old/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/old/vlib/context/deadline_test.v b/v_windows/v/old/vlib/context/deadline_test.v new file mode 100644 index 0000000..e4d7280 --- /dev/null +++ b/v_windows/v/old/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/old/vlib/context/empty.v b/v_windows/v/old/vlib/context/empty.v new file mode 100644 index 0000000..335369a --- /dev/null +++ b/v_windows/v/old/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/old/vlib/context/empty_test.v b/v_windows/v/old/vlib/context/empty_test.v new file mode 100644 index 0000000..433d8c8 --- /dev/null +++ b/v_windows/v/old/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/old/vlib/context/err.v b/v_windows/v/old/vlib/context/err.v new file mode 100644 index 0000000..23cfe56 --- /dev/null +++ b/v_windows/v/old/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/old/vlib/context/value.v b/v_windows/v/old/vlib/context/value.v new file mode 100644 index 0000000..19a2289 --- /dev/null +++ b/v_windows/v/old/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/old/vlib/context/value_test.v b/v_windows/v/old/vlib/context/value_test.v new file mode 100644 index 0000000..a8ed5b5 --- /dev/null +++ b/v_windows/v/old/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')) +}  | 
