blob: ef462ca259e86c0d3ee2e6dd3770cbc6b9245315 [file] [log] [blame]
/***************************************************************************
*
* 19.exceptions.mt.cpp - test exercising the thread safety
* of C++ Standard Library exception classes
*
* $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.
*
**************************************************************************/
#include <stdexcept> // for exceptions
#include <string> // for string
#include <cassert> // for assert
#include <cstdio> // for printf()
#include <cmdopt.h>
#include <driver.h>
#include <rw_alarm.h> // for rw_alarm()
#include <rw_thread.h> // for rw_thread_pool()
#include <valcmp.h>
/**************************************************************************/
#ifndef NTHREADS
# ifndef _RWSTD_REENTRANT
# define MAX_THREADS 0
# define NTHREADS 0
# else
# define MAX_THREADS 32
# define NTHREADS 4
# endif // _RWSTD_REENTRANT
#endif // NTHREADS
/**************************************************************************/
/* extern */ int rw_opt_nloops = 256 * 1024;
/* extern */ int rw_opt_nthreads = NTHREADS;
/**************************************************************************/
volatile int alarm_expired;
extern "C" {
static void handle_alarm (int)
{
alarm_expired = 1;
}
} // extern "C"
// string to intialize exceptions from
static const char what_buf [] = {
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"0123456789ABCDEF"
};
enum {
exception_tag,
logic_error_tag,
domain_error_tag,
invalid_argument_tag,
length_error_tag,
out_of_range_tag,
runtime_error_tag,
range_error_tag,
overflow_error_tag,
underflow_error_tag,
Derived_tag
};
struct DerivedException: std::invalid_argument
{
DerivedException (const char *str)
: std::invalid_argument (str) { }
};
static void
throw_exception (unsigned which, const char *what)
{
switch (which) {
case exception_tag: throw std::exception ();
case logic_error_tag: throw std::logic_error (what);
case domain_error_tag: throw std::domain_error (what);
case invalid_argument_tag: throw std::invalid_argument (what);
case length_error_tag: throw std::length_error (what);
case out_of_range_tag: throw std::out_of_range (what);
case runtime_error_tag: throw std::runtime_error (what);
case range_error_tag: throw std::range_error (what);
case overflow_error_tag: throw std::overflow_error (what);
case underflow_error_tag: throw std::underflow_error (what);
case Derived_tag: throw DerivedException (what);
default: _RWSTD_ASSERT (!"logic error: bad exception tag");
}
}
/**************************************************************************/
extern "C" void*
test_single_exception (void *arg)
{
const rw_thread_t* const tid = (rw_thread_t*)arg;
std::printf ("thread procedure %ld starting...\n", tid->threadno);
for (unsigned i = 0; i < unsigned (rw_opt_nloops); ++i) {
if (alarm_expired)
break;
const std::size_t what_len = std::size_t (i % 1024);
const char* const what = what_buf + sizeof what_buf - what_len - 1;
const unsigned thrown = i % 11;
unsigned caught = _RWSTD_UINT_MAX;
try {
// construct and throw an exception object of one
// of the predefined standard exception classes
// initialized with a distinct what string
throw_exception (thrown, what);
}
catch (DerivedException ex) {
// catch the exception object by value and verify
// that the pointer returned by what() compares
// equal to the string the thrown object was
// constructed with
caught = Derived_tag;
assert (0 == rw_strncmp (ex.what (), what));
}
catch (std::domain_error ex) {
caught = domain_error_tag;
assert (0 == rw_strncmp (ex.what (), what));
}
catch (std::invalid_argument ex) {
caught = invalid_argument_tag;
assert (0 == rw_strncmp (ex.what (), what));
}
catch (std::length_error ex) {
caught = length_error_tag;
assert (0 == rw_strncmp (ex.what (), what));
}
catch (std::out_of_range ex) {
caught = out_of_range_tag;
assert (0 == rw_strncmp (ex.what (), what));
}
catch (std::range_error ex) {
caught = range_error_tag;
assert (0 == rw_strncmp (ex.what (), what));
}
catch (std::overflow_error ex) {
caught = overflow_error_tag;
assert (0 == rw_strncmp (ex.what (), what));
}
catch (std::underflow_error ex) {
caught = underflow_error_tag;
assert (0 == rw_strncmp (ex.what (), what));
}
catch (std::logic_error ex) {
caught = logic_error_tag;
assert (0 == rw_strncmp (ex.what (), what));
}
catch (std::runtime_error ex) {
caught = runtime_error_tag;
assert (0 == rw_strncmp (ex.what (), what));
}
catch (std::exception ex) {
caught = exception_tag;
rw_strncmp (ex.what (), what);
}
// verify that an object of the thrown type was caught
assert (caught == thrown);
}
return 0;
}
/**************************************************************************/
static void
test_multi (unsigned i, unsigned nactive)
{
const std::size_t what_len = (i + nactive) % sizeof what_buf;
const char* const what = what_buf + sizeof what_buf - what_len - 1;
const unsigned thrown = (i + nactive) % 10 + 1;
try {
// construct and throw an exception object of a distinct type
// with a distinct what string at each level of recursion
throw_exception (thrown, what);
}
catch (std::exception &ex) {
// recursively throw another exception while the caught
// exception object is still active
if (nactive)
test_multi (i, nactive - 1);
// verify that the caught object's what string matches
// the string the object was originally constructed with
assert (0 == rw_strncmp (ex.what (), what));
}
}
extern "C" void*
test_multi_exceptions (void *arg)
{
const rw_thread_t* const tid = (rw_thread_t*)arg;
std::printf ("thread procedure %ld starting...\n", tid->threadno);
for (unsigned i = 0; i < unsigned (rw_opt_nloops); ++i) {
if (alarm_expired)
break;
// exercise up to 4 simultaneously active exceptions
test_multi (i, i % 4);
}
return 0;
}
/**************************************************************************/
static int
run_test (int, char**)
{
// get the current alarm (if any) set for the test
// on the command line without resetting it
const unsigned max_sec = rw_alarm (0, rw_sig_hold);
// compute a shorter timeout for each of the two subtests
const unsigned nsec = 3 < max_sec ? max_sec / 2 : 0;
rw_info (0, 0, 0,
"single active exception per thread"
"%{?}; timeout in %u seconds%{;}",
0 != max_sec, nsec ? nsec : max_sec);
// set a shorter alarm if possible
if (nsec) {
alarm_expired = 0;
rw_alarm (nsec, handle_alarm);
}
const std::size_t nthreads = std::size_t (rw_opt_nthreads);
#if 0 < NTHREADS
rw_fatal (0 == rw_thread_pool (0, nthreads, 0, test_single_exception, 0),
0, __LINE__, "rw_thread_pool() failed");
#else // if !(0 < NTHREADS)
rw_thread_t tid = rw_thread_t ();
test_single_exception (&tid);
#endif // NTHREADS
rw_info (0, 0, 0,
"multiple active exceptions per thread"
"%{?}; timeout in %u seconds%{;}",
0 != max_sec, nsec ? nsec : max_sec);
// set another shorter alarm if possible
if (nsec) {
alarm_expired = 0;
rw_alarm (nsec, handle_alarm);
}
#if 0 < NTHREADS
rw_fatal (0 == rw_thread_pool (0, nthreads, 0, test_multi_exceptions, 0),
0, __LINE__, "rw_thread_pool() failed");
#else // if !(0 < NTHREADS)
test_multi_exceptions (&tid);
#endif // NTHREADS
// restore the original alarm to go off approximately
// when the original alar would have if it hadn't been
// replaced above
if (nsec)
rw_alarm (2 * nsec, rw_sig_restore);
return 0;
}
/**************************************************************************/
int main (int argc, char *argv[])
{
return rw_test (argc, argv, __FILE__,
"lib.std.exceptions",
"thread safety", run_test,
"|-nloops#0 " // must be non-negative
"|-nthreads#0-*", // must be in [0, MAX_THREADS]
&rw_opt_nloops,
int (MAX_THREADS),
&rw_opt_nthreads);
}