void runtime·cgocall(void (*fn)(void*), void *arg) { Defer d; if(m->racecall) { runtime·asmcgocall(fn, arg); return; } if(!runtime·iscgo && !Windows) runtime·throw("cgocall unavailable"); if(fn == 0) runtime·throw("cgocall nil"); if(raceenabled) runtime·racereleasemerge(&cgosync); // Create an extra M for callbacks on threads not created by Go on first cgo call. if(runtime·needextram && runtime·cas(&runtime·needextram, 1, 0)) runtime·newextram(); m->ncgocall++; /* * Lock g to m to ensure we stay on the same stack if we do a * cgo callback. Add entry to defer stack in case of panic. */ runtime·lockOSThread(); d.fn = &endcgoV; d.siz = 0; d.link = g->defer; d.argp = (void*)-1; // unused because unlockm never recovers d.special = true; d.free = false; g->defer = &d; m->ncgo++; /* * Announce we are entering a system call * so that the scheduler knows to create another * M to run goroutines while we are in the * foreign code. * * The call to asmcgocall is guaranteed not to * split the stack and does not allocate memory, * so it is safe to call while "in a system call", outside * the $GOMAXPROCS accounting. */ runtime·entersyscall(); runtime·asmcgocall(fn, arg); runtime·exitsyscall(); if(g->defer != &d || d.fn != &endcgoV) runtime·throw("runtime: bad defer entry in cgocallback"); g->defer = d.link; endcgo(); }
runtime·newm(void) { M *m; m = runtime·malloc(sizeof(M)); mcommoninit(m); if(runtime·iscgo) { CgoThreadStart ts; if(libcgo_thread_start == nil) runtime·throw("libcgo_thread_start missing"); // pthread_create will make us a stack. m->g0 = runtime·malg(-1); ts.m = m; ts.g = m->g0; ts.fn = runtime·mstart; runtime·asmcgocall(libcgo_thread_start, &ts); } else { if(Windows) // windows will layout sched stack on os stack m->g0 = runtime·malg(-1); else m->g0 = runtime·malg(8192); runtime·newosproc(m, m->g0, m->g0->stackbase, runtime·mstart); } return m; }
// Update the C environment if cgo is loaded. // Called from syscall.Setenv. void syscall·setenv_c(String k, String v) { byte *arg[2]; uintptr len; if(_cgo_setenv == nil) return; // Objects that are explicitly freed must be at least 16 bytes in size, // so that they are not allocated using tiny alloc. len = k.len + 1; if(len < TinySize) len = TinySize; arg[0] = runtime·malloc(len); runtime·memmove(arg[0], k.str, k.len); arg[0][k.len] = 0; len = v.len + 1; if(len < TinySize) len = TinySize; arg[1] = runtime·malloc(len); runtime·memmove(arg[1], v.str, v.len); arg[1][v.len] = 0; runtime·asmcgocall((void*)_cgo_setenv, arg); runtime·free(arg[0]); runtime·free(arg[1]); }
// Called to initialize a new m (including the bootstrap m). // Called on the new thread, can not allocate memory. void runtime·minit(void) { runtime·asmcgocall(runtime·miniterrno, (void *)libc·___errno); // Initialize signal handling runtime·signalstack((byte*)g->m->gsignal->stack.lo, 32*1024); runtime·sigprocmask(SIG_SETMASK, &sigset_none, nil); }
runtime·stdcall(void *fn, int32 count, ...) { WinCall c; c.fn = fn; c.n = count; c.args = (uintptr*)&count + 1; runtime·asmcgocall(runtime·asmstdcall, &c); return (void*)c.r1; }
void runtime·cgocall(void (*fn)(void*), void *arg) { Defer d; if(!runtime·iscgo && !Windows) runtime·throw("cgocall unavailable"); if(fn == 0) runtime·throw("cgocall nil"); m->ncgocall++; /* * Lock g to m to ensure we stay on the same stack if we do a * cgo callback. */ d.nofree = false; if(m->lockedg == nil) { m->lockedg = g; g->lockedm = m; // Add entry to defer stack in case of panic. d.fn = (byte*)unlockm; d.siz = 0; d.link = g->defer; d.argp = (void*)-1; // unused because unlockm never recovers d.nofree = true; g->defer = &d; } /* * Announce we are entering a system call * so that the scheduler knows to create another * M to run goroutines while we are in the * foreign code. * * The call to asmcgocall is guaranteed not to * split the stack and does not allocate memory, * so it is safe to call while "in a system call", outside * the $GOMAXPROCS accounting. */ runtime·entersyscall(); runtime·asmcgocall(fn, arg); runtime·exitsyscall(); if(d.nofree) { if(g->defer != &d || d.fn != (byte*)unlockm) runtime·throw("runtime: bad defer entry in cgocallback"); g->defer = d.link; unlockm(); } }
uintptr runtime·sysvicall6(uintptr fn, int32 count, ...) { runtime·memclr((byte*)&m->scratch, sizeof(m->scratch)); m->libcall.fn = (void*)fn; m->libcall.n = (uintptr)count; for(;count; count--) m->scratch.v[count - 1] = *((uintptr*)&count + count); m->libcall.args = (uintptr*)&m->scratch.v[0]; runtime·asmcgocall(runtime·asmsysvicall6, &m->libcall); return m->libcall.r1; }
int32 runtime·semasleep(int64 ns) { M *m; m = g->m; if(ns >= 0) { m->ts.tv_sec = ns / 1000000000LL; m->ts.tv_nsec = ns % 1000000000LL; m->libcall.fn = (uintptr)(void*)libc·sem_reltimedwait_np; m->libcall.n = 2; runtime·memclr((byte*)&m->scratch, sizeof(m->scratch)); m->scratch.v[0] = m->waitsema; m->scratch.v[1] = (uintptr)&m->ts; m->libcall.args = (uintptr)(uintptr*)&m->scratch; runtime·asmcgocall(runtime·asmsysvicall6, &m->libcall); if(*m->perrno != 0) { if(*m->perrno == ETIMEDOUT || *m->perrno == EAGAIN || *m->perrno == EINTR) return -1; runtime·throw("sem_reltimedwait_np"); } return 0; } for(;;) { m->libcall.fn = (uintptr)(void*)libc·sem_wait; m->libcall.n = 1; runtime·memclr((byte*)&m->scratch, sizeof(m->scratch)); m->scratch.v[0] = m->waitsema; m->libcall.args = (uintptr)(uintptr*)&m->scratch; runtime·asmcgocall(runtime·asmsysvicall6, &m->libcall); if(m->libcall.r1 == 0) break; if(*m->perrno == EINTR) continue; runtime·throw("sem_wait"); } return 0; }
runtime·stdcall(void *fn, int32 count, ...) { m->libcall.fn = fn; m->libcall.n = count; m->libcall.args = (uintptr*)&count + 1; if(m->profilehz != 0) { // leave pc/sp for cpu profiler m->libcallg = g; m->libcallpc = (uintptr)runtime·getcallerpc(&fn); // sp must be the last, because once async cpu profiler finds // all three values to be non-zero, it will use them m->libcallsp = (uintptr)runtime·getcallersp(&fn); } runtime·asmcgocall(runtime·asmstdcall, &m->libcall); m->libcallsp = 0; return (void*)m->libcall.r1; }
uintptr runtime·semacreate(void) { SemT* sem; // Call libc's malloc rather than runtime·malloc. This will // allocate space on the C heap. We can't call runtime·malloc // here because it could cause a deadlock. g->m->libcall.fn = (uintptr)(void*)libc·malloc; g->m->libcall.n = 1; runtime·memclr((byte*)&g->m->scratch, sizeof(g->m->scratch)); g->m->scratch.v[0] = (uintptr)sizeof(*sem); g->m->libcall.args = (uintptr)(uintptr*)&g->m->scratch; runtime·asmcgocall(runtime·asmsysvicall6, &g->m->libcall); sem = (void*)g->m->libcall.r1; if(runtime·sem_init(sem, 0, 0) != 0) runtime·throw("sem_init"); return (uintptr)sem; }
// Kick off new ms as needed (up to mcpumax). // There are already `other' other cpus that will // start looking for goroutines shortly. // Sched is locked. static void matchmg(void) { G *g; if(m->mallocing || m->gcing) return; while(runtime·sched.mcpu < runtime·sched.mcpumax && (g = gget()) != nil){ M *m; // Find the m that will run g. if((m = mget(g)) == nil){ m = runtime·malloc(sizeof(M)); // 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·allm = m; m->id = runtime·sched.mcount++; if(runtime·iscgo) { CgoThreadStart ts; if(libcgo_thread_start == nil) runtime·throw("libcgo_thread_start missing"); // pthread_create will make us a stack. m->g0 = runtime·malg(-1); ts.m = m; ts.g = m->g0; ts.fn = runtime·mstart; runtime·asmcgocall(libcgo_thread_start, &ts); } else { if(Windows) // windows will layout sched stack on os stack m->g0 = runtime·malg(-1); else m->g0 = runtime·malg(8192); runtime·newosproc(m, m->g0, m->g0->stackbase, runtime·mstart); } } mnextg(m, g); } }
// Update the C environment if cgo is loaded. // Called from syscall.Setenv. void syscall·setenv_c(String k, String v) { byte *arg[2]; if(libcgo_setenv == nil) return; arg[0] = runtime·malloc(k.len + 1); runtime·memmove(arg[0], k.str, k.len); arg[0][k.len] = 0; arg[1] = runtime·malloc(v.len + 1); runtime·memmove(arg[1], v.str, v.len); arg[1][v.len] = 0; runtime·asmcgocall((void*)libcgo_setenv, arg); runtime·free(arg[0]); runtime·free(arg[1]); }
runtime·newm(void) { M *mp; static Type *mtype; // The Go type M if(mtype == nil) { Eface e; runtime·gc_m_ptr(&e); mtype = ((PtrType*)e.type)->elem; } mp = runtime·cnew(mtype); mcommoninit(mp); 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); } return mp; }