Skip to content

proposal: C API for the runtime #17048

@DemiMarie

Description

@DemiMarie

Purpose

This is a proposal for a basic C API for the runtime. It allows for non-Go main programs and for using specially written Go libraries from any language that can call C functions, with a few additional features beyond what are currently available by buildmode=c-shared. This will be a new buildmode.

Background

Go can call almost any C function via CGo. However, the ability to have a non-Go main program is much more limited. Go does have buildmode=c-shared, but it is limited by #11100 and also does not allow for useful features like passing GOMAXPROCS other than as an environment variable.

Goals

  • Allow for writing programs in a mixture of Go and another language, such that Go is not necessarily the language in which the program's entry point is written.
  • Allow for writing a a library partly or completely in Go for use by another language.
  • Provide handles that can passed to C and which can refer to any Go object.

Non-Goals

  • Allowing arbitrary Go functions to be called from C
  • Allowing C code to manipulate arbitrary Go data structures.

The API

Ground rules.

The entire API is provided in a single C header file, goapi.h.

Unless otherwise specified, any function can be called from multiple threads simultaneously. However, a single pointer in the API cannot be passed to API functions in 2 or more threads simultaneously, unless both functions take pointers to const data.

The API itself

int go_runtime_init(char * const* errormsg, ssize_t *already_initialized, const char * const *options, const void * const* arguments);

Initializes the Go runtime. Must be called before calling any other Go function. Thread-safe and may be called multiple times.

options is a pointer to an array of NUL-terminated strings. Each string corresponds to a matching void* in arguments that corresponds to a matching pointer, which points to a flag that can be used to configure the runtime. Currently, the only string in options that is meaningful is GOMAXPROCS, which replaces the GOMAXPROCS environment variable: the corresponding void* must point to a size_t*. All other values are reserved and must not be used.

If the defaults are OK, both arguments and options may be set to NULL. They are ignored if the runtime is already initialized.

Returns 0 on success, a negative number on failure.

  • On success, *errormsg is set to NULL and *already_initialized is set to the number of times the runtime has been initialized (including this one). It is safe to set errormsg and/or already_initialized to NULL, in which case they are not accessed.
  • On failure, *errormsg points to a NUL-terminated, human-readable error message, and *already_initialized holds a negative number. Again, neither are written to if NULL.
int go_runtime_shutdown(void);

This function shuts down the runtime. It must be called once for each call to go_runtime_init. Only the last such call has any effect.

Once the number of calls to go_runtime_shutdown equals the number of calls to go_runtime_init, this function terminates all active goroutines and resets any signal handlers.

The following sentence may not be initially implemented:
Afterwards, it is safe to dlclose the shared library containing the runtime, or to restart the runtime with go_runtime_init.

typedef struct Go_Handle *Go_Handle;

A handle to an arbitrary Go object. Analogous to a Haskell stable pointer. It stays valid even across garbage collections.

A Go_Handle is not guaranteed to point to valid memory. Dereferencing it invokes undefined behavior.

void go_runtime_delete_handle(Go_Handle handle_to_be_freed);

Deletes the handle passed as argument, rendering it invalid and freeing all underlying resources. After this function is called, the object the handle pointed to may be garbage collected if there are no more references to it.

Go_Handle go_runtime_duplicate_handle(Go_Handle handle_to_be_duplicated);

Duplicates the handle passed as argument. The returned handle points to the same object as the original handle, but must be deallocated separately.

uint8_t go_runtime_are_identical_handles(Go_Handle handle1, Go_Handle handle2);

Tests if the 2 handles passed are identical. Returns true if they point to the same object in memory. Otherwise, returns false.

The Go side

These are additional functions exposed from the runtime package.

type CHandle

The type of C handles to Go data. When passed via CGo, becomes a Go_Handle on the C side.

May be accessed by multiple goroutines simultaneously.

func NewHandle(object interface {}) CHandle

Creates a handle to a Go object that can safely be passed to C.

func DuplicateHandle(handle CHandle) CHandle

Duplicates the handle.

func TestIfIdenticalHandles(handle1 CHandle, handle2 CHandle) bool

Tests if the 2 handles passed point to the same object; that is, if modifications of the object pointed to by one will affect the object pointed to by the other.

func DeallocateHandle(handle CHandle)

Deallocates the handle, rendering it invalid.

func DereferenceHandle(handle CHandle) interface {}

Dereferences the handle, returning the contained object. If the handle is invalid, invokes undefined behavior.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions