Implementation of Go Select

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

Select syntax summarizes each case for Select if there is an already prepared case for Chan Read and write operations, or if no defualt statement is executed, and if none is blocking the current goroutine until a certain Chan is ready for readable or writable , and exit after completing the corresponding case.

Memory layout for Select

After understanding the implementation of Chanel, there is a question about the syntax of SELECT, how select Implements Multiplexing, and why it is not blocked in the first channel operation, resulting in subsequent case execution. To solve the problem, the corresponding code looks at the functions of the runtime layer called by the Assembly, and finds that the SELECT syntax block is translated into the following procedure by the compiler.

Create select–> Register case–> perform select–> release select

select {  case c1 <-1: // non-blocking  case <-c2: // non-blocking  default: // will do this }
runtime.newselectruntime.selectsendruntime.selectrecvruntime.selectdefaultruntime.selectgo

The select is actually a hselect structure in which the registered case is placed in the scase. Scase saves the Hchan with the current case operation. Pollorder points to the scase sequence number after the disorder. The address of the Hchan that corresponds to each case will be saved in Lockorder.

type hselect struct {    tcase     uint16   // total count of scase[]    ncase     uint16   // currently filled scase[]    pollorder *uint16  // case poll order    lockorder **hchan  // channel lock order    scase     [1]scase // one per case (in order of appearance)}type scase struct {    elem        unsafe.Pointer // data element    c           *hchan         // chan    pc          uintptr        // return pc    kind        uint16    so          uint16 // vararg of selected bool    receivedp   *bool  // pointer to received bool (recv2)    releasetime int64}

The select is finally [1]scase means that only one case space is saved in select, stating that select is just a header, and that all scase are saved in the Select, and the Scases is tcase. This is often seen in the go runtime implementation in the form of Head + continuous memory.

Implementation of SELECT

Select Create

The number of case is already known in the Newselect object, and the above space has been allocated.

func selectsize(size uintptr) uintptr {    selsize := unsafe.Sizeof(hselect{}) +        (size-1)*unsafe.Sizeof(hselect{}.scase[0]) +        size*unsafe.Sizeof(*hselect{}.lockorder) +        size*unsafe.Sizeof(*hselect{}.pollorder)    return round(selsize, _Int64Align)}func newselect(sel *hselect, selsize int64, size int32) {    if selsize != int64(selectsize(uintptr(size))) {        print("runtime: bad select size ", selsize, ", want ", selectsize(uintptr(size)), "\n")        throw("bad select size")    }    sel.tcase = uint16(size)    sel.ncase = 0    sel.lockorder = (**hchan)(add(unsafe.Pointer(&sel.scase), uintptr(size)*unsafe.Sizeof(hselect{}.scase[0])))    sel.pollorder = (*uint16)(add(unsafe.Pointer(sel.lockorder), uintptr(size)*unsafe.Sizeof(*hselect{}.lockorder)))}

Register case

There are three types of case channel registrations selectsend selectrecv selectdefault , each corresponding to a different one. They are registered in the same way, are ncase+1, and then populate the relevant fields of the Scase array of the Scases field with the current index, mainly by populating the C and kind fields with the Chan and case types in case.

func selectsendImpl(sel *hselect, c *hchan, pc uintptr, elem unsafe.Pointer, so uintptr) {    i := sel.ncase    sel.ncase = i + 1    cas := (*scase)(add(unsafe.Pointer(&sel.scase), uintptr(i)*unsafe.Sizeof(sel.scase[0])))    cas.pc = pc    cas.c = c    cas.so = uint16(so)    cas.kind = caseSend    cas.elem = elem}

Select execution

Pollorder is the sequence number of the scase, and the random order is for the subsequent execution.

Lockorder saved all the address of the channel in the case, where the contiguous memory corresponding to the Lockorder is stacked according to the address size. Chan was ordered to go to the heavy, guaranteeing that all channel locks will not be re-locked when locked.

The entire Chanel is locked when the SELECT statement executes

The SELECT statement creates a select object that can be frequently allocated memory if placed in a for loop for long-term execution

The select execution process is summarized as follows:

    The
    • iterates through the scase to find the case that is ready through the ordinal of the Pollorder. Perform normal Chan read-write operations if any. The prepared case refers to the can not block the completion of the read-write Chan case, or read the closed Chan's case .
    • If a case is not prepared, try to defualt it.
    • If none of the above is present, the current G package is attached to the Scase list of all Chan, and is hung to SENDQ or RECVQ according to the type of Chan's operation.
    • This g is awakened by some Chan, traversing scase to find the target case, discarding the current G in other Chan's wait, return.
Func Selectgoimpl (sel *hselect) (UIntPtr, uint16) {//Pollorder unordered fill ordinal//lockorder sort fill scase corresponding Hchan//via Lo Ckorder Traverse each chan lockout sellock (SEL) Loop://Follow the Pollorder sequence scase see if there is a case ready for I: = 0; i < int (sel.ncase); i++ {cas = &scases[pollorder[i]] Switch cas.kind {case Caserecv:case casesend:c ASE CASEDEFAULT:DFL = cas}}//If Scase is not ready, try to execute defaut if DFL! = nil {selunlock (SE L) cas = DFL goto RETC}//If there are no available case to hang the current g to all case-corresponding Chan//waiting list SENDQ or recvq waiting to be awakened for I: = 0; i < int (sel.ncase);        i++ {cas = &scases[pollorder[i]] c = cas.c sg: = Acquiresudog () switch Cas.kind { Case Caserecv:c.recvq.enqueue (SG) case casesend:c.sendq.enqueue (SG)}} gp.par am = Nil Gopark (selparkcommit, unsafe.  Pointer (SEL), "Select", Traceevgoblockselect|futile, 2)//Wake up and lock again!  Sellock (SEL) sg = (*sudog) (gp.param) Gp.param = nil//Wake up the Sudog of the current G is a saved list match for i: = Int (sglist) before the SG traversal ASE)-1; I >= 0; i--{k = &scases[pollorder[i]] if sg = = sglist {cas = k} else {//if not matched            Retract the current G in this Chan queue c = k.c if K.kind = = casesend {C.sendq.dequeuesudog (sglist) } else {C.recvq.dequeuesudog (sglist)}} sgnext = Sglist.waitlink rele Asesudog (sglist) sglist = sgnext} selunlock (sel) goto Retcretc:return cas.pc, cas.so}

Reference articles

Select in Go Runtime

Go1.5 Source Code Analysis

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.