PageRenderTime 44ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/mordor/tests/iomanager.cpp

http://github.com/mozy/mordor
C++ | 242 lines | 200 code | 25 blank | 17 comment | 8 complexity | b9cb0964119f8783c99df5b7ad635ddd MD5 | raw file
Possible License(s): BSD-3-Clause
  1. // Copyright (c) 2009 - Mozy, Inc.
  2. #include <boost/bind.hpp>
  3. #include "mordor/future.h"
  4. #include "mordor/iomanager.h"
  5. #include "mordor/test/test.h"
  6. #include "mordor/thread.h"
  7. #include "mordor/version.h"
  8. #include "mordor/socket.h"
  9. using namespace Mordor;
  10. using namespace Mordor::Test;
  11. namespace
  12. {
  13. class EmptyTimeClass
  14. {
  15. public:
  16. typedef boost::shared_ptr<EmptyTimeClass> ptr;
  17. public:
  18. EmptyTimeClass()
  19. : m_timedOut(false)
  20. , m_future(NULL)
  21. {}
  22. void onTimer() {
  23. m_timedOut = true;
  24. if (m_future)
  25. m_future->signal();
  26. }
  27. void setFuture(Future<> *future) { m_future = future; }
  28. bool timedOut() const { return m_timedOut; }
  29. private:
  30. bool m_timedOut;
  31. Future<> * m_future;
  32. };
  33. void testTimerExpired(IOManager &manager)
  34. {
  35. Future<> future;
  36. EmptyTimeClass::ptr tester(new EmptyTimeClass());
  37. tester->setFuture(&future);
  38. MORDOR_TEST_ASSERT(tester.unique());
  39. manager.registerTimer(1000, boost::bind(&EmptyTimeClass::onTimer, tester));
  40. MORDOR_TEST_ASSERT(!tester.unique());
  41. // wait until timed out
  42. future.wait();
  43. MORDOR_TEST_ASSERT(tester->timedOut());
  44. MORDOR_TEST_ASSERT(tester.unique());
  45. }
  46. void testTimerNoExpire(IOManager &manager)
  47. {
  48. EmptyTimeClass::ptr tester(new EmptyTimeClass());
  49. MORDOR_TEST_ASSERT(tester.unique());
  50. Timer::ptr timer = manager.registerTimer(30000000,
  51. boost::bind(&EmptyTimeClass::onTimer, tester));
  52. MORDOR_TEST_ASSERT(!tester.unique());
  53. timer->cancel();
  54. MORDOR_TEST_ASSERT(!tester->timedOut());
  55. MORDOR_TEST_ASSERT(tester.unique());
  56. }
  57. }
  58. static void
  59. singleTimer(int &sequence, int &expected)
  60. {
  61. ++sequence;
  62. MORDOR_TEST_ASSERT_EQUAL(sequence, expected);
  63. }
  64. MORDOR_UNITTEST(IOManager, singleTimer)
  65. {
  66. int sequence = 0;
  67. IOManager manager;
  68. manager.registerTimer(0, boost::bind(&singleTimer, boost::ref(sequence), 1));
  69. MORDOR_TEST_ASSERT_EQUAL(sequence, 0);
  70. manager.dispatch();
  71. ++sequence;
  72. MORDOR_TEST_ASSERT_EQUAL(sequence, 2);
  73. }
  74. MORDOR_UNITTEST(IOManager, laterTimer)
  75. {
  76. int sequence = 0;
  77. IOManager manager;
  78. manager.registerTimer(100000, boost::bind(&singleTimer, boost::ref(sequence), 1));
  79. MORDOR_TEST_ASSERT_EQUAL(sequence, 0);
  80. manager.dispatch();
  81. ++sequence;
  82. MORDOR_TEST_ASSERT_EQUAL(sequence, 2);
  83. }
  84. MORDOR_UNITTEST(IOManager, timerRefCountNoExpired)
  85. {
  86. IOManager manager;
  87. manager.schedule(boost::bind(testTimerNoExpire, boost::ref(manager)));
  88. manager.dispatch();
  89. }
  90. MORDOR_UNITTEST(IOManager, timerRefCountExpired)
  91. {
  92. IOManager manager;
  93. manager.schedule(boost::bind(testTimerExpired, boost::ref(manager)));
  94. manager.dispatch();
  95. }
  96. static void
  97. busyFiber(int &sequence)
  98. {
  99. // this function will keep the scheduler busy (no idle)
  100. while (true) {
  101. --sequence;
  102. if (sequence <= 0)
  103. break;
  104. MORDOR_LOG_INFO(Mordor::Log::root()) << " sequence: " << sequence;
  105. Scheduler::yield();
  106. }
  107. }
  108. static void
  109. switchThreadFiber(int &sequence, tid_t tidA, tid_t tidB)
  110. {
  111. // this function will switch itself to anther thread and continue
  112. while (true) {
  113. if (sequence <= 0)
  114. return;
  115. tid_t otherTid = (gettid() == tidA) ? tidB : tidA;
  116. Scheduler::getThis()->schedule(Fiber::getThis(), otherTid);
  117. Scheduler::yieldTo();
  118. }
  119. }
  120. MORDOR_UNITTEST(IOManager, tickleDeadlock)
  121. {
  122. IOManager manager(2, true);
  123. tid_t tidA = manager.rootThreadId();
  124. tid_t tidB = 0;
  125. {
  126. const std::vector<boost::shared_ptr<Thread> > threads =
  127. manager.threads();
  128. MORDOR_TEST_ASSERT_EQUAL(threads.size(), 1U);
  129. tidB = threads[0]->tid();
  130. }
  131. MORDOR_TEST_ASSERT_NOT_EQUAL(tidA, tidB);
  132. // there are >2 tickles each time the busyFiber get running.
  133. // sequence initial value will control how long the case will run
  134. #ifdef LINUX
  135. // pipe buffer is 64K on Linux 2.6.x kernel
  136. int sequence = 32768;
  137. #elif defined(OSX)
  138. // MAC pipe buffer is 8K by default
  139. int sequence = 8192;
  140. #else
  141. // Other platform, sanity check with a small number
  142. int sequence = 4096;
  143. #endif
  144. // schedule a busy fiber
  145. manager.schedule(boost::bind(busyFiber, boost::ref(sequence)));
  146. // schedule aditional two fibers to ensure that each thread will get
  147. // get at least one fiber to execute at anytime, no chance to idle.
  148. manager.schedule(boost::bind(switchThreadFiber,
  149. boost::ref(sequence), tidA, tidB),
  150. tidA);
  151. manager.schedule(boost::bind(switchThreadFiber,
  152. boost::ref(sequence), tidA, tidB),
  153. tidB);
  154. manager.stop();
  155. }
  156. namespace {
  157. struct Connection
  158. {
  159. Socket::ptr connect;
  160. Socket::ptr listen;
  161. Socket::ptr accept;
  162. IPAddress::ptr address;
  163. };
  164. }
  165. static Connection establishConn(IOManager &ioManager)
  166. {
  167. Connection result;
  168. std::vector<Address::ptr> addresses = Address::lookup("localhost");
  169. MORDOR_TEST_ASSERT(!addresses.empty());
  170. result.address = boost::dynamic_pointer_cast<IPAddress>(addresses.front());
  171. result.listen = result.address->createSocket(ioManager, SOCK_STREAM);
  172. unsigned int opt = 1;
  173. result.listen->setOption(SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
  174. while (true) {
  175. try {
  176. // Random port > 1000
  177. result.address->port(rand() % 50000 + 1000);
  178. result.listen->bind(result.address);
  179. break;
  180. } catch (AddressInUseException &) {
  181. }
  182. }
  183. result.listen->listen();
  184. result.connect = result.address->createSocket(ioManager, SOCK_STREAM);
  185. return result;
  186. }
  187. static void connect(IOManager &manager)
  188. {
  189. try {
  190. Connection conns = establishConn(manager);
  191. conns.connect->sendTimeout(30000000);
  192. conns.connect->receiveTimeout(30000000);
  193. conns.listen->receiveTimeout(30000000);
  194. conns.connect->connect(conns.address);
  195. conns.connect->shutdown();
  196. } catch (TimedOutException &) {
  197. }
  198. }
  199. #ifdef OSX
  200. #define THREADCOUNT 100U
  201. #else
  202. #define THREADCOUNT 10U
  203. #endif
  204. //Use 100 threads creating sockets, add/delete kevent into/from kqueue at same time,
  205. //overwhelm the system so that it cannot respond to all the events,
  206. //This could easily make kqueue return with EINPROGRESS (deferred deletion) error
  207. //and we make sure error ignored in MacOS -- redmine #145605
  208. MORDOR_UNITTEST(IOManager, ignoreEinprogressErrorOnMac)
  209. {
  210. IOManager manager(THREADCOUNT, false);
  211. const std::vector<boost::shared_ptr<Thread> > threads = manager.threads();
  212. MORDOR_TEST_ASSERT_EQUAL(threads.size(), THREADCOUNT);
  213. for(size_t i=0; i<threads.size(); i++) {
  214. manager.schedule(boost::bind(connect, boost::ref(manager)), threads[i]->tid());
  215. }
  216. manager.stop();
  217. }