diff options
Diffstat (limited to 'misc/cgo')
31 files changed, 1675 insertions, 0 deletions
diff --git a/misc/cgo/gmp/Makefile b/misc/cgo/gmp/Makefile new file mode 100644 index 000000000..fc6209f27 --- /dev/null +++ b/misc/cgo/gmp/Makefile @@ -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 ../../../src/Make.inc + +TARG=gmp + +# Can have plain GOFILES too, but this example doesn't. + +CGOFILES=\ + gmp.go + +CGO_LDFLAGS=-lgmp + +# To add flags necessary for locating the library or its include files, +# set CGO_CFLAGS or CGO_LDFLAGS. For example, to use an +# alternate installation of the library: +# CGO_CFLAGS=-I/home/rsc/gmp32/include +# CGO_LDFLAGS+=-L/home/rsc/gmp32/lib +# Note the += on the second line. + +CLEANFILES+=pi fib + +include ../../../src/Make.pkg + +# Simple test programs + +# Computes 1000 digits of pi; single-threaded. +pi: install pi.go + $(GC) pi.go + $(LD) -o $@ pi.$O + +# Computes 200 Fibonacci numbers; multi-threaded. +fib: install fib.go + $(GC) fib.go + $(LD) -o $@ fib.$O + diff --git a/misc/cgo/gmp/fib.go b/misc/cgo/gmp/fib.go new file mode 100644 index 000000000..3eda39e17 --- /dev/null +++ b/misc/cgo/gmp/fib.go @@ -0,0 +1,43 @@ +// 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. + +// Compute Fibonacci numbers with two goroutines +// that pass integers back and forth. No actual +// concurrency, just threads and synchronization +// and foreign code on multiple pthreads. + +package main + +import ( + big "gmp" + "runtime" +) + +func fibber(c chan *big.Int, out chan string, n int64) { + // Keep the fibbers in dedicated operating system + // threads, so that this program tests coordination + // between pthreads and not just goroutines. + runtime.LockOSThread() + + i := big.NewInt(n) + if n == 0 { + c <- i + } + for { + j := <-c + out <- j.String() + i.Add(i, j) + c <- i + } +} + +func main() { + c := make(chan *big.Int) + out := make(chan string) + go fibber(c, out, 0) + go fibber(c, out, 1) + for i := 0; i < 200; i++ { + println(<-out) + } +} diff --git a/misc/cgo/gmp/gmp.go b/misc/cgo/gmp/gmp.go new file mode 100644 index 000000000..3dbc022ce --- /dev/null +++ b/misc/cgo/gmp/gmp.go @@ -0,0 +1,368 @@ +// 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. + +/* +An example of wrapping a C library in Go. This is the GNU +multiprecision library gmp's integer type mpz_t wrapped to look like +the Go package big's integer type Int. + +This is a syntactically valid Go program—it can be parsed with the Go +parser and processed by godoc—but it is not compiled directly by 6g. +Instead, a separate tool, cgo, processes it to produce three output +files. The first two, 6g.go and 6c.c, are a Go source file for 6g and +a C source file for 6c; both compile as part of the named package +(gmp, in this example). The third, gcc.c, is a C source file for gcc; +it compiles into a shared object (.so) that is dynamically linked into +any 6.out that imports the first two files. + +The stanza + + // #include <gmp.h> + import "C" + +is a signal to cgo. The doc comment on the import of "C" provides +additional context for the C file. Here it is just a single #include +but it could contain arbitrary C definitions to be imported and used. + +Cgo recognizes any use of a qualified identifier C.xxx and uses gcc to +find the definition of xxx. If xxx is a type, cgo replaces C.xxx with +a Go translation. C arithmetic types translate to precisely-sized Go +arithmetic types. A C struct translates to a Go struct, field by +field; unrepresentable fields are replaced with opaque byte arrays. A +C union translates into a struct containing the first union member and +perhaps additional padding. C arrays become Go arrays. C pointers +become Go pointers. C function pointers become Go's uintptr. +C void pointer's become Go's unsafe.Pointer. + +For example, mpz_t is defined in <gmp.h> as: + + typedef unsigned long int mp_limb_t; + + typedef struct + { + int _mp_alloc; + int _mp_size; + mp_limb_t *_mp_d; + } __mpz_struct; + + typedef __mpz_struct mpz_t[1]; + +Cgo generates: + + type _C_int int32 + type _C_mp_limb_t uint64 + type _C___mpz_struct struct { + _mp_alloc _C_int; + _mp_size _C_int; + _mp_d *_C_mp_limb_t; + } + type _C_mpz_t [1]_C___mpz_struct + +and then replaces each occurrence of a type C.xxx with _C_xxx. + +If xxx is data, cgo arranges for C.xxx to refer to the C variable, +with the type translated as described above. To do this, cgo must +introduce a Go variable that points at the C variable (the linker can +be told to initialize this pointer). For example, if the gmp library +provided + + mpz_t zero; + +then cgo would rewrite a reference to C.zero by introducing + + var _C_zero *C.mpz_t + +and then replacing all instances of C.zero with (*_C_zero). + +Cgo's most interesting translation is for functions. If xxx is a C +function, then cgo rewrites C.xxx into a new function _C_xxx that +calls the C xxx in a standard pthread. The new function translates +its arguments, calls xxx, and translates the return value. + +Translation of parameters and the return value follows the type +translation above except that arrays passed as parameters translate +explicitly in Go to pointers to arrays, as they do (implicitly) in C. + +Garbage collection is the big problem. It is fine for the Go world to +have pointers into the C world and to free those pointers when they +are no longer needed. To help, the Go code can define Go objects +holding the C pointers and use runtime.SetFinalizer on those Go objects. + +It is much more difficult for the C world to have pointers into the Go +world, because the Go garbage collector is unaware of the memory +allocated by C. The most important consideration is not to +constrain future implementations, so the rule is that Go code can +hand a Go pointer to C code but must separately arrange for +Go to hang on to a reference to the pointer until C is done with it. +*/ +package gmp + +// #include <gmp.h> +// #include <stdlib.h> +import "C" + +import ( + "os" + "unsafe" +) + +/* + * one of a kind + */ + +// An Int represents a signed multi-precision integer. +// The zero value for an Int represents the value 0. +type Int struct { + i C.mpz_t + init bool +} + +// NewInt returns a new Int initialized to x. +func NewInt(x int64) *Int { return new(Int).SetInt64(x) } + +// Int promises that the zero value is a 0, but in gmp +// the zero value is a crash. To bridge the gap, the +// init bool says whether this is a valid gmp value. +// doinit initializes z.i if it needs it. This is not inherent +// to FFI, just a mismatch between Go's convention of +// making zero values useful and gmp's decision not to. +func (z *Int) doinit() { + if z.init { + return + } + z.init = true + C.mpz_init(&z.i[0]) +} + +// Bytes returns z's representation as a big-endian byte array. +func (z *Int) Bytes() []byte { + b := make([]byte, (z.Len()+7)/8) + n := C.size_t(len(b)) + C.mpz_export(unsafe.Pointer(&b[0]), &n, 1, 1, 1, 0, &z.i[0]) + return b[0:n] +} + +// Len returns the length of z in bits. 0 is considered to have length 1. +func (z *Int) Len() int { + z.doinit() + return int(C.mpz_sizeinbase(&z.i[0], 2)) +} + +// Set sets z = x and returns z. +func (z *Int) Set(x *Int) *Int { + z.doinit() + C.mpz_set(&z.i[0], &x.i[0]) + return z +} + +// SetBytes interprets b as the bytes of a big-endian integer +// and sets z to that value. +func (z *Int) SetBytes(b []byte) *Int { + z.doinit() + if len(b) == 0 { + z.SetInt64(0) + } else { + C.mpz_import(&z.i[0], C.size_t(len(b)), 1, 1, 1, 0, unsafe.Pointer(&b[0])) + } + return z +} + +// SetInt64 sets z = x and returns z. +func (z *Int) SetInt64(x int64) *Int { + z.doinit() + // TODO(rsc): more work on 32-bit platforms + C.mpz_set_si(&z.i[0], C.long(x)) + return z +} + +// SetString interprets s as a number in the given base +// and sets z to that value. The base must be in the range [2,36]. +// SetString returns an error if s cannot be parsed or the base is invalid. +func (z *Int) SetString(s string, base int) os.Error { + z.doinit() + if base < 2 || base > 36 { + return os.EINVAL + } + p := C.CString(s) + defer C.free(unsafe.Pointer(p)) + if C.mpz_set_str(&z.i[0], p, C.int(base)) < 0 { + return os.EINVAL + } + return nil +} + +// String returns the decimal representation of z. +func (z *Int) String() string { + if z == nil { + return "nil" + } + z.doinit() + p := C.mpz_get_str(nil, 10, &z.i[0]) + s := C.GoString(p) + C.free(unsafe.Pointer(p)) + return s +} + +func (z *Int) destroy() { + if z.init { + C.mpz_clear(&z.i[0]) + } + z.init = false +} + +/* + * arithmetic + */ + +// Add sets z = x + y and returns z. +func (z *Int) Add(x, y *Int) *Int { + x.doinit() + y.doinit() + z.doinit() + C.mpz_add(&z.i[0], &x.i[0], &y.i[0]) + return z +} + +// Sub sets z = x - y and returns z. +func (z *Int) Sub(x, y *Int) *Int { + x.doinit() + y.doinit() + z.doinit() + C.mpz_sub(&z.i[0], &x.i[0], &y.i[0]) + return z +} + +// Mul sets z = x * y and returns z. +func (z *Int) Mul(x, y *Int) *Int { + x.doinit() + y.doinit() + z.doinit() + C.mpz_mul(&z.i[0], &x.i[0], &y.i[0]) + return z +} + +// Div sets z = x / y, rounding toward zero, and returns z. +func (z *Int) Div(x, y *Int) *Int { + x.doinit() + y.doinit() + z.doinit() + C.mpz_tdiv_q(&z.i[0], &x.i[0], &y.i[0]) + return z +} + +// Mod sets z = x % y and returns z. +// Like the result of the Go % operator, z has the same sign as x. +func (z *Int) Mod(x, y *Int) *Int { + x.doinit() + y.doinit() + z.doinit() + C.mpz_tdiv_r(&z.i[0], &x.i[0], &y.i[0]) + return z +} + +// Lsh sets z = x << s and returns z. +func (z *Int) Lsh(x *Int, s uint) *Int { + x.doinit() + z.doinit() + C.mpz_mul_2exp(&z.i[0], &x.i[0], C.mp_bitcnt_t(s)) + return z +} + +// Rsh sets z = x >> s and returns z. +func (z *Int) Rsh(x *Int, s uint) *Int { + x.doinit() + z.doinit() + C.mpz_div_2exp(&z.i[0], &x.i[0], C.mp_bitcnt_t(s)) + return z +} + +// Exp sets z = x^y % m and returns z. +// If m == nil, Exp sets z = x^y. +func (z *Int) Exp(x, y, m *Int) *Int { + m.doinit() + x.doinit() + y.doinit() + z.doinit() + if m == nil { + C.mpz_pow_ui(&z.i[0], &x.i[0], C.mpz_get_ui(&y.i[0])) + } else { + C.mpz_powm(&z.i[0], &x.i[0], &y.i[0], &m.i[0]) + } + return z +} + +func (z *Int) Int64() int64 { + if !z.init { + return 0 + } + return int64(C.mpz_get_si(&z.i[0])) +} + +// Neg sets z = -x and returns z. +func (z *Int) Neg(x *Int) *Int { + x.doinit() + z.doinit() + C.mpz_neg(&z.i[0], &x.i[0]) + return z +} + +// Abs sets z to the absolute value of x and returns z. +func (z *Int) Abs(x *Int) *Int { + x.doinit() + z.doinit() + C.mpz_abs(&z.i[0], &x.i[0]) + return z +} + +/* + * functions without a clear receiver + */ + +// CmpInt compares x and y. The result is +// +// -1 if x < y +// 0 if x == y +// +1 if x > y +// +func CmpInt(x, y *Int) int { + x.doinit() + y.doinit() + switch cmp := C.mpz_cmp(&x.i[0], &y.i[0]); { + case cmp < 0: + return -1 + case cmp == 0: + return 0 + } + return +1 +} + +// DivModInt sets q = x / y and r = x % y. +func DivModInt(q, r, x, y *Int) { + q.doinit() + r.doinit() + x.doinit() + y.doinit() + C.mpz_tdiv_qr(&q.i[0], &r.i[0], &x.i[0], &y.i[0]) +} + +// GcdInt sets d to the greatest common divisor of a and b, +// which must be positive numbers. +// If x and y are not nil, GcdInt sets x and y such that d = a*x + b*y. +// If either a or b is not positive, GcdInt sets d = x = y = 0. +func GcdInt(d, x, y, a, b *Int) { + d.doinit() + x.doinit() + y.doinit() + a.doinit() + b.doinit() + C.mpz_gcdext(&d.i[0], &x.i[0], &y.i[0], &a.i[0], &b.i[0]) +} + +// ProbablyPrime performs n Miller-Rabin tests to check whether z is prime. +// If it returns true, z is prime with probability 1 - 1/4^n. +// If it returns false, z is not prime. +func (z *Int) ProbablyPrime(n int) bool { + z.doinit() + return int(C.mpz_probab_prime_p(&z.i[0], C.int(n))) > 0 +} diff --git a/misc/cgo/gmp/pi.go b/misc/cgo/gmp/pi.go new file mode 100644 index 000000000..45f61abbd --- /dev/null +++ b/misc/cgo/gmp/pi.go @@ -0,0 +1,104 @@ +/* +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + * Neither the name of "The Computer Language Benchmarks Game" nor the + name of "The Computer Language Shootout Benchmarks" nor the names of + its contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +*/ + +/* The Computer Language Benchmarks Game + * http://shootout.alioth.debian.org/ + * + * contributed by The Go Authors. + * based on pidigits.c (by Paolo Bonzini & Sean Bartlett, + * modified by Michael Mellor) + */ + +package main + +import ( + big "gmp" + "fmt" + "runtime" +) + +var ( + tmp1 = big.NewInt(0) + tmp2 = big.NewInt(0) + numer = big.NewInt(1) + accum = big.NewInt(0) + denom = big.NewInt(1) + ten = big.NewInt(10) +) + +func extractDigit() int64 { + if big.CmpInt(numer, accum) > 0 { + return -1 + } + tmp1.Lsh(numer, 1).Add(tmp1, numer).Add(tmp1, accum) + big.DivModInt(tmp1, tmp2, tmp1, denom) + tmp2.Add(tmp2, numer) + if big.CmpInt(tmp2, denom) >= 0 { + return -1 + } + return tmp1.Int64() +} + +func nextTerm(k int64) { + y2 := k*2 + 1 + accum.Add(accum, tmp1.Lsh(numer, 1)) + accum.Mul(accum, tmp1.SetInt64(y2)) + numer.Mul(numer, tmp1.SetInt64(k)) + denom.Mul(denom, tmp1.SetInt64(y2)) +} + +func eliminateDigit(d int64) { + accum.Sub(accum, tmp1.Mul(denom, tmp1.SetInt64(d))) + accum.Mul(accum, ten) + numer.Mul(numer, ten) +} + +func main() { + i := 0 + k := int64(0) + for { + d := int64(-1) + for d < 0 { + k++ + nextTerm(k) + d = extractDigit() + } + eliminateDigit(d) + fmt.Printf("%c", d+'0') + + if i++; i%50 == 0 { + fmt.Printf("\n") + if i >= 1000 { + break + } + } + } + + fmt.Printf("\n%d calls; bit sizes: %d %d %d\n", runtime.Cgocalls(), numer.Len(), accum.Len(), denom.Len()) +} diff --git a/misc/cgo/life/Makefile b/misc/cgo/life/Makefile new file mode 100644 index 000000000..5a10380ed --- /dev/null +++ b/misc/cgo/life/Makefile @@ -0,0 +1,21 @@ +# Copyright 2010 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 ../../../src/Make.inc + +TARG=life + +CGOFILES=\ + life.go\ + +CGO_OFILES=\ + c-life.o\ + +CLEANFILES+=life + +include ../../../src/Make.pkg + +life: install main.go + $(GC) main.go + $(LD) -o $@ main.$O diff --git a/misc/cgo/life/c-life.c b/misc/cgo/life/c-life.c new file mode 100644 index 000000000..657245595 --- /dev/null +++ b/misc/cgo/life/c-life.c @@ -0,0 +1,56 @@ +// Copyright 2010 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 <assert.h> +#include "life.h" +#include "_cgo_export.h" + +const int MYCONST = 0; + +// Do the actual manipulation of the life board in C. This could be +// done easily in Go, we are just using C for demonstration +// purposes. +void +Step(int x, int y, int *a, int *n) +{ + struct GoStart_return r; + + // Use Go to start 4 goroutines each of which handles 1/4 of the + // board. + r = GoStart(0, x, y, 0, x / 2, 0, y / 2, a, n); + assert(r.r0 == 0 && r.r1 == 100); // test multiple returns + r = GoStart(1, x, y, x / 2, x, 0, y / 2, a, n); + assert(r.r0 == 1 && r.r1 == 101); // test multiple returns + GoStart(2, x, y, 0, x / 2, y / 2, y, a, n); + GoStart(3, x, y, x / 2, x, y / 2, y, a, n); + GoWait(0); + GoWait(1); + GoWait(2); + GoWait(3); +} + +// The actual computation. This is called in parallel. +void +DoStep(int xdim, int ydim, int xstart, int xend, int ystart, int yend, int *a, int *n) +{ + int x, y, c, i, j; + + for(x = xstart; x < xend; x++) { + for(y = ystart; y < yend; y++) { + c = 0; + for(i = -1; i <= 1; i++) { + for(j = -1; j <= 1; j++) { + if(x+i >= 0 && x+i < xdim && + y+j >= 0 && y+j < ydim && + (i != 0 || j != 0)) + c += a[(x+i)*xdim + (y+j)] != 0; + } + } + if(c == 3 || (c == 2 && a[x*xdim + y] != 0)) + n[x*xdim + y] = 1; + else + n[x*xdim + y] = 0; + } + } +} diff --git a/misc/cgo/life/golden.out b/misc/cgo/life/golden.out new file mode 100644 index 000000000..539d2106d --- /dev/null +++ b/misc/cgo/life/golden.out @@ -0,0 +1,17 @@ +* life + + + XXX XXX + + + + + + + + XXX XXX + + + + + diff --git a/misc/cgo/life/life.go b/misc/cgo/life/life.go new file mode 100644 index 000000000..ec000ce3a --- /dev/null +++ b/misc/cgo/life/life.go @@ -0,0 +1,39 @@ +// Copyright 2010 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 life + +// #include "life.h" +import "C" + +import "unsafe" + +func Run(gen, x, y int, a []int) { + n := make([]int, x*y) + for i := 0; i < gen; i++ { + C.Step(C.int(x), C.int(y), (*C.int)(unsafe.Pointer(&a[0])), (*C.int)(unsafe.Pointer(&n[0]))) + copy(a, n) + } +} + +// Keep the channels visible from Go. +var chans [4]chan bool + +//export GoStart +// Double return value is just for testing. +func GoStart(i, xdim, ydim, xstart, xend, ystart, yend C.int, a *C.int, n *C.int) (int, int) { + c := make(chan bool, int(C.MYCONST)) + go func() { + C.DoStep(xdim, ydim, xstart, xend, ystart, yend, a, n) + c <- true + }() + chans[i] = c + return int(i), int(i + 100) +} + +//export GoWait +func GoWait(i C.int) { + <-chans[i] + chans[i] = nil +} diff --git a/misc/cgo/life/life.h b/misc/cgo/life/life.h new file mode 100644 index 000000000..b2011b25f --- /dev/null +++ b/misc/cgo/life/life.h @@ -0,0 +1,7 @@ +// Copyright 2010 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. + +extern void Step(int, int, int *, int *); +extern void DoStep(int, int, int, int, int, int, int *, int *); +extern const int MYCONST; diff --git a/misc/cgo/life/main.go b/misc/cgo/life/main.go new file mode 100644 index 000000000..9cfed434b --- /dev/null +++ b/misc/cgo/life/main.go @@ -0,0 +1,44 @@ +// Copyright 2010 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. + +// Run the game of life in C using Go for parallelization. + +package main + +import ( + "flag" + "fmt" + "life" +) + +const MAXDIM = 100 + +var dim = flag.Int("dim", 16, "board dimensions") +var gen = flag.Int("gen", 10, "generations") + +func main() { + flag.Parse() + + var a [MAXDIM * MAXDIM]int + for i := 2; i < *dim; i += 8 { + for j := 2; j < *dim-3; j += 8 { + for y := 0; y < 3; y++ { + a[i**dim+j+y] = 1 + } + } + } + + life.Run(*gen, *dim, *dim, a[:]) + + for i := 0; i < *dim; i++ { + for j := 0; j < *dim; j++ { + if a[i**dim+j] == 0 { + fmt.Print(" ") + } else { + fmt.Print("X") + } + } + fmt.Print("\n") + } +} diff --git a/misc/cgo/life/test.bash b/misc/cgo/life/test.bash new file mode 100755 index 000000000..5c5fba1a9 --- /dev/null +++ b/misc/cgo/life/test.bash @@ -0,0 +1,11 @@ +#!/bin/sh +# Copyright 2010 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. + +set -e +gomake life +echo '*' life >run.out +./life >>run.out +diff run.out golden.out +gomake clean diff --git a/misc/cgo/stdio/Makefile b/misc/cgo/stdio/Makefile new file mode 100644 index 000000000..3f7a4c01c --- /dev/null +++ b/misc/cgo/stdio/Makefile @@ -0,0 +1,17 @@ +# 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 ../../../src/Make.inc + +TARG=stdio +CGOFILES=\ + file.go\ + +CLEANFILES+=hello fib chain run.out + +include ../../../src/Make.pkg + +%: install %.go + $(GC) $*.go + $(LD) -o $@ $*.$O diff --git a/misc/cgo/stdio/chain.go b/misc/cgo/stdio/chain.go new file mode 100644 index 000000000..c188b2dd9 --- /dev/null +++ b/misc/cgo/stdio/chain.go @@ -0,0 +1,43 @@ +// 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. + +// Pass numbers along a chain of threads. + +package main + +import ( + "runtime" + "stdio" + "strconv" +) + +const N = 10 +const R = 5 + +func link(left chan<- int, right <-chan int) { + // Keep the links in dedicated operating system + // threads, so that this program tests coordination + // between pthreads and not just goroutines. + runtime.LockOSThread() + for { + v := <-right + stdio.Stdout.WriteString(strconv.Itoa(v) + "\n") + left <- 1 + v + } +} + +func main() { + leftmost := make(chan int) + var left chan int + right := leftmost + for i := 0; i < N; i++ { + left, right = right, make(chan int) + go link(left, right) + } + for i := 0; i < R; i++ { + right <- 0 + x := <-leftmost + stdio.Stdout.WriteString(strconv.Itoa(x) + "\n") + } +} diff --git a/misc/cgo/stdio/fib.go b/misc/cgo/stdio/fib.go new file mode 100644 index 000000000..c02e31fd8 --- /dev/null +++ b/misc/cgo/stdio/fib.go @@ -0,0 +1,47 @@ +// 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. + +// Compute Fibonacci numbers with two goroutines +// that pass integers back and forth. No actual +// concurrency, just threads and synchronization +// and foreign code on multiple pthreads. + +package main + +import ( + "runtime" + "stdio" + "strconv" +) + +func fibber(c, out chan int64, i int64) { + // Keep the fibbers in dedicated operating system + // threads, so that this program tests coordination + // between pthreads and not just goroutines. + runtime.LockOSThread() + + if i == 0 { + c <- i + } + for { + j := <-c + stdio.Stdout.WriteString(strconv.Itoa64(j) + "\n") + out <- j + <-out + i += j + c <- i + } +} + +func main() { + c := make(chan int64) + out := make(chan int64) + go fibber(c, out, 0) + go fibber(c, out, 1) + <-out + for i := 0; i < 90; i++ { + out <- 1 + <-out + } +} diff --git a/misc/cgo/stdio/file.go b/misc/cgo/stdio/file.go new file mode 100644 index 000000000..ab1e88436 --- /dev/null +++ b/misc/cgo/stdio/file.go @@ -0,0 +1,45 @@ +// 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. + +/* +A trivial example of wrapping a C library in Go. +For a more complex example and explanation, +see ../gmp/gmp.go. +*/ + +package stdio + +/* +#include <stdio.h> +#include <stdlib.h> +#include <sys/stat.h> +#include <errno.h> + +char* greeting = "hello, world"; +*/ +import "C" +import "unsafe" + +type File C.FILE + +var Stdout = (*File)(C.stdout) +var Stderr = (*File)(C.stderr) + +// Test reference to library symbol. +// Stdout and stderr are too special to be a reliable test. +var myerr = C.sys_errlist + +func (f *File) WriteString(s string) { + p := C.CString(s) + C.fputs(p, (*C.FILE)(f)) + C.free(unsafe.Pointer(p)) + f.Flush() +} + +func (f *File) Flush() { + C.fflush((*C.FILE)(f)) +} + +var Greeting = C.GoString(C.greeting) +var Gbytes = C.GoBytes(unsafe.Pointer(C.greeting), C.int(len(Greeting))) diff --git a/misc/cgo/stdio/golden.out b/misc/cgo/stdio/golden.out new file mode 100644 index 000000000..c0e496547 --- /dev/null +++ b/misc/cgo/stdio/golden.out @@ -0,0 +1,150 @@ +* hello +hello, world +* fib +0 +1 +1 +2 +3 +5 +8 +13 +21 +34 +55 +89 +144 +233 +377 +610 +987 +1597 +2584 +4181 +6765 +10946 +17711 +28657 +46368 +75025 +121393 +196418 +317811 +514229 +832040 +1346269 +2178309 +3524578 +5702887 +9227465 +14930352 +24157817 +39088169 +63245986 +102334155 +165580141 +267914296 +433494437 +701408733 +1134903170 +1836311903 +2971215073 +4807526976 +7778742049 +12586269025 +20365011074 +32951280099 +53316291173 +86267571272 +139583862445 +225851433717 +365435296162 +591286729879 +956722026041 +1548008755920 +2504730781961 +4052739537881 +6557470319842 +10610209857723 +17167680177565 +27777890035288 +44945570212853 +72723460248141 +117669030460994 +190392490709135 +308061521170129 +498454011879264 +806515533049393 +1304969544928657 +2111485077978050 +3416454622906707 +5527939700884757 +8944394323791464 +14472334024676221 +23416728348467685 +37889062373143906 +61305790721611591 +99194853094755497 +160500643816367088 +259695496911122585 +420196140727489673 +679891637638612258 +1100087778366101931 +1779979416004714189 +2880067194370816120 +* chain +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 diff --git a/misc/cgo/stdio/hello.go b/misc/cgo/stdio/hello.go new file mode 100644 index 000000000..58fc6d574 --- /dev/null +++ b/misc/cgo/stdio/hello.go @@ -0,0 +1,11 @@ +// 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 "stdio" + +func main() { + stdio.Stdout.WriteString(stdio.Greeting + "\n") +} diff --git a/misc/cgo/stdio/test.bash b/misc/cgo/stdio/test.bash new file mode 100755 index 000000000..82e3f7b45 --- /dev/null +++ b/misc/cgo/stdio/test.bash @@ -0,0 +1,15 @@ +#!/bin/sh +# 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. + +set -e +gomake hello fib chain +echo '*' hello >run.out +./hello >>run.out +echo '*' fib >>run.out +./fib >>run.out +echo '*' chain >>run.out +./chain >>run.out +diff run.out golden.out +gomake clean diff --git a/misc/cgo/test/Makefile b/misc/cgo/test/Makefile new file mode 100644 index 000000000..d4309be3c --- /dev/null +++ b/misc/cgo/test/Makefile @@ -0,0 +1,26 @@ +# Copyright 2011 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 ../../../src/Make.inc + +TARG=runtime/cgotest + +CGOFILES=\ + align.go\ + basic.go\ + callback.go\ + env.go\ + exports.go\ + issue1222.go\ + issue1328.go\ + issue1560.go\ + duplicate_symbol.go\ + +CGO_OFILES=\ + callback_c.o\ + +OFILES=\ + runtime.$O\ + +include ../../../src/Make.pkg diff --git a/misc/cgo/test/align.go b/misc/cgo/test/align.go new file mode 100644 index 000000000..07ab9ef50 --- /dev/null +++ b/misc/cgo/test/align.go @@ -0,0 +1,72 @@ +package cgotest + +/* +#include <stdio.h> + +typedef unsigned char Uint8; +typedef unsigned short Uint16; + +typedef enum { + MOD1 = 0x0000, + MODX = 0x8000 +} SDLMod; + +typedef enum { + A = 1, + B = 322, + SDLK_LAST +} SDLKey; + +typedef struct SDL_keysym { + Uint8 scancode; + SDLKey sym; + SDLMod mod; + Uint16 unicode; +} SDL_keysym; + +typedef struct SDL_KeyboardEvent { + Uint8 typ; + Uint8 which; + Uint8 state; + SDL_keysym keysym; +} SDL_KeyboardEvent; + +void makeEvent(SDL_KeyboardEvent *event) { + unsigned char *p; + int i; + + p = (unsigned char*)event; + for (i=0; i<sizeof *event; i++) { + p[i] = i; + } +} + +int same(SDL_KeyboardEvent* e, Uint8 typ, Uint8 which, Uint8 state, Uint8 scan, SDLKey sym, SDLMod mod, Uint16 uni) { + return e->typ == typ && e->which == which && e->state == state && e->keysym.scancode == scan && e->keysym.sym == sym && e->keysym.mod == mod && e->keysym.unicode == uni; +} + +void cTest(SDL_KeyboardEvent *event) { + printf("C: %#x %#x %#x %#x %#x %#x %#x\n", event->typ, event->which, event->state, + event->keysym.scancode, event->keysym.sym, event->keysym.mod, event->keysym.unicode); + fflush(stdout); +} + +*/ +import "C" + +import ( + "testing" +) + +func testAlign(t *testing.T) { + var evt C.SDL_KeyboardEvent + C.makeEvent(&evt) + if C.same(&evt, evt.typ, evt.which, evt.state, evt.keysym.scancode, evt.keysym.sym, evt.keysym.mod, evt.keysym.unicode) == 0 { + t.Error("*** bad alignment") + C.cTest(&evt) + t.Errorf("Go: %#x %#x %#x %#x %#x %#x %#x\n", + evt.typ, evt.which, evt.state, evt.keysym.scancode, + evt.keysym.sym, evt.keysym.mod, evt.keysym.unicode) + t.Error(evt) + } +} diff --git a/misc/cgo/test/basic.go b/misc/cgo/test/basic.go new file mode 100644 index 000000000..b9d0953bd --- /dev/null +++ b/misc/cgo/test/basic.go @@ -0,0 +1,134 @@ +// Copyright 2010 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. + +// Basic test cases for cgo. + +package cgotest + +/* +#include <stdio.h> +#include <stdlib.h> +#include <sys/stat.h> +#include <errno.h> + +#define SHIFT(x, y) ((x)<<(y)) +#define KILO SHIFT(1, 10) + +enum E { + Enum1 = 1, + Enum2 = 2, +}; + +typedef unsigned char uuid_t[20]; + +void uuid_generate(uuid_t x) { + x[0] = 0; +} + +struct S { + int x; +}; + +extern enum E myConstFunc(struct S* const ctx, int const id, struct S **const filter); + +enum E myConstFunc(struct S *const ctx, int const id, struct S **const filter) { return 0; } + +// issue 1222 +typedef union { + long align; +} xxpthread_mutex_t; + +struct ibv_async_event { + union { + int x; + } element; +}; + +struct ibv_context { + xxpthread_mutex_t mutex; +}; +*/ +import "C" +import ( + "os" + "testing" + "unsafe" +) + +const EINVAL = C.EINVAL /* test #define */ + +var KILO = C.KILO + +func uuidgen() { + var uuid C.uuid_t + C.uuid_generate(&uuid[0]) +} + +func Size(name string) (int64, os.Error) { + var st C.struct_stat + p := C.CString(name) + _, err := C.stat(p, &st) + C.free(unsafe.Pointer(p)) + if err != nil { + return 0, err + } + return int64(C.ulong(st.st_size)), nil +} + +func Strtol(s string, base int) (int, os.Error) { + p := C.CString(s) + n, err := C.strtol(p, nil, C.int(base)) + C.free(unsafe.Pointer(p)) + return int(n), err +} + +func Atol(s string) int { + p := C.CString(s) + n := C.atol(p) + C.free(unsafe.Pointer(p)) + return int(n) +} + +func testConst(t *testing.T) { + C.myConstFunc(nil, 0, nil) +} + +func testEnum(t *testing.T) { + if C.Enum1 != 1 || C.Enum2 != 2 { + t.Error("bad enum", C.Enum1, C.Enum2) + } +} + +func testAtol(t *testing.T) { + l := Atol("123") + if l != 123 { + t.Error("Atol 123: ", l) + } +} + +func testErrno(t *testing.T) { + n, err := Strtol("asdf", 123) + if n != 0 || err != os.EINVAL { + t.Error("Strtol: ", n, err) + } +} + +func testMultipleAssign(t *testing.T) { + p := C.CString("234") + n, m := C.strtol(p, nil, 345), C.strtol(p, nil, 10) + if n != 0 || m != 234 { + t.Fatal("Strtol x2: ", n, m) + } + C.free(unsafe.Pointer(p)) +} + +var ( + uint = (C.uint)(0) + ulong C.ulong + char C.char +) + +type Context struct { + ctx *C.struct_ibv_context +} diff --git a/misc/cgo/test/callback.go b/misc/cgo/test/callback.go new file mode 100644 index 000000000..3edee9758 --- /dev/null +++ b/misc/cgo/test/callback.go @@ -0,0 +1,136 @@ +package cgotest + +/* +void callback(void *f); +void callGoFoo(void) { + extern void goFoo(void); + goFoo(); +} +*/ +import "C" + +import ( + "runtime" + "testing" + "unsafe" +) + +// nestedCall calls into C, back into Go, and finally to f. +func nestedCall(f func()) { + // NOTE: Depends on representation of f. + // callback(x) calls goCallback(x) + C.callback(*(*unsafe.Pointer)(unsafe.Pointer(&f))) +} + +//export goCallback +func goCallback(p unsafe.Pointer) { + (*(*func())(unsafe.Pointer(&p)))() +} + +func testCallback(t *testing.T) { + var x = false + nestedCall(func() { x = true }) + if !x { + t.Fatal("nestedCall did not call func") + } +} + +func testCallbackGC(t *testing.T) { + nestedCall(runtime.GC) +} + +func lockedOSThread() bool // in runtime.c + +func testCallbackPanic(t *testing.T) { + // Make sure panic during callback unwinds properly. + if lockedOSThread() { + t.Fatal("locked OS thread on entry to TestCallbackPanic") + } + defer func() { + s := recover() + if s == nil { + t.Fatal("did not panic") + } + if s.(string) != "callback panic" { + t.Fatal("wrong panic:", s) + } + if lockedOSThread() { + t.Fatal("locked OS thread on exit from TestCallbackPanic") + } + }() + nestedCall(func() { panic("callback panic") }) + panic("nestedCall returned") +} + +func testCallbackPanicLoop(t *testing.T) { + // Make sure we don't blow out m->g0 stack. + for i := 0; i < 100000; i++ { + TestCallbackPanic(t) + } +} + +func testCallbackPanicLocked(t *testing.T) { + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + if !lockedOSThread() { + t.Fatal("runtime.LockOSThread didn't") + } + defer func() { + s := recover() + if s == nil { + t.Fatal("did not panic") + } + if s.(string) != "callback panic" { + t.Fatal("wrong panic:", s) + } + if !lockedOSThread() { + t.Fatal("lost lock on OS thread after panic") + } + }() + nestedCall(func() { panic("callback panic") }) + panic("nestedCall returned") +} + +// Callback with zero arguments used to make the stack misaligned, +// which broke the garbage collector and other things. +func testZeroArgCallback(t *testing.T) { + defer func() { + s := recover() + if s != nil { + t.Fatal("panic during callback:", s) + } + }() + C.callGoFoo() +} + +//export goFoo +func goFoo() { + x := 1 + for i := 0; i < 10000; i++ { + // variadic call mallocs + writes to + variadic(x, x, x) + if x != 1 { + panic("bad x") + } + } +} + +func variadic(x ...interface{}) {} + +func testBlocking(t *testing.T) { + c := make(chan int) + go func() { + for i := 0; i < 10; i++ { + c <- <-c + } + }() + nestedCall(func() { + for i := 0; i < 10; i++ { + c <- i + if j := <-c; j != i { + t.Errorf("out of sync %d != %d", j, i) + } + } + }) +} diff --git a/misc/cgo/test/callback_c.c b/misc/cgo/test/callback_c.c new file mode 100644 index 000000000..5983a5e11 --- /dev/null +++ b/misc/cgo/test/callback_c.c @@ -0,0 +1,12 @@ +// Copyright 2011 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 <sys/types.h> +#include "_cgo_export.h" + +void +callback(void *f) +{ + goCallback(f); +} diff --git a/misc/cgo/test/cgo_test.go b/misc/cgo/test/cgo_test.go new file mode 100644 index 000000000..94fba15db --- /dev/null +++ b/misc/cgo/test/cgo_test.go @@ -0,0 +1,28 @@ +// Copyright 2011 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 + +import "testing" + +// The actual test functions are in non-_test.go files +// so that they can use cgo (import "C"). +// These wrappers are here for gotest to find. + +func TestAlign(t *testing.T) { testAlign(t) } +func TestConst(t *testing.T) { testConst(t) } +func TestEnum(t *testing.T) { testEnum(t) } +func TestAtol(t *testing.T) { testAtol(t) } +func TestErrno(t *testing.T) { testErrno(t) } +func TestMultipleAssign(t *testing.T) { testMultipleAssign(t) } +func TestCallback(t *testing.T) { testCallback(t) } +func TestCallbackGC(t *testing.T) { testCallbackGC(t) } +func TestCallbackPanic(t *testing.T) { testCallbackPanic(t) } +func TestCallbackPanicLoop(t *testing.T) { testCallbackPanicLoop(t) } +func TestCallbackPanicLocked(t *testing.T) { testCallbackPanicLocked(t) } +func TestZeroArgCallback(t *testing.T) { testZeroArgCallback(t) } +func TestBlocking(t *testing.T) { testBlocking(t) } +func Test1328(t *testing.T) { test1328(t) } +func TestParallelSleep(t *testing.T) { testParallelSleep(t) } +func TestSetEnv(t *testing.T) { testSetEnv(t) } diff --git a/misc/cgo/test/duplicate_symbol.go b/misc/cgo/test/duplicate_symbol.go new file mode 100644 index 000000000..69600de9c --- /dev/null +++ b/misc/cgo/test/duplicate_symbol.go @@ -0,0 +1,21 @@ +// Copyright 2010 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. + +// This file contains test cases for cgo. + +package cgotest + +/* +int base_symbol = 0; + +#define alias_one base_symbol +#define alias_two base_symbol +*/ +import "C" + +import "fmt" + +func duplicateSymbols() { + fmt.Printf("%v %v %v\n", C.base_symbol, C.alias_one, C.alias_two) +} diff --git a/misc/cgo/test/env.go b/misc/cgo/test/env.go new file mode 100644 index 000000000..1fb4e684c --- /dev/null +++ b/misc/cgo/test/env.go @@ -0,0 +1,32 @@ +// Copyright 2011 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 + +/* +#include <stdlib.h> +*/ +import "C" +import ( + "os" + "testing" + "unsafe" +) + +// This is really an os package test but here for convenience. +func testSetEnv(t *testing.T) { + const key = "CGO_OS_TEST_KEY" + const val = "CGO_OS_TEST_VALUE" + os.Setenv(key, val) + keyc := C.CString(key) + defer C.free(unsafe.Pointer(keyc)) + v := C.getenv(keyc) + if v == (*C.char)(unsafe.Pointer(uintptr(0))) { + t.Fatal("getenv returned NULL") + } + vs := C.GoString(v) + if vs != val { + t.Fatalf("getenv() = %q; want %q", vs, val) + } +} diff --git a/misc/cgo/test/exports.go b/misc/cgo/test/exports.go new file mode 100644 index 000000000..f96c60b00 --- /dev/null +++ b/misc/cgo/test/exports.go @@ -0,0 +1,12 @@ +// Copyright 2011 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 + +import "C" + +//export ReturnIntLong +func ReturnIntLong() (int, C.long) { + return 1, 2 +} diff --git a/misc/cgo/test/issue1222.go b/misc/cgo/test/issue1222.go new file mode 100644 index 000000000..c396a0c41 --- /dev/null +++ b/misc/cgo/test/issue1222.go @@ -0,0 +1,29 @@ +// Copyright 2010 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. + +// This file contains test cases for cgo. + +package cgotest + +/* +// issue 1222 +typedef union { + long align; +} xxpthread_mutex_t; + +struct ibv_async_event { + union { + int x; + } element; +}; + +struct ibv_context { + xxpthread_mutex_t mutex; +}; +*/ +import "C" + +type AsyncEvent struct { + event C.struct_ibv_async_event +} diff --git a/misc/cgo/test/issue1328.go b/misc/cgo/test/issue1328.go new file mode 100644 index 000000000..e01207dd9 --- /dev/null +++ b/misc/cgo/test/issue1328.go @@ -0,0 +1,30 @@ +// Copyright 2011 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 + +import "testing" + +// extern void BackIntoGo(void); +// void IntoC() { BackIntoGo(); } +import "C" + +//export BackIntoGo +func BackIntoGo() { + x := 1 + + for i := 0; i < 10000; i++ { + xvariadic(x) + if x != 1 { + panic("x is not 1?") + } + } +} + +func xvariadic(x ...interface{}) { +} + +func test1328(t *testing.T) { + C.IntoC() +} diff --git a/misc/cgo/test/issue1560.go b/misc/cgo/test/issue1560.go new file mode 100644 index 000000000..e534cce47 --- /dev/null +++ b/misc/cgo/test/issue1560.go @@ -0,0 +1,46 @@ +// Copyright 2011 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 + +/* +#include <unistd.h> + +extern void BackgroundSleep(int); +void twoSleep(int n) { + BackgroundSleep(n); + sleep(n); +} +*/ +import "C" + +import ( + "testing" + "time" +) + +var sleepDone = make(chan bool) + +func parallelSleep(n int) { + C.twoSleep(C.int(n)) + <-sleepDone +} + +//export BackgroundSleep +func BackgroundSleep(n int) { + go func() { + C.sleep(C.uint(n)) + sleepDone <- true + }() +} + +func testParallelSleep(t *testing.T) { + dt := -time.Nanoseconds() + parallelSleep(1) + dt += time.Nanoseconds() + // bug used to run sleeps in serial, producing a 2-second delay. + if dt >= 1.3e9 { + t.Fatalf("parallel 1-second sleeps slept for %f seconds", float64(dt)/1e9) + } +} diff --git a/misc/cgo/test/runtime.c b/misc/cgo/test/runtime.c new file mode 100644 index 000000000..e087c7622 --- /dev/null +++ b/misc/cgo/test/runtime.c @@ -0,0 +1,21 @@ +// Copyright 2011 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. + +// Expose some runtime functions for testing. + +typedef char bool; + +bool runtime·lockedOSThread(void); + +static void +FLUSH(void*) +{ +} + +void +·lockedOSThread(bool b) +{ + b = runtime·lockedOSThread(); + FLUSH(&b); +} |