mirror of
https://github.com/golang/go
synced 2024-11-22 03:24:41 -07:00
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
This commit is contained in:
parent
43da336b15
commit
6c976393ae
@ -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) }
|
||||
|
44
misc/cgo/test/cthread.go
Normal file
44
misc/cgo/test/cthread.go
Normal file
@ -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)
|
||||
}
|
||||
}
|
34
misc/cgo/test/cthread_unix.c
Normal file
34
misc/cgo/test/cthread_unix.c
Normal file
@ -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 <pthread.h>
|
||||
#include "_cgo_export.h"
|
||||
|
||||
static void*
|
||||
addThread(void *p)
|
||||
{
|
||||
int i, max;
|
||||
|
||||
max = *(int*)p;
|
||||
for(i=0; i<max; i++)
|
||||
Add(i);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
doAdd(int max, int nthread)
|
||||
{
|
||||
enum { MaxThread = 20 };
|
||||
int i;
|
||||
pthread_t thread_id[MaxThread];
|
||||
|
||||
if(nthread > MaxThread)
|
||||
nthread = MaxThread;
|
||||
for(i=0; i<nthread; i++)
|
||||
pthread_create(&thread_id[i], 0, addThread, &max);
|
||||
for(i=0; i<nthread; i++)
|
||||
pthread_join(thread_id[i], 0);
|
||||
}
|
37
misc/cgo/test/cthread_windows.c
Normal file
37
misc/cgo/test/cthread_windows.c
Normal file
@ -0,0 +1,37 @@
|
||||
// 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.
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#include <process.h>
|
||||
#include "_cgo_export.h"
|
||||
|
||||
__stdcall
|
||||
static unsigned int
|
||||
addThread(void *p)
|
||||
{
|
||||
int i, max;
|
||||
|
||||
max = *(int*)p;
|
||||
for(i=0; i<max; i++)
|
||||
Add(i);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
doAdd(int max, int nthread)
|
||||
{
|
||||
enum { MaxThread = 20 };
|
||||
int i;
|
||||
uintptr_t thread_id[MaxThread];
|
||||
|
||||
if(nthread > MaxThread)
|
||||
nthread = MaxThread;
|
||||
for(i=0; i<nthread; i++)
|
||||
thread_id[i] = _beginthreadex(0, 0, addThread, &max, 0, 0);
|
||||
for(i=0; i<nthread; i++) {
|
||||
WaitForSingleObject((HANDLE)thread_id[i], INFINITE);
|
||||
CloseHandle((HANDLE)thread_id[i]);
|
||||
}
|
||||
}
|
@ -476,21 +476,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,$12
|
||||
MOVL fn+0(FP), AX
|
||||
MOVL frame+4(FP), BX
|
||||
MOVL framesize+8(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
|
||||
CMPL CX, $0
|
||||
JNE 3(PC)
|
||||
PUSHL $0
|
||||
JMP needm
|
||||
#endif
|
||||
MOVL m(CX), BP
|
||||
PUSHL BP
|
||||
CMPL BP, $0
|
||||
JNE havem
|
||||
needm:
|
||||
MOVL $runtime·needm(SB), AX
|
||||
CALL AX
|
||||
get_tls(CX)
|
||||
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.
|
||||
CMPL BP, $0
|
||||
JNE 2(PC)
|
||||
CALL runtime·badcallback(SB)
|
||||
|
||||
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)
|
||||
|
@ -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
|
||||
PUSHQ BP
|
||||
CMPQ BP, $0
|
||||
JNE havem
|
||||
needm:
|
||||
MOVQ $runtime·needm(SB), AX
|
||||
CALL AX
|
||||
get_tls(CX)
|
||||
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.
|
||||
CMPQ BP, $0
|
||||
JNE 2(PC)
|
||||
CALL runtime·badcallback(SB)
|
||||
|
||||
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)
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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)
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user