thread_unittest.cc 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286
  1. // Copyright (C) 2012, 2015 Internet Systems Consortium, Inc. ("ISC")
  2. //
  3. // Permission to use, copy, modify, and/or distribute this software for any
  4. // purpose with or without fee is hereby granted, provided that the above
  5. // copyright notice and this permission notice appear in all copies.
  6. //
  7. // THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
  8. // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
  9. // AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
  10. // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  11. // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
  12. // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  13. // PERFORMANCE OF THIS SOFTWARE.
  14. #include <config.h>
  15. #include <util/process_spawn.h>
  16. #include <util/threads/sync.h>
  17. #include <util/threads/thread.h>
  18. #include <util/unittests/check_valgrind.h>
  19. #include <boost/bind.hpp>
  20. #include <boost/scoped_ptr.hpp>
  21. #include <gtest/gtest.h>
  22. #include <signal.h>
  23. #include <unistd.h>
  24. // This file tests the Thread class. It's hard to test an actual thread is
  25. // started, but we at least check the function is run and exceptions are
  26. // propagated as they should.
  27. //
  28. // We run some tests multiple times to see if there happen to be a race
  29. // condition (then it would have better chance showing up).
  30. //
  31. // The detached tests are not run as many times to prevent many threads being
  32. // started in parallel (the other tests wait for the previous one to terminate
  33. // before starting new one).
  34. using namespace isc::util;
  35. using namespace isc::util::thread;
  36. namespace {
  37. const size_t iterations = 200;
  38. const size_t detached_iterations = 25;
  39. /// @brief Implements a thread which can be stopped.
  40. ///
  41. /// This class implements a worker thread which can be stopped by calling
  42. /// StoppableThread::stop. The call to this function blocks until the thread
  43. /// terminates. This class is useful for testing scenarios when the thread
  44. /// needs to be run for a specific amount of time.
  45. class StoppableThread {
  46. public:
  47. /// @brief Constructor.
  48. ///
  49. /// It doesn't run the thread yet. It merely initializes required
  50. /// class members.
  51. StoppableThread()
  52. : stopping_(false), mutex_(), thread_() {
  53. }
  54. /// @brief Destructor.
  55. ///
  56. /// Detaches the thread.
  57. ~StoppableThread() {
  58. }
  59. /// @brief Starts the execution of the thread.
  60. ///
  61. /// This method will not start the thread if the thread has been stopped.
  62. /// In order to start the thread again, @c StoppableThread::reset must be
  63. /// called first.
  64. void run() {
  65. if (!amStopping()) {
  66. thread_.reset(new Thread(boost::bind(&StoppableThread::loop, this)));
  67. }
  68. }
  69. /// @brief Stops the thread as soon as possible.
  70. void stop() {
  71. if (!amStopping()) {
  72. setStopping(true);
  73. if (thread_) {
  74. thread_->wait();
  75. thread_.reset();
  76. }
  77. }
  78. }
  79. /// @brief Resets the thread state so as it can be ran again.
  80. void reset() {
  81. setStopping(false);
  82. }
  83. private:
  84. /// @brief Checks if the thread is being stopped.
  85. ///
  86. /// @return true if the thread is being stopped.
  87. bool amStopping() {
  88. Mutex::Locker lock(mutex_);
  89. return (stopping_);
  90. }
  91. /// @brief Sets the stopping state.
  92. ///
  93. /// @param stopping New value for @c stopping_ state.
  94. void setStopping(const bool stopping) {
  95. Mutex::Locker lock(mutex_);
  96. stopping_ = stopping;
  97. }
  98. /// @brief Worker thread loop.
  99. ///
  100. /// It runs until the @c StoppableThread::stop is called.
  101. void loop() {
  102. for (;;) {
  103. if (amStopping()) {
  104. break;
  105. }
  106. usleep(100);
  107. }
  108. }
  109. /// @brief Flag indicating if the thread is being stopped.
  110. bool stopping_;
  111. /// @brief Mutex used for protecting @c stopping_ flag.
  112. Mutex mutex_;
  113. /// @brief Pointer to the thread instance.
  114. boost::scoped_ptr<Thread> thread_;
  115. };
  116. /// @brief Static instance of the stoppable thread.
  117. boost::scoped_ptr<StoppableThread> thread;
  118. /// @brief Test fixture class for testing @c Thread.
  119. class ThreadTest : public ::testing::Test {
  120. public:
  121. /// @brief Destructor.
  122. ///
  123. /// Stops the thread and resets the static pointer to
  124. /// @c StoppableThread.
  125. virtual ~ThreadTest() {
  126. if (thread) {
  127. thread->stop();
  128. }
  129. thread.reset();
  130. }
  131. /// @brief No-op method.
  132. static void doSomething(int*) { }
  133. /// @brief Marks specified boolean value as true to indicate that the
  134. /// function has been run.
  135. static void markRun(bool* mark) {
  136. EXPECT_FALSE(*mark);
  137. *mark = true;
  138. }
  139. /// @brief Throws 42.
  140. static void throwSomething() {
  141. throw 42; // Throw something really unusual, to see everything is caught.
  142. }
  143. /// @brief Throws standard exception.
  144. static void throwException() {
  145. throw std::exception();
  146. }
  147. /// @brief Returns signal mask set for a thread.
  148. ///
  149. /// @parm mask Pointer to signal mask set for the calling thread.
  150. static void getSignalMask(sigset_t* mask) {
  151. pthread_sigmask(SIG_SETMASK, 0, mask);
  152. }
  153. };
  154. // We just test that we can forget about the thread and nothing
  155. // bad will happen on our side.
  156. TEST_F(ThreadTest, detached) {
  157. if (!isc::util::unittests::runningOnValgrind()) {
  158. int x;
  159. for (size_t i = 0; i < detached_iterations; ++i) {
  160. Thread thread(boost::bind(&ThreadTest::doSomething, &x));
  161. }
  162. }
  163. }
  164. // Wait for a thread to end first. The variable must be set at the time.
  165. TEST_F(ThreadTest, wait) {
  166. if (!isc::util::unittests::runningOnValgrind()) {
  167. for (size_t i = 0; i < iterations; ++i) {
  168. bool mark = false;
  169. Thread thread(boost::bind(&ThreadTest::markRun, &mark));
  170. thread.wait();
  171. ASSERT_TRUE(mark) << "Not finished yet in " << i << "th iteration";
  172. // Can't wait second time
  173. ASSERT_THROW(thread.wait(), isc::InvalidOperation);
  174. }
  175. }
  176. }
  177. // Exception in the thread we forget about should not do anything to us
  178. TEST_F(ThreadTest, detachedException) {
  179. if (!isc::util::unittests::runningOnValgrind()) {
  180. for (size_t i = 0; i < detached_iterations; ++i) {
  181. Thread thread(&ThreadTest::throwSomething);
  182. }
  183. for (size_t i = 0; i < detached_iterations; ++i) {
  184. Thread thread(&ThreadTest::throwException);
  185. }
  186. }
  187. }
  188. // An uncaught exception in the thread should propagate through wait
  189. TEST_F(ThreadTest, exception) {
  190. if (!isc::util::unittests::runningOnValgrind()) {
  191. for (size_t i = 0; i < iterations; ++i) {
  192. Thread thread(throwSomething);
  193. Thread thread2(throwException);
  194. ASSERT_THROW(thread.wait(), Thread::UncaughtException);
  195. ASSERT_THROW(thread2.wait(), Thread::UncaughtException);
  196. }
  197. }
  198. }
  199. // Verify that all signals are blocked.
  200. TEST_F(ThreadTest, sigmask) {
  201. sigset_t mask;
  202. sigemptyset(&mask);
  203. Thread thread(boost::bind(&ThreadTest::getSignalMask, &mask));
  204. ASSERT_NO_THROW(thread.wait());
  205. EXPECT_EQ(1, sigismember(&mask, SIGHUP));
  206. EXPECT_EQ(1, sigismember(&mask, SIGINT));
  207. EXPECT_EQ(1, sigismember(&mask, SIGTERM));
  208. }
  209. /// The @c ProcessSpawn class spawns new processes using the fork/exec
  210. /// scheme. If the call to exec fails, child process exits with the
  211. /// EXIT_FAILURE status code. The call to exit triggers destruction of
  212. /// all static objects, i.e. destructors of statically declared
  213. /// @c Thread objects are called in the child process. The call to
  214. /// fork doesn't clone threads existing in the main process. So, the
  215. /// @c Thread objects in the child process have invalid state, because
  216. /// they point to non-existing threads. As a result any attempts to
  217. /// detach or join the thread may result in errors or asserts.
  218. ///
  219. /// This test verifies that the @c Thread class protects against such
  220. /// errors. It is supposed to detect that the @c Thread object is in
  221. /// the child process and not assert when the destruction fails.
  222. TEST_F(ThreadTest, spawnProcessWithThread) {
  223. // Initialize and run the stoppable thread. Note that the 'thread'
  224. // is a static variable, which will be 'cloned' into the child
  225. // process. Its destructor will be called when the child process
  226. // terminates with EXIT_FAILURE status. So in fact the destructor
  227. // of the @c StoppableThread will be called twice: in the main
  228. // process and in the child process.
  229. thread.reset(new StoppableThread());
  230. thread->run();
  231. // Spawn the new process, using some non-existing executable. The
  232. // current process will fork but the execvp should fail.
  233. ProcessSpawn process_spawn("kea-dhcp4-a86570943h");
  234. pid_t pid = process_spawn.spawn();
  235. // Wait for the process to terminate.
  236. while (process_spawn.isRunning(pid)) {
  237. usleep(100);
  238. }
  239. // When the child process terminates it will call destructors of
  240. // static objects. This means that it will call the destructor of
  241. // the 'thread' object too. The 'thread' object in the child
  242. // process points to non-existing thread, but we expect the
  243. // graceful destruction, i.e. the destructor should not assert.
  244. // If the destructor asserts the exit code returned here will
  245. // be 0 - same as if we aborted.
  246. EXPECT_EQ(EXIT_FAILURE, process_spawn.getExitStatus(pid));
  247. }
  248. }