363 lines
11 KiB
C++
363 lines
11 KiB
C++
/***************************************************************************
|
|
*
|
|
* 19.exceptions.mt.cpp - test exercising the thread safety
|
|
* of C++ Standard Library exception classes
|
|
*
|
|
* $Id: 19.exceptions.mt.cpp 425530 2006-07-25 21:39:50Z sebor $
|
|
*
|
|
***************************************************************************
|
|
*
|
|
* 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);
|
|
}
|