mirror of
https://github.com/golang/go
synced 2024-11-25 20:27:57 -07:00
start of FFI support, and a demo.
R=r DELTA=494 (492 added, 0 deleted, 2 changed) OCL=33784 CL=33810
This commit is contained in:
parent
165a99038f
commit
fdc4b4a47f
24
src/libcgo/Makefile
Normal file
24
src/libcgo/Makefile
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
# 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
|
||||||
|
|
278
src/libcgo/cgocall.c
Normal file
278
src/libcgo/cgocall.c
Normal file
@ -0,0 +1,278 @@
|
|||||||
|
// 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=\
|
|||||||
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=\
|
|||||||
$(OFILES_$(GOARCH))\
|
$(OFILES_$(GOARCH))\
|
||||||
|
|
||||||
HFILES=\
|
HFILES=\
|
||||||
|
cgocall.h\
|
||||||
runtime.h\
|
runtime.h\
|
||||||
hashmap.h\
|
hashmap.h\
|
||||||
malloc.h\
|
malloc.h\
|
||||||
|
38
src/pkg/runtime/cgocall.c
Normal file
38
src/pkg/runtime/cgocall.c
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
// 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);
|
||||||
|
}
|
39
src/pkg/runtime/cgocall.h
Normal file
39
src/pkg/runtime/cgocall.h
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
// 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 @@
|
|||||||
// 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
|
|||||||
MOVQ $_rt0_amd64(SB), AX
|
MOVQ $_rt0_amd64(SB), AX
|
||||||
JMP AX
|
JMP AX
|
||||||
|
|
||||||
GLOBL _initffi(SB), $8
|
GLOBL initcgo(SB), $8
|
||||||
|
20
usr/rsc/fib/6c.c
Normal file
20
usr/rsc/fib/6c.c
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
// 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);
|
||||||
|
}
|
36
usr/rsc/fib/Makefile
Normal file
36
usr/rsc/fib/Makefile
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
# 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
|
||||||
|
|
34
usr/rsc/fib/gcc.c
Normal file
34
usr/rsc/fib/gcc.c
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
// 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);
|
||||||
|
}
|
8
usr/rsc/fib/go.go
Normal file
8
usr/rsc/fib/go.go
Normal file
@ -0,0 +1,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.
|
||||||
|
|
||||||
|
package fib
|
||||||
|
|
||||||
|
func Fib(n int) int
|
||||||
|
|
13
usr/rsc/fib/main.go
Normal file
13
usr/rsc/fib/main.go
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
// 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));
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user