Threads and Teams

Declared in: be/kernel/OS.h

Library: libroot.so

A thread is a synchronous 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 multithreaded: 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.

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 Death and the Main Thread.)

Threads and the POSIX fork() function are not compatible. You can't mix calls to spawn_thread() (the function that creates a new thread) and fork() in the same application: If you call spawn_thread() and then try to call fork(), the fork() call will fail. And vice versa.


Teams

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.


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.

Threads and App Images

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_image() function. Loading an image causes a separate program, identified as a file, to be launched by the system. For more information on the load_image() function, see "Threads and Teams."


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() is typically used to start the thread that was created through load_image().


The Thread Function

When you call spawn_thread(), you must identify the new thread's thread 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 thread function exits, the thread is automatically killed.

The thread_func type represents a pointer to a thread function:

   typedef int32 (*thread_func)(void *);

You specify a thread function by passing a thread_func as the first argument to spawn_thread(); the last argument to spawn_thread() is forwarded as the thread 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 thread function. For example, let's say you define a thread function called lister() that takes a pointer to a BList object as an argument:

   int32 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 Thread 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 thread function will receive any CPU attention before the stack frame from which spawn_thread() was called is destroyed. Thus, the thread function won't necessarily have a chance to copy the pointed-to data before the data pointer is freed. There are ways around this restriction--for example, you could use a semaphore to ensure that the thread function has copied the data before the calling frame exits. A better solution is to forego the data argument and use the send_data() function (which does copy its data). See Passing Data to a Thread.

Using a C++ Thread 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 function. Unfortunately, you can't pass a normal (non-static) member function directly as the thread 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 thread_func(), and a non-static function called threadFunc(). By convention, these two are private. In addition, the class declares a public Go() function, and a private thread_id variable:

   class MyClass {
   public:
      status_t Go(void);
   
   private:
      static int32 thread_func(void *arg);
      int32 threadFunc(void);
      thread_id my_thread;
   };

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

   int32 MyClass::thread_func(void *arg)
   {
      MyClass *obj = (MyClass *)arg;
      return (obj->threadFunc());
   }

threadFunc() performs the actual work:

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

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

   status_t MyClass::Go(void)
   {
      my_thread = spawn_thread(thread_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.

Thread Function Return Values

The thread function's protocol declares that the function should return a int32 value when it exits. This value can be captured by sitting in a wait_for_thread() call until the thread function exits. wait_for_thread() takes two arguments:

For example:

   thread_id other_thread;
   status_t 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.

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" and "real-time."

The Kernel Kit defines seven priority constants. Although you can use other, "in-between" value as the priority argument to spawn_thread(), it's 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

You can call the suggest_thread_priority() function to let the Kernel Kit determine a good priority for your thread. This function accepts as arguments information about the thread's scheduling and CPU needs, and returns a reasonable priority value to use when spawning the thread.

      int32 suggest_thread_priority(uint32 what = B_DEFAULT_MEDIA_PRIORITY,
         int32 period = 0,
         bigtime_t jitter = 0,
         bigtime_t length = 0)

The what indicates the type of processing the thread will handle; period is the number of times per second the thread needs to run. The jitter indicates how far, in microseconds, the period can vary, and length specifies approximately how long each invocation of the thread will take, in microseconds.

For example, if you're spawning a thread to handle refreshing the video for a computer game, and you want the display to update 30 times per second, you might use code similar to the following:

   int32 priority;
   priority = suggest_thread_priority(B_LIVE_3D_RENDERING, 30, 1000, 150);
   th = spawn_thread(func, "render_thread", priority, NULL)

This spawns the rendering thread with a priority appropriate for a thread for live 3D rendering which wants to be run 30 times per second, with a variation of only 1000 microseconds. Each invocation of the thread's code is estimated to take 150 microseconds. Obviously the jitter and length values would have to be tuned to the particular application.


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 four ways to control a thread while it's running:

Feeling itchy? Try killing an entire team of threads: The kill_team() function is more than a system call. It's therapy.

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. However, the death of an application's main thread can affect the other threads:

When a main thread dies, the game is pretty much over. The main thread 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 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:

The function's protocol is shown below

   status_t send_data(thread_id thread, 
               int32 code, 
               void *buffer, 
               size_t buffer_size)

The arguments are:

In the following example, the main thread spawns a thread, sends it some data, and then tells the thread to run:

   main()
   {
      thread_id other_thread;
      int32 code = 63;
      char *buf = "Hello";
   
      other_thread = spawn_thread(thread_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():

   status_t receive_data(thread_id *sender, 
               void *buffer, 
               size_t buffer_size)

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:

   int32 thread_func(void *data)
   {
      thread_id sender;
      int32 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...

There isn't any way for the data-receiving thread to determine how much data is in the message cache. It can't tell, 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.


Thread Functions


estimate_max_scheduling_latency()

      bigtime_t estimate_max_scheduling_latency(thread_id th = -1)

Returns the scheduling latency, in microseconds, of the specified thread. Specify a thread_id of -1 to return the scheduling latency of the current thread.


exit_thread(), kill_thread(), kill_team()

      void exit_thread(status_t return_value)
      status_t kill_thread(thread_id thread)
      status_t 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. In addition, killing a thread can leak memory since resources that were allocated by the thread may not be freed. Killing an entire team, on the other hand, won't leak since the system reclaims all resources when the team dies.

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

RETURN CODES


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.

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. There's no way to iterate through identically-named threads.

RETURN CODES


get_team_info(), get_next_team_info(), team_info

      status_t get_team_info(team_id team, team_info *info)
      status_t get_next_team_info(int32 *cookie, team_info *info)
      typedef struct {} team_info

The functions copy, into the info argument, the team_info structure for a particular team. The get_team_info() function retrieves information for the team identified by team.

The get_next_team_info() version lets you step through the list of all teams. The cookie argument is a placemark; you set it to 0 on your first call, and let the function do the rest. The function returns B_BAD_VALUE when there are no more areas to visit:

   /* Get the team_info for every team. */
   team_info info;
   int32 cookie = 0;
   
   while (get_next_team_info(0, &cookie, &info) == B_NO_ERROR)
      ...

The team_info structure is defined as:

      typedef struct {
            team_id team;
            int32 thread_count;
            int32 image_count;
            int32 area_count;
            thread_id debugger_nub_thread;
            port_id debugger_nub_port;
            int32 argc;
            char args[64];
            uid_t uid;
            gid_t gid;
         } 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.

uid and gid identify the user and group that "owns" the team. You can use these values to play permission games.

RETURN CODES


get_thread_info(), get_next_thread_info(), thread_info

      status_t get_thread_info(thread_id thread, thread_info *info)
      status_t get_next_thread_info(team_id team, 
         int32 *cookie, 
         thread_info *info)
      typedef struct {} thread_info

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

The get_thread_info() function gets the information for the thread identified by thread.

The get_next_thread_info() function lets you step through the list of a team's threads through iterated calls. The team argument identifies the team you want to look at; a team value of 0 means the team of the calling thread. The cookie argument is a placemark; you set it to 0 on your first call, and let the function do the rest. The function returns B_BAD_VALUE when there are no more threads to visit:

   /* Get the thread_info for every thread in this team. */
   thread_info info;
   int32 cookie = 0;
   
   while (get_next_thread_info(0, &cookie, &info) == B_OK)
      ...

The thread_info structure is defined as:

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

The fields in the structure are:

The last two fields are only meaningful if you understand the execution stack format. Currently, the stack size is fixed at around 256k.

The two stack pointers are currently inverted such that stack_base is less than stack_end. (In a stack-grows-down world, the base should be greater than the 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. 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. (Note that when a thread is blocked in wait_for_thread(), read_port(), or write_port(), it's actually waiting to acquire a semaphore.)
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 describes the thread's "urgency"; the higher the value, the more urgent the thread. The more urgent the thread, the more attention it gets from the CPU. Expected priority values fall between 0 and 120. See "Thread Priority" for the full story.

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.

RETURN CODES


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().


receive_data()

      int32 receive_data(thread_id *sender, 
         void *buffer, 
         size_t 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:

Unfortunately, there's no way to tell how much data is in the cache before you call receive_data():

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).

RETURN CODES


rename_thread()

      status_t 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).

RETURN CODES


resume_thread()

      status_t resume_thread(thread_id thread)

Tells a new or suspended thread to begin executing instructions.

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).

However, you can unblock a thread by suspending it and then resuming it. Blocked threads that are resumed return B_INTERRUPTED.

resume_thread() is the same as sending a SIGCONT signal to the thread.

RETURN CODES


send_data()

      status_t send_data(thread_id thread, 
         int32 code, 
         void *buffer, 
         size_t 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.

RETURN CODES


set_thread_priority()

       status_t set_thread_priority(thread_id thread, int32 new_priority)

Resets the given thread's priority to new_priority.

The value of the new_priority can be any positive integer, but it's recommended that you stick with the following 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

RETURN CODES


snooze()

       status_t snooze(bigtime_t microseconds)

Pauses the calling thread for the given number of microseconds (the function blocks until then). The thread's state is set to B_THREAD_ASLEEP while it's snoozing.

To wake up a thread prematurely, suspend the thread and then resume it:

   thread_info tinfo;
   
   get_thread_info(thread, &tinfo);
   if (tinfo.state == B_THREAD_ASLEEP) {
      suspend_thread(thread);
      /* Just to be sure... */
      snooze(1000);
      resume_thread(thread);
   }

The snooze() in the "controlling" thread is a precaution; if you're pulling this sort of stunt in your own code, you should also snooze for a bit between the suspend and resume--signals are funny that way.

There is, of course, a race condition in this example--the thread could wake up just after the get_thread_info() call. But, as with all signalling operations, the suspend/resume trick is indeterminate and wee bit dangerous. (Actually, the only situation where the race could cause trouble is if the thread wakes up, blocks on a semaphore, is then suspended/resumed AND it doesn't check the acquire_sem() return value.)

RETURN CODES


snooze_until()

      status_t snooze_until(bigtime_t time, int timebase)

Pauses the thread until an absolute time measured in the given timebase. Currently, the only allowed value for timebase is B_SYSTEM_TIMEBASE, for which time is equivalent to the system time as returned by system_time().

RETURN CODES


spawn_thread()

      thread_id spawn_thread(thread_func func, 
         const char *name, 
         int32 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 thread function exits, or until the thread is explicitly killed (through a signal or a call to exit_thread(), kill_thread(), or kill_team()).

RETURN CODES


suggest_thread_priority()

      int32 suggest_thread_priority(uint32 what = B_DEFAULT_MEDIA_PRIORITY,
         int32 period = 0,
         bigtime_t jitter = 0,
         bigtime_t length = 0)

Given information about a thread that's going to be created, this function returns a suggested priority to be assigned to that thread when it's created.

The what value is a bit mask that indicates the type of activity for which the thread will be used. The possible values are:

Constant Meaning
B_DEFAULT_MEDIA_PRIORITY The thread isn't doing anything specialized.
B_OFFLINE_PROCESSING The thread is doing offline computationss (non-real-time).
B_STATUS_RENDERING The thread is rendering a status or preview display.
B_USER_INPUT_HANDLING The thread is handling user input.
B_LIVE_VIDEO_MANIPULATION The thread is processing live video.
B_VIDEO_PLAYBACK The thread is playing back video from a hardware device.
B_VIDEO_RECORDING The thread is recording video from a hardware device.
B_LIVE_AUDIO_MANIPULATION The thread is doing real-time manipulation of live audio data.
B_AUDIO_PLAYBACK The thread is playing back audio from a hardware device
B_AUDIO_RECORDING The thread is recording audio from a hardware device.
B_LIVE_3D_RENDERING The thread is performing live 3D rendering.
B_NUMBER_CRUNCHING The thread is doing data processing.

The B_LIVE_VIDEO_MANIPULATION and B_LIVE_AUDIO_MANIPULATION values can be used for both filtering and compression or decompression. The B_*_PLAYBACK and B_*_RECORDING constants indicate recording to or playback from an actual hardware device.

period is the number of times per second the thread needs to be run (specify 0 if it needs to run continuously). jitter is an estimate, in microseconds, of how much the period can vary as long as the average stays at period times per second.

length is an approximation of the amount of time, in microseconds, the thread will typically run per invocation (ie, the amount of time that will pass between the moment it receives a message, through processing it, until it's again waiting for another message).


suspend_thread()

      status_t suspend_thread(thread_id thread)

Halts the execution of the given thread, but doesn't kill the thread entirely. The thread remains suspended (suspend_thread() blocks) until it's 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.

You can suspend any thread, regardless of its current state. But be careful: If the thread is blocked on a semaphore (for example), the subsequent resume_thread() call will "hop over" the semaphore acquisition.

Suspensions don't nest. A single resume_thread() unsuspends a thread regardless of the number of suspend_thread() calls it has received.

suspend_thread() is the same as sending a SIGSTOP signal to the thread.

RETURN CODES


wait_for_thread()

      status_t wait_for_thread(thread_id thread, status_t *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 thread 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 thread function doesn't return a value, the value returned in exit_value is unreliable.

RETURN CODES






The Be Book, in lovely HTML, for BeOS Release 4.

Copyright © 1998 Be, Inc. All rights reserved.

Last modified November 10, 1998.