| Rogue Wave Standard C++ Library 2.2.1 | Rogue Wave Standard C++ Library 1.2.1 and Tools.h++ 7.0.6 |
|---|---|
For both 32-bit and 64-bit libraries:
|
For both 32-bit and 64-bit libraries:
|
// create a mutex and initialize it pthread_mutex_t the_mutex; #ifdef _PTHREADS_DRAFT4 // for user threads pthread_mutex_init(&the_mutex, pthread_mutexattr_default); #else // for kernel threads pthread_mutex_init(&the_mutex, (pthread_mutexattr_t *)NULL); #endif pthread_mutex_lock(&the_mutex); cout << "something" ... ; pthread_mutex_unlock(&the_mutex);
Note that conditional compilation may be necessary to accommodate both the user threads and the kernel threads interfaces, as in the above example. An alternative might be to compose a buffer with an ostrstream and output with one write. The following example could be used with the cfront compatible libstream.
ostrstream ostr; ostr << "something" /*...*/ ; ostr << " or another" /*...*/ << endl; cout.write(ostr.str(), ostr.pcount()); ostr.rdbuf()->freeze(0);Note that the above example works with with the new library, though with the deprecated ostrstream.
Or something similar can be done with the Rogue Wave Standard C++ Library 2.2.1 (libstd_v2) with standard ostringstream, as in the following example:
ostringstream ostr; ostr << "something" /*...*/ ; ostr << " or another" /*...*/ << endl; cout.write(ostr.str().c_str(), ostr.str().length());Note that cout.flush() may be needed if sharing the file with stdio.
| Rogue Wave Standard C++ Library 2.2.1 | Rogue Wave Standard C++ Library 1.2.1 and Tools.h++ 7.0.6 |
|---|---|
For both 32-bit and 64-bit libraries:
|
For both 32-bit and 64-bit libraries:
|
WARNING: If you do not specify these options as described in the above table, a run-time error will be generated or multi-thread behavior will be incorrect.
void f(ostream &out, int x, int y) {
out << setw(3) << x << setw(10) << y;
}
This function would not be thread safe if called from multiple
threads with the same object, since the "width" in the shared object
could be changed at any time. Therefore, such objects are not
protected from interactions between multiple threads, and the
result of sharing such an object between threads is undefined.
If the same object is shared between threads, a runtime crash, abort, or intermingled output may occur. With the Rogue Wave Standard C++ Library 2.2.1, output may be intermingled but no aborts will occur.
-D_THREAD_SAFE with the
cfront Compatible libstreamcout, cin, cerr, and clog,
you can specify the -D_THREAD_SAFE compile time flag for
any file that includes <iostream.h>.
In this case, a new instance of the object is transparently created for
each thread that uses it. All instances share the same file descriptor.
The f() function in the above example will now work, because it receives
one new "out" object per thread. However, the results of two
simultaneous executions of f() will be mixed in any order in the output.
Using -D_THREAD_SAFE with the global scope operator is not supported for
cout, cin, cerr, and clog.
For example, the following code would generate an error:
::cout << endl;
Note, if you use locks, you need not use the -D_THREAD_SAFE compile time flag since you are now responsible for ensuring thread safety.
libstreamVisible differences would be as follows. In the case of standard iostreams, there is intermingling of each component being inserted. With cfront compatible iostreams, there is intermingling of complete buffers (depending on when endl or flush is called).
-D__HPACC_THREAD_SAFE_RB_TREErb_tree class is involved.
In other words, if the tree header file
(which includes tree.cc) under /opt/aCC/include/ is used,
these libraries are not thread safe.
Most likely, it is indirectly referenced by including the standard C++ library
container class map or set headers,
or by including a RogueWave tools.h++ header like tvset.h,
tpmset.h, tpmset.h, tvset.h, tvmset.h, tvmset.h, tpmap.h, tpmmap.h,
tpmmap.h, tvmap.h, tvmmap.h.
Since changing the rb_tree implementation to
make it thread safe would break binary compatibility,
the preprocessing macro, __HPACC_THREAD_SAFE_RB_TREE, must be defined.
The macro is automatically defined in the IPF environment.
A new object file compiled with the macro
defined should not be linked with older ones that were compiled without
the macro defined. Library providers whose library is built with the
the macro defined may need to notify their users to also compile their source
with the macro defined when the tree header is included.
The following example illustrates that you cannot catch an object which has been thrown in a different thread. To do so will result in a runtime abort since HP aC++ finds no available catch handler and terminate is called.
#include <pthread.h>
void foo() {
int i = 10;
throw i;
}
int main() {
pthread_t tid;
try {
ret=pthread_create(&tid, 0, (void*(*)(void*))foo, 0);
}
catch(int n) {}
}
OpenMP is an industry-standard parallel programming model that implements a fork-join model of parallel execution. The HP C++ OpenMP pragmas are based on the OpenMP Standard for C/C++, version 2.0.
To view the details about the standard and details about usage, syntax and values, please go to http://www.openmp.org/specs. You can download an Adobe Acrobat (PDF) version of the C/C++ Version 2.0 OpenMP standard from this website.
See Pragmas section, to know more about OpenMP pragmas.
Every C++ program that contains OpenMP pragmas is to be compiled for the current version of HP-UX and must include the header file <omp.h>. If it does not, the OpenMP pragmas are ignored. The default path for <omp.h> is /usr/include.
The OpenMP APIs are defined in the library libomp.
The _OPENMP macro name is defined by OpenMP compliant implementation as the decimal constant 200203. This macro must not be the subject of #define or #undef preprocessing directive.
Example:
#ifdef _OPENMP iam = omp_get_thread_num() + index; #endif
This section summarizes some of the OpenMP directives behavior that are described as "implementation dependent" in the OpenMP v2.0 API. Each behavior is cross-referenced back to its description in the OpenMP v2.0 main specification. HP, in conformance with the OpenMP v2.0 API, define and document the following behavior.
If the dynamic threads mechanism is disabled, requests for additional threads will result in no additional threads being created. Programs should not assume that a request will result in additional threads for all requests. If the dynamic threads mechanism is enabled, requests for more threads than an implementation can support are satisfied by creating additional pthreads which are then scheduled by the HP-UX scheduler using a smaller number of threads (Section 2.3, page 9 of OpenMP C/C++ specs).
The OpenMP environment variables recognized by HP aC++ compiler control the execution of parallel code. Note that the environment variable names are case sensitive and they must be in uppercase.
The following environment variables are available in HP aC++ compiler:
OMP_SCHEDULE |
export OMP_SCHEDULE="kind[,chunk_size]" setenv OMP_SCHEDULE "kind[,chunk_size]"where, kind is one of static, dynamic, or guided.
The default value of the environment variable is a static schedule with a chunk_size of 1. If the optional chunk_size is set, the value must be positive. If chunk_size is not set, a value of 1 is assumed, except for static schedule. For a static schedule, the default chunk_size is set to the loop iteration space divided by a number of threads applied to the loop.
NOTE: OMP_SCHEDULE is ignored for for and parallel for directives that have a schedule type other than runtime.
OMP_NUM_THREADS |
export OMP_NUM_THREADS=value setenv OMP_NUM_THREADS value
The default value is the number of physical processors on the system.
OMP_DYNAMIC |
export OMP_DYNAMIC=value setenv OMP_DYNAMIC value
If the value is set to FALSE, dynamic adjustment is disabled. If the value is set to TRUE, the number of threads that are used for executing parallel regions may be adjusted by the runtime environment to best utilize system resources.
OMP_NESTED |
export OMP_NESTED=value setenv OMP_NESTED value
If the value is set to TRUE, nested parallelism is enabled and if the value is set to FALSE, the nested parallelism is disabled. The default value is FALSE.
The descriptions of library functions are divided into the following topics:
The execution environment functions affect and monitor threads, processors, and the parallel environment:
omp_set_num_threads |
#include <omp.h> void omp_set_num_threads(int num_threads);
For more information on this subject, see the omp_set_dynamic and omp_get_dynamic functions. This call has precedence over the OMP_NUM_THREADS environment variable.
omp_get_num_threads |
#include <omp.h> int omp_get_num_threads(void);
omp_get_max_threads |
#include <omp.h> int omp_get_max_threads(void);
omp_get_thread_num |
#include <omp.h> int omp_get_thread_num(void);
omp_get_num_procs |
#include <omp.h> int omp_get_num_procs(void);
omp_in_parallel |
#include <omp.h> int omp_in_parallel(void);
omp_set_dynamic |
#include <omp.h> void omp_set_dynamic(int dynamic_threads);
The default for the dynamic adjustment of threads is 0.
omp_get_dynamic |
#include <omp.h> int omp_get_dynamic(void);
omp_set_nested |
#include <omp.h> void omp_set_nested(int nested);
omp_get_nested |
#include <omp.h> int omp_get_nested(void);
The functions described in this section manipulate locks used for synchronization. For the following functions, the lock variable must have type omp_lock_t. This variable must only be accessed through these functions. All lock functions require an argument that has a pointer to omp_lock_t type.
For the following functions, the lock variable must have type omp_nest_lock_t. This variable must only be accessed through these functions. All nestable lock functions require an argument that has a pointer to omp_nest_lock_t type.
omp_init_lock & omp_init_nest_lock Functions |
#include <omp.h> void omp_init_lock(omp_lock_t *lock); void omp_init_nest_lock(omp_nest_lock_t *lock);
These functions provide the only means of initializing a lock. Each function initializes the lock associated with the parameter lock for use in subsequent calls. The initial state is unlocked (that is, no thread owns the lock). For a nestable lock, the initial nesting count is zero.
omp_destroy_lock & omp_destroy_nest_lock Functions |
#include <omp.h> void omp_destroy_lock(omp_lock_t *lock); void omp_destroy_nest_lock(omp_nest_lock_t *lock);
These functions ensure that the pointed to lock variable lock is uninitialized. The argument to these functions must point to an initialized lock variable that is locked.
omp_set_lock & omp_set_nest_lock Functions |
#include <omp.h> void omp_set_lock(omp_lock_t *lock); void omp_set_nest_lock(omp_nest_lock_t *lock);
Each of these functions blocks the thread executing the function until the specified lock
is available and then sets the lock. A simple lock is available if it is unlocked. A nestable lock is available if it is unlocked or if it is already owned by the thread executing the function.
For a simple lock, the argument to the omp_set_lock function must point to an initialized lock variable. Ownership of the lock is granted to the thread executing the function.
For a nestable lock, the argument to the omp_set_nest_lock function must point to an initialized lock variable. The nesting count is incremented, and the thread is granted, or retains, ownership of the lock.
omp_set_lock & omp_unset_nest_lock Functions |
#include <omp.h> void omp_unset_lock(omp_lock_t *lock); void omp_unset_nest_lock(omp_nest_lock_t *lock);
These functions provide the means of releasing ownership of a lock. The argument to each of these functions must point to an initialized lock variable owned by the thread executing the function. The behavior is undefined if the thread does not own that lock.
For a simple lock, the omp_unset_lock function releases the thread executing the function from ownership of the lock.
For a nestable lock, the omp_unset_nest_lock function decrements the
nesting count, and releases the thread executing the function from ownership of the lock if the resulting count is zero.
omp_test_lock & omp_test_nest_lock Functions |
#include <omp.h> int omp_test_lock(omp_lock_t *lock); int omp_test_nest_lock(omp_nest_lock_t *lock);
omp_get_wtime Function |
#include <omp.h> double omp_get_wtime(void);
The function may be used to measure elapsed times as shown in the following example:
double start;
double end;
start = omp_get_wtime();
... work to be timed ...
end = omp_get_wtime();
printf("Work took %f sec. time.\n", end-start);
The time returned are "per-thread times". They are not required to be globally consistent across all the threads participating in an application.
omp_get_wtick Function |
#include <omp.h> double omp_get_wtick(void);