Starting a thread

Now that we've seen how to start another process, let's see how to start another thread. Any thread can create another thread in the same process; there are no restrictions (short of memory space, of course!). The most common way of doing this is via the POSIX pthread_create() call:

#include <pthread.h>

int
pthread_create (pthread_t *thread,
                const pthread_attr_t *attr,
                void *(*start_routine) (void *),
                void *arg);

The pthread_create() function takes four arguments:

thread
A pointer to a pthread_t where the thread ID is stored.
attr
An attributes structure.
start_routine
The routine where the thread begins.
arg
An argument passed to the thread's start_routine.

Note that the thread pointer and the attributes structure (attr) are optional—you can pass them as NULL.

The thread parameter can be used to store the thread ID of the newly created thread. Notice that in the following examples, we pass a NULL, meaning that we don't care what the ID is of the newly created thread. If we did care, we could do something like this:

pthread_t tid;

pthread_create (&tid, …
printf ("Newly created thread id is %d\n", tid);

This use is actually quite typical, because often you want to know which thread ID is running which piece of code.

Note: A small subtle point. It's possible that the newly created thread may be running before the thread ID (the tid parameter) is filled. This means that you should be careful about using the tid as a global variable. The usage shown above is okay, because the pthread_create() call has returned, which means that the tid value is stuffed correctly.

The new thread begins executing at start_routine(), with the argument arg.

The thread attributes structure

When you start a new thread, it can assume some well-defined defaults, or you can explicitly specify its characteristics. Before we jump into a discussion of the thread attribute functions, let's look at the pthread_attr_t data type:

typedef struct {
    int                 __flags;
    size_t              __stacksize;
    void                *__stackaddr;
    void                (*__exitfunc)(void *status);
    int                 __policy;
    struct sched_param  __param;
    unsigned            __guardsize;
} pthread_attr_t;

Basically, the fields are used as follows:

__flags
Non-numerical (Boolean) characteristics (for example, whether the thread should run detached or joinable).
__stacksize, __stackaddr, and __guardsize
Stack specifications.
__exitfunc
Function to execute at thread exit.
__policy and __param
Scheduling parameters.

The following functions are available:

Attribute administration
Flags (Boolean characteristics)
Stack related
Scheduling related

This looks like a pretty big list (20 functions), but in reality we have to worry about only half of them, because they're paired: get and set (with the exception of pthread_attr_init() and pthread_attr_destroy()).

Before we examine the attribute functions, there's one thing to note. You must call pthread_attr_init() to initialize the attribute structure before using it, set it with the appropriate pthread_attr_set*() function(s), and then call pthread_create() to create the thread. Changing the attribute structure after the thread's been created has no effect.

Thread attribute administration

The function pthread_attr_init() must be called to initialize the attribute structure before using it:

...
pthread_attr_t  attr;
...
pthread_attr_init (&attr);

You can call pthread_attr_destroy() to uninitialize the thread attribute structure, but almost no one ever does (unless you have POSIX-compliant code).

In the descriptions that follow, the default values are marked (default).

The flags thread attribute

The three functions, pthread_attr_setdetachstate(), pthread_attr_setinheritsched(), and pthread_attr_setscope() determine whether the thread is created joinable or detached, whether the thread inherits the scheduling attributes of the creating thread or uses the scheduling attributes specified by pthread_attr_setschedparam() and pthread_attr_setschedpolicy(), and finally whether the thread has a scope of system or process.

To create a joinable thread (meaning that another thread can synchronize to its termination via pthread_join() ), you can use:

(default)
pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_JOINABLE);

To create one that can't be joined (called a detached thread), you can use:

pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED);

If you want the thread to inherit the scheduling attributes of the creating thread (that is, to have the same scheduling policy and the same priority), you can use:

(default)
pthread_attr_setinheritsched (&attr, PTHREAD_INHERIT_SCHED);

To create one that uses the scheduling attributes specified in the attribute structure itself (which you can set using pthread_attr_setschedparam() and pthread_attr_setschedpolicy() ), you'd use:

pthread_attr_setinheritsched (&attr, PTHREAD_EXPLICIT_SCHED);

Finally, you never call pthread_attr_setscope() . Why? Because BlackBerry 10 OS supports only scope, and it's the default when you initialize the attribute. (System scope means that all threads in the system compete against each other for CPU; the other value, process, means that threads compete against each other for CPU within the process, and the kernel schedules the processes.)

If you do insist on calling it, you can call it only as follows:

(default)
pthread_attr_setscope (&attr, PTHREAD_SCOPE_SYSTEM);

The stack thread attributes

The thread attribute stack parameters are prototyped as follows:

int
pthread_attr_setguardsize (pthread_attr_t *attr, size_t gsize);

int
pthread_attr_setstackaddr (pthread_attr_t *attr, void *addr);

int
pthread_attr_setstacksize (pthread_attr_t *attr, size_t ssize);

int
pthread_attr_setstacklazy (pthread_attr_t *attr, int lazystack);

These functions all take the attribute structure as their first parameter; their second parameters are selected from the following:

gsize
The size of the guard area.
addr
The address of the stack, if you're providing one.
ssize
The size of the stack.
lazystack
Indicates if the stack should be allocated on demand or up front from physical memory.

The guard area is a memory area immediately after the stack that the thread can't write to. If it does (meaning that the stack was about to overflow), the thread gets hit with a SIGSEGV. If the guardsize is 0, it means that there's no guard area. This also implies that there's no stack overflow checking. If the guardsize is nonzero, then it's set to at least the system-wide default guardsize (which you can obtain with a call to sysconf() with the constant _SC_PAGESIZE). Note that the guardsize is at least as big as a (for example, 4 KB on an x86 processor). Also, note that the guard page doesn't take up any physical memory—it's done as a virtual address (MMU) trick.

The addr is the address of the stack, in case you're providing it. You can set it to NULL meaning that the system allocates (and frees!) the stack for the thread. The advantage of specifying a stack is that you can do postmortem stack depth analysis. This is accomplished by allocating a stack area, filling it with a signature (for example, the string STACK repeated over and over), and letting the thread run. When the thread has completed, you look at the stack area and see how far the thread had scribbled over your signature, giving you the maximum depth of the stack used during this particular run.

The ssize parameter specifies how big the stack is. If you provide the stack in addr, then ssize should be the size of that data area. If you don't provide the stack in addr (meaning you passed a NULL), then the ssize parameter tells the system how big a stack it should allocate for you. If you specify a 0 for ssize, the system selects the default stack size for you. Obviously, it's bad practice to specify a 0 for ssize and specify a stack using addr—effectively you're saying Here's a pointer to an object, and the object is some default size. The problem is that there's no binding between the object size and the passed value.

Note: If a stack is being provided via addr, no automatic stack overflow protection exists for that thread (that is, there's no guard area). However, you can certainly set this up yourself using mmap() and mprotect() .

Finally, the lazystack parameter indicates if the physical memory should be allocated as required (use the value PTHREAD_STACK_LAZY) or all up front (use the value PTHREAD_STACK_NOTLAZY). The advantage of allocating the stack on demand (as required) is that the thread won't use up more physical memory than it absolutely has to. The disadvantage (and hence the advantage of the all up front method) is that in a low-memory environment the thread won't mysteriously die some time during operating when it needs that extra bit of stack, and there isn't any memory left. If you are using PTHREAD_STACK_NOTLAZY, you can set the actual size of the stack instead of accepting the default, because the default is quite large.

The scheduling thread attributes

Finally, if you do specify PTHREAD_EXPLICIT_SCHED for pthread_attr_setinheritsched(), then you need a way to specify both the scheduling policy and the priority of the thread you're about to create.

This is done with the two functions:

int
pthread_attr_setschedparam (pthread_attr_t *attr,
                            const struct sched_param *param);

int
pthread_attr_setschedpolicy (pthread_attr_t *attr,
                             int policy);

The policy is simple; it's one of SCHED_FIFO, SCHED_RR, or SCHED_OTHER.

Note: SCHED_OTHER is currently mapped to SCHED_RR.

The param is a structure that contains one member of relevance here: sched_priority. Set this value via direct assignment to the desired priority.

Note: A common bug to watch out for is specifying PTHREAD_EXPLICIT_SCHED and then setting only the scheduling policy. The problem is that in an initialized attribute structure, the value of param.sched_priority is 0. This is the same priority as the IDLE process, meaning that your newly created thread is competing for CPU with the IDLE process.

Enough people have been caught by this that QSS has made priority zero reserved for only the idle thread. You cannot run a thread at priority zero.

A few examples

Let's take a look at some examples. We assume that the proper include files (<pthread.h> and <sched.h>) has been included, and that the thread to be created is called new_thread() and is correctly prototyped and defined. The most common way of creating a thread is to let the values default:

pthread_create (NULL, NULL, new_thread, NULL);

In the above example, we created our new thread with the defaults, and passed it a NULL as its one and only parameter (that's the third NULL in the pthread_create() call above).

Generally, you can pass anything you want (via the arg field) to your new thread. Here we pass the number 123:

pthread_create (NULL, NULL, new_thread, (void *) 123);

A more complicated example is to create a non-joinable thread with round-robin scheduling at priority 15:

pthread_attr_t attr;

// initialize the attribute structure
pthread_attr_init (&attr);

// set the detach state to "detached"
pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED);

// override the default of INHERIT_SCHED
pthread_attr_setinheritsched (&attr, PTHREAD_EXPLICIT_SCHED);
pthread_attr_setschedpolicy (&attr, SCHED_RR);
attr.param.sched_priority = 15;

// finally, create the thread
pthread_create (NULL, &attr, new_thread, NULL);

To see what a multithreaded program looks like, you can run the pidin command from the shell. If our program was called spud, if we run pidin once before spud creates a thread and once after spud creates two more threads (for three total), here's what the output looks like (the pidin output is shortened to show only spud):

# pidin
pid    tid name               prio STATE       Blocked
 12301   1 spud                10r READY

# pidin
pid    tid name               prio STATE       Blocked
 12301   1 spud                10r READY
 12301   2 spud                10r READY
 12301   3 spud                10r READY

As you can see, the process spud (process ID 12301) has three threads (under the tid column). The three threads are running at priority 10 with a scheduling algorithm of round robin (indicated by the r after the 10). All three threads are READY, meaning that they're able to use CPU but aren't currently running on the CPU (another, higher-priority thread, is currently running).

Now that we know all about creating threads, let's take a look at how and where we'd use them.