From 6c976393aea607e67f4d31e3a2ae7b3c0dc15ade Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Wed, 20 Feb 2013 17:48:23 -0500 Subject: [PATCH] runtime: allow cgo callbacks on non-Go threads Fixes #4435. R=golang-dev, iant, alex.brainman, minux.ma, dvyukov CC=golang-dev https://golang.org/cl/7304104 --- misc/cgo/test/cgo_test.go | 1 + misc/cgo/test/cthread.go | 44 ++++++ misc/cgo/test/cthread_unix.c | 34 +++++ misc/cgo/test/cthread_windows.c | 37 +++++ src/pkg/runtime/asm_386.s | 66 ++++++-- src/pkg/runtime/asm_amd64.s | 65 ++++++-- src/pkg/runtime/asm_arm.s | 44 +++++- src/pkg/runtime/cgocall.c | 5 + src/pkg/runtime/os_windows.h | 1 + src/pkg/runtime/proc.c | 229 ++++++++++++++++++++++++++-- src/pkg/runtime/runtime.h | 6 +- src/pkg/runtime/sys_windows_386.s | 12 ++ src/pkg/runtime/sys_windows_amd64.s | 3 + src/pkg/runtime/thread_darwin.c | 7 + src/pkg/runtime/thread_freebsd.c | 7 + src/pkg/runtime/thread_linux.c | 7 + src/pkg/runtime/thread_netbsd.c | 7 + src/pkg/runtime/thread_openbsd.c | 7 + src/pkg/runtime/thread_plan9.c | 8 + src/pkg/runtime/thread_windows.c | 8 + 20 files changed, 564 insertions(+), 34 deletions(-) create mode 100644 misc/cgo/test/cthread.go create mode 100644 misc/cgo/test/cthread_unix.c create mode 100644 misc/cgo/test/cthread_windows.c diff --git a/misc/cgo/test/cgo_test.go b/misc/cgo/test/cgo_test.go index d2514582a2..536fa507ae 100644 --- a/misc/cgo/test/cgo_test.go +++ b/misc/cgo/test/cgo_test.go @@ -35,5 +35,6 @@ func Test4029(t *testing.T) { test4029(t) } func TestBoolAlign(t *testing.T) { testBoolAlign(t) } func Test3729(t *testing.T) { test3729(t) } func Test3775(t *testing.T) { test3775(t) } +func TestCthread(t *testing.T) { testCthread(t) } func BenchmarkCgoCall(b *testing.B) { benchCgoCall(b) } diff --git a/misc/cgo/test/cthread.go b/misc/cgo/test/cthread.go new file mode 100644 index 0000000000..d918d033fe --- /dev/null +++ b/misc/cgo/test/cthread.go @@ -0,0 +1,44 @@ +// Copyright 2013 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. + +package cgotest + +// extern void doAdd(int, int); +import "C" + +import ( + "runtime" + "sync" + "testing" +) + +var sum struct { + sync.Mutex + i int +} + +//export Add +func Add(x int) { + defer func() { + recover() + }() + sum.Lock() + sum.i += x + sum.Unlock() + var p *int + *p = 2 +} + +func testCthread(t *testing.T) { + if runtime.GOARCH == "arm" { + t.Skip("testCthread disabled on arm") + } + + C.doAdd(10, 6) + + want := 10 * (10 - 1) / 2 * 6 + if sum.i != want { + t.Fatalf("sum=%d, want %d", sum.i, want) + } +} diff --git a/misc/cgo/test/cthread_unix.c b/misc/cgo/test/cthread_unix.c new file mode 100644 index 0000000000..998bc00cb7 --- /dev/null +++ b/misc/cgo/test/cthread_unix.c @@ -0,0 +1,34 @@ +// Copyright 2013 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. + +// +build darwin freebsd linux netbsd openbsd + +#include +#include "_cgo_export.h" + +static void* +addThread(void *p) +{ + int i, max; + + max = *(int*)p; + for(i=0; i MaxThread) + nthread = MaxThread; + for(i=0; i +#include +#include "_cgo_export.h" + +__stdcall +static unsigned int +addThread(void *p) +{ + int i, max; + + max = *(int*)p; + for(i=0; i MaxThread) + nthread = MaxThread; + for(i=0; ig0->sched.sp on stack and then set it to SP. + // If m is nil, Go did not create the current thread. + // Call needm to obtain one for temporary use. + // In this case, we're running on the thread stack, so there's + // lots of space, but the linker doesn't know. Hide the call from + // the linker analysis by using an indirect call through AX. get_tls(CX) +#ifdef GOOS_windows + CMPL CX, $0 + JNE 3(PC) + PUSHL $0 + JMP needm +#endif MOVL m(CX), BP - - // If m is nil, it is almost certainly because we have been called - // on a thread that Go did not create. We're going to crash as - // soon as we try to use m; instead, try to print a nice error and exit. + PUSHL BP CMPL BP, $0 - JNE 2(PC) - CALL runtime·badcallback(SB) + JNE havem +needm: + MOVL $runtime·needm(SB), AX + CALL AX + get_tls(CX) + MOVL m(CX), BP +havem: + // Now there's a valid m, and we're running on its m->g0. + // Save current m->g0->sched.sp on stack and then set it to SP. + // Save current sp in m->g0->sched.sp in preparation for + // switch back to m->curg stack. MOVL m_g0(BP), SI PUSHL (g_sched+gobuf_sp)(SI) MOVL SP, (g_sched+gobuf_sp)(SI) @@ -509,6 +521,10 @@ TEXT runtime·cgocallback(SB),7,$12 // a frame size of 12, the same amount that we use below), // so that the traceback will seamlessly trace back into // the earlier calls. + MOVL fn+0(FP), AX + MOVL frame+4(FP), BX + MOVL framesize+8(FP), DX + MOVL m_curg(BP), SI MOVL SI, g(CX) MOVL (g_sched+gobuf_sp)(SI), DI // prepare stack as DI @@ -546,10 +562,38 @@ TEXT runtime·cgocallback(SB),7,$12 MOVL SI, g(CX) MOVL (g_sched+gobuf_sp)(SI), SP POPL (g_sched+gobuf_sp)(SI) + + // If the m on entry was nil, we called needm above to borrow an m + // for the duration of the call. Since the call is over, return it with dropm. + POPL BP + CMPL BP, $0 + JNE 3(PC) + MOVL $runtime·dropm(SB), AX + CALL AX // Done! RET +// void setmg(M*, G*); set m and g. for use by needm. +TEXT runtime·setmg(SB), 7, $0 +#ifdef GOOS_windows + MOVL mm+0(FP), AX + CMPL AX, $0 + JNE settls + MOVL $0, 0x14(FS) + RET +settls: + LEAL m_tls(AX), AX + MOVL AX, 0x14(FS) +#endif + MOVL mm+0(FP), AX + get_tls(CX) + MOVL mm+0(FP), AX + MOVL AX, m(CX) + MOVL gg+4(FP), BX + MOVL BX, g(CX) + RET + // check that SP is in range [g->stackbase, g->stackguard) TEXT runtime·stackcheck(SB), 7, $0 get_tls(CX) diff --git a/src/pkg/runtime/asm_amd64.s b/src/pkg/runtime/asm_amd64.s index 216c89c29f..159b7639be 100644 --- a/src/pkg/runtime/asm_amd64.s +++ b/src/pkg/runtime/asm_amd64.s @@ -509,21 +509,33 @@ TEXT runtime·asmcgocall(SB),7,$0 // cgocallback(void (*fn)(void*), void *frame, uintptr framesize) // See cgocall.c for more details. TEXT runtime·cgocallback(SB),7,$24 - MOVQ fn+0(FP), AX - MOVQ frame+8(FP), BX - MOVQ framesize+16(FP), DX - - // Save current m->g0->sched.sp on stack and then set it to SP. + // If m is nil, Go did not create the current thread. + // Call needm to obtain one for temporary use. + // In this case, we're running on the thread stack, so there's + // lots of space, but the linker doesn't know. Hide the call from + // the linker analysis by using an indirect call through AX. get_tls(CX) +#ifdef GOOS_windows + CMPQ CX, $0 + JNE 3(PC) + PUSHQ $0 + JMP needm +#endif MOVQ m(CX), BP - - // If m is nil, it is almost certainly because we have been called - // on a thread that Go did not create. We're going to crash as - // soon as we try to use m; instead, try to print a nice error and exit. + PUSHQ BP CMPQ BP, $0 - JNE 2(PC) - CALL runtime·badcallback(SB) + JNE havem +needm: + MOVQ $runtime·needm(SB), AX + CALL AX + get_tls(CX) + MOVQ m(CX), BP +havem: + // Now there's a valid m, and we're running on its m->g0. + // Save current m->g0->sched.sp on stack and then set it to SP. + // Save current sp in m->g0->sched.sp in preparation for + // switch back to m->curg stack. MOVQ m_g0(BP), SI PUSHQ (g_sched+gobuf_sp)(SI) MOVQ SP, (g_sched+gobuf_sp)(SI) @@ -542,6 +554,10 @@ TEXT runtime·cgocallback(SB),7,$24 // a frame size of 24, the same amount that we use below), // so that the traceback will seamlessly trace back into // the earlier calls. + MOVQ fn+0(FP), AX + MOVQ frame+8(FP), BX + MOVQ framesize+16(FP), DX + MOVQ m_curg(BP), SI MOVQ SI, g(CX) MOVQ (g_sched+gobuf_sp)(SI), DI // prepare stack as DI @@ -579,10 +595,37 @@ TEXT runtime·cgocallback(SB),7,$24 MOVQ SI, g(CX) MOVQ (g_sched+gobuf_sp)(SI), SP POPQ (g_sched+gobuf_sp)(SI) + + // If the m on entry was nil, we called needm above to borrow an m + // for the duration of the call. Since the call is over, return it with dropm. + POPQ BP + CMPQ BP, $0 + JNE 3(PC) + MOVQ $runtime·dropm(SB), AX + CALL AX // Done! RET +// void setmg(M*, G*); set m and g. for use by needm. +TEXT runtime·setmg(SB), 7, $0 + MOVQ mm+0(FP), AX +#ifdef GOOS_windows + CMPQ AX, $0 + JNE settls + MOVQ $0, 0x28(GS) + RET +settls: + LEAQ m_tls(AX), AX + MOVQ AX, 0x28(GS) +#endif + get_tls(CX) + MOVQ mm+0(FP), AX + MOVQ AX, m(CX) + MOVQ gg+8(FP), BX + MOVQ BX, g(CX) + RET + // check that SP is in range [g->stackbase, g->stackguard) TEXT runtime·stackcheck(SB), 7, $0 get_tls(CX) diff --git a/src/pkg/runtime/asm_arm.s b/src/pkg/runtime/asm_arm.s index 9af5a8a0df..b0678bcd0b 100644 --- a/src/pkg/runtime/asm_arm.s +++ b/src/pkg/runtime/asm_arm.s @@ -290,11 +290,31 @@ TEXT runtime·asmcgocall(SB),7,$0 // cgocallback(void (*fn)(void*), void *frame, uintptr framesize) // See cgocall.c for more details. TEXT runtime·cgocallback(SB),7,$16 + // Load m and g from thread-local storage. + MOVW cgo_load_gm(SB), R0 + CMP $0, R0 + BL.NE (R0) + + // If m is nil, Go did not create the current thread. + // Call needm to obtain one for temporary use. + // In this case, we're running on the thread stack, so there's + // lots of space, but the linker doesn't know. Hide the call from + // the linker analysis by using an indirect call. + MOVW m, savedm-16(SP) + CMP $0, m + B.NE havem + MOVW $runtime·needm(SB), R0 + BL (R0) + +havem: + // Now there's a valid m, and we're running on its m->g0. + // Save current m->g0->sched.sp on stack and then set it to SP. + // Save current sp in m->g0->sched.sp in preparation for + // switch back to m->curg stack. MOVW fn+0(FP), R0 MOVW frame+4(FP), R1 MOVW framesize+8(FP), R2 - // Save current m->g0->sched.sp on stack and then set it to SP. MOVW m_g0(m), R3 MOVW (g_sched+gobuf_sp)(R3), R4 MOVW.W R4, -4(R13) @@ -314,6 +334,8 @@ TEXT runtime·cgocallback(SB),7,$16 // a frame size of 16, the same amount that we use below), // so that the traceback will seamlessly trace back into // the earlier calls. + + // Save current m->g0->sched.sp on stack and then set it to SP. MOVW m_curg(m), g MOVW (g_sched+gobuf_sp)(g), R4 // prepare stack as R4 @@ -350,9 +372,29 @@ TEXT runtime·cgocallback(SB),7,$16 ADD $4, R13 MOVW R6, (g_sched+gobuf_sp)(g) + // If the m on entry was nil, we called needm above to borrow an m + // for the duration of the call. Since the call is over, return it with dropm. + MOVW savedm-16(SP), R6 + CMP $0, R6 + B.NE 3(PC) + MOVW $runtime·dropm(SB), R0 + BL (R0) + // Done! RET +// void setmg(M*, G*); set m and g. for use by needm. +TEXT runtime·setmg(SB), 7, $-4 + MOVW mm+0(FP), m + MOVW gg+4(FP), g + + // Save m and g to thread-local storage. + MOVW cgo_save_gm(SB), R0 + CMP $0, R0 + BL.NE (R0) + + RET + TEXT runtime·getcallerpc(SB),7,$-4 MOVW 0(SP), R0 RET diff --git a/src/pkg/runtime/cgocall.c b/src/pkg/runtime/cgocall.c index 519a5386b9..4f68b466fe 100644 --- a/src/pkg/runtime/cgocall.c +++ b/src/pkg/runtime/cgocall.c @@ -216,6 +216,11 @@ runtime·cgocallbackg(void (*fn)(void), void *arg, uintptr argsize) runtime·exitsyscall(); // coming out of cgo call + if(m->needextram) { + m->needextram = 0; + runtime·newextram(); + } + // Add entry to defer stack in case of panic. d.fn = (byte*)unwindm; d.siz = 0; diff --git a/src/pkg/runtime/os_windows.h b/src/pkg/runtime/os_windows.h index e8962265d5..cf0ecb68ee 100644 --- a/src/pkg/runtime/os_windows.h +++ b/src/pkg/runtime/os_windows.h @@ -29,6 +29,7 @@ byte *runtime·compilecallback(Eface fn, bool cleanstack); void *runtime·callbackasm(void); void runtime·install_exception_handler(void); +void runtime·remove_exception_handler(void); // TODO(brainman): should not need those #define NSIG 65 diff --git a/src/pkg/runtime/proc.c b/src/pkg/runtime/proc.c index b83bd9066f..67d6dad488 100644 --- a/src/pkg/runtime/proc.c +++ b/src/pkg/runtime/proc.c @@ -25,6 +25,7 @@ int32 runtime·gcwaiting; G* runtime·allg; G* runtime·lastg; M* runtime·allm; +M* runtime·extram; int8* runtime·goos; int32 runtime·ncpu; @@ -792,8 +793,11 @@ runtime·mstart(void) // Install signal handlers; after minit so that minit can // prepare the thread to be able to handle the signals. - if(m == &runtime·m0) + if(m == &runtime·m0) { runtime·initsig(); + if(runtime·iscgo) + runtime·newextram(); + } schedule(nil); @@ -838,9 +842,9 @@ matchmg(void) } } -// Create a new m. It will start off with a call to runtime·mstart. +// Allocate a new m unassociated with any thread. M* -runtime·newm(void) +runtime·allocm(void) { M *mp; static Type *mtype; // The Go type M @@ -854,23 +858,228 @@ runtime·newm(void) mp = runtime·cnew(mtype); mcommoninit(mp); + if(runtime·iscgo || Windows) + mp->g0 = runtime·malg(-1); + else + mp->g0 = runtime·malg(8192); + + return mp; +} + +static M* lockextra(bool nilokay); +static void unlockextra(M*); + +// needm is called when a cgo callback happens on a +// thread without an m (a thread not created by Go). +// In this case, needm is expected to find an m to use +// and return with m, g initialized correctly. +// Since m and g are not set now (likely nil, but see below) +// needm is limited in what routines it can call. In particular +// it can only call nosplit functions (textflag 7) and cannot +// do any scheduling that requires an m. +// +// In order to avoid needing heavy lifting here, we adopt +// the following strategy: there is a stack of available m's +// that can be stolen. Using compare-and-swap +// to pop from the stack has ABA races, so we simulate +// a lock by doing an exchange (via casp) to steal the stack +// head and replace the top pointer with MLOCKED (1). +// This serves as a simple spin lock that we can use even +// without an m. The thread that locks the stack in this way +// unlocks the stack by storing a valid stack head pointer. +// +// In order to make sure that there is always an m structure +// available to be stolen, we maintain the invariant that there +// is always one more than needed. At the beginning of the +// program (if cgo is in use) the list is seeded with a single m. +// If needm finds that it has taken the last m off the list, its job +// is - once it has installed its own m so that it can do things like +// allocate memory - to create a spare m and put it on the list. +// +// Each of these extra m's also has a g0 and a curg that are +// pressed into service as the scheduling stack and current +// goroutine for the duration of the cgo callback. +// +// When the callback is done with the m, it calls dropm to +// put the m back on the list. +#pragma textflag 7 +void +runtime·needm(byte x) +{ + M *mp; + + // Lock extra list, take head, unlock popped list. + // nilokay=false is safe here because of the invariant above, + // that the extra list always contains or will soon contain + // at least one m. + mp = lockextra(false); + + // Set needextram when we've just emptied the list, + // so that the eventual call into cgocallbackg will + // allocate a new m for the extra list. We delay the + // allocation until then so that it can be done + // after exitsyscall makes sure it is okay to be + // running at all (that is, there's no garbage collection + // running right now). + mp->needextram = mp->schedlink == nil; + unlockextra(mp->schedlink); + + // Install m and g (= m->g0) and set the stack bounds + // to match the current stack. We don't actually know + // how big the stack is, like we don't know how big any + // scheduling stack is, but we assume there's at least 32 kB, + // which is more than enough for us. + runtime·setmg(mp, mp->g0); + g->stackbase = (uintptr)(&x + 1024); + g->stackguard = (uintptr)(&x - 32*1024); + + // On windows/386, we need to put an SEH frame (two words) + // somewhere on the current stack. We are called + // from needm, and we know there is some available + // space one word into the argument frame. Use that. + m->seh = (SEH*)((uintptr*)&x + 1); + + // Initialize this thread to use the m. + runtime·asminit(); + runtime·minit(); +} + +// newextram allocates an m and puts it on the extra list. +// It is called with a working local m, so that it can do things +// like call schedlock and allocate. +void +runtime·newextram(void) +{ + M *mp, *mnext; + G *gp; + + // Scheduler protects allocation of new m's and g's. + // Create extra goroutine locked to extra m. + // The goroutine is the context in which the cgo callback will run. + // The sched.pc will never be returned to, but setting it to + // runtime.goexit makes clear to the traceback routines where + // the goroutine stack ends. + schedlock(); + mp = runtime·allocm(); + gp = runtime·malg(4096); + gp->sched.pc = (void*)runtime·goexit; + gp->sched.sp = gp->stackbase; + gp->sched.g = gp; + gp->status = Gsyscall; + mp->curg = gp; + mp->locked = LockInternal; + mp->lockedg = gp; + gp->lockedm = mp; + schedunlock(); + + // Add m to the extra list. + mnext = lockextra(true); + mp->schedlink = mnext; + unlockextra(mp); +} + +// dropm is called when a cgo callback has called needm but is now +// done with the callback and returning back into the non-Go thread. +// It puts the current m back onto the extra list. +// +// The main expense here is the call to signalstack to release the +// m's signal stack, and then the call to needm on the next callback +// from this thread. It is tempting to try to save the m for next time, +// which would eliminate both these costs, but there might not be +// a next time: the current thread (which Go does not control) might exit. +// If we saved the m for that thread, there would be an m leak each time +// such a thread exited. Instead, we acquire and release an m on each +// call. These should typically not be scheduling operations, just a few +// atomics, so the cost should be small. +// +// TODO(rsc): An alternative would be to allocate a dummy pthread per-thread +// variable using pthread_key_create. Unlike the pthread keys we already use +// on OS X, this dummy key would never be read by Go code. It would exist +// only so that we could register at thread-exit-time destructor. +// That destructor would put the m back onto the extra list. +// This is purely a performance optimization. The current version, +// in which dropm happens on each cgo call, is still correct too. +// We may have to keep the current version on systems with cgo +// but without pthreads, like Windows. +void +runtime·dropm(void) +{ + M *mp, *mnext; + + // Undo whatever initialization minit did during needm. + runtime·unminit(); + + // Clear m and g, and return m to the extra list. + // After the call to setmg we can only call nosplit functions. + mp = m; + runtime·setmg(nil, nil); + + mnext = lockextra(true); + mp->schedlink = mnext; + unlockextra(mp); +} + +#define MLOCKED ((M*)1) + +// lockextra locks the extra list and returns the list head. +// The caller must unlock the list by storing a new list head +// to runtime.extram. If nilokay is true, then lockextra will +// return a nil list head if that's what it finds. If nilokay is false, +// lockextra will keep waiting until the list head is no longer nil. +#pragma textflag 7 +static M* +lockextra(bool nilokay) +{ + M *mp; + void (*yield)(void); + + for(;;) { + mp = runtime·atomicloadp(&runtime·extram); + if(mp == MLOCKED) { + yield = runtime·osyield; + yield(); + continue; + } + if(mp == nil && !nilokay) { + runtime·usleep(1); + continue; + } + if(!runtime·casp(&runtime·extram, mp, MLOCKED)) { + yield = runtime·osyield; + yield(); + continue; + } + break; + } + return mp; +} + +#pragma textflag 7 +static void +unlockextra(M *mp) +{ + runtime·atomicstorep(&runtime·extram, mp); +} + + +// Create a new m. It will start off with a call to runtime·mstart. +M* +runtime·newm(void) +{ + M *mp; + + mp = runtime·allocm(); + if(runtime·iscgo) { CgoThreadStart ts; if(libcgo_thread_start == nil) runtime·throw("libcgo_thread_start missing"); - // pthread_create will make us a stack. - mp->g0 = runtime·malg(-1); ts.m = mp; ts.g = mp->g0; ts.fn = runtime·mstart; runtime·asmcgocall(libcgo_thread_start, &ts); } else { - if(Windows) - // windows will layout sched stack on os stack - mp->g0 = runtime·malg(-1); - else - mp->g0 = runtime·malg(8192); runtime·newosproc(mp, mp->g0, (byte*)mp->g0->stackbase, runtime·mstart); } diff --git a/src/pkg/runtime/runtime.h b/src/pkg/runtime/runtime.h index e63877e681..8162874bbe 100644 --- a/src/pkg/runtime/runtime.h +++ b/src/pkg/runtime/runtime.h @@ -289,6 +289,7 @@ struct M uint32 waitsemalock; GCStats gcstats; bool racecall; + bool needextram; void* racepc; uint32 moreframesize_minalloc; @@ -657,10 +658,11 @@ void runtime·ready(G*); byte* runtime·getenv(int8*); int32 runtime·atoi(byte*); void runtime·newosproc(M *mp, G *gp, void *stk, void (*fn)(void)); -void runtime·signalstack(byte*, int32); G* runtime·malg(int32); void runtime·asminit(void); void runtime·minit(void); +void runtime·unminit(void); +void runtime·signalstack(byte*, int32); Func* runtime·findfunc(uintptr); int32 runtime·funcline(Func*, uintptr); void* runtime·stackalloc(uint32); @@ -683,6 +685,8 @@ int32 runtime·gcount(void); void runtime·mcall(void(*)(G*)); uint32 runtime·fastrand1(void); +void runtime·setmg(M*, G*); +void runtime·newextram(void); void runtime·exit(int32); void runtime·breakpoint(void); void runtime·gosched(void); diff --git a/src/pkg/runtime/sys_windows_386.s b/src/pkg/runtime/sys_windows_386.s index ab6d7f2209..dbc4352e2e 100644 --- a/src/pkg/runtime/sys_windows_386.s +++ b/src/pkg/runtime/sys_windows_386.s @@ -303,3 +303,15 @@ TEXT runtime·install_exception_handler(SB),7,$0 MOVL DX, 0(FS) RET + +// void remove_exception_handler() +TEXT runtime·remove_exception_handler(SB),7,$0 + get_tls(CX) + MOVL m(CX), CX // m + + // Remove SEH frame + MOVL m_seh(CX), DX + MOVL seh_prev(DX), AX + MOVL AX, 0(FS) + + RET diff --git a/src/pkg/runtime/sys_windows_amd64.s b/src/pkg/runtime/sys_windows_amd64.s index b2b8de5025..33ec33640b 100644 --- a/src/pkg/runtime/sys_windows_amd64.s +++ b/src/pkg/runtime/sys_windows_amd64.s @@ -343,3 +343,6 @@ TEXT runtime·settls(SB),7,$0 TEXT runtime·install_exception_handler(SB),7,$0 CALL runtime·setstacklimits(SB) RET + +TEXT runtime·remove_exception_handler(SB),7,$0 + RET diff --git a/src/pkg/runtime/thread_darwin.c b/src/pkg/runtime/thread_darwin.c index 83c638067d..1d6037b48b 100644 --- a/src/pkg/runtime/thread_darwin.c +++ b/src/pkg/runtime/thread_darwin.c @@ -121,6 +121,13 @@ runtime·minit(void) runtime·setprof(m->profilehz > 0); } +// Called from dropm to undo the effect of an minit. +void +runtime·unminit(void) +{ + runtime·signalstack(nil, 0); +} + // Mach IPC, to get at semaphores // Definitions are in /usr/include/mach on a Mac. diff --git a/src/pkg/runtime/thread_freebsd.c b/src/pkg/runtime/thread_freebsd.c index 861e6b0f70..4d5f69a9f3 100644 --- a/src/pkg/runtime/thread_freebsd.c +++ b/src/pkg/runtime/thread_freebsd.c @@ -130,6 +130,13 @@ runtime·minit(void) runtime·sigprocmask(&sigset_none, nil); } +// Called from dropm to undo the effect of an minit. +void +runtime·unminit(void) +{ + runtime·signalstack(nil, 0); +} + void runtime·sigpanic(void) { diff --git a/src/pkg/runtime/thread_linux.c b/src/pkg/runtime/thread_linux.c index fdd40c223e..02a5eaee2f 100644 --- a/src/pkg/runtime/thread_linux.c +++ b/src/pkg/runtime/thread_linux.c @@ -181,6 +181,13 @@ runtime·minit(void) runtime·rtsigprocmask(SIG_SETMASK, &sigset_none, nil, sizeof(Sigset)); } +// Called from dropm to undo the effect of an minit. +void +runtime·unminit(void) +{ + runtime·signalstack(nil, 0); +} + void runtime·sigpanic(void) { diff --git a/src/pkg/runtime/thread_netbsd.c b/src/pkg/runtime/thread_netbsd.c index bbe7df6e98..ebef45e757 100644 --- a/src/pkg/runtime/thread_netbsd.c +++ b/src/pkg/runtime/thread_netbsd.c @@ -199,6 +199,13 @@ runtime·minit(void) runtime·sigprocmask(SIG_SETMASK, &sigset_none, nil); } +// Called from dropm to undo the effect of an minit. +void +runtime·unminit(void) +{ + runtime·signalstack(nil, 0); +} + void runtime·sigpanic(void) { diff --git a/src/pkg/runtime/thread_openbsd.c b/src/pkg/runtime/thread_openbsd.c index ce8043f016..8433e8bae5 100644 --- a/src/pkg/runtime/thread_openbsd.c +++ b/src/pkg/runtime/thread_openbsd.c @@ -176,6 +176,13 @@ runtime·minit(void) runtime·sigprocmask(SIG_SETMASK, sigset_none); } +// Called from dropm to undo the effect of an minit. +void +runtime·unminit(void) +{ + runtime·signalstack(nil, 0); +} + void runtime·sigpanic(void) { diff --git a/src/pkg/runtime/thread_plan9.c b/src/pkg/runtime/thread_plan9.c index 932135dca8..bca0deac62 100644 --- a/src/pkg/runtime/thread_plan9.c +++ b/src/pkg/runtime/thread_plan9.c @@ -23,6 +23,13 @@ runtime·minit(void) runtime·setfpmasks(); } +// Called from dropm to undo the effect of an minit. +void +runtime·unminit(void) +{ +} + + static int32 getproccount(void) { @@ -82,6 +89,7 @@ runtime·initsig(void) { } +#pragma textflag 7 void runtime·osyield(void) { diff --git a/src/pkg/runtime/thread_windows.c b/src/pkg/runtime/thread_windows.c index 600a48ab62..7110c6efe4 100644 --- a/src/pkg/runtime/thread_windows.c +++ b/src/pkg/runtime/thread_windows.c @@ -135,6 +135,7 @@ runtime·write(int32 fd, void *buf, int32 n) return written; } +#pragma textflag 7 void runtime·osyield(void) { @@ -211,6 +212,13 @@ runtime·minit(void) runtime·install_exception_handler(); } +// Called from dropm to undo the effect of an minit. +void +runtime·unminit(void) +{ + runtime·remove_exception_handler(); +} + int64 runtime·nanotime(void) { -- 2.48.1