]> Cypherpunks repositories - gostls13.git/commitdiff
runtime: run deferred calls at Goexit
authorRuss Cox <rsc@golang.org>
Tue, 30 Mar 2010 04:48:22 +0000 (21:48 -0700)
committerRuss Cox <rsc@golang.org>
Tue, 30 Mar 2010 04:48:22 +0000 (21:48 -0700)
baby step toward panic+recover.

Fixes #349.

R=r
CC=golang-dev
https://golang.org/cl/825043

src/pkg/runtime/386/asm.s
src/pkg/runtime/amd64/asm.s
src/pkg/runtime/arm/asm.s
src/pkg/runtime/extern.go
src/pkg/runtime/proc.c
src/pkg/runtime/runtime.h

index 0e49b151a27d823354b73a92030f5f5127cf4328..c6c8b4a85bebdbc868863c0c8278d54378cee1ed 100644 (file)
@@ -199,11 +199,11 @@ TEXT reflect·call(SB), 7, $0
        MOVL    AX, (m_morebuf+gobuf_g)(BX)
 
        // Set up morestack arguments to call f on a new stack.
-       // We set f's frame size to zero, meaning
-       // allocate a standard sized stack segment.
-       // If it turns out that f needs a larger frame than this,
-       // f's usual stack growth prolog will allocate
-       // a new segment (and recopy the arguments).
+       // We set f's frame size to 1, as a hint to newstack
+       // that this is a call from reflect·call.
+       // If it turns out that f needs a larger frame than
+       // the default stack, f's usual stack growth prolog will
+       // allocate a new segment (and recopy the arguments).
        MOVL    4(SP), AX       // fn
        MOVL    8(SP), DX       // arg frame
        MOVL    12(SP), CX      // arg size
@@ -211,7 +211,7 @@ TEXT reflect·call(SB), 7, $0
        MOVL    AX, m_morepc(BX)        // f's PC
        MOVL    DX, m_morefp(BX)        // argument frame pointer
        MOVL    CX, m_moreargs(BX)      // f's argument size
-       MOVL    $0, m_moreframe(BX)     // f's frame size
+       MOVL    $1, m_moreframe(BX)     // f's frame size
 
        // Call newstack on m's scheduling stack.
        MOVL    m_g0(BX), BP
index a7d1c9711e963641f85f6b2e815f8dfc74a4286a..c8466318c1181b5a28057f6626b569bda7ad6efa 100644 (file)
@@ -143,11 +143,11 @@ TEXT reflect·call(SB), 7, $0
        MOVQ    g, (m_morebuf+gobuf_g)(m)
 
        // Set up morestack arguments to call f on a new stack.
-       // We set f's frame size to zero, meaning
-       // allocate a standard sized stack segment.
-       // If it turns out that f needs a larger frame than this,
-       // f's usual stack growth prolog will allocate
-       // a new segment (and recopy the arguments).
+       // We set f's frame size to 1, as a hint to newstack
+       // that this is a call from reflect·call.
+       // If it turns out that f needs a larger frame than
+       // the default stack, f's usual stack growth prolog will
+       // allocate a new segment (and recopy the arguments).
        MOVQ    8(SP), AX       // fn
        MOVQ    16(SP), BX      // arg frame
        MOVL    24(SP), CX      // arg size
@@ -155,7 +155,7 @@ TEXT reflect·call(SB), 7, $0
        MOVQ    AX, m_morepc(m) // f's PC
        MOVQ    BX, m_morefp(m) // argument frame pointer
        MOVL    CX, m_moreargs(m)       // f's argument size
-       MOVL    $0, m_moreframe(m)      // f's frame size
+       MOVL    $1, m_moreframe(m)      // f's frame size
 
        // Call newstack on m's scheduling stack.
        MOVQ    m_g0(m), g
index 31765d2144b8baecc1e131b6c14b9113764edd76..19fa1cc2e3343b98fc34a92b32a09a1d8d3ffb99 100644 (file)
@@ -175,11 +175,11 @@ TEXT reflect·call(SB), 7, $-4
        MOVW    g,  (m_morebuf+gobuf_g)(m)
 
        // Set up morestack arguments to call f on a new stack.
-       // We set f's frame size to zero, meaning
-       // allocate a standard sized stack segment.
-       // If it turns out that f needs a larger frame than this,
-       // f's usual stack growth prolog will allocate
-       // a new segment (and recopy the arguments).
+       // We set f's frame size to 1, as a hint to newstack
+       // that this is a call from reflect·call.
+       // If it turns out that f needs a larger frame than
+       // the default stack, f's usual stack growth prolog will
+       // allocate a new segment (and recopy the arguments).
        MOVW    4(SP), R0       // fn
        MOVW    8(SP), R1       // arg frame
        MOVW    12(SP), R2      // arg size
@@ -187,7 +187,7 @@ TEXT reflect·call(SB), 7, $-4
        MOVW    R0, m_morepc(m) // f's PC
        MOVW    R1, m_morefp(m) // argument frame pointer
        MOVW    R2, m_moreargs(m)       // f's argument size
-       MOVW    $0, R3
+       MOVW    $1, R3
        MOVW    R3, m_moreframe(m)      // f's frame size
 
        // Call newstack on m's scheduling stack.
index 6d98e50db423eaae8517bb920dae62179595a500..1e284e8d711dd751d804b75537240df4f2117a17 100644 (file)
@@ -15,6 +15,7 @@ package runtime
 func Gosched()
 
 // Goexit terminates the goroutine that calls it.  No other goroutine is affected.
+// Goexit runs all deferred calls before terminating the goroutine.
 func Goexit()
 
 // Breakpoint() executes a breakpoint trap.
index cc48b61debbede9fa47639965d46cf7503161bdc..3ef6ae8efe762179f36d16d5dabab2e92518f384 100644 (file)
@@ -7,6 +7,8 @@
 #include "malloc.h"
 #include "os.h"
 
+static void unwindstack(G*, byte*);
+
 typedef struct Sched Sched;
 
 M      m0;
@@ -223,26 +225,6 @@ mget(G *g)
        return m;
 }
 
-// Put on gfree list.  Sched must be locked.
-static void
-gfput(G *g)
-{
-       g->schedlink = sched.gfree;
-       sched.gfree = g;
-}
-
-// Get from gfree list.  Sched must be locked.
-static G*
-gfget(void)
-{
-       G *g;
-
-       g = sched.gfree;
-       if(g)
-               sched.gfree = g->schedlink;
-       return g;
-}
-
 // Mark g ready to run.
 void
 ready(G *g)
@@ -494,6 +476,7 @@ scheduler(void)
                                gp->lockedm = nil;
                                m->lockedg = nil;
                        }
+                       unwindstack(gp, nil);
                        gfput(gp);
                        if(--sched.gcount == 0)
                                exit(0);
@@ -684,7 +667,8 @@ oldstack(void)
        }
        goid = old.gobuf.g->goid;       // fault if g is bad, before gogo
 
-       stackfree(g1->stackguard - StackGuard);
+       if(old.free)
+               stackfree(g1->stackguard - StackGuard);
        g1->stackbase = old.stackbase;
        g1->stackguard = old.stackguard;
 
@@ -699,29 +683,42 @@ newstack(void)
        byte *stk, *sp;
        G *g1;
        Gobuf label;
+       bool free;
 
        frame = m->moreframe;
        args = m->moreargs;
-
-       // Round up to align things nicely.
-       // This is sufficient for both 32- and 64-bit machines.
-       args = (args+7) & ~7;
-
-       if(frame < StackBig)
-               frame = StackBig;
-       frame += 1024;  // for more functions, Stktop.
-       stk = stackalloc(frame);
-
+       g1 = m->curg;
+       
+       if(frame == 1 && args > 0 && m->morebuf.sp - sizeof(Stktop) - args - 32 > g1->stackguard) {
+               // special case: called from reflect.call (frame == 1)
+               // to call code with an arbitrary argument size,
+               // and we have enough space on the current stack.
+               // the new Stktop* is necessary to unwind, but
+               // we don't need to create a new segment.
+               top = (Stktop*)(m->morebuf.sp - sizeof(*top));
+               stk = g1->stackguard - StackGuard;
+               free = false;
+       } else {
+               // allocate new segment.
+               if(frame == 1)  // failed reflect.call hint
+                       frame = 0;
+               frame += args;
+               if(frame < StackBig)
+                       frame = StackBig;
+               frame += 1024;  // room for more functions, Stktop.
+               stk = stackalloc(frame);
+               top = (Stktop*)(stk+frame-sizeof(*top));
+               free = true;
+       }
 
 //printf("newstack frame=%d args=%d morepc=%p morefp=%p gobuf=%p, %p newstk=%p\n", frame, args, m->morepc, m->morefp, g->sched.pc, g->sched.sp, stk);
 
-       g1 = m->curg;
-       top = (Stktop*)(stk+frame-sizeof(*top));
        top->stackbase = g1->stackbase;
        top->stackguard = g1->stackguard;
        top->gobuf = m->morebuf;
        top->fp = m->morefp;
        top->args = args;
+       top->free = free;
 
        g1->stackbase = (byte*)top;
        g1->stackguard = stk + StackGuard;
@@ -792,6 +789,8 @@ newproc1(byte *fn, byte *argp, int32 narg, int32 nret)
 
        if((newg = gfget()) != nil){
                newg->status = Gwaiting;
+               if(newg->stackguard - StackGuard != newg->stack0)
+                       throw("invalid stack in newg");
        } else {
                newg = malg(4096);
                newg->status = Gwaiting;
@@ -853,7 +852,63 @@ void
        fn = d->fn;
        free(d);
        jmpdefer(fn, sp);
-  }
+}
+
+static void
+rundefer(void)
+{      
+       Defer *d;
+       
+       while((d = g->defer) != nil) {
+               g->defer = d->link;
+               reflect·call(d->fn, d->args, d->siz);
+               free(d);
+       }
+}
+
+// Free stack frames until we hit the last one
+// or until we find the one that contains the sp.
+static void
+unwindstack(G *gp, byte *sp)
+{
+       Stktop *top;
+       byte *stk;
+       
+       // Must be called from a different goroutine, usually m->g0.
+       if(g == gp)
+               throw("unwindstack on self");
+
+       while((top = (Stktop*)gp->stackbase) != nil && top->stackbase != nil) {
+               stk = gp->stackguard - StackGuard;
+               if(stk <= sp && sp < gp->stackbase)
+                       break;
+               gp->stackbase = top->stackbase;
+               gp->stackguard = top->stackguard;
+               free(stk);
+       }
+}
+
+// Put on gfree list.  Sched must be locked.
+static void
+gfput(G *g)
+{
+       if(g->stackguard - StackGuard != g->stack0)
+               throw("invalid stack in gfput");
+       g->schedlink = sched.gfree;
+       sched.gfree = g;
+}
+
+// Get from gfree list.  Sched must be locked.
+static G*
+gfget(void)
+{
+       G *g;
+
+       g = sched.gfree;
+       if(g)
+               sched.gfree = g->schedlink;
+       return g;
+}
 
 void
 ·Breakpoint(void)
@@ -864,6 +919,7 @@ void
 void
 ·Goexit(void)
 {
+       rundefer();
        goexit();
 }
 
index d20d5b9362edf086104ec53e1cd5e29f0f1ebc81..2671a0592490ef1a5f97135048a9fa8616c4ab63 100644 (file)
@@ -239,6 +239,7 @@ struct      Stktop
        // fp == gobuf.sp except in the case of a reflected
        // function call, which uses an off-stack argument frame.
        uint8*  fp;
+       bool    free;   // call stackfree for this frame?
 };
 struct Alg
 {