The Kernel Kit: Threads and Teams

Declared in: <kernel/OS.h>


Overview

A thread is a synchronous computer process that executes a series of program instructions. Every application has at least one thread: When you launch an application, an initial thread--the main thread--is automatically created (or spawned) and told to run. The main thread executes the ubiquitous main() function, winds through the functions that are called from main(), and is automatically deleted (or killed ) when main() exits.

The Be operating system is multi-threaded: From the main thread you can spawn and run additional threads; from each of these threads you can spawn and run more threads, and so on. All the threads in all applications run concurrently and asynchronously with each other. Furthermore, threads are independent of each other; most notably, a given thread doesn't own the other threads it has spawned. For example, if thread A spawns thread B, and thread A dies (for whatever reason), thread B will continue to run. (But before you get carried away with the idea of leap-frogging threads, you should take note of the caveat in the section Death and the Main Thread ,)

Although threads are independent, they do fall into groups called teams. A team consists of a main thread and all other threads that "descend" from it (that are spawned by the main thread directly, or by any thread that was spawned by the main thread, and so on). Viewed from a higher level, a team is the group of threads that are created by a single application. You can't "transfer" threads from one team to another. The team is set when the thread is spawned; it remains the same throughout the thread's life.

All the threads in a particular team share the same address space: Global variables that are declared by one thread will be visible to all other threads in that team.

The following sections describe how to spawn, control, and examine threads and teams.


Spawning a Thread

You spawn a thread by calling the spawn_thread() function. The function assigns and returns a system-wide thread_id number that you use to identify the new thread in subsequent function calls. Valid thread_id numbers are positive integers; you can check the success of a spawn thus:

   thread_id my_thread = spawn_thread(...);
   
   if ((my_thread) < B_NO_ERROR)
      /* failure */
   else
      /* success */ 

The arguments to spawn_thread() , which are examined throughout this description, supply information such as what the thread is supposed to do, the urgency of its operation, and so on.

Note: A conceptual neighbor of spawning a thread is the act of loading an executable (or loading an app image). This is performed by calling the load_executable() function. Loading an executable causes a separate program, identified as a file, to be launched by the system. For more information on the load_executable() function, see Threads .


Telling a Thread to Run

Spawning a thread isn't enough to make it run. To tell a thread to start running, you must pass its thread_id number to either the resume_thread() or wait_for_thread() function:

Of these two functions, resume_thread() is the more common means for starting a thread that was created through spawn_thread(). wait_for_thread(), on the other hand, is often used to start a thread that was created through load_executable().


The Entry Function

When you call spawn_thread() , you must identify the new thread's entry function. This is a global C function (or a static C++ member function) that the new thread will execute when it's told to run. When the entry function exits, the thread is automatically killed by the operating system.

A thread's entry function assumes the following protocol:

   long thread_entry(void *data);

The protocol signifies that the function can return a value (to whom the value is returned is a topic that will be explored later), and that it accepts a pointer to a buffer of arbitrarily- typed data. (The function's name isn't prescribed by the protocol; in other words, an entry function doesn't have to be named "thread_entry".)

You specify a thread's entry function by passing a pointer to the function as the first argument to spawn_thread(); the last argument to spawn_thread() is forwarded as the entry function's data argument. Since data is delivered as a void *, you have to cast the value to the appropriate type within your implementation of the entry function. For example, let's say you define an entry function called lister() that takes a pointer to a BList object as an argument:

   long lister(void *data)
   {
      /* Cast the argument. */
      BList *listObj = (BList *)data;
      ...
   }

To create and run a thread that would execute the lister() function, you call spawn_thread() and resume_thread() thus (excluding error checks):

   BList *listObj = new BList();
   thread_id my_thread;
   
   my_thread = spawn_thread(lister, ..., (void *)listObj);
   resume_thread(my_thread);

The Entry Function's Argument

The spawn_thread() function doesn't copy the data that data points to. It simply passes the pointer through literally. Because of this, you should never pass a pointer that's allocated locally (on the stack).

The reason for this restriction is that there's no guarantee that the entry function will receive any CPU attention before the stack frame from which spawn_thread() was called is destroyed. Thus, the entry function won't necessarily have a chance to copy the pointed-to data before the pointer vanishes. There are ways around this restriction --for example, you could use a semaphore to ensure that the entry function has copied the data before the calling frame exits. A better solution is to use the send_data() function (which does copy its data). See Passing Data to a Thread .

Using a C++ Entry Function

If you're up in C++ territory, you'll probably want to define a class member function that you can use as a thread's entry function. Unfortunately, you can't pass a normal (non- static) member function directly as the entry function argument to spawn_thread()--the system won't know which object it's supposed to invoke the function on (it won't have a this pointer). To get from here to there, you have to declare two member functions:

To "connect" the two functions, you pass an object of the appropriate class (through the data argument) to the static function, and then allow the static function to invoke the non- static function upon that object. An example is called for: Here we define a class that contains a static function called entry_func(), and a non-static function called entryFunc(). By convention, these two are private. In addition, the class declares a public Go() function, and a private thread_id variable:

   class MyClass : public BObject {
   public:
      long Go(void);
   
   private:
      static long entry_func(void *arg);
      long entryFunc(void);
      thread_id my_thread;
   };

entry_func() is the literal entry function. It doesn't really do anything --it simply casts its argument as a MyClass object, and then invokes entryFunc() on the object:

   long MyClass::entry_func(void *arg)
   {
      MyClass *obj = MyClass *arg;
      return (obj->entryFunc());
   }

entryFunc() performs the actual work:

   long MyClass::entryFunc(void)
   {
      /* do something here */
      ...
      return (whatever);
   }

The Go() function contains the spawn_thread() call that starts the whole thing going:

   long MyClass::Go(void)
   {
      my_thread = spawn_thread(entry_func, ..., this);
      return (resume_thread(my_thread));
   }

If you aren't familiar with static member functions, you should consult a qualified C++ textbook. Briefly, the only thing you need to know for the purposes of the technique shown here, is that a static function's implementation can't call (non-static) member functions nor can it refer to member data. Maintain the form demonstrated above and you'll be rewarded in heaven.

Entry Function Return Values

The entry function's protocol declares that the function should return a long value when it exits. This value can be captured by sitting in a wait_for_thread() call until the entry function exits. wait_for_thread() takes two arguments: The thread_id of the thread that you're waiting for, and a pointer to a long into which the value returned by that thread's entry function will be placed. For example:

   thread_id other_thread;
   long result;
   
   other_thread = spawn_thread(...);
   resume_thread(other_thread);
   
   ...
   wait_for_thread(other_thread, &result);

If the target thread is already dead, wait_for_thread() returns immediately (with an error code as described in the function's full description), and the second argument will be set to an invalid value. If you're late for the train, you'll miss the boat.

Warning: You must pass a valid pointer as the second argument to wait_for_thread(); you mustn't pass NULL even if you're not interested in the return value.


Thread Names

A thread can be given a name which you assign through the second argument to spawn_thread(). The name can be 32 characters long (as represented by the B_OS_NAME_LENGTH constant) and needn't be unique--more than one thread can have the same name.

You can look for a thread based on its name by passing the name to the find_thread() function; the function returns the thread_id of the so-named thread. If two or more threads bear the same name, the find_thread() function returns the first of these threads that it finds.

You can retrieve the thread_id of the calling thread by passing NULL to find_thread() :

   thread_id this_thread = find_thread(NULL);

To retrieve a thread's name, you must look in the thread's thread_info structure. This structure is described in the get_thread_info() function description.

Dissatisfied with a thread's name? Use the rename_thread() function to change it. Fool your friends.


Thread Priority

In a multi-threaded environment, the CPUs must divide their attention between the candidate threads, executing a few instructions from this thread, then a few from that thread, and so on. But the division of attention isn't always equal: You can assign a higher or lower priority to a thread and so declare it to be more or less important than other threads.

You assign a thread's priority (an integer) as the third argument to spawn_thread(). There are two categories of priorities:

  • "Time-sharing" priorities (priority values from 1 to 99).

  • "Real-time" priorities (100 and greater).
  • A time-sharing thread (a thread with a time-sharing priority value) is executed only if there are no real-time threads in the ready queue. In the absence of real-time threads, a time-sharing thread is elected to run once every "scheduler quantum" (currently, every three milliseconds). The higher the time-sharing thread's priority value, the greater the chance that it will be the next thread to run.

    A real-time thread is executed as soon as it's ready. If more than one real-time thread is ready at the same time, the thread with the highest priority is executed first. The thread is allowed to run without being preempted (except by a real-time thread with a higher priority) until it blocks, snoozes, is suspended, or otherwise gives up its plea for attention.

    The Kernel Kit defines seven priority constants. Although you can use other, "in- between" value as the priority argument to spawn_thread() , it's strongly suggested that you stick with these:

    Time-Sharing Priority Value
    B_LOW_PRIORITY 5
    B_NORMAL_PRIORITY 10
    B_DISPLAY_PRIORITY 15
    B_URGENT_DISPLAY_PRIORITY 20

    Real-Time Priority Value
    B_REAL_TIME_DISPLAY_PRIORITY 100
    B_URGENT_PRIORITY 110
    B_REAL_TIME_PRIORITY 120


    Synchronizing Threads

    There are times when you may want a particular thread to pause at a designated point until some other (known) thread finishes some task. Here are three ways to effect this sort of synchronization:


    Controlling a Thread

    There are three ways to control a thread while it's running:

    Death and the Main Thread

    As mentioned earlier, the control that's imposed upon a particular thread isn't visited upon the "children" that have been spawned from that thread. (Recall the "thread A spawns thread B then dies" business near the beginning of this overview.) However, the death of an application's main thread can affect the other threads:

    When a main thread dies, it takes the team's heap, its statically allocated objects, and other team-wide resources--such as access to standard IO--with it. This may seriously cripple any threads that linger beyond the death of the main thread.

    It's certainly possible to create an application in which the main thread sets up one or more other threads, gets them running, and then dies. But such applications should be rare. In general, you should try to keep your main thread around until all other threads in the team are dead.


    Passing Data to a Thread

    There are three ways to pass data to a thread:

    The send_data() function sends data from one thread to another. With each send_data() call, you can send two packets of information:

  • a single four-byte value (this is called the code),

  • and an arbitrarily long buffer of arbitrarily-typed data.
  • The function's four arguments identify, in order,

  • the thread that you want to send the data to,

  • the four-byte code,

  • a pointer to the buffer of data (a void *),

  • and the size of the buffer of data, in bytes.
  • In the following example, the main thread spawns a thread, sends it some data, and then tells the thread to run:

       main(int argc, char *argv[])
       {
          thread_id other_thread;
          long code = 63;
          char *buf = "Hello";
       
          other_thread = spawn_thread(entry_func, ...);
          send_data(other_thread, code, (void *)buf, strlen(buf));
          resume_thread(other_thread);
          ...
       }

    The send_data() call copies the code and the buffer (the second and third arguments) into the target thread's message cache and then (usually) returns immediately. In some cases, the four-byte code is all you need to send; in such cases, the buffer pointer can be NULL and the buffer size set to 0.

    To retrieve the data that's been sent to it, the target thread (having been told to run) calls receive_data(). This function returns the four-byte code directly, and copies the data from the message cache into its second argument. It also returns, by reference in its first argument, the thread_id of the thread that sent the data:

       long entry_func(void *data)
       {
          thread_id sender;
          long code;
          char buf[512];
       
          code = receive_data(&sender, (void *)buf, sizeof(buf));
          ...
       }

    Keep in mind that the message data is copied into the second argument; you must allocate adequate storage for the data, and pass, as the final argument to receive_data(), the size of the buffer that you allocated. A slightly annoying aspect of this mechanism is that there isn't any way for the data-receiving thread to determine how much data is in the message cache, so it can't know, before it receives the data, what an "adequate" size for its buffer is. If the buffer isn't big enough to accommodate all the data, the left-over portion is simply thrown away. (But at least you don't get a segmentation fault.)

    As shown in the example, send_data() is called before the target thread is running. This feature of the system is essential in situations where you want the target thread to receive some data as its first act (as demonstrated above). However, send_data() isn't limited to this use--you can also send data to a thread that's already running.

    Blocking when Sending and Receiving

    A thread's message cache isn't a queue; it can only hold one message at a time. If you call send_data() twice with the same target thread, the second call will block until the target reads the first transmission through a call to receive_data(). Analogously, receive_data() will block if there isn't (yet) any data to receive.

    If you want to make sure that you won't block when receiving data, you should call has_data() before calling receive_data(). has_data() takes a thread_id argument, and returns TRUE if that thread has a message waiting to be read:

       if (has_data(find_thread(NULL)))
          code = receive_data(...);

    You can also use has_data() to query the target thread before sending it data. This, you hope, will ensure that the send_data() call won't block:

       if (!has_data(target_thread))
          send_data(target_thread, ...);

    This usually works, but be aware that there's a race condition between the has_data() and send_data() calls. If yet another thread sends a message to the same target in that time interval, your send_data() (might) block.


    Functions


    exit_thread(), kill_thread() , kill_team()

          void exit_thread(long return_value)
          long kill_thread(thread_id thread)
          long kill_team(team_id team)

    These functions command one or more threads to halt execution:

    Exiting a thread is a fairly safe thing to do--since a thread can only exit itself, it's assumed that the thread knows what it's doing. Killing some other thread or an entire team is a bit more drastic since the death certificate(s) will be delivered at an indeterminate time. Nonetheless, in every case (exiting or killing) the system reclaims the resources that the thread (or team) had claimed. So executing a thread shouldn't cause a memory leak.

    Keep in mind that threads die automatically (and their resources are reclaimed) if allowed to exit naturally from their entry functions. You should only need to kill a thread if something has gone screwy.

    The kill functions return B_BAD_THREAD_ID or B_BAD_TEAM_ID if the argument is invalid. Otherwise, they return B_NO_ERROR.


    find_thread()

          thread_id find_thread(const char *name)

    Finds and returns the thread with the given name. A name argument of NULL returns the calling thread. If name doesn't identify a thread, B_NAME_NOT_FOUND is returned.

    A thread's name is assigned when the thread is spawned. The name can be changed thereafter through the rename_thread() function. Keep in mind that thread names needn't be unique: If two (or more) threads boast the same name, a find_thread() call on that name returns the first so-named thread that it finds.


    get_team_info(), get_nth_team_info()

          long get_team_info(team_id team, team_info *info)
          long get_nth_team_info(long n, team_info *info)

    These functions copy, into the info argument, the team_info structure for a particular team:

    The team_info structure is defined as:

          typedef struct {
                team_id team;
                long thread_count;
                long image_count;
                long area_count;
                thread_id debugger_nub_thread;
                port_id debugger_nub_port;
                long argc;
                char args[64];
             } team_info

    The first field is obvious; the next three reasonably so: They give the number of threads that have been spawned, images that have been loaded, and areas that have been created or cloned within this team.

    The debugger fields are used by the, uhm, the...debugger?

    The argc field is the number of command line arguments that were used to launch the team; args is a copy of the first 64 characters from the command line invocation. If this team is an application that was launched through the user interface (by double-clicking, or by accepting a dropped icon), then argc is 1 and args is the name of the application's executable file.

    Both functions return B_NO_ERROR upon success. If the designated team isn't found --because team in get_team_info() isn't valid, or n in get_nth_team_info() is out-of-bounds--the functions return BAD_TEAM_ID .


    get_thread_info(), get_nth_thread_info()

          long get_thread_info(thread_id thread, thread_info *info)
          long get_nth_thread_info(team_id team, long n, thread_info *info)

    These functions copy, into the info argument, the thread_info structure for a particular thread:

    The thread_info structure is defined as:

          typedef struct {
                thread_id thread;
                team_id team;
                char name[B_OS_NAME_LENGTH];
                thread_state state;
                long priority;
                sem_id sem;
                double user_time;
                double kernel_time;
                void *stack_base;
                void *stack_end;
             } thread_info

    The fields in the structure are:

  • thread. The thread_id number of the thread.

  • team. The team_id of the thread's team.

  • name. The name assigned to the thread.

  • state. What the thread is currently doing (see the thread state constants, below).

  • priority. The level of attention the thread gets (see the priority constants, below).

  • sem. If the thread is waiting to acquire a semaphore, this is that semaphore.

  • user_time. The time, in microseconds, the thread has spent executing user code.

  • kernel_time. The amount of time the kernel has run on the thread's behalf.

  • stack_base. A pointer to the first byte in the thread's execution stack.

  • stack_end. A pointer to the last byte in the thread's execution stack.
  • The last two fields are only meaningful if you understand the execution stack format. Keep in mind that the stack grows down, from higher to lower addresses. Thus, stack_base will always be greater than stack_end.

    The value of the state field is one of following thread_state constants:

    Constant Meaning
    B_THREAD_RUNNING The thread is currently receiving attention from a CPU.
    B_THREAD_READY The thread is waiting for its turn to receive attention.
    B_THREAD_SUSPENDED The thread has been suspended or is freshly-spawned and is waiting to start.
    B_THREAD_WAITING The thread is waiting to acquire a semaphore. (Note that when a thread is sitting in a wait_for_thread() call, or is waiting to read from or write to a port, it's actually waiting to acquire a semaphore.) When in this state, the sem field of the thread_info structure is set to the sem_id number of the semaphore the thread is attempting to acquire.
    B_THREAD_RECEIVING The thread is sitting in a receive_data() function call.
    B_THREAD_ASLEEP The thread is sitting in a snooze() call.

    The value of the priority field takes one of the following long constants (the difference between "time-sharing" priorities and "real-time" priorities is explained in Thread Priority ):

    Time-Sharing Priority Value
    B_LOW_PRIORITY 5
    B_NORMAL_PRIORITY 10
    B_DISPLAY_PRIORITY 15
    B_URGENT_DISPLAY_PRIORITY 20

    Real-Time Priority Value
    B_REAL_TIME_DISPLAY_PRIORITY 100
    B_URGENT_PRIORITY 110
    B_REAL_TIME_PRIORITY 120

    Thread info is provided primarily as a debugging aid. None of the values that you find in a thread_info structure are guaranteed to be valid --the thread's state, for example, will almost certainly have changed by the time get_thread_info() returns.

    Both functions return B_NO_ERROR upon success. If the designated thread isn't found --because thread in get_thread_info() isn't valid, or n in get_nth_thread_info() is out of range--the functions return B_BAD_THREAD_ID. If its team argument is invalid, get_nth_thread_info() return B_BAD_TEAM_ID .

    See also: get_team_info()


    has_data()

          bool has_data(thread_id thread)

    Returns TRUE if the given thread has an unread message in its message cache, otherwise returns FALSE . Messages are sent to a thread's message cache through the send_data() call. To retrieve a message, you call receive_data().

    See also: send_data(), receive_data()


    kill_team() see exit_thread()


    receive_data()

          long receive_data(thread_id *sender, 
             void *buffer, 
             long buffer_size)

    Retrieves a message from the thread's message cache. The message will have been placed there through a previous send_data() function call. If the cache is empty, receive_data() blocks until one shows up--it never returns empty-handed.

    The thread_id of the thread that called send_data() is returned by reference in the sender argument. Note that there's no guarantee that the sender will still be alive by the time you get its ID. Also, the value of sender going into the function is ignored --you can't ask for a message from a particular sender.

    The send_data() function copies two pieces of data into a thread's message cache: A single four-byte code, and a arbitrarily long data buffer. The four-byte code is delivered, here, as receive_data()'s return value. The contents of the buffer part of the cache is copied into receive_data()'s buffer argument (you must allocate and free buffer yourself). The buffer_size argument tells the function how many bytes of data to copy. If you don't need the data buffer--if the code value returned directly by the function is sufficient --you set buffer to NULL and buffer_size to 0.

    Unfortunately, there's no way to tell how much data is in the cache before you call receive_data(). If there's more data than buffer can accommodate, the unaccommodated portion is discarded--a second receive_data() call will not read the rest of the message. Conversely, if receive_data() asks for more data than was sent, the function returns with the excess portion of buffer unmodified--receive_data() doesn't wait for another send_data() call to provide more data with which to fill up the buffer.

    Each receive_data() corresponds to exactly one send_data(). Lacking a previous invocation of its mate, receive_data() will block until send_data() is called. If you don't want to block, you should call has_data() before calling receive_data() (and proceed to receive_data() only if has_data() returns TRUE).

    See also: send_data(), has_data()


    rename_thread()

          long rename_thread(thread_id thread, const char *name)

    Changes the name of the given thread to name. Keep in mind that the maximum length of a thread name is B_OS_NAME_LENGTH (32 characters).

    If the thread argument isn't a valid thread_id number, B_BAD_THREAD_ID is returned. Otherwise, the function returns B_NO_ERROR.


    resume_thread()

           long resume_thread(thread_id thread)

    Tells a new or suspended thread to begin executing instructions. If the thread has just been spawned, its execution begins with the entry-point function (keep in mind that a freshly spawned thread doesn't run until told to do so through this function). If the thread was previously suspended (through suspend_thread() ), it continues from where it was suspended.

    This function only works on threads that have a status of B_THREAD_SUSPENDED (newly spawned threads are born with this state). You can't use this function to wake up a sleeping thread ( B_THREAD_ASLEEP), or to unblock a thread that's waiting to acquire a semaphore (B_THREAD_WAITING ) or waiting in a receive_data() call (B_THREAD_RECEIVING ).

    If the thread argument isn't a valid thread_id number, B_BAD_THREAD_ID is returned. If the thread exists but isn't suspended, B_BAD_THREAD_STATE is returned (the target thread is unaffected in this case). Otherwise, the function returns B_NO_ERROR .

    See also: wait_for_thread()


    send_data()

          long send_data(thread_id thread, 
             long code, 
             void *buffer, 
             long buffer_size)

    Copies data into thread's message cache. The target thread can then retrieve the data from the cache by calling receive_data() . There are two parts to the data that you send:

    If you only need to send the code, you should set buffer to NULL and buffer_size to 0. After send_data() returns you can free the buffer argument

    Normally, send_data() returns immediately --it doesn't wait for the target to call receive_data() . However, send_data() will block if the target has an unread message from a previous send_data()--keep in mind that a thread's message cache is only one message deep. A thread that's blocked in send_data() assumes B_THREAD_WAITING status.

    If the target thread couldn't allocate enough memory for its copy of buffer, this function fails and returns B_NO_MEMORY. If thread doesn't identify a valid thread, BAD_THREAD_ID is returned. Otherwise, the function succeeds and returns B_NO_ERROR.

    See also: receive_data() , has_data()


    set_thread_priority()

           long set_thread_priority(thread_id thread, long new_priority)

    Resets the given thread's priority to new_priority. The priority level constants that are defined by the Kernel Kit are:

    The value of the priority field takes one of the following long constants (the difference between "time-sharing" priorities and "real-time" priorities is explained in Thread Priority ):

    Time-Sharing Priority Value
    B_LOW_PRIORITY 5
    B_NORMAL_PRIORITY 10
    B_DISPLAY_PRIORITY 15
    B_URGENT_DISPLAY_PRIORITY 20

    Real-Time Priority Value
    B_REAL_TIME_DISPLAY_PRIORITY 100
    B_URGENT_PRIORITY 110
    B_REAL_TIME_PRIORITY 120

    The difference between "time-sharing" priorities and "real-time" priorities is explained in Thread Priority .

    If thread is invalid, B_BAD_THREAD_ID is returned. Otherwise, the priority to which the thread was set is returned.


    snooze()

           long snooze(double microseconds)

    Pauses the calling thread for the given number of microseconds. The thread's state is set to B_THREAD_ASLEEP while it's snoozing and restored to its previous state when it awakes.

    The function returns B_ERROR if microseconds is less than 0.0, otherwise it returns B_NO_ERROR. Note that it isn't illegal to put a thread to sleep for 0.0 microseconds, but neither is it effectual; a call of snooze(0.0) is, essentially, ignored.


    spawn_thread()

          thread_id spawn_thread(thread_entry func, 
             const char *name, 
             long priority, 
             void *data)

    Creates a new thread and returns its thread_id identifier (a positive integer). The arguments are:

    Time-Sharing Priority Value
    B_LOW_PRIORITY 5
    B_NORMAL_PRIORITY 10
    B_DISPLAY_PRIORITY 15
    B_URGENT_DISPLAY_PRIORITY 20

    Real-Time Priority Value
    B_REAL_TIME_DISPLAY_PRIORITY 100
    B_URGENT_PRIORITY 110
    B_REAL_TIME_PRIORITY 120

    For a complete explanation of these constants, see Thread Priority .

    A newly spawned thread is in a suspended state (B_THREAD_SUSPENDED). To tell the thread to run, you pass its thread_id to the resume_thread() function. The thread will continue to run until the entry-point function exits, or until the thread is explicitly killed (through a call to exit_thread(), kill_thread(), or kill_team()).

    If all thread_id numbers are currently in use, spawn_thread() returns B_NO_MORE_THREADS; if the operating system lacks the memory needed to create the thread (which should be rare), B_NO_MEMORY is returned.


    suspend_thread()

          long suspend_thread(thread_id thread)

    Halts the execution of the given thread, but doesn't kill the thread entirely. The thread remains suspended until it is told to run through the resume_thread() function. Nothing prevents you from suspending your own thread, i.e.:

       suspend_thread(find_thread(NULL));

    Of course, this is only smart if you have some other thread that will resume you later.

    This function only works on threads that have a status of B_THREAD_RUNNING or B_THREAD_READY . In other words, you can't suspend a thread that's sleeping, waiting to acquire a semaphore, waiting to receive data, or that's already suspended.

    If the thread argument isn't a valid thread_id number, B_BAD_THREAD_ID is returned. If the thread exists, but is neither running nor ready to run, B_BAD_THREAD_STATE is returned. Otherwise, the function returns B_NO_ERROR .


    wait_for_thread()

          long wait_for_thread(thread_id thread, long *exit_value)

    This function causes the calling thread to wait until thread (the "target thread") has died. If thread is suspended, the wait_for_thread() call will cause it to resume. Thus, you can use wait_for_thread() to tell a newly-spawned thread to start running.

    When the target thread is dead, the value that was returned by its entry function (or that's imposed by exit_thread(), if such was called) is returned by reference in exit_value. If the target thread was killed (by kill_thread() or kill_team() ), or if the entry function doesn't return a value, the value returned in exit_value will be unreliable.

    If the target thread has already exited or is otherwise invalid, this function returns B_BAD_THREAD_ID, otherwise it returns B_NO_ERROR., Note that if the thread is killed while you're waiting for it, the function returns B_NO_ERROR.

    See also: resume_thread()




    The Be Book, HTML Edition, for Developer Release 8 of the Be Operating System.

    Copyright © 1996 Be, Inc. All rights reserved.

    Be, the Be logo, BeBox, BeOS, BeWare, and GeekPort are trademarks of Be, Inc.

    Last modified September 6, 1996.