PageRenderTime 164ms CodeModel.GetById 80ms app.highlight 18ms RepoModel.GetById 62ms app.codeStats 0ms

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