Golang Context Source Learning

Source: Internet
Author: User
This is a creation in Article, where the information may have evolved or changed.

Cause

Recently, learning the Golang framework found that many places used the context concept, such as GRPC request ETCD access and many other places. In the spirit of understanding the way to the realization of the momentum, decided to study the principle of implementation.

Use

    1. Generally used in the framework of GRPC, such as setting the time-out period, such as
ctx, cancel := context.WithTimeout(context.Background(), 2 * time.Second)dial, err := grpc.DialContext(ctx, etcdAddr, grpc.WithInsecure(),grpc.WithBalancer(balancer))    cancel()

Here a timeout context is obtained by Withtimeout to Grpc. Dialcontext as a parameter, the context itself has a timer timer that automatically cancel the context and close the done Chan inside the context when the timer timer time is up. In general, the method of using CTX as a parameter will check that done Chan once found Chan closed, then should think that the operation needs to end, and thus return the error (this error is also within the context of err, within the context of cancel)

    1. When using the context in your own program, the internal implementation method is similar to the following
func doSomething(ctx Context) error{    //go .....doSomethingLong......    select{        case <-ctx.Done():            return ctx.Err()        case err <- somethingChan:            return err    }}

Approximate structure

//Context接口type Context interface {    Deadline() (deadline time.Time, ok bool)    Done() <-chan struct{}    Err() error    Value(key interface{}) interface{}}//主要暴露的方法func WithCancel(parent Context) (ctx Context, cancel CancelFunc)func WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc)func WithValue(parent Context, key, val interface{}) Contextfunc WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc)

The above is the Contxt interface structure and the most commonly exposed methods,

//内部interfacetype canceler interface {    cancel(removeFromParent bool, err error)    Done() <-chan struct{}}//内部重要structtype timerCtx struct {    cancelCtx    timer *time.Timer // Under cancelCtx.mu.    deadline time.Time}type cancelCtx struct {    Context    done chan struct{} // closed by the first cancel call.    mu       sync.Mutex    children map[canceler]struct{} // set to nil by the first cancel call    err      error                 // set to non-nil by the first cancel call}type valueCtx struct {    Context    key, val interface{}}

Canceler interface

The Cancel method cancels the context directly, and the approximate implementation method is to place the Err field of the cancelctx, which means that the context is invalidated when the Err field is not empty;
The Done method returns whether the channel is completed and the context is completed successfully

Timerctx

The actual structure returned by Withdeadline and Withtimeout (when the parent is not invalidated), which contains a cancelctx. The context of CANCELCTX is the real parent of Timerctx.
Implements the interface Canceler.

func (c *timerCtx) cancel(removeFromParent bool, err error) {    c.cancelCtx.cancel(false, err)    if removeFromParent {        // Remove this timerCtx from its parent cancelCtx's children.        removeChild(c.cancelCtx.Context, c)    }    c.mu.Lock()    if c.timer != nil {        c.timer.Stop()        c.timer = nil    }    c.mu.Unlock()}

Cancelctx

The corresponding withcancel contains the context for its parent context.
Implements the interface Canceler

func (c *cancelCtx) cancel(removeFromParent bool, err error) {    if err == nil {        panic("context: internal error: missing cancel error")    }    c.mu.Lock()    if c.err != nil {        c.mu.Unlock()        return // already canceled    }    c.err = err    close(c.done)    for child := range c.children {        // NOTE: acquiring the child's lock while holding parent's lock.        child.cancel(false, err)    }    c.children = nil    c.mu.Unlock()    if removeFromParent {        removeChild(c.Context, c)    }}

There are three main things to do

    1. Err was placed
    2. Close Chan Done
    3. Recursive call to sub-context cancel method

Valuectx

Corresponds to Withvalue. Contains the context for its parent context. The simplest of VALUECTX logic is the addition of a pair of key-value pairs, which mainly provide the role of context variable preservation. The value method can recursively look up the value corresponding to key. See the value method below

func (c *valueCtx) Value(key interface{}) interface{} {    if c.key == key {        return c.val    }    return c.Context.Value(key)}

The main concrete implementation

Func Withdeadline (parent Context, Deadline time. Time) (Context, cancelfunc) {if cur, OK: = parent. Deadline (); OK && cur.        Before (deadline) {//The current deadline is already sooner than the new one.    return Withcancel (Parent)} c: = &timerctx{cancelctx:newcancelctx (parent), Deadline:deadline, } propagatecancel (parent, c) d: = time. Until (Deadline) if D <= 0 {C.cancel (true, deadlineexceeded)//deadline has already passed return C, Func () {C.cancel (True, Canceled)}} c.mu.lock () defer c.mu.unlock () if C.err = nil {C.timer = time. Afterfunc (D, func () {C.cancel (True, Deadlineexceeded)})} return C, func () {C.cancel (True, Canc eled)}}func Withcancel (the parent Context) (CTX context, cancel Cancelfunc) {c: = Newcancelctx (parent) Propagatecancel (parent, &c) return &c, func () {C.cancel (True, Canceled)}}func propagatecancel (parent Context, child canCeler) {if parent. Done () = = Nil {return//parent is never canceled} if p, OK: = Parentcancelctx (parent); OK {p.mu.lock () if p.err! = Nil {//parent has already been canceled Child.cancel (FA            LSE, P.ERR)} else {if P.children = = Nil {P.children = make (map[canceler]struct{})            } P.children[child] = struct{}{}} p.mu.unlock ()} else {go func () { Select {Case <-parent. Done (): Child.cancel (False, parent.) ERR ()) Case <-child. Done ():}} ()}}func removechild (parent Context, child Canceler) {p, OK: = Parentcancelctx (Parent If!ok {return} p.mu.lock () if p.children! = nil {Delete (P.children, child)} p.mu.un Lock ()}

First determine whether the parent itself has expired, if it expires, only return CANCELCTX, because it has expired, there is no need to use TIMERCTX set the expiration time and so on. Otherwise, create the timerctx.
Then find out the nearest parent cancelctx, if there is a timerctx that is placed on his child, does not exist on a goroutine polling waits for the parent to complete, and once the parent completes, cancel the timerctx.
Based on the child's expiration date, if it has expired, cancel the timerctx directly and remove it from the parent, preventing the resource from accumulating, such as not expiring setting the timer expires when the Timerctx is cancel off.

The Cancel method has the parameter removefromparent, which indicates whether to remove the Canceler from the parent context.
Because only Cancelctx has a child field, you need to find the nearest cancel parent to remove the child.

Summarize

A loop of code to look down, the principle and structure actually understand, but actually there are some small details of the implementation of the people around for a long, such as

    1. Cancelctx's child put all the Canceler interface map, because the implementation of the Canceler interface structure to implement the Cancel method.

    2. Timerctx's Cancel method calls C.cancelctx.cancel (false, Err) first, and then calls RemoveChild (C.cancelctx.context, C, in the Judgment removefromparent). ). Because the direct call to C.cancelctx.canel (true, err) shows no purpose to remove C, because this is removed from C.cancelctx's child, C is in the child of C.cancelctx.context.

Concluding impressions

The original has not written this article, wrote an article only to find the time. And write these two is not good, but do write can deepen understanding, find superficial look at the code did not find the place, or very good. Keep going.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.