view libgo/runtime/proc.c @ 158:494b0b89df80 default tip

...
author Shinji KONO <kono@ie.u-ryukyu.ac.jp>
date Mon, 25 May 2020 18:13:55 +0900
parents 1830386684a0
children
line wrap: on
line source

// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

#include <errno.h>
#include <limits.h>
#include <signal.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>

#include "config.h"

#ifdef HAVE_DL_ITERATE_PHDR
#include <link.h>
#endif

#include "runtime.h"
#include "arch.h"
#include "defs.h"

#ifdef USING_SPLIT_STACK

/* FIXME: These are not declared anywhere.  */

extern void __splitstack_getcontext(void *context[10]);

extern void __splitstack_setcontext(void *context[10]);

extern void *__splitstack_makecontext(size_t, void *context[10], size_t *);

extern void * __splitstack_resetcontext(void *context[10], size_t *);

extern void __splitstack_releasecontext(void *context[10]);

extern void *__splitstack_find(void *, void *, size_t *, void **, void **,
			       void **);

extern void __splitstack_block_signals (int *, int *);

extern void __splitstack_block_signals_context (void *context[10], int *,
						int *);

#endif

#ifndef PTHREAD_STACK_MIN
# define PTHREAD_STACK_MIN 8192
#endif

#if defined(USING_SPLIT_STACK) && defined(LINKER_SUPPORTS_SPLIT_STACK)
# define StackMin PTHREAD_STACK_MIN
#else
# define StackMin ((sizeof(char *) < 8) ? 2 * 1024 * 1024 : 4 * 1024 * 1024)
#endif

uintptr runtime_stacks_sys;

void gtraceback(G*)
  __asm__(GOSYM_PREFIX "runtime.gtraceback");

static void gscanstack(G*);

#ifdef __rtems__
#define __thread
#endif

__thread G *g __asm__(GOSYM_PREFIX "runtime.g");

#ifndef SETCONTEXT_CLOBBERS_TLS

static inline void
initcontext(void)
{
}

static inline void
fixcontext(__go_context_t *c __attribute__ ((unused)))
{
}

#else

# if defined(__x86_64__) && defined(__sun__)

// x86_64 Solaris 10 and 11 have a bug: setcontext switches the %fs
// register to that of the thread which called getcontext.  The effect
// is that the address of all __thread variables changes.  This bug
// also affects pthread_self() and pthread_getspecific.  We work
// around it by clobbering the context field directly to keep %fs the
// same.

static __thread greg_t fs;

static inline void
initcontext(void)
{
	ucontext_t c;

	getcontext(&c);
	fs = c.uc_mcontext.gregs[REG_FSBASE];
}

static inline void
fixcontext(ucontext_t* c)
{
	c->uc_mcontext.gregs[REG_FSBASE] = fs;
}

# elif defined(__NetBSD__)

// NetBSD has a bug: setcontext clobbers tlsbase, we need to save
// and restore it ourselves.

static __thread __greg_t tlsbase;

static inline void
initcontext(void)
{
	ucontext_t c;

	getcontext(&c);
	tlsbase = c.uc_mcontext._mc_tlsbase;
}

static inline void
fixcontext(ucontext_t* c)
{
	c->uc_mcontext._mc_tlsbase = tlsbase;
}

# elif defined(__sparc__)

static inline void
initcontext(void)
{
}

static inline void
fixcontext(ucontext_t *c)
{
	/* ??? Using 
	     register unsigned long thread __asm__("%g7");
	     c->uc_mcontext.gregs[REG_G7] = thread;
	   results in
	     error: variable ‘thread’ might be clobbered by \
		‘longjmp’ or ‘vfork’ [-Werror=clobbered]
	   which ought to be false, as %g7 is a fixed register.  */

	if (sizeof (c->uc_mcontext.gregs[REG_G7]) == 8)
		asm ("stx %%g7, %0" : "=m"(c->uc_mcontext.gregs[REG_G7]));
	else
		asm ("st %%g7, %0" : "=m"(c->uc_mcontext.gregs[REG_G7]));
}

# elif defined(_AIX)

static inline void
initcontext(void)
{
}

static inline void
fixcontext(ucontext_t* c)
{
	// Thread pointer is in r13, per 64-bit ABI.
	if (sizeof (c->uc_mcontext.jmp_context.gpr[13]) == 8)
		asm ("std 13, %0" : "=m"(c->uc_mcontext.jmp_context.gpr[13]));
}

# else

#  error unknown case for SETCONTEXT_CLOBBERS_TLS

# endif

#endif

// ucontext_arg returns a properly aligned ucontext_t value.  On some
// systems a ucontext_t value must be aligned to a 16-byte boundary.
// The g structure that has fields of type ucontext_t is defined in
// Go, and Go has no simple way to align a field to such a boundary.
// So we make the field larger in runtime2.go and pick an appropriate
// offset within the field here.
static __go_context_t*
ucontext_arg(uintptr_t* go_ucontext)
{
	uintptr_t p = (uintptr_t)go_ucontext;
	size_t align = __alignof__(__go_context_t);
	if(align > 16) {
		// We only ensured space for up to a 16 byte alignment
		// in libgo/go/runtime/runtime2.go.
		runtime_throw("required alignment of __go_context_t too large");
	}
	p = (p + align - 1) &~ (uintptr_t)(align - 1);
	return (__go_context_t*)p;
}

// We can not always refer to the TLS variables directly.  The
// compiler will call tls_get_addr to get the address of the variable,
// and it may hold it in a register across a call to schedule.  When
// we get back from the call we may be running in a different thread,
// in which case the register now points to the TLS variable for a
// different thread.  We use non-inlinable functions to avoid this
// when necessary.

G* runtime_g(void) __attribute__ ((noinline, no_split_stack));

G*
runtime_g(void)
{
	return g;
}

M* runtime_m(void) __attribute__ ((noinline, no_split_stack));

M*
runtime_m(void)
{
	if(g == nil)
		return nil;
	return g->m;
}

// Set g.
void
runtime_setg(G* gp)
{
	g = gp;
}

void runtime_newosproc(M *)
  __asm__(GOSYM_PREFIX "runtime.newosproc");

// Start a new thread.
void
runtime_newosproc(M *mp)
{
	pthread_attr_t attr;
	sigset_t clear, old;
	pthread_t tid;
	int tries;
	int ret;

	if(pthread_attr_init(&attr) != 0)
		runtime_throw("pthread_attr_init");
	if(pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED) != 0)
		runtime_throw("pthread_attr_setdetachstate");

	// Block signals during pthread_create so that the new thread
	// starts with signals disabled.  It will enable them in minit.
	sigfillset(&clear);

#ifdef SIGTRAP
	// Blocking SIGTRAP reportedly breaks gdb on Alpha GNU/Linux.
	sigdelset(&clear, SIGTRAP);
#endif

	sigemptyset(&old);
	pthread_sigmask(SIG_BLOCK, &clear, &old);

	for (tries = 0; tries < 20; tries++) {
		ret = pthread_create(&tid, &attr, runtime_mstart, mp);
		if (ret != EAGAIN) {
			break;
		}
		runtime_usleep((tries + 1) * 1000); // Milliseconds.
	}

	pthread_sigmask(SIG_SETMASK, &old, nil);

	if (ret != 0) {
		runtime_printf("pthread_create failed: %d\n", ret);
		runtime_throw("pthread_create");
	}

	if(pthread_attr_destroy(&attr) != 0)
		runtime_throw("pthread_attr_destroy");
}

// Switch context to a different goroutine.  This is like longjmp.
void runtime_gogo(G*) __attribute__ ((noinline));
void
runtime_gogo(G* newg)
{
#ifdef USING_SPLIT_STACK
	__splitstack_setcontext((void*)(&newg->stackcontext[0]));
#endif
	g = newg;
	newg->fromgogo = true;
	fixcontext(ucontext_arg(&newg->context[0]));
	__go_setcontext(ucontext_arg(&newg->context[0]));
	runtime_throw("gogo setcontext returned");
}

// Save context and call fn passing g as a parameter.  This is like
// setjmp.  Because getcontext always returns 0, unlike setjmp, we use
// g->fromgogo as a code.  It will be true if we got here via
// setcontext.  g == nil the first time this is called in a new m.
void runtime_mcall(FuncVal *) __attribute__ ((noinline));
void
runtime_mcall(FuncVal *fv)
{
	M *mp;
	G *gp;
#ifndef USING_SPLIT_STACK
	void *afterregs;
#endif

	// Ensure that all registers are on the stack for the garbage
	// collector.
	__builtin_unwind_init();
	flush_registers_to_secondary_stack();

	gp = g;
	mp = gp->m;
	if(gp == mp->g0)
		runtime_throw("runtime: mcall called on m->g0 stack");

	if(gp != nil) {

#ifdef USING_SPLIT_STACK
		__splitstack_getcontext((void*)(&gp->stackcontext[0]));
#else
		// We have to point to an address on the stack that is
		// below the saved registers.
		gp->gcnextsp = (uintptr)(&afterregs);
		gp->gcnextsp2 = (uintptr)(secondary_stack_pointer());
#endif
		gp->fromgogo = false;
		__go_getcontext(ucontext_arg(&gp->context[0]));

		// When we return from getcontext, we may be running
		// in a new thread.  That means that g may have
		// changed.  It is a global variables so we will
		// reload it, but the address of g may be cached in
		// our local stack frame, and that address may be
		// wrong.  Call the function to reload the value for
		// this thread.
		gp = runtime_g();
		mp = gp->m;

		if(gp->traceback != 0)
			gtraceback(gp);
		if(gp->scang != 0)
			gscanstack(gp);
	}
	if (gp == nil || !gp->fromgogo) {
#ifdef USING_SPLIT_STACK
		__splitstack_setcontext((void*)(&mp->g0->stackcontext[0]));
#endif
		mp->g0->entry = fv;
		mp->g0->param = gp;

		// It's OK to set g directly here because this case
		// can not occur if we got here via a setcontext to
		// the getcontext call just above.
		g = mp->g0;

		fixcontext(ucontext_arg(&mp->g0->context[0]));
		__go_setcontext(ucontext_arg(&mp->g0->context[0]));
		runtime_throw("runtime: mcall function returned");
	}
}

// Goroutine scheduler
// The scheduler's job is to distribute ready-to-run goroutines over worker threads.
//
// The main concepts are:
// G - goroutine.
// M - worker thread, or machine.
// P - processor, a resource that is required to execute Go code.
//     M must have an associated P to execute Go code, however it can be
//     blocked or in a syscall w/o an associated P.
//
// Design doc at http://golang.org/s/go11sched.

extern G* allocg(void)
  __asm__ (GOSYM_PREFIX "runtime.allocg");

bool	runtime_isarchive;

extern void kickoff(void)
  __asm__(GOSYM_PREFIX "runtime.kickoff");
extern void minit(void)
  __asm__(GOSYM_PREFIX "runtime.minit");
extern void mstart1()
  __asm__(GOSYM_PREFIX "runtime.mstart1");
extern void stopm(void)
  __asm__(GOSYM_PREFIX "runtime.stopm");
extern void mexit(bool)
  __asm__(GOSYM_PREFIX "runtime.mexit");
extern void handoffp(P*)
  __asm__(GOSYM_PREFIX "runtime.handoffp");
extern void wakep(void)
  __asm__(GOSYM_PREFIX "runtime.wakep");
extern void stoplockedm(void)
  __asm__(GOSYM_PREFIX "runtime.stoplockedm");
extern void schedule(void)
  __asm__(GOSYM_PREFIX "runtime.schedule");
extern void execute(G*, bool)
  __asm__(GOSYM_PREFIX "runtime.execute");
extern void reentersyscall(uintptr, uintptr)
  __asm__(GOSYM_PREFIX "runtime.reentersyscall");
extern void reentersyscallblock(uintptr, uintptr)
  __asm__(GOSYM_PREFIX "runtime.reentersyscallblock");
extern G* gfget(P*)
  __asm__(GOSYM_PREFIX "runtime.gfget");
extern void acquirep(P*)
  __asm__(GOSYM_PREFIX "runtime.acquirep");
extern P* releasep(void)
  __asm__(GOSYM_PREFIX "runtime.releasep");
extern void incidlelocked(int32)
  __asm__(GOSYM_PREFIX "runtime.incidlelocked");
extern void globrunqput(G*)
  __asm__(GOSYM_PREFIX "runtime.globrunqput");
extern P* pidleget(void)
  __asm__(GOSYM_PREFIX "runtime.pidleget");
extern struct mstats* getMemstats(void)
  __asm__(GOSYM_PREFIX "runtime.getMemstats");

bool runtime_isstarted;

// Used to determine the field alignment.

struct field_align
{
  char c;
  Hchan *p;
};

void getTraceback(G*, G*) __asm__(GOSYM_PREFIX "runtime.getTraceback");

// getTraceback stores a traceback of gp in the g's traceback field
// and then returns to me.  We expect that gp's traceback is not nil.
// It works by saving me's current context, and checking gp's traceback field.
// If gp's traceback field is not nil, it starts running gp.
// In places where we call getcontext, we check the traceback field.
// If it is not nil, we collect a traceback, and then return to the
// goroutine stored in the traceback field, which is me.
void getTraceback(G* me, G* gp)
{
	M* holdm;

	holdm = gp->m;
	gp->m = me->m;

#ifdef USING_SPLIT_STACK
	__splitstack_getcontext((void*)(&me->stackcontext[0]));
#endif
	__go_getcontext(ucontext_arg(&me->context[0]));

	if (gp->traceback != 0) {
		runtime_gogo(gp);
	}

	gp->m = holdm;
}

// Do a stack trace of gp, and then restore the context to
// gp->traceback->gp.

void
gtraceback(G* gp)
{
	Traceback* traceback;

	traceback = (Traceback*)gp->traceback;
	gp->traceback = 0;
	traceback->c = runtime_callers(1, traceback->locbuf,
		sizeof traceback->locbuf / sizeof traceback->locbuf[0], false);
	runtime_gogo(traceback->gp);
}

void doscanstackswitch(G*, G*) __asm__(GOSYM_PREFIX "runtime.doscanstackswitch");

// Switch to gp and let it scan its stack.
// The first time gp->scang is set (to me). The second time here
// gp is done scanning, and has unset gp->scang, so we just return.
void
doscanstackswitch(G* me, G* gp)
{
	M* holdm;

	__go_assert(me->entry == nil);
	me->fromgogo = false;

	holdm = gp->m;
	gp->m = me->m;

#ifdef USING_SPLIT_STACK
	__splitstack_getcontext((void*)(&me->stackcontext[0]));
#endif
	__go_getcontext(ucontext_arg(&me->context[0]));

	if(me->entry != nil) {
		// Got here from mcall.
		// The stack scanning code may call systemstack, which calls
		// mcall, which calls setcontext.
		// Run the function, which at the end will switch back to gp.
		FuncVal *fv = me->entry;
		void (*pfn)(G*) = (void (*)(G*))fv->fn;
		G* gp1 = (G*)me->param;
		__go_assert(gp1 == gp);
		me->entry = nil;
		me->param = nil;
		__builtin_call_with_static_chain(pfn(gp1), fv);
		abort();
	}

	if (gp->scang != 0)
		runtime_gogo(gp);

	gp->m = holdm;
}

// Do a stack scan, then switch back to the g that triggers this scan.
// We come here from doscanstackswitch.
static void
gscanstack(G *gp)
{
	G *oldg, *oldcurg;

	oldg = (G*)gp->scang;
	oldcurg = oldg->m->curg;
	oldg->m->curg = gp;
	gp->scang = 0;

	doscanstack(gp, (void*)gp->scangcw);

	gp->scangcw = 0;
	oldg->m->curg = oldcurg;
	runtime_gogo(oldg);
}

// Called by pthread_create to start an M.
void*
runtime_mstart(void *arg)
{
	M* mp;
	G* gp;

	mp = (M*)(arg);
	gp = mp->g0;
	gp->m = mp;

	g = gp;

	gp->entry = nil;
	gp->param = nil;

	// We have to call minit before we call getcontext,
	// because getcontext will copy the signal mask.
	minit();

	initcontext();

	// Record top of stack for use by mcall.
	// Once we call schedule we're never coming back,
	// so other calls can reuse this stack space.
#ifdef USING_SPLIT_STACK
	__splitstack_getcontext((void*)(&gp->stackcontext[0]));
#else
	gp->gcinitialsp = &arg;
	// Setting gcstacksize to 0 is a marker meaning that gcinitialsp
	// is the top of the stack, not the bottom.
	gp->gcstacksize = 0;
	gp->gcnextsp = (uintptr)(&arg);
	gp->gcinitialsp2 = secondary_stack_pointer();
	gp->gcnextsp2 = (uintptr)(gp->gcinitialsp2);
#endif

	// Save the currently active context.  This will return
	// multiple times via the setcontext call in mcall.
	__go_getcontext(ucontext_arg(&gp->context[0]));

	if(gp->traceback != 0) {
		// Got here from getTraceback.
		// I'm not sure this ever actually happens--getTraceback
		// may always go to the getcontext call in mcall.
		gtraceback(gp);
	}
	if(gp->scang != 0)
		// Got here from doscanswitch. Should not happen.
		runtime_throw("mstart with scang");

	if(gp->entry != nil) {
		// Got here from mcall.
		FuncVal *fv = gp->entry;
		void (*pfn)(G*) = (void (*)(G*))fv->fn;
		G* gp1 = (G*)gp->param;
		gp->entry = nil;
		gp->param = nil;
		__builtin_call_with_static_chain(pfn(gp1), fv);
		*(int*)0x21 = 0x21;
	}

	if(mp->exiting) {
		mexit(true);
		return nil;
	}

	// Initial call to getcontext--starting thread.

#ifdef USING_SPLIT_STACK
	{
		int dont_block_signals = 0;
		__splitstack_block_signals(&dont_block_signals, nil);
	}
#endif

	mstart1();

	// mstart1 does not return, but we need a return statement
	// here to avoid a compiler warning.
	return nil;
}

typedef struct CgoThreadStart CgoThreadStart;
struct CgoThreadStart
{
	M *m;
	G *g;
	uintptr *tls;
	void (*fn)(void);
};

void setGContext(void) __asm__ (GOSYM_PREFIX "runtime.setGContext");

// setGContext sets up a new goroutine context for the current g.
void
setGContext(void)
{
	int val;
	G *gp;

	initcontext();
	gp = g;
	gp->entry = nil;
	gp->param = nil;
#ifdef USING_SPLIT_STACK
	__splitstack_getcontext((void*)(&gp->stackcontext[0]));
	val = 0;
	__splitstack_block_signals(&val, nil);
#else
	gp->gcinitialsp = &val;
	gp->gcstack = 0;
	gp->gcstacksize = 0;
	gp->gcnextsp = (uintptr)(&val);
	gp->gcinitialsp2 = secondary_stack_pointer();
	gp->gcnextsp2 = (uintptr)(gp->gcinitialsp2);
#endif
	__go_getcontext(ucontext_arg(&gp->context[0]));

	if(gp->entry != nil) {
		// Got here from mcall.
		FuncVal *fv = gp->entry;
		void (*pfn)(G*) = (void (*)(G*))fv->fn;
		G* gp1 = (G*)gp->param;
		gp->entry = nil;
		gp->param = nil;
		__builtin_call_with_static_chain(pfn(gp1), fv);
		*(int*)0x22 = 0x22;
	}
}

void makeGContext(G*, byte*, uintptr)
	__asm__(GOSYM_PREFIX "runtime.makeGContext");

// makeGContext makes a new context for a g.
void
makeGContext(G* gp, byte* sp, uintptr spsize) {
	__go_context_t *uc;

	uc = ucontext_arg(&gp->context[0]);
	__go_getcontext(uc);
	__go_makecontext(uc, kickoff, sp, (size_t)spsize);
}

// The goroutine g is about to enter a system call.
// Record that it's not using the cpu anymore.
// This is called only from the go syscall library and cgocall,
// not from the low-level system calls used by the runtime.
//
// Entersyscall cannot split the stack: the runtime_gosave must
// make g->sched refer to the caller's stack segment, because
// entersyscall is going to return immediately after.

void runtime_entersyscall() __attribute__ ((no_split_stack));
static void doentersyscall(uintptr, uintptr)
  __attribute__ ((no_split_stack, noinline));

void
runtime_entersyscall()
{
	// Save the registers in the g structure so that any pointers
	// held in registers will be seen by the garbage collector.
	if (!runtime_usestackmaps)
		__go_getcontext(ucontext_arg(&g->gcregs[0]));

	// Note that if this function does save any registers itself,
	// we might store the wrong value in the call to getcontext.
	// FIXME: This assumes that we do not need to save any
	// callee-saved registers to access the TLS variable g.  We
	// don't want to put the ucontext_t on the stack because it is
	// large and we can not split the stack here.
	doentersyscall((uintptr)runtime_getcallerpc(),
		       (uintptr)runtime_getcallersp());
}

static void
doentersyscall(uintptr pc, uintptr sp)
{
	// Leave SP around for GC and traceback.
#ifdef USING_SPLIT_STACK
	{
	  size_t gcstacksize;
	  g->gcstack = (uintptr)(__splitstack_find(nil, nil, &gcstacksize,
						   (void**)(&g->gcnextsegment),
						   (void**)(&g->gcnextsp),
						   &g->gcinitialsp));
	  g->gcstacksize = (uintptr)gcstacksize;
	}
#else
	{
		void *v;

		g->gcnextsp = (uintptr)(&v);
		g->gcnextsp2 = (uintptr)(secondary_stack_pointer());
	}
#endif

	reentersyscall(pc, sp);
}

static void doentersyscallblock(uintptr, uintptr)
  __attribute__ ((no_split_stack, noinline));

// The same as runtime_entersyscall(), but with a hint that the syscall is blocking.
void
runtime_entersyscallblock()
{
	// Save the registers in the g structure so that any pointers
	// held in registers will be seen by the garbage collector.
	if (!runtime_usestackmaps)
		__go_getcontext(ucontext_arg(&g->gcregs[0]));

	// See comment in runtime_entersyscall.
	doentersyscallblock((uintptr)runtime_getcallerpc(),
			    (uintptr)runtime_getcallersp());
}

static void
doentersyscallblock(uintptr pc, uintptr sp)
{
	// Leave SP around for GC and traceback.
#ifdef USING_SPLIT_STACK
	{
	  size_t gcstacksize;
	  g->gcstack = (uintptr)(__splitstack_find(nil, nil, &gcstacksize,
						   (void**)(&g->gcnextsegment),
						   (void**)(&g->gcnextsp),
						   &g->gcinitialsp));
	  g->gcstacksize = (uintptr)gcstacksize;
	}
#else
	{
		void *v;

		g->gcnextsp = (uintptr)(&v);
		g->gcnextsp2 = (uintptr)(secondary_stack_pointer());
	}
#endif

	reentersyscallblock(pc, sp);
}

// Allocate a new g, with a stack big enough for stacksize bytes.
G*
runtime_malg(bool allocatestack, bool signalstack, byte** ret_stack, uintptr* ret_stacksize)
{
	uintptr stacksize;
	G *newg;
	byte* unused_stack;
	uintptr unused_stacksize;
#ifdef USING_SPLIT_STACK
	int dont_block_signals = 0;
	size_t ss_stacksize;
#endif

	if (ret_stack == nil) {
		ret_stack = &unused_stack;
	}
	if (ret_stacksize == nil) {
		ret_stacksize = &unused_stacksize;
	}
	newg = allocg();
	if(allocatestack) {
		stacksize = StackMin;
		if(signalstack) {
			stacksize = 32 * 1024; // OS X wants >= 8K, GNU/Linux >= 2K
#ifdef SIGSTKSZ
			if(stacksize < SIGSTKSZ)
				stacksize = SIGSTKSZ;
#endif
		}

#ifdef USING_SPLIT_STACK
		*ret_stack = __splitstack_makecontext(stacksize,
						      (void*)(&newg->stackcontext[0]),
						      &ss_stacksize);
		*ret_stacksize = (uintptr)ss_stacksize;
		__splitstack_block_signals_context((void*)(&newg->stackcontext[0]),
						   &dont_block_signals, nil);
#else
                // In 64-bit mode, the maximum Go allocation space is
                // 128G.  Our stack size is 4M, which only permits 32K
                // goroutines.  In order to not limit ourselves,
                // allocate the stacks out of separate memory.  In
                // 32-bit mode, the Go allocation space is all of
                // memory anyhow.
		if(sizeof(void*) == 8) {
			void *p = runtime_sysAlloc(stacksize, &getMemstats()->stacks_sys);
			if(p == nil)
				runtime_throw("runtime: cannot allocate memory for goroutine stack");
			*ret_stack = (byte*)p;
		} else {
			*ret_stack = runtime_mallocgc(stacksize, nil, false);
			runtime_xadd(&runtime_stacks_sys, stacksize);
		}
		*ret_stacksize = (uintptr)stacksize;
		newg->gcinitialsp = *ret_stack;
		newg->gcstacksize = (uintptr)stacksize;
		newg->gcinitialsp2 = initial_secondary_stack_pointer(*ret_stack);
#endif
	}
	return newg;
}

void stackfree(G*)
  __asm__(GOSYM_PREFIX "runtime.stackfree");

// stackfree frees the stack of a g.
void
stackfree(G* gp)
{
#ifdef USING_SPLIT_STACK
  __splitstack_releasecontext((void*)(&gp->stackcontext[0]));
#else
  // If gcstacksize is 0, the stack is allocated by libc and will be
  // released when the thread exits. Otherwise, in 64-bit mode it was
  // allocated using sysAlloc and in 32-bit mode it was allocated
  // using garbage collected memory.
  if (gp->gcstacksize != 0) {
    if (sizeof(void*) == 8) {
      runtime_sysFree(gp->gcinitialsp, gp->gcstacksize, &getMemstats()->stacks_sys);
    }
    gp->gcinitialsp = nil;
    gp->gcstacksize = 0;
  }
#endif
}

void resetNewG(G*, void **, uintptr*)
  __asm__(GOSYM_PREFIX "runtime.resetNewG");

// Reset stack information for g pulled out of the cache to start a
// new goroutine.
void
resetNewG(G *newg, void **sp, uintptr *spsize)
{
#ifdef USING_SPLIT_STACK
  int dont_block_signals = 0;
  size_t ss_spsize;

  *sp = __splitstack_resetcontext((void*)(&newg->stackcontext[0]), &ss_spsize);
  *spsize = ss_spsize;
  __splitstack_block_signals_context((void*)(&newg->stackcontext[0]),
				     &dont_block_signals, nil);
#else
  *sp = newg->gcinitialsp;
  *spsize = newg->gcstacksize;
  if(*spsize == 0)
    runtime_throw("bad spsize in resetNewG");
  newg->gcnextsp = (uintptr)(*sp);
  newg->gcnextsp2 = (uintptr)(newg->gcinitialsp2);
#endif
}