Home > Back-end >  Why does Go stdlib use a mutually exclusive lock to read context's error field?
Why does Go stdlib use a mutually exclusive lock to read context's error field?

Time:07-19

There are a number of under-the-hood implementations of the Context interface in the Go standard library. For instance, the Background and TODO contexts are backed by the unexposed emptyCtx type which is essentially just int with some stub methods (proof). Similarly, every call to context.WithCancel() returns an instance of the cancelCtx type which is already a proper struct with a bunch of mutex-protected attributes (proof):

// A cancelCtx can be canceled. When canceled, it also cancels any children
// that implement canceler.
type cancelCtx struct {
    Context

    mu       sync.Mutex            // protects following fields
    done     atomic.Value          // of chan struct{}, created lazily, closed by first cancel call
    children map[canceler]struct{} // set to nil by the first cancel call
    err      error                 // set to non-nil by the first cancel call
}

Why does the cancelCtx struct use a mutually exclusive lock and not an RWLock? For instance, the Err() method currently acquires a full lock while it (probably) could have used just an RLock:

func (c *cancelCtx) Err() error {
    c.mu.Lock()
    err := c.err
    c.mu.Unlock()
    return err
}

CodePudding user response:

One reason should be RWLock has poor performance.

The performance of locks doesn't depends on the features it provides, it depends on the underlying implementation. Although theoretically RWLock can provides higher throughputs, for this specific scenario (mutating a tiny variable), Mutex could provide lower unnecessary overhead.

  • Related