The Kernel Kit Table of Contents     The Kernel Kit Index

Threads and Teams

Declared in: be/kernel/OS.h unless otherwise noted

Library: libroot.so

A thread is a synchronous process that executes a series of program instructions. When you launch an application, an initial thread—the main thread—is automatically created (or spawned) and told to run. 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. The collection of threads that are spawned from the main thread—in other words, the threads that comprise an application—is called a team. All the threads in all teams run concurrently and asynchronously with each other.

For more information on threads and teams, see "Thread and Team Concepts".


Thread and Team Functions


estimate_max_scheduling_latency()

Declared in: be/kernel/scheduler.h

                                                         
  

bigtime_t estimate_max_scheduling_latency(thread_id thread = -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() , on_exit_thread()

                                                         
  

void exit_thread(status_t return_value)

status_t kill_thread(thread_id thread)

status_t kill_team(team_id team)

status_t on_exit_thread(void (*callback)(void *), void *data)

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

                                                         
  

status_t get_team_info(team_id team, team_info *info)

status_t get_next_team_info(int32 *cookie, team_info *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. For information about the kernel, use B_SYSTEM_TEAM as the team argument.

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

See team_info for a description of that structure.

RETURN CODES


get_thread_info() , get_next_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)

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 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 Priorities" 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() see send_data()


rename_thread()

                                                         
  

status_t rename_thread(thread_id thread, const char *name)

Changes the name of the given thread to name. The name can be no longer than 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. If the thread has just been spawned, it enters and executes the thread function declared in spawn_thread(). If the thread was previously suspended (through suspend_thread()), it continues from where it was suspended.

You can't use this function to wake up a sleeping thread, or to unblock a thread that's waiting to acquire a semaphore or waiting in a receive_data() call. However, you can unblock any of these threads by suspending and then resuming. Blocked threads that are resumed return B_INTERRUPTED.

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

RETURN CODES


receive_data()

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


send_data() , receive_data() , has_data()

                                                         
  

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

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

bool has_data(thread_id thread)

Every thread has a one-message-deep message cache associated with it. These functions access that cache.

send_data() copies a message into thread's message cache. The target thread retrieves the message (and empties the cache) by calling receive_data().

There are two parts to the message:

In addition to returning the code directly, and copying the message data into its buffer argument, receive_data() sets sender to the id of the thread that sent the message.

send_data() blocks if there's an unread message in the target thread's cache; otherwise it returns immediately (i.e. it doesn't wait for the target to call receive_data()). Analogously, receive_data() blocks until there's a message to retrieve.

In the following example, the main thread spawns a thread, sends it a message, 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);
      ...
   }

To retrieve the message, the target thread calls receive_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 buffer; you must allocate adequate storage for the data. If the buffer isn't big enough to accommodate all the datain the message, the left-over portion is thrown away. Note, however, that there isn't any way for a thread to determine how much data has been copied into its message cache.

has_data() returns true if thread has a message in its message cache. Ostensibly, you use this function before calling send_data() or receive_data() to avoid blocking:

   if (!has_data(target_thread)) 
      err = send_data(target_thread, ...);
   
   /* or */
   
   if (has_data(find_thread(NULL))
      code = receive_data(...);

This works for receive_data(), but notice that there's a race condition between the has_data() and send_data() calls. Another thread could send a message to the target in the interim.

RETURN CODES

send_data() returns:


set_thread_priority() , suggest_thread_priority()

                                                         
  

status_t set_thread_priority(thread_id thread, int32 new_priority)

Declared in: be/kernel/scheduler.h
                                                         
  

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

set_thread_priority() resets the given thread's priority to new_priority. The priority is expected to be between 0 and 120. See "Thread Priorities" for a description of the priority scheme, and "Thread Priority Values" for a list of pre-defined priority constants.

suggest_thread_priority() takes information about a thread and returns a suggested priority that you can pass to set_thread_priority() (or, more likely, to spawn_thread()).

The what value is a bit mask that indicates the type of activities the thread will be used for. The possible values are listed in Suggested Thread Priorities.

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 (i.e., 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).

For example, if you're spawning a thread to handle video refresh 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.

RETURN CODES

set_thread_priority() returns...


snooze() , snooze_until()

                                                         
  

status_t snooze(bigtime_t microseconds)

status_t snooze_until(bigtime_t microseconds, int timebase)

snooze() blocks the calling thread for the given number of microseconds.

snooze_until() blocks until an absolute time measured in the given timebase. Currently, the only allowed value for timebase is B_SYSTEM_TIMEBASE, which measures time against the system clock (as reported by system_time()).

RETURN CODES


snooze_until() see snooze()


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:

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() see set_thread_priority()


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 (or freshly spawned), wait_for_thread() will resume it.

When the target thread is dead, the value that was returned by its thread function (or imposed by exit_thread()) is returned 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 will be unreliable.

 
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.


RETURN CODES


Thread and Team Structures and Types


team_id , thread_id

typedef int32 team_id ;

typedef int32 thread_id ;

These id numbers uniquely identify teams and threads, respecitvely.


team_info

                                                         
  

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 team_info structure returns information about a team. To retrieve one of these structures, use get_team_info() or get_next_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.


thread_func

                                                         
  

typedef int32 (*thread_func)(void *data);

thread_func is the prototype for a thread's thread function. 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. When the thread function exits, the spawned thread is automatically killed. To retrieve a thread_func's return value, some other thread must be waiting in a wait_for_thread() call.

Note that spawn_thread() doesn't copy the data that data points to. It simply passes the pointer through literally. Never pass a pointer that's allocated locally (on the stack).


thread_info

                                                         
  

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

The thread_info structure contains information about a thread. To retrieve one of these structure, use get_thread_info() or get_next_thread_info().

The thread, team, and name fields contain the indicated information.

state describes what the thread is currently doing (see thread_state for the list of states). If the thread is waiting to acquire a semaphore, sem is that semaphore.

priority is a value that indicates the level of attention the thread gets (see Thread Priority).

user_time and kernel_time are the amounts of time, in microseconds, the thread has spent executing user code and the amount of time the kernel has run on the thread's behalf, respectively.

stack_base and stack_end are pointers to the first byte and last bytes in the thread's execution stack. 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.)



Thread and Team Constants


B_SYSTEM_TEAM

                                                         
  

#define B_SYSTEM_TEAM ...

Use this constant as the first argument to get_team_info() to get team information about the kernel).


B_SYSTEM_TIMEBASE

                                                         
  

#define B_SYSTEM_TIMEBASE ...

The system timebase constant is used as a basis for time measurement in the snooze_until() function. (Currently, it's the only timebase available.)


be_task_flags

Declared in: be/kernel/scheduler.h
                                                         
  

enum be_task_flags { B_DEFAULT_MEDIA_PRIORITY,
      B_OFFLINE_PROCESSING,
       B_STATUS_RENDERING,
      B_USER_INPUT_HANDLING
      
B_LIVE_VIDEO_MANIPULATION
      };

Constant Meaning
B_DEFAULT_MEDIA_PRIORITY The thread isn't doing anything specialized.
B_OFFLINE_PROCESSING The thread is doing non-real-time computations.
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_MANIPULATIO N The thread is processing live video (filtering, compression, decompression, etc.).
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_MANIPULATIO N The thread is doing real-time manipulation of live audio data (filtering, compression, decompression, etc.).
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.

These constants describe what the thread is designed to do. You use these constants when asking for a suggested priority (see suggest_thread_priority()).

 
These constants may not be used as actual thread priority values—do not pass one of these values as the priority argument to spawn_thread().



Thread Priority Values

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

Real-Time Priority Value
B_REAL_TIME_DISPLAY_PRIORITY 100
B_URGENT_PRIORITY 110
B_REAL_TIME_PRIORITY 120

The thread priority values are used to set the "urgency" of a thread. Although you can reset a thread's priority through set_thread_priority(), the priority is initially—and almost always permanently—set in spawn_thread(). As shown here, there are two types of


thread_state

                                                         
  

enum { ... } thread_state

State 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. The sem field of the thread's thread_info structure will tell you which 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.

A thread's state tells you what the thread is currently doing. To get the state, look in the state field of the thread_info structure (retrieved through get_thread_info()).


The Kernel Kit Table of Contents     The Kernel Kit Index


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

Copyright © 2000 Be, Inc. All rights reserved..