/*************************************************************************** * * 18.atomic.add.cpp - test exercising the __rw_atomic_preincrement() * and __rw_atomic_predecrement function templates * * $Id: atomic_add.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 2003-2006 Rogue Wave Software. * **************************************************************************/ #include #include #include #include #include #include #include // for rw_thread_create() /**************************************************************************/ struct thr_args_base { static enum tag_t { Char, SChar, UChar, Short, UShort, Int, UInt, Long, ULong, LLong, ULLong } type_tag_; }; thr_args_base::tag_t thr_args_base::type_tag_; template struct thr_args: thr_args_base { unsigned threadno_; // thread ordinal number static int inc_; // increment, decrement, or both static unsigned nincr_; // number of increments static intT *shared_; // shared variables static unsigned nthreads_; // number of threads static intT* get_array (); }; template int thr_args::inc_; template unsigned thr_args::nincr_; template intT* thr_args::shared_ = thr_args::get_array (); template unsigned thr_args::nthreads_; // working around compiler bugs that prevent us from defining // a static array data member of a class template (PR #30009) template intT* thr_args::get_array () { static intT array [2]; return array; } /**************************************************************************/ template T preincrement (T &x) { #ifndef _RWSTD_REENTRANT return ++x; #else // if defined (_RWSTD_REENTRANT) return _RW::__rw_atomic_preincrement (x, false); #endif // _RWSTD_REENTRANT } template T predecrement (T &x) { #ifndef _RWSTD_REENTRANT return --x; #else // if defined (_RWSTD_REENTRANT) return _RW::__rw_atomic_predecrement (x, false); #endif // _RWSTD_REENTRANT } /**************************************************************************/ template void* thread_routine (thr_args *args) { printf ("thread %u starting to %screment\n", args->threadno_, args->inc_ < 0 ? "de" : args->inc_ > 0 ? "in" : "decrement and in"); if (args->inc_ > 0) { // exercise atomic_preincrement() in a tight loop for (unsigned i = 0; i != args->nincr_; ++i) { preincrement (args->shared_ [0]); preincrement (args->shared_ [1]); } } else if (args->inc_ < 0) { // exercise atomic_predecrement() in a tight loop for (unsigned i = 0; i != args->nincr_; ++i) { predecrement (args->shared_ [0]); predecrement (args->shared_ [1]); } } else { // exercise both atomic_preincrement() and atomic_predecrement() for (unsigned i = 0; i != args->nincr_; ++i) { preincrement (args->shared_ [0]); predecrement (args->shared_ [0]); preincrement (args->shared_ [1]); predecrement (args->shared_ [1]); } } return 0; } /**************************************************************************/ extern "C" void* thread_routine (void *arg) { thr_args_base* const args = (thr_args_base*)arg; switch (args->type_tag_) { case thr_args_base::Char: return thread_routine ((thr_args*)(arg)); case thr_args_base::SChar: return thread_routine ((thr_args*)(arg)); case thr_args_base::UChar: return thread_routine ((thr_args*)(arg)); case thr_args_base::Short: return thread_routine ((thr_args*)(arg)); case thr_args_base::UShort: return thread_routine ((thr_args*)(arg)); case thr_args_base::Int: return thread_routine ((thr_args*)(arg)); case thr_args_base::UInt: return thread_routine ((thr_args*)(arg)); case thr_args_base::Long: return thread_routine ((thr_args*)(arg)); case thr_args_base::ULong: return thread_routine ((thr_args*)(arg)); #ifdef _RWSTD_LONG_LONG case thr_args_base::LLong: return thread_routine ((thr_args<_RWSTD_LONG_LONG>*)(arg)); case thr_args_base::ULLong: return thread_routine ((thr_args*)(arg)); #endif // _RWSTD_LONG_LONG }; return 0; } /**************************************************************************/ /* extern */ int rw_opt_nloops = 1024 * 1024; /* extern */ int rw_opt_nthreads = 4; #define MAX_THREADS 32 template void run_test (intT, thr_args_base::tag_t tag, int inc) { static const char* const tname = rw_any_t (intT ()).type_name (); if (!rw_enabled (tname)) { rw_note (0, 0, 0, "%s test disabled", tname); return; } #if defined (_RWSTD_REENTRANT) static const char* const fun = inc < 0 ? "__rw_atomic_predecrement" : inc > 0 ? ":__rw_atomic_preincrement" : "__rw_atomic_pre{in,de}crement"; rw_info (0, 0, 0, "__rw::%s (%s&): %d iterations in %d threads", fun, tname, rw_opt_nloops, rw_opt_nthreads); rw_thread_t tid [MAX_THREADS]; typedef thr_args Args; Args::nthreads_ = rw_opt_nthreads; Args::type_tag_ = tag; Args::inc_ = inc; Args::nincr_ = unsigned (rw_opt_nloops); Args::shared_ [0] = intT (); Args::shared_ [1] = intT (); _RWSTD_ASSERT (Args::nthreads_ < sizeof tid / sizeof *tid); Args args [sizeof tid / sizeof *tid]; memset (args, 0, sizeof args); for (unsigned i = 0; i != Args::nthreads_; ++i) { args [i].threadno_ = i; rw_fatal (0 == rw_thread_create (tid + i, 0, thread_routine, args + i), 0, __LINE__, "thread_create() failed"); } for (unsigned i = 0; i != Args::nthreads_; ++i) { rw_error (0 == rw_thread_join (tid [i], 0), 0, __LINE__, "thread_join() failed"); } // compute the expected result intT expect = intT (); if (inc < 0) { for (unsigned i = 0; i != Args::nthreads_ * Args::nincr_; ++i) --expect; } else if (inc > 0) { for (unsigned i = 0; i != Args::nthreads_ * Args::nincr_; ++i) ++expect; } // verify that the final value of the variable shared among all // threads equals the number of increments or decrements performed // by all threads rw_assert (Args::shared_ [0] == expect, 0, __LINE__, "1. %s (%s&); %s == %s failed", fun, tname, TOSTR (Args::shared_ [0]), TOSTR (expect)); rw_assert (Args::shared_ [1] == expect, 0, __LINE__, "2. %s (%s&); %s == %s failed", fun, tname, TOSTR (Args::shared_ [1]), TOSTR (expect)); #else // if !defined (_RWSTD_REENTRANT) _RWSTD_UNUSED (tag); _RWSTD_UNUSED (inc); #endif // _RWSTD_REENTRANT } /**************************************************************************/ static int run_test (int, char**) { // exercise atomic subtract run_test ((char)0, thr_args_base::Char, -1); run_test ((signed char)0, thr_args_base::SChar, -1); run_test ((unsigned char)0, thr_args_base::UChar, -1); run_test ((short)0, thr_args_base::Short, -1); run_test ((unsigned short)0, thr_args_base::UShort, -1); run_test ((int)0, thr_args_base::Int, -1); run_test ((unsigned int)0, thr_args_base::UInt, -1); run_test ((long)0, thr_args_base::Long, -1); run_test ((unsigned long)0, thr_args_base::ULong, -1); #ifdef _RWSTD_LONG_LONG run_test ((_RWSTD_LONG_LONG)0, thr_args_base::LLong, -1); run_test ((unsigned _RWSTD_LONG_LONG)0, thr_args_base::ULLong, -1); #endif // _RWSTD_LONG_LONG // exercise atomic add run_test ((char)0, thr_args_base::Char, +1); run_test ((signed char)0, thr_args_base::SChar, +1); run_test ((unsigned char)0, thr_args_base::UChar, +1); run_test ((short)0, thr_args_base::Short, +1); run_test ((unsigned short)0, thr_args_base::UShort, +1); run_test ((int)0, thr_args_base::Int, +1); run_test ((unsigned int)0, thr_args_base::UInt, +1); run_test ((long)0, thr_args_base::Long, +1); run_test ((unsigned long)0, thr_args_base::ULong, +1); #ifdef _RWSTD_LONG_LONG run_test ((_RWSTD_LONG_LONG)0, thr_args_base::LLong, +1); run_test ((unsigned _RWSTD_LONG_LONG)0, thr_args_base::ULLong, +1); #endif // _RWSTD_LONG_LONG // exercise both atomic add and subtract run_test ((char)0, thr_args_base::Char, 0); run_test ((signed char)0, thr_args_base::SChar, 0); run_test ((unsigned char)0, thr_args_base::UChar, 0); run_test ((short)0, thr_args_base::Short, 0); run_test ((unsigned short)0, thr_args_base::UShort, 0); run_test ((int)0, thr_args_base::Int, 0); run_test ((unsigned int)0, thr_args_base::UInt, 0); run_test ((long)0, thr_args_base::Long, 0); run_test ((unsigned long)0, thr_args_base::ULong, 0); #ifdef _RWSTD_LONG_LONG run_test ((_RWSTD_LONG_LONG)0, thr_args_base::LLong, 0); run_test ((unsigned _RWSTD_LONG_LONG)0, thr_args_base::ULLong, 0); #endif // _RWSTD_LONG_LONG return 0; } /**************************************************************************/ int main (int argc, char *argv[]) { return rw_test (argc, argv, __FILE__, 0 /* no clause */, 0 /* no comment */, 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); }