5.2.10. C Integration: Threading

This tutorial demonstrates how to use daslang contexts and compilation pipelines across multiple threads from a pure C host application using the daScriptC.h API.

Topics covered:

  • Part A — Running a compiled context on a worker thread

  • Part B — Compiling a script from scratch on a worker thread

  • das_environment_get_bound() / das_environment_set_bound() — thread-local environment binding

  • das_reuse_cache_guard_create() / das_reuse_cache_guard_release() — thread-local free-list cache management

  • das_context_clone() with DAS_CONTEXT_CATEGORY_THREAD_CLONE

  • DAS_POLICY_THREADLOCK_CONTEXT — context mutex for threads

5.2.10.1. Prerequisites

  • Tutorial 1 completed (tutorial_integration_c_hello_world) — basic compile → simulate → eval cycle.

  • Familiarity with platform threading primitives (_beginthreadex on Windows, pthread_create on POSIX).

5.2.10.2. Why threading matters

daslang uses thread-local storage (TLS) for its environment object. Every thread that touches the daslang C API must have a valid environment bound. There are two common patterns:

  1. Share the environment — the worker thread binds the same environment as the main thread via das_environment_set_bound(). Use this when the worker only executes and the main thread is idle.

  2. Independent environment — the worker thread calls das_initialize() / das_shutdown() to create and destroy its own module registry and compilation pipeline.

5.2.10.3. New C API functions for threading

These functions were added to daScriptC.h specifically for multi-threaded host applications:

Function

Purpose

das_environment_get_bound()

Return the environment bound to the calling thread (TLS).

das_environment_set_bound(env)

Bind an environment on the calling thread.

das_reuse_cache_guard_create()

Create a thread-local free-list cache guard (call first on any worker thread).

das_reuse_cache_guard_release(guard)

Release the guard (call before thread exit).

das_context_clone(ctx, category)

Clone a context for use on another thread.

DAS_CONTEXT_CATEGORY_THREAD_CLONE

Category constant for thread-owned clones.

5.2.10.4. The daslang script

A simple script that computes the sum 0 + 1 + … + 99 = 4950:

options gen2

[export]
def compute() : int {
    var total = 0
    for (i in range(100)) {
        total += i
    }
    return total
}

5.2.10.5. Part A — Run on a worker thread

Compile and simulate on the main thread, then clone the context and run it on a worker thread.

// Compile with threadlock_context enabled
das_policies * pol = das_policies_make();
das_policies_set_bool(pol, DAS_POLICY_THREADLOCK_CONTEXT, 1);
das_program * prog = das_program_compile_policies(path, fac, tout, lib, pol);
das_policies_release(pol);

// Simulate on the main thread
das_context * ctx = das_context_make(das_program_context_stack_size(prog));
das_program_simulate(prog, ctx, tout);

// Clone the context for the worker thread
das_context * clone = das_context_clone(ctx,
                          DAS_CONTEXT_CATEGORY_THREAD_CLONE);

// Capture the environment
das_environment * env = das_environment_get_bound();

// On the worker thread:
das_environment_set_bound(env);
das_function * fn = das_context_find_function(clone, "compute");
vec4f res = das_context_eval_with_catch(clone, fn, NULL);
int result = das_argument_int(res);

Key points:

Function

Explanation

das_environment_get_bound()

Returns the current thread’s TLS environment pointer. Must be captured before launching the worker.

das_environment_set_bound(env)

Binds the environment on the worker thread. Without this, any daslang C API call will crash.

das_context_clone()

Creates a new context that shares the simulated program. Release with das_context_release().

DAS_CONTEXT_CATEGORY_THREAD_CLONE

Marks the clone as thread-owned for diagnostics and safety.

DAS_POLICY_THREADLOCK_CONTEXT

Compile-time policy that gives the context a mutex. Required when the context (or its clone) runs on a non-main thread.

5.2.10.6. Part B — Compile on a worker thread

Create a fully independent daslang environment on a new thread.

// On the worker thread:

// 1. Thread-local free-list cache — must be first.
das_reuse_cache_guard * guard = das_reuse_cache_guard_create();

// 2+3. Register modules + create environment in TLS.
das_initialize();

// 4. Standard compile → simulate → eval cycle.
das_policies * pol = das_policies_make();
das_policies_set_bool(pol, DAS_POLICY_THREADLOCK_CONTEXT, 1);
das_program * prog = das_program_compile_policies(path, fac, tout, lib, pol);
das_policies_release(pol);
das_context * ctx = das_context_make(das_program_context_stack_size(prog));
das_program_simulate(prog, ctx, tout);
das_function * fn = das_context_find_function(ctx, "compute");
vec4f res = das_context_eval_with_catch(ctx, fn, NULL);
int result = das_argument_int(res);

// Cleanup
das_context_release(ctx);
das_program_release(prog);
/* ... release fac, lib, tout ... */

// 5. Shut down this thread's module system.
das_shutdown();
das_reuse_cache_guard_release(guard);

Key points:

Function

Explanation

das_reuse_cache_guard_create()

Initializes per-thread free-list caches. Must be created first on any thread that uses daslang.

das_initialize()

Registers module factories and creates a new environment in TLS. Safe to call on multiple threads — module registration is idempotent.

das_shutdown()

Destroys this thread’s modules and environment. Must be called before the thread exits.

das_reuse_cache_guard_release()

Tears down thread-local caches. Call after das_shutdown().

DAS_POLICY_THREADLOCK_CONTEXT

Same policy as Part A — ensures the context has a mutex.

5.2.10.7. Choosing the right pattern

Criteria

Part A (clone + run)

Part B (compile on thread)

Compilation cost

Zero on worker (pre-compiled)

Full compilation on worker

Isolation

Shares environment with main

Fully independent

Concurrent compilation

Not safe (shared env)

Safe (separate env per thread)

Use case

Game threads running pre-compiled AI/logic

Build servers, parallel test runners

5.2.10.8. Portable threading

The tutorial uses _beginthreadex on Windows and pthread_create on POSIX, wrapped in a small helper. The DAS_C_INTEGRATION_TUTORIAL CMake macro already links Threads::Threads, so no extra setup is needed for pthreads.

5.2.10.9. Build & run

cmake --build build --config Release --target integration_c_10
bin/Release/integration_c_10

Expected output:

=== Part A: Run on a worker thread ===
  compute() on worker thread returned: 4950
  PASS

=== Part B: Compile on a worker thread ===
  compute() compiled + run on worker thread returned: 4950
  PASS

See also

Full source: 10_threading.c, 10_threading.das

Previous tutorial: tutorial_integration_c_aot

C++ equivalent: tutorial_integration_cpp_threading

daScriptC.h API header: include/daScript/daScriptC.h