context package: why need run goroutine in propagateCancel function

167 views
Skip to first unread message

qinggeer Bao

unread,
Apr 26, 2021, 12:59:14 PM4/26/21
to golang-nuts
For learning purpose, I'm reading the source code of Golang standard package: context.
Roughly understand the logics with one confusing point in the propagateCancel below:

func propagateCancel(parent Context, child canceler) {
    done := parent.Done()
    if done == nil {
        return // parent is never canceled
    }

    select {
    case <-done:
        // parent is already canceled
        child.cancel(false, parent.Err())
        return
    default:
    }

    if p, ok := parentCancelCtx(parent); ok {
        p.mu.Lock()
        if p.err != nil {
            // parent has already been canceled
            child.cancel(false, p.err)
        } else {
            if p.children == nil {
                p.children = make(map[canceler]struct{})
            }
            p.children[child] = struct{}{}
        }
        p.mu.Unlock()
    } else {
        atomic.AddInt32(&goroutines, +1)
        go func() {
            select {
            case <-parent.Done():
                child.cancel(false, parent.Err())
            case <-child.Done():
            }
        }()
    }
}


propagateCancel function is used to bind the new cancel context to its cancellable ancestor context. The confusing point is this else block:
else {
        atomic.AddInt32(&goroutines, +1)
        go func() {
            select {
            case <-parent.Done():
                child.cancel(false, parent.Err())
            case <-child.Done():
            }
        }()
    }

if parentCancelCtx return false, it means no cancellable ancestors found, then why do we need run another goroutine to listen for parent's cancel signals? How it can happen in what kind of cases?

Ian Lance Taylor

unread,
Apr 26, 2021, 5:50:09 PM4/26/21
to qinggeer Bao, golang-nuts
On Mon, Apr 26, 2021 at 9:59 AM qinggeer Bao <bao...@gmail.com> wrote:
>
> if parentCancelCtx return false, it means no cancellable ancestors found, then why do we need run another goroutine to listen for parent's cancel signals? How it can happen in what kind of cases?

See the documentation of parentCancelCtx: "If not, the *cancelCtx has
been wrapped in a custom implementation providing a different done
channel, in which case we should not bypass it." In other words,
people can and do provide their own custom implementations of the
context.Context interface. For those implementations, parentCancelCtx
will return nil, but that doesn't mean that the custom implementation
can't be cancelled. propagateCancel has to handle that case
correctly. See also XTestWithCancel and
XTestWithCancelCanceledParent.

Ian

qinggeer Bao

unread,
Apr 26, 2021, 9:52:51 PM4/26/21
to golang-nuts
Thanks. I see.
Reply all
Reply to author
Forward
0 new messages