147 lines
		
	
	
		
			3.0 KiB
		
	
	
	
		
			V
		
	
	
			
		
		
	
	
			147 lines
		
	
	
		
			3.0 KiB
		
	
	
	
		
			V
		
	
	
module onecontext
 | 
						|
 | 
						|
import context
 | 
						|
import sync
 | 
						|
import time
 | 
						|
 | 
						|
// canceled is the error returned when the cancel function is called on a merged context
 | 
						|
pub const canceled = error('canceled context')
 | 
						|
 | 
						|
struct OneContext {
 | 
						|
mut:
 | 
						|
	ctx        context.Context
 | 
						|
	ctxs       []context.Context
 | 
						|
	done       chan int
 | 
						|
	err        IError = none
 | 
						|
	err_mutex  sync.Mutex
 | 
						|
	cancel_fn  context.CancelFn
 | 
						|
	cancel_ctx context.Context
 | 
						|
}
 | 
						|
 | 
						|
// merge allows to merge multiple contexts
 | 
						|
// it returns the merged context
 | 
						|
pub fn merge(ctx context.Context, ctxs ...context.Context) (context.Context, context.CancelFn) {
 | 
						|
	mut background := context.background()
 | 
						|
	cancel_ctx, cancel := context.with_cancel(mut &background)
 | 
						|
	mut octx := &OneContext{
 | 
						|
		done: chan int{cap: 3}
 | 
						|
		ctx: ctx
 | 
						|
		ctxs: ctxs
 | 
						|
		cancel_fn: cancel
 | 
						|
		cancel_ctx: cancel_ctx
 | 
						|
	}
 | 
						|
	go octx.run()
 | 
						|
	return context.Context(octx), context.CancelFn(cancel)
 | 
						|
}
 | 
						|
 | 
						|
pub fn (octx OneContext) deadline() ?time.Time {
 | 
						|
	mut min := time.Time{}
 | 
						|
 | 
						|
	if deadline := octx.ctx.deadline() {
 | 
						|
		min = deadline
 | 
						|
	}
 | 
						|
 | 
						|
	for ctx in octx.ctxs {
 | 
						|
		if deadline := ctx.deadline() {
 | 
						|
			if min.unix_time() == 0 || deadline < min {
 | 
						|
				min = deadline
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if min.unix_time() == 0 {
 | 
						|
		return none
 | 
						|
	}
 | 
						|
 | 
						|
	return min
 | 
						|
}
 | 
						|
 | 
						|
pub fn (octx OneContext) done() chan int {
 | 
						|
	return octx.done
 | 
						|
}
 | 
						|
 | 
						|
pub fn (mut octx OneContext) err() IError {
 | 
						|
	octx.err_mutex.@lock()
 | 
						|
	defer {
 | 
						|
		octx.err_mutex.unlock()
 | 
						|
	}
 | 
						|
	return octx.err
 | 
						|
}
 | 
						|
 | 
						|
pub fn (octx OneContext) value(key context.Key) ?context.Any {
 | 
						|
	if value := octx.ctx.value(key) {
 | 
						|
		return value
 | 
						|
	}
 | 
						|
 | 
						|
	for ctx in octx.ctxs {
 | 
						|
		if value := ctx.value(key) {
 | 
						|
			return value
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return none
 | 
						|
}
 | 
						|
 | 
						|
pub fn (mut octx OneContext) run() {
 | 
						|
	mut wrapped_ctx := &octx.ctx
 | 
						|
	if octx.ctxs.len == 1 {
 | 
						|
		mut first_ctx := &octx.ctxs[0]
 | 
						|
		octx.run_two_contexts(mut wrapped_ctx, mut first_ctx)
 | 
						|
		return
 | 
						|
	}
 | 
						|
 | 
						|
	octx.run_multiple_contexts(mut wrapped_ctx)
 | 
						|
	for mut ctx in octx.ctxs {
 | 
						|
		octx.run_multiple_contexts(mut &ctx)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
pub fn (octx OneContext) str() string {
 | 
						|
	return ''
 | 
						|
}
 | 
						|
 | 
						|
pub fn (mut octx OneContext) cancel(err IError) {
 | 
						|
	octx.cancel_fn()
 | 
						|
	octx.err_mutex.@lock()
 | 
						|
	octx.err = err
 | 
						|
	octx.err_mutex.unlock()
 | 
						|
	if !octx.done.closed {
 | 
						|
		octx.done <- 0
 | 
						|
		octx.done.close()
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
pub fn (mut octx OneContext) run_two_contexts(mut ctx1 context.Context, mut ctx2 context.Context) {
 | 
						|
	go fn (mut octx OneContext, mut ctx1 context.Context, mut ctx2 context.Context) {
 | 
						|
		octx_cancel_done := octx.cancel_ctx.done()
 | 
						|
		c1done := ctx1.done()
 | 
						|
		c2done := ctx2.done()
 | 
						|
		select {
 | 
						|
			_ := <-octx_cancel_done {
 | 
						|
				octx.cancel(onecontext.canceled)
 | 
						|
			}
 | 
						|
			_ := <-c1done {
 | 
						|
				octx.cancel(ctx1.err())
 | 
						|
			}
 | 
						|
			_ := <-c2done {
 | 
						|
				octx.cancel(ctx1.err())
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}(mut &octx, mut &ctx1, mut &ctx2)
 | 
						|
}
 | 
						|
 | 
						|
pub fn (mut octx OneContext) run_multiple_contexts(mut ctx context.Context) {
 | 
						|
	go fn (mut octx OneContext, mut ctx context.Context) {
 | 
						|
		octx_cancel_done := octx.cancel_ctx.done()
 | 
						|
		cdone := ctx.done()
 | 
						|
		select {
 | 
						|
			_ := <-octx_cancel_done {
 | 
						|
				octx.cancel(onecontext.canceled)
 | 
						|
			}
 | 
						|
			_ := <-cdone {
 | 
						|
				octx.cancel(ctx.err())
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}(mut &octx, mut &ctx)
 | 
						|
}
 |