Patchwork libgo patch committed: Update to weekly.2012-02-22 release

login
register
mail settings
Submitter Ian Taylor
Date March 2, 2012, 8:01 p.m.
Message ID <mcr1upaixga.fsf@dhcp-172-18-216-180.mtv.corp.google.com>
Download mbox | patch
Permalink /patch/144330/
State New
Headers show

Comments

Ian Taylor - March 2, 2012, 8:01 p.m.
I have committed a patch to update libgo to the weekly.2012-02-22
release.  As usual this e-mail message only includes the changes to
gccgo-specific files.  Bootstrapped and ran Go testsuite on
x86_64-unknown-linux-gnu.  Committed to mainline.

Ian

Patch

diff -r a2b8eab75cae libgo/MERGE
--- a/libgo/MERGE	Fri Mar 02 08:23:02 2012 -0800
+++ b/libgo/MERGE	Fri Mar 02 10:14:12 2012 -0800
@@ -1,4 +1,4 @@ 
-43cf9b39b647
+96bd78e7d35e
 
 The first line of this file holds the Mercurial revision number of the
 last merge done from the master library sources.
diff -r a2b8eab75cae libgo/Makefile.am
--- a/libgo/Makefile.am	Fri Mar 02 08:23:02 2012 -0800
+++ b/libgo/Makefile.am	Fri Mar 02 10:14:12 2012 -0800
@@ -504,7 +504,7 @@ 
 	mv -f $@.tmp $@
 
 sema.c: $(srcdir)/runtime/sema.goc goc2c
-	./goc2c --gcc --go-prefix libgo_runtime $< > $@.tmp
+	./goc2c --gcc --go-prefix libgo_sync $< > $@.tmp
 	mv -f $@.tmp $@
 
 sigqueue.c: $(srcdir)/runtime/sigqueue.goc goc2c
@@ -847,6 +847,7 @@ 
 	go/sync/cond.go \
 	go/sync/mutex.go \
 	go/sync/once.go \
+	go/sync/runtime.go \
 	go/sync/rwmutex.go \
 	go/sync/waitgroup.go
 
@@ -878,6 +879,7 @@ 
 	go/time/tick.go \
 	go/time/time.go \
 	go/time/zoneinfo.go \
+	go/time/zoneinfo_read.go \
 	go/time/zoneinfo_unix.go
 
 go_unicode_files = \
@@ -1091,6 +1093,7 @@ 
 	go/exp/norm/composition.go \
 	go/exp/norm/forminfo.go \
 	go/exp/norm/input.go \
+	go/exp/norm/iter.go \
 	go/exp/norm/normalize.go \
 	go/exp/norm/readwriter.go \
 	go/exp/norm/tables.go \
@@ -1132,7 +1135,8 @@ 
 	go/go/doc/example.go \
 	go/go/doc/exports.go \
 	go/go/doc/filter.go \
-	go/go/doc/reader.go
+	go/go/doc/reader.go \
+	go/go/doc/synopsis.go
 go_go_parser_files = \
 	go/go/parser/interface.go \
 	go/go/parser/parser.go
@@ -1159,7 +1163,6 @@ 
 
 go_html_template_files = \
 	go/html/template/attr.go \
-	go/html/template/clone.go \
 	go/html/template/content.go \
 	go/html/template/context.go \
 	go/html/template/css.go \
diff -r a2b8eab75cae libgo/runtime/malloc.goc
--- a/libgo/runtime/malloc.goc	Fri Mar 02 08:23:02 2012 -0800
+++ b/libgo/runtime/malloc.goc	Fri Mar 02 10:14:12 2012 -0800
@@ -19,6 +19,7 @@ 
 #include "go-type.h"
 
 MHeap runtime_mheap;
+
 extern MStats mstats;	// defined in extern.go
 
 extern volatile int32 runtime_MemProfileRate
@@ -429,18 +430,6 @@ 
 	ret = runtime_mallocgc(typ->__size, flag, 1, 1);
 }
 
-func Alloc(n uintptr) (p *byte) {
-	p = runtime_malloc(n);
-}
-
-func Free(p *byte) {
-	runtime_free(p);
-}
-
-func Lookup(p *byte) (base *byte, size uintptr) {
-	runtime_mlookup(p, &base, &size, nil);
-}
-
 func GC() {
 	runtime_gc(1);
 }
diff -r a2b8eab75cae libgo/runtime/malloc.h
--- a/libgo/runtime/malloc.h	Fri Mar 02 08:23:02 2012 -0800
+++ b/libgo/runtime/malloc.h	Fri Mar 02 10:14:12 2012 -0800
@@ -205,6 +205,7 @@ 
 	uint64	heap_sys;	// bytes obtained from system
 	uint64	heap_idle;	// bytes in idle spans
 	uint64	heap_inuse;	// bytes in non-idle spans
+	uint64	heap_released;	// bytes released to the OS
 	uint64	heap_objects;	// total number of allocated objects
 
 	// Statistics about allocation of low-level fixed-size structures.
@@ -220,6 +221,7 @@ 
 	// Statistics about garbage collector.
 	// Protected by stopping the world during GC.
 	uint64	next_gc;	// next GC (in heap_alloc time)
+	uint64  last_gc;	// last GC (in absolute time)
 	uint64	pause_total_ns;
 	uint64	pause_ns[256];
 	uint32	numgc;
@@ -304,14 +306,16 @@ 
 {
 	MSpan	*next;		// in a span linked list
 	MSpan	*prev;		// in a span linked list
-	MSpan	*allnext;		// in the list of all spans
+	MSpan	*allnext;	// in the list of all spans
 	PageID	start;		// starting page number
 	uintptr	npages;		// number of pages in span
 	MLink	*freelist;	// list of free objects
 	uint32	ref;		// number of allocated objects in this span
 	uint32	sizeclass;	// size class
 	uint32	state;		// MSpanInUse etc
-	byte	*limit;	// end of data in span
+	int64   unusedsince;	// First time spotted by GC in MSpanFree state
+	uintptr npreleased;	// number of pages released to the OS
+	byte	*limit;		// end of data in span
 };
 
 void	runtime_MSpan_Init(MSpan *span, PageID start, uintptr npages);
@@ -381,6 +385,7 @@ 
 void	runtime_MGetSizeClassInfo(int32 sizeclass, uintptr *size, int32 *npages, int32 *nobj);
 void*	runtime_MHeap_SysAlloc(MHeap *h, uintptr n);
 void	runtime_MHeap_MapBits(MHeap *h);
+void	runtime_MHeap_Scavenger(void*);
 
 void*	runtime_mallocgc(uintptr size, uint32 flag, int32 dogc, int32 zeroed);
 int32	runtime_mlookup(void *v, byte **base, uintptr *size, MSpan **s);
@@ -406,19 +411,11 @@ 
 
 void	runtime_MProf_Malloc(void*, uintptr);
 void	runtime_MProf_Free(void*, uintptr);
+void	runtime_MProf_GC(void);
 void	runtime_MProf_Mark(void (*scan)(byte *, int64));
 int32	runtime_helpgc(bool*);
 void	runtime_gchelper(void);
 
-// Malloc profiling settings.
-// Must match definition in extern.go.
-enum {
-	MProf_None = 0,
-	MProf_Sample = 1,
-	MProf_All = 2,
-};
-extern int32 runtime_malloc_profile;
-
 struct __go_func_type;
 bool	runtime_getfinalizer(void *p, bool del, void (**fn)(void*), const struct __go_func_type **ft);
 void	runtime_walkfintab(void (*fn)(void*), void (*scan)(byte *, int64));
diff -r a2b8eab75cae libgo/runtime/mgc0.c
--- a/libgo/runtime/mgc0.c	Fri Mar 02 08:23:02 2012 -0800
+++ b/libgo/runtime/mgc0.c	Fri Mar 02 10:14:12 2012 -0800
@@ -61,6 +61,21 @@ 
 
 #define bitMask (bitBlockBoundary | bitAllocated | bitMarked | bitSpecial)
 
+// Holding worldsema grants an M the right to try to stop the world.
+// The procedure is:
+//
+//	runtime_semacquire(&runtime_worldsema);
+//	m->gcing = 1;
+//	runtime_stoptheworld();
+//
+//	... do stuff ...
+//
+//	m->gcing = 0;
+//	runtime_semrelease(&runtime_worldsema);
+//	runtime_starttheworld();
+//
+uint32 runtime_worldsema = 1;
+
 // TODO: Make these per-M.
 static uint64 nhandoff;
 
@@ -92,7 +107,6 @@ 
 	Finalizer fin[1];
 };
 
-
 static G *fing;
 static FinBlock *finq; // list of finalizers that are to be executed
 static FinBlock *finc; // cache of free blocks
@@ -778,9 +792,11 @@ 
 	byte *p;
 	MCache *c;
 	byte *arena_start;
+	int64 now;
 
 	m = runtime_m();
 	arena_start = runtime_mheap.arena_start;
+	now = runtime_nanotime();
 
 	for(;;) {
 		s = work.spans;
@@ -789,6 +805,11 @@ 
 		if(!runtime_casp(&work.spans, s, s->allnext))
 			continue;
 
+		// Stamp newly unused spans. The scavenger will use that
+		// info to potentially give back some pages to the OS.
+		if(s->state == MSpanFree && s->unusedsince == 0)
+			s->unusedsince = now;
+
 		if(s->state != MSpanInUse)
 			continue;
 
@@ -875,11 +896,6 @@ 
 		runtime_notewakeup(&work.alldone);
 }
 
-// Semaphore, not Lock, so that the goroutine
-// reschedules when there is contention rather
-// than spinning.
-static uint32 gcsema = 1;
-
 // Initialized from $GOGC.  GOGC=off means no gc.
 //
 // Next gc is after we've allocated an extra amount of
@@ -968,9 +984,9 @@ 
 	if(gcpercent < 0)
 		return;
 
-	runtime_semacquire(&gcsema);
+	runtime_semacquire(&runtime_worldsema);
 	if(!force && mstats.heap_alloc < mstats.next_gc) {
-		runtime_semrelease(&gcsema);
+		runtime_semrelease(&runtime_worldsema);
 		return;
 	}
 
@@ -1032,6 +1048,7 @@ 
 	obj1 = mstats.nmalloc - mstats.nfree;
 
 	t3 = runtime_nanotime();
+	mstats.last_gc = t3;
 	mstats.pause_ns[mstats.numgc%nelem(mstats.pause_ns)] = t3 - t0;
 	mstats.pause_total_ns += t3 - t0;
 	mstats.numgc++;
@@ -1045,8 +1062,9 @@ 
 			(unsigned long long) mstats.nmalloc, (unsigned long long)mstats.nfree,
 			(unsigned long long) nhandoff);
 	}
-
-	runtime_semrelease(&gcsema);
+	
+	runtime_MProf_GC();
+	runtime_semrelease(&runtime_worldsema);
 
 	// If we could have used another helper proc, start one now,
 	// in the hope that it will be available next time.
@@ -1073,18 +1091,18 @@ 
 {
 	M *m;
 
-	// Have to acquire gcsema to stop the world,
+	// Have to acquire worldsema to stop the world,
 	// because stoptheworld can only be used by
 	// one goroutine at a time, and there might be
 	// a pending garbage collection already calling it.
-	runtime_semacquire(&gcsema);
+	runtime_semacquire(&runtime_worldsema);
 	m = runtime_m();
 	m->gcing = 1;
 	runtime_stoptheworld();
 	cachestats();
 	*stats = mstats;
 	m->gcing = 0;
-	runtime_semrelease(&gcsema);
+	runtime_semrelease(&runtime_worldsema);
 	runtime_starttheworld(false);
 }
 
diff -r a2b8eab75cae libgo/runtime/mheap.c
--- a/libgo/runtime/mheap.c	Fri Mar 02 08:23:02 2012 -0800
+++ b/libgo/runtime/mheap.c	Fri Mar 02 10:14:12 2012 -0800
@@ -103,6 +103,8 @@ 
 	runtime_MSpanList_Remove(s);
 	s->state = MSpanInUse;
 	mstats.heap_idle -= s->npages<<PageShift;
+	mstats.heap_released -= s->npreleased<<PageShift;
+	s->npreleased = 0;
 
 	if(s->npages > npage) {
 		// Trim extra and put it back in the heap.
@@ -280,6 +282,8 @@ 
 	}
 	mstats.heap_idle += s->npages<<PageShift;
 	s->state = MSpanFree;
+	s->unusedsince = 0;
+	s->npreleased = 0;
 	runtime_MSpanList_Remove(s);
 	sp = (uintptr*)(s->start<<PageShift);
 
@@ -292,6 +296,7 @@ 
 		*tp |= *sp;	// propagate "needs zeroing" mark
 		s->start = t->start;
 		s->npages += t->npages;
+		s->npreleased = t->npreleased; // absorb released pages
 		p -= t->npages;
 		h->map[p] = s;
 		runtime_MSpanList_Remove(t);
@@ -304,6 +309,7 @@ 
 		tp = (uintptr*)(t->start<<PageShift);
 		*sp |= *tp;	// propagate "needs zeroing" mark
 		s->npages += t->npages;
+		s->npreleased += t->npreleased;
 		h->map[p + s->npages - 1] = s;
 		runtime_MSpanList_Remove(t);
 		t->state = MSpanDead;
@@ -317,8 +323,86 @@ 
 		runtime_MSpanList_Insert(&h->free[s->npages], s);
 	else
 		runtime_MSpanList_Insert(&h->large, s);
+}
 
-	// TODO(rsc): IncrementalScavenge() to return memory to OS.
+// Release (part of) unused memory to OS.
+// Goroutine created in runtime_schedinit.
+// Loop forever.
+void
+runtime_MHeap_Scavenger(void* dummy)
+{
+	MHeap *h;
+	MSpan *s, *list;
+	uint64 tick, now, forcegc, limit;
+	uint32 k, i;
+	uintptr released, sumreleased;
+	const byte *env;
+	bool trace;
+	Note note;
+
+	USED(dummy);
+
+	// If we go two minutes without a garbage collection, force one to run.
+	forcegc = 2*60*1e9;
+	// If a span goes unused for 5 minutes after a garbage collection,
+	// we hand it back to the operating system.
+	limit = 5*60*1e9;
+	// Make wake-up period small enough for the sampling to be correct.
+	if(forcegc < limit)
+		tick = forcegc/2;
+	else
+		tick = limit/2;
+
+	trace = false;
+	env = runtime_getenv("GOGCTRACE");
+	if(env != nil)
+		trace = runtime_atoi(env) > 0;
+
+	h = &runtime_mheap;
+	for(k=0;; k++) {
+		runtime_noteclear(&note);
+		runtime_entersyscall();
+		runtime_notetsleep(&note, tick);
+		runtime_exitsyscall();
+
+		runtime_lock(h);
+		now = runtime_nanotime();
+		if(now - mstats.last_gc > forcegc) {
+			runtime_unlock(h);
+			runtime_gc(1);
+			runtime_lock(h);
+			now = runtime_nanotime();
+			if (trace)
+				runtime_printf("scvg%d: GC forced\n", k);
+		}
+		sumreleased = 0;
+		for(i=0; i < nelem(h->free)+1; i++) {
+			if(i < nelem(h->free))
+				list = &h->free[i];
+			else
+				list = &h->large;
+			if(runtime_MSpanList_IsEmpty(list))
+				continue;
+			for(s=list->next; s != list; s=s->next) {
+				if(s->unusedsince != 0 && (now - s->unusedsince) > limit) {
+					released = (s->npages - s->npreleased) << PageShift;
+					mstats.heap_released += released;
+					sumreleased += released;
+					s->npreleased = s->npages;
+					runtime_SysUnused((void*)(s->start << PageShift), s->npages << PageShift);
+				}
+			}
+		}
+		runtime_unlock(h);
+
+		if(trace) {
+			if(sumreleased > 0)
+				runtime_printf("scvg%d: %p MB released\n", k, (void*)(sumreleased>>20));
+			runtime_printf("scvg%d: inuse: %lld, idle: %lld, sys: %lld, released: %lld, consumed: %lld (MB)\n",
+				k, (long long)(mstats.heap_inuse>>20), (long long)(mstats.heap_idle>>20), (long long)(mstats.heap_sys>>20),
+				(long long)(mstats.heap_released>>20), (long long)((mstats.heap_sys - mstats.heap_released)>>20));
+		}
+	}
 }
 
 // Initialize a new span with the given start and npages.
@@ -333,6 +417,8 @@ 
 	span->ref = 0;
 	span->sizeclass = 0;
 	span->state = 0;
+	span->unusedsince = 0;
+	span->npreleased = 0;
 }
 
 // Initialize an empty doubly-linked list.
diff -r a2b8eab75cae libgo/runtime/mprof.goc
--- a/libgo/runtime/mprof.goc	Fri Mar 02 08:23:02 2012 -0800
+++ b/libgo/runtime/mprof.goc	Fri Mar 02 10:14:12 2012 -0800
@@ -26,6 +26,10 @@ 
 	uintptr	frees;
 	uintptr	alloc_bytes;
 	uintptr	free_bytes;
+	uintptr	recent_allocs;  // since last gc
+	uintptr	recent_frees;
+	uintptr	recent_alloc_bytes;
+	uintptr	recent_free_bytes;
 	uintptr	hash;
 	uintptr	nstk;
 	uintptr	stk[1];
@@ -39,7 +43,7 @@ 
 
 // Return the bucket for stk[0:nstk], allocating new bucket if needed.
 static Bucket*
-stkbucket(uintptr *stk, int32 nstk)
+stkbucket(uintptr *stk, int32 nstk, bool alloc)
 {
 	int32 i;
 	uintptr h;
@@ -66,6 +70,9 @@ 
 		   runtime_mcmp((byte*)b->stk, (byte*)stk, nstk*sizeof stk[0]) == 0)
 			return b;
 
+	if(!alloc)
+		return nil;
+
 	b = runtime_mallocgc(sizeof *b + nstk*sizeof stk[0], FlagNoProfiling, 0, 1);
 	bucketmem += sizeof *b + nstk*sizeof stk[0];
 	runtime_memmove(b->stk, stk, nstk*sizeof stk[0]);
@@ -78,6 +85,26 @@ 
 	return b;
 }
 
+// Record that a gc just happened: all the 'recent' statistics are now real.
+void
+runtime_MProf_GC(void)
+{
+	Bucket *b;
+	
+	runtime_lock(&proflock);
+	for(b=buckets; b; b=b->allnext) {
+		b->allocs += b->recent_allocs;
+		b->frees += b->recent_frees;
+		b->alloc_bytes += b->recent_alloc_bytes;
+		b->free_bytes += b->recent_free_bytes;
+		b->recent_allocs = 0;
+		b->recent_frees = 0;
+		b->recent_alloc_bytes = 0;
+		b->recent_free_bytes = 0;
+	}
+	runtime_unlock(&proflock);
+}
+
 // Map from pointer to Bucket* that allocated it.
 // Three levels:
 //	Linked-list hash table for top N-20 bits.
@@ -204,9 +231,9 @@ 
 	nstk = 0;
 #endif
 	runtime_lock(&proflock);
-	b = stkbucket(stk, nstk);
-	b->allocs++;
-	b->alloc_bytes += size;
+	b = stkbucket(stk, nstk, true);
+	b->recent_allocs++;
+	b->recent_alloc_bytes += size;
 	setaddrbucket((uintptr)p, b);
 	runtime_unlock(&proflock);
 	m = runtime_m();
@@ -228,8 +255,8 @@ 
 	runtime_lock(&proflock);
 	b = getaddrbucket((uintptr)p);
 	if(b != nil) {
-		b->frees++;
-		b->free_bytes += size;
+		b->recent_frees++;
+		b->recent_free_bytes += size;
 	}
 	runtime_unlock(&proflock);
 	m = runtime_m();
@@ -293,13 +320,13 @@ 
 	scan((byte*)&addrfree, sizeof addrfree);
 }
 
-// Must match ThreadProfileRecord in debug.go.
+// Must match StackRecord in debug.go.
 typedef struct TRecord TRecord;
 struct TRecord {
 	uintptr stk[32];
 };
 
-func ThreadProfile(p Slice) (n int32, ok bool) {
+func ThreadCreateProfile(p Slice) (n int32, ok bool) {
 	TRecord *r;
 	M *first, *m;
 	
@@ -317,3 +344,89 @@ 
 		}
 	}
 }
+
+func Stack(b Slice, all bool) (n int32) {
+	byte *pc, *sp;
+	
+	sp = runtime_getcallersp(&b);
+	pc = runtime_getcallerpc(&b);
+
+	if(all) {
+		runtime_semacquire(&runtime_worldsema);
+		runtime_m()->gcing = 1;
+		runtime_stoptheworld();
+	}
+
+	if(b.__count == 0)
+		n = 0;
+	else{
+		G* g = runtime_g();
+		g->writebuf = (byte*)b.__values;
+		g->writenbuf = b.__count;
+		USED(pc);
+		USED(sp);
+		// runtime_goroutineheader(g);
+		// runtime_traceback(pc, sp, 0, g);
+		// if(all)
+		//	runtime_tracebackothers(g);
+		n = b.__count - g->writenbuf;
+		g->writebuf = nil;
+		g->writenbuf = 0;
+	}
+	
+	if(all) {
+		runtime_m()->gcing = 0;
+		runtime_semrelease(&runtime_worldsema);
+		runtime_starttheworld(false);
+	}
+}
+
+static void
+saveg(byte *pc, byte *sp, G *g, TRecord *r)
+{
+	int32 n;
+
+	USED(pc);
+	USED(sp);
+	USED(g);
+	// n = runtime_gentraceback(pc, sp, 0, g, 0, r->stk, nelem(r->stk));
+	n = 0;
+	if((size_t)n < nelem(r->stk))
+		r->stk[n] = 0;
+}
+
+func GoroutineProfile(b Slice) (n int32, ok bool) {
+	byte *pc, *sp;
+	TRecord *r;
+	G *gp;
+	
+	sp = runtime_getcallersp(&b);
+	pc = runtime_getcallerpc(&b);
+	
+	ok = false;
+	n = runtime_gcount();
+	if(n <= b.__count) {
+		runtime_semacquire(&runtime_worldsema);
+		runtime_m()->gcing = 1;
+		runtime_stoptheworld();
+
+		n = runtime_gcount();
+		if(n <= b.__count) {
+			G* g = runtime_g();
+			ok = true;
+			r = (TRecord*)b.__values;
+			saveg(pc, sp, g, r++);
+			for(gp = runtime_allg; gp != nil; gp = gp->alllink) {
+				if(gp == g || gp->status == Gdead)
+					continue;
+				//saveg(gp->sched.pc, gp->sched.sp, gp, r++);
+				r++;
+			}
+		}
+	
+		runtime_m()->gcing = 0;
+		runtime_semrelease(&runtime_worldsema);
+		runtime_starttheworld(false);
+	}
+}
+
diff -r a2b8eab75cae libgo/runtime/proc.c
--- a/libgo/runtime/proc.c	Fri Mar 02 08:23:02 2012 -0800
+++ b/libgo/runtime/proc.c	Fri Mar 02 10:14:12 2012 -0800
@@ -362,6 +362,9 @@ 
 	}
 }
 
+// Keep trace of scavenger's goroutine for deadlock detection.
+static G *scvg;
+
 // The bootstrap sequence is:
 //
 //	call osinit
@@ -413,6 +416,8 @@ 
 	// Can not enable GC until all roots are registered.
 	// mstats.enablegc = 1;
 	m->nomemprof--;
+
+	scvg = __go_go(runtime_MHeap_Scavenger, nil);
 }
 
 extern void main_init(void) __asm__ ("__go_init_main");
@@ -547,7 +552,7 @@ 
 	// Add to runtime_allm so garbage collector doesn't free m
 	// when it is just in a register or thread-local storage.
 	m->alllink = runtime_allm;
-	// runtime_Cgocalls() iterates over allm w/o schedlock,
+	// runtime_NumCgoCall() iterates over allm w/o schedlock,
 	// so we need to publish it safely.
 	runtime_atomicstorep(&runtime_allm, m);
 }
@@ -786,9 +791,12 @@ 
 		mput(m);
 	}
 
-	v = runtime_atomicload(&runtime_sched.atomic);
-	if(runtime_sched.grunning == 0)
-		runtime_throw("all goroutines are asleep - deadlock!");
+	// Look for deadlock situation: one single active g which happens to be scvg.
+	if(runtime_sched.grunning == 1 && runtime_sched.gwait == 0) {
+		if(scvg->status == Grunning || scvg->status == Gsyscall)
+			runtime_throw("all goroutines are asleep - deadlock!");
+	}
+
 	m->nextg = nil;
 	m->waitnextg = 1;
 	runtime_noteclear(&m->havenextg);
@@ -797,6 +805,7 @@ 
 	// Entersyscall might have decremented mcpu too, but if so
 	// it will see the waitstop and take the slow path.
 	// Exitsyscall never increments mcpu beyond mcpumax.
+	v = runtime_atomicload(&runtime_sched.atomic);
 	if(atomic_waitstop(v) && atomic_mcpu(v) <= atomic_mcpumax(v)) {
 		// set waitstop = 0 (known to be 1)
 		runtime_xadd(&runtime_sched.atomic, -1<<waitstopShift);
@@ -1472,11 +1481,17 @@ 
 	return m->id;
 }
 
-int32 runtime_Goroutines (void)
-  __asm__ ("libgo_runtime.runtime.Goroutines");
+int32 runtime_NumGoroutine (void)
+  __asm__ ("libgo_runtime.runtime.NumGoroutine");
 
 int32
-runtime_Goroutines()
+runtime_NumGoroutine()
+{
+	return runtime_sched.gcount;
+}
+
+int32
+runtime_gcount(void)
 {
 	return runtime_sched.gcount;
 }
diff -r a2b8eab75cae libgo/runtime/runtime.h
--- a/libgo/runtime/runtime.h	Fri Mar 02 08:23:02 2012 -0800
+++ b/libgo/runtime/runtime.h	Fri Mar 02 10:14:12 2012 -0800
@@ -143,6 +143,8 @@ 
 	M*	lockedm;
 	M*	idlem;
 	// int32	sig;
+	int32	writenbuf;
+	byte*	writebuf;
 	// uintptr	sigcode0;
 	// uintptr	sigcode1;
 	// uintptr	sigpc;
@@ -189,9 +191,9 @@ 
 enum
 {
 	SigNotify = 1<<0,	// let signal.Notify have signal, even if from kernel
-	SigKill = 1<<1,  // if signal.Notify doesn't take it, exit quietly
-	SigThrow = 1<<2,  // if signal.Notify doesn't take it, exit loudly
-	SigPanic = 1<<3,  // if the signal is from the kernel, panic
+	SigKill = 1<<1,		// if signal.Notify doesn't take it, exit quietly
+	SigThrow = 1<<2,	// if signal.Notify doesn't take it, exit loudly
+	SigPanic = 1<<3,	// if the signal is from the kernel, panic
 	SigDefault = 1<<4,	// if the signal isn't explicitly requested, don't monitor it
 };
 
@@ -277,6 +279,7 @@ 
 void*	runtime_mal(uintptr);
 void	runtime_schedinit(void);
 void	runtime_initsig(void);
+void	runtime_sigenable(uint32 sig);
 String	runtime_gostringnocopy(const byte*);
 void*	runtime_mstart(void*);
 G*	runtime_malg(int32, byte**, size_t*);
@@ -296,6 +299,7 @@ 
 
 void	runtime_stoptheworld(void);
 void	runtime_starttheworld(bool);
+extern uint32 runtime_worldsema;
 G*	__go_go(void (*pfn)(void*), void*);
 
 /*
@@ -348,6 +352,7 @@ 
 #define runtime_munmap munmap
 #define runtime_madvise madvise
 #define runtime_memclr(buf, size) __builtin_memset((buf), 0, (size))
+#define runtime_getcallerpc(p) __builtin_return_address(0)
 
 #ifdef __rtems__
 void __wrap_rtems_task_variable_add(void **);
@@ -373,8 +378,6 @@ 
 #define runtime_exit(s) exit(s)
 MCache*	runtime_allocmcache(void);
 void	free(void *v);
-struct __go_func_type;
-bool	runtime_addfinalizer(void*, void(*fn)(void*), const struct __go_func_type *);
 #define runtime_cas(pval, old, new) __sync_bool_compare_and_swap (pval, old, new)
 #define runtime_casp(pval, old, new) __sync_bool_compare_and_swap (pval, old, new)
 #define runtime_xadd(p, v) __sync_add_and_fetch (p, v)
@@ -384,6 +387,11 @@ 
 #define runtime_atomicloadp(p) __atomic_load_n (p, __ATOMIC_SEQ_CST)
 #define runtime_atomicstorep(p, v) __atomic_store_n (p, v, __ATOMIC_SEQ_CST)
 
+struct __go_func_type;
+bool	runtime_addfinalizer(void*, void(*fn)(void*), const struct __go_func_type *);
+#define runtime_getcallersp(p) __builtin_frame_address(1)
+int32	runtime_mcount(void);
+int32	runtime_gcount(void);
 void	runtime_dopanic(int32) __attribute__ ((noreturn));
 void	runtime_startpanic(void);
 void	runtime_ready(G*);
diff -r a2b8eab75cae libgo/runtime/sema.goc
--- a/libgo/runtime/sema.goc	Fri Mar 02 08:23:02 2012 -0800
+++ b/libgo/runtime/sema.goc	Fri Mar 02 10:14:12 2012 -0800
@@ -17,7 +17,7 @@ 
 // See Mullender and Cox, ``Semaphores in Plan 9,''
 // http://swtch.com/semaphore.pdf
 
-package runtime
+package sync
 #include "runtime.h"
 #include "arch.h"
 
@@ -172,10 +172,10 @@ 
 		runtime_ready(s->g);
 }
 
-func Semacquire(addr *uint32) {
+func runtime_Semacquire(addr *uint32) {
 	runtime_semacquire(addr);
 }
 
-func Semrelease(addr *uint32) {
+func runtime_Semrelease(addr *uint32) {
 	runtime_semrelease(addr);
 }
diff -r a2b8eab75cae libgo/runtime/sigqueue.goc
--- a/libgo/runtime/sigqueue.goc	Fri Mar 02 08:23:02 2012 -0800
+++ b/libgo/runtime/sigqueue.goc	Fri Mar 02 10:14:12 2012 -0800
@@ -142,10 +142,12 @@ 
 		// Special case: want everything.
 		for(i=0; (size_t)i<nelem(sig.wanted); i++)
 			sig.wanted[i] = ~(uint32)0;
+		runtime_sigenable(s);
 		return;
 	}
 
 	if(s >= nelem(sig.wanted)*32)
 		return;
 	sig.wanted[s/32] |= 1U<<(s&31);
+	runtime_sigenable(s);
 }