NSPR Reference Previous Contents Next |
Threading Types and Constants
Threading Functions
A thread has a limited number of resources that it truly owns. These resources
include a stack and the CPU registers (including PC). To an NSPR client, a thread is
represented by a pointer to an opaque structure of type PRThread
. A thread is
created by an explicit client request and remains a valid, independent execution
entity until it returns from its root function or the process abnormally terminates.
Threads are critical resources and therefore require some management. To
synchronize the termination of a thread, you can join it with another thread (see
PR_JoinThread
). Joining a thread provides definitive proof that the target thread
has terminated and has finished with both the resources to which the thread has
access and the resources of the thread itself.
For an overview of the NSPR threading model and sample code that illustrates its use, see Chapter 1 "Introduction to NSPR."
For API reference information related to thread synchronization, see Chapter 5 "Locks" and Chapter 6 "Condition Variables."
#include <prthread.h>
typedef struct PRThread PRThread;
PRThread
. This pointer is a required parameter for most of the functions that
operate on threads.
A PRThread*
is the successful result of creating a new thread. The identifier
remains valid until it returns from its root function and, if the thread was created
joinable, is joined.
PR_CreateThread
.
#include <prthread.h>
typedef enum PRThreadType {
PR_USER_THREAD,
PR_SYSTEM_THREAD
} PRThreadType;
PR_USER_THREAD |
PR_Cleanup blocks until the last thread of type
PR_USER_THREAD terminates.
|
PR_SYSTEM_THREAD |
NSPR ignores threads of type PR_SYSTEM_THREAD when
determining when a call to PR_Cleanup should return.
|
PR_CreateThread
or
returned by PR_GetThreadScope
.
#include <prthread.h>
typedef enum PRThreadScope {
PR_LOCAL_THREAD,
PR_GLOBAL_THREAD
PR_GLOBAL_BOUND_THREAD
} PRThreadScope;
PRThreadScope
specifies how a thread is scheduled: either
locally by NSPR within the process (a local thread) or globally by the host (a global
thread).
Global threads are scheduled by the host OS and compete with all other threads on the host OS for resources. They are subject to fairly sophisticated scheduling techniques.
Local threads are scheduled by NSPR within the process. The process is assumed to be globally scheduled, but NSPR can manipulate local threads without system intervention. In most cases, this leads to a significant performance benefit.
However, on systems that require NSPR to make a distinction between global and local threads, global threads are invariably required to do any form of I/O. If a thread is likely to do a lot of I/O, making it a global thread early is probably warranted.
On systems that don't make a distinction between local and global threads, NSPR
silently ignores the scheduling request. To find the scope of the thread, call
PR_GetThreadScope
.
#include <prthread.h>
typedef enum PRThreadState {
PR_JOINABLE_THREAD,
PR_UNJOINABLE_THREAD
} PRThreadState;
|
Thread termination happens implicitly when the thread
returns from the root function. The time of release of the
resources assigned to the thread cannot be determined in
advance. Threads created with a
PR_UNJOINABLE_THREAD state cannot be used as
arguments to PR_JoinThread .
|
|
Joinable thread references remain valid after they have
returned from their root function until PR_JoinThread
is called. This approach facilitates management of the
process' critical resources.
|
The lifetime of a thread extends from the time it is created to the time it returns
from its root function. What happens when it returns from its root function
depends on the thread state passed to PR_CreateThread
when the thread was
created.
If a thread is created as a joinable thread, it continues to exist after returning from its root function until another thread joins it. The join process permits strict synchronization of thread termination and therefore promotes effective resource management.
If a thread is created as an unjoinable (also called detached) thread, it terminates and cleans up after itself after returning from its root function. This results in some ambiguity after the thread's root function has returned and before the thread has finished terminating itself.
#include <prthread.h>
typedef enum PRThreadPriority
{
PR_PRIORITY_FIRST = 0,
PR_PRIORITY_LOW = 0,
PR_PRIORITY_NORMAL = 1,
PR_PRIORITY_HIGH = 2,
PR_PRIORITY_URGENT = 3,
PR_PRIORITY_LAST = 3
} PRThreadPriority;
PR_NewThreadPrivateIndex
that is associated
with the resulting thread private index.
#include <prthread.h>
typedef void (PR_CALLBACK *PRThreadPrivateDTOR)(void *priv);
PR_SetThreadPrivate
, the value of the data is NULL
. If the data associated with the
index is not NULL
, NSPR passes a reference to the data to the destructor function
when the thread terminates.
Creating, Joining, and Identifying Threads
Controlling Thread Priorities
Interrupting and Yielding
Setting Global Thread Concurrency
Getting a Thread's Scope
PR_CreateThread
creates a new thread.
PR_JoinThread
blocks the calling thread until a specified thread terminates.
PR_GetCurrentThread
returns the current thread object for the currently
running code.
PR_AttachThread
associates a PRThread
object with an existing native thread.
#include <prthread.h>
PRThread* PR_CreateThread(
PRThreadType type,
void (*start)(void *arg),
void *arg,
PRThreadPriority priority,
PRThreadScope scope,
PRThreadState state,
PRUint32 stackSize);
PR_CreateThread
has the following parameters:
If unsuccessful, (for example, if system resources are unavailable), NULL
.
If you want to detect the completion of the created thread, make it joinable. You
can then use PR_JoinThread
to synchronize the termination of another thread.
#include <prthread.h>
PRStatus PR_JoinThread(PRThread *thread);
PR_JoinThread
has the following parameter:
|
A valid identifier for the thread that is to be joined.
|
PR_SUCCESS
If unsuccessful--for example, if no joinable thread can be found that
corresponds to the specified target thread, or if the target thread is
unjoinable--PR_FAILURE
.
PR_JoinThread
is used to synchronize the termination of a thread. The function is
synchronous in that it blocks the calling thread until the target thread is in a
joinable state. PR_JoinThread
returns to the caller only after the target thread
returns from its root function.
Several threads cannot wait for the same thread to complete. One of the calling
threads operates successfully, and the others terminate with the error PR_FAILURE
.
The calling thread is not blocked if the target thread has already terminated.
PR_JoinThread
is interruptable.
#include <prthread.h>
PRThread* PR_GetCurrentThread(void);
PR_GetCurrentThread
.
Note |
This is the only safe way to establish the identity of a thread.
Creation and enumeration are both subject to race conditions.
|
PRThread
object with an existing native thread.
#include <pprthread.h>
PRThread* PR_AttachThread(
PRThreadType type,
PRThreadPriority priority,
PRThreadStack *stack);
PR_AttachThread
has the following parameters:
PRThread
object.
If unsuccessful, for example if system resources are not available, NULL
.
PR_AttachThread
when you want to use NSS functions on the native
thread that was not created with NSPR. PR_AttachThread
informs NSPR about the
new thread by associating a PRThread
object with the native thread.
The thread object is automatically destroyed when it is no longer needed.
You don't need to call PR_AttachThread
unless you create your own native thread.
PR_Init
calls PR_AttachThread
automatically for the primordial thread.
Note |
As of NSPR release v3.0, PR_AttachThread and PR_DetachThread
are obsolete. A native thread not created by NSPR is automatically
attached the first time it calls an NSPR function, and automatically
detached when it exits.
|
In NSPR release 19980529B and earlier, it is necessary for a native thread not
created by NSPR to call PR_AttachThread
before it calls any NSPR functions, and
call PR_DetachThread
when it is done calling NSPR functions.
PRThread
object from a native thread.
#include <pprthread.h>
void PR_DetachThread(void);
PR_DetachThread
has no parameters.
This call is needed only if you attached the thread using PR_AttachThread
.
Note |
As of NSPR release v3.0, PR_AttachThread and PR_DetachThread
are obsolete. A native thread not created by NSPR is automatically
attached the first time it calls an NSPR function, and automatically
detached when it exits.
|
In NSPR release 19980529B and earlier, it is necessary for a native thread not
created by NSPR to call PR_AttachThread
before it calls any NSPR functions, and
call PR_DetachThread when it is done calling NSPR functions.
You set a thread's NSPR priority when you create it with PR_CreateThread
. After
a thread has been created, you can get and set its priority with these functions:
PR_GetThreadPriority
PR_SetThreadPriority
#include <prthread.h>
PRThreadPriority PR_GetThreadPriority(PRThread *thread);
PR_GetThreadPriority
has the following parameter:
|
A valid identifier for the thread whose priority you want to know.
|
#include <prthread.h>
void PR_SetThreadPriority(
PRThread *thread,
PRThreadPriority priority);
PR_SetThreadPriority
has the following parameters:
|
A valid identifier for the thread whose priority you want to set.
|
|
The priority you want to set.
|
thread
parameter when it calls PR_SetThreadPriority
.
PR_NewThreadPrivateIndex
allocates a unique index. If the call is successful,
every thread in the same process is capable of associating private data with the
new index.
PR_SetThreadPrivate
associates private thread data with an index.
PR_GetThreadPrivate
retrieves data associated with an index.
#include <prthread.h>
PRStatus PR_NewThreadPrivateIndex(
PRUintn *newIndex,
PRThreadPrivateDTOR destructor);
PR_NewThreadPrivateIndex
has the following parameters:
|
On output, an index that is valid for all threads in the process. You
use this index with PR_SetThreadPrivate and
PR_GetThreadPrivate .
|
|
Specifies a destructor function PRThreadPrivateDTOR for the
private data associated with the index. This function can be
specified as NULL .
|
PR_NewThreadPrivateIndex
is successful, every thread in the same process is
capable of associating private data with the new index. Until the data for an index
is actually set, the value of the private data at that index is NULL
. You pass this
index to PR_SetThreadPrivate
and PR_GetThreadPrivate
to set and retrieve
data associated with the index.
When you allocate the index, you may also register a destructor function of type
PRThreadPrivateDTOR
. If a destructor function is registered with a new index, it
will be called at one of two times, as long as the private data is not NULL
:
The index maintains independent data values for each binding thread. A thread can get access only to its own thread-specific data. There is no way to deallocate a private data index once it is allocated.
#include <prthread.h>
PRStatus PR_SetThreadPrivate(PRUintn index, void *priv);
PR_SetThreadPrivate
has the following parameters:
|
An index into the per-thread private data table.
|
|
The per-thread private data, or more likely, a pointer to the data.
|
NULL
private data associated with it, and if the
destructor function for the index is known (not NULL
), NSPR calls the destructor
function associated with the index before setting the new data value. The pointer at
the index is swapped with NULL
. If the swapped out value is not NULL
, the
destructor function is called. On return, the private data associated with the index
is reassigned the new private data's value, even if it is NULL
. The runtime provides
no protection for the private data. The destructor is called with the runtime holding
no locks. Synchronization is the client's responsibility.
The only way to eliminate thread private data at an index prior to the thread's
termination is to call PR_SetThreadPrivate
with a NULL
argument. This causes the
index's destructor function to be called, and afterwards assigns a NULL
in the table.
A client must not delete the referant object of a non-NULL
private data without first
eliminating it from the table.
#include <prthread.h>
void* PR_GetThreadPrivate(PRUintn index);
PR_GetThreadPrivate
has the following parameters:
|
The index into the per-thread private data table.
|
NULL
if the data has not been set.
PR_GetThreadPrivate
may be called at any time during a thread's execution. A
thread can get access only to its own per-thread private data. Do not delete the
object that the private data refers to without first clearing the thread's value.
PR_Interrupt
requests an interrupt of another thread. Once the target thread
has been notified of the request, the request stays with the thread until the
notification either has been delivered exactly once or is cleared.
PR_ClearInterrupt
clears a previous interrupt request.
PR_Sleep
causes a thread to yield to other threads for a specified number of
ticks.
#include <prthread.h>
PRStatus PR_Interrupt(PRThread *thread);
PR_Interrupt
has the following parameter:
|
The thread whose interrupt request you want to set.
|
PR_Interrupt
is to request that a thread performing some task
stop what it is doing and return to some control point. It is assumed that a control
point has been mutually arranged between the thread doing the interrupting and
the thread being interrupted. When the interrupted thread reaches the prearranged
point, it can communicate with its peer to discover the real reason behind the
change in plans.
The interrupt request remains in the thread's state until it is delivered exactly once
or explicitly canceled. The interrupted thread returns PR_FAILURE
(-1) with an
error code (see PR_GetError
) for blocking operations that return a PRStatus
(such
as I/O operations, monitor waits, or waiting on a condition). To check whether the
thread was interrupted, compare the result of PR_GetError
with
PR_PENDING_INTERRUPT_ERROR
.
PR_Interrupt
may itself fail if the target thread is invalid.
PR_Interrupt
has the following limitations and known bugs:
File I/O is considered instantaneous, so file I/O functions cannot be
interrupted. Unfortunately the standard input, output, and error streams are
treated as files by NSPR, so a PR_Read
call on PR_STDIN
cannot be interrupted
even though it may block indefinitely.
In the NT implementation, PR_Connect
cannot be interrupted.
In the NT implementation, a file descriptor is not usable and must be closed after an I/O function on the file descriptor is interrupted. See the memo Using IO Timeout and Interrupt on NT for details.
#include <prthread.h>
void PR_ClearInterrupt(void);
PR_Interrupt
may never respond to the interrupt request. For example, the target
thread may reach the agreed-on control point without providing an opportunity
for the runtime to notify the thread of the interrupt request. In this case, the request
for interrupt is still pending with the thread and must be explicitly canceled.
Therefore it is sometimes necessary to call PR_ClearInterrupt
to clear a previous
interrupt request.
If no interrupt request is pending, PR_ClearInterrupt
is a no-op.
#include <prthread.h>
PRStatus PR_Sleep(PRIntervalTime ticks);
PR_Sleep
has the following parameter:
|
The number of ticks you want the thread to sleep for (see PRIntervalTime ).
|
PR_Sleep
with a parameter equivalent to PR_INTERVAL_NO_TIMEOUT
is an
error and results in a PR_FAILURE
error.
PR_Sleep
simply waits on a condition for the amount of time specified. If you set
ticks
to PR_INTERVAL_NO_WAIT
, the thread yields.
If ticks
is not PR_INTERVAL_NO_WAIT
, PR_Sleep
uses an existing lock, but has to
create a new condition for this purpose. If you have already created such
structures, it is more efficient to use them directly.
Calling PR_Sleep
with the value of ticks
set to PR_INTERVAL_NO_WAIT
simply
surrenders the processor to ready threads of the same priority. All other values of
ticks cause PR_Sleep
to block the calling thread for the specified interval.
Threads blocked in PR_Sleep
are interruptible.
#include <prthread.h>
void PR_SetConcurrency(PRUintn numCPUs);
PR_SetConcurrency
has the following parameter:
|
The number of concurrent global threads desired.
|
You can use PR_SetConcurrency
to exercise similar fine-grained control over the
number of global threads that NSPR utilizes. The default value of concurrency is 1.
There's no harm in setting the number larger than the number of physical
processors available.
#include <prthread.h>
PRThreadScope PR_GetThreadScope(void);
PRThreadScope
indicating whether the thread is local or global.
Last Updated May 18, 2001