/mordor/streams/throttle.cpp
C++ | 99 lines | 85 code | 8 blank | 6 comment | 26 complexity | b88ee6f53ee6828404a2299b77cef8b5 MD5 | raw file
1// Copyright (c) 2009 - Mozy, Inc. 2 3#include "throttle.h" 4 5#include "mordor/assert.h" 6#include "mordor/log.h" 7#include "mordor/sleep.h" 8#include "mordor/timer.h" 9 10namespace Mordor { 11 12static Logger::ptr g_log = Log::lookup("mordor:streams:throttle"); 13 14size_t 15ThrottleStream::read(Buffer &b, size_t len) 16{ 17 MORDOR_ASSERT(len != 0); 18 unsigned int throttle = m_dg(); 19 if (throttle == 0 || throttle == ~0u) { 20 if (m_read > 0){ 21 m_read = 0; 22 MORDOR_LOG_DEBUG(g_log) << this << " no longer throttling on read"; 23 } 24 MORDOR_LOG_DEBUG(g_log) << this << " read " << len << "B unthrottled "; 25 return parent()->read(b, len); 26 } 27 unsigned long long now = TimerManager::now(); 28 if (m_read == 0) { 29 MORDOR_LOG_DEBUG(g_log) << this << " starting throttling on read " << throttle << "bps"; 30 } else { 31 unsigned long long minTime = 1000000ull * (m_read * 8) / throttle; 32 unsigned long long actualTime = (now - m_readTimestamp); 33 34 MORDOR_LOG_DEBUG(g_log) << this << " read " << m_read << "B throttle " 35 << throttle << "bps now " << now << "us last " << m_readTimestamp 36 << "us min" << minTime << " us actual " << actualTime << "us"; 37 if (actualTime < minTime) { 38 unsigned long long sleepTime = minTime - actualTime; 39 // Never sleep for longer than a tenth of a second 40 sleepTime = (std::min)(100000ull, sleepTime); 41 if (m_timerManager) 42 sleep(*m_timerManager, sleepTime); 43 else 44 sleep(sleepTime); 45 now = TimerManager::now(); 46 } 47 } 48 // Aim for no more than a tenth of a second's worth of data 49 len = std::min<size_t>(throttle / 8 / 10, len); 50 if (len == 0) 51 len = 1; 52 m_readTimestamp = now; 53 return m_read = parent()->read(b, len); 54} 55 56size_t 57ThrottleStream::write(const Buffer &b, size_t len) 58{ 59 MORDOR_ASSERT(len != 0); 60 unsigned int throttle = m_dg(); 61 if (throttle == 0 || throttle == ~0u) { 62 if (m_written > 0){ 63 m_written = 0; 64 MORDOR_LOG_DEBUG(g_log) << this << " no longer throttling"; 65 } 66 MORDOR_LOG_DEBUG(g_log) << this << " write " << len << "B unthrottled "; 67 m_writeTimestamp = TimerManager::now(); 68 return parent()->write(b, len); 69 } 70 unsigned long long now = TimerManager::now(); 71 if (m_written == 0) { 72 MORDOR_LOG_DEBUG(g_log) << this << " starting throttling " << throttle << "bps"; 73 } else { 74 unsigned long long minTime = 1000000ull * (m_written * 8) / throttle; 75 unsigned long long actualTime = (now - m_writeTimestamp); 76 MORDOR_LOG_DEBUG(g_log) << this << " write " << m_written << "B throttle " 77 << throttle << "bps now " << now << "us last " << m_writeTimestamp 78 << "us min " << minTime << "us actual " << actualTime << "us"; 79 if (actualTime < minTime) { 80 unsigned long long sleepTime = minTime - actualTime; 81 // Never sleep for longer than a tenth of a second 82 sleepTime = (std::min)(100000ull, sleepTime); 83 if (m_timerManager) 84 sleep(*m_timerManager, sleepTime); 85 else 86 sleep(sleepTime); 87 now = TimerManager::now(); 88 } 89 } 90 // Aim for no more than a tenth of a second's worth of data 91 len = std::min<size_t>(throttle / 8 / 10, len); 92 if (len == 0) 93 len = 1; 94 // set timestamp to now. This needs to include the sleep time above - with multiple connections per host, we may be doing context switching when we sleep. 95 m_writeTimestamp = now; 96 return m_written = parent()->write(b, len); 97} 98 99}