Skip to main content

Memory Management

Rules

  1. All allocated strings must be freed
    • boxlite_box_id() -> boxlite_free_string()
    • boxlite_list_info() -> boxlite_free_string()
    • Info/metrics JSON -> boxlite_free_string()
  2. Error structs must be freed
    • CBoxliteError -> boxlite_error_free()
  3. Results must be freed
    • CBoxliteExecResult -> boxlite_result_free()
  4. All cleanup functions are NULL-safe

Functions

boxlite_free_string

Free a string allocated by BoxLite.
void boxlite_free_string(char* str);
Use this for any string returned by BoxLite API functions such as boxlite_box_id(), boxlite_list_info(), boxlite_get_info(), boxlite_box_info(), boxlite_runtime_metrics(), and boxlite_box_metrics(). Safe to call with NULL.

boxlite_error_free

Free error struct (message only - struct itself is stack-allocated).
void boxlite_error_free(CBoxliteError* error);
Safe to call with NULL.

boxlite_result_free

Free an execution result.
void boxlite_result_free(CBoxliteExecResult* result);
Safe to call with NULL.

boxlite_simple_free

Free a simple box (auto-stops and removes).
void boxlite_simple_free(CBoxliteSimple* box);
Safe to call with NULL.

boxlite_runtime_free

Free a runtime instance.
void boxlite_runtime_free(CBoxliteRuntime* runtime);
Safe to call with NULL. Automatically frees all boxes.

Memory Ownership Summary

Returned ByFree With
boxlite_box_id()boxlite_free_string()
boxlite_list_info() (out_json)boxlite_free_string()
boxlite_get_info() (out_json)boxlite_free_string()
boxlite_box_info() (out_json)boxlite_free_string()
boxlite_runtime_metrics() (out_json)boxlite_free_string()
boxlite_box_metrics() (out_json)boxlite_free_string()
boxlite_simple_run() (out_result)boxlite_result_free()
CBoxliteError (from any function)boxlite_error_free()
boxlite_version()Do not free (static string)

JSON Schema Reference

BoxOptions Schema

{
  "rootfs": {"Image": "alpine:3.19"},
  "cpus": 2,
  "memory_mib": 512,
  "disk_size_gb": 10,
  "working_dir": "/workspace",
  "env": [["KEY", "value"], ["ANOTHER", "value"]],
  "volumes": [
    {
      "host_path": "/host/data",
      "guest_path": "/data",
      "readonly": false
    }
  ],
  "network": "Isolated",
  "ports": [
    {
      "host_port": 8080,
      "guest_port": 80,
      "protocol": "Tcp"
    }
  ],
  "auto_remove": true
}

Required Fields

All BoxOptions JSON must include these fields:
{
  "rootfs": {"Image": "..."},
  "env": [],
  "volumes": [],
  "network": "Isolated",
  "ports": []
}
Omitting any of the required fields will result in a JSON parsing error. Always include rootfs, env, volumes, network, and ports even if they are empty.

Field Reference

FieldTypeDefaultDescription
rootfsobjectRequiredRoot filesystem source
cpusinteger2Number of CPUs
memory_mibinteger512Memory in MiB
disk_size_gbintegernullDisk size in GB
working_dirstringnullWorking directory
envarrayRequiredEnvironment variables as [key, value] pairs
volumesarrayRequiredVolume mounts
networkstringRequiredNetwork mode: "Isolated"
portsarrayRequiredPort mappings
auto_removebooleantrueRemove box when stopped

RootfsSpec

{"Image": "python:3.11-slim"}
or
{"RootfsPath": "/path/to/rootfs"}

VolumeSpec

{
  "host_path": "/absolute/path/on/host",
  "guest_path": "/path/in/guest",
  "readonly": false
}

PortSpec

{
  "host_port": 8080,
  "guest_port": 80,
  "protocol": "Tcp"
}

Building JSON in C

Since C has no native JSON support, here are common approaches for constructing BoxOptions JSON:

String Concatenation

const char* options = "{"
    "\"rootfs\":{\"Image\":\"alpine:3.19\"},"
    "\"env\":[],"
    "\"volumes\":[],"
    "\"network\":\"Isolated\","
    "\"ports\":[]"
"}";

Using snprintf for Dynamic Values

char options[1024];
snprintf(options, sizeof(options),
    "{"
    "\"rootfs\":{\"Image\":\"%s\"},"
    "\"cpus\":%d,"
    "\"memory_mib\":%d,"
    "\"env\":[],"
    "\"volumes\":[],"
    "\"network\":\"Isolated\","
    "\"ports\":[]"
    "}",
    image_name, num_cpus, memory_mib
);

With Environment Variables

char options[2048];
snprintf(options, sizeof(options),
    "{"
    "\"rootfs\":{\"Image\":\"python:3.11-slim\"},"
    "\"env\":[[\"API_KEY\",\"%s\"],[\"DEBUG\",\"1\"]],"
    "\"volumes\":[],"
    "\"network\":\"Isolated\","
    "\"ports\":[]"
    "}",
    api_key
);

With Volumes and Ports

const char* options = "{"
    "\"rootfs\":{\"Image\":\"nginx:alpine\"},"
    "\"env\":[],"
    "\"volumes\":[{\"host_path\":\"/var/www\",\"guest_path\":\"/usr/share/nginx/html\",\"readonly\":true}],"
    "\"network\":\"Isolated\","
    "\"ports\":[{\"host_port\":8080,\"guest_port\":80,\"protocol\":\"Tcp\"}]"
"}";

Thread Safety

ComponentThread Safety
CBoxliteRuntimeThread-safe
CBoxHandleNOT thread-safe - do not share across threads
CBoxliteSimpleNOT thread-safe - do not share across threads
CallbacksInvoked on the calling thread
The safe pattern is: share a single CBoxliteRuntime across threads, but have each thread create and manage its own box handles.

Safe Multi-threaded Usage

// CORRECT: Share runtime, create per-thread boxes
void* thread_func(void* arg) {
    CBoxliteRuntime* runtime = (CBoxliteRuntime*)arg;
    CBoxliteError error = {0};
    CBoxHandle* box = NULL;

    const char* options = "{"
        "\"rootfs\":{\"Image\":\"alpine:3.19\"},"
        "\"env\":[],\"volumes\":[],\"network\":\"Isolated\",\"ports\":[]"
    "}";

    // Each thread creates its own box
    boxlite_create_box(runtime, options, &box, &error);
    // Use box in this thread only
    boxlite_stop_box(box, &error);
    return NULL;
}

CBoxliteRuntime* runtime;
CBoxliteError error = {0};
boxlite_runtime_new(NULL, NULL, &runtime, &error);

pthread_t threads[4];
for (int i = 0; i < 4; i++) {
    pthread_create(&threads[i], NULL, thread_func, runtime);
}

for (int i = 0; i < 4; i++) {
    pthread_join(threads[i], NULL);
}

boxlite_runtime_free(runtime);

Callback Threading

Callbacks passed to boxlite_execute are always invoked on the calling thread. This means:
  • You do not need to synchronize within the callback itself
  • The callback blocks the execution until it returns
  • Heavy processing in callbacks will slow down command execution
// Callback runs on the same thread that called boxlite_execute
void output_handler(const char* text, int is_stderr, void* data) {
    // Safe to access thread-local data
    // Safe to write to thread-local buffers
    FILE* stream = is_stderr ? stderr : stdout;
    fprintf(stream, "%s", text);
}