Build and run:
rm *.h *.a c-to-go ; mv c-to-go.c c-to-go.c.tmp ; GO111MODULE=off gotip build --buildmode=c-archive ; mv c-to-go.c.tmp c-to-go.c ; gcc -pthread c-to-go.c c-to-go-to-c-to-go.a -o c-to-go && GOMAXPROCS=4 ./c-to-go
| /** | |
| * See | |
| * https://stackoverflow.com/questions/32215509/using-go-code-in-an-existing-c-project | |
| */ | |
| #define _GNU_SOURCE | |
| #include <errno.h> | |
| #include <pthread.h> | |
| #include <stdio.h> | |
| #include <stdlib.h> | |
| #include <unistd.h> | |
| /* To produce, run: | |
| * | |
| * GO111MODULE=off go build --buildmode=c-archive | |
| * | |
| * NOTE: Go doesn't seem to like the pre-existing C file, and tries to compile | |
| * it, which fails. So "temporarily" remove it. | |
| * | |
| * rm *.h *.a c-to-go ; mv c-to-go.c c-to-go.c.tmp | |
| * GO111MODULE=off gotip build --buildmode=c-archive | |
| * mv c-to-go.c.tmp c-to-go.c | |
| * gcc -pthread c-to-go.c c-to-go.a -o c-to-go | |
| * ./c-to-go | |
| */ | |
| #include "c-to-go-to-c-to-go.h" | |
| static void* threadSleepCToGo(void* arg) { | |
| fprintf(stderr, "%d: calling Go time.Sleep\n", gettid()); | |
| Sleep(3); | |
| fprintf(stderr, "%d: ending Go sleep, starting C sleep\n", gettid()); | |
| sleep(3); | |
| fprintf(stderr, "%d: ending C sleep\n", gettid()); | |
| return NULL; | |
| } | |
| static void* threadSleepCToGoToC(void* arg) { | |
| fprintf(stderr, "%d: calling go.GoCallCSleep\n", gettid()); | |
| GoCallCSleep(3); | |
| fprintf(stderr, "%d: ending Go sleep, starting C sleep\n", gettid()); | |
| sleep(3); | |
| fprintf(stderr, "%d: ending C sleep\n", gettid()); | |
| return NULL; | |
| } | |
| static void* threadSleepCToGoToCToGo(void* arg) { | |
| fprintf(stderr, "%d: calling go.GoCallCgoSleep\n", gettid()); | |
| GoCallCgoSleep(3); | |
| fprintf(stderr, "%d: ending Go sleep, starting C sleep\n", gettid()); | |
| sleep(3); | |
| fprintf(stderr, "%d: ending C sleep\n", gettid()); | |
| return NULL; | |
| } | |
| void goSleep(unsigned int seconds) { | |
| fprintf(stderr, " goSleep <- in C, now calling go.Sleep(%d)!\n", seconds); | |
| Sleep(seconds); | |
| } | |
| static void* threadPrintStats(void* arg) { | |
| for (int i = 0; i < 10; ++i) { | |
| fprintf(stderr, "%d: ", gettid()); | |
| PrintStats(); | |
| usleep(1000 * 500); | |
| } | |
| return NULL; | |
| } | |
| #define handle_error_en(en, msg) \ | |
| do { \ | |
| errno = en; \ | |
| perror(msg); \ | |
| exit(EXIT_FAILURE); \ | |
| } while (0) | |
| static pthread_t rspawn(const char* name, void* (*start_routine)(void*)) { | |
| pthread_t tid = -1; /* ID returned by pthread_create() */ | |
| int err = pthread_create(&tid, NULL /*attr*/, start_routine, NULL); | |
| if (err != 0) { | |
| handle_error_en(err, "pthread_create"); | |
| } | |
| printf("spawned %s: (pthread_t) %lu\n", name, tid); | |
| return tid; | |
| } | |
| static void rjoin(pthread_t tid) { | |
| int err = pthread_join(tid, NULL); | |
| if (err != 0) { | |
| handle_error_en(err, "pthread_join"); | |
| } | |
| printf("joined (pthread_t) %lu\n", tid); | |
| } | |
| static void orchestrate(void* (*fn)(void*)) { | |
| PrintStats(); | |
| pthread_t notInGoPrinter = rspawn( | |
| "PRINTSTATS", threadPrintStats); /* periodically print some stats */ | |
| pthread_t threads[] = { | |
| rspawn("SLEEP", fn), rspawn("SLEEP", fn), rspawn("SLEEP", fn), | |
| rspawn("SLEEP", fn), rspawn("SLEEP", fn), rspawn("SLEEP", fn), | |
| }; | |
| for (int i = 0; i < sizeof(threads) / sizeof(threads[0]); i++) { | |
| rjoin(threads[i]); | |
| } | |
| PrintStats(); | |
| rjoin(notInGoPrinter); | |
| PrintStats(); | |
| sleep(2); | |
| PrintStats(); | |
| } | |
| int main() { | |
| // CToGo(); | |
| orchestrate(threadSleepCToGo); | |
| fprintf(stderr, "%d: -------- now starting C-to-Go-to-C:\n", gettid()); | |
| orchestrate(threadSleepCToGoToC); | |
| fprintf(stderr, "%d: -------- now starting C-to-Go-to-C-to-Go:\n", gettid()); | |
| orchestrate(threadSleepCToGoToCToGo); | |
| return 0; | |
| } |
| package main | |
| /* | |
| #include <unistd.h> | |
| void goSleep(unsigned int); | |
| */ | |
| import ( | |
| "C" | |
| ) | |
| import ( | |
| "fmt" | |
| "os" | |
| "runtime/metrics" | |
| "time" | |
| ) | |
| //export PrintStats | |
| func PrintStats() { | |
| m := []metrics.Sample{ | |
| {Name: "/sched/gomaxprocs:threads"}, | |
| {Name: "/sched/goroutines/not-in-go:goroutines"}, | |
| {Name: "/sched/goroutines/runnable:goroutines"}, | |
| {Name: "/sched/goroutines/running:goroutines"}, | |
| {Name: "/sched/goroutines/waiting:goroutines"}, | |
| {Name: "/sched/goroutines:goroutines"}, | |
| {Name: "/sched/threads/total:threads"}, | |
| } | |
| metrics.Read(m) | |
| fmt.Fprintf(os.Stderr, "GOMAXPROCS=%d | not-in-go: %d | runnable: %d | running: %d | waiting: %d | total: %d | go-runtime-threads: %d\n", | |
| m[0].Value.Uint64(), // GOMAXPROCS | |
| m[1].Value.Uint64(), // not-in-go | |
| m[2].Value.Uint64(), // runnable | |
| m[3].Value.Uint64(), // running | |
| m[4].Value.Uint64(), // waiting | |
| m[5].Value.Uint64(), // goroutines | |
| m[6].Value.Uint64(), // threads | |
| ) | |
| } | |
| //export Sleep | |
| func Sleep(seconds uint64) { | |
| time.Sleep(time.Duration(seconds) * time.Second) | |
| } | |
| //export GoCallCSleep | |
| func GoCallCSleep(seconds uint64) { | |
| C.sleep(C.uint(seconds)) | |
| } | |
| //export GoCallCgoSleep | |
| func GoCallCgoSleep(seconds uint64) { | |
| C.goSleep(C.uint(seconds)) | |
| } | |
| func main() {} |
Build and run:
rm *.h *.a c-to-go ; mv c-to-go.c c-to-go.c.tmp ; GO111MODULE=off gotip build --buildmode=c-archive ; mv c-to-go.c.tmp c-to-go.c ; gcc -pthread c-to-go.c c-to-go-to-c-to-go.a -o c-to-go && GOMAXPROCS=4 ./c-to-go