File: ctxstack.go

package info (click to toggle)
fq 0.9.0-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 106,624 kB
  • sloc: xml: 2,835; makefile: 250; sh: 241; exp: 57; ansic: 21
file content (73 lines) | stat: -rw-r--r-- 1,602 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
// Package ctxstack manages a stack of contexts. When triggerFn returns and closeCh is not closed
// it will cancel the top context. Stack is popped in top first order when returned cancel funcition
// is called.
// This can be used to keep track of contexts for nested REPL:s were you only want to cancel
// the current active "top" REPL.
// TODO: should New take a parent context?
package ctxstack

import (
	"context"
)

// Stack is a context stack
type Stack struct {
	cancelFns []func()
	stopCh    chan struct{}
}

// New context stack
func New(triggerCh func(stopCh chan struct{})) *Stack {
	stopCh := make(chan struct{})
	s := &Stack{stopCh: stopCh}

	go func() {
		for {
			triggerCh(stopCh)
			select {
			case <-stopCh:
				// stop if stopCh closed
			default:
				// ignore if triggered before any context pushed
				if len(s.cancelFns) > 0 {
					s.cancelFns[len(s.cancelFns)-1]()
				}
				continue
			}
			break
		}
	}()

	return s
}

// Stop context stack
func (s *Stack) Stop() {
	for i := len(s.cancelFns) - 1; i >= 0; i-- {
		s.cancelFns[i]()
	}
	close(s.stopCh)
}

// Push creates, pushes and returns new context. Cancel pops it.
func (s *Stack) Push(parent context.Context) (context.Context, func()) {
	stackCtx, stackCtxCancel := context.WithCancel(parent)
	stackIdx := len(s.cancelFns)

	s.cancelFns = append(s.cancelFns, stackCtxCancel)
	cancelled := false

	return stackCtx, func() {
		if cancelled {
			return
		}
		cancelled = true

		for i := len(s.cancelFns) - 1; i >= stackIdx; i-- {
			s.cancelFns[i]()
		}
		s.cancelFns = s.cancelFns[0:stackIdx]

		stackCtxCancel()
	}
}