Commit fdc4b4a4 authored by Russ Cox's avatar Russ Cox

start of FFI support, and a demo.

R=r
DELTA=494  (492 added, 0 deleted, 2 changed)
OCL=33784
CL=33810
parent 165a9903
# 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.
# not linked into build for now
TARG=libcgo.so
all: libcgo.so
cgocall.o: cgocall.c
gcc -O2 -fPIC -o cgocall.o -c cgocall.c
libcgo.so: cgocall.o
gcc -shared -o libcgo.so cgocall.o -lpthread -lm
install: $(GOROOT)/pkg/$(GOOS)_$(GOARCH)/libcgo.so
$(GOROOT)/pkg/$(GOOS)_$(GOARCH)/libcgo.so: libcgo.so
cp libcgo.so $@
clean:
rm -f *.o *.so
// 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.
#define _GNU_SOURCE
#include <stdio.h>
#include <errno.h>
#include <linux/futex.h>
#include <sys/syscall.h>
#include <sys/time.h>
#include <pthread.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#define nil ((void*)0)
/*
* gcc implementation of src/pkg/runtime/linux/thread.c
*/
typedef struct Lock Lock;
typedef struct Note Note;
typedef uint32_t uint32;
struct Lock
{
uint32 key;
uint32 sema; // ignored
};
struct Note
{
Lock lock;
uint32 pad;
};
static struct timespec longtime =
{
1<<30, // 34 years
0
};
static int
cas(uint32 *val, uint32 old, uint32 new)
{
int ret;
__asm__ __volatile__(
"lock; cmpxchgl %2, 0(%3)\n"
"setz %%al\n"
: "=a" (ret)
: "a" (old),
"r" (new),
"r" (val)
: "memory", "cc"
);
return ret & 1;
}
static void
futexsleep(uint32 *addr, uint32 val)
{
int ret;
ret = syscall(SYS_futex, (int*)addr, FUTEX_WAIT, val, &longtime, nil, 0);
if(ret >= 0 || errno == EAGAIN || errno == EINTR)
return;
fprintf(stderr, "futexsleep: %s\n", strerror(errno));
*(int*)0 = 0;
}
static void
futexwakeup(uint32 *addr)
{
int ret;
ret = syscall(SYS_futex, (int*)addr, FUTEX_WAKE, 1, nil, nil, 0);
if(ret >= 0)
return;
fprintf(stderr, "futexwakeup: %s\n", strerror(errno));
*(int*)0 = 0;
}
static void
futexlock(Lock *l)
{
uint32 v;
again:
v = l->key;
if((v&1) == 0){
if(cas(&l->key, v, v|1)){
// Lock wasn't held; we grabbed it.
return;
}
goto again;
}
if(!cas(&l->key, v, v+2))
goto again;
futexsleep(&l->key, v+2);
for(;;){
v = l->key;
if((int)v < 2) {
fprintf(stderr, "futexsleep: invalid key %d\n", (int)v);
*(int*)0 = 0;
}
if(cas(&l->key, v, v-2))
break;
}
goto again;
}
static void
futexunlock(Lock *l)
{
uint32 v;
again:
v = l->key;
if((v&1) == 0)
*(int*)0 = 0;
if(!cas(&l->key, v, v&~1))
goto again;
// If there were waiters, wake one.
if(v & ~1)
futexwakeup(&l->key);
}
static void
lock(Lock *l)
{
futexlock(l);
}
static void
unlock(Lock *l)
{
futexunlock(l);
}
void
noteclear(Note *n)
{
n->lock.key = 0;
futexlock(&n->lock);
}
static void
notewakeup(Note *n)
{
futexunlock(&n->lock);
}
static void
notesleep(Note *n)
{
futexlock(&n->lock);
futexunlock(&n->lock);
}
/*
* runtime Cgo server.
* gcc half of src/pkg/runtime/cgocall.c
*/
typedef struct CgoWork CgoWork;
typedef struct CgoServer CgoServer;
typedef struct Cgo Cgo;
struct Cgo
{
Lock lock;
CgoServer *idle;
CgoWork *whead;
CgoWork *wtail;
};
struct CgoServer
{
CgoServer *next;
Note note;
CgoWork *work;
};
struct CgoWork
{
CgoWork *next;
Note note;
void (*fn)(void*);
void *arg;
};
Cgo cgo;
static void newserver(void);
void
initcgo(void)
{
newserver();
}
static void* go_pthread(void*);
/*
* allocate servers to handle any work that has piled up
* and one more server to sit idle and wait for new work.
*/
static void
newserver(void)
{
CgoServer *f;
CgoWork *w, *next;
pthread_t p;
lock(&cgo.lock);
if(cgo.idle == nil) {
// kick off new servers with work to do
for(w=cgo.whead; w; w=next) {
next = w;
w->next = nil;
f = malloc(sizeof *f);
memset(f, 0, sizeof *f);
f->work = w;
noteclear(&f->note);
notewakeup(&f->note);
if(pthread_create(&p, nil, go_pthread, f) < 0) {
fprintf(stderr, "pthread_create: %s\n", strerror(errno));
*(int*)0 = 0;
}
}
cgo.whead = nil;
cgo.wtail = nil;
// kick off one more server to sit idle
f = malloc(sizeof *f);
memset(f, 0, sizeof *f);
f->next = cgo.idle;
noteclear(&f->note);
cgo.idle = f;
if(pthread_create(&p, nil, go_pthread, f) < 0) {
fprintf(stderr, "pthread_create: %s\n", strerror(errno));
*(int*)0 = 0;
}
}
unlock(&cgo.lock);
}
static void*
go_pthread(void *v)
{
CgoServer *f;
CgoWork *w;
f = v;
for(;;) {
// wait for work
notesleep(&f->note);
// do work
w = f->work;
w->fn(w->arg);
notewakeup(&w->note);
// queue f on idle list
f->work = nil;
noteclear(&f->note);
lock(&cgo.lock);
f->next = cgo.idle;
cgo.idle = f;
unlock(&cgo.lock);
}
}
...@@ -37,6 +37,7 @@ OFILES_arm=\ ...@@ -37,6 +37,7 @@ OFILES_arm=\
OFILES=\ OFILES=\
array.$O\ array.$O\
asm.$O\ asm.$O\
cgocall.$O\
chan.$O\ chan.$O\
closure.$O\ closure.$O\
float.$O\ float.$O\
...@@ -67,6 +68,7 @@ OFILES=\ ...@@ -67,6 +68,7 @@ OFILES=\
$(OFILES_$(GOARCH))\ $(OFILES_$(GOARCH))\
HFILES=\ HFILES=\
cgocall.h\
runtime.h\ runtime.h\
hashmap.h\ hashmap.h\
malloc.h\ malloc.h\
......
// 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 "cgocall.h"
Cgo *cgo; /* filled in by dynamic linker when Cgo is available */
void
cgocall(void (*fn)(void*), void *arg)
{
CgoWork w;
CgoServer *s;
if(cgo == nil)
throw("cgocall unavailable");
noteclear(&w.note);
w.next = nil;
w.fn = fn;
w.arg = arg;
lock(&cgo->lock);
if((s = cgo->idle) != nil) {
cgo->idle = s->next;
s->work = &w;
unlock(&cgo->lock);
notewakeup(&s->note);
} else {
if(cgo->whead == nil) {
cgo->whead = &w;
} else
cgo->wtail->next = &w;
cgo->wtail = &w;
unlock(&cgo->lock);
}
notesleep(&w.note);
}
// 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.
/*
* Cgo interface.
* Dynamically linked shared libraries compiled with gcc
* know these data structures too. See ../../libcgo/cgocall.c
*/
typedef struct CgoWork CgoWork;
typedef struct CgoServer CgoServer;
typedef struct Cgo Cgo;
struct Cgo
{
Lock lock;
CgoServer *idle;
CgoWork *whead;
CgoWork *wtail;
};
struct CgoServer
{
CgoServer *next;
Note note;
CgoWork *work;
};
struct CgoWork
{
CgoWork *next;
Note note;
void (*fn)(void*);
void *arg;
};
void cgocall(void (*fn)(void*), void*);
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
// Darwin and Linux use the same linkage to main // Darwin and Linux use the same linkage to main
TEXT _rt0_amd64_linux(SB),7,$-8 TEXT _rt0_amd64_linux(SB),7,$-8
MOVQ _initffi(SB), AX MOVQ initcgo(SB), AX
TESTQ AX, AX TESTQ AX, AX
JZ 2(PC) JZ 2(PC)
CALL AX CALL AX
...@@ -13,4 +13,4 @@ TEXT _rt0_amd64_linux(SB),7,$-8 ...@@ -13,4 +13,4 @@ TEXT _rt0_amd64_linux(SB),7,$-8
MOVQ $_rt0_amd64(SB), AX MOVQ $_rt0_amd64(SB), AX
JMP AX JMP AX
GLOBL _initffi(SB), $8 GLOBL initcgo(SB), $8
// 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 "cgocall.h"
// turn on ffi
#pragma dynld initcgo initcgo "libcgo.so"
#pragma dynld cgo cgo "libcgo.so"
// pull in fib from fib.so
#pragma dynld extern_c_fib fib "fib.so"
void (*extern_c_fib)(void*);
void
fib·Fib(int32 n, int32, int32)
{
cgocall(extern_c_fib, &n);
}
# 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.
# FFI demo
all: fib.a fib.so
gcc.o: gcc.c
gcc -fPIC -O2 -o gcc.o -c gcc.c
fib.so: gcc.o
gcc -shared -o fib.so gcc.o -L$(GOROOT)/pkg/$(GOOS)_$(GOARCH) -lcgo
fib.a: 6c.6 go.6
gopack grc fib.a 6c.6 go.6
6c.6: 6c.c
6c -FVw -I$(GOROOT)/src/pkg/runtime 6c.c
go.6: go.go
6g go.go
PKG=$(GOROOT)/pkg/$(GOOS)_$(GOARCH)
install: $(PKG)/fib.so $(PKG)/fib.a
$(PKG)/fib.so: fib.so
cp fib.so $@
$(PKG)/fib.a: fib.a
cp fib.a $@
clean:
rm -f *.6 *.o *.so *.a
// 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 <stdint.h>
typedef int32_t int32;
static int32
fib1(int32 n)
{
int32 a, b, t;
a = 0;
b = 1;
for(; n>0; n--) {
t = a;
a = b;
b += t;
}
return a;
}
void
fib(void *v)
{
struct { // 6g func(n int) int
int32 n;
int32 pad;
int32 ret;
} *args = v;
args->ret = fib1(args->n);
}
// 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.
package fib
func Fib(n int) int
// 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.
package main
import "fib"
func main() {
for i := 0; i < 10; i++ {
println(fib.Fib(i));
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment