diff options
-rw-r--r-- | cryptlib.cpp | 4 | ||||
-rw-r--r-- | cryptlib.h | 11 | ||||
-rw-r--r-- | filters.h | 4 | ||||
-rw-r--r-- | hrtimer.cpp | 16 | ||||
-rw-r--r-- | hrtimer.h | 2 | ||||
-rw-r--r-- | misc.h | 18 | ||||
-rw-r--r-- | network.cpp | 343 | ||||
-rw-r--r-- | network.h | 99 | ||||
-rw-r--r-- | socketft.cpp | 75 | ||||
-rw-r--r-- | socketft.h | 11 | ||||
-rw-r--r-- | test.cpp | 6 | ||||
-rw-r--r-- | wait.cpp | 165 | ||||
-rw-r--r-- | wait.h | 160 | ||||
-rw-r--r-- | winpipes.cpp | 16 | ||||
-rw-r--r-- | winpipes.h | 5 | ||||
-rw-r--r-- | zdeflate.cpp | 8 | ||||
-rw-r--r-- | zinflate.cpp | 2 |
17 files changed, 772 insertions, 173 deletions
diff --git a/cryptlib.cpp b/cryptlib.cpp index c85d891..fe0d764 100644 --- a/cryptlib.cpp +++ b/cryptlib.cpp @@ -179,11 +179,11 @@ unsigned int BufferedTransformation::GetMaxWaitObjectCount() const return t ? t->GetMaxWaitObjectCount() : 0; } -void BufferedTransformation::GetWaitObjects(WaitObjectContainer &container) +void BufferedTransformation::GetWaitObjects(WaitObjectContainer &container, CallStack const& callStack) { BufferedTransformation *t = AttachedTransformation(); if (t) - t->GetWaitObjects(container); + t->GetWaitObjects(container, callStack); // reduce clutter by not adding to stack here } void BufferedTransformation::Initialize(const NameValuePairs ¶meters, int propagation) @@ -661,6 +661,7 @@ public: CRYPTOPP_DLL RandomNumberGenerator & CRYPTOPP_API NullRNG(); class WaitObjectContainer; +class CallStack; //! interface for objects that you can wait for @@ -670,10 +671,14 @@ public: //! maximum number of wait objects that this object can return virtual unsigned int GetMaxWaitObjectCount() const =0; //! put wait objects into container - virtual void GetWaitObjects(WaitObjectContainer &container) =0; + /*! \param callStack is used for tracing no wait loops, example: + something.GetWaitObjects(c, CallStack("my func after X", 0)); + - or in an outer GetWaitObjects() method that itself takes a callStack parameter: + innerThing.GetWaitObjects(c, CallStack("MyClass::GetWaitObjects at X", &callStack)); */ + virtual void GetWaitObjects(WaitObjectContainer &container, CallStack const& callStack) =0; //! wait on this object /*! same as creating an empty container, calling GetWaitObjects(), and calling Wait() on the container */ - bool Wait(unsigned long milliseconds); + bool Wait(unsigned long milliseconds, CallStack const& callStack); }; //! interface for buffered transformations @@ -761,7 +766,7 @@ public: //! \name WAITING //@{ unsigned int GetMaxWaitObjectCount() const; - void GetWaitObjects(WaitObjectContainer &container); + void GetWaitObjects(WaitObjectContainer &container, CallStack const& callStack); //@} //! \name SIGNALS @@ -420,8 +420,8 @@ public: unsigned int GetMaxWaitObjectCount() const { return m_target && GetPassWaitObjects() ? m_target->GetMaxWaitObjectCount() : 0; } - void GetWaitObjects(WaitObjectContainer &container) - { if (m_target && GetPassWaitObjects()) m_target->GetWaitObjects(container); } + void GetWaitObjects(WaitObjectContainer &container, CallStack const& callStack) + { if (m_target && GetPassWaitObjects()) m_target->GetWaitObjects(container, callStack); } private: BufferedTransformation *m_target; diff --git a/hrtimer.cpp b/hrtimer.cpp index b29f745..c965ba0 100644 --- a/hrtimer.cpp +++ b/hrtimer.cpp @@ -34,7 +34,7 @@ double TimerBase::ConvertTo(word64 t, Unit unit) void TimerBase::StartTimer() { - m_start = GetCurrentTimerValue(); + m_last = m_start = GetCurrentTimerValue(); m_started = true; } @@ -42,13 +42,17 @@ double TimerBase::ElapsedTimeAsDouble() { if (m_stuckAtZero) return 0; - else if (m_started) - return ConvertTo(GetCurrentTimerValue() - m_start, m_timerUnit); - else + + if (m_started) { - StartTimer(); - return 0; + word64 now = GetCurrentTimerValue(); + if (m_last < now) // protect against OS bugs where time goes backwards + m_last = now; + return ConvertTo(m_last - m_start, m_timerUnit); } + + StartTimer(); + return 0; } unsigned long TimerBase::ElapsedTime() @@ -24,7 +24,7 @@ private: Unit m_timerUnit; // HPUX workaround: m_unit is a system macro on HPUX bool m_stuckAtZero, m_started; - word64 m_start; + word64 m_start, m_last; }; //! measure CPU time spent executing instructions of this thread (if supported by OS) @@ -116,6 +116,22 @@ retry: // ************** misc functions *************** +#if (!__STDC_WANT_SECURE_LIB__) +inline void memcpy_s(void *dest, size_t sizeInBytes, const void *src, size_t count) +{ + if (count > sizeInBytes) + throw InvalidArgument("memcpy_s: buffer overflow"); + memcpy(dest, src, count); +} + +inline void memmove_s(void *dest, size_t sizeInBytes, const void *src, size_t count) +{ + if (count > sizeInBytes) + throw InvalidArgument("memmove_s: buffer overflow"); + memmove(dest, src, count); +} +#endif + // can't use std::min or std::max in MSVC60 or Cygwin 1.1.0 template <class T> inline const T& STDMIN(const T& a, const T& b) { @@ -331,8 +347,6 @@ std::string IntToString(T a, unsigned int base = 10) template <class T1, class T2> inline T1 SaturatingSubtract(const T1 &a, const T2 &b) { - CRYPTOPP_COMPILE_ASSERT_INSTANCE(T1(-1)>0, 0); // T1 is unsigned type - CRYPTOPP_COMPILE_ASSERT_INSTANCE(T2(-1)>0, 1); // T2 is unsigned type return T1((a > b) ? (a - b) : 0); } diff --git a/network.cpp b/network.cpp index 49800fd..24f5013 100644 --- a/network.cpp +++ b/network.cpp @@ -8,6 +8,133 @@ NAMESPACE_BEGIN(CryptoPP) +lword LimitedBandwidth::ComputeCurrentTransceiveLimit() +{ + if (!m_maxBytesPerSecond) + return ULONG_MAX; + + double curTime = GetCurTimeAndCleanUp(); + lword total = 0; + for (OpQueue::size_type i=0; i!=m_ops.size(); ++i) + total += m_ops[i].second; + return SaturatingSubtract(m_maxBytesPerSecond, total); +} + +double LimitedBandwidth::TimeToNextTransceive() +{ + if (!m_maxBytesPerSecond) + return 0; + + if (!m_nextTransceiveTime) + ComputeNextTransceiveTime(); + + return SaturatingSubtract(m_nextTransceiveTime, m_timer.ElapsedTimeAsDouble()); +} + +void LimitedBandwidth::NoteTransceive(lword size) +{ + if (m_maxBytesPerSecond) + { + double curTime = GetCurTimeAndCleanUp(); + m_ops.push_back(std::make_pair(curTime, size)); + m_nextTransceiveTime = 0; + } +} + +void LimitedBandwidth::ComputeNextTransceiveTime() +{ + double curTime = GetCurTimeAndCleanUp(); + lword total = 0; + for (unsigned int i=0; i!=m_ops.size(); ++i) + total += m_ops[i].second; + m_nextTransceiveTime = + (total < m_maxBytesPerSecond) ? curTime : m_ops.front().first + 1000; +} + +double LimitedBandwidth::GetCurTimeAndCleanUp() +{ + if (!m_maxBytesPerSecond) + return 0; + + double curTime = m_timer.ElapsedTimeAsDouble(); + while (m_ops.size() && (m_ops.front().first + 1000 < curTime)) + m_ops.pop_front(); + return curTime; +} + +void LimitedBandwidth::GetWaitObjects(WaitObjectContainer &container, CallStack const& callStack) +{ + double nextTransceiveTime = TimeToNextTransceive(); + if (nextTransceiveTime) + container.ScheduleEvent(nextTransceiveTime, CallStack("LimitedBandwidth::GetWaitObjects()", &callStack)); +} + +// ************************************************************* + +size_t NonblockingSource::GeneralPump2( + lword& byteCount, bool blockingOutput, + unsigned long maxTime, bool checkDelimiter, byte delimiter) +{ + m_blockedBySpeedLimit = false; + + if (!GetMaxBytesPerSecond()) + { + size_t ret = DoPump(byteCount, blockingOutput, maxTime, checkDelimiter, delimiter); + m_doPumpBlocked = (ret != 0); + return ret; + } + + bool forever = (maxTime == INFINITE_TIME); + unsigned long timeToGo = maxTime; + Timer timer(Timer::MILLISECONDS, forever); + lword maxSize = byteCount; + byteCount = 0; + + timer.StartTimer(); + + while (true) + { + lword curMaxSize = UnsignedMin(ComputeCurrentTransceiveLimit(), maxSize - byteCount); + + if (curMaxSize || m_doPumpBlocked) + { + if (!forever) timeToGo = SaturatingSubtract(maxTime, timer.ElapsedTime()); + size_t ret = DoPump(curMaxSize, blockingOutput, timeToGo, checkDelimiter, delimiter); + m_doPumpBlocked = (ret != 0); + if (curMaxSize) + { + NoteTransceive(curMaxSize); + byteCount += curMaxSize; + } + if (ret) + return ret; + } + + if (maxSize != ULONG_MAX && byteCount >= maxSize) + break; + + if (!forever) + { + timeToGo = SaturatingSubtract(maxTime, timer.ElapsedTime()); + if (!timeToGo) + break; + } + + double waitTime = TimeToNextTransceive(); + if (!forever && waitTime > timeToGo) + { + m_blockedBySpeedLimit = true; + break; + } + + WaitObjectContainer container; + LimitedBandwidth::GetWaitObjects(container, CallStack("NonblockingSource::GeneralPump2() - speed limit", 0)); + container.Wait((unsigned long)waitTime); + } + + return 0; +} + size_t NonblockingSource::PumpMessages2(unsigned int &messageCount, bool blocking) { if (messageCount == 0) @@ -30,10 +157,68 @@ size_t NonblockingSource::PumpMessages2(unsigned int &messageCount, bool blockin return 0; } +lword NonblockingSink::TimedFlush(unsigned long maxTime, size_t targetSize) +{ + m_blockedBySpeedLimit = false; + + size_t curBufSize = GetCurrentBufferSize(); + if (curBufSize <= targetSize && (targetSize || !EofPending())) + return 0; + + if (!GetMaxBytesPerSecond()) + return DoFlush(maxTime, targetSize); + + bool forever = (maxTime == INFINITE_TIME); + unsigned long timeToGo = maxTime; + Timer timer(Timer::MILLISECONDS, forever); + lword totalFlushed = 0; + + timer.StartTimer(); + + while (true) + { + size_t flushSize = UnsignedMin(curBufSize - targetSize, ComputeCurrentTransceiveLimit()); + if (flushSize || EofPending()) + { + if (!forever) timeToGo = SaturatingSubtract(maxTime, timer.ElapsedTime()); + size_t ret = (size_t)DoFlush(timeToGo, curBufSize - flushSize); + if (ret) + { + NoteTransceive(ret); + curBufSize -= ret; + totalFlushed += ret; + } + } + + if (curBufSize <= targetSize && (targetSize || !EofPending())) + break; + + if (!forever) + { + timeToGo = SaturatingSubtract(maxTime, timer.ElapsedTime()); + if (!timeToGo) + break; + } + + double waitTime = TimeToNextTransceive(); + if (!forever && waitTime > timeToGo) + { + m_blockedBySpeedLimit = true; + break; + } + + WaitObjectContainer container; + LimitedBandwidth::GetWaitObjects(container, CallStack("NonblockingSink::TimedFlush() - speed limit", 0)); + container.Wait((unsigned long)waitTime); + } + + return totalFlushed; +} + bool NonblockingSink::IsolatedFlush(bool hardFlush, bool blocking) { TimedFlush(blocking ? INFINITE_TIME : 0); - return hardFlush && !!GetCurrentBufferSize(); + return hardFlush && (!!GetCurrentBufferSize() || EofPending()); } // ************************************************************* @@ -47,19 +232,29 @@ NetworkSource::NetworkSource(BufferedTransformation *attachment) { } -void NetworkSource::GetWaitObjects(WaitObjectContainer &container) +unsigned int NetworkSource::GetMaxWaitObjectCount() const +{ + return LimitedBandwidth::GetMaxWaitObjectCount() + + GetReceiver().GetMaxWaitObjectCount() + + AttachedTransformation()->GetMaxWaitObjectCount(); +} + +void NetworkSource::GetWaitObjects(WaitObjectContainer &container, CallStack const& callStack) { - if (!m_outputBlocked) + if (BlockedBySpeedLimit()) + LimitedBandwidth::GetWaitObjects(container, CallStack("NetworkSource::GetWaitObjects() - speed limit", &callStack)); + else if (!m_outputBlocked) { if (m_dataBegin == m_dataEnd) - AccessReceiver().GetWaitObjects(container); + AccessReceiver().GetWaitObjects(container, CallStack("NetworkSource::GetWaitObjects() - no data", &callStack)); else - container.SetNoWait(); + container.SetNoWait(CallStack("NetworkSource::GetWaitObjects() - have data", &callStack)); } - AttachedTransformation()->GetWaitObjects(container); + + AttachedTransformation()->GetWaitObjects(container, CallStack("NetworkSource::GetWaitObjects() - attachment", &callStack)); } -size_t NetworkSource::GeneralPump2(lword &byteCount, bool blockingOutput, unsigned long maxTime, bool checkDelimiter, byte delimiter) +size_t NetworkSource::DoPump(lword &byteCount, bool blockingOutput, unsigned long maxTime, bool checkDelimiter, byte delimiter) { NetworkReceiver &receiver = AccessReceiver(); @@ -81,7 +276,9 @@ size_t NetworkSource::GeneralPump2(lword &byteCount, bool blockingOutput, unsign if (m_waitingForResult) { - if (receiver.MustWaitForResult() && !receiver.Wait(SaturatingSubtract(maxTime, timer.ElapsedTime()))) + if (receiver.MustWaitForResult() && + !receiver.Wait(SaturatingSubtract(maxTime, timer.ElapsedTime()), + CallStack("NetworkSource::DoPump() - wait receive result", 0))) break; unsigned int recvResult = receiver.GetReceiveResult(); @@ -100,7 +297,8 @@ size_t NetworkSource::GeneralPump2(lword &byteCount, bool blockingOutput, unsign if (receiver.MustWaitToReceive()) { - if (!receiver.Wait(SaturatingSubtract(maxTime, timer.ElapsedTime()))) + if (!receiver.Wait(SaturatingSubtract(maxTime, timer.ElapsedTime()), + CallStack("NetworkSource::DoPump() - wait receive", 0))) break; receiver.Receive(m_buf+m_dataEnd, m_buf.size()-m_dataEnd); @@ -133,7 +331,8 @@ ReceiveNoWait: } else { - m_putSize = (size_t)STDMIN((lword)m_dataEnd-m_dataBegin, maxSize-byteCount); + m_putSize = UnsignedMin(m_dataEnd - m_dataBegin, maxSize - byteCount); + if (checkDelimiter) m_putSize = std::find(m_buf+m_dataBegin, m_buf+m_dataBegin+m_putSize, delimiter) - (m_buf+m_dataBegin); @@ -141,7 +340,8 @@ DoOutput: size_t result = t->PutModifiable2(m_buf+m_dataBegin, m_putSize, 0, forever || blockingOutput); if (result) { - if (t->Wait(SaturatingSubtract(maxTime, timer.ElapsedTime()))) + if (t->Wait(SaturatingSubtract(maxTime, timer.ElapsedTime()), + CallStack("NetworkSource::DoPump() - wait attachment", 0))) goto DoOutput; else { @@ -155,7 +355,7 @@ DoOutput: m_dataBegin += m_putSize; if (checkDelimiter && m_dataBegin < m_dataEnd && m_buf[m_dataBegin] == delimiter) break; - if (byteCount == maxSize) + if (maxSize != ULONG_MAX && byteCount == maxSize) break; // once time limit is reached, return even if there is more data waiting // but make 0 a special case so caller can request a large amount of data to be @@ -172,7 +372,7 @@ DoOutput: NetworkSink::NetworkSink(unsigned int maxBufferSize, unsigned int autoFlushBound) : m_maxBufferSize(maxBufferSize), m_autoFlushBound(autoFlushBound) - , m_needSendResult(false), m_wasBlocked(false) + , m_needSendResult(false), m_wasBlocked(false), m_eofState(EOF_NONE) , m_buffer(STDMIN(16U*1024U+256, maxBufferSize)), m_skipBytes(0) , m_speedTimer(Timer::MILLISECONDS), m_byteCountSinceLastTimerReset(0) , m_currentSpeed(0), m_maxObservedSpeed(0) @@ -192,44 +392,86 @@ float NetworkSink::ComputeCurrentSpeed() return m_currentSpeed; } +float NetworkSink::GetMaxObservedSpeed() const +{ + lword m = GetMaxBytesPerSecond(); + return m ? STDMIN(m_maxObservedSpeed, float(m)) : m_maxObservedSpeed; +} + +unsigned int NetworkSink::GetMaxWaitObjectCount() const +{ + return LimitedBandwidth::GetMaxWaitObjectCount() + GetSender().GetMaxWaitObjectCount(); +} + +void NetworkSink::GetWaitObjects(WaitObjectContainer &container, CallStack const& callStack) +{ + if (BlockedBySpeedLimit()) + LimitedBandwidth::GetWaitObjects(container, CallStack("NetworkSink::GetWaitObjects() - speed limit", &callStack)); + else if (m_wasBlocked) + AccessSender().GetWaitObjects(container, CallStack("NetworkSink::GetWaitObjects() - was blocked", &callStack)); + else if (!m_buffer.IsEmpty()) + AccessSender().GetWaitObjects(container, CallStack("NetworkSink::GetWaitObjects() - buffer not empty", &callStack)); + else if (EofPending()) + AccessSender().GetWaitObjects(container, CallStack("NetworkSink::GetWaitObjects() - EOF pending", &callStack)); +} + size_t NetworkSink::Put2(const byte *inString, size_t length, int messageEnd, bool blocking) { - if (m_skipBytes) + if (m_eofState == EOF_DONE) { - assert(length >= m_skipBytes); - inString += m_skipBytes; - length -= m_skipBytes; - } - m_buffer.LazyPut(inString, length); + if (length || messageEnd) + throw Exception(Exception::OTHER_ERROR, "NetworkSink::Put2() being called after EOF had been sent"); - if (!blocking || m_buffer.CurrentSize() > m_autoFlushBound) - TimedFlush(0, 0); + return 0; + } - size_t targetSize = messageEnd ? 0 : m_maxBufferSize; - if (blocking) - TimedFlush(INFINITE_TIME, targetSize); + if (m_eofState > EOF_NONE) + goto EofSite; - if (m_buffer.CurrentSize() > targetSize) { - assert(!blocking); - size_t blockedBytes = (size_t)STDMIN(m_buffer.CurrentSize() - targetSize, (lword)length); - m_buffer.UndoLazyPut(blockedBytes); - m_buffer.FinalizeLazyPut(); - m_wasBlocked = true; - m_skipBytes += length - blockedBytes; - return UnsignedMin(1, blockedBytes); - } + if (m_skipBytes) + { + assert(length >= m_skipBytes); + inString += m_skipBytes; + length -= m_skipBytes; + } + + m_buffer.Put(inString, length); + + if (!blocking || m_buffer.CurrentSize() > m_autoFlushBound) + TimedFlush(0, 0); + + size_t targetSize = messageEnd ? 0 : m_maxBufferSize; + if (blocking) + TimedFlush(INFINITE_TIME, targetSize); + + if (m_buffer.CurrentSize() > targetSize) + { + assert(!blocking); + m_wasBlocked = true; + m_skipBytes += length; + size_t blockedBytes = UnsignedMin(length, m_buffer.CurrentSize() - targetSize); + return STDMAX<size_t>(blockedBytes, 1); + } - m_buffer.FinalizeLazyPut(); - m_wasBlocked = false; - m_skipBytes = 0; + m_wasBlocked = false; + m_skipBytes = 0; + } if (messageEnd) - AccessSender().SendEof(); + { + m_eofState = EOF_PENDING_SEND; + + EofSite: + TimedFlush(blocking ? INFINITE_TIME : 0, 0); + if (m_eofState != EOF_DONE) + return 1; + } + return 0; } -lword NetworkSink::TimedFlush(unsigned long maxTime, size_t targetSize) +lword NetworkSink::DoFlush(unsigned long maxTime, size_t targetSize) { NetworkSender &sender = AccessSender(); @@ -244,7 +486,9 @@ lword NetworkSink::TimedFlush(unsigned long maxTime, size_t targetSize) if (m_needSendResult) { - if (sender.MustWaitForResult() && !sender.Wait(SaturatingSubtract(maxTime, timer.ElapsedTime()))) + if (sender.MustWaitForResult() && + !sender.Wait(SaturatingSubtract(maxTime, timer.ElapsedTime()), + CallStack("NetworkSink::DoFlush() - wait send result", 0))) break; unsigned int sendResult = sender.GetSendResult(); @@ -260,7 +504,7 @@ lword NetworkSink::TimedFlush(unsigned long maxTime, size_t targetSize) } unsigned long timeOut = maxTime ? SaturatingSubtract(maxTime, timer.ElapsedTime()) : 0; - if (sender.MustWaitToSend() && !sender.Wait(timeOut)) + if (sender.MustWaitToSend() && !sender.Wait(timeOut, CallStack("NetworkSink::DoFlush() - wait send", 0))) break; size_t contiguousSize = 0; @@ -279,6 +523,25 @@ lword NetworkSink::TimedFlush(unsigned long maxTime, size_t targetSize) m_byteCountSinceLastTimerReset += totalFlushSize; ComputeCurrentSpeed(); + if (m_buffer.IsEmpty() && !m_needSendResult) + { + if (m_eofState == EOF_PENDING_SEND) + { + sender.SendEof(); + m_eofState = sender.MustWaitForEof() ? EOF_PENDING_DELIVERY : EOF_DONE; + } + + while (m_eofState == EOF_PENDING_DELIVERY) + { + unsigned long timeOut = maxTime ? SaturatingSubtract(maxTime, timer.ElapsedTime()) : 0; + if (!sender.Wait(timeOut, CallStack("NetworkSink::DoFlush() - wait EOF", 0))) + break; + + if (sender.EofSent()) + m_eofState = EOF_DONE; + } + } + return totalFlushSize; } @@ -4,21 +4,62 @@ #include "filters.h" #include "hrtimer.h" +#include <deque> + NAMESPACE_BEGIN(CryptoPP) +class LimitedBandwidth +{ +public: + LimitedBandwidth(lword maxBytesPerSecond = 0) + : m_maxBytesPerSecond(maxBytesPerSecond), m_timer(Timer::MILLISECONDS) + , m_nextTransceiveTime(0) + { m_timer.StartTimer(); } + + lword GetMaxBytesPerSecond() const + { return m_maxBytesPerSecond; } + + void SetMaxBytesPerSecond(lword v) + { m_maxBytesPerSecond = v; } + + lword ComputeCurrentTransceiveLimit(); + + double TimeToNextTransceive(); + + void NoteTransceive(lword size); + +public: + /*! GetWaitObjects() must be called despite the 0 return from GetMaxWaitObjectCount(); + the 0 is because the ScheduleEvent() method is used instead of adding a wait object */ + unsigned int GetMaxWaitObjectCount() const { return 0; } + void GetWaitObjects(WaitObjectContainer &container, CallStack const& callStack); + +private: + lword m_maxBytesPerSecond; + + typedef std::deque<std::pair<double, lword> > OpQueue; + OpQueue m_ops; + + Timer m_timer; + double m_nextTransceiveTime; + + void ComputeNextTransceiveTime(); + double GetCurTimeAndCleanUp(); +}; + //! a Source class that can pump from a device for a specified amount of time. -class CRYPTOPP_NO_VTABLE NonblockingSource : public AutoSignaling<Source> +class CRYPTOPP_NO_VTABLE NonblockingSource : public AutoSignaling<Source>, public LimitedBandwidth { public: NonblockingSource(BufferedTransformation *attachment) - : m_messageEndSent(false) {Detach(attachment);} + : m_messageEndSent(false) , m_doPumpBlocked(false), m_blockedBySpeedLimit(false) {Detach(attachment);} //! \name NONBLOCKING SOURCE //@{ //! pump up to maxSize bytes using at most maxTime milliseconds /*! If checkDelimiter is true, pump up to delimiter, which itself is not extracted or pumped. */ - virtual size_t GeneralPump2(lword &byteCount, bool blockingOutput=true, unsigned long maxTime=INFINITE_TIME, bool checkDelimiter=false, byte delimiter='\n') =0; + size_t GeneralPump2(lword &byteCount, bool blockingOutput=true, unsigned long maxTime=INFINITE_TIME, bool checkDelimiter=false, byte delimiter='\n'); lword GeneralPump(lword maxSize=LWORD_MAX, unsigned long maxTime=INFINITE_TIME, bool checkDelimiter=false, byte delimiter='\n') { @@ -35,8 +76,14 @@ public: size_t PumpMessages2(unsigned int &messageCount, bool blocking=true); //@} +protected: + virtual size_t DoPump(lword &byteCount, bool blockingOutput, + unsigned long maxTime, bool checkDelimiter, byte delimiter) =0; + + bool BlockedBySpeedLimit() const { return m_blockedBySpeedLimit; } + private: - bool m_messageEndSent; + bool m_messageEndSent, m_doPumpBlocked, m_blockedBySpeedLimit; }; //! Network Receiver @@ -57,6 +104,7 @@ public: virtual ~NonblockingSinkInfo() {} virtual size_t GetMaxBufferSize() const =0; virtual size_t GetCurrentBufferSize() const =0; + virtual bool EofPending() const =0; //! compute the current speed of this sink in bytes per second virtual float ComputeCurrentSpeed() =0; //! get the maximum observed speed of this sink in bytes per second @@ -64,9 +112,11 @@ public: }; //! a Sink class that queues input and can flush to a device for a specified amount of time. -class CRYPTOPP_NO_VTABLE NonblockingSink : public Sink, public NonblockingSinkInfo +class CRYPTOPP_NO_VTABLE NonblockingSink : public Sink, public NonblockingSinkInfo, public LimitedBandwidth { public: + NonblockingSink() : m_blockedBySpeedLimit(false) {} + bool IsolatedFlush(bool hardFlush, bool blocking); //! flush to device for no more than maxTime milliseconds @@ -79,11 +129,19 @@ public: For example: while (sink.TimedFlush(0) > 0) {} \return number of bytes flushed */ - virtual lword TimedFlush(unsigned long maxTime, size_t targetSize=0) =0; + lword TimedFlush(unsigned long maxTime, size_t targetSize = 0); virtual void SetMaxBufferSize(size_t maxBufferSize) =0; //! set a bound which will cause sink to flush if exceeded by GetCurrentBufferSize() virtual void SetAutoFlushBound(size_t bound) =0; + +protected: + virtual lword DoFlush(unsigned long maxTime, size_t targetSize) = 0; + + bool BlockedBySpeedLimit() const { return m_blockedBySpeedLimit; } + +private: + bool m_blockedBySpeedLimit; }; //! Network Sender @@ -94,7 +152,9 @@ public: virtual bool MustWaitForResult() {return false;} virtual void Send(const byte* buf, size_t bufLen) =0; virtual unsigned int GetSendResult() =0; + virtual bool MustWaitForEof() {return false;} virtual void SendEof() =0; + virtual bool EofSent() {return false;} // implement if MustWaitForEof() == true }; #ifdef HIGHRES_TIMER_AVAILABLE @@ -105,14 +165,14 @@ class CRYPTOPP_NO_VTABLE NetworkSource : public NonblockingSource public: NetworkSource(BufferedTransformation *attachment); - unsigned int GetMaxWaitObjectCount() const - {return GetReceiver().GetMaxWaitObjectCount() + AttachedTransformation()->GetMaxWaitObjectCount();} - void GetWaitObjects(WaitObjectContainer &container); + unsigned int GetMaxWaitObjectCount() const; + void GetWaitObjects(WaitObjectContainer &container, CallStack const& callStack); - size_t GeneralPump2(lword &byteCount, bool blockingOutput=true, unsigned long maxTime=INFINITE_TIME, bool checkDelimiter=false, byte delimiter='\n'); bool SourceExhausted() const {return m_dataBegin == m_dataEnd && GetReceiver().EofReceived();} protected: + size_t DoPump(lword &byteCount, bool blockingOutput, unsigned long maxTime, bool checkDelimiter, byte delimiter); + virtual NetworkReceiver & AccessReceiver() =0; const NetworkReceiver & GetReceiver() const {return const_cast<NetworkSource *>(this)->AccessReceiver();} @@ -128,35 +188,38 @@ class CRYPTOPP_NO_VTABLE NetworkSink : public NonblockingSink public: NetworkSink(unsigned int maxBufferSize, unsigned int autoFlushBound); - unsigned int GetMaxWaitObjectCount() const - {return GetSender().GetMaxWaitObjectCount();} - void GetWaitObjects(WaitObjectContainer &container) - {if (m_wasBlocked || !m_buffer.IsEmpty()) AccessSender().GetWaitObjects(container);} + unsigned int GetMaxWaitObjectCount() const; + void GetWaitObjects(WaitObjectContainer &container, CallStack const& callStack); size_t Put2(const byte *inString, size_t length, int messageEnd, bool blocking); - lword TimedFlush(unsigned long maxTime, size_t targetSize = 0); - void SetMaxBufferSize(size_t maxBufferSize) {m_maxBufferSize = maxBufferSize; m_buffer.SetNodeSize(UnsignedMin(maxBufferSize, 16U*1024U+256U));} void SetAutoFlushBound(size_t bound) {m_autoFlushBound = bound;} size_t GetMaxBufferSize() const {return m_maxBufferSize;} size_t GetCurrentBufferSize() const {return (size_t)m_buffer.CurrentSize();} - void ClearBuffer() {m_buffer.Clear();} + void ClearBuffer() { m_buffer.Clear(); } + + bool EofPending() const { return m_eofState > EOF_NONE && m_eofState < EOF_DONE; } //! compute the current speed of this sink in bytes per second float ComputeCurrentSpeed(); //! get the maximum observed speed of this sink in bytes per second - float GetMaxObservedSpeed() const {return m_maxObservedSpeed;} + float GetMaxObservedSpeed() const; protected: + lword DoFlush(unsigned long maxTime, size_t targetSize); + virtual NetworkSender & AccessSender() =0; const NetworkSender & GetSender() const {return const_cast<NetworkSink *>(this)->AccessSender();} private: + enum EofState { EOF_NONE, EOF_PENDING_SEND, EOF_PENDING_DELIVERY, EOF_DONE }; + size_t m_maxBufferSize, m_autoFlushBound; bool m_needSendResult, m_wasBlocked; + EofState m_eofState; ByteQueue m_buffer; size_t m_skipBytes; Timer m_speedTimer; diff --git a/socketft.cpp b/socketft.cpp index 6071f24..5790502 100644 --- a/socketft.cpp +++ b/socketft.cpp @@ -79,6 +79,7 @@ void Socket::CloseSocket() if (m_s != INVALID_SOCKET) { #ifdef USE_WINDOWS_STYLE_SOCKETS + CancelIo((HANDLE) m_s); CheckAndHandleError_int("closesocket", closesocket(m_s)); #else CheckAndHandleError_int("close", close(m_s)); @@ -178,6 +179,12 @@ void Socket::GetSockName(sockaddr *psa, socklen_t *psaLen) CheckAndHandleError_int("getsockname", getsockname(m_s, psa, psaLen)); } +void Socket::GetPeerName(sockaddr *psa, socklen_t *psaLen) +{ + assert(m_s != INVALID_SOCKET); + CheckAndHandleError_int("getpeername", getpeername(m_s, psa, psaLen)); +} + unsigned int Socket::Send(const byte* buf, size_t bufLen, int flags) { assert(m_s != INVALID_SOCKET); @@ -261,7 +268,7 @@ void Socket::StartSockets() { #ifdef USE_WINDOWS_STYLE_SOCKETS WSADATA wsd; - int result = WSAStartup(0x0002, &wsd); + int result = WSAStartup(0x0202, &wsd); if (result != 0) throw Err(INVALID_SOCKET, "WSAStartup", result); #endif @@ -311,13 +318,20 @@ SocketReceiver::SocketReceiver(Socket &s) m_overlapped.hEvent = m_event; } +SocketReceiver::~SocketReceiver() +{ +#ifdef USE_WINDOWS_STYLE_SOCKETS + CancelIo((HANDLE) m_s.GetSocket()); +#endif +} + bool SocketReceiver::Receive(byte* buf, size_t bufLen) { assert(!m_resultPending && !m_eofReceived); DWORD flags = 0; // don't queue too much at once, or we might use up non-paged memory - WSABUF wsabuf = {UnsignedMin(128U*1024U, bufLen), (char *)buf}; + WSABUF wsabuf = {UnsignedMin((u_long)128*1024, bufLen), (char *)buf}; if (WSARecv(m_s, &wsabuf, 1, &m_lastResult, &flags, &m_overlapped, NULL) == 0) { if (m_lastResult == 0) @@ -340,12 +354,12 @@ bool SocketReceiver::Receive(byte* buf, size_t bufLen) return !m_resultPending; } -void SocketReceiver::GetWaitObjects(WaitObjectContainer &container) +void SocketReceiver::GetWaitObjects(WaitObjectContainer &container, CallStack const& callStack) { if (m_resultPending) - container.AddHandle(m_event); + container.AddHandle(m_event, CallStack("SocketReceiver::GetWaitObjects() - result pending", &callStack)); else if (!m_eofReceived) - container.SetNoWait(); + container.SetNoWait(CallStack("SocketReceiver::GetWaitObjects() - result ready", &callStack)); } unsigned int SocketReceiver::GetReceiveResult() @@ -385,11 +399,20 @@ SocketSender::SocketSender(Socket &s) m_overlapped.hEvent = m_event; } + +SocketSender::~SocketSender() +{ +#ifdef USE_WINDOWS_STYLE_SOCKETS + CancelIo((HANDLE) m_s.GetSocket()); +#endif +} + void SocketSender::Send(const byte* buf, size_t bufLen) { + assert(!m_resultPending); DWORD written = 0; // don't queue too much at once, or we might use up non-paged memory - WSABUF wsabuf = {UnsignedMin(128U*1024U, bufLen), (char *)buf}; + WSABUF wsabuf = {UnsignedMin((u_long)128*1024, bufLen), (char *)buf}; if (WSASend(m_s, &wsabuf, 1, &written, 0, &m_overlapped, NULL) == 0) { m_resultPending = false; @@ -404,12 +427,33 @@ void SocketSender::Send(const byte* buf, size_t bufLen) } } -void SocketSender::GetWaitObjects(WaitObjectContainer &container) +void SocketSender::SendEof() +{ + assert(!m_resultPending); + m_s.ShutDown(SD_SEND); + m_s.CheckAndHandleError("ResetEvent", ResetEvent(m_event)); + m_s.CheckAndHandleError_int("WSAEventSelect", WSAEventSelect(m_s, m_event, FD_CLOSE)); + m_resultPending = true; +} + +bool SocketSender::EofSent() +{ + if (m_resultPending) + { + WSANETWORKEVENTS events; + m_s.CheckAndHandleError_int("WSAEnumNetworkEvents", WSAEnumNetworkEvents(m_s, m_event, &events)); + m_lastResult = (events.lNetworkEvents & FD_CLOSE) ? 1 : 0; + m_resultPending = false; + } + return m_lastResult != 0; +} + +void SocketSender::GetWaitObjects(WaitObjectContainer &container, CallStack const& callStack) { if (m_resultPending) - container.AddHandle(m_event); + container.AddHandle(m_event, CallStack("SocketSender::GetWaitObjects() - result pending", &callStack)); else - container.SetNoWait(); + container.SetNoWait(CallStack("SocketSender::GetWaitObjects() - result ready", &callStack)); } unsigned int SocketSender::GetSendResult() @@ -433,10 +477,10 @@ SocketReceiver::SocketReceiver(Socket &s) { } -void SocketReceiver::GetWaitObjects(WaitObjectContainer &container) +void SocketReceiver::GetWaitObjects(WaitObjectContainer &container, CallStack const& callStack) { if (!m_eofReceived) - container.AddReadFd(m_s); + container.AddReadFd(m_s, CallStack("SocketReceiver::GetWaitObjects()", &callStack)); } bool SocketReceiver::Receive(byte* buf, size_t bufLen) @@ -462,14 +506,19 @@ void SocketSender::Send(const byte* buf, size_t bufLen) m_lastResult = m_s.Send(buf, bufLen); } +void SocketSender::SendEof() +{ + m_s.ShutDown(SD_SEND); +} + unsigned int SocketSender::GetSendResult() { return m_lastResult; } -void SocketSender::GetWaitObjects(WaitObjectContainer &container) +void SocketSender::GetWaitObjects(WaitObjectContainer &container, CallStack const& callStack) { - container.AddWriteFd(m_s); + container.AddWriteFd(m_s, CallStack("SocketSender::GetWaitObjects()", &callStack)); } #endif @@ -77,6 +77,7 @@ public: bool Connect(const sockaddr* psa, socklen_t saLen); bool Accept(Socket& s, sockaddr *psa=NULL, socklen_t *psaLen=NULL); void GetSockName(sockaddr *psa, socklen_t *psaLen); + void GetPeerName(sockaddr *psa, socklen_t *psaLen); unsigned int Send(const byte* buf, size_t bufLen, int flags=0); unsigned int Receive(byte* buf, size_t bufLen, int flags=0); void ShutDown(int how = SD_SEND); @@ -128,6 +129,7 @@ public: #ifdef USE_BERKELEY_STYLE_SOCKETS bool MustWaitToReceive() {return true;} #else + ~SocketReceiver(); bool MustWaitForResult() {return true;} #endif bool Receive(byte* buf, size_t bufLen); @@ -135,7 +137,7 @@ public: bool EofReceived() const {return m_eofReceived;} unsigned int GetMaxWaitObjectCount() const {return 1;} - void GetWaitObjects(WaitObjectContainer &container); + void GetWaitObjects(WaitObjectContainer &container, CallStack const& callStack); private: Socket &m_s; @@ -159,14 +161,17 @@ public: #ifdef USE_BERKELEY_STYLE_SOCKETS bool MustWaitToSend() {return true;} #else + ~SocketSender(); bool MustWaitForResult() {return true;} + bool MustWaitForEof() { return true; } + bool EofSent(); #endif void Send(const byte* buf, size_t bufLen); unsigned int GetSendResult(); - void SendEof() {m_s.ShutDown(SD_SEND);} + void SendEof(); unsigned int GetMaxWaitObjectCount() const {return 1;} - void GetWaitObjects(WaitObjectContainer &container); + void GetWaitObjects(WaitObjectContainer &container, CallStack const& callStack); private: Socket &m_s; @@ -192,7 +192,7 @@ int __cdecl main(int argc, char *argv[]) return 1; } - unsigned int macPos = unsigned int(found-buf.begin()); + unsigned int macPos = (unsigned int)(found-buf.begin()); member_ptr<MessageAuthenticationCode> pMac(NewIntegrityCheckingMAC()); pMac->Update(buf.begin(), macPos); pMac->Update(buf.begin() + macPos + sizeof(dummyMac), fileSize - sizeof(dummyMac) - macPos); @@ -683,8 +683,8 @@ void ForwardTcpPort(const char *sourcePortName, const char *destinationHost, con { waitObjects.Clear(); - out.GetWaitObjects(waitObjects); - in.GetWaitObjects(waitObjects); + out.GetWaitObjects(waitObjects, CallStack("ForwardTcpPort - out", NULL)); + in.GetWaitObjects(waitObjects, CallStack("ForwardTcpPort - in", NULL)); waitObjects.Wait(INFINITE_TIME); @@ -13,12 +13,6 @@ #include <unistd.h> #endif -#define TRACE_WAIT 0 - -#if TRACE_WAIT -#include "hrtimer.h" -#endif - NAMESPACE_BEGIN(CryptoPP) unsigned int WaitObjectContainer::MaxWaitObjects() @@ -30,12 +24,12 @@ unsigned int WaitObjectContainer::MaxWaitObjects() #endif } -WaitObjectContainer::WaitObjectContainer() -#if CRYPTOPP_DETECT_NO_WAIT - : m_sameResultCount(0), m_timer(Timer::MILLISECONDS) -#endif +WaitObjectContainer::WaitObjectContainer(WaitObjectsTracer* tracer) + : m_tracer(tracer), m_eventTimer(Timer::MILLISECONDS) + , m_sameResultCount(0), m_noWaitTimer(Timer::MILLISECONDS) { Clear(); + m_eventTimer.StartTimer(); } void WaitObjectContainer::Clear() @@ -48,21 +42,57 @@ void WaitObjectContainer::Clear() FD_ZERO(&m_writefds); #endif m_noWait = false; + m_firstEventTime = 0; } -void WaitObjectContainer::SetNoWait() +inline void WaitObjectContainer::SetLastResult(LastResultType result) { -#if CRYPTOPP_DETECT_NO_WAIT - if (-1 == m_lastResult && m_timer.ElapsedTime() > 1000) + if (result == m_lastResult) + m_sameResultCount++; + else { - if (m_sameResultCount > m_timer.ElapsedTime()) - try {throw 0;} catch (...) {} // possible no-wait loop, break in debugger - m_timer.StartTimer(); + m_lastResult = result; + m_sameResultCount = 0; } -#endif +} + +void WaitObjectContainer::DetectNoWait(LastResultType result, CallStack const& callStack) +{ + if (result == m_lastResult && m_noWaitTimer.ElapsedTime() > 1000) + { + if (m_sameResultCount > m_noWaitTimer.ElapsedTime()) + { + if (m_tracer) + { + std::string desc = "No wait loop detected - m_lastResult: "; + desc.append(IntToString(m_lastResult)).append(", call stack:"); + for (CallStack const* cs = &callStack; cs; cs = cs->Prev()) + desc.append("\n- ").append(cs->Format()); + m_tracer->TraceNoWaitLoop(desc); + } + try { throw 0; } catch (...) {} // help debugger break + } + + m_noWaitTimer.StartTimer(); + m_sameResultCount = 0; + } +} + +void WaitObjectContainer::SetNoWait(CallStack const& callStack) +{ + DetectNoWait(LASTRESULT_NOWAIT, CallStack("WaitObjectContainer::SetNoWait()", &callStack)); m_noWait = true; } +void WaitObjectContainer::ScheduleEvent(double milliseconds, CallStack const& callStack) +{ + if (milliseconds <= 3) + DetectNoWait(LASTRESULT_SCHEDULED, CallStack("WaitObjectContainer::ScheduleEvent()", &callStack)); + double thisEventTime = m_eventTimer.ElapsedTimeAsDouble() + milliseconds; + if (!m_firstEventTime || thisEventTime < m_firstEventTime) + m_firstEventTime = thisEventTime; +} + #ifdef USE_WINDOWS_STYLE_SOCKETS struct WaitingThreadData @@ -106,16 +136,9 @@ WaitObjectContainer::~WaitObjectContainer() } -void WaitObjectContainer::AddHandle(HANDLE handle) +void WaitObjectContainer::AddHandle(HANDLE handle, CallStack const& callStack) { -#if CRYPTOPP_DETECT_NO_WAIT - if (m_handles.size() == m_lastResult && m_timer.ElapsedTime() > 1000) - { - if (m_sameResultCount > m_timer.ElapsedTime()) - try {throw 0;} catch (...) {} // possible no-wait loop, break in debugger - m_timer.StartTimer(); - } -#endif + DetectNoWait(m_handles.size(), CallStack("WaitObjectContainer::AddHandle()", &callStack)); m_handles.push_back(handle); } @@ -157,7 +180,7 @@ DWORD WINAPI WaitingThread(LPVOID lParam) void WaitObjectContainer::CreateThreads(unsigned int count) { - unsigned int currentCount = (unsigned int)m_threads.size(); + size_t currentCount = m_threads.size(); if (currentCount == 0) { m_startWaiting = ::CreateEvent(NULL, TRUE, FALSE, NULL); @@ -167,7 +190,7 @@ void WaitObjectContainer::CreateThreads(unsigned int count) if (currentCount < count) { m_threads.resize(count); - for (unsigned int i=currentCount; i<count; i++) + for (size_t i=currentCount; i<count; i++) { m_threads[i] = new WaitingThreadData; WaitingThreadData &thread = *m_threads[i]; @@ -182,18 +205,31 @@ void WaitObjectContainer::CreateThreads(unsigned int count) bool WaitObjectContainer::Wait(unsigned long milliseconds) { - if (m_noWait || m_handles.empty()) + if (m_noWait || (m_handles.empty() && !m_firstEventTime)) { -#if CRYPTOPP_DETECT_NO_WAIT - if (-1 == m_lastResult) - m_sameResultCount++; - else + SetLastResult(LASTRESULT_NOWAIT); + return true; + } + + bool timeoutIsScheduledEvent = false; + + if (m_firstEventTime) + { + double timeToFirstEvent = SaturatingSubtract(m_firstEventTime, m_eventTimer.ElapsedTimeAsDouble()); + + if (timeToFirstEvent <= milliseconds) { - m_lastResult = -1; - m_sameResultCount = 0; + milliseconds = (unsigned long)timeToFirstEvent; + timeoutIsScheduledEvent = true; + } + + if (m_handles.empty() || !milliseconds) + { + if (milliseconds) + Sleep(milliseconds); + SetLastResult(timeoutIsScheduledEvent ? LASTRESULT_SCHEDULED : LASTRESULT_TIMEOUT); + return timeoutIsScheduledEvent; } -#endif - return true; } if (m_handles.size() > MAXIMUM_WAIT_OBJECTS) @@ -230,11 +266,14 @@ bool WaitObjectContainer::Wait(unsigned long milliseconds) if (error == S_OK) return true; else - throw Err("WaitObjectContainer: WaitForMultipleObjects failed with error " + IntToString(error)); + throw Err("WaitObjectContainer: WaitForMultipleObjects in thread failed with error " + IntToString(error)); } SetEvent(m_stopWaiting); if (result == WAIT_TIMEOUT) - return false; + { + SetLastResult(timeoutIsScheduledEvent ? LASTRESULT_SCHEDULED : LASTRESULT_TIMEOUT); + return timeoutIsScheduledEvent; + } else throw Err("WaitObjectContainer: WaitForSingleObject failed with error " + IntToString(::GetLastError())); } @@ -256,7 +295,6 @@ bool WaitObjectContainer::Wait(unsigned long milliseconds) #endif if (result >= WAIT_OBJECT_0 && result < WAIT_OBJECT_0 + m_handles.size()) { -#if CRYPTOPP_DETECT_NO_WAIT if (result == m_lastResult) m_sameResultCount++; else @@ -264,25 +302,27 @@ bool WaitObjectContainer::Wait(unsigned long milliseconds) m_lastResult = result; m_sameResultCount = 0; } -#endif return true; } else if (result == WAIT_TIMEOUT) - return false; + { + SetLastResult(timeoutIsScheduledEvent ? LASTRESULT_SCHEDULED : LASTRESULT_TIMEOUT); + return timeoutIsScheduledEvent; + } else throw Err("WaitObjectContainer: WaitForMultipleObjects failed with error " + IntToString(::GetLastError())); } } -#else +#else // #ifdef USE_WINDOWS_STYLE_SOCKETS -void WaitObjectContainer::AddReadFd(int fd) +void WaitObjectContainer::AddReadFd(int fd, CallStack const& callStack) // TODO: do something with callStack { FD_SET(fd, &m_readfds); m_maxFd = STDMAX(m_maxFd, fd); } -void WaitObjectContainer::AddWriteFd(int fd) +void WaitObjectContainer::AddWriteFd(int fd, CallStack const& callStack) // TODO: do something with callStack { FD_SET(fd, &m_writefds); m_maxFd = STDMAX(m_maxFd, fd); @@ -290,9 +330,21 @@ void WaitObjectContainer::AddWriteFd(int fd) bool WaitObjectContainer::Wait(unsigned long milliseconds) { - if (m_noWait || m_maxFd == 0) + if (m_noWait || (!m_maxFd && !m_firstEventTime)) return true; + bool timeoutIsScheduledEvent = false; + + if (m_firstEventTime) + { + double timeToFirstEvent = SaturatingSubtract(m_firstEventTime, m_eventTimer.ElapsedTimeAsDouble()); + if (timeToFirstEvent <= milliseconds) + { + milliseconds = (unsigned long)timeToFirstEvent; + timeoutIsScheduledEvent = true; + } + } + timeval tv, *timeout; if (milliseconds == INFINITE_TIME) @@ -309,7 +361,7 @@ bool WaitObjectContainer::Wait(unsigned long milliseconds) if (result > 0) return true; else if (result == 0) - return false; + return timeoutIsScheduledEvent; else throw Err("WaitObjectContainer: select failed with error " + errno); } @@ -318,10 +370,25 @@ bool WaitObjectContainer::Wait(unsigned long milliseconds) // ******************************************************** -bool Waitable::Wait(unsigned long milliseconds) +std::string CallStack::Format() const +{ + return m_info; +} + +std::string CallStackWithNr::Format() const +{ + return std::string(m_info) + " / nr: " + IntToString(m_nr); +} + +std::string CallStackWithStr::Format() const +{ + return std::string(m_info) + " / " + std::string(m_z); +} + +bool Waitable::Wait(unsigned long milliseconds, CallStack const& callStack) { WaitObjectContainer container; - GetWaitObjects(container); + GetWaitObjects(container, callStack); // reduce clutter by not adding this func to stack return container.Wait(milliseconds); } @@ -5,6 +5,7 @@ #ifdef SOCKETS_AVAILABLE +#include "misc.h" #include "cryptlib.h" #include <vector> @@ -14,22 +15,135 @@ #include <sys/types.h> #endif -#ifdef NDEBUG -#define CRYPTOPP_DETECT_NO_WAIT 0 -#else -#define CRYPTOPP_DETECT_NO_WAIT 1 -#endif - -#if CRYPTOPP_DETECT_NO_WAIT #include "hrtimer.h" -#endif NAMESPACE_BEGIN(CryptoPP) +class Tracer +{ +public: + Tracer(unsigned int level) : m_level(level) {} + virtual ~Tracer() {} + +protected: + //! Override this in your most-derived tracer to do the actual tracing. + virtual void Trace(unsigned int n, std::string const& s) = 0; + + /*! By default, tracers will decide which trace messages to trace according to a trace level + mechanism. If your most-derived tracer uses a different mechanism, override this to + return false. If this method returns false, the default TraceXxxx(void) methods will all + return 0 and must be overridden explicitly by your tracer for trace messages you want. */ + virtual bool UsingDefaults() const { return true; } + +protected: + unsigned int m_level; + + void TraceIf(unsigned int n, std::string const&s) + { if (n) Trace(n, s); } + + /*! Returns nr if, according to the default log settings mechanism (using log levels), + the message should be traced. Returns 0 if the default trace level mechanism is not + in use, or if it is in use but the event should not be traced. Provided as a utility + method for easier and shorter coding of default TraceXxxx(void) implementations. */ + unsigned int Tracing(unsigned int nr, unsigned int minLevel) const + { return (UsingDefaults() && m_level >= minLevel) ? nr : 0; } +}; + +// Your Tracer-derived class should inherit as virtual public from Tracer or another +// Tracer-derived class, and should pass the log level in its constructor. You can use the +// following methods to begin and end your Tracer definition. + +// This constructor macro initializes Tracer directly even if not derived directly from it; +// this is intended, virtual base classes are always initialized by the most derived class. +#define CRYPTOPP_TRACER_CONSTRUCTOR(DERIVED) \ + public: DERIVED(unsigned int level = 0) : Tracer(level) {} + +#define CRYPTOPP_BEGIN_TRACER_CLASS_1(DERIVED, BASE1) \ + class DERIVED : virtual public BASE1 { CRYPTOPP_TRACER_CONSTRUCTOR(DERIVED) + +#define CRYPTOPP_BEGIN_TRACER_CLASS_2(DERIVED, BASE1, BASE2) \ + class DERIVED : virtual public BASE1, virtual public BASE2 { CRYPTOPP_TRACER_CONSTRUCTOR(DERIVED) + +#define CRYPTOPP_END_TRACER_CLASS }; + +// In your Tracer-derived class, you should define a globally unique event number for each +// new event defined. This can be done using the following macros. + +#define CRYPTOPP_BEGIN_TRACER_EVENTS(UNIQUENR) enum { EVENTBASE = UNIQUENR, +#define CRYPTOPP_TRACER_EVENT(EVENTNAME) EventNr_##EVENTNAME, +#define CRYPTOPP_END_TRACER_EVENTS }; + +// In your own Tracer-derived class, you must define two methods per new trace event type: +// - unsigned int TraceXxxx() const +// Your default implementation of this method should return the event number if according +// to the default trace level system the event should be traced, or 0 if it should not. +// - void TraceXxxx(string const& s) +// This method should call TraceIf(TraceXxxx(), s); to do the tracing. +// For your convenience, a macro to define these two types of methods are defined below. +// If you use this macro, you should also use the TRACER_EVENTS macros above to associate +// event names with numbers. + +#define CRYPTOPP_TRACER_EVENT_METHODS(EVENTNAME, LOGLEVEL) \ + virtual unsigned int Trace##EVENTNAME() const { return Tracing(EventNr_##EVENTNAME, LOGLEVEL); } \ + virtual void Trace##EVENTNAME(std::string const& s) { TraceIf(Trace##EVENTNAME(), s); } + + +/*! A simple unidirectional linked list with m_prev == 0 to indicate the final entry. + The aim of this implementation is to provide a very lightweight and practical + tracing mechanism with a low performance impact. Functions and methods supporting + this call-stack mechanism would take a parameter of the form "CallStack const& callStack", + and would pass this parameter to subsequent functions they call using the construct: + + SubFunc(arg1, arg2, CallStack("my func at place such and such", &callStack)); + + The advantage of this approach is that it is easy to use and should be very efficient, + involving no allocation from the heap, just a linked list of stack objects containing + pointers to static ASCIIZ strings (or possibly additional but simple data if derived). */ +class CallStack +{ +public: + CallStack(char const* i, CallStack const* p) : m_info(i), m_prev(p) {} + CallStack const* Prev() const { return m_prev; } + virtual std::string Format() const; + +protected: + char const* m_info; + CallStack const* m_prev; +}; + +/*! An extended CallStack entry type with an additional numeric parameter. */ +class CallStackWithNr : public CallStack +{ +public: + CallStackWithNr(char const* i, word32 n, CallStack const* p) : CallStack(i, p), m_nr(n) {} + std::string Format() const; + +protected: + word32 m_nr; +}; + +/*! An extended CallStack entry type with an additional string parameter. */ +class CallStackWithStr : public CallStack +{ +public: + CallStackWithStr(char const* i, char const* z, CallStack const* p) : CallStack(i, p), m_z(z) {} + std::string Format() const; + +protected: + char const* m_z; +}; + +CRYPTOPP_BEGIN_TRACER_CLASS_1(WaitObjectsTracer, Tracer) + CRYPTOPP_BEGIN_TRACER_EVENTS(0x48752841) + CRYPTOPP_TRACER_EVENT(NoWaitLoop) + CRYPTOPP_END_TRACER_EVENTS + CRYPTOPP_TRACER_EVENT_METHODS(NoWaitLoop, 1) +CRYPTOPP_END_TRACER_CLASS + struct WaitingThreadData; //! container of wait objects -class WaitObjectContainer +class WaitObjectContainer : public NotCopyable { public: //! exception thrown by WaitObjectContainer @@ -41,21 +155,25 @@ public: static unsigned int MaxWaitObjects(); - WaitObjectContainer(); + WaitObjectContainer(WaitObjectsTracer* tracer = 0); void Clear(); - void SetNoWait(); + void SetNoWait(CallStack const& callStack); + void ScheduleEvent(double milliseconds, CallStack const& callStack); + // returns false if timed out bool Wait(unsigned long milliseconds); #ifdef USE_WINDOWS_STYLE_SOCKETS ~WaitObjectContainer(); - void AddHandle(HANDLE handle); + void AddHandle(HANDLE handle, CallStack const& callStack); #else - void AddReadFd(int fd); - void AddWriteFd(int fd); + void AddReadFd(int fd, CallStack const& callStack); + void AddWriteFd(int fd, CallStack const& callStack); #endif private: + WaitObjectsTracer* m_tracer; + #ifdef USE_WINDOWS_STYLE_SOCKETS void CreateThreads(unsigned int count); std::vector<HANDLE> m_handles; @@ -67,16 +185,20 @@ private: int m_maxFd; #endif bool m_noWait; + double m_firstEventTime; + Timer m_eventTimer; -#if CRYPTOPP_DETECT_NO_WAIT #ifdef USE_WINDOWS_STYLE_SOCKETS - DWORD m_lastResult; + typedef size_t LastResultType; #else - int m_lastResult; + typedef int LastResultType; #endif + enum { LASTRESULT_NOWAIT = -1, LASTRESULT_SCHEDULED = -2, LASTRESULT_TIMEOUT = -3 }; + LastResultType m_lastResult; unsigned int m_sameResultCount; - Timer m_timer; -#endif + Timer m_noWaitTimer; + void SetLastResult(LastResultType result); + void DetectNoWait(LastResultType result, CallStack const& callStack); }; NAMESPACE_END diff --git a/winpipes.cpp b/winpipes.cpp index 3c2f7d5..1c2e047 100644 --- a/winpipes.cpp +++ b/winpipes.cpp @@ -92,7 +92,7 @@ bool WindowsPipeReceiver::Receive(byte* buf, size_t bufLen) HANDLE h = GetHandle(); // don't queue too much at once, or we might use up non-paged memory - if (ReadFile(h, buf, UnsignedMin(128U*1024U, bufLen), &m_lastResult, &m_overlapped)) + if (ReadFile(h, buf, UnsignedMin((DWORD)128*1024, bufLen), &m_lastResult, &m_overlapped)) { if (m_lastResult == 0) m_eofReceived = true; @@ -115,12 +115,12 @@ bool WindowsPipeReceiver::Receive(byte* buf, size_t bufLen) return !m_resultPending; } -void WindowsPipeReceiver::GetWaitObjects(WaitObjectContainer &container) +void WindowsPipeReceiver::GetWaitObjects(WaitObjectContainer &container, CallStack const& callStack) { if (m_resultPending) - container.AddHandle(m_event); + container.AddHandle(m_event, CallStack("WindowsPipeReceiver::GetWaitObjects() - result pending", &callStack)); else if (!m_eofReceived) - container.SetNoWait(); + container.SetNoWait(CallStack("WindowsPipeReceiver::GetWaitObjects() - result ready", &callStack)); } unsigned int WindowsPipeReceiver::GetReceiveResult() @@ -166,7 +166,7 @@ void WindowsPipeSender::Send(const byte* buf, size_t bufLen) DWORD written = 0; HANDLE h = GetHandle(); // don't queue too much at once, or we might use up non-paged memory - if (WriteFile(h, buf, UnsignedMin(128U*1024U, bufLen), &written, &m_overlapped)) + if (WriteFile(h, buf, UnsignedMin((DWORD)128*1024, bufLen), &written, &m_overlapped)) { m_resultPending = false; m_lastResult = written; @@ -180,12 +180,12 @@ void WindowsPipeSender::Send(const byte* buf, size_t bufLen) } } -void WindowsPipeSender::GetWaitObjects(WaitObjectContainer &container) +void WindowsPipeSender::GetWaitObjects(WaitObjectContainer &container, CallStack const& callStack) { if (m_resultPending) - container.AddHandle(m_event); + container.AddHandle(m_event, CallStack("WindowsPipeSender::GetWaitObjects() - result pending", &callStack)); else - container.SetNoWait(); + container.SetNoWait(CallStack("WindowsPipeSender::GetWaitObjects() - result ready", &callStack)); } unsigned int WindowsPipeSender::GetSendResult() @@ -69,7 +69,7 @@ public: bool EofReceived() const {return m_eofReceived;} unsigned int GetMaxWaitObjectCount() const {return 1;} - void GetWaitObjects(WaitObjectContainer &container); + void GetWaitObjects(WaitObjectContainer &container, CallStack const& callStack); private: WindowsHandle m_event; @@ -88,10 +88,11 @@ public: bool MustWaitForResult() {return true;} void Send(const byte* buf, size_t bufLen); unsigned int GetSendResult(); + bool MustWaitForEof() { return false; } void SendEof() {} unsigned int GetMaxWaitObjectCount() const {return 1;} - void GetWaitObjects(WaitObjectContainer &container); + void GetWaitObjects(WaitObjectContainer &container, CallStack const& callStack); private: WindowsHandle m_event; diff --git a/zdeflate.cpp b/zdeflate.cpp index b83243c..102dc4d 100644 --- a/zdeflate.cpp +++ b/zdeflate.cpp @@ -383,7 +383,13 @@ unsigned int Deflator::LongestMatch(unsigned int &bestMatch) const if (scan[bestLength-1] == match[bestLength-1] && scan[bestLength] == match[bestLength] && scan[0] == match[0] && scan[1] == match[1]) { assert(scan[2] == match[2]); - unsigned int len = (unsigned int)(stdext::unchecked_mismatch(scan+3, scanEnd, match+3).first - scan); + unsigned int len = (unsigned int)( +#ifdef _STDEXT_BEGIN + stdext::unchecked_mismatch +#else + std::mismatch +#endif + (scan+3, scanEnd, match+3).first - scan); assert(len != bestLength); if (len > bestLength) { diff --git a/zinflate.cpp b/zinflate.cpp index 4a43420..4018e11 100644 --- a/zinflate.cpp +++ b/zinflate.cpp @@ -242,7 +242,7 @@ void Inflator::OutputString(const byte *string, size_t length) { while (length) { - size_t len = STDMIN(length, m_window.size() - m_current); + size_t len = UnsignedMin(length, m_window.size() - m_current); memcpy(m_window + m_current, string, len); m_current += len; if (m_current == m_window.size()) |