mirror of
https://github.com/golang/go
synced 2024-11-22 02:44:39 -07:00
runtime: run deferred calls at Goexit
baby step toward panic+recover. Fixes #349. R=r CC=golang-dev https://golang.org/cl/825043
This commit is contained in:
parent
bc32dd9ec4
commit
83727ccf7c
@ -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
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
g1 = m->curg;
|
||||
|
||||
// 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);
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user