root/src/pkg/runtime/cgocall.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. runtime·cgocall
  2. endcgo
  3. runtime·cmalloc
  4. runtime·cfree
  5. runtime·cgocallbackg
  6. runtime·cgocallbackg1
  7. unwindm
  8. runtime·badcgocallback
  9. runtime·cgounimpl

// 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 "runtime.h"
#include "arch_GOARCH.h"
#include "stack.h"
#include "cgocall.h"
#include "race.h"
#include "../../cmd/ld/textflag.h"

// Cgo call and callback support.
//
// To call into the C function f from Go, the cgo-generated code calls
// runtime.cgocall(_cgo_Cfunc_f, frame), where _cgo_Cfunc_f is a
// gcc-compiled function written by cgo.
//
// runtime.cgocall (below) locks g to m, calls entersyscall
// so as not to block other goroutines or the garbage collector,
// and then calls runtime.asmcgocall(_cgo_Cfunc_f, frame). 
//
// runtime.asmcgocall (in asm_$GOARCH.s) switches to the m->g0 stack
// (assumed to be an operating system-allocated stack, so safe to run
// gcc-compiled code on) and calls _cgo_Cfunc_f(frame).
//
// _cgo_Cfunc_f invokes the actual C function f with arguments
// taken from the frame structure, records the results in the frame,
// and returns to runtime.asmcgocall.
//
// After it regains control, runtime.asmcgocall switches back to the
// original g (m->curg)'s stack and returns to runtime.cgocall.
//
// After it regains control, runtime.cgocall calls exitsyscall, which blocks
// until this m can run Go code without violating the $GOMAXPROCS limit,
// and then unlocks g from m.
//
// The above description skipped over the possibility of the gcc-compiled
// function f calling back into Go.  If that happens, we continue down
// the rabbit hole during the execution of f.
//
// To make it possible for gcc-compiled C code to call a Go function p.GoF,
// cgo writes a gcc-compiled function named GoF (not p.GoF, since gcc doesn't
// know about packages).  The gcc-compiled C function f calls GoF.
//
// GoF calls crosscall2(_cgoexp_GoF, frame, framesize).  Crosscall2
// (in cgo/gcc_$GOARCH.S, a gcc-compiled assembly file) is a two-argument
// adapter from the gcc function call ABI to the 6c function call ABI.
// It is called from gcc to call 6c functions.  In this case it calls
// _cgoexp_GoF(frame, framesize), still running on m->g0's stack
// and outside the $GOMAXPROCS limit.  Thus, this code cannot yet
// call arbitrary Go code directly and must be careful not to allocate
// memory or use up m->g0's stack.
//
// _cgoexp_GoF calls runtime.cgocallback(p.GoF, frame, framesize).
// (The reason for having _cgoexp_GoF instead of writing a crosscall3
// to make this call directly is that _cgoexp_GoF, because it is compiled
// with 6c instead of gcc, can refer to dotted names like
// runtime.cgocallback and p.GoF.)
//
// runtime.cgocallback (in asm_$GOARCH.s) switches from m->g0's
// stack to the original g (m->curg)'s stack, on which it calls
// runtime.cgocallbackg(p.GoF, frame, framesize).
// As part of the stack switch, runtime.cgocallback saves the current
// SP as m->g0->sched.sp, so that any use of m->g0's stack during the
// execution of the callback will be done below the existing stack frames.
// Before overwriting m->g0->sched.sp, it pushes the old value on the
// m->g0 stack, so that it can be restored later.
//
// runtime.cgocallbackg (below) is now running on a real goroutine
// stack (not an m->g0 stack).  First it calls runtime.exitsyscall, which will
// block until the $GOMAXPROCS limit allows running this goroutine.
// Once exitsyscall has returned, it is safe to do things like call the memory
// allocator or invoke the Go callback function p.GoF.  runtime.cgocallbackg
// first defers a function to unwind m->g0.sched.sp, so that if p.GoF
// panics, m->g0.sched.sp will be restored to its old value: the m->g0 stack
// and the m->curg stack will be unwound in lock step.
// Then it calls p.GoF.  Finally it pops but does not execute the deferred
// function, calls runtime.entersyscall, and returns to runtime.cgocallback.
//
// After it regains control, runtime.cgocallback switches back to
// m->g0's stack (the pointer is still in m->g0.sched.sp), restores the old
// m->g0.sched.sp value from the stack, and returns to _cgoexp_GoF.
//
// _cgoexp_GoF immediately returns to crosscall2, which restores the
// callee-save registers for gcc and returns to GoF, which returns to f.

void *_cgo_init;        /* filled in by dynamic linker when Cgo is available */
static int64 cgosync;  /* represents possible synchronization in C code */

static void unwindm(void);

// Call from Go to C.

static void endcgo(void);
static FuncVal endcgoV = { endcgo };

void
runtime·cgocall(void (*fn)(void*), void *arg)
{
        Defer d;

        if(!runtime·iscgo && !Solaris && !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 = NoArgs;
        d.special = true;
        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();
}

static void
endcgo(void)
{
        runtime·unlockOSThread();
        m->ncgo--;
        if(m->ncgo == 0) {
                // We are going back to Go and are not in a recursive
                // call.  Let the GC collect any memory allocated via
                // _cgo_allocate that is no longer referenced.
                m->cgomal = nil;
        }

        if(raceenabled)
                runtime·raceacquire(&cgosync);
}

// Helper functions for cgo code.

void (*_cgo_malloc)(void*);
void (*_cgo_free)(void*);

void*
runtime·cmalloc(uintptr n)
{
        struct {
                uint64 n;
                void *ret;
        } a;

        a.n = n;
        a.ret = nil;
        runtime·cgocall(_cgo_malloc, &a);
        if(a.ret == nil)
                runtime·throw("runtime: C malloc failed");
        return a.ret;
}

void
runtime·cfree(void *p)
{
        runtime·cgocall(_cgo_free, p);
}

// Call from C back to Go.

static FuncVal unwindmf = {unwindm};

typedef struct CallbackArgs CallbackArgs;
struct CallbackArgs
{
        FuncVal *fn;
        void *arg;
        uintptr argsize;
};

// Location of callback arguments depends on stack frame layout
// and size of stack frame of cgocallback_gofunc.

// On arm, stack frame is two words and there's a saved LR between
// SP and the stack frame and between the stack frame and the arguments.
#ifdef GOARCH_arm
#define CBARGS (CallbackArgs*)((byte*)m->g0->sched.sp+4*sizeof(void*))
#endif

// On amd64, stack frame is one word, plus caller PC.
#ifdef GOARCH_amd64
#define CBARGS (CallbackArgs*)((byte*)m->g0->sched.sp+2*sizeof(void*))
#endif

// Unimplemented on amd64p32
#ifdef GOARCH_amd64p32
#define CBARGS (CallbackArgs*)(nil)
#endif

// On 386, stack frame is three words, plus caller PC.
#ifdef GOARCH_386
#define CBARGS (CallbackArgs*)((byte*)m->g0->sched.sp+4*sizeof(void*))
#endif

void runtime·cgocallbackg1(void);

#pragma textflag NOSPLIT
void
runtime·cgocallbackg(void)
{
        if(g != m->curg) {
                runtime·prints("runtime: bad g in cgocallback");
                runtime·exit(2);
        }

        runtime·exitsyscall(); // coming out of cgo call
        runtime·cgocallbackg1();
        runtime·entersyscall();        // going back to cgo call
}

void
runtime·cgocallbackg1(void)
{
        CallbackArgs *cb;
        Defer d;

        if(m->needextram) {
                m->needextram = 0;
                runtime·newextram();
        }

        // Add entry to defer stack in case of panic.
        d.fn = &unwindmf;
        d.siz = 0;
        d.link = g->defer;
        d.argp = NoArgs;
        d.special = true;
        g->defer = &d;

        if(raceenabled)
                runtime·raceacquire(&cgosync);

        // Invoke callback.
        cb = CBARGS;
        runtime·newstackcall(cb->fn, cb->arg, cb->argsize);

        if(raceenabled)
                runtime·racereleasemerge(&cgosync);

        // Pop defer.
        // Do not unwind m->g0->sched.sp.
        // Our caller, cgocallback, will do that.
        if(g->defer != &d || d.fn != &unwindmf)
                runtime·throw("runtime: bad defer entry in cgocallback");
        g->defer = d.link;
}

static void
unwindm(void)
{
        // Restore sp saved by cgocallback during
        // unwind of g's stack (see comment at top of file).
        switch(thechar){
        default:
                runtime·throw("runtime: unwindm not implemented");
        case '8':
        case '6':
                m->g0->sched.sp = *(uintptr*)m->g0->sched.sp;
                break;
        case '5':
                m->g0->sched.sp = *(uintptr*)((byte*)m->g0->sched.sp + 4);
                break;
        }
}

void
runtime·badcgocallback(void)   // called from assembly
{
        runtime·throw("runtime: misaligned stack in cgocallback");
}

void
runtime·cgounimpl(void)        // called from (incomplete) assembly
{
        runtime·throw("runtime: cgo not implemented");
}

// For cgo-using programs with external linking,
// export "main" (defined in assembly) so that libc can handle basic
// C runtime startup and call the Go program as if it were
// the C main function.
#pragma cgo_export_static main

/* [<][>][^][v][top][bottom][index][help] */