From: Alex Rousskov Date: Thu, 25 Sep 2008 17:22:12 +0000 (-0600) Subject: Added a DescriptorSet class to manage an unordered collection of unique X-Git-Tag: SQUID_3_1_0_1~49^2~3 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=877ce94479c54199c17a591a78b49c3b175d628d;p=thirdparty%2Fsquid.git Added a DescriptorSet class to manage an unordered collection of unique descriptors. DescriptorSet is used for half-closed descriptor monitoring. It might be useful for deferred reads as well, but that remains to be seen. DescriptorSet has O(1) complexity for search, insertion, and deletion. It uses about 2*sizeof(int)*MaxFD bytes total. Splay tree that used to store half-closed descriptors previously uses less RAM for small number of descriptors but has O(log n) complexity. Same for std::set, a potential DescriptorSet replacement. --- diff --git a/src/DescriptorSet.cc b/src/DescriptorSet.cc new file mode 100644 index 0000000000..3bd2b6a4aa --- /dev/null +++ b/src/DescriptorSet.cc @@ -0,0 +1,96 @@ + +/* + * $Id$ + * + * DEBUG: section 5 Comm + */ + +#include "DescriptorSet.h" + +// pre-allocates descriptor store and index for Squid_MaxFD descriptors +DescriptorSet::DescriptorSet(): descriptors_(NULL), index_(NULL), + capacity_(0), size_(0) +{ + // we allocate once and never realloc, at least for now + capacity_ = Squid_MaxFD; + descriptors_ = new int[capacity_]; + index_ = new int[capacity_]; + + // fill index with -1s to be able to say whether a descriptor is present + // it is not essential to fill the descriptors, but it enables more checks + for (int i = 0; i < capacity_; ++i) + index_[i] = descriptors_[i] = -1; +} + +DescriptorSet::~DescriptorSet() +{ + delete[] descriptors_; + delete[] index_; +} + +/// adds if unique; returns true if added +bool +DescriptorSet::add(int fd) +{ + assert(0 <= fd && fd < capacity_); // \todo: replace with Must() + + if (has(fd)) + return false; // already have it + + assert(size_ < capacity_); // \todo: replace with Must() + const int pos = size_++; + index_[fd] = pos; + descriptors_[pos] = fd; + return true; // really added +} + +/// deletes if there; returns true if deleted +bool +DescriptorSet::del(int fd) +{ + assert(0 <= fd && fd < capacity_); // \todo: here and below, use Must() + + if (!has(fd)) + return false; // we do not have it + + assert(!empty()); + const int delPos = index_[fd]; + assert(0 <= delPos && delPos < capacity_); + + // move the last descriptor to the deleted fd position + // to avoid skipping deleted descriptors in pop() + const int lastPos = size_-1; + const int lastFd = descriptors_[lastPos]; + assert(delPos <= lastPos); // may be the same + descriptors_[delPos] = lastFd; + index_[lastFd] = delPos; + + descriptors_[lastPos] = -1; + index_[fd] = -1; + --size_; + + return true; // really added +} + +/// ejects one descriptor in unspecified order +int +DescriptorSet::pop() +{ + assert(!empty()); + const int lastPos =--size_; + const int lastFd = descriptors_[lastPos]; + assert(0 <= lastFd && lastFd < capacity_); + + // cleanup + descriptors_[lastPos] = -1; + index_[lastFd] = -1; + + return lastFd; +} + +void +DescriptorSet::print(std::ostream &os) const +{ + // \todo add "name" if the set is used for more than just half-closed FDs + os << size_ << " FDs"; +} diff --git a/src/DescriptorSet.h b/src/DescriptorSet.h new file mode 100644 index 0000000000..44846c7c6b --- /dev/null +++ b/src/DescriptorSet.h @@ -0,0 +1,58 @@ +#ifndef SQUID_DESCRIPTOR_SET_H +#define SQUID_DESCRIPTOR_SET_H + +#include "squid.h" + +/** \ingroup Comm + + \todo: Should we use std::set with its flexibility? Our implementation + has constant overhead, which is smaller than log(n) of std::set. + +an unordered collection of unique descriptors with O(1) add/del/has ops */ +class DescriptorSet { +public: + // for STL compatibility, should we decide to switch to std::set or similar + typedef const int *const_iterator; + + DescriptorSet(); + ~DescriptorSet(); + + /// checks whether fd is in the set + bool has(const int fd) const { return 0 <= fd && fd < capacity_ && + index_[fd] >= 0; } + + bool add(int fd); ///< adds if unique; returns true if added + bool del(int fd); ///< deletes if there; returns true if deleted + int pop(); ///< deletes and returns one descriptor, in unspecified order + + bool empty() const { return !size_; } ///< number of descriptors in the set + + /// begin iterator a la STL; may become invalid if the object is modified + const_iterator begin() const { return descriptors_; } + /// end iterator a la STL; may become invalid if the object is modified + const_iterator end() const { return begin() + size_; } + + /// outputs debugging info about the set + void print(std::ostream &os) const; + +private: + // these would be easy to support when needed; prohibit for now + DescriptorSet(const DescriptorSet &s); // declared but undefined + DescriptorSet &operator =(const DescriptorSet &s); // declared, undefined + + int *descriptors_; ///< descriptor values in random order + int *index_; ///< descriptor:position index into descriptors_ + int capacity_; ///< total number of descriptor slots + int size_; ///< number of descriptors in the set +}; + +/// convenience wrapper to be used in debugs() context +inline std::ostream & +operator <<(std::ostream &os, const DescriptorSet &ds) +{ + ds.print(os); + return os; +} + +#endif /* SQUID_DESCRIPTOR_SET_H */ + diff --git a/src/Makefile.am b/src/Makefile.am index ea6a27a9fc..d16a8c4f37 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -427,6 +427,8 @@ libsquid_la_SOURCES = \ comm.h \ CommCalls.cc \ CommCalls.h \ + DescriptorSet.cc \ + DescriptorSet.h \ IPInterception.cc \ IPInterception.h \ ICAP/AsyncJob.cc \