Skip to content

Thread System ($thread)

Overview

Grapa's $thread system provides a complete coroutine and threading implementation with full synchronization primitives, condition variables, and cooperative multitasking capabilities. This is not a planned feature - it's already fully implemented and powers Grapa's own execution pipeline.

Sleep Function in Multi-Threaded Applications

Good News: Grapa's .sleep() function works correctly in multi-threaded applications with proper thread-local behavior.

Thread-Safe Sleep Behavior

  • Sleep calls are thread-local - each thread can sleep independently
  • Concurrent sleep calls work correctly - multiple threads can sleep simultaneously
  • Sleep duration is respected per thread - each thread sleeps for its specified duration
  • No blocking between threads - one thread's sleep doesn't block others

Example of Proper Sleep Behavior

/* This demonstrates proper multi-threaded sleep behavior */
thread1 = $thread();
thread2 = $thread();

/* Thread 1: 3 second sleep */
thread1.start(
    op() {
        "Thread 1: Starting 3 second sleep".echo();
        3000.sleep();  /* 3 seconds */
        "Thread 1: Sleep completed".echo();
        return "Done";
    },
    null,
    null
);

/* Main thread: 50ms sleep */
"Main: Starting 50ms sleep".echo();
50.sleep();  /* Takes exactly 50ms, independent of thread1 */

/* Both sleeps run concurrently and complete at their respective times */

How It Works

  • Grapa's sleep implementation provides proper thread isolation
  • Each thread maintains its own sleep state without interfering with others
  • Sleep calls complete independently based on their individual durations
  • Thread coordination works seamlessly with sleep, suspend, and resume

Best Practices

  1. Use sleep freely in multi-threaded code - it works correctly
  2. Combine sleep with suspend/resume for advanced thread coordination
  3. Use sleep for timing delays without worrying about thread interference
  4. Sleep works well with locks and condition variables for complex synchronization

Key Features

  • Complete Coroutine Support: Full suspend/resume capabilities
  • Synchronization Primitives: Locks, condition variables, trylock operations
  • Cross-Platform: Consistent behavior across Windows, Linux, and macOS
  • High Performance: Optimized critical sections and condition variables
  • Thread Safety: Built-in protection against race conditions
  • Production Ready: Powers Grapa's lexer→compiler→executor pipeline

Thread Object Creation

/* Create a new thread object */
thread = $thread();

Core Thread Management

start(runCode, param, doneCode)

Start a thread with execution code and callbacks.

Parameters: - runCode: Function to execute in the thread - param: Parameter to pass to the run function (optional) - doneCode: Completion callback function (optional)

Returns: Error code (0 = success, -1 = failure)

Example:

thread = $thread();
thread.start(
    op(input) {
        "Thread starting with input: ".echo() + input.echo();
        /* Note: Avoid sleep() in multi-threaded code */
        return "Thread completed";
    },
    "Hello from main thread",  /* Parameter */
    op(input, result) {
        "Thread finished with result: ".echo() + result.echo();
    }
);

stop()

Stop the thread gracefully.

Parameters: None

Returns: Error code

Example:

thread = $thread();
thread.start(op() { /* long running work */ }, null, null);
/* Use suspend/resume instead of sleep for coordination */
thread.suspend();
thread.stop();

started()

Check if thread is currently running.

Parameters: None

Returns: Boolean (true = running, false = stopped)

Example:

thread = $thread();
("Thread started: " + thread.started()).echo();  /* false */

thread.start(op() { /* work without sleep */ }, null, null);
("Thread started: " + thread.started()).echo();  /* true */

Coroutine Control

suspend()

Pause thread execution (coroutine suspend).

Parameters: None

Returns: Error code

Example:

thread = $thread();
thread.start(
    op() {
        "Starting work".echo();
        /* Use suspend instead of sleep for coordination */
        "About to suspend".echo();
        thread.suspend();  /* Suspend self */
        "Resumed from suspension".echo();
        return "Completed";
    },
    null,
    null
);

/* Resume the suspended thread */
thread.resume();

resume()

Resume suspended thread (coroutine resume).

Parameters: None

Returns: Error code

Example:

/* Resume a suspended thread */
if (thread.suspended()) {
    thread.resume();
}

suspended()

Check if thread is currently suspended.

Parameters: None

Returns: Boolean (true = suspended, false = running)

Example:

thread = $thread();
("Thread suspended: " + thread.suspended()).echo();  /* false */

thread.start(
    op() {
        thread.suspend();
    },
    null,
    null
);

("Thread suspended: " + thread.suspended()).echo();  /* true */

Synchronization Primitives

trylock()

Try to acquire lock (non-blocking).

Parameters: None

Returns: Boolean (true = acquired, false = failed)

Example:

lock = $thread();

/* Try to acquire lock */
if (lock.trylock()) {
    "Lock acquired".echo();
    lock.unlock();
} else {
    "Lock busy".echo();
}

lock()

Acquire lock (blocking).

Parameters: None

Returns: Error code

Example:

lock = $thread();

/* Block until lock is available */
lock.lock();
"Critical section".echo();
lock.unlock();

unlock()

Release lock.

Parameters: None

Returns: Error code

Example:

lock = $thread();
lock.lock();
/* Critical section code */
lock.unlock();

Condition Variables

wait()

Wait for signal (condition variable).

Parameters: None

Returns: Error code

Example:

condition = $thread();
data_ready = false;

/* Producer thread */
producer = $thread();
producer.start(
    op() {
        /* Use suspend/resume instead of sleep */
        producer.suspend();
        data_ready = true;
        condition.signal();
        return "Producer done";
    },
    null,
    null
);

/* Consumer thread */
consumer = $thread();
consumer.start(
    op() {
        while (!data_ready) {
            condition.wait();
        };
        "Data is ready!".echo();
        return "Consumer done";
    },
    null,
    null
);

signal()

Signal waiting threads.

Parameters: None

Returns: Error code

Example:

condition = $thread();

/* Wake up waiting threads */
condition.signal();

waiting()

Check if thread is waiting for signal.

Parameters: None

Returns: Boolean (true = waiting, false = not waiting)

Example:

condition = $thread();

/* Check if any threads are waiting */
if (condition.waiting()) {
    "Threads are waiting".echo();
} else {
    "No threads waiting".echo();
}

Utility Methods

type()

Get the type of the thread object.

Parameters: None

Returns: String ("$thread")

Example:

thread = $thread();
("Thread type: " + thread.type()).echo();  /* "$thread" */

describe()

Get a description of the thread object.

Parameters: None

Returns: String description

Example:

thread = $thread();
thread.describe().echo();

Working Thread Examples

The following examples demonstrate working thread patterns with proper sleep behavior:

Example 1: Basic Thread Lifecycle

/* Basic thread creation and management */
basic_thread = $thread();
basic_thread.start(
    op() {
        "Basic thread: Starting work".echo();
        /* Note: 3000.sleep() works here because it's the only sleep */
        "Basic thread: Work completed".echo();
        return ("Basic thread result");
    },
    0,
    op(input, result) {
        ("Basic thread callback: " + result).echo();
    }
);

/* Wait for thread completion */
while(basic_thread.started()==false) ;
while(basic_thread.started()==true) ;
"Main: Basic thread done".echo();

Example 2: Coroutine Suspend/Resume

/* Coroutine pattern using suspend/resume */
coroutine = $thread();
coroutine.start(
    op() {
        "Coroutine: Starting".echo();
        "Coroutine: About to suspend".echo();
        coroutine.suspend();
        "Coroutine: Resumed from suspension".echo();
        "Coroutine: Completed".echo();
        return ("Coroutine completed");
    },
    null,
    op(input, result) {
        ("Coroutine callback: " + result).echo();
    }
);

/* Coordinate with suspend/resume instead of sleep */
while(coroutine.suspended()==false) ;
while(coroutine.suspended()==true) ;
"Main: Resuming coroutine".echo();
coroutine.resume();
while(coroutine.started()==true) ;

Example 3: Thread-Safe Counter with Lock

/* Thread-safe counter using locks */
counter_worker = [];
shared_counter = 0;
counter_lock = $thread();

for (i = 0; i < 3; i+=1) {
    counter_worker += $thread();
    counter_worker[i].start(
        op(worker_id) {
            counter_worker[worker_id].suspend();
            for (j = 0; j < 5; j+=1) {
                counter_lock.lock();
                shared_counter += 1;
                $local.current = shared_counter;
                counter_lock.unlock();
                ("Worker " + worker_id + ": Counter = " + current).echo();
            };
            return ("Worker " + worker_id + " completed");
        },
        i,
        op(input, result) {
            ("Counter worker callback: " + result).echo();
        }
    );
};

/* Coordinate workers using suspend/resume */
for (i = 0; i < 3; i+=1)
     while(counter_worker[i].suspended()==false) ;
for (i = 0; i < 3; i+=1)
    counter_worker[i].resume();
for (i = 0; i < 3; i+=1)
    while(counter_worker[i].started()==false) ;

("Final counter value: " + shared_counter).echo();

Example 4: Producer-Consumer Pattern

/* Producer-consumer with proper coordination */
consumer = [];
shared_queue = [];
queue_lock = $thread();
consumer_count = 3;
consumer_message_count = 4;

/* Consumer threads */
for(consumer_id=0;consumer_id<consumer_count;consumer_id+=1) {
    consumer += $thread();
    consumer[-1].start(
        op(id) {
            ("Consumer " + id + ": Starting consumption").echo();
            $local.items_consumed = 0;
            while (items_consumed < consumer_message_count) {
                queue_lock.lock();
                while (shared_queue.len() == 0) {
                    queue_lock.unlock();
                    consumer[id].suspend();  /* Use suspend instead of sleep */
                    queue_lock.lock();
                };

                item = shared_queue[0];
                shared_queue -= shared_queue[0];
                ("Consumer " + id + ": extracted: " + item).echo();

                queue_lock.unlock();

                /* Note: tm.sleep() works here because it's coordinated */
                $local.tm = 100 * 4.random();
                ("Consumer " + id + ": sleeping: " + tm + "ms").echo();
                tm.sleep();

                ("Consumer " + id + ": consumed: " + item).echo();
                items_consumed += 1;
            };
            return ("Consumer " + id + " completed: " + items_consumed);
        },
        consumer_id,
        op(input, result) {
            ("Consumer callback: " + result).echo();
        }
    );
};

/* Producer thread */
producer = $thread();
producer.start(
    op() {
        producer.suspend();
        $local.i = 0;
        while(i<producer_message_count) {
            queue_lock.lock();
            shared_queue += "Item " + i;
            queue_lock.unlock();

            /* Resume suspended consumers */
            for(consumer_id=0;consumer_id<consumer_count;consumer_id+=1) {
                if (consumer[consumer_id].suspended()) {
                    consumer[consumer_id].resume();
                };
            };
            i += 1;
        };
        return ("Producer completed");
    },
    null,
    op(input, result) {
        ("Producer callback: " + result).echo();
    }
);

/* Coordinate using suspend/resume */
while(producer.suspended()==false) ;
producer.resume();
while(producer.started()==true) ;

for(consumer_id=0;consumer_count;consumer_id+=1)
    while(consumer[consumer_id].started()) ;

Best Practices for Multi-Threaded Code

1. Avoid Sleep in Multi-Threaded Applications

/* BAD: Sleep can cause coordination issues */
thread1.start(op() { 1000.sleep(); }, null, null);
thread2.start(op() { 500.sleep(); }, null, null);

/* GOOD: Use suspend/resume for coordination */
thread1.start(op() { thread1.suspend(); }, null, null);
thread2.start(op() { thread2.suspend(); }, null, null);

2. Use Suspend/Resume for Thread Coordination

/* Coordinate threads without sleep */
worker = $thread();
worker.start(
    op() {
        "Worker starting".echo();
        worker.suspend();  /* Wait for resume */
        "Worker resumed".echo();
        return "Done";
    },
    null,
    null
);

/* Main thread coordinates */
while(worker.suspended()==false) ;
worker.resume();
while(worker.started()==true) ;

3. Use Locks for Shared Resource Protection

/* Protect shared resources with locks */
$global shared_data = [];
$global data_lock = $thread();

worker = $thread();
worker.start(
    op() {
        data_lock.lock();
        shared_data += "new item";
        data_lock.unlock();
        return "Done";
    },
    null,
    null
);

4. Use Condition Variables for Signaling

/* Signal between threads without sleep */
$global data_ready = false;
$global ready_condition = $thread();

producer = $thread();
producer.start(
    op() {
        data_ready = true;
        ready_condition.signal();
        return "Done";
    },
    null,
    null
);

consumer = $thread();
consumer.start(
    op() {
        while (!data_ready) {
            ready_condition.wait();
        };
        "Data is ready!".echo();
        return "Done";
    },
    null,
    null
);

Performance Characteristics

Thread Creation Overhead

  • Linux/Mac (pthread): ~1-2ms per thread
  • Windows (CreateThread): ~2-3ms per thread
  • Memory overhead: ~1-2MB per thread (stack + context)

Synchronization Performance

  • Critical Section: ~50-100ns lock/unlock
  • Mutex: ~100-200ns lock/unlock
  • Condition Variable: ~1-5μs wait/signal

Scalability Considerations

/* Recommended thread limits */
thread_limits = {
    windows: 2000,    /* Windows thread limit */
    linux: 32768,     /* Linux thread limit */
    mac: 2048         /* macOS thread limit */
};

/* Practical limits for Grapa */
practical_limits = {
    small_system: 8,      /* 4-8 cores */
    medium_system: 16,    /* 8-16 cores */
    large_system: 32      /* 16+ cores */
};

Integration with Grapa's Execution Pipeline

Grapa's $thread system powers the language's own execution pipeline:

  1. Lexer Thread: Processes input text and generates tokens
  2. Compiler Thread: Converts tokens to execution trees
  3. Executor Thread: Executes the compiled code

Each stage uses suspend(), resume(), wait(), and signal() to coordinate: - Lexer suspends when input queue is empty - Compiler suspends when token queue is empty - Executor suspends when code queue is empty - Each stage signals the next when data is available

This demonstrates the system's production readiness and reliability.

Cross-Platform Compatibility

The $thread system provides consistent behavior across all supported platforms:

  • Windows: Uses Windows API (CreateThread, CRITICAL_SECTION, WaitOnAddress)
  • Linux/Mac: Uses POSIX threads (pthread_create, pthread_mutex, pthread_cond)
  • Abstraction Layer: Common interface across all platforms
  • Error Handling: Consistent error reporting across platforms

Thread-Safe Variable Declarations

When writing concurrent code in Grapa, it's essential to use proper thread-safe variable declarations. See Thread-Safe Variable Declarations for comprehensive guidance on:

  • Using $global for explicitly shared variables
  • Protecting shared variables with locks
  • Avoiding race conditions
  • Best practices for concurrent programming

Conclusion

Grapa's $thread system provides world-class coroutine and threading capabilities that are:

  1. Fully Implemented: All methods are working and tested
  2. Production Ready: Powers Grapa's own execution pipeline
  3. Cross-Platform: Consistent behavior across Windows, Linux, and macOS
  4. High Performance: Optimized for real-world usage
  5. Thread Safe: Built-in protection against race conditions
  6. Coroutine Ready: Full suspend/resume capabilities

Important Note: The system is robust and feature-complete, with the .sleep() function working correctly in multi-threaded applications. Each thread can sleep independently without blocking others, making it safe to use sleep in concurrent programming scenarios.