/mordor/streams/timeout.cpp

http://github.com/mozy/mordor · C++ · 186 lines · 160 code · 17 blank · 9 comment · 24 complexity · 70fc8d141bc9bf1fedde0dcd05c082ab MD5 · raw file

  1. // Copyright (c) 2010 - Mozy, Inc.
  2. #include "timeout.h"
  3. #include "mordor/assert.h"
  4. #include "mordor/exception.h"
  5. #include "mordor/log.h"
  6. #include "mordor/socket.h"
  7. #include "mordor/timer.h"
  8. namespace Mordor {
  9. static Logger::ptr g_log = Log::lookup("mordor:streams:timeout");
  10. static void cancelReadLocal(Stream::ptr stream) { stream->cancelRead(); }
  11. static void cancelWriteLocal(Stream::ptr stream) { stream->cancelWrite(); }
  12. static void cancelReadWriteLocal(Stream::ptr stream)
  13. {
  14. cancelReadLocal(stream);
  15. cancelWriteLocal(stream);
  16. }
  17. TimeoutHandler::~TimeoutHandler()
  18. {
  19. if (m_timer)
  20. cancelTimer();
  21. }
  22. void
  23. TimeoutHandler::onTimeout()
  24. {
  25. if (m_lastTimedOut == TIMING) {
  26. MORDOR_LOG_DEBUG(g_log) << this << " timeout";
  27. m_lastTimedOut = m_permaTimedOut = TIMEDOUT;
  28. if (m_timeoutDg)
  29. m_timeoutDg();
  30. } else {
  31. MORDOR_LOG_DEBUG(g_log) << this << " timeout no longer registered";
  32. }
  33. }
  34. void
  35. TimeoutHandler::setTimeout(unsigned long long timeout, TimeoutDg dg)
  36. {
  37. m_timeout = timeout;
  38. m_timeoutDg = dg;
  39. if (m_timer) {
  40. // timer is running, need to stop it or reset its counting down
  41. if (!isTimeoutSet()) {
  42. cancelTimer();
  43. } else {
  44. m_timer->reset(timeout, true);
  45. }
  46. } else {
  47. // timer is not running, start it
  48. if (isTimeoutSet()) {
  49. // start new timer if read/write is ongoing
  50. // OR auto start is set
  51. if (m_lastTimedOut == TIMING || m_autoStart) {
  52. m_timer = m_timerManager.registerConditionTimer(timeout,
  53. boost::bind(&TimeoutHandler::onTimeout, this),
  54. shared_from_this());
  55. }
  56. }
  57. }
  58. }
  59. void
  60. TimeoutHandler::startTimer()
  61. {
  62. MORDOR_LOG_TRACE(g_log) << this << " startTimer()";
  63. if (m_permaTimedOut == TIMEDOUT)
  64. MORDOR_THROW_EXCEPTION(TimedOutException());
  65. MORDOR_ASSERT(!m_timer);
  66. m_lastTimedOut = TIMING;
  67. if (isTimeoutSet())
  68. m_timer = m_timerManager.registerConditionTimer(m_timeout,
  69. boost::bind(&TimeoutHandler::onTimeout, this),
  70. shared_from_this());
  71. }
  72. bool
  73. TimeoutHandler::cancelTimer()
  74. {
  75. MORDOR_LOG_TRACE(g_log) << this << " cancelTimer()";
  76. bool res = (m_lastTimedOut == TIMEDOUT);
  77. if (m_timer) {
  78. m_timer->cancel();
  79. m_timer.reset();
  80. }
  81. m_lastTimedOut = NONE;
  82. return res;
  83. }
  84. bool
  85. TimeoutHandler::refreshTimer()
  86. {
  87. MORDOR_LOG_TRACE(g_log) << this << " refreshTimer()";
  88. bool res = (m_lastTimedOut == TIMEDOUT);
  89. if (m_timer) {
  90. m_timer->refresh();
  91. }
  92. m_lastTimedOut = TIMING;
  93. return res;
  94. }
  95. void
  96. TimeoutStream::readTimeout(unsigned long long readTimeout)
  97. {
  98. FiberMutex::ScopedLock lock(m_mutex);
  99. m_reader->setTimeout(readTimeout, boost::bind(&cancelReadLocal, parent()));
  100. }
  101. void
  102. TimeoutStream::writeTimeout(unsigned long long writeTimeout)
  103. {
  104. FiberMutex::ScopedLock lock(m_mutex);
  105. m_writer->setTimeout(writeTimeout, boost::bind(&cancelWriteLocal, parent()));
  106. }
  107. void
  108. TimeoutStream::idleTimeout(unsigned long long idleTimeout)
  109. {
  110. FiberMutex::ScopedLock lock(m_mutex);
  111. m_idler->setTimeout(idleTimeout, boost::bind(&cancelReadWriteLocal, parent()));
  112. }
  113. size_t
  114. TimeoutStream::read(Buffer &buffer, size_t length)
  115. {
  116. FiberMutex::ScopedLock lock(m_mutex);
  117. // start read timer & tickle idle
  118. m_reader->startTimer();
  119. m_idler->refreshTimer();
  120. lock.unlock();
  121. size_t result;
  122. try {
  123. result = parent()->read(buffer, length);
  124. } catch (OperationAbortedException &) {
  125. lock.lock();
  126. if (m_reader->cancelTimer() || m_idler->cancelTimer())
  127. MORDOR_THROW_EXCEPTION(TimedOutException());
  128. throw;
  129. } catch (...) {
  130. lock.lock();
  131. m_reader->cancelTimer();
  132. m_idler->cancelTimer();
  133. throw;
  134. }
  135. lock.lock();
  136. // read done, stop read timer & tickle idle
  137. m_reader->cancelTimer();
  138. m_idler->refreshTimer();
  139. return result;
  140. }
  141. size_t
  142. TimeoutStream::write(const Buffer &buffer, size_t length)
  143. {
  144. FiberMutex::ScopedLock lock(m_mutex);
  145. // start write timer & tickle idle
  146. m_writer->startTimer();
  147. m_idler->refreshTimer();
  148. lock.unlock();
  149. size_t result;
  150. try {
  151. result = parent()->write(buffer, length);
  152. } catch (OperationAbortedException &) {
  153. lock.lock();
  154. if (m_writer->cancelTimer() || m_idler->cancelTimer())
  155. MORDOR_THROW_EXCEPTION(TimedOutException());
  156. throw;
  157. } catch (...) {
  158. lock.lock();
  159. m_writer->cancelTimer();
  160. m_idler->cancelTimer();
  161. throw;
  162. }
  163. lock.lock();
  164. // write done, stop write timer & tickle idle
  165. m_writer->cancelTimer();
  166. m_idler->refreshTimer();
  167. return result;
  168. }
  169. }