Documentation
¶
Index ¶
- Variables
- type Halter
- type IdemCloseChan
- func (c *IdemCloseChan) AddChild(child *IdemCloseChan)
- func (c *IdemCloseChan) Close() error
- func (c *IdemCloseChan) CloseWithReason(why error) error
- func (c *IdemCloseChan) FirstTreeReason() (err error)
- func (c *IdemCloseChan) IsClosed() bool
- func (c *IdemCloseChan) Reason() (why error, isClosed bool)
- func (c *IdemCloseChan) RemoveChild(child *IdemCloseChan)
- func (c *IdemCloseChan) WaitTilChildrenDone(giveup <-chan struct{}) (err error)
- func (c *IdemCloseChan) WaitTilDone(giveup <-chan struct{}) (err error)
Constants ¶
This section is empty.
Variables ¶
var ErrAlreadyClosed = fmt.Errorf("Chan already closed")
var ErrGiveUp = fmt.Errorf("giveup channel was closed.")
ErrGiveUp is returned by IdemCloseChan.WaitTilDone if the giveup channel close was the reason that WaitTilDone returned.
var ErrNotClosed = fmt.Errorf("tree is not closed")
ErrNotClosed is returned by FirstTreeReason if it is called on a tree that is not completely closed.
Functions ¶
This section is empty.
Types ¶
type Halter ¶
type Halter struct { // The owning goutine should call Done.Close() as its last // action once it has received the ReqStop() signal. Done *IdemCloseChan // Other goroutines call ReqStop.Close() in order // to request that the owning goroutine stop immediately. // The owning goroutine should select on ReqStop.Chan // in order to recognize shutdown requests. ReqStop *IdemCloseChan // contains filtered or unexported fields }
Halter helps shutdown a goroutine
func (*Halter) AddChild ¶ added in v0.0.5
AddChild adds child to h's children; and adds child.ReqStop to the children of h.ReqStop.
Note that if h.ReqStop is already closed, then child.ReqStop will also be ClosedWithReason with the same reason as h, if any is available.
func (*Halter) IsStopRequested ¶
IsStopRequested returns true iff h.ReqStop has been Closed().
func (*Halter) MarkDone ¶
func (h *Halter) MarkDone()
MarkDone closes the h.Done channel if it has not already done so. Safe for multiple goroutine access.
func (*Halter) RemoveChild ¶ added in v0.0.5
RemoveChild removes child from the set of h.children. It does not undo any close operation that AddChild did on addition.
func (*Halter) RequestStop ¶
func (h *Halter) RequestStop()
RequestStop closes the h.ReqStop channel if it has not already done so. Safe for multiple goroutine access.
func (*Halter) StopTreeAndWaitTilDone ¶ added in v0.0.5
StopTreeAndWaitTilDone first calls ReqStop.CloseWithReason(why) on h (which will recursively call ReqStop.CloseWithReason(why) on all non-closed children in the ReqStop parallel, tree, efficiently stopping at the first already-closed node). Then it waits up to atMost time for all tree members to Close their Done.Chan. It may return much more quickly than atMost, but will never wait longer. An atMost duration <= 0 will wait indefinitely.
type IdemCloseChan ¶
type IdemCloseChan struct { Chan chan struct{} // contains filtered or unexported fields }
IdemCloseChan can have Close() called on it multiple times, and it will only close Chan once.
func NewIdemCloseChan ¶
func NewIdemCloseChan() *IdemCloseChan
NewIdemCloseChan makes a new IdemCloseChan.
func (*IdemCloseChan) AddChild ¶
func (c *IdemCloseChan) AddChild(child *IdemCloseChan)
AddChild adds a child IdemCloseChan that will be closed when its parent is Close()-ed. If c is already closed, we close child immediately.
func (*IdemCloseChan) Close ¶
func (c *IdemCloseChan) Close() error
Close returns ErrAlreadyClosed if it has been called before. It never closes IdemClose.Chan more than once, so it is safe to ignore the returned error value. Close() is safe for concurrent access by multiple goroutines. Close returns nil on the initial call, and ErrAlreadyClosed on any subsequent call.
When we actually close the underlying c.Chan, we also call Close on any children, while holding our mutex. This does not happen if the underlying channel is already closed, so the recursion stops at the depth of the first already-closed node in the tree.
func (*IdemCloseChan) CloseWithReason ¶ added in v0.0.7
func (c *IdemCloseChan) CloseWithReason(why error) error
CloseWithReason is a no-op if c is already closed. Otherwise, we record the why and close c. Use Reason() to get the why back. Like Close(), we recursive call CloseWithReason() on any children. Naturally decendents will only set why if they were still open when the recursive CloseWithReason gets to them, and if any child is already closed the recursion stops.
func (*IdemCloseChan) FirstTreeReason ¶ added in v0.4.0
func (c *IdemCloseChan) FirstTreeReason() (err error)
FirstTreeReason scans the whole tree root at c for the first non-nil close reason, and returns it. If the tree is not completely closed at any node, it returns ErrNotClosed.
func (*IdemCloseChan) IsClosed ¶
func (c *IdemCloseChan) IsClosed() bool
IsClosed tells you if Chan is already closed or not.
func (*IdemCloseChan) Reason ¶ added in v0.0.7
func (c *IdemCloseChan) Reason() (why error, isClosed bool)
Reason gets the why set with CloseWithReason(). It also returns the closed state of c. If c is still open, why will be nil, as why is only set during the initial CloseWithReason if c was still open. The returned why will also be nil if c was closed without a reason, by Close() for example.
Callers should be prepared to handle a nil why no matter what the state of isClosed.
func (*IdemCloseChan) RemoveChild ¶
func (c *IdemCloseChan) RemoveChild(child *IdemCloseChan)
func (*IdemCloseChan) WaitTilChildrenDone ¶ added in v0.5.0
func (c *IdemCloseChan) WaitTilChildrenDone(giveup <-chan struct{}) (err error)
WaitTilChildrenDone is just like WaitTilDone, but does not require (or check) if we ourselves, the root of our tree, is closed.
func (*IdemCloseChan) WaitTilDone ¶ added in v0.2.1
func (c *IdemCloseChan) WaitTilDone(giveup <-chan struct{}) (err error)
WaitTilDone does not return until either a) we and all our children have closed the Done channel; or b) the supplied giveup channel has been closed.
We recursively call WaitTilDone on all of our children, and then wait for our own Done close.
Note we cannot use a time.After channel for giveup, since that only fires once--we have possibly many children to wait for. Hence giveup must be _closed_, not just sent on, to have them all stop waiting.
There is no protection against racey mutation of our child tree once this operation has started. The user must guarantee that nobody will do AddChild or RemoveChild while we are waiting.
If the giveup channel was closed, the returned error will be ErrGiveUp. Otherwise it will be the first Reason() supplied by any CloseWithReason() calls in the tree. The first non-nil 'why' error we encounter is sticky. If err is returned nil, you know that either CloseWithReason was not used, or that there were only nil reason closes.