Timing of the scheduling of co-operation: preemptive scheduling

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

Description

Although we have always emphasized that the Golang Scheduler is non-preemptive. One of the biggest drawbacks of non-preemption is the inability to guarantee fairness, and if a G is in a dead loop, other processes may starve. Fortunately, Golang in the 1.4 version of the preemptive scheduling logic, preemptive scheduling is bound to be at some point in g execution is stripped of the CPU, to other co-process.

Realize

Remember what we said earlier about Golang's Sysmon, which periodically wakes up as a system state check, we said earlier how it checks p in the Psyscall state so that p in the system call state can be continued without starvation. In addition to checking the accident, Sysmon also checks p in the prunning state, checking it to avoid a certain g here taking up too much CPU time, and at some point depriving it of its CPU run time.

Static UInt32 Retake (Int64 now) {UInt32 I, S, N;    Int64 T;    P *p;    Pdesc *PD;    n = 0;        for (i = 0; i < runtime gomaxprocs; i++) {p = runtime allp[i];        if (P==nil) continue;        PD = &pdesc[i];        s = p->status; if (s = = Psyscall) {...} else if (s = = prunning) {//Preempt G If it ' s running for more T             Han 10ms.            t = p->schedtick;                if (Pd->schedtick! = t) {Pd->schedtick = t;                Pd->schedwhen = Now;            Continue            } if (Pd->schedwhen + 10*1000*1000 > Now) continue;       If the schedule has exceeded 10ms Preemptone (p) Since the last time it occurred; }} return n;}    The preemption here just sets the G's preempt to true//only checks the flag bit when G makes a function call//And, in turn, may dispatch, very weak static bool Preemptone (P *p) {M *mp;    G *GP;    MP = p->m;    if (MP = = Nil | | mp = = G-&GT;M) return false;    GP = mp->curg; if (gp = = Nil | | gp = = MP-&GT;G0) return false;    Gp->preempt = true; Every call in a go routine checks for stacks overflow by//comparing the current stack pointer to Gp->stackguard     0.//Setting gp->stackguard0 to stackpreempt folds//preemption into the normal stack overflow check.    gp->stackguard0 = stackpreempt; return true;}

As we said before in the function call stack detection, now set GP->STACKGUARD0 to Stackpreempt (-1314, very small value), will definitely call once Runtime.morestack, the logic is as follows:

TEXT runtime Morestack (SB), nosplit,$0-0//Cannot grow scheduler stack (M-&GT;G0). GET_TLS (CX) movq g (CX), BX movq g_m (BX), BX movq m_g0 (BX), Si cmpq g (CX), Si JNE 2 (PC) I    NT $//Cannot grow signal stack (m->gsignal).    Movq m_gsignal (BX), Si cmpq g (CX), Si JNE 2 (PC) INT $ $//called from F.    Set m->morebuf to F ' s caller.    MOVQ 8 (SP), Ax//F ' s caller ' s PC movq AX, (M_MOREBUF+GOBUF_PC) (BX) Leaq (SP), Ax//F's caller ' s SP Movq AX, (M_MOREBUF+GOBUF_SP) (BX) GET_TLS (CX) movq g (CX), Si movq si, (m_morebuf+gobuf_g) (BX)//Se    T g->sched to context in F. MOVQ 0 (SP), Ax//F ' s PC movq ax, (g_sched+gobuf_pc) (SI) movq SI, (g_sched+gobuf_g) (SI) Leaq 8 (SP), Ax//F    ' s SP movq AX, (g_sched+gobuf_sp) (SI) movq DX, (g_sched+gobuf_ctxt) (SI) movq BP, (G_SCHED+GOBUF_BP) (SI)    Call Newstack on M->g0 ' s stack. MOVQ m_g0 (BX), BX MOVQ BX, G (CX) Movq (G_SCHED+GOBUF_SP) (BX), SP call Runtime Newstack (SB) movq $, 0x1003//crash if Newstack  Returns RET

Final call to Newstack for stack expansion:

Func Newstack () {thisg: = GETG ()//todo:double Check all GP. Shouldn ' t is GETG (). If Thisg.m.morebuf.g.ptr (). stackguard0 = = stackfork {throw ("stack growth after fork")} if THISG.M.MOREBUF.G . PTR ()! = Thisg.m.curg {print ("runtime:newstack called from g=", THISG.M.MOREBUF.G, "\ n" + "\tm=", THISG.M, "M-&gt ; curg= ", Thisg.m.curg," m->g0= ", Thisg.m.g0," m->gsignal= ", thisg.m.gsignal," \ n ") Morebuf: = Thisg.m.moreb UF traceback (morebuf.pc, Morebuf.sp, MOREBUF.LR, Morebuf.g.ptr ()) throw ("Runtime:wrong goroutine in Newsta CK ")} GP: = Thisg.m.curg morebuf: = Thisg.m.morebuf thisg.m.morebuf.pc = 0 thisg.m.morebuf.lr = 0 thi SG.M.MOREBUF.SP = 0 thisg.m.morebuf.g = 0 rewindmorestack (&gp.sched)//note:stackguard0 Change Underfoo T, if another thread//is the about-to-try to preempt GP.    Read it just once and use this same//value now and below. Preempt: = Atomicloaduintptr (&gp.stackguard0)= = Stackpreempt If preempt {if thisg.m.locks! = 0 | | thisg.m.mallocing! = 0 | | Thisg.m.preemptoff! = "" | |            Thisg.m.p.ptr (). Status! = _prunning {//Let the Goroutine keep running for now.            Gp->preempt is set and so it'll be preempted next time. gp.stackguard0 = Gp.stack.lo + _stackguard gogo (&gp.sched)//Never return}} ...// Rescheduling if preempt {if GP = = thisg.m.g0 {throw ("Runtime:preempt G0")} if THISG.M.P = =  0 && Thisg.m.locks = = 0 {throw ("Runtime:g is running but P was not")} if Gp.preemptscan                {for!castogscanstatus (GP, _gwaiting, _gscanwaiting) {//likely to is racing with the GC as It sees a _gwaiting and does the//stack scan.            If So, Gcworkdone'll/be set and gcphasework'll simply//return. } If!gp.gcscanDone {scanstack (gp) Gp.gcscandone = true} Gp.preemptscan = False Gp.preempt = False Casfrom_gscanstatus (GP, _gscanwaiting, _gwaiting) casgstatus (GP, _gwaitin        G, _grunning) gp.stackguard0 = Gp.stack.lo + _stackguard gogo (&gp.sched)//Never return }//Act like goroutine called Runtime.        Gosched. Casgstatus (GP, _gwaiting, _grunning)//Discard current coprocessor, schedule new coprocessor Execution Gopreempt_m (GP)//Never return}}

Here are two things to note:

    • THISG: = GETG (): This is the stack that represents the current execution of the Newstack () function, and also the G0 of the current thread;
    • GP: = Thisg.m.curg: This represents the application stack expansion process, and the above THISG is not a thing.

Because although the call to Newstack, but for the stackguard0==stackpreempt, it is not the purpose of the stack expansion, but to initiate a dispatch, so directly into the gopreempt_m, where the current process hangs, and initiate a schedule ().

Related Article

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.