Using the thread class


Introduction

The thread class provides a simple thread implementation.

In this implementation, each instance of the thread class corresponds to a separate thread. When the spawn() method is called, a new thread is created, and the function is run in the new thread. An argument may be passed to the function via the spawn() method. This argument is usually used to pass a pointer to a class or struct, providing parameters for the thread function.

In these examples, two threads are spawned. Each runs a separate instance of a "count" function which counts from 0 to some number and prints out the numbers. Each thread is passed in a structure containing a pointer to the thread class, a thread id and a maximum number to count to. The threads are not synchronized in any way, so the numbers will be printed out in seemingly random blocks.

Note that rudiments supports platforms that don't support threads, and it is possible to explicitly compile rudiments without thread support. The supportsThreads() method returns true or false, indicating whether thread support is enabled or not.


Ordinary Threads

Ordinarily, a thread runs until its function returns, or calls the exit() method. Another thread (usually the main thread of the parent process) must call the wait() method to completely dispose of the thread (analagous to "waiting on a child process") and get its exit status.

#include <rudiments/thread.h>
#include <rudiments/process.h>
#include <rudiments/stdio.h>

// struct for passing arguments into the thread
struct args {
        thread          *th;
        int32_t         id;
        uint16_t        max;
};

// function that will be spawned as a thread
void count(void *args) {

        struct args     *a=(struct args *)args;

        for (uint16_t c=0; c<a->max; c++) {
                stdoutput.printf("  %d%d\n",a->id,c);
        }

        a->th->exit(&(a->id));
}

int main(int argc, const char **argv) {

        // do we even support threads?
        if (!thread::supportsThreads()) {
                stdoutput.printf("thread are not supported\n");
                process::exit(1);
        }

        // create the threads
        thread  t1;
        thread  t2;

        // define arguments to pass to each thread
        struct args     a1;
        a1.th=&t1;
        a1.id=1;
        a1.max=100;

        struct args     a2;
        a2.th=&t2;
        a2.id=2;
        a2.max=200;

        // spawn the threads
        t1.spawn((void *(*)(void *))count,(void *)&a1,false);
        t2.spawn((void *(*)(void *))count,(void *)&a2,false);

        // wait for the threads to exit
        int32_t t1status=-1;
        int32_t t2status=-1;
        t1.wait(&t1status);
        t2.wait(&t2status);

        // print out the thread's exit status
        stdoutput.printf("t1 status: %d\n",t1status);
        stdoutput.printf("t2 status: %d\n",t2status);
}

Detached Threads

Alternatively, a thread may dissociate itself from the parent by calling the detach() method. In this case, the thread is completely disposed of when its function returns. The parent process will only exit when all threads have exited. Note that if a thread calls detach(), there is no way for it to return an exit status.

#include <rudiments/thread.h>
#include <rudiments/process.h>
#include <rudiments/snooze.h>
#include <rudiments/stdio.h>

// struct for passing arguments into the thread
struct args {
        thread          *th;
        int32_t         id;
        uint16_t        max;
};

// function that will be spawned as a thread
void count(void *args) {

        struct args     *a=(struct args *)args;

        a->th->detach();

        for (uint16_t c=0; c<a->max; c++) {
                stdoutput.printf("  %d%d\n",a->id,c);
        }
}

int main(int argc, const char **argv) {

        // do we even support threads?
        if (!thread::supportsThreads()) {
                stdoutput.printf("thread are not supported\n");
                process::exit(1);
        }

        // create the threads
        thread  t1;
        thread  t2;

        // define arguments to pass to each thread
        struct args     a1;
        a1.th=&t1;
        a1.id=1;
        a1.max=100;

        struct args     a2;
        a2.th=&t2;
        a2.id=2;
        a2.max=200;

        // spawn the threads
        t1.spawn((void *(*)(void *))count,(void *)&a1,true);
        t2.spawn((void *(*)(void *))count,(void *)&a2,true);
}