Ethereum  PoC-8
The C++ Implementation of Ethereum
BlockQueue.cpp
Go to the documentation of this file.
1 /*
2  This file is part of cpp-ethereum.
3 
4  cpp-ethereum is free software: you can redistribute it and/or modify
5  it under the terms of the GNU General Public License as published by
6  the Free Software Foundation, either version 3 of the License, or
7  (at your option) any later version.
8 
9  cpp-ethereum is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  GNU General Public License for more details.
13 
14  You should have received a copy of the GNU General Public License
15  along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
16 */
22 #include "BlockQueue.h"
23 #include <thread>
24 #include <sstream>
25 #include <libdevcore/Log.h>
26 #include <libethcore/Exceptions.h>
27 #include <libethcore/BlockHeader.h>
28 #include "BlockChain.h"
29 #include "VerifiedBlock.h"
30 #include "State.h"
31 using namespace std;
32 using namespace dev;
33 using namespace dev::eth;
34 
35 size_t const c_maxKnownCount = 100000;
36 size_t const c_maxKnownSize = 128 * 1024 * 1024;
37 size_t const c_maxUnknownCount = 100000;
38 size_t const c_maxUnknownSize = 512 * 1024 * 1024; // Block size can be ~50kb
39 
40 BlockQueue::BlockQueue()
41 {
42  // Allow some room for other activity
43  unsigned verifierThreads = std::max(thread::hardware_concurrency(), 3U) - 2U;
44  for (unsigned i = 0; i < verifierThreads; ++i)
45  m_verifiers.emplace_back([=](){
46  setThreadName("verifier" + toString(i));
47  this->verifierBody();
48  });
49 }
50 
51 BlockQueue::~BlockQueue()
52 {
53  stop();
54 }
55 
56 void BlockQueue::stop()
57 {
58  DEV_GUARDED(m_verification)
59  m_deleting = true;
60 
61  m_moreToVerify.notify_all();
62  for (auto& i: m_verifiers)
63  i.join();
64  m_verifiers.clear();
65 }
66 
67 void BlockQueue::clear()
68 {
69  WriteGuard l(m_lock);
71  Guard l2(m_verification);
72  m_readySet.clear();
73  m_drainingSet.clear();
74  m_verified.clear();
75  m_unverified.clear();
76  m_verifying.clear();
77  m_unknownSet.clear();
78  m_unknown.clear();
79  m_future.clear();
80  m_futureSet.clear();
81  m_difficulty = 0;
82  m_drainingDifficulty = 0;
83 }
84 
85 void BlockQueue::verifierBody()
86 {
87  while (!m_deleting)
88  {
89  UnverifiedBlock work;
90 
91  {
92  unique_lock<Mutex> l(m_verification);
93  m_moreToVerify.wait(l, [&](){ return !m_unverified.isEmpty() || m_deleting; });
94  if (m_deleting)
95  return;
96  work = m_unverified.dequeue();
97 
98  BlockHeader bi;
99  bi.setSha3Uncles(work.hash);
100  bi.setParentHash(work.parentHash);
101  m_verifying.enqueue(move(bi));
102  }
103 
104  VerifiedBlock res;
105  swap(work.blockData, res.blockData);
106  try
107  {
108  res.verified = m_bc->verifyBlock(&res.blockData, m_onBad, ImportRequirements::OutOfOrderChecks);
109  }
110  catch (std::exception const& _ex)
111  {
112  // bad block.
113  // has to be this order as that's how invariants() assumes.
114  WriteGuard l2(m_lock);
115  unique_lock<Mutex> l(m_verification);
116  m_readySet.erase(work.hash);
117  m_knownBad.insert(work.hash);
118  if (!m_verifying.remove(work.hash))
119  cwarn << "Unexpected exception when verifying block: " << _ex.what();
120  drainVerified_WITH_BOTH_LOCKS();
121  continue;
122  }
123 
124  bool ready = false;
125  {
126  WriteGuard l2(m_lock);
127  unique_lock<Mutex> l(m_verification);
128  if (!m_verifying.isEmpty() && m_verifying.nextHash() == work.hash)
129  {
130  // we're next!
131  m_verifying.dequeue();
132  if (m_knownBad.count(res.verified.info.parentHash()))
133  {
134  m_readySet.erase(res.verified.info.hash());
135  m_knownBad.insert(res.verified.info.hash());
136  }
137  else
138  m_verified.enqueue(move(res));
139 
140  drainVerified_WITH_BOTH_LOCKS();
141  ready = true;
142  }
143  else
144  {
145  if (!m_verifying.replace(work.hash, move(res)))
146  cwarn << "BlockQueue missing our job: was there a GM?";
147  }
148  }
149  if (ready)
150  m_onReady();
151  }
152 }
153 
154 void BlockQueue::drainVerified_WITH_BOTH_LOCKS()
155 {
156  while (!m_verifying.isEmpty() && !m_verifying.next().blockData.empty())
157  {
158  VerifiedBlock block = m_verifying.dequeue();
159  if (m_knownBad.count(block.verified.info.parentHash()))
160  {
161  m_readySet.erase(block.verified.info.hash());
162  m_knownBad.insert(block.verified.info.hash());
163  }
164  else
165  m_verified.enqueue(move(block));
166  }
167 }
168 
169 ImportResult BlockQueue::import(bytesConstRef _block, bool _isOurs)
170 {
171  // Check if we already know this block.
172  h256 h = BlockHeader::headerHashFromBlock(_block);
173 
174  LOG(m_loggerDetail) << "Queuing block " << h << " for import...";
175 
176  UpgradableGuard l(m_lock);
177 
178  if (contains(m_readySet, h) || contains(m_drainingSet, h) || contains(m_unknownSet, h) ||
179  contains(m_knownBad, h) || contains(m_futureSet, h))
180  {
181  // Already know about this one.
182  LOG(m_loggerDetail) << "Already known.";
183  return ImportResult::AlreadyKnown;
184  }
185 
186  BlockHeader bi;
187  try
188  {
189  // TODO: quick verification of seal - will require BlockQueue to be templated on SealEngine
190  // VERIFY: populates from the block and checks the block is internally coherent.
191  bi = m_bc->verifyBlock(_block, m_onBad, ImportRequirements::PostGenesis).info;
192  }
193  catch (Exception const& _e)
194  {
195  cwarn << "Ignoring malformed block: " << diagnostic_information(_e);
196  return ImportResult::Malformed;
197  }
198 
199  LOG(m_loggerDetail) << "Block " << h << " is " << bi.number() << " parent is " << bi.parentHash();
200 
201  // Check block doesn't already exist first!
202  if (m_bc->isKnown(h))
203  {
204  LOG(m_logger) << "Already known in chain.";
205  return ImportResult::AlreadyInChain;
206  }
207 
208  UpgradeGuard ul(l);
210 
211  // Check it's not in the future
212  if (bi.timestamp() > utcTime() && !_isOurs)
213  {
214  m_future.insert(static_cast<time_t>(bi.timestamp()), h, _block.toBytes());
215  m_futureSet.insert(h);
216  char buf[24];
217  time_t bit = static_cast<time_t>(bi.timestamp());
218  if (strftime(buf, 24, "%X", localtime(&bit)) == 0)
219  buf[0] = '\0'; // empty if case strftime fails
220  LOG(m_loggerDetail) << "OK - queued for future [" << bi.timestamp() << " vs " << utcTime()
221  << "] - will wait until " << buf;
222  m_difficulty += bi.difficulty();
223  h256 const parentHash = bi.parentHash();
224  bool const unknown = !contains(m_readySet, parentHash) &&
225  !contains(m_drainingSet, parentHash) &&
226  !contains(m_futureSet, parentHash) && !m_bc->isKnown(parentHash);
227  return unknown ? ImportResult::FutureTimeUnknown : ImportResult::FutureTimeKnown;
228  }
229  else
230  {
231  // We now know it.
232  if (m_knownBad.count(bi.parentHash()))
233  {
234  m_knownBad.insert(bi.hash());
235  updateBad_WITH_LOCK(bi.hash());
236  // bad parent; this is bad too, note it as such
237  return ImportResult::BadChain;
238  }
239  else if (!m_readySet.count(bi.parentHash()) && !m_drainingSet.count(bi.parentHash()) && !m_bc->isKnown(bi.parentHash()))
240  {
241  // We don't know the parent (yet) - queue it up for later. It'll get resent to us if we find out about its ancestry later on.
242  LOG(m_loggerDetail) << "OK - queued as unknown parent: " << bi.parentHash();
243  m_unknown.insert(bi.parentHash(), h, _block.toBytes());
244  m_unknownSet.insert(h);
245  m_difficulty += bi.difficulty();
246 
247  return ImportResult::UnknownParent;
248  }
249  else
250  {
251  // If valid, append to blocks.
252  LOG(m_loggerDetail) << "OK - ready for chain insertion.";
253  DEV_GUARDED(m_verification)
254  m_unverified.enqueue(UnverifiedBlock { h, bi.parentHash(), _block.toBytes() });
255  m_moreToVerify.notify_one();
256  m_readySet.insert(h);
257  m_difficulty += bi.difficulty();
258 
259  noteReady_WITH_LOCK(h);
260 
261  return ImportResult::Success;
262  }
263  }
264 }
265 
266 void BlockQueue::updateBad_WITH_LOCK(h256 const& _bad)
267 {
269  DEV_GUARDED(m_verification)
270  {
271  collectUnknownBad_WITH_BOTH_LOCKS(_bad);
272  bool moreBad = true;
273  while (moreBad)
274  {
275  moreBad = false;
276  std::vector<VerifiedBlock> badVerified = m_verified.removeIf([this](VerifiedBlock const& _b)
277  {
278  return m_knownBad.count(_b.verified.info.parentHash()) || m_knownBad.count(_b.verified.info.hash());
279  });
280  for (auto& b: badVerified)
281  {
282  m_knownBad.insert(b.verified.info.hash());
283  m_readySet.erase(b.verified.info.hash());
284  collectUnknownBad_WITH_BOTH_LOCKS(b.verified.info.hash());
285  moreBad = true;
286  }
287 
288  std::vector<UnverifiedBlock> badUnverified = m_unverified.removeIf([this](UnverifiedBlock const& _b)
289  {
290  return m_knownBad.count(_b.parentHash) || m_knownBad.count(_b.hash);
291  });
292  for (auto& b: badUnverified)
293  {
294  m_knownBad.insert(b.hash);
295  m_readySet.erase(b.hash);
296  collectUnknownBad_WITH_BOTH_LOCKS(b.hash);
297  moreBad = true;
298  }
299 
300  std::vector<VerifiedBlock> badVerifying = m_verifying.removeIf([this](VerifiedBlock const& _b)
301  {
302  return m_knownBad.count(_b.verified.info.parentHash()) || m_knownBad.count(_b.verified.info.sha3Uncles());
303  });
304  for (auto& b: badVerifying)
305  {
306  h256 const& h = b.blockData.size() != 0 ? b.verified.info.hash() : b.verified.info.sha3Uncles();
307  m_knownBad.insert(h);
308  m_readySet.erase(h);
309  collectUnknownBad_WITH_BOTH_LOCKS(h);
310  moreBad = true;
311  }
312  }
313  }
314 }
315 
316 void BlockQueue::collectUnknownBad_WITH_BOTH_LOCKS(h256 const& _bad)
317 {
318  list<h256> badQueue(1, _bad);
319  while (!badQueue.empty())
320  {
321  vector<pair<h256, bytes>> const removed = m_unknown.removeByKeyEqual(badQueue.front());
322  badQueue.pop_front();
323  for (auto& newBad: removed)
324  {
325  m_unknownSet.erase(newBad.first);
326  m_knownBad.insert(newBad.first);
327  badQueue.push_back(newBad.first);
328  }
329  }
330 }
331 
332 bool BlockQueue::doneDrain(h256s const& _bad)
333 {
334  WriteGuard l(m_lock);
336  m_drainingSet.clear();
337  m_difficulty -= m_drainingDifficulty;
338  m_drainingDifficulty = 0;
339  if (_bad.size())
340  {
341  // at least one of them was bad.
342  m_knownBad += _bad;
343  for (h256 const& b: _bad)
344  updateBad_WITH_LOCK(b);
345  }
346  return !m_readySet.empty();
347 }
348 
349 void BlockQueue::tick()
350 {
351  vector<pair<h256, bytes>> todo;
352  {
353  UpgradableGuard l(m_lock);
354  if (m_future.isEmpty())
355  return;
356 
357  LOG(m_logger) << "Checking past-future blocks...";
358 
359  time_t t = utcTime();
360  if (t < m_future.firstKey())
361  return;
362 
363  LOG(m_logger) << "Past-future blocks ready.";
364 
365  {
366  UpgradeGuard l2(l);
368  todo = m_future.removeByKeyNotGreater(t);
369  for (auto const& hash : todo)
370  m_futureSet.erase(hash.first);
371  }
372  }
373  LOG(m_logger) << "Importing " << todo.size() << " past-future blocks.";
374 
375  for (auto const& b: todo)
376  import(&b.second);
377 }
378 
379 BlockQueueStatus BlockQueue::status() const
380 {
381  ReadGuard l(m_lock);
382  Guard l2(m_verification);
383  return BlockQueueStatus{ m_drainingSet.size(), m_verified.count(), m_verifying.count(), m_unverified.count(),
384  m_future.count(), m_unknown.count(), m_knownBad.size() };
385 }
386 
387 QueueStatus BlockQueue::blockStatus(h256 const& _h) const
388 {
389  ReadGuard l(m_lock);
390  return
391  m_readySet.count(_h) ?
392  QueueStatus::Ready :
393  m_drainingSet.count(_h) ?
394  QueueStatus::Importing :
395  m_unknownSet.count(_h) ?
396  QueueStatus::UnknownParent :
397  m_knownBad.count(_h) ?
398  QueueStatus::Bad :
399  QueueStatus::Unknown;
400 }
401 
402 bool BlockQueue::knownFull() const
403 {
404  Guard l(m_verification);
405  return knownSize() > c_maxKnownSize || knownCount() > c_maxKnownCount;
406 }
407 
408 std::size_t BlockQueue::knownSize() const
409 {
410  return m_verified.size() + m_unverified.size() + m_verifying.size();
411 }
412 
413 std::size_t BlockQueue::knownCount() const
414 {
415  return m_verified.count() + m_unverified.count() + m_verifying.count();
416 }
417 
418 bool BlockQueue::unknownFull() const
419 {
420  ReadGuard l(m_lock);
421  return unknownSize() > c_maxUnknownSize || unknownCount() > c_maxUnknownCount;
422 }
423 
424 std::size_t BlockQueue::unknownSize() const
425 {
426  return m_future.size() + m_unknown.size();
427 }
428 
429 std::size_t BlockQueue::unknownCount() const
430 {
431  return m_future.count() + m_unknown.count();
432 }
433 
434 void BlockQueue::drain(VerifiedBlocks& o_out, unsigned _max)
435 {
436  bool wasFull = false;
437  DEV_WRITE_GUARDED(m_lock)
438  {
440  wasFull = knownFull();
441  if (m_drainingSet.empty())
442  {
443  m_drainingDifficulty = 0;
444  DEV_GUARDED(m_verification)
445  o_out = m_verified.dequeueMultiple(min<unsigned>(_max, m_verified.count()));
446 
447  for (auto const& bs: o_out)
448  {
449  // TODO: @optimise use map<h256, bytes> rather than vector<bytes> & set<h256>.
450  auto h = bs.verified.info.hash();
451  m_drainingSet.insert(h);
452  m_drainingDifficulty += bs.verified.info.difficulty();
453  m_readySet.erase(h);
454  }
455  }
456  }
457  if (wasFull && !knownFull())
458  m_onRoomAvailable();
459 }
460 
461 bool BlockQueue::invariants() const
462 {
463  Guard l(m_verification);
464  if (m_readySet.size() != knownCount())
465  {
466  std::stringstream s;
467  s << "Failed BlockQueue invariant: m_readySet: " << m_readySet.size() << " m_verified: " << m_verified.count() << " m_unverified: " << m_unverified.count() << " m_verifying" << m_verifying.count();
468  BOOST_THROW_EXCEPTION(FailedInvariant() << errinfo_comment(s.str()));
469  }
470  return true;
471 }
472 
473 void BlockQueue::noteReady_WITH_LOCK(h256 const& _good)
474 {
476  list<h256> goodQueue(1, _good);
477  bool notify = false;
478  while (!goodQueue.empty())
479  {
480  h256 const parent = goodQueue.front();
481  vector<pair<h256, bytes>> const removed = m_unknown.removeByKeyEqual(parent);
482  goodQueue.pop_front();
483  for (auto& newReady: removed)
484  {
485  DEV_GUARDED(m_verification)
486  m_unverified.enqueue(UnverifiedBlock { newReady.first, parent, move(newReady.second) });
487  m_unknownSet.erase(newReady.first);
488  m_readySet.insert(newReady.first);
489  goodQueue.push_back(newReady.first);
490  notify = true;
491  }
492  }
493  if (notify)
494  m_moreToVerify.notify_all();
495 }
496 
497 void BlockQueue::retryAllUnknown()
498 {
499  WriteGuard l(m_lock);
501  while (!m_unknown.isEmpty())
502  {
503  h256 parent = m_unknown.firstKey();
504  vector<pair<h256, bytes>> removed = m_unknown.removeByKeyEqual(parent);
505  for (auto& newReady: removed)
506  {
507  DEV_GUARDED(m_verification)
508  m_unverified.enqueue(UnverifiedBlock{ newReady.first, parent, move(newReady.second) });
509  m_unknownSet.erase(newReady.first);
510  m_readySet.insert(newReady.first);
511  m_moreToVerify.notify_one();
512  }
513  }
514  m_moreToVerify.notify_all();
515 }
516 
517 std::ostream& dev::eth::operator<<(std::ostream& _out, BlockQueueStatus const& _bqs)
518 {
519  _out << "importing: " << _bqs.importing << endl;
520  _out << "verified: " << _bqs.verified << endl;
521  _out << "verifying: " << _bqs.verifying << endl;
522  _out << "unverified: " << _bqs.unverified << endl;
523  _out << "future: " << _bqs.future << endl;
524  _out << "unknown: " << _bqs.unknown << endl;
525  _out << "bad: " << _bqs.bad << endl;
526 
527  return _out;
528 }
529 
530 u256 BlockQueue::difficulty() const
531 {
532  UpgradableGuard l(m_lock);
533  return m_difficulty;
534 }
535 
536 bool BlockQueue::isActive() const
537 {
538  UpgradableGuard l(m_lock);
539  if (m_readySet.empty() && m_drainingSet.empty())
540  DEV_GUARDED(m_verification)
541  if (m_verified.isEmpty() && m_verifying.isEmpty() && m_unverified.isEmpty())
542  return false;
543  return true;
544 }
545 
546 std::ostream& dev::eth::operator<< (std::ostream& os, QueueStatus const& obj)
547 {
548  os << static_cast<std::underlying_type<QueueStatus>::type>(obj);
549  return os;
550 }
dev::eth::VerifiedBlock
Verified block info, combines block data and verified info/transactions.
Definition: VerifiedBlock.h:45
dev::eth::VerifiedBlocks
std::vector< VerifiedBlock > VerifiedBlocks
Definition: VerifiedBlock.h:76
BlockQueue.h
dev::eth::BlockQueueStatus::importing
size_t importing
Definition: BlockQueue.h:45
dev::eth::ImportResult
ImportResult
Definition: Common.h:97
dev::eth::BlockHeader::hash
h256 hash(IncludeSeal _i=WithSeal) const
Definition: BlockHeader.cpp:119
dev::vector_ref< byte const >
dev::eth::BlockHeader
Encapsulation of a block header. Class to contain all of a block header's data. It is able to parse a...
Definition: BlockHeader.h:97
dev::UpgradableGuard
boost::upgrade_lock< boost::shared_mutex > UpgradableGuard
Definition: Guards.h:45
dev::eth::BlockQueueStatus::verified
size_t verified
Definition: BlockQueue.h:46
dev::eth::BlockHeader::sha3Uncles
h256 const & sha3Uncles() const
Definition: BlockHeader.h:158
VerifiedBlock.h
dev::Guard
std::lock_guard< std::mutex > Guard
Definition: Guards.h:41
std::swap
void swap(dev::eth::Watch &_a, dev::eth::Watch &_b)
Definition: Interface.h:282
dev::eth::VerifiedBlock::verified
VerifiedBlockRef verified
Verified block structures.
Definition: VerifiedBlock.h:68
Exceptions.h
dev::eth::BlockQueueStatus::future
size_t future
Definition: BlockQueue.h:49
dev::eth
Definition: BasicAuthority.h:32
c_maxKnownCount
size_t const c_maxKnownCount
Definition: BlockQueue.cpp:35
BlockHeader.h
dev::utcTime
int64_t utcTime()
Get the current time in seconds since the epoch in UTC.
Definition: Common.cpp:53
dev::FixedHash< 32 >
DEV_GUARDED
#define DEV_GUARDED(MUTEX)
Simple block guard. The expression/block following is guarded though the given mutex....
Definition: Guards.h:132
dev::Exception
Base class for all exceptions.
Definition: Exceptions.h:39
LOG
#define LOG
Definition: Log.h:63
dev::WriteGuard
boost::unique_lock< boost::shared_mutex > WriteGuard
Definition: Guards.h:47
c_maxUnknownCount
size_t const c_maxUnknownCount
Definition: BlockQueue.cpp:37
dev::eth::BlockHeader::setSha3Uncles
void setSha3Uncles(h256 const &_v)
Definition: BlockHeader.h:144
dev::eth::VerifiedBlock::blockData
bytes blockData
Block data.
Definition: VerifiedBlock.h:69
dev::eth::BlockQueueStatus::unknown
size_t unknown
Definition: BlockQueue.h:50
dev::h256s
std::vector< h256 > h256s
Definition: FixedHash.h:361
dev::eth::BlockHeader::parentHash
h256 const & parentHash() const
Definition: BlockHeader.h:157
DEV_INVARIANT_CHECK
#define DEV_INVARIANT_CHECK
Scope guard for invariant check in a class derived from HasInvariants.
Definition: Common.h:238
dev::eth::QueueStatus
QueueStatus
Definition: BlockQueue.h:55
dev::eth::BlockQueueStatus
Definition: BlockQueue.h:44
DEV_WRITE_GUARDED
#define DEV_WRITE_GUARDED(MUTEX)
Definition: Guards.h:136
dev::ReadGuard
boost::shared_lock< boost::shared_mutex > ReadGuard
Definition: Guards.h:44
dev::eth::BlockHeader::number
int64_t number() const
Definition: BlockHeader.h:166
dev::eth::BlockQueueStatus::unverified
size_t unverified
Definition: BlockQueue.h:48
BlockChain.h
dev::eth::BlockQueueStatus::verifying
size_t verifying
Definition: BlockQueue.h:47
dev::UpgradeGuard
boost::upgrade_to_unique_lock< boost::shared_mutex > UpgradeGuard
Definition: Guards.h:46
dev::FixedHash::size
@ size
Definition: FixedHash.h:53
dev::contains
bool contains(T const &_t, V const &_v)
Definition: CommonData.h:324
dev::vector_ref::toBytes
std::vector< unsigned char > toBytes() const
Definition: vector_ref.h:43
std
Definition: FixedHash.h:393
dev::eth::Success
@ Success
Definition: Common.h:223
dev::eth::BlockHeader::setParentHash
void setParentHash(h256 const &_v)
Definition: BlockHeader.h:143
dev::u256
boost::multiprecision::number< boost::multiprecision::cpp_int_backend< 256, 256, boost::multiprecision::unsigned_magnitude, boost::multiprecision::unchecked, void > > u256
Definition: Common.h:121
dev::eth::operator<<
std::ostream & operator<<(std::ostream &_out, BlockHeader const &_bi)
Definition: BlockHeader.h:217
dev::eth::BlockQueueStatus::bad
size_t bad
Definition: BlockQueue.h:51
c_maxKnownSize
size_t const c_maxKnownSize
Definition: BlockQueue.cpp:36
dev
Definition: Address.cpp:21
cwarn
#define cwarn
dev::eth::BlockHeader::difficulty
u256 const & difficulty() const
Definition: BlockHeader.h:170
Log.h
dev::errinfo_comment
boost::error_info< struct tag_comment, std::string > errinfo_comment
Definition: Assertions.h:69
State.h
dev::eth::BlockHeader::timestamp
int64_t timestamp() const
Definition: BlockHeader.h:160
dev::eth::VerifiedBlockRef::info
BlockHeader info
Prepopulated block info.
Definition: VerifiedBlock.h:39
c_maxUnknownSize
size_t const c_maxUnknownSize
Definition: BlockQueue.cpp:38