summaryrefslogtreecommitdiff
path: root/ACE/tests/Recursive_Mutex_Test.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'ACE/tests/Recursive_Mutex_Test.cpp')
-rw-r--r--ACE/tests/Recursive_Mutex_Test.cpp340
1 files changed, 340 insertions, 0 deletions
diff --git a/ACE/tests/Recursive_Mutex_Test.cpp b/ACE/tests/Recursive_Mutex_Test.cpp
new file mode 100644
index 00000000000..f99dba58ff0
--- /dev/null
+++ b/ACE/tests/Recursive_Mutex_Test.cpp
@@ -0,0 +1,340 @@
+// $Id$
+
+// ============================================================================
+//
+// = LIBRARY
+// tests
+//
+// = FILENAME
+// Recursive_Mutex_Test.cpp
+//
+// = DESCRIPTION
+// This test program verifies the functionality of the ACE_OS
+// implementation of recursive mutexes on Win32 and Posix
+// pthreads.
+//
+// = AUTHOR
+// Prashant Jain <pjain@cs.wustl.edu> and Douglas C. Schmidt <schmidt@cs.wustl.edu>
+//
+// ============================================================================
+
+#include "test_config.h"
+#include "ace/Get_Opt.h"
+#include "ace/Thread_Manager.h"
+#include "ace/OS_NS_time.h"
+#include "ace/OS_NS_sys_time.h"
+#include "ace/OS_NS_unistd.h"
+#include "ace/Recursive_Thread_Mutex.h"
+
+ACE_RCSID(tests, Recursive_Mutex_Test, "$Id$")
+
+#if defined (ACE_HAS_THREADS)
+
+#include "ace/Guard_T.h"
+
+// For all platforms except for Windows use the
+// ACE_Recursive_Thread_Mutex. Since Windows only supports timed
+// recursive process mutexes and not timed recursive thread mutexes,
+// use ACE_Process_Mutex.
+#if defined (ACE_HAS_WTHREADS)
+# include "ace/Process_Mutex.h"
+ typedef ACE_Process_Mutex ACE_TEST_MUTEX;
+#else
+# include "ace/Thread_Mutex.h"
+ typedef ACE_Recursive_Thread_Mutex ACE_TEST_MUTEX;
+#endif
+
+#if !defined (ACE_HAS_MUTEX_TIMEOUTS)
+static int reported_notsup = 0;
+#endif /* ACE_HAS_MUTEX_TIMEOUTS */
+
+// Total number of iterations.
+static int n_iterations = 100;
+static size_t n_threads = ACE_MAX_THREADS;
+
+static void
+test_recursion_depth (int nesting_level,
+ ACE_TEST_MUTEX *rm)
+{
+ if (nesting_level < n_iterations)
+ {
+ int result = rm->acquire ();
+ ACE_ASSERT (result == 0);
+#if !defined (ACE_HAS_WTHREADS)
+ ACE_DEBUG ((LM_DEBUG,
+ ACE_TEXT ("(%P|%t) = acquired, nesting = %d, thread id = %u\n"),
+ rm->get_nesting_level (),
+ rm->get_thread_id ()));
+#endif /* !ACE_HAS_WTHREADS */
+
+ test_recursion_depth (nesting_level + 1,
+ rm);
+
+ result = rm->release ();
+ ACE_ASSERT (result == 0);
+#if !defined (ACE_HAS_WTHREADS)
+ ACE_DEBUG ((LM_DEBUG,
+ ACE_TEXT ("(%P|%t) = released, nesting = %d, thread id = %u\n"),
+ rm->get_nesting_level (),
+ rm->get_thread_id ()));
+#endif /* !ACE_HAS_WTHREADS */
+ }
+}
+
+static void
+test_timed_wait (int nesting_level,
+ ACE_TEST_MUTEX *rm)
+{
+ // Make sure that we're inside of a recursive level.
+ if (nesting_level == 0)
+ test_timed_wait (nesting_level + 1,
+ rm);
+ else
+ {
+ ACE_OS::srand (ACE_OS::time (0));
+
+ for (size_t i = 0; i < ACE_MAX_ITERATIONS / 2; i++)
+ {
+ int result = 0;
+
+ // First attempt to acquire the mutex with a timeout to verify
+ // that mutex timeouts are working.
+
+ ACE_DEBUG ((LM_DEBUG,
+ ACE_TEXT ("(%P|%t) = trying timed acquire on ")
+ ACE_TEXT ("iteration %d\n"),
+ i));
+
+ ACE_Time_Value delta (1, 0); // One second timeout
+ ACE_Time_Value timeout = ACE_OS::gettimeofday ();
+ timeout += delta; // Must pass absolute time to acquire().
+
+ if (rm->acquire (timeout) != 0)
+ {
+ if (errno == ETIME)
+ ACE_DEBUG ((LM_DEBUG,
+ ACE_TEXT ("(%P|%t) = mutex acquisition ")
+ ACE_TEXT ("timed out\n")));
+ else if (errno == ENOTSUP)
+ {
+#if !defined (ACE_HAS_MUTEX_TIMEOUTS)
+ if (!reported_notsup)
+ {
+ ACE_DEBUG ((LM_INFO,
+ ACE_TEXT ("(%P|%t) %p, but ACE_HAS_MUTEX_TIMEOUTS is not defined - Ok\n"),
+ ACE_TEXT ("mutex timed acquire")));
+ reported_notsup = 1;
+ }
+#else
+ ACE_DEBUG ((LM_ERROR,
+ ACE_TEXT ("(%P|%t) %p - maybe ACE_HAS_MUTEX_TIMEOUTS should not be defined?\n"),
+ ACE_TEXT ("mutex timed acquire")));
+#endif /* ACE_HAS_MUTEX_TIMEOUTS */
+ }
+ else
+ {
+ ACE_ERROR ((LM_ERROR,
+ ACE_TEXT ("(%P|%t) %p\n%a"),
+ ACE_TEXT ("mutex timeout failed\n")));
+ return;
+ }
+ }
+ else
+ {
+ result = rm->release ();
+ ACE_ASSERT (result == 0);
+ }
+
+ // Now try the standard mutex.
+
+ ACE_DEBUG ((LM_DEBUG,
+ ACE_TEXT ("(%P|%t) = trying to acquire on iteration %d\n"),
+ i));
+ result = rm->acquire ();
+ ACE_ASSERT (result == 0);
+ ACE_DEBUG ((LM_DEBUG,
+ ACE_TEXT ("(%P|%t) = acquired on iteration %d\n"),
+ i));
+
+ // Sleep for a random amount of time between 0 and 2 seconds.
+ // Note that it's ok to use rand() here because we are running
+ // within the critical section defined by the Thread_Mutex.
+ ACE_OS::sleep (ACE_OS::rand () % 2);
+
+ result = rm->release ();
+ ACE_ASSERT (result == 0);
+ ACE_DEBUG ((LM_DEBUG,
+ ACE_TEXT ("(%P|%t) = released on iteration %d\n"),
+ i));
+
+ // Basic ACE_Guard usage - automatically acquire the mutex on
+ // guard construction and automatically release it on
+ // destruction.
+ {
+ // Construct an ACE_Guard to implicitly acquire the mutex.
+ ACE_Guard<ACE_TEST_MUTEX> guard (*rm);
+ ACE_ASSERT (guard.locked () != 0);
+
+ // Perform some operation which might exit the current scope
+ // prematurely, e.g. by returning or throwing an exception.
+ // ...
+
+ // ACE_Guard object is destroyed when exiting scope and guard
+ // destructor automatically releases mutex.
+ }
+
+ // Use an ACE_Guard to automatically acquire a mutex, but release
+ // the mutex early.
+ {
+ // Construct an ACE_Guard to implicitly acquire the mutex.
+ ACE_Guard<ACE_TEST_MUTEX> guard (*rm);
+ ACE_ASSERT (guard.locked () != 0);
+
+ // Perform some operation which might exit the current scope
+ // prematurely, e.g. by returning or throwing an exception.
+ // ...
+
+ // Release the mutex since we no longer need it.
+ guard.release ();
+ ACE_ASSERT (guard.locked () == 0);
+
+ // Do something else which does not require the mutex to be locked.
+ // ...
+
+ // ACE_Guard object's destructor will not release the mutex.
+ }
+
+ // Use an ACE_Guard to automatically acquire a mutex, but
+ // relinquish ownership of the lock so that the mutex is not
+ // automatically released on guard destruction. This is useful
+ // when an operation might not release the mutex in some
+ // conditions, in which case responsibility for releasing it is
+ // passed to someone else.
+ {
+ // Construct an ACE_Guard to implicitly acquire the mutex.
+ ACE_Guard<ACE_TEST_MUTEX> guard (*rm);
+ ACE_ASSERT (guard.locked () != 0);
+
+ // Perform some operation which might exit the current scope
+ // prematurely, e.g. by returning or throwing an exception.
+ // ...
+
+ // Relinquish ownership of the mutex lock. Someone else must
+ // now release it.
+ guard.disown ();
+ ACE_ASSERT (guard.locked () == 0);
+
+ // ACE_Guard object's destructor will not release the mutex.
+ }
+ // We are now responsible for releasing the mutex.
+ result = rm->release ();
+ ACE_ASSERT (result == 0);
+
+ // Construct an ACE_Guard without automatically acquiring the lock.
+ {
+ // Construct an ACE_Guard object without automatically
+ // acquiring the mutex or taking ownership of an existing
+ // lock. The third parameter tells the guard that the mutex
+ // has not been locked.
+ ACE_Guard<ACE_TEST_MUTEX> guard (*rm, 0, 0);
+ ACE_ASSERT (guard.locked () == 0);
+
+ // Conditionally acquire the mutex.
+ if (i % 2 == 0)
+ {
+ guard.acquire ();
+ ACE_ASSERT (guard.locked () != 0);
+ }
+
+ // Perform some operation that might exit the current scope
+ // prematurely, e.g. by returning or throwing an exception.
+ // ...
+
+ // ACE_Guard object is destroyed when exiting scope and guard
+ // destructor automatically releases if it was acquired above.
+ }
+
+ // Use an ACE_Guard to take ownership of a previously acquired
+ // mutex.
+ timeout = ACE_OS::gettimeofday ();
+ timeout += delta; // Must pass absolute time to acquire().
+ if (rm->acquire (timeout) == 0)
+ {
+ // Construct an ACE_Guard object without automatically
+ // acquiring the mutex, but instead take ownership of the
+ // existing lock. The third parameter tells the guard that
+ // the mutex has already been locked.
+ ACE_Guard<ACE_TEST_MUTEX> guard (*rm, 0, 1);
+ ACE_ASSERT (guard.locked () != 0);
+
+ // Perform some operation which might exit the current scope
+ // prematurely, e.g. by returning or throwing an exception.
+ // ...
+
+ // ACE_Guard object is destroyed when exiting scope and guard
+ // destructor automatically releases mutex.
+ }
+ }
+
+ return;
+ }
+}
+
+static void *
+recursion_worker (void *arg)
+{
+ ACE_TEST_MUTEX *rm =
+ reinterpret_cast<ACE_TEST_MUTEX *> (arg);
+
+ ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("%P|%t) Starting test of recursion depth\n")));
+ test_recursion_depth (0, rm);
+
+ return 0;
+}
+
+static void *
+timed_worker (void *arg)
+{
+ ACE_TEST_MUTEX *rm =
+ reinterpret_cast<ACE_TEST_MUTEX *> (arg);
+
+ ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("%P|%t) Starting test of timed wait\n")));
+ test_timed_wait (0, rm);
+
+ return 0;
+}
+
+#endif /* ACE_HAS_THREADS */
+
+int
+run_main (int argc, ACE_TCHAR *argv[])
+{
+ ACE_START_TEST (ACE_TEXT ("Recursive_Mutex_Test"));
+
+#if defined (ACE_HAS_THREADS)
+ if (argc > 1)
+ {
+ n_threads = ACE_OS::atoi (argv[1]);
+ }
+
+ ACE_TEST_MUTEX rm;
+
+ ACE_Thread_Manager::instance ()->spawn_n (n_threads,
+ ACE_THR_FUNC (recursion_worker),
+ (void *) &rm);
+ ACE_Thread_Manager::instance ()->wait ();
+
+ ACE_Thread_Manager::instance ()->spawn_n (n_threads,
+ ACE_THR_FUNC (timed_worker),
+ (void *) &rm);
+ ACE_Thread_Manager::instance ()->wait ();
+#else
+ ACE_UNUSED_ARG (argc);
+ ACE_UNUSED_ARG (argv);
+ ACE_ERROR ((LM_ERROR,
+ ACE_TEXT ("ACE doesn't support recursive process ")
+ ACE_TEXT ("mutexes on this platform\n")));
+#endif /* ACE_HAS_THREADS */
+ ACE_END_TEST;
+ return 0;
+}