| /*************************************************************************** |
| * |
| * 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); |
| } |