blob: 482afa28c2919eafdf7e11303a0e73de2e1b4b0e [file] [log] [blame]
/************************************************************************
*
* thread.cpp - definitions of testsuite thread helpers
*
* $Id$
*
************************************************************************
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright
* ownership. The ASF licenses this file to you under the Apache
* License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
* implied. See the License for the specific language governing
* permissions and limitations under the License.
*
* Copyright 2005-2006 Rogue Wave Software.
*
**************************************************************************/
// expand _TEST_EXPORT macros
#define _RWSTD_TEST_SRC
#include <rw_thread.h>
#include <stddef.h> // for size_t
#include <string.h> // for memset()
/**************************************************************************/
static long maxthreads;
#if defined (_RWSTD_POSIX_THREADS)
# include <pthread.h>
extern "C" {
_TEST_EXPORT int
rw_thread_create (rw_thread_t *thr_id,
rw_thread_attr_t*,
void* (*thr_proc)(void*),
void *thr_arg)
{
#ifdef _RWSTD_OS_SUNOS
static int concurrency_set;
if (0 == concurrency_set) {
pthread_setconcurrency (4);
concurrency_set = 1;
}
#endif // _RWSTD_OS_SUNOS
rw_thread_t tmpid;
if (0 == thr_id) {
thr_id = &tmpid;
}
pthread_t tid;
// set the thread number *before* creating the thread
// so that it's visible in thr_proc when it starts to
// run even before pthread_create returns
thr_id->threadno = maxthreads;
const int result = pthread_create (&tid, 0, thr_proc, thr_arg);
if (0 == result) {
thr_id->id = (long)tid;
thr_id->handle = 0;
++maxthreads;
}
return result;
}
_TEST_EXPORT int
rw_thread_join (rw_thread_t thr_id, void **parg)
{
const int result = pthread_join ((pthread_t)thr_id.id, parg);
return result;
}
} // extern "C"
/**************************************************************************/
#elif defined (_RWSTD_SOLARIS_THREADS)
# include <thread.h>
extern "C" {
_TEST_EXPORT int
rw_thread_create (rw_thread_t *thr_id,
rw_thread_attr_t*,
void* (*thr_proc)(void*),
void *thr_arg)
{
static int concurrency_set;
if (0 == concurrency_set) {
thr_setconcurrency (4);
concurrency_set = 1;
}
rw_thread_t tmpid;
if (0 == thr_id) {
thr_id = &tmpid;
}
thread_t tid;
// set the thread number *before* creating the thread
// so that it's visible in thr_proc when it starts to
// run even before thr_create returns
thr_id->threadno = maxthreads;
const int result =
thr_create (0, // stack_base
0, // stack_size
thr_proc, // start_func
thr_arg, // arg
0, // flags
&tid); // new_thread_ID
if (0 == result) {
thr_id->id = (long)tid;
thr_id->handle = 0;
++maxthreads;
}
return result;
}
_TEST_EXPORT int
rw_thread_join (rw_thread_t thr_id, void **parg)
{
const int result = thr_join ((thread_t)thr_id.id, 0, parg);
return result;
}
} // extern "C"
/**************************************************************************/
#elif defined (_RWSTD_DEC_THREADS)
# include <setjmp.h>
# include <cma.h>
extern "C" {
# ifdef __PURE_CNAME
extern int sigsetjmp (jmp_buf, int);
# endif
_TEST_EXPORT int
rw_thread_create (rw_thread_t *thr_id,
rw_thread_attr_t*,
void* (*thr_proc)(void*),
void *thr_arg)
{
rw_thread_t tmpid;
if (0 == thr_id) {
thr_id = &tmpid;
}
int result = 0;
cma_t_thread tid;
// set the thread number *before* creating the thread
// so that it's visible in thr_proc when it starts to
// run even before cma_thread_create returns
thr_id->threadno = maxthreads;
TRY {
// cma_thread_create() returns void but throws an exception on error
cma_thread_create (&tid, // new_thread
0, // attr
thr_proc, // start_routine
&thr_arg); // arg
thr_id->id = tid.field1;
thr_id->handle = (void*)tid.field2;
++maxthreads;
}
CATCH_ALL {
result = -1;
}
ENDTRY
return result;
}
_TEST_EXPORT int
rw_thread_join (rw_thread_t thr_id, void **parg)
{
int status = 0;
cma_t_thread tid = {
thr_id.id, (long)thr_id.handle
};
TRY {
// cma_thread_join() returns void but throws an exception on error
cma_thread_join (&tid, 0, parg);
}
CATCH_ALL {
status = -1;
}
ENDTRY
return status;
}
} // extern "C"
/**************************************************************************/
#elif defined (_WIN32) || defined (_WIN64)
# include <windows.h>
extern "C" {
_TEST_EXPORT int
rw_thread_create (rw_thread_t *thr_id,
rw_thread_attr_t*,
void* (*thr_proc)(void*),
void *thr_arg)
{
int result = 0;
rw_thread_t tmpid;
if (0 == thr_id)
thr_id = &tmpid;
DWORD nid; // numerical id
typedef DWORD ThreadProc (LPVOID);
LPTHREAD_START_ROUTINE win32_thr_proc =
_RWSTD_REINTERPRET_CAST (LPTHREAD_START_ROUTINE, thr_proc);
// set the thread number *before* creating the thread
// so that it's visible in thr_proc when it starts to
// run even before CreateThread returns
thr_id->threadno = maxthreads;
const HANDLE hthread =
CreateThread (0, // lpThreadAttributes
0, // dwStackSize
win32_thr_proc, // lpStartAddress
thr_arg, // lpParameter
0, // dwCreationFlags
&nid); // lpThreadId
if (INVALID_HANDLE_VALUE == hthread) {
thr_id->id = -1;
thr_id->handle = 0;
result = -1;
}
else {
thr_id->id = nid;
thr_id->handle = hthread;
++maxthreads;
}
return result;
}
_TEST_EXPORT int
rw_thread_join (rw_thread_t thr_id, void **parg)
{
int result = 0;
const DWORD retcode = WaitForSingleObject (thr_id.handle, INFINITE);
if (WAIT_OBJECT_0 == retcode) {
if (parg) {
DWORD exit_code;
if (GetExitCodeThread (thr_id.handle, &exit_code))
*parg = (void*)exit_code;
else
result = -1;
}
}
else {
result = -1;
}
return result;
}
} // extern "C"
/**************************************************************************/
#else // unknown/missing threads environment
# include <errno.h>
# ifndef ENOTSUP
# if defined (_RWSTD_OS_AIX)
# define ENOTSUP 124
# elif defined (_RWSTD_OS_HP_UX)
# define ENOTSUP 252
# elif defined (_RWSTD_OS_IRIX64)
# define ENOTSUP 1008
# elif defined (_RWSTD_OS_LINUX)
# define ENOTSUP 524
# elif defined (_RWSTD_OS_OSF1)
# define ENOTSUP 99
# elif defined (_RWSTD_OS_SUNOS)
# define ENOTSUP 48
# elif defined (_WIN32) || defined (_WIN64)
# define ENOTSUP ENOSYS
# else
# define ENOTSUP 9999
# endif
# endif // ENOTSUP
extern "C" {
_TEST_EXPORT int
rw_thread_create (rw_thread_t*,
rw_thread_attr_t*,
void* (*)(void*),
void*)
{
_RWSTD_UNUSED (maxthreads);
return ENOTSUP;
}
_TEST_EXPORT int
rw_thread_join (rw_thread_t, void**)
{
return ENOTSUP;
}
} // extern "C"
#endif // threads environment
extern "C" {
_TEST_EXPORT int
rw_thread_pool (rw_thread_t *thr_id, size_t nthrs,
rw_thread_attr_t*,
void* (*thr_proc)(void*),
void **thr_arg)
{
// small buffer for thread ids when invoked with (thr_id == 0)
rw_thread_t id_buf [16];
const bool join = 0 == thr_id;
#ifndef _RWSTD_REENTRANT
// when not reentrant/thread safe emulate the creation
// of a single thread and then waiting for it to finish
// by simply calling the thread procedure
if (1 == nthrs && join) {
if (0 == thr_id) {
thr_id = id_buf;
memset (thr_id, 0, sizeof *thr_id);
}
// when the thr_arg pointer is 0 pass the address
// of each thread's id as the argument to thr_proc
void* const arg = thr_arg ? thr_arg [0] : (void*)(thr_id);
void* const thr_result = thr_proc (arg);
if (thr_arg)
thr_arg [0] = thr_result;
return 0;
}
#endif // !_RWSTD_REENTRANT
bool delete_ids = false;
if (0 == thr_id) {
// save thread idsso that they (and no other threads)
// can be joined later
if (sizeof id_buf / sizeof *id_buf < nthrs) {
delete_ids = true;
thr_id = new rw_thread_t [nthrs];
}
else
thr_id = id_buf;
}
// create a pool of threads storing their id's
// in consecutive elements of the thr_id array
for (size_t i = 0; i != nthrs; ++i) {
// when the thr_arg pointer is 0 pass the address
// of each thread's id as the argument to thr_proc
void* const next_arg = thr_arg ? thr_arg [i] : (void*)(thr_id + i);
if (rw_thread_create (thr_id + i, 0, thr_proc, next_arg)) {
if (delete_ids)
delete[] thr_id;
return int (i + 1);
}
}
// invoking the function with a 0 thr_id pointer
// is a request to join all threads in the pool
if (join) {
for (size_t i = 0; i != nthrs; ++i) {
// avoid advancing through the thr_arg array
// when it's 0 (and have rw_thread_join() simply
// ignore the thread's return value)
void** next_arg = thr_arg ? thr_arg + i : 0;
rw_thread_join (thr_id [i], next_arg);
}
if (delete_ids)
delete[] thr_id;
}
return 0;
}
} // extern "C"