summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cryptlib.cpp4
-rw-r--r--cryptlib.h11
-rw-r--r--filters.h4
-rw-r--r--hrtimer.cpp16
-rw-r--r--hrtimer.h2
-rw-r--r--misc.h18
-rw-r--r--network.cpp343
-rw-r--r--network.h99
-rw-r--r--socketft.cpp75
-rw-r--r--socketft.h11
-rw-r--r--test.cpp6
-rw-r--r--wait.cpp165
-rw-r--r--wait.h160
-rw-r--r--winpipes.cpp16
-rw-r--r--winpipes.h5
-rw-r--r--zdeflate.cpp8
-rw-r--r--zinflate.cpp2
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 &parameters, int propagation)
diff --git a/cryptlib.h b/cryptlib.h
index 46ac0be..5851d95 100644
--- a/cryptlib.h
+++ b/cryptlib.h
@@ -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
diff --git a/filters.h b/filters.h
index 8dc9c56..0b4fa74 100644
--- a/filters.h
+++ b/filters.h
@@ -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()
diff --git a/hrtimer.h b/hrtimer.h
index 659b734..a6a96df 100644
--- a/hrtimer.h
+++ b/hrtimer.h
@@ -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)
diff --git a/misc.h b/misc.h
index 3b25ee0..a2119bf 100644
--- a/misc.h
+++ b/misc.h
@@ -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;
}
diff --git a/network.h b/network.h
index 6efbf99..4373862 100644
--- a/network.h
+++ b/network.h
@@ -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
diff --git a/socketft.h b/socketft.h
index c7d33fd..d23dd40 100644
--- a/socketft.h
+++ b/socketft.h
@@ -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;
diff --git a/test.cpp b/test.cpp
index e5ef22c..6ac7f4b 100644
--- a/test.cpp
+++ b/test.cpp
@@ -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);
diff --git a/wait.cpp b/wait.cpp
index 68e66ff..4e9ed23 100644
--- a/wait.cpp
+++ b/wait.cpp
@@ -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);
}
diff --git a/wait.h b/wait.h
index 7adcdd7..045afbc 100644
--- a/wait.h
+++ b/wait.h
@@ -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()
diff --git a/winpipes.h b/winpipes.h
index 80c1143..07225f9 100644
--- a/winpipes.h
+++ b/winpipes.h
@@ -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())