Skip to content

Instantly share code, notes, and snippets.

@aktau
Created December 23, 2025 10:54
Show Gist options
  • Select an option

  • Save aktau/1ce9db0818f1b8114aa4d914c67495ed to your computer and use it in GitHub Desktop.

Select an option

Save aktau/1ce9db0818f1b8114aa4d914c67495ed to your computer and use it in GitHub Desktop.
/**
* 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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment