├── .gitignore ├── .gitpod.yml ├── Makefile ├── README.md ├── doc └── img │ └── dexter.jpeg ├── src ├── .gitignore ├── FIFO.h ├── Makefile ├── abool.cpp ├── abool.h ├── adapter.h ├── aint16.cpp ├── aint16.h ├── aint32.cpp ├── aint32.h ├── aint64.cpp ├── aint64.h ├── aint8.cpp ├── aint8.h ├── atomic.h ├── cqueue.h ├── io │ ├── channel.cpp │ ├── channel.h │ ├── datagram.cpp │ ├── datagram.h │ ├── multicast_channel.cpp │ ├── multicast_channel.h │ ├── tcp_receiver.cpp │ ├── tcp_receiver.h │ ├── tcp_transmitter.cpp │ ├── tcp_transmitter.h │ ├── udp_receiver.cpp │ ├── udp_receiver.h │ ├── udp_transmitter.cpp │ └── udp_transmitter.h ├── mpsc │ ├── CircularFIFO.h │ └── LinkedFIFO.h ├── pool.h ├── sink.h ├── source.h ├── spmc │ ├── CircularFIFO.h │ └── LinkedFIFO.h ├── spsc │ └── CircularFIFO.h ├── trie.h └── util │ └── timer.h └── tests ├── .gitignore ├── Makefile ├── atomic.cpp ├── cqueue.cpp ├── drain.h ├── hammer.h ├── linkedFIFO.cpp ├── mpsc_fifo.cpp ├── pool.cpp ├── receiver.cpp ├── sender.cpp ├── spmc_fifo.cpp ├── spsc_fifo.cpp ├── trie.cpp └── udp_receiver.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | *.bak 2 | *.dSYM 3 | *.swp 4 | lib 5 | -------------------------------------------------------------------------------- /.gitpod.yml: -------------------------------------------------------------------------------- 1 | image: 2 | file: Dockerfile 3 | tasks: 4 | - init: echo "Adding the tools I need - boost" 5 | command: | 6 | brew install boost 7 | export BOOST_ROOT='/home/linuxbrew/.linuxbrew/Cellar/boost/1.72.0_1' 8 | export LD_LIBRARY_PATH="$BOOST_ROOT/lib" 9 | export GITPOD=1 10 | make 11 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # Makefile for DKit a simple C++ library of fast, lightweight, lock-less or 3 | # near lock-less datastructures. 4 | # 5 | # 6 | # We need to have a few system-level dependencies to make things cleanly 7 | # compile on a few different systems. 8 | # 9 | ifeq ($(shell uname),SunOS) 10 | MAKE = make 11 | CTAGS = ctags 12 | endif 13 | ifeq ($(shell uname),Linux) 14 | MAKE = make 15 | CTAGS = ctags 16 | endif 17 | ifeq ($(shell uname),Darwin) 18 | MAKE = make 19 | CTAGS = ectags --excmd=number --tag-relative=no --fields=+a+m+n+S -R 20 | endif 21 | 22 | # 23 | # These are the locations of the directories we'll use 24 | # 25 | SRC_DIR = src 26 | TESTS_DIR = tests 27 | 28 | # 29 | # These are the main targets that we'll be making 30 | # 31 | all: 32 | @ cd $(SRC_DIR); $(MAKE) all 33 | @ cd $(TESTS_DIR); $(MAKE) all 34 | 35 | clean: 36 | @ cd $(SRC_DIR); $(MAKE) clean 37 | @ cd $(TESTS_DIR); $(MAKE) clean 38 | 39 | depend: 40 | @ cd $(SRC_DIR); $(MAKE) depend 41 | @ cd $(TESTS_DIR); $(MAKE) depend 42 | 43 | ctags: 44 | @ $(CTAGS) `pwd`/src 45 | 46 | -------------------------------------------------------------------------------- /doc/img/dexter.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drbobbeaty/DKit/b3ae323449a8ad6c93e46dd4d07443f8fcc1a001/doc/img/dexter.jpeg -------------------------------------------------------------------------------- /src/.gitignore: -------------------------------------------------------------------------------- 1 | *.bak 2 | *.o 3 | -------------------------------------------------------------------------------- /src/FIFO.h: -------------------------------------------------------------------------------- 1 | /** 2 | * FIFO.h - this file defines a general first-in, first-out queue that all 3 | * the specific implementations will inherit from. This class is 4 | * primarily abstract methods because we need them to be implemented 5 | * by the subclasses, but it's important to have a super class that 6 | * can stand in for any one of the FIFO queues in this library. 7 | */ 8 | #ifndef __DKIT_FIFO_H 9 | #define __DKIT_FIFO_H 10 | 11 | // System Headers 12 | 13 | // Third-Party Headers 14 | 15 | // Other Headers 16 | 17 | // Forward Declarations 18 | 19 | // Public Constants 20 | 21 | // Public Datatypes 22 | 23 | // Public Data Constants 24 | 25 | 26 | namespace dkit { 27 | /** 28 | * This is the main class definition 29 | */ 30 | template class FIFO 31 | { 32 | public: 33 | /******************************************************************* 34 | * 35 | * Constructors/Destructor 36 | * 37 | *******************************************************************/ 38 | /** 39 | * This is the default constructor that assumes NOTHING - it just 40 | * makes a simple queue ready to hold instances of T. 41 | */ 42 | FIFO() 43 | { 44 | } 45 | 46 | 47 | /** 48 | * This is the standard copy constructor that needs to be in every 49 | * class to make sure that we control how many copies we have 50 | * floating around in the system. 51 | */ 52 | FIFO( const FIFO & anOther ) 53 | { 54 | // let the '=' operator do the heavy lifting... 55 | *this = anOther; 56 | } 57 | 58 | 59 | /** 60 | * This is the standard destructor and needs to be virtual to make 61 | * sure that if we subclass off this, the right destructor will be 62 | * called. 63 | */ 64 | virtual ~FIFO() 65 | { 66 | } 67 | 68 | 69 | /** 70 | * When we process the result of an equality we need to make sure 71 | * that we do this right by always having an equals operator on 72 | * all classes. 73 | */ 74 | FIFO & operator=( const FIFO & anOther ) 75 | { 76 | if (this != & anOther) { 77 | // this is where we might clean things up 78 | } 79 | return *this; 80 | } 81 | 82 | 83 | /******************************************************************* 84 | * 85 | * Accessor Methods 86 | * 87 | *******************************************************************/ 88 | /** 89 | * This method takes an item and places it in the queue - if it can. 90 | * If so, then it will return 'true', otherwise, it'll return 'false'. 91 | */ 92 | virtual bool push( const T & anElem ) = 0; 93 | /** 94 | * This method updates the passed-in reference with the value on the 95 | * top of the queue - if it can. If so, it'll return the value and 96 | * 'true', but if it can't, as in the queue is empty, then the method 97 | * will return 'false' and the value will be untouched. 98 | */ 99 | virtual bool pop( T & anElem ) = 0; 100 | /** 101 | * This form of the pop() method will throw a std::exception 102 | * if there is nothing to pop, but otherwise, will return the 103 | * the first element on the queue. This is a slightly different 104 | * form that fits a different use-case, and so it's a handy 105 | * thing to have around at times. 106 | */ 107 | virtual T pop() = 0; 108 | /** 109 | * If there is an item on the queue, this method will return a look 110 | * at that item without updating the queue. The return value will be 111 | * 'true' if there is something, but 'false' if the queue is empty. 112 | */ 113 | virtual bool peek( T & anElem ) = 0; 114 | /** 115 | * This form of the peek() method is very much like the non-argument 116 | * version of the pop() method. If there is something on the top of 117 | * the queue, this method will return a COPY of it. If not, it will 118 | * throw a std::exception, that needs to be caught. 119 | */ 120 | virtual T peek() = 0; 121 | /** 122 | * This method will clear out the contents of the queue so if 123 | * you're storing pointers, then you need to be careful as this 124 | * could leak. 125 | */ 126 | virtual void clear() = 0; 127 | /** 128 | * This method will return 'true' if there are no items in the 129 | * queue. Simple. 130 | */ 131 | virtual bool empty() = 0; 132 | /** 133 | * This method will return the total number of items in the 134 | * queue. Since it's possible that multiple threads are adding 135 | * to this queue at any one point in time, it's really at BEST 136 | * a snapshot of the size, and is only completely accurate 137 | * when the queue is stable. 138 | */ 139 | virtual size_t size() const = 0; 140 | 141 | /******************************************************************* 142 | * 143 | * Utility Methods 144 | * 145 | *******************************************************************/ 146 | /** 147 | * This method checks to see if two queues are equal in their 148 | * contents and not their pointer values. This is how you'd likely 149 | * expect equality to work. 150 | */ 151 | bool operator==( const FIFO & anOther ) const 152 | { 153 | // right now, identity is the only equality we know 154 | return (this == & anOther); 155 | } 156 | 157 | 158 | /** 159 | * This method checks to see if two queues are NOT equal in their 160 | * contents and not their pointer values. This is how you'd likely 161 | * expect equality to work. 162 | */ 163 | bool operator!=( const FIFO & anOther ) const 164 | { 165 | return !operator==(anOther); 166 | } 167 | }; 168 | } // end of namespace dkit 169 | 170 | #endif // __DKIT_FIFO_H 171 | -------------------------------------------------------------------------------- /src/Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # Makefile for DKit src directory 3 | # 4 | # 5 | # We need to have a few system-level dependencies to make things cleanly 6 | # compile on a few different systems. 7 | # 8 | ifeq ($(shell uname),SunOS) 9 | CXX = g++ 10 | CXX_DEFS = -D_REENTRANT -g -O3 11 | CXX_FLAGS = 12 | LDD_FLAGS = 13 | ifeq ($(shell uname -r),5.6) 14 | OS_LIBS = -lsocket -lnsl -lposix4 15 | else 16 | OS_LIBS = -lsocket -lnsl -lrt 17 | endif 18 | SO_EXT = so 19 | SO_FLAG = -shared 20 | endif 21 | ifeq ($(shell uname),Linux) 22 | CXX = g++ 23 | ifeq ($(BOOST_ROOT),) 24 | CXX_DEFS = -D_REENTRANT -g -O3 25 | CXX_FLAGS = -m64 26 | LDD_FLAGS = -m64 27 | else 28 | CXX_DEFS = -D_REENTRANT -g -O3 -I$(BOOST_ROOT)/include 29 | CXX_FLAGS = -m64 30 | LDD_FLAGS = -m64 -L$(BOOST_ROOT)/lib 31 | endif 32 | OS_LIBS = -lpthread -lrt 33 | SO_EXT = so 34 | SO_FLAG = -shared 35 | endif 36 | ifeq ($(shell uname),Darwin) 37 | CXX = g++ 38 | CXX_DEFS = -D_REENTRANT -DBOOST_ASIO_HAS_STD_ATOMIC -g -O3 -Wunused-local-typedef 39 | CXX_FLAGS = -arch x86_64 40 | LDD_FLAGS = -arch x86_64 41 | OS_LIBS = 42 | SO_EXT = so 43 | SO_FLAG = -shared 44 | endif 45 | 46 | # 47 | # These are the locations of the directories we'll use 48 | # 49 | SRC_DIR = . 50 | LIB_DIR = ../lib 51 | BIN_DIR = ../bin 52 | 53 | # 54 | # This is the ultimate target we're making - the library with the entire 55 | # DKit contained within it. 56 | # 57 | LIB_FILE = $(LIB_DIR)/libDKit.$(SO_EXT) 58 | 59 | # 60 | # These are the pretty standard C++-style defines for a makefile - the 61 | # includes, the libs, the compiler flags, etc. 62 | # 63 | INCLUDES = -I. 64 | DEFINES = $(CXX_DEFS) 65 | CXXFLAGS = -fPIC -Wall $(INCLUDES) $(DEFINES) 66 | LIBS = -L$(LIB_DIR) $(OS_LIBS) -lboost_thread-mt -lboost_system-mt -lstdc++ 67 | LDFLAGS = -fPIC $(LIBS) $(LDD_FLAGS) 68 | 69 | # 70 | # These are all the components of DKit 71 | # 72 | .SUFFIXES: .h .cpp .o 73 | OBJS = abool.o aint8.o aint16.o aint32.o aint64.o \ 74 | io/datagram.o io/multicast_channel.o io/channel.o \ 75 | io/tcp_receiver.o io/tcp_transmitter.o \ 76 | io/udp_receiver.o io/udp_transmitter.o 77 | SRCS = $(OBJS:%.o=%.cpp) 78 | 79 | # 80 | # These are the main targets that we'll be making 81 | # 82 | all: $(LIB_FILE) 83 | 84 | clean: 85 | rm -f $(OBJS) $(LIB_FILE) 86 | 87 | depend: 88 | makedepend -Y -o.o -- $(INCLUDES) -- $(SRCS) ; rm Makefile.bak 89 | 90 | .cpp.o: 91 | $(CXX) $(CXX_FLAGS) -c $(CXXFLAGS) $(DEBUG) $< -o $@ 92 | 93 | $(LIB_DIR): 94 | mkdir -p $(LIB_DIR) 95 | 96 | $(LIB_FILE): $(LIB_DIR) $(OBJS) 97 | $(CXX) $(SO_FLAG) $(OBJS) -o $(LIB_FILE) $(LDFLAGS) 98 | 99 | # DO NOT DELETE 100 | 101 | abool.o: abool.h aint8.h aint16.h aint32.h aint64.h 102 | aint8.o: abool.h aint8.h aint16.h aint32.h aint64.h 103 | aint16.o: abool.h aint8.h aint16.h aint32.h aint64.h 104 | aint32.o: abool.h aint8.h aint16.h aint32.h aint64.h 105 | aint64.o: abool.h aint8.h aint16.h aint32.h aint64.h 106 | io/datagram.o: io/datagram.h util/timer.h 107 | io/multicast_channel.o: io/multicast_channel.h abool.h 108 | io/channel.o: io/channel.h abool.h 109 | io/tcp_receiver.o: io/tcp_receiver.h source.h abool.h sink.h io/datagram.h 110 | io/tcp_receiver.o: util/timer.h io/channel.h aint32.h pool.h FIFO.h 111 | io/tcp_receiver.o: spsc/CircularFIFO.h mpsc/CircularFIFO.h 112 | io/tcp_receiver.o: spmc/CircularFIFO.h 113 | io/tcp_transmitter.o: io/tcp_transmitter.h sink.h abool.h source.h 114 | io/tcp_transmitter.o: io/datagram.h util/timer.h io/channel.h 115 | io/tcp_transmitter.o: pool.h FIFO.h spsc/CircularFIFO.h mpsc/CircularFIFO.h 116 | io/tcp_transmitter.o: spmc/CircularFIFO.h aint32.h 117 | io/udp_receiver.o: io/udp_receiver.h source.h abool.h sink.h io/datagram.h 118 | io/udp_receiver.o: util/timer.h io/multicast_channel.h aint32.h pool.h FIFO.h 119 | io/udp_receiver.o: spsc/CircularFIFO.h mpsc/CircularFIFO.h 120 | io/udp_receiver.o: spmc/CircularFIFO.h 121 | io/udp_transmitter.o: io/udp_transmitter.h sink.h abool.h source.h 122 | io/udp_transmitter.o: io/datagram.h util/timer.h io/multicast_channel.h 123 | io/udp_transmitter.o: pool.h FIFO.h spsc/CircularFIFO.h mpsc/CircularFIFO.h 124 | io/udp_transmitter.o: spmc/CircularFIFO.h aint32.h 125 | -------------------------------------------------------------------------------- /src/abool.h: -------------------------------------------------------------------------------- 1 | /** 2 | * abool.h - this file defines the atomic bool class and mimics the simple 3 | * bool datatype in the C spec. The class is to be treated like 4 | * the regular datatypes in C++ - just when they are read/written, 5 | * the operations are atomic in nature. 6 | */ 7 | #ifndef __DKIT_ABOOL_H 8 | #define __DKIT_ABOOL_H 9 | 10 | // System Headers 11 | #include 12 | #include 13 | 14 | // Third-Party Headers 15 | 16 | // Other Headers 17 | 18 | // Forward Declarations 19 | /** 20 | * Let's place all the declarations here so that the individual definitions 21 | * of methods, etc. can use these as references in their signatures. This 22 | * way, the abool can use the auint8_t in an operator=() signature, but not 23 | * have to worry about the order of the definitions. 24 | */ 25 | class abool; 26 | class auint8_t; 27 | class aint8_t; 28 | class auint16_t; 29 | class aint16_t; 30 | class auint32_t; 31 | class aint32_t; 32 | class auint64_t; 33 | class aint64_t; 34 | 35 | // Public Constants 36 | 37 | // Public Datatypes 38 | 39 | // Public Data Constants 40 | 41 | /** 42 | * Start off with a simple atomic bool value - you will find dozens of uses 43 | * for an atomic bool. 44 | */ 45 | class abool 46 | { 47 | public: 48 | /******************************************************** 49 | * 50 | * Constructors/Destructor 51 | * 52 | ********************************************************/ 53 | /** 54 | * This is the default constructor that sets up the value of 55 | * false (0) in the variable. This is the same default that a 56 | * traditional boolean would have. 57 | */ 58 | abool(); 59 | /** 60 | * This constructor takes a traditional bool and creates a new 61 | * atomic bool based on this value. It's not a copy constructor 62 | * because we haven't really 'copied' anything - we're just taking 63 | * the value. 64 | */ 65 | abool( bool aValue ); 66 | /** 67 | * This is the standard copy constructor and needs to be in every 68 | * class to make sure that we don't have too many things running 69 | * around. 70 | */ 71 | abool( const abool & anOther ); 72 | /** 73 | * This is the standard destructor and needs to be virtual to make 74 | * sure that if we subclass off this the right destructor will be 75 | * called. 76 | */ 77 | virtual ~abool(); 78 | 79 | /** 80 | * When we want to process the result of an equality we need to 81 | * make sure that we do this right by always having an equals 82 | * operator on all classes. 83 | */ 84 | abool & operator=( const abool & anOther ); 85 | /** 86 | * Let's also allow this value to be taken from one of the other 87 | * data types defined in stdint.h - again, according to the zero/ 88 | * non-zero rule for booleans. This just makes it very easy to work 89 | * in this atomic bool into existing code. 90 | */ 91 | abool & operator=( bool aValue ); 92 | abool & operator=( uint8_t aValue ); 93 | abool & operator=( int8_t aValue ); 94 | abool & operator=( uint16_t aValue ); 95 | abool & operator=( int16_t aValue ); 96 | abool & operator=( uint32_t aValue ); 97 | abool & operator=( int32_t aValue ); 98 | abool & operator=( uint64_t aValue ); 99 | abool & operator=( int64_t aValue ); 100 | /** 101 | * Let's also allow this guy to take values from the classes in 102 | * this package so that there is no unnecessary casting or method 103 | * calling to get these values into the atomic boolean. 104 | */ 105 | abool & operator=( const auint8_t & aValue ); 106 | abool & operator=( const aint8_t & aValue ); 107 | abool & operator=( const auint16_t & aValue ); 108 | abool & operator=( const aint16_t & aValue ); 109 | abool & operator=( const auint32_t & aValue ); 110 | abool & operator=( const aint32_t & aValue ); 111 | abool & operator=( const auint64_t & aValue ); 112 | abool & operator=( const aint64_t & aValue ); 113 | 114 | /******************************************************** 115 | * 116 | * Accessor Methods 117 | * 118 | ********************************************************/ 119 | /** 120 | * This method returns the current value at the time of the 121 | * call. Since this can change at any time, this is really just 122 | * a snapshot of the value, and should not be considered a long- 123 | * term condition. 124 | */ 125 | bool getValue() const; 126 | /** 127 | * This method allows a more deliberate setting of the value by 128 | * the caller. This is typically not used, but it can be in those 129 | * times when the explicit method call is cleaner to use. 130 | */ 131 | void setValue( bool aValue ); 132 | 133 | /******************************************************** 134 | * 135 | * Useful Operator Methods 136 | * 137 | ********************************************************/ 138 | /** 139 | * This casting operator takes the atomic bool and maps it's 140 | * value into a simple bool for all those times when you really 141 | * need that bool. This is again a snapshot as the value can change 142 | * immediately upon return, but it's as good as you'll get. 143 | */ 144 | operator bool() const; 145 | /** 146 | * These map the abool's value into the other basic data types in 147 | * the stdint.h package. 148 | */ 149 | operator int() const; 150 | operator uint8_t() const; 151 | operator int8_t() const; 152 | operator uint16_t() const; 153 | operator int16_t() const; 154 | operator uint32_t() const; 155 | operator uint64_t() const; 156 | operator int64_t() const; 157 | 158 | /** 159 | * These are the prefix and postfix increment operators, in that 160 | * order, and the first returns a reference to the same instance 161 | * while the latter has to receive a copy prior to the increment. 162 | * In the case of the boolean, these simply "flip" the state of the 163 | * bool from true->false->true, etc. 164 | */ 165 | abool & operator++(); 166 | abool operator++(int); 167 | /** 168 | * These are the prefix and postfix decrement operators, in that 169 | * order, and the first returns a reference to the same instance 170 | * while the latter has to receive a copy prior to the decrement. 171 | * In the case of the boolean, these simply "flip" the state of the 172 | * bool from true->false->true, etc. 173 | */ 174 | abool & operator--(); 175 | abool operator--(int); 176 | /** 177 | * These are the standard RHS operators for this guy, and are all 178 | * handled in a consistent, thread-safe way. The way this works for 179 | * a boolean is that each move "flips" the state, so a simple 180 | * modulo 2 gives us whether or not to flip this value at all. 181 | */ 182 | abool & operator+=( bool aValue ); 183 | abool & operator+=( uint8_t aValue ); 184 | abool & operator+=( int8_t aValue ); 185 | abool & operator+=( uint16_t aValue ); 186 | abool & operator+=( int16_t aValue ); 187 | abool & operator+=( uint32_t aValue ); 188 | abool & operator+=( int32_t aValue ); 189 | abool & operator+=( uint64_t aValue ); 190 | abool & operator+=( int64_t aValue ); 191 | 192 | abool & operator-=( bool aValue ); 193 | abool & operator-=( uint8_t aValue ); 194 | abool & operator-=( int8_t aValue ); 195 | abool & operator-=( uint16_t aValue ); 196 | abool & operator-=( int16_t aValue ); 197 | abool & operator-=( uint32_t aValue ); 198 | abool & operator-=( int32_t aValue ); 199 | abool & operator-=( uint64_t aValue ); 200 | abool & operator-=( int64_t aValue ); 201 | 202 | /** 203 | * These versions are the same operators, but using the components 204 | * in this package as opposed to the basic data types they are modeled 205 | * after. 206 | */ 207 | abool & operator+=( const abool & aValue ); 208 | abool & operator+=( const auint8_t & aValue ); 209 | abool & operator+=( const aint8_t & aValue ); 210 | abool & operator+=( const auint16_t & aValue ); 211 | abool & operator+=( const aint16_t & aValue ); 212 | abool & operator+=( const auint32_t & aValue ); 213 | abool & operator+=( const aint32_t & aValue ); 214 | abool & operator+=( const auint64_t & aValue ); 215 | abool & operator+=( const aint64_t & aValue ); 216 | 217 | abool & operator-=( const abool & aValue ); 218 | abool & operator-=( const auint8_t & aValue ); 219 | abool & operator-=( const aint8_t & aValue ); 220 | abool & operator-=( const auint16_t & aValue ); 221 | abool & operator-=( const aint16_t & aValue ); 222 | abool & operator-=( const auint32_t & aValue ); 223 | abool & operator-=( const aint32_t & aValue ); 224 | abool & operator-=( const auint64_t & aValue ); 225 | abool & operator-=( const aint64_t & aValue ); 226 | 227 | /******************************************************** 228 | * 229 | * Utility Methods 230 | * 231 | ********************************************************/ 232 | /** 233 | * This method checks to see if the two values are equal to one 234 | * another based on the values they represent and *not* on the 235 | * actual pointers themselves. If they are equal, then this method 236 | * returns true, otherwise it returns false. 237 | */ 238 | bool operator==( const abool & anOther ) const; 239 | /** 240 | * This method checks to see if the two values are not equal to 241 | * one another based on the values they represent and *not* on the 242 | * actual pointers themselves. If they are not equal, then this 243 | * method returns true, otherwise it returns false. 244 | */ 245 | bool operator!=( const abool & anOther ) const; 246 | 247 | /** 248 | * These are the equals and not equals operators for the atomic 249 | * bool w.r.t. the plain data types in the stdint.h package. 250 | */ 251 | bool operator==( bool aValue ) const; 252 | bool operator==( uint8_t aValue ) const; 253 | bool operator==( int8_t aValue ) const; 254 | bool operator==( uint16_t aValue ) const; 255 | bool operator==( int16_t aValue ) const; 256 | bool operator==( uint32_t aValue ) const; 257 | bool operator==( int32_t aValue ) const; 258 | bool operator==( uint64_t aValue ) const; 259 | bool operator==( int64_t aValue ) const; 260 | 261 | bool operator!=( bool aValue ) const; 262 | bool operator!=( uint8_t aValue ) const; 263 | bool operator!=( int8_t aValue ) const; 264 | bool operator!=( uint16_t aValue ) const; 265 | bool operator!=( int16_t aValue ) const; 266 | bool operator!=( uint32_t aValue ) const; 267 | bool operator!=( int32_t aValue ) const; 268 | bool operator!=( uint64_t aValue ) const; 269 | bool operator!=( int64_t aValue ) const; 270 | /** 271 | * These are the equals and not equals operators for the atomic 272 | * bool w.r.t. the classes in this package. This is done to make 273 | * the difference between an atomic and non-atomic version as 274 | * small as possible. 275 | */ 276 | bool operator==( const auint8_t & aValue ) const; 277 | bool operator==( const aint8_t & aValue ) const; 278 | bool operator==( const auint16_t & aValue ) const; 279 | bool operator==( const aint16_t & aValue ) const; 280 | bool operator==( const auint32_t & aValue ) const; 281 | bool operator==( const aint32_t & aValue ) const; 282 | bool operator==( const auint64_t & aValue ) const; 283 | bool operator==( const aint64_t & aValue ) const; 284 | 285 | bool operator!=( const auint8_t & aValue ) const; 286 | bool operator!=( const aint8_t & aValue ) const; 287 | bool operator!=( const auint16_t & aValue ) const; 288 | bool operator!=( const aint16_t & aValue ) const; 289 | bool operator!=( const auint32_t & aValue ) const; 290 | bool operator!=( const aint32_t & aValue ) const; 291 | bool operator!=( const auint64_t & aValue ) const; 292 | bool operator!=( const aint64_t & aValue ) const; 293 | 294 | private: 295 | /** 296 | * This is the actual value - a single unsigned byte that will 297 | * be 0 for false and 1 for true. Simple and easy to use. 298 | */ 299 | volatile uint8_t _value; 300 | }; 301 | 302 | /** 303 | * For debugging purposes, let's make it easy for the user to stream 304 | * out this value. It basically is just the value of the base data type 305 | * and puts it into the stream. 306 | */ 307 | std::ostream & operator<<( std::ostream & aStream, const abool & aValue ); 308 | 309 | #endif // __DKIT_ABOOL_H 310 | -------------------------------------------------------------------------------- /src/adapter.h: -------------------------------------------------------------------------------- 1 | /** 2 | * adapter.h - this file defines the general 'adapter' of two objects: an 3 | * incoming object, TIN, and an outgoing object, TOUT. This is 4 | * all about templates, so what that the objects are depends on 5 | * the user, but the idea is that this class is the base class 6 | * for all things that take one object instances, and produce 7 | * another object instances. This is a fusion of the source and 8 | * sink templates, but no multiple inheritance overhead. 9 | */ 10 | #ifndef __DKIT_ADAPTER_H 11 | #define __DKIT_ADAPTER_H 12 | 13 | // System Headers 14 | #include 15 | #include 16 | #include 17 | 18 | // Third-Party Headers 19 | #include 20 | #include 21 | #include 22 | 23 | // Other Headers 24 | #include "abool.h" 25 | 26 | // Forward Declarations 27 | /** 28 | * We are going to be dealing with sources INTO this object, and sinks 29 | * OUT of this object, so we really need to know what they are so we can 30 | * properly deal with them. 31 | */ 32 | #include "source.h" 33 | #include "sink.h" 34 | 35 | // Public Constants 36 | 37 | // Public Datatypes 38 | 39 | // Public Data Constants 40 | 41 | /** 42 | * Main class definition 43 | */ 44 | namespace dkit { 45 | template class adapter : 46 | public sink, 47 | public source 48 | { 49 | public: 50 | /******************************************************** 51 | * 52 | * Constructors/Destructor 53 | * 54 | ********************************************************/ 55 | /** 56 | * This is the default constructor that sets up the adapter 57 | * with NO listeners, but ready to take on as many as you need. 58 | */ 59 | adapter() : 60 | sink(), 61 | source(), 62 | _name("adapter"), 63 | _mutex(), 64 | _online(true) 65 | { 66 | } 67 | 68 | 69 | /** 70 | * This is the standard copy constructor and needs to be in every 71 | * class to make sure that we don't have too many things running 72 | * around. 73 | */ 74 | adapter( const adapter & anOther ) : 75 | sink(), 76 | source(), 77 | _name("adapter"), 78 | _mutex(), 79 | _online(true) 80 | { 81 | // let the '=' operator do all the heavy lifting 82 | *this = anOther; 83 | } 84 | 85 | 86 | /** 87 | * This is the standard destructor and needs to be virtual to make 88 | * sure that if we subclass off this the right destructor will be 89 | * called. 90 | */ 91 | virtual ~adapter() 92 | { 93 | // remove all the sources for this guy - no dangling pointers 94 | removeAllPublishers(); 95 | // remove all the listeners of this guy - no dangling pointers 96 | removeAllListeners(); 97 | } 98 | 99 | 100 | /** 101 | * When we want to process the result of an equality we need to 102 | * make sure that we do this right by always having an equals 103 | * operator on all classes. 104 | */ 105 | adapter & operator=( const adapter & anOther ) 106 | { 107 | /** 108 | * Make sure that we don't do this to ourselves... 109 | */ 110 | if (this != & anOther) { 111 | // copy over the name - just to be complete 112 | _name = anOther._name; 113 | // for each of his sources, add them into my stuff 114 | BOOST_FOREACH( source *s, anOther.getSources() ) { 115 | addToPublishers(s); 116 | } 117 | // for each of his sinks, add them into my stuff 118 | BOOST_FOREACH( sink *t, anOther.getSinks() ) { 119 | addToListeners(t); 120 | } 121 | // now just copy over the online status 122 | _online = anOther._online; 123 | } 124 | return *this; 125 | } 126 | 127 | 128 | /******************************************************** 129 | * 130 | * Accessor Methods 131 | * 132 | ********************************************************/ 133 | /** 134 | * This method allows the user to set the name of this adapter 135 | * for debugging and tracking purposes. It's not required, and 136 | * there's no requirement that the names are unique, it's just 137 | * a convenience that can be quite useful at times. 138 | */ 139 | virtual void setName( const std::string & aName ) 140 | { 141 | boost::detail::spinlock::scoped_lock lock(_mutex); 142 | _name = aName; 143 | } 144 | 145 | 146 | /** 147 | * This method gets the current name of this adapter so that it 148 | * can be logged, or used in whatever manner desired. There is 149 | * no other significance to the name. 150 | */ 151 | virtual const std::string & getName() const 152 | { 153 | boost::detail::spinlock::scoped_lock lock(_mutex); 154 | return _name; 155 | } 156 | 157 | 158 | /** 159 | * This method is called to add the provided sink as a listener 160 | * to this adapter's list of targets for the send() method. It's 161 | * a one-time registration, meaning, there's no way to get the 162 | * same message twice if you mistakenly attempted to add yourself 163 | * twice. If this is the first registration for this sink, then 164 | * a 'true' will be returned. 165 | */ 166 | virtual bool addToListeners( sink *aSink ) 167 | { 168 | return source::addToListeners(aSink); 169 | } 170 | 171 | 172 | /** 173 | * This method attempts to un-register the provided sink from 174 | * the list of registered listeners for this adapter. If the 175 | * sink is NOT a registered listener, then this method will do 176 | * nothing and return 'false'. Otherwise, the sink will no longer 177 | * receive calls, and a 'true' will be returned. 178 | */ 179 | virtual bool removeFromListeners( sink *aSink ) 180 | { 181 | return source::removeFromListeners(aSink); 182 | } 183 | 184 | 185 | /** 186 | * This method removes ALL the registered sinks from the list 187 | * for this adapter. This effectively puts this adapter "offline" 188 | * for the time-being as there's nothing for him to do. The 189 | * consequence of this might be severe as the incoming events 190 | * might be lost or queue up, depending on the implementation. 191 | */ 192 | virtual void removeAllListeners() 193 | { 194 | source::removeAllListeners(); 195 | } 196 | 197 | 198 | /** 199 | * This method is called to add the provided source as a publisher 200 | * to this adapter's recv() method. It's a one-time registration, 201 | * meaning, there's no way to register the same publisher twice 202 | * if you mistakenly attempted to add yourself twice. If this is 203 | * the first registration for this source, then a 'true' will be 204 | * returned. 205 | */ 206 | virtual bool addToPublishers( source *aSource ) 207 | { 208 | return sink::addToPublishers(aSource); 209 | } 210 | 211 | 212 | /** 213 | * This method attempts to un-register the provided source from 214 | * the list of registered publishers for this adapter. If the 215 | * source is NOT a registered publisher, then this method will do 216 | * nothing and return 'false'. Otherwise, the source will no longer 217 | * send data to this instance, and a 'true' will be returned. 218 | */ 219 | virtual bool removeFromPublishers( source *aSource ) 220 | { 221 | return sink::removeFromPublishers(aSource); 222 | } 223 | 224 | 225 | /** 226 | * This method removes ALL the registered sources from the list 227 | * for this adapter. This effectively puts this sink "offline" 228 | * for the time-being as there's nothing for him to process. 229 | */ 230 | virtual void removeAllPublishers() 231 | { 232 | sink::removeAllPublishers(); 233 | } 234 | 235 | 236 | /** 237 | * This method, and it's convenience methods, are here to allow 238 | * the user to leave the listener/subscriber connections in place, 239 | * but shut down the flow of data from source to sink by taking 240 | * this guy offline. This will simple make the send() a no-op, and 241 | * the caller won't know the difference. It's a clean way to simply 242 | * stop the flow of data. 243 | */ 244 | virtual void setOnline( bool aFlag ) 245 | { 246 | _online = aFlag; 247 | } 248 | 249 | 250 | virtual void takeOnline() 251 | { 252 | _online = true; 253 | } 254 | 255 | 256 | virtual void takeOffline() 257 | { 258 | _online = false; 259 | } 260 | 261 | 262 | /** 263 | * This method is used to see if the adapter is online or not. 264 | * By default, it's online but it's possible that it's been taken 265 | * offline for some reason, and this is a good way to find out. 266 | */ 267 | virtual bool isOnline() const 268 | { 269 | return (bool)_online; 270 | } 271 | 272 | 273 | /******************************************************** 274 | * 275 | * Distribution Methods 276 | * 277 | ********************************************************/ 278 | /** 279 | * This method sends the item out to all the registered users 280 | * of this adapter - one at a time, in no particular order, so 281 | * that they might be able to process/handle this item as best 282 | * they see fit. This is a constant, so the sinks are NOT going 283 | * to be able to modify the data, but they will get the chance 284 | * to copy it, if they wish, and update that copy. 285 | */ 286 | virtual bool send( const TOUT anItem ) 287 | { 288 | return source::send(anItem); 289 | } 290 | 291 | 292 | /******************************************************** 293 | * 294 | * Processing Methods 295 | * 296 | ********************************************************/ 297 | /** 298 | * This method is called when a source has an item to deliver 299 | * to this, a registered listener of that sender. It is up to 300 | * this method to process that WITH ALL HASTE, so that the 301 | * sender can send the SAME item to the next registered listener 302 | * in the set. We can copy this item, but we can't mutate it 303 | * as it's a constant to us. 304 | */ 305 | virtual bool recv( const TIN anItem ) 306 | { 307 | /** 308 | * Here's where we need to process the item and do what we 309 | * need to with it. We need to be quick about it, though, 310 | * as the source is waiting on us to finish, and send the 311 | * same item to all the other registered listeners. 312 | */ 313 | return true; 314 | } 315 | 316 | 317 | /******************************************************** 318 | * 319 | * Utility Methods 320 | * 321 | ********************************************************/ 322 | /** 323 | * There are a lot of times that a human-readable version of 324 | * this instance will come in handy. This is that method. It's 325 | * not necessarily meant to be something to process, but most 326 | * likely what a debugging system would want to write out for 327 | * this guy. 328 | */ 329 | virtual std::string toString() const 330 | { 331 | std::ostringstream msg; 332 | msg << "[adapter '" << _name << "' w/ " 333 | << source::getSinks().size() << " sinks, " 334 | << sink::getSources().size() << " sources]"; 335 | return msg.str(); 336 | } 337 | 338 | 339 | /** 340 | * When we have a custom '==' operator, it's wise to have a hash 341 | * method as well. This makes it much easier to used hash-based 342 | * containers like boost, etc. It's a requirement that if two 343 | * instances are '==', then their hash() methods must be '==' as 344 | * well. 345 | */ 346 | virtual size_t hash() const 347 | { 348 | size_t ans = boost::hash_value(_name); 349 | BOOST_FOREACH( sink *s, source::getSinks() ) { 350 | if (s != NULL) { 351 | boost::hash_combine(ans, s); 352 | } 353 | } 354 | BOOST_FOREACH( source *t, sink::getSources() ) { 355 | if (t != NULL) { 356 | boost::hash_combine(ans, t); 357 | } 358 | } 359 | boost::hash_combine(ans, (bool)_online); 360 | return ans; 361 | } 362 | 363 | 364 | /** 365 | * This method checks to see if the two adapters are equal to one 366 | * another based on the values they represent and *not* on the 367 | * actual pointers themselves. If they are equal, then this method 368 | * returns true, otherwise it returns false. 369 | */ 370 | bool operator==( const adapter & anOther ) const 371 | { 372 | bool equals = false; 373 | if ((this == & anOther) || 374 | ((_name == anOther._name) && 375 | (source::getSinks() == ((source &)anOther).getSinks()) && 376 | (sink::getSources() == ((sink &)anOther).getSources()) && 377 | (_online == anOther._online))) { 378 | equals = true; 379 | } 380 | return equals; 381 | } 382 | 383 | 384 | /** 385 | * This method checks to see if the two adapters are not equal to 386 | * one another based on the values they represent and *not* on the 387 | * actual pointers themselves. If they are not equal, then this 388 | * method returns true, otherwise it returns false. 389 | */ 390 | bool operator!=( const adapter & anOther ) const 391 | { 392 | return !operator==(anOther); 393 | } 394 | 395 | 396 | private: 397 | /** 398 | * There will be times that naming the sources will be very useful. 399 | * For this reason, we'll have a name here - initially just "source", 400 | * but the user can change this as they see fit. It's just a useful 401 | * thing to have when dealng with a great number of sources. 402 | */ 403 | std::string _name; 404 | // ...and a spinlock to protect what's left here in the adapter 405 | mutable boost::detail::spinlock _mutex; 406 | /** 407 | * We can take this source offline, and have it "idle", but 408 | * in order to do that, we need a nice boolean that we can 409 | * flip without any threading issues. This is it. 410 | */ 411 | abool _online; 412 | }; 413 | } // end of namespace dkit 414 | 415 | #endif // __DKIT_ADAPTER_H 416 | -------------------------------------------------------------------------------- /src/atomic.h: -------------------------------------------------------------------------------- 1 | /** 2 | * atomic.h - this file defines the classes of atomic values that mimic the 3 | * datatypes found in the GCC stdint.h header file: the signed 4 | * and unsigned integers of different sizes. These are nice 5 | * because in a mixed 32- and 64-bit environment, these datatypes 6 | * retain their size and max/min values. The classes are to be 7 | * treated like regular datatypes in stdint.h - just when they 8 | * are read/written, the operations are atomic in nature. 9 | */ 10 | #ifndef __DKIT_ATOMIC_H 11 | #define __DKIT_ATOMIC_H 12 | 13 | // System Headers 14 | 15 | // Third-Party Headers 16 | 17 | // Other Headers 18 | #include "abool.h" 19 | #include "aint8.h" 20 | #include "aint16.h" 21 | #include "aint32.h" 22 | #include "aint64.h" 23 | 24 | // Forward Declarations 25 | 26 | // Public Constants 27 | 28 | // Public Datatypes 29 | 30 | // Public Data Constants 31 | 32 | #endif // __DKIT_ATOMIC_H 33 | -------------------------------------------------------------------------------- /src/cqueue.h: -------------------------------------------------------------------------------- 1 | /** 2 | * cqueue.h - this file defines a conflation queue that uses the key_value() 3 | * of the elements placed into this queue as the index for 4 | * conflation. Specifically, if two values with the same key_value() 5 | * are placed in the cqueue, the second will override the first, 6 | * and the location in the queue will be defined by the location 7 | * of the first. This "conflating" is based on the key_value() so 8 | * that updated values of the same "kind" of data can be placed 9 | * into this queue, and only the most recent value will be popped 10 | * off when the time comes. This is all done locklessly with the 11 | * other components of DKit, and is a very useful tool to have. 12 | */ 13 | #ifndef __DKIT_CQUEUE_H 14 | #define __DKIT_CQUEUE_H 15 | 16 | // System Headers 17 | #include 18 | 19 | // Third-Party Headers 20 | 21 | // Other Headers 22 | #include "FIFO.h" 23 | #include "spsc/CircularFIFO.h" 24 | #include "mpsc/CircularFIFO.h" 25 | #include "spmc/CircularFIFO.h" 26 | #include "trie.h" 27 | #include "pool.h" 28 | 29 | // Forward Declarations 30 | 31 | // Public Constants 32 | /** 33 | * We need to have a simple enum for the different "types" of queues that 34 | * we can use for the pool - all based on the complexity of the access. This 35 | * is meant to allow the user to have complete flexibility in how to ask for, 36 | * and recycle items from the pool. 37 | */ 38 | #ifndef __DKIT_QUEUE_TYPE 39 | #define __DKIT_QUEUE_TYPE 40 | namespace dkit { 41 | enum queue_type { 42 | sp_sc = 0, 43 | mp_sc, 44 | sp_mc, 45 | }; 46 | } // end of namespace dkit 47 | #endif // __DKIT_QUEUE_TYPE 48 | 49 | // Public Datatypes 50 | 51 | // Public Data Constants 52 | 53 | 54 | namespace dkit { 55 | /** 56 | * This is the main class definition. The paramteres are as follows: 57 | * T = the type of data to store in the conflation queue 58 | * N = power of 2 for the size of the queue (2^N) 59 | * Q = the type of access the queue has to have (SP/MP & SC/MC) 60 | * KS = the size of the key for the value 'T' 61 | * PN = the power of two of pooled keys to have in reserve (default: 2^17) 62 | */ 63 | template class cqueue : 64 | public FIFO 65 | { 66 | private: 67 | /** 68 | * In order to put the keys into the queue, we need to be able to 69 | * put them into a pool so taht we don't thrash memory too much. 70 | * In order to do that, we need to new and delete the keys. This 71 | * is most easily done if we throw it into a struct, and then just 72 | * use it as a simple C-style struct of the right size. Simple. 73 | */ 74 | struct key_t { 75 | uint8_t bytes[KS]; 76 | /** 77 | * These setters make it much easier to set the value of the 78 | * key based on the different template values that we might 79 | * be getting back from the key_value() function of the 80 | * caller. 81 | */ 82 | void set( uint16_t aValue ) { memcpy(bytes, &aValue, 2); } 83 | void set( uint32_t aValue ) { memcpy(bytes, &aValue, 4); } 84 | void set( uint64_t aValue ) { memcpy(bytes, &aValue, 8); } 85 | void set( uint8_t aValue[] ) { memcpy(bytes, aValue, eKeyBytes); } 86 | }; 87 | 88 | public: 89 | /******************************************************************* 90 | * 91 | * Constructors/Destructor 92 | * 93 | *******************************************************************/ 94 | /** 95 | * This is the default constructor that assumes NOTHING - it just 96 | * makes an empty queue of the requested type, and then it's ready 97 | * to be used by the caller. 98 | */ 99 | cqueue() : 100 | FIFO(), 101 | _queue(NULL), 102 | _map() 103 | { 104 | /** 105 | * We need to look at the 'type' and then create the FIFO 106 | * that makes sense to what he's asking for. The size is the 107 | * power of two (second arg), and will limit how many things 108 | * can be in the pool at once. 109 | */ 110 | switch (Q) { 111 | case sp_sc: 112 | _queue = new spsc::CircularFIFO(); 113 | break; 114 | case mp_sc: 115 | _queue = new mpsc::CircularFIFO(); 116 | break; 117 | case sp_mc: 118 | _queue = new spmc::CircularFIFO(); 119 | break; 120 | } 121 | } 122 | 123 | 124 | /** 125 | * This is the standard copy constructor that needs to be in every 126 | * class to make sure that we control how many copies we have 127 | * floating around in the system. 128 | */ 129 | cqueue( const cqueue & anOther ) : 130 | FIFO(), 131 | _queue(NULL), 132 | _map() 133 | { 134 | // let the '=' operator do the heavy lifting... 135 | *this = anOther; 136 | } 137 | 138 | 139 | /** 140 | * This is the standard destructor and needs to be virtual to make 141 | * sure that if we subclass off this, the right destructor will be 142 | * called. 143 | */ 144 | virtual ~cqueue() 145 | { 146 | /** 147 | * If we have a queue, then we need to know if it's holding 148 | * pointers. If so, then we need to pop every one out and 149 | * then delete each item. It's the only clean way to do it. 150 | */ 151 | if (_queue != NULL) { 152 | // we need to empty the queue of all keys - and delete each 153 | key_t *key = NULL; 154 | while (_queue->pop(key)) { 155 | if (key != NULL) { 156 | delete key; 157 | } 158 | } 159 | // finally, drop the queue itself 160 | delete _queue; 161 | } 162 | } 163 | 164 | 165 | /** 166 | * When we process the result of an equality we need to make sure 167 | * that we do this right by always having an equals operator on 168 | * all classes. 169 | */ 170 | cqueue & operator=( const cqueue & anOther ) 171 | { 172 | if (this != & anOther) { 173 | if (_queue == NULL) { 174 | switch (Q) { 175 | case sp_sc: 176 | _queue = new spsc::CircularFIFO(*(anOther._queue)); 177 | break; 178 | case mp_sc: 179 | _queue = new mpsc::CircularFIFO(*(anOther._queue)); 180 | break; 181 | case sp_mc: 182 | _queue = new spmc::CircularFIFO(*(anOther._queue)); 183 | break; 184 | } 185 | } else { 186 | *_queue = *(anOther._queue); 187 | } 188 | _map = anOther._map; 189 | } 190 | return *this; 191 | } 192 | 193 | 194 | /******************************************************************* 195 | * 196 | * Accessor Methods 197 | * 198 | *******************************************************************/ 199 | /** 200 | * This method takes an item and places it in the queue - if it can. 201 | * If so, then it will return 'true', otherwise, it'll return 'false'. 202 | */ 203 | virtual bool push( const T & anElem ) 204 | { 205 | // check on the existance of the queue 206 | if (_queue == NULL) { 207 | throw std::runtime_error("There is no defined queue for the keys - can't continue"); 208 | } 209 | // see if we need to add the key to the queue 210 | if (!_map.upsert(anElem)) { 211 | // get an empty key from the pool 212 | key_t *key = _pool.next(); 213 | if (key == NULL) { 214 | throw std::runtime_error("Unable to pull a key from the pool - can't continue"); 215 | } 216 | // copy in the value for this element 217 | key->set(key_value(anElem)); 218 | // ...and then save it into the queue in the right place 219 | _queue->push(key); 220 | } 221 | return true; 222 | } 223 | 224 | 225 | /** 226 | * This method updates the passed-in reference with the value on the 227 | * top of the queue - if it can. If so, it'll return the value and 228 | * 'true', but if it can't, as in the queue is empty, then the method 229 | * will return 'false' and the value will be untouched. 230 | */ 231 | virtual bool pop( T & anElem ) 232 | { 233 | bool success = false; 234 | // check on the existance of the queue 235 | if (_queue == NULL) { 236 | throw std::runtime_error("There is no defined queue for the keys - can't continue"); 237 | } 238 | // try to pop a key, and if we can, then extract the value 239 | key_t *key = NULL; 240 | if (_queue->pop(key)) { 241 | success = _map.remove(key->bytes, anElem); 242 | _pool.recycle(key); 243 | } 244 | // return what we got from the trie 245 | return success; 246 | } 247 | 248 | 249 | /** 250 | * This form of the pop() method will throw a std::exception 251 | * if there is nothing to pop, but otherwise, will return the 252 | * the first element on the queue. This is a slightly different 253 | * form that fits a different use-case, and so it's a handy 254 | * thing to have around at times. 255 | */ 256 | virtual T pop() 257 | { 258 | T v; 259 | if (!pop(v)) { 260 | throw std::exception(); 261 | } 262 | return v; 263 | } 264 | 265 | 266 | /** 267 | * If there is an item on the queue, this method will return a look 268 | * at that item without updating the queue. The return value will be 269 | * 'true' if there is something, but 'false' if the queue is empty. 270 | */ 271 | virtual bool peek( T & anElem ) 272 | { 273 | bool success = false; 274 | // check on the existance of the queue 275 | if (_queue == NULL) { 276 | throw std::runtime_error("There is no defined queue for the keys - can't continue"); 277 | } 278 | // try to pop a key, and if we can, then copy the value 279 | key_t *key = NULL; 280 | if (_queue->peek(key)) { 281 | success = _map.get(key->bytes, anElem); 282 | } 283 | // return what we got from the trie 284 | return success; 285 | } 286 | 287 | 288 | /** 289 | * This form of the peek() method is very much like the non-argument 290 | * version of the pop() method. If there is something on the top of 291 | * the queue, this method will return a COPY of it. If not, it will 292 | * throw a std::exception, that needs to be caught. 293 | */ 294 | virtual T peek() 295 | { 296 | T v; 297 | if (!peek(v)) { 298 | throw std::exception(); 299 | } 300 | return v; 301 | } 302 | 303 | 304 | /** 305 | * This method will clear out the contents of the queue so if 306 | * you're storing pointers, then you need to be careful as this 307 | * could leak. 308 | */ 309 | virtual void clear() 310 | { 311 | // we need to empty the queue of all keys - and delete each 312 | if (_queue != NULL) { 313 | key_t *key = NULL; 314 | while (_queue->pop(key)) { 315 | _pool.recycle(key); 316 | } 317 | } 318 | // ...and also empty the trie 319 | _map.clear(); 320 | } 321 | 322 | 323 | /** 324 | * This method will return 'true' if there are no items in the 325 | * queue. Simple. 326 | */ 327 | virtual bool empty() 328 | { 329 | if (_queue != NULL) { 330 | return _queue->empty(); 331 | } 332 | return true; 333 | } 334 | 335 | 336 | /** 337 | * This method will return the total number of items in the 338 | * queue. Since it's possible that multiple threads are adding 339 | * to this queue at any one point in time, it's really at BEST 340 | * a snapshot of the size, and is only completely accurate 341 | * when the queue is stable. 342 | */ 343 | virtual size_t size() const 344 | { 345 | if (_queue != NULL) { 346 | return _queue->size(); 347 | } 348 | return 0; 349 | } 350 | 351 | 352 | /******************************************************** 353 | * 354 | * Functor Methods 355 | * 356 | ********************************************************/ 357 | /** 358 | * This method takes the functor subclass instance and applies 359 | * it's process() method to all the valid entries in the cqueue. 360 | * Since the processing will take place on the Node level, it's 361 | * completely up to the implementor to decide what they want 362 | * to do, and how they want to do it. 363 | */ 364 | bool apply( typename trie::functor & aFunctor ) 365 | { 366 | return _map.apply(aFunctor); 367 | } 368 | 369 | 370 | /******************************************************************* 371 | * 372 | * Utility Methods 373 | * 374 | *******************************************************************/ 375 | /** 376 | * This method checks to see if two queues are equal in their 377 | * contents and not their pointer values. This is how you'd likely 378 | * expect equality to work. 379 | */ 380 | bool operator==( const cqueue & anOther ) const 381 | { 382 | // right now, identity is the only equality we know 383 | return (this == & anOther); 384 | } 385 | 386 | 387 | /** 388 | * This method checks to see if two queues are NOT equal in their 389 | * contents and not their pointer values. This is how you'd likely 390 | * expect equality to work. 391 | */ 392 | bool operator!=( const cqueue & anOther ) const 393 | { 394 | return !operator=(anOther); 395 | } 396 | 397 | private: 398 | /** 399 | * Just to make things clear, we're making an enum for the number 400 | * of bytes in the key for this conflation queue. This will be 401 | * used in the code to know what to make, and how to copy the 402 | * values around. 403 | */ 404 | enum { 405 | eKeyBytes = KS 406 | }; 407 | 408 | /** 409 | * The queue of "keys" based on the style Q, is going 410 | * to be a pointer we create in the constructor and use here. It's 411 | * the same API no matter what Q is, it's just a cleaner way to 412 | * implement the queue. 413 | */ 414 | FIFO *_queue; 415 | /** 416 | * This is the pool of keys that we are going to be using for the 417 | * queue. The pool just makes it faster and easier to get them in 418 | * and out, and we've got a simple structure defined to make the 419 | * allocation and clean up clean and easy. 420 | */ 421 | pool _pool; 422 | /** 423 | * The trie/map to hold the values as they come in. Since this is 424 | * a "map" of sorts, the same keyed value will be placed on top of 425 | * itself as many times as necessary. The old values will be disposed 426 | * of properly by the trie, so we don't need to worry about leaking. 427 | */ 428 | trie _map; 429 | }; 430 | } // end of namespace dkit 431 | 432 | #endif // __DKIT_CQUEUE_H 433 | -------------------------------------------------------------------------------- /src/io/channel.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * channel.cpp - this file implements a simple TCP channel 3 | * class for DKit - something that identifies an 4 | * address and a port for simple TCP traffic. 5 | * This can be used in reading from channels 6 | * and writing to them as well. 7 | * 8 | * Since we have decided to use boost in DKit, it 9 | * makes good sense to utilize it for all we can in 10 | * it's ASIO library. This includes the async reading 11 | * and writing as well as the simpler classes like 12 | * endpoints and sockets. 13 | */ 14 | 15 | // System Headers 16 | 17 | // Third-Party Headers 18 | 19 | // Other Headers 20 | #include "channel.h" 21 | 22 | // Forward Declarations 23 | 24 | // Private Constants 25 | 26 | // Private Datatypes 27 | 28 | // Private Data Constants 29 | 30 | 31 | namespace dkit { 32 | namespace io { 33 | 34 | /** 35 | * In order to allow this object to be a key in the hash-based containers 36 | * of boost, it's essential that we have the following function as it's 37 | * what boost expects from all it's supported classes. 38 | */ 39 | std::size_t hash_value( channel const & aValue ) 40 | { 41 | return aValue.hash(); 42 | } 43 | } // end of namespace io 44 | } // end of namespace dkit 45 | 46 | 47 | /** 48 | * For debugging purposes, let's make it easy for the user to stream 49 | * out this value. It basically is just the value of the base data type 50 | * and puts it into the stream. 51 | */ 52 | std::ostream & operator<<( std::ostream & aStream, const dkit::io::channel & aValue ) 53 | { 54 | aStream << aValue.toString(); 55 | return aStream; 56 | } 57 | -------------------------------------------------------------------------------- /src/io/channel.h: -------------------------------------------------------------------------------- 1 | /** 2 | * channel.h - this file defines a simple TCP point-to-point channel 3 | * class for DKit - something that identifies an 4 | * address and a port for simple TCP traffic. 5 | * This can be used in reading from channels 6 | * and writing to them as well. 7 | * 8 | * Since we have decided to use boost in DKit, it 9 | * makes good sense to utilize it for all we can in 10 | * it's ASIO library. This includes the async reading 11 | * and writing as well as the simpler classes like 12 | * endpoints and sockets. 13 | */ 14 | #ifndef __DKIT_IO_CHANNEL_H 15 | #define __DKIT_IO_CHANNEL_H 16 | 17 | // System Headers 18 | #include 19 | #include 20 | #include 21 | 22 | // Third-Party Headers 23 | #include 24 | #include 25 | 26 | // Other Headers 27 | #include "abool.h" 28 | 29 | // Forward Declarations 30 | 31 | // Public Constants 32 | 33 | // Public Datatypes 34 | 35 | // Public Data Constants 36 | 37 | 38 | using namespace boost::asio::ip; 39 | 40 | namespace dkit { 41 | namespace io { 42 | /** 43 | * This is the main class definition. 44 | */ 45 | struct channel 46 | { 47 | /** 48 | * Since we're using boost, and we're all about a TCP 49 | * channel, it makes sense to use a boost endpoint here to hold 50 | * the details of the address and port as well as a few other 51 | * housekeeping things. 52 | */ 53 | tcp::endpoint endpoint; 54 | /** 55 | * In addition to the endpoint, we typically have some channel 56 | * metadata that helps in the use of this channel in a larger 57 | * context. For instance, in exchange feeds there is a name and 58 | * a primary channel in a group - these things need to be taken 59 | * into account here so that this can be used for exchange feeds. 60 | */ 61 | // this is the simple channel identifier - A, B, etc. 62 | char id; 63 | // this is the boolean saying if this channel is the primary in a group 64 | abool primary; 65 | 66 | public: 67 | /******************************************************************* 68 | * 69 | * Constructors/Destructor 70 | * 71 | *******************************************************************/ 72 | /** 73 | * This is the default constructor that sets up the channel 74 | * with no endpoint or associated metadata. This isn't very good by 75 | * itself, but the values can be set, and this default constructor 76 | * is necessary if we are going to place these into an STL container. 77 | */ 78 | channel() : 79 | endpoint(), 80 | id('\0'), 81 | primary(false) 82 | { 83 | } 84 | 85 | 86 | /** 87 | * This form of the constructor sets up the channel with 88 | * the provided address and port number. The rest of the metadata 89 | * is left in it's default state. 90 | */ 91 | channel( const std::string & anAddr, uint32_t aPort ) : 92 | endpoint(address::from_string(anAddr), aPort), 93 | id('\0'), 94 | primary(false) 95 | { 96 | } 97 | 98 | 99 | /** 100 | * This form of the constructor sets up the channel with 101 | * the provided address and port number embedded in the URL. The 102 | * rest of the metadata is left in it's default state. 103 | */ 104 | channel( const std::string & aURL ) : 105 | endpoint(), 106 | id('\0'), 107 | primary(false) 108 | { 109 | // set the endpoint from the given URL 110 | setEndpoint(aURL); 111 | } 112 | 113 | 114 | /** 115 | * This form of the constructor sets up the channel with 116 | * the provided address and port number. The rest of the metadata 117 | * is included as well - with the 'primary' flag being optional and 118 | * defaulting to 'true'. 119 | */ 120 | channel( const std::string & anAddr, uint32_t aPort, char anID, bool aPrimary = true ) : 121 | endpoint(address::from_string(anAddr), aPort), 122 | id(anID), 123 | primary(aPrimary) 124 | { 125 | } 126 | 127 | 128 | /** 129 | * This form of the constructor sets up the channel with 130 | * the provided address and port number in a URL. The rest of the 131 | * metadata is included as well - with the 'primary' flag being 132 | * optional and defaulting to 'true'. 133 | */ 134 | channel( const std::string & aURL, char anID, bool aPrimary = true ) : 135 | endpoint(), 136 | id(anID), 137 | primary(aPrimary) 138 | { 139 | // set the endpoint from the given URL 140 | setEndpoint(aURL); 141 | } 142 | 143 | 144 | /** 145 | * This is the standard copy constructor and needs to be in every 146 | * class to make sure that we don't have too many things running 147 | * around. 148 | */ 149 | channel( const channel & anOther ) : 150 | endpoint(), 151 | id('\0'), 152 | primary(false) 153 | { 154 | // let the '=' operator do the heavy lifting... 155 | *this = anOther; 156 | } 157 | 158 | 159 | /** 160 | * This is the standard destructor and needs to be virtual to make 161 | * sure that if we subclass off this the right destructor will be 162 | * called. 163 | */ 164 | virtual ~channel() 165 | { 166 | // not much to do here - yet 167 | } 168 | 169 | 170 | /** 171 | * When we want to process the result of an equality we need to 172 | * make sure that we do this right by always having an equals 173 | * operator on all classes. 174 | */ 175 | channel & operator=( const channel & anOther ) 176 | { 177 | if (this != & anOther) { 178 | endpoint = anOther.endpoint; 179 | id = anOther.id; 180 | primary = anOther.primary; 181 | } 182 | return *this; 183 | } 184 | 185 | 186 | /******************************************************************* 187 | * 188 | * Accessor Methods 189 | * 190 | *******************************************************************/ 191 | /** 192 | * This method gets the multicast channel as a URL of the form: 193 | * 'tcp://:' so that it can be used as a human-readable 194 | * version of the endpoint's data. This is primarily for logging and 195 | * such. 196 | */ 197 | std::string getURL() const 198 | { 199 | char url[80]; 200 | bzero(url, 80); 201 | if (!empty()) { 202 | snprintf(url, 79, "tcp://%s:%d", 203 | endpoint.address().to_string().c_str(), 204 | endpoint.port()); 205 | } 206 | return url; 207 | } 208 | 209 | 210 | /** 211 | * This method returns true if the instance is not yet initialized 212 | * to something. This will happen if the default constructor is 213 | * used, and no assignments have been made to the endpoint or id. 214 | */ 215 | bool empty() const 216 | { 217 | return ((endpoint.port() == 0) && (id == '\0')); 218 | } 219 | 220 | 221 | /******************************************************************* 222 | * 223 | * Utility Methods 224 | * 225 | *******************************************************************/ 226 | /** 227 | * There are a lot of times that a human-readable version of 228 | * this instance will come in handy. This is that method. It's 229 | * not necessarily meant to be something to process, but most 230 | * likely what a debugging system would want to write out for 231 | * this guy. 232 | */ 233 | virtual std::string toString() const 234 | { 235 | std::ostringstream msg; 236 | msg << "[channel "; 237 | if (id != '\0') { 238 | msg << id << "="; 239 | } 240 | msg << getURL() << ((bool)primary ? " Primary" : "") << "]"; 241 | return msg.str(); 242 | } 243 | 244 | 245 | /** 246 | * When we have a custom '==' operator, it's wise to have a hash 247 | * method as well. This makes it much easier to used hash-based 248 | * containers like boost, etc. It's a requirement that if two 249 | * instances are '==', then their hash() methods must be '==' as 250 | * well. 251 | */ 252 | size_t hash() const 253 | { 254 | size_t ans = boost::hash_value(endpoint.address().to_string()); 255 | boost::hash_combine(ans, endpoint.port()); 256 | boost::hash_combine(ans, id); 257 | boost::hash_combine(ans, ((bool)primary)); 258 | return ans; 259 | } 260 | 261 | 262 | /** 263 | * This method checks to see if two channels are equal in their 264 | * contents and not their pointer values. This is how you'd likely 265 | * expect equality to work. 266 | */ 267 | bool operator==( const channel & anOther ) const 268 | { 269 | bool equals = false; 270 | 271 | if ((this == & anOther) || 272 | ((endpoint == anOther.endpoint) && 273 | (id == anOther.id) && 274 | (primary == anOther.primary))) { 275 | equals = true; 276 | } 277 | 278 | return equals; 279 | } 280 | 281 | 282 | /** 283 | * This method checks to see if two channels are NOT equal in their 284 | * contents and not their pointer values. This is how you'd likely 285 | * expect equality to work. 286 | */ 287 | bool operator!=( const channel & anOther ) const 288 | { 289 | return !operator==(anOther); 290 | } 291 | 292 | protected: 293 | /******************************************************************* 294 | * 295 | * Accessor Methods 296 | * 297 | *******************************************************************/ 298 | /** 299 | * This method provides the functionality to set the boost::endpoint 300 | * with a single URL - something boost didn't have. We want to be 301 | * able to do this just for the simplicity of specifying a new 302 | * multicast channel. 303 | */ 304 | void setEndpoint( const std::string & aURL ) 305 | { 306 | /** 307 | * We need to parse the URL into the address and port. Start 308 | * by looking for the '://' and only keep what's AFTER that, 309 | * if it's there. 310 | */ 311 | size_t pos = aURL.find("//"); 312 | if (pos != std::string::npos) { 313 | // go past the "//" to the address 314 | pos += 2; 315 | } else { 316 | // the address starts at the first character 317 | pos = 0; 318 | } 319 | // now get the address/port separator 320 | size_t sep = aURL.find(":", pos); 321 | if (sep == std::string::npos) { 322 | throw std::runtime_error("Improperly formatted URL! No port available!"); 323 | } else { 324 | // get the address and port as std::string values 325 | std::string addr = aURL.substr(pos, (sep - pos)); 326 | std::string pstr = aURL.substr(sep+1); 327 | // now set the endpoint's values from these 328 | endpoint.address(address::from_string(addr)); 329 | endpoint.port(atoi(pstr.c_str())); 330 | } 331 | } 332 | }; 333 | 334 | /** 335 | * In order to allow this object to be a key in the hash-based containers 336 | * of boost, it's essential that we have the following function as it's 337 | * what boost expects from all it's supported classes. 338 | */ 339 | std::size_t hash_value( channel const & aValue ); 340 | } // end of namespace io 341 | } // end of namespace dkit 342 | 343 | /** 344 | * For debugging purposes, let's make it easy for the user to stream 345 | * out this value. It basically is just the value of the base data type 346 | * and puts it into the stream. 347 | */ 348 | std::ostream & operator<<( std::ostream & aStream, const dkit::io::channel & aValue ); 349 | 350 | #endif // __DKIT_IO_MULTICAST_CHANNEL_H 351 | -------------------------------------------------------------------------------- /src/io/datagram.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * datagram.cpp - this file implements a simple UDP multicast datagram - that 3 | * atomic block of data read in/written to a UDP Multicast 4 | * channel for processing/transmission. The implementation is 5 | * really quite simple - a block of data, but with the added 6 | * timestamp of when it was read off the OS socket for timing 7 | * purposes. 8 | */ 9 | 10 | // System Headers 11 | 12 | // Third-Party Headers 13 | 14 | // Other Headers 15 | #include "datagram.h" 16 | 17 | // Forward Declarations 18 | 19 | // Private Constants 20 | 21 | // Private Datatypes 22 | 23 | // Private Data Constants 24 | 25 | 26 | namespace dkit { 27 | namespace io { 28 | /** 29 | * In order to allow this object to be a key in the hash-based containers 30 | * of boost, it's essential that we have the following function as it's 31 | * what boost expects from all it's supported classes. 32 | */ 33 | std::size_t hash_value( datagram const & aValue ) 34 | { 35 | return aValue.hash(); 36 | } 37 | } // end of namespace io 38 | } // end of namespace dkit 39 | 40 | 41 | /** 42 | * For debugging purposes, let's make it easy for the user to stream 43 | * out this value. It basically is just the value of the base data type 44 | * and puts it into the stream. 45 | */ 46 | std::ostream & operator<<( std::ostream & aStream, const dkit::io::datagram & aValue ) 47 | { 48 | aStream << aValue.toString(); 49 | return aStream; 50 | } 51 | -------------------------------------------------------------------------------- /src/io/multicast_channel.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * multicast_channel.cpp - this file implements a simple UDP multicast channel 3 | * class for DKit - something that identifies an 4 | * address and a port for simple UDP multicast traffic. 5 | * This can be used in reading from multicast channels 6 | * and writing to them as well. 7 | * 8 | * Since we have decided to use boost in DKit, it 9 | * makes good sense to utilize it for all we can in 10 | * it's ASIO library. This includes the async reading 11 | * and writing as well as the simpler classes like 12 | * endpoints and sockets. 13 | */ 14 | 15 | // System Headers 16 | 17 | // Third-Party Headers 18 | 19 | // Other Headers 20 | #include "multicast_channel.h" 21 | 22 | // Forward Declarations 23 | 24 | // Private Constants 25 | 26 | // Private Datatypes 27 | 28 | // Private Data Constants 29 | 30 | 31 | namespace dkit { 32 | namespace io { 33 | 34 | /** 35 | * In order to allow this object to be a key in the hash-based containers 36 | * of boost, it's essential that we have the following function as it's 37 | * what boost expects from all it's supported classes. 38 | */ 39 | std::size_t hash_value( multicast_channel const & aValue ) 40 | { 41 | return aValue.hash(); 42 | } 43 | } // end of namespace io 44 | } // end of namespace dkit 45 | 46 | 47 | /** 48 | * For debugging purposes, let's make it easy for the user to stream 49 | * out this value. It basically is just the value of the base data type 50 | * and puts it into the stream. 51 | */ 52 | std::ostream & operator<<( std::ostream & aStream, const dkit::io::multicast_channel & aValue ) 53 | { 54 | aStream << aValue.toString(); 55 | return aStream; 56 | } 57 | -------------------------------------------------------------------------------- /src/io/multicast_channel.h: -------------------------------------------------------------------------------- 1 | /** 2 | * multicast_channel.h - this file defines a simple UDP multicast channel 3 | * class for DKit - something that identifies an 4 | * address and a port for simple UDP multicast traffic. 5 | * This can be used in reading from multicast channels 6 | * and writing to them as well. 7 | * 8 | * Since we have decided to use boost in DKit, it 9 | * makes good sense to utilize it for all we can in 10 | * it's ASIO library. This includes the async reading 11 | * and writing as well as the simpler classes like 12 | * endpoints and sockets. 13 | */ 14 | #ifndef __DKIT_IO_MULTICAST_CHANNEL_H 15 | #define __DKIT_IO_MULTICAST_CHANNEL_H 16 | 17 | // System Headers 18 | #include 19 | #include 20 | #include 21 | 22 | // Third-Party Headers 23 | #include 24 | #include 25 | 26 | // Other Headers 27 | #include "abool.h" 28 | 29 | // Forward Declarations 30 | 31 | // Public Constants 32 | 33 | // Public Datatypes 34 | 35 | // Public Data Constants 36 | 37 | 38 | using namespace boost::asio::ip; 39 | 40 | namespace dkit { 41 | namespace io { 42 | /** 43 | * This is the main class definition. 44 | */ 45 | struct multicast_channel 46 | { 47 | /** 48 | * Since we're using boost, and we're all about a UDP multicast 49 | * channel, it makes sense to use a boost endpoint here to hold 50 | * the details of the address and port as well as a few other 51 | * housekeeping things. 52 | */ 53 | udp::endpoint endpoint; 54 | /** 55 | * In addition to the endpoint, we typically have some channel 56 | * metadata that helps in the use of this channel in a larger 57 | * context. For instance, in exchange feeds there is a name and 58 | * a primary channel in a group - these things need to be taken 59 | * into account here so that this can be used for exchange feeds. 60 | */ 61 | // this is the simple channel identifier - A, B, etc. 62 | char id; 63 | // this is the boolean saying if this channel is the primary in a group 64 | abool primary; 65 | 66 | public: 67 | /******************************************************************* 68 | * 69 | * Constructors/Destructor 70 | * 71 | *******************************************************************/ 72 | /** 73 | * This is the default constructor that sets up the multicast channel 74 | * with no endpoint or associated metadata. This isn't very good by 75 | * itself, but the values can be set, and this default constructor 76 | * is necessary if we are going to place these into an STL container. 77 | */ 78 | multicast_channel() : 79 | endpoint(), 80 | id('\0'), 81 | primary(false) 82 | { 83 | } 84 | 85 | 86 | /** 87 | * This form of the constructor sets up the multicast channel with 88 | * the provided address and port number. The rest of the metadata 89 | * is left in it's default state. 90 | */ 91 | multicast_channel( const std::string & anAddr, uint32_t aPort ) : 92 | endpoint(address::from_string(anAddr), aPort), 93 | id('\0'), 94 | primary(false) 95 | { 96 | } 97 | 98 | 99 | /** 100 | * This form of the constructor sets up the multicast channel with 101 | * the provided address and port number embedded in the URL. The 102 | * rest of the metadata is left in it's default state. 103 | */ 104 | multicast_channel( const std::string & aURL ) : 105 | endpoint(), 106 | id('\0'), 107 | primary(false) 108 | { 109 | // set the endpoint from the given URL 110 | setEndpoint(aURL); 111 | } 112 | 113 | 114 | /** 115 | * This form of the constructor sets up the multicast channel with 116 | * the provided address and port number. The rest of the metadata 117 | * is included as well - with the 'primary' flag being optional and 118 | * defaulting to 'true'. 119 | */ 120 | multicast_channel( const std::string & anAddr, uint32_t aPort, char anID, bool aPrimary = true ) : 121 | endpoint(address::from_string(anAddr), aPort), 122 | id(anID), 123 | primary(aPrimary) 124 | { 125 | } 126 | 127 | 128 | /** 129 | * This form of the constructor sets up the multicast channel with 130 | * the provided address and port number in a URL. The rest of the 131 | * metadata is included as well - with the 'primary' flag being 132 | * optional and defaulting to 'true'. 133 | */ 134 | multicast_channel( const std::string & aURL, char anID, bool aPrimary = true ) : 135 | endpoint(), 136 | id(anID), 137 | primary(aPrimary) 138 | { 139 | // set the endpoint from the given URL 140 | setEndpoint(aURL); 141 | } 142 | 143 | 144 | /** 145 | * This is the standard copy constructor and needs to be in every 146 | * class to make sure that we don't have too many things running 147 | * around. 148 | */ 149 | multicast_channel( const multicast_channel & anOther ) : 150 | endpoint(), 151 | id('\0'), 152 | primary(false) 153 | { 154 | // let the '=' operator do the heavy lifting... 155 | *this = anOther; 156 | } 157 | 158 | 159 | /** 160 | * This is the standard destructor and needs to be virtual to make 161 | * sure that if we subclass off this the right destructor will be 162 | * called. 163 | */ 164 | virtual ~multicast_channel() 165 | { 166 | // not much to do here - yet 167 | } 168 | 169 | 170 | /** 171 | * When we want to process the result of an equality we need to 172 | * make sure that we do this right by always having an equals 173 | * operator on all classes. 174 | */ 175 | multicast_channel & operator=( const multicast_channel & anOther ) 176 | { 177 | if (this != & anOther) { 178 | endpoint = anOther.endpoint; 179 | id = anOther.id; 180 | primary = anOther.primary; 181 | } 182 | return *this; 183 | } 184 | 185 | 186 | /******************************************************************* 187 | * 188 | * Accessor Methods 189 | * 190 | *******************************************************************/ 191 | /** 192 | * This method gets the multicast channel as a URL of the form: 193 | * 'udp://:' so that it can be used as a human-readable 194 | * version of the endpoint's data. This is primarily for logging and 195 | * such. 196 | */ 197 | std::string getURL() const 198 | { 199 | char url[80]; 200 | bzero(url, 80); 201 | if (!empty()) { 202 | snprintf(url, 79, "udp://%s:%d", 203 | endpoint.address().to_string().c_str(), 204 | endpoint.port()); 205 | } 206 | return url; 207 | } 208 | 209 | 210 | /** 211 | * This method returns true if the instance is not yet initialized 212 | * to something. This will happen if the default constructor is 213 | * used, and no assignments have been made to the endpoint or id. 214 | */ 215 | bool empty() const 216 | { 217 | return ((endpoint.port() == 0) && (id == '\0')); 218 | } 219 | 220 | 221 | /******************************************************************* 222 | * 223 | * Utility Methods 224 | * 225 | *******************************************************************/ 226 | /** 227 | * There are a lot of times that a human-readable version of 228 | * this instance will come in handy. This is that method. It's 229 | * not necessarily meant to be something to process, but most 230 | * likely what a debugging system would want to write out for 231 | * this guy. 232 | */ 233 | virtual std::string toString() const 234 | { 235 | std::ostringstream msg; 236 | msg << "[multicast_channel "; 237 | if (id != '\0') { 238 | msg << id << "="; 239 | } 240 | msg << getURL() << ((bool)primary ? " Primary" : "") << "]"; 241 | return msg.str(); 242 | } 243 | 244 | 245 | /** 246 | * When we have a custom '==' operator, it's wise to have a hash 247 | * method as well. This makes it much easier to used hash-based 248 | * containers like boost, etc. It's a requirement that if two 249 | * instances are '==', then their hash() methods must be '==' as 250 | * well. 251 | */ 252 | size_t hash() const 253 | { 254 | size_t ans = boost::hash_value(endpoint.address().to_string()); 255 | boost::hash_combine(ans, endpoint.port()); 256 | boost::hash_combine(ans, id); 257 | boost::hash_combine(ans, ((bool)primary)); 258 | return ans; 259 | } 260 | 261 | 262 | /** 263 | * This method checks to see if two channels are equal in their 264 | * contents and not their pointer values. This is how you'd likely 265 | * expect equality to work. 266 | */ 267 | bool operator==( const multicast_channel & anOther ) const 268 | { 269 | bool equals = false; 270 | 271 | if ((this == & anOther) || 272 | ((endpoint == anOther.endpoint) && 273 | (id == anOther.id) && 274 | (primary == anOther.primary))) { 275 | equals = true; 276 | } 277 | 278 | return equals; 279 | } 280 | 281 | 282 | /** 283 | * This method checks to see if two channels are NOT equal in their 284 | * contents and not their pointer values. This is how you'd likely 285 | * expect equality to work. 286 | */ 287 | bool operator!=( const multicast_channel & anOther ) const 288 | { 289 | return !operator==(anOther); 290 | } 291 | 292 | protected: 293 | /******************************************************************* 294 | * 295 | * Accessor Methods 296 | * 297 | *******************************************************************/ 298 | /** 299 | * This method provides the functionality to set the boost::endpoint 300 | * with a single URL - something boost didn't have. We want to be 301 | * able to do this just for the simplicity of specifying a new 302 | * multicast channel. 303 | */ 304 | void setEndpoint( const std::string & aURL ) 305 | { 306 | /** 307 | * We need to parse the URL into the address and port. Start 308 | * by looking for the '://' and only keep what's AFTER that, 309 | * if it's there. 310 | */ 311 | size_t pos = aURL.find("//"); 312 | if (pos != std::string::npos) { 313 | // go past the "//" to the address 314 | pos += 2; 315 | } else { 316 | // the address starts at the first character 317 | pos = 0; 318 | } 319 | // now get the address/port separator 320 | size_t sep = aURL.find(":", pos); 321 | if (sep == std::string::npos) { 322 | throw std::runtime_error("Improperly formatted URL! No port available!"); 323 | } else { 324 | // get the address and port as std::string values 325 | std::string addr = aURL.substr(pos, (sep - pos)); 326 | std::string pstr = aURL.substr(sep+1); 327 | // now set the endpoint's values from these 328 | endpoint.address(address::from_string(addr)); 329 | endpoint.port(atoi(pstr.c_str())); 330 | } 331 | } 332 | }; 333 | 334 | /** 335 | * In order to allow this object to be a key in the hash-based containers 336 | * of boost, it's essential that we have the following function as it's 337 | * what boost expects from all it's supported classes. 338 | */ 339 | std::size_t hash_value( multicast_channel const & aValue ); 340 | } // end of namespace io 341 | } // end of namespace dkit 342 | 343 | /** 344 | * For debugging purposes, let's make it easy for the user to stream 345 | * out this value. It basically is just the value of the base data type 346 | * and puts it into the stream. 347 | */ 348 | std::ostream & operator<<( std::ostream & aStream, const dkit::io::multicast_channel & aValue ); 349 | 350 | #endif // __DKIT_IO_MULTICAST_CHANNEL_H 351 | -------------------------------------------------------------------------------- /src/mpsc/CircularFIFO.h: -------------------------------------------------------------------------------- 1 | /** 2 | * CircularFIFO.h - this file defines the template class for a multi-producer, 3 | * single-consumer, circular FIFO queue with a size initially 4 | * specified in the definition of the instance. This queue 5 | * is completely thread-safe so long as there is ONE and ONLY 6 | * ONE thread removing elements from this container, as there 7 | * can be as many as necessary placing them into the queue. 8 | * The syntax is very simple - push() will push an element, 9 | * returning 'true' if there is room, and the value has been 10 | * placed in the queue. 11 | * 12 | * The peek() method allows the caller to see the element on 13 | * the top of the queue, and it HAS to be the same thread as 14 | * the SINGLE consumer, but the value stays on the queue. 15 | * The pop() will return true if there's something to pop 16 | * off the queue. 17 | */ 18 | #ifndef __DKIT_MPSC_CIRCULARFIFO_H 19 | #define __DKIT_MPSC_CIRCULARFIFO_H 20 | 21 | // System Headers 22 | #include 23 | 24 | // Third-Party Headers 25 | 26 | // Other Headers 27 | #include "FIFO.h" 28 | 29 | // Forward Declarations 30 | 31 | // Public Constants 32 | 33 | // Public Datatypes 34 | 35 | // Public Data Constants 36 | 37 | 38 | 39 | namespace dkit { 40 | namespace mpsc { 41 | /** 42 | * This is the main class definition 43 | */ 44 | template class CircularFIFO : 45 | public FIFO 46 | { 47 | public: 48 | /******************************************************************* 49 | * 50 | * Constructors/Destructor 51 | * 52 | *******************************************************************/ 53 | /** 54 | * This is the default constructor that assumes NOTHING - it just 55 | * makes a simple queue ready to hold things. 56 | */ 57 | CircularFIFO() : 58 | FIFO(), 59 | _elements(), 60 | _head(0), 61 | _tail(0) 62 | { 63 | } 64 | 65 | 66 | /** 67 | * This is the standard copy constructor that needs to be in every 68 | * class to make sure that we control how many copies we have 69 | * floating around in the system. 70 | */ 71 | CircularFIFO( const CircularFIFO & anOther ) : 72 | FIFO(), 73 | _elements(), 74 | _head(0), 75 | _tail(0) 76 | { 77 | // let the '=' operator do the heavy lifting... 78 | *this = anOther; 79 | } 80 | 81 | 82 | /** 83 | * This is the standard destructor and needs to be virtual to make 84 | * sure that if we subclass off this, the right destructor will be 85 | * called. 86 | */ 87 | virtual ~CircularFIFO() 88 | { 89 | clear(); 90 | } 91 | 92 | 93 | /** 94 | * When we process the result of an equality we need to make sure 95 | * that we do this right by always having an equals operator on 96 | * all classes. 97 | * 98 | * Because of the multi-producer, single-consumer, nature of 99 | * this class, it is IMPOSSIBLE to have the assignment operator 100 | * be thread-safe without a mutex. This defeats the entire 101 | * purpose, so what we have is a non-thread-safe assignment 102 | * operator that is still useful if care is exercised to make 103 | * sure that no use-case exists where there will be readers or 104 | * writers to this queue while it is being copied in this assignment. 105 | */ 106 | CircularFIFO & operator=( const CircularFIFO & anOther ) 107 | { 108 | if (this != & anOther) { 109 | // now let's copy in the elements one by one 110 | for (size_t i = 0; i < eSize; i++) { 111 | _elements[i] = anOther._elements[i]; 112 | } 113 | // now copy the pointers 114 | _head = anOther._head; 115 | _tail = anOther._tail; 116 | } 117 | return *this; 118 | } 119 | 120 | 121 | /******************************************************************* 122 | * 123 | * Accessor Methods 124 | * 125 | *******************************************************************/ 126 | /** 127 | * This pair of methods does what you'd expect - it returns the 128 | * length of the queue as it exists at the present time. It's 129 | * got two names because there are so many different kinds of 130 | * implementations that it's often convenient to use one or the 131 | * other to remain consistent. 132 | */ 133 | virtual size_t size() const 134 | { 135 | size_t sz = _tail - _head; 136 | // see if we wrapped around - and correct the unsigned math 137 | if (sz > eSize) { 138 | sz = (sz + eSize) & eMask; 139 | } 140 | return sz; 141 | } 142 | 143 | 144 | virtual size_t length() const 145 | { 146 | return size(); 147 | } 148 | 149 | 150 | /** 151 | * This method returns the current capacity of the vector and 152 | * is NOT the size per se. The capacity is what this queue 153 | * will hold. 154 | */ 155 | virtual size_t capacity() const 156 | { 157 | return eSize; 158 | } 159 | 160 | 161 | /******************************************************** 162 | * 163 | * Element Accessing Methods 164 | * 165 | ********************************************************/ 166 | /** 167 | * This method takes an item and places it in the queue - if it can. 168 | * If so, then it will return 'true', otherwise, it'll return 'false'. 169 | */ 170 | virtual bool push( const T & anElem ) 171 | { 172 | bool error = false; 173 | 174 | // move the tail (insertion point) and get the old value for me 175 | size_t insPt = __sync_fetch_and_add(&_tail, 1) & eMask; 176 | // see if we've crashed the queue all the way around... 177 | if (_elements[insPt].valid) { 178 | // try to back out the damage we've done to the tail 179 | __sync_sub_and_fetch(&_tail, 1); 180 | error = true; 181 | } else { 182 | // save the data in the right spot and flag it as valid 183 | const_cast(_elements[insPt].value) = anElem; 184 | _elements[insPt].valid = true; 185 | } 186 | 187 | return !error; 188 | } 189 | 190 | 191 | /** 192 | * This method updates the passed-in reference with the value on the 193 | * top of the queue - if it can. If so, it'll return the value and 194 | * 'true', but if it can't, as in the queue is empty, then the method 195 | * will return 'false' and the value will be untouched. 196 | */ 197 | virtual bool pop( T & anElem ) 198 | { 199 | bool error = false; 200 | 201 | // see if we have an empty queue... 202 | if (!_elements[_head & eMask].valid) { 203 | error = true; 204 | } else { 205 | /** 206 | * We need to get the current _head, and post-fetch increment 207 | * it, and then pull out the value from the head and then 208 | * invalidate the location in the queue. This looks a little 209 | * off until you remember that the fetch is done BEFORE the 210 | * adding of the 1 to _head. 211 | */ 212 | size_t grab = __sync_fetch_and_add(&_head, 1) & eMask; 213 | anElem = const_cast(_elements[grab].value); 214 | _elements[grab].valid = false; 215 | } 216 | 217 | return !error; 218 | } 219 | 220 | 221 | /** 222 | * This form of the pop() method will throw a std::exception 223 | * if there is nothing to pop, but otherwise, will return the 224 | * the first element on the queue. This is a slightly different 225 | * form that fits a different use-case, and so it's a handy 226 | * thing to have around at times. 227 | */ 228 | virtual T pop() 229 | { 230 | T v; 231 | if (!pop(v)) { 232 | throw std::exception(); 233 | } 234 | return v; 235 | } 236 | 237 | 238 | /** 239 | * If there is an item on the queue, this method will return a look 240 | * at that item without updating the queue. The return value will be 241 | * 'true' if there is something, but 'false' if the queue is empty. 242 | */ 243 | virtual bool peek( T & anElem ) 244 | { 245 | bool error = false; 246 | 247 | // see if we have an empty queue... 248 | if (!_elements[_head & eMask].valid) { 249 | error = true; 250 | } else { 251 | anElem = const_cast(_elements[_head & eMask].value); 252 | } 253 | 254 | return !error; 255 | } 256 | 257 | 258 | /** 259 | * This form of the peek() method is very much like the non-argument 260 | * version of the pop() method. If there is something on the top of 261 | * the queue, this method will return a COPY of it. If not, it will 262 | * throw a std::exception, that needs to be caught. 263 | */ 264 | virtual T peek() 265 | { 266 | T v; 267 | if (!peek(v)) { 268 | throw std::exception(); 269 | } 270 | return v; 271 | } 272 | 273 | 274 | /** 275 | * This method will clear out the contents of the queue so if 276 | * you're storing pointers, then you need to be careful as this 277 | * could leak. 278 | */ 279 | virtual void clear() 280 | { 281 | T v; 282 | while (pop(v)); 283 | } 284 | 285 | 286 | /** 287 | * This method will return 'true' if there are no items in the 288 | * queue. Simple. 289 | */ 290 | virtual bool empty() 291 | { 292 | return (_head == _tail); 293 | } 294 | 295 | 296 | /******************************************************************* 297 | * 298 | * Utility Methods 299 | * 300 | *******************************************************************/ 301 | /** 302 | * Traditionally, this operator would look at the elements in this 303 | * queue, compare them to the elements in the argument queue, and 304 | * see if they are the same. The problem with this is that in a 305 | * single-producer, single-consumer system, there's no thread that 306 | * can actually perform this operation in a thread-safe manner. 307 | * 308 | * Moreover, the complexity in doing this is far more than we want to 309 | * take on in this class. It's lightweight, and while it's certainly 310 | * possible to make a good equals operator, it's not worth the cost 311 | * at this time. This method will always return 'false'. 312 | */ 313 | bool operator==( const CircularFIFO & anOther ) const 314 | { 315 | return false; 316 | } 317 | 318 | 319 | /** 320 | * Traditionally, this operator would look at the elements in this 321 | * queue, compare them to the elements in the argument queue, and 322 | * see if they are NOT the same. The problem with this is that in a 323 | * single-producer, single-consumer system, there's no thread that 324 | * can actually perform this operation in a thread-safe manner. 325 | * 326 | * Moreover, the complexity in doing this is far more than we want to 327 | * take on in this class. It's lightweight, and while it's certainly 328 | * possible to make a good not-equals operator, it's not worth the cost 329 | * at this time. This method will always return 'true'. 330 | */ 331 | bool operator!=( const CircularFIFO & anOther ) const 332 | { 333 | return !operator==(anOther); 334 | } 335 | 336 | 337 | private: 338 | /** 339 | * Since the size of the queue is in the definition of the 340 | * instance, it's possible to make some very simple enums that 341 | * are the size and the masking bits for the index values, so that 342 | * we know before anything starts up, how big to make things and 343 | * how to "wrap around" when the time comes. 344 | */ 345 | enum { 346 | eMask = ((1 << N) - 1), 347 | eSize = (1 << N) 348 | }; 349 | 350 | /** 351 | * In order to simplify the ring buffer access, I'm going to actually 352 | * have the ring a series of 'nodes', and for each, there will be a 353 | * value and a valid 'flag'. If the flag isn't true, then the value 354 | * in the node isn't valid. We'll use this to decouple the push() 355 | * and pop() so that each only needs to know it's own location and 356 | * then interrogate the node for state. 357 | */ 358 | struct Node { 359 | T value; 360 | bool valid; 361 | 362 | Node() : value(), valid(false) { } 363 | Node( const T & aValue ) : value(aValue), valid(true) { } 364 | ~Node() { } 365 | }; 366 | 367 | /** 368 | * We have a very simple structure - an array of values of a fixed 369 | * size and a simple head and tail. 370 | */ 371 | volatile Node _elements[eSize]; 372 | volatile size_t _head; 373 | volatile size_t _tail; 374 | }; 375 | } // end of namespace mpsc 376 | } // end of namespace dkit 377 | 378 | #endif // __DKIT_MPSC_CIRCULARFIFO_H 379 | -------------------------------------------------------------------------------- /src/mpsc/LinkedFIFO.h: -------------------------------------------------------------------------------- 1 | /** 2 | * LinkedFIFO.h - this file defines a multi-producer, single-consumer, linked 3 | * FIFO queue and is using the compare-and-swap to achieve 4 | * the lock-free goal. There can be any number of threads that 5 | * call the push() methods, but there can be ONE and ONLY ONE 6 | * thread that calls the pop() methods. 7 | */ 8 | #ifndef __DKIT_MPSC_LINKEDFIFO_H 9 | #define __DKIT_MPSC_LINKEDFIFO_H 10 | 11 | // System Headers 12 | 13 | // Third-Party Headers 14 | 15 | // Other Headers 16 | #include "FIFO.h" 17 | 18 | // Forward Declarations 19 | 20 | // Public Constants 21 | 22 | // Public Datatypes 23 | 24 | // Public Data Constants 25 | 26 | 27 | namespace dkit { 28 | namespace mpsc { 29 | /** 30 | * This is the main class definition 31 | */ 32 | template class LinkedFIFO : 33 | public FIFO 34 | { 35 | public: 36 | /******************************************************************* 37 | * 38 | * Constructors/Destructor 39 | * 40 | *******************************************************************/ 41 | /** 42 | * This is the default constructor that assumes NOTHING - it just 43 | * makes a simple queue ready to hold instances of T. 44 | */ 45 | LinkedFIFO() : 46 | FIFO(), 47 | _head(NULL), 48 | _tail(NULL) 49 | { 50 | // start with my first (empty) node 51 | _head = new Node(); 52 | _tail = _head; 53 | } 54 | 55 | 56 | /** 57 | * This is the standard copy constructor that needs to be in every 58 | * class to make sure that we control how many copies we have 59 | * floating around in the system. 60 | */ 61 | LinkedFIFO( const LinkedFIFO & anOther ) : 62 | FIFO(), 63 | _head(NULL), 64 | _tail(NULL) 65 | { 66 | // let the '=' operator do the heavy lifting... 67 | *this = anOther; 68 | } 69 | 70 | 71 | /** 72 | * This is the standard destructor and needs to be virtual to make 73 | * sure that if we subclass off this, the right destructor will be 74 | * called. 75 | */ 76 | virtual ~LinkedFIFO() 77 | { 78 | clear(); 79 | // now clean out the last node (empty) in the list 80 | if (_head != NULL) { 81 | delete _head; 82 | _head = NULL; 83 | } 84 | } 85 | 86 | 87 | /** 88 | * When we process the result of an equality we need to make sure 89 | * that we do this right by always having an equals operator on 90 | * all classes. 91 | * 92 | * Because of the multiple-producer, single-consumer, nature of 93 | * this class, it is IMPOSSIBLE to have the assignment operator 94 | * be thread-safe without a mutex. This defeats the entire 95 | * purpose, so what we have is a non-thread-safe assignment 96 | * operator that is still useful if care is exercised to make 97 | * sure that no use-case exists where there will be readers or 98 | * writers to this queue while it is being copied in this assignment. 99 | */ 100 | LinkedFIFO & operator=( const LinkedFIFO & anOther ) 101 | { 102 | if (this != & anOther) { 103 | /** 104 | * Assuming the argument is stable, then we're just going 105 | * to walk it, pushing on the values from it in the right 106 | * order so they are appended to us. 107 | */ 108 | for (Node *n = anOther._head->next; n != NULL; n = n->next) { 109 | if (!push(n->value)) { 110 | break; 111 | } 112 | } 113 | } 114 | return *this; 115 | } 116 | 117 | 118 | /******************************************************************* 119 | * 120 | * Accessor Methods 121 | * 122 | *******************************************************************/ 123 | /** 124 | * This method takes an item and places it in the queue - if it can. 125 | * If so, then it will return 'true', otherwise, it'll return 'false'. 126 | */ 127 | virtual bool push( const T & anElem ) 128 | { 129 | bool error = false; 130 | 131 | // create a new Node with the provided value 132 | Node *me = new Node(anElem); 133 | if (me == NULL) { 134 | error = true; 135 | } else { 136 | /** 137 | * We need to add the new value to the tail and then link it 138 | * back into the list. Not too bad. 139 | */ 140 | // put in the new tail, and get the old one 141 | Node *oldTail = _tail; 142 | while (!__sync_bool_compare_and_swap(&_tail, oldTail, me)) { 143 | oldTail = _tail; 144 | } 145 | // OK, make sure that the list remains intact 146 | if (oldTail != NULL) { 147 | oldTail->next = me; 148 | } 149 | } 150 | 151 | return !error; 152 | } 153 | 154 | 155 | /** 156 | * This method updates the passed-in reference with the value on the 157 | * top of the queue - if it can. If so, it'll return the value and 158 | * 'true', but if it can't, as in the queue is empty, then the method 159 | * will return 'false' and the value will be untouched. 160 | */ 161 | virtual bool pop( T & anElem ) 162 | { 163 | bool error = false; 164 | 165 | // if the next guy is NULL, we're empty 166 | if (__sync_bool_compare_and_swap(&(_head->next), NULL, NULL)) { 167 | error = true; 168 | } else { 169 | // move the head to the next Node and get the value 170 | Node *oldHead = __sync_val_compare_and_swap(&_head, _head, _head->next); 171 | anElem = _head->value; 172 | // if there is an oldHead (should be), delete it now 173 | if (oldHead != NULL) { 174 | delete oldHead; 175 | } 176 | } 177 | 178 | return !error; 179 | } 180 | 181 | 182 | /** 183 | * This form of the pop() method will throw a std::exception 184 | * if there is nothing to pop, but otherwise, will return the 185 | * the first element on the queue. This is a slightly different 186 | * form that fits a different use-case, and so it's a handy 187 | * thing to have around at times. 188 | */ 189 | virtual T pop() 190 | { 191 | T v; 192 | if (!pop(v)) { 193 | throw std::exception(); 194 | } 195 | return v; 196 | } 197 | 198 | 199 | /** 200 | * If there is an item on the queue, this method will return a look 201 | * at that item without updating the queue. The return value will be 202 | * 'true' if there is something, but 'false' if the queue is empty. 203 | */ 204 | virtual bool peek( T & anElem ) 205 | { 206 | bool error = false; 207 | 208 | // if the next guy is NULL, we're empty 209 | if (__sync_bool_compare_and_swap(&(_head->next), NULL, NULL)) { 210 | error = true; 211 | } else { 212 | // look at the next valid node to get the next value 213 | anElem = _head->next->value; 214 | } 215 | 216 | return !error; 217 | } 218 | 219 | 220 | /** 221 | * This form of the peek() method is very much like the non-argument 222 | * version of the pop() method. If there is something on the top of 223 | * the queue, this method will return a COPY of it. If not, it will 224 | * throw a std::exception, that needs to be caught. 225 | */ 226 | virtual T peek() 227 | { 228 | T v; 229 | if (!peek(v)) { 230 | throw std::exception(); 231 | } 232 | return v; 233 | } 234 | 235 | 236 | /** 237 | * This method will clear out the contents of the queue so if 238 | * you're storing pointers, then you need to be careful as this 239 | * could leak. 240 | */ 241 | virtual void clear() 242 | { 243 | // pretty simple - just pop everything off the queue 244 | T v; 245 | while (pop(v)); 246 | } 247 | 248 | 249 | /** 250 | * This method will return 'true' if there are no items in the 251 | * queue. Simple. 252 | */ 253 | virtual bool empty() 254 | { 255 | return (__sync_bool_compare_and_swap(&_head->next, NULL, NULL)); 256 | } 257 | 258 | 259 | /** 260 | * This method will return the total number of items in the 261 | * queue. Since it's possible that multiple threads are adding 262 | * to this queue at any one point in time, it's really at BEST 263 | * a snapshot of the size, and is only completely accurate 264 | * when the queue is stable. 265 | */ 266 | virtual size_t size() const 267 | { 268 | size_t retval = 0; 269 | for (Node *n = _head->next; n != NULL; n = n->next) { 270 | ++retval; 271 | } 272 | return retval; 273 | } 274 | 275 | 276 | /******************************************************************* 277 | * 278 | * Utility Methods 279 | * 280 | *******************************************************************/ 281 | /** 282 | * This method checks to see if two queues are equal in their 283 | * contents and not their pointer values. This is how you'd likely 284 | * expect equality to work. 285 | * 286 | * Once again, since it's possible that there are multiple threads 287 | * adding element to BOTH queues, this is only really going to be 288 | * accutate when BOTH queues are stable and not undergoing change. 289 | * Only then, can we be assured of the stability of the contents. 290 | */ 291 | bool operator==( const LinkedFIFO & anOther ) const 292 | { 293 | bool equals = true; 294 | 295 | // next, check the elements for equality 296 | if (equals) { 297 | Node *me = _head; 298 | Node *him = anOther._head; 299 | while (me != NULL) { 300 | if ((him == NULL) || (me->value != him->value)) { 301 | equals = false; 302 | break; 303 | } 304 | me = me->next; 305 | him = him->next; 306 | } 307 | } 308 | 309 | return equals; 310 | } 311 | 312 | 313 | /** 314 | * This method checks to see if two queues are NOT equal in their 315 | * contents and not their pointer values. This is how you'd likely 316 | * expect equality to work. 317 | */ 318 | bool operator!=( const LinkedFIFO & anOther ) const 319 | { 320 | return !operator=(anOther); 321 | } 322 | 323 | private: 324 | /** 325 | * My linked list will be made of these nodes, and I'll make them 326 | * with the one-argument constructor that just sets the value and 327 | * nulls out the next pointer. Pretty simple. 328 | */ 329 | struct Node { 330 | T value; 331 | Node *next; 332 | 333 | Node() : value(), next(NULL) { } 334 | Node( const T & aValue ) : value(aValue), next(NULL) { } 335 | ~Node() { } 336 | }; 337 | 338 | /** 339 | * We have a very simple structure - a singly-linked list of values 340 | * that I'm just going to be very careful about modifying. 341 | */ 342 | Node *volatile _head; 343 | Node *volatile _tail; 344 | }; 345 | } // end of namespace mpsc 346 | } // end of namespace dkit 347 | 348 | #endif // __DKIT_MPSC_LINKEDFIFO_H 349 | -------------------------------------------------------------------------------- /src/pool.h: -------------------------------------------------------------------------------- 1 | /** 2 | * pool.h - this file defines a general pool of items that can be used as a 3 | * more efficient way to deal with reuse than the traditional 4 | * create/use/destroy scheme that's so common. The storage for the 5 | * pool will be somewhat dictated by the usage - in that the client 6 | * must give the pool a TYPE of queue to use: SPSC, MPSC, or SPMC. 7 | * The pool will use this to create the storage it will use and then 8 | * it's a simple matter of calling next() to get the next available 9 | * item, and then recycle() to recycle it. 10 | */ 11 | #ifndef __DKIT_POOL_H 12 | #define __DKIT_POOL_H 13 | 14 | // System Headers 15 | 16 | // Third-Party Headers 17 | #include 18 | 19 | // Other Headers 20 | #include "FIFO.h" 21 | #include "spsc/CircularFIFO.h" 22 | #include "mpsc/CircularFIFO.h" 23 | #include "spmc/CircularFIFO.h" 24 | 25 | // Forward Declarations 26 | /** 27 | * In order to allow for pointers AND plain old datatypes to be the contents 28 | * of the pool, we need to have a few template functions that will take care 29 | * of creation and destruction on the items. It's really pretty sweet. These 30 | * will be defined after the class, and are used in only one place in the 31 | * class, but it's an important place. 32 | */ 33 | namespace dkit { 34 | namespace pool_util { 35 | template void create( T t ); 36 | template void create( T * & t ); 37 | template void destroy( T t ); 38 | template void destroy( T * & t ); 39 | } // end of namespace pool_util 40 | } // end of namespace dkit 41 | 42 | // Public Constants 43 | /** 44 | * We need to have a simple enum for the different "types" of queues that 45 | * we can use for the pool - all based on the complexity of the access. This 46 | * is meant to allow the user to have complete flexibility in how to ask for, 47 | * and recycle items from the pool. 48 | */ 49 | #ifndef __DKIT_QUEUE_TYPE 50 | #define __DKIT_QUEUE_TYPE 51 | namespace dkit { 52 | enum queue_type { 53 | sp_sc = 0, 54 | mp_sc, 55 | sp_mc, 56 | }; 57 | } // end of namespace dkit 58 | #endif // __DKIT_QUEUE_TYPE 59 | 60 | // Public Datatypes 61 | 62 | // Public Data Constants 63 | 64 | 65 | namespace dkit { 66 | /** 67 | * This is the main class definition 68 | */ 69 | template class pool 70 | { 71 | public: 72 | /******************************************************************* 73 | * 74 | * Constructors/Destructor 75 | * 76 | *******************************************************************/ 77 | /** 78 | * This is the default constructor that assumes NOTHING - it just 79 | * makes a simple pool of no elements, but ready to generate what's 80 | * needed, and store recycled values to a given limit. 81 | */ 82 | pool() : 83 | _queue(NULL) 84 | { 85 | /** 86 | * We need to look at the 'type' and then create the FIFO 87 | * that makes sense to what he's asking for. The size is the 88 | * power of two (second arg), and will limit how many things 89 | * can be in the pool at once. 90 | */ 91 | switch (Q) { 92 | case sp_sc: 93 | _queue = new spsc::CircularFIFO(); 94 | break; 95 | case mp_sc: 96 | _queue = new mpsc::CircularFIFO(); 97 | break; 98 | case sp_mc: 99 | _queue = new spmc::CircularFIFO(); 100 | break; 101 | } 102 | } 103 | 104 | 105 | /** 106 | * This is the standard copy constructor that needs to be in every 107 | * class to make sure that we control how many copies we have 108 | * floating around in the system. 109 | */ 110 | pool( const pool & anOther ) : 111 | _queue(NULL) 112 | { 113 | // let the '=' operator do the heavy lifting... 114 | *this = anOther; 115 | } 116 | 117 | 118 | /** 119 | * This is the standard destructor and needs to be virtual to make 120 | * sure that if we subclass off this, the right destructor will be 121 | * called. 122 | */ 123 | virtual ~pool() 124 | { 125 | /** 126 | * If we have a queue, then we need to know if it's holding 127 | * pointers. If so, then we need to pop every one out and 128 | * then delete each item. It's the only clean way to do it. 129 | */ 130 | if (_queue != NULL) { 131 | // if it's pointers, then delete each one 132 | if (boost::is_pointer::value) { 133 | T val; 134 | while (_queue->pop(val)) { 135 | pool_util::destroy(val); 136 | } 137 | } 138 | // finally, drop the queue itself 139 | delete _queue; 140 | } 141 | } 142 | 143 | 144 | /** 145 | * When we process the result of an equality we need to make sure 146 | * that we do this right by always having an equals operator on 147 | * all classes. 148 | */ 149 | pool & operator=( const pool & anOther ) 150 | { 151 | if (this != & anOther) { 152 | /** 153 | * At this point in time, we're not going to assign 154 | * one pool to another. If they are pointers, we need 155 | * to handle things very differently, and there's no 156 | * quarantee that we'll be able to clone these effectively. 157 | */ 158 | } 159 | return *this; 160 | } 161 | 162 | 163 | /******************************************************************* 164 | * 165 | * Accessor Methods 166 | * 167 | *******************************************************************/ 168 | /** 169 | * This method is called to pull another item from the pool, or 170 | * create a new one if nothing is in the pool. This is the classic 171 | * way of getting the "next" item to work with. 172 | */ 173 | T next() 174 | { 175 | T n; 176 | // see if we can pop one off the queue. If not, make one 177 | if ((_queue == NULL) || !_queue->pop(n)) { 178 | pool_util::create(n); 179 | } 180 | // return what we have - new or used 181 | return n; 182 | } 183 | 184 | 185 | /** 186 | * This method is called when the user wants to recycle one of 187 | * the items to the pool. If the pool is full, then we'll simply 188 | * delete it. Otherwise, we'll put it back in the pool for use 189 | * the next time. 190 | */ 191 | void recycle( T anItem ) 192 | { 193 | if ((_queue == NULL) || !_queue->push(anItem)) { 194 | pool_util::destroy(anItem); 195 | } 196 | } 197 | 198 | 199 | /** 200 | * This method returns the number of items in the pool at this time. 201 | * When starting out, this will initially be zero, but as we put 202 | * things into the pool via recycle(), this will build up to the 203 | * maximum size allowed. 204 | */ 205 | size_t size() const 206 | { 207 | size_t sz = 0; 208 | if (_queue != NULL) { 209 | sz = _queue->size(); 210 | } 211 | return sz; 212 | } 213 | 214 | 215 | /** 216 | * This method will return 'true' if there are no items in the pool 217 | * to supply. Not to worry, any requests from the pool will first 218 | * try the pool for existing items, and if none exists, one will be 219 | * created for you. 220 | */ 221 | bool empty() 222 | { 223 | bool ans = true; 224 | if ((_queue != NULL) && !_queue->empty()) { 225 | ans = false; 226 | } 227 | return ans; 228 | } 229 | 230 | 231 | /******************************************************************* 232 | * 233 | * Utility Methods 234 | * 235 | *******************************************************************/ 236 | /** 237 | * This method checks to see if two pools are equal in their 238 | * contents and not their pointer values. This is how you'd likely 239 | * expect equality to work. 240 | */ 241 | bool operator==( const FIFO & anOther ) const 242 | { 243 | // right now, identity is the only equality we know 244 | return (this == & anOther); 245 | } 246 | 247 | 248 | /** 249 | * This method checks to see if two pools are NOT equal in their 250 | * contents and not their pointer values. This is how you'd likely 251 | * expect equality to work. 252 | */ 253 | bool operator!=( const FIFO & anOther ) const 254 | { 255 | return !operator=(anOther); 256 | } 257 | 258 | private: 259 | /** 260 | * The queue of T based on the style Q, is going to be a pointer 261 | * we create in the constructor and use here. It's the same API 262 | * no matter what Q is, it's just a cleaner way to implement the 263 | * queue. 264 | */ 265 | FIFO *_queue; 266 | }; 267 | 268 | 269 | namespace pool_util { 270 | /** 271 | * In order to handle both pointers and non-pointers as data 272 | * types 'T' in the pool, we need to take advantage of the 273 | * template methods and make create() and delete() methods 274 | * for pointers and non-pointers. 275 | * 276 | * For create(), it's pretty easy - we allow for nothing to be 277 | * done for the non-pointer, and a standard 'new' for the pointer. 278 | * For delete(), it's the same - we delete it and then NULL it 279 | * out if it's a pointer, if it's not, we do nothing. 280 | */ 281 | template void create( T t ) { } 282 | template void create( T * & t ) 283 | { 284 | t = new T(); 285 | } 286 | 287 | template void destroy( T t ) { } 288 | template void destroy( T * & t ) 289 | { 290 | if (t != NULL) { 291 | delete t; 292 | t = NULL; 293 | } 294 | } 295 | } // end of namespace pool_util 296 | } // end of namespace dkit 297 | 298 | #endif // __DKIT_POOL_H 299 | -------------------------------------------------------------------------------- /src/spmc/CircularFIFO.h: -------------------------------------------------------------------------------- 1 | /** 2 | * CircularFIFO.h - this file defines the template class for a single-producer, 3 | * multi-consumer, circular FIFO queue with a size initially 4 | * specified in the definition of the instance. This queue 5 | * is completely thread-safe so long as there is ONE and ONLY 6 | * ONE thread placing elements into this container, and there 7 | * can be as many as necessary remobing them from the queue. 8 | * The syntax is very simple - push() will push an element, 9 | * returning 'true' if there is room, and the value has been 10 | * placed in the queue. 11 | * 12 | * The peek() method allows the caller to see the element on 13 | * the top of the queue, and it HAS to be the same thread as 14 | * the SINGLE consumer, but the value stays on the queue. 15 | * The pop() will return true if there's something to pop 16 | * off the queue. 17 | */ 18 | #ifndef __DKIT_SPMC_CIRCULARFIFO_H 19 | #define __DKIT_SPMC_CIRCULARFIFO_H 20 | 21 | // System Headers 22 | #include 23 | 24 | // Third-Party Headers 25 | 26 | // Other Headers 27 | #include "FIFO.h" 28 | 29 | // Forward Declarations 30 | 31 | // Public Constants 32 | 33 | // Public Datatypes 34 | 35 | // Public Data Constants 36 | 37 | 38 | 39 | namespace dkit { 40 | namespace spmc { 41 | /** 42 | * This is the main class definition 43 | */ 44 | template class CircularFIFO : 45 | public FIFO 46 | { 47 | public: 48 | /******************************************************************* 49 | * 50 | * Constructors/Destructor 51 | * 52 | *******************************************************************/ 53 | /** 54 | * This is the default constructor that assumes NOTHING - it just 55 | * makes a simple queue ready to hold things. 56 | */ 57 | CircularFIFO() : 58 | FIFO(), 59 | _elements(), 60 | _head(0), 61 | _tail(0), 62 | _size(0) 63 | { 64 | } 65 | 66 | 67 | /** 68 | * This is the standard copy constructor that needs to be in every 69 | * class to make sure that we control how many copies we have 70 | * floating around in the system. 71 | */ 72 | CircularFIFO( const CircularFIFO & anOther ) : 73 | FIFO(), 74 | _elements(), 75 | _head(0), 76 | _tail(0), 77 | _size(0) 78 | { 79 | // let the '=' operator do the heavy lifting... 80 | *this = anOther; 81 | } 82 | 83 | 84 | /** 85 | * This is the standard destructor and needs to be virtual to make 86 | * sure that if we subclass off this, the right destructor will be 87 | * called. 88 | */ 89 | virtual ~CircularFIFO() 90 | { 91 | clear(); 92 | } 93 | 94 | 95 | /** 96 | * When we process the result of an equality we need to make sure 97 | * that we do this right by always having an equals operator on 98 | * all classes. 99 | * 100 | * Because of the single-producer, multi-consumer, nature of 101 | * this class, it is IMPOSSIBLE to have the assignment operator 102 | * be thread-safe without a mutex. This defeats the entire 103 | * purpose, so what we have is a non-thread-safe assignment 104 | * operator that is still useful if care is exercised to make 105 | * sure that no use-case exists where there will be readers or 106 | * writers to this queue while it is being copied in this assignment. 107 | */ 108 | CircularFIFO & operator=( const CircularFIFO & anOther ) 109 | { 110 | if (this != & anOther) { 111 | // now let's copy in the elements one by one 112 | for (size_t i = 0; i < eSize; i++) { 113 | _elements[i] = anOther._elements[i]; 114 | } 115 | // now copy the pointers 116 | _head = anOther._head; 117 | _tail = anOther._tail; 118 | } 119 | return *this; 120 | } 121 | 122 | 123 | /******************************************************************* 124 | * 125 | * Accessor Methods 126 | * 127 | *******************************************************************/ 128 | /** 129 | * This pair of methods does what you'd expect - it returns the 130 | * length of the queue as it exists at the present time. It's 131 | * got two names because there are so many different kinds of 132 | * implementations that it's often convenient to use one or the 133 | * other to remain consistent. 134 | */ 135 | virtual size_t size() const 136 | { 137 | return __sync_or_and_fetch((volatile size_t *)(&_size), 0x00); 138 | } 139 | 140 | 141 | virtual size_t length() const 142 | { 143 | return size(); 144 | } 145 | 146 | 147 | /** 148 | * This method returns the current capacity of the vector and 149 | * is NOT the size per se. The capacity is what this queue 150 | * will hold. 151 | */ 152 | virtual size_t capacity() const 153 | { 154 | return eSize; 155 | } 156 | 157 | 158 | /******************************************************** 159 | * 160 | * Element Accessing Methods 161 | * 162 | ********************************************************/ 163 | /** 164 | * This method takes an item and places it in the queue - if it can. 165 | * If so, then it will return 'true', otherwise, it'll return 'false'. 166 | */ 167 | virtual bool push( const T & anElem ) 168 | { 169 | bool error = false; 170 | 171 | // move the tail (insertion point) and get the old value for me 172 | size_t insPt = __sync_fetch_and_add(&_tail, 1) & eMask; 173 | // see if we've crashed the queue all the way around... 174 | if (_elements[insPt].valid) { 175 | // try to back out the damage we've done to the tail 176 | __sync_sub_and_fetch(&_tail, 1); 177 | error = true; 178 | } else { 179 | // save the data in the right spot and flag it as valid 180 | const_cast(_elements[insPt].value) = anElem; 181 | _elements[insPt].valid = true; 182 | // update the size by one as we've added something 183 | __sync_add_and_fetch(&_size, 1); 184 | } 185 | 186 | return !error; 187 | } 188 | 189 | 190 | /** 191 | * This method updates the passed-in reference with the value on the 192 | * top of the queue - if it can. If so, it'll return the value and 193 | * 'true', but if it can't, as in the queue is empty, then the method 194 | * will return 'false' and the value will be untouched. 195 | */ 196 | virtual bool pop( T & anElem ) 197 | { 198 | bool error = false; 199 | 200 | // move the head (extraction point) and get the old value for me 201 | size_t grab = __sync_fetch_and_add(&_head, 1) & eMask; 202 | // see if we've emptied the queue all the way around... 203 | if (!_elements[grab].valid) { 204 | // try to back out the damage we've done to the head 205 | __sync_sub_and_fetch(&_head, 1); 206 | error = true; 207 | } else { 208 | anElem = const_cast(_elements[grab].value); 209 | _elements[grab].valid = false; 210 | // update the size by one because we've removed something 211 | __sync_sub_and_fetch(&_size, 1); 212 | } 213 | 214 | return !error; 215 | } 216 | 217 | 218 | /** 219 | * This form of the pop() method will throw a std::exception 220 | * if there is nothing to pop, but otherwise, will return the 221 | * the first element on the queue. This is a slightly different 222 | * form that fits a different use-case, and so it's a handy 223 | * thing to have around at times. 224 | */ 225 | virtual T pop() 226 | { 227 | T v; 228 | if (!pop(v)) { 229 | throw std::exception(); 230 | } 231 | return v; 232 | } 233 | 234 | 235 | /** 236 | * If there is an item on the queue, this method will return a look 237 | * at that item without updating the queue. The return value will be 238 | * 'true' if there is something, but 'false' if the queue is empty. 239 | */ 240 | virtual bool peek( T & anElem ) 241 | { 242 | bool error = false; 243 | 244 | // see if we have an empty queue... 245 | if (!_elements[_head & eMask].valid) { 246 | error = true; 247 | } else { 248 | anElem = const_cast(_elements[_head & eMask].value); 249 | } 250 | 251 | return !error; 252 | } 253 | 254 | 255 | /** 256 | * This form of the peek() method is very much like the non-argument 257 | * version of the pop() method. If there is something on the top of 258 | * the queue, this method will return a COPY of it. If not, it will 259 | * throw a std::exception, that needs to be caught. 260 | */ 261 | virtual T peek() 262 | { 263 | T v; 264 | if (!peek(v)) { 265 | throw std::exception(); 266 | } 267 | return v; 268 | } 269 | 270 | 271 | /** 272 | * This method will clear out the contents of the queue so if 273 | * you're storing pointers, then you need to be careful as this 274 | * could leak. 275 | */ 276 | virtual void clear() 277 | { 278 | T v; 279 | while (pop(v)); 280 | } 281 | 282 | 283 | /** 284 | * This method will return 'true' if there are no items in the 285 | * queue. Simple. 286 | */ 287 | virtual bool empty() 288 | { 289 | return __sync_bool_compare_and_swap(&_size, 0, 0); 290 | } 291 | 292 | 293 | /******************************************************************* 294 | * 295 | * Utility Methods 296 | * 297 | *******************************************************************/ 298 | /** 299 | * Traditionally, this operator would look at the elements in this 300 | * queue, compare them to the elements in the argument queue, and 301 | * see if they are the same. The problem with this is that in a 302 | * single-producer, single-consumer system, there's no thread that 303 | * can actually perform this operation in a thread-safe manner. 304 | * 305 | * Moreover, the complexity in doing this is far more than we want to 306 | * take on in this class. It's lightweight, and while it's certainly 307 | * possible to make a good equals operator, it's not worth the cost 308 | * at this time. This method will always return 'false'. 309 | */ 310 | bool operator==( const CircularFIFO & anOther ) const 311 | { 312 | return false; 313 | } 314 | 315 | 316 | /** 317 | * Traditionally, this operator would look at the elements in this 318 | * queue, compare them to the elements in the argument queue, and 319 | * see if they are NOT the same. The problem with this is that in a 320 | * single-producer, single-consumer system, there's no thread that 321 | * can actually perform this operation in a thread-safe manner. 322 | * 323 | * Moreover, the complexity in doing this is far more than we want to 324 | * take on in this class. It's lightweight, and while it's certainly 325 | * possible to make a good not-equals operator, it's not worth the cost 326 | * at this time. This method will always return 'true'. 327 | */ 328 | bool operator!=( const CircularFIFO & anOther ) const 329 | { 330 | return !operator==(anOther); 331 | } 332 | 333 | 334 | private: 335 | /** 336 | * Since the size of the queue is in the definition of the 337 | * instance, it's possible to make some very simple enums that 338 | * are the size and the masking bits for the index values, so that 339 | * we know before anything starts up, how big to make things and 340 | * how to "wrap around" when the time comes. 341 | */ 342 | enum { 343 | eMask = ((1 << N) - 1), 344 | eSize = (1 << N) 345 | }; 346 | 347 | /** 348 | * In order to simplify the ring buffer access, I'm going to actually 349 | * have the ring a series of 'nodes', and for each, there will be a 350 | * value and a valid 'flag'. If the flag isn't true, then the value 351 | * in the node isn't valid. We'll use this to decouple the push() 352 | * and pop() so that each only needs to know it's own location and 353 | * then interrogate the node for state. 354 | */ 355 | struct Node { 356 | T value; 357 | bool valid; 358 | 359 | Node() : value(), valid(false) { } 360 | Node( const T & aValue ) : value(aValue), valid(true) { } 361 | ~Node() { } 362 | }; 363 | 364 | /** 365 | * We have a very simple structure - an array of values of a fixed 366 | * size and a simple head and tail. 367 | */ 368 | volatile Node _elements[eSize]; 369 | volatile size_t _head; 370 | volatile size_t _tail; 371 | volatile size_t _size; 372 | }; 373 | } // end of namespace spmc 374 | } // end of namespace dkit 375 | 376 | #endif // __DKIT_SPMC_CIRCULARFIFO_H 377 | -------------------------------------------------------------------------------- /src/spmc/LinkedFIFO.h: -------------------------------------------------------------------------------- 1 | /** 2 | * LinkedFIFO.h - this file defines a single-producer, multi-consumer, linked 3 | * FIFO queue and is using the compare-and-swap to achieve 4 | * the lock-free goal. There can be any number of threads that 5 | * call the pop() methods, but there can be ONE and ONLY ONE 6 | * thread that calls the push() method. 7 | */ 8 | #ifndef __DKIT_SPMC_LINKEDFIFO_H 9 | #define __DKIT_SPMC_LINKEDFIFO_H 10 | 11 | // System Headers 12 | 13 | // Third-Party Headers 14 | 15 | // Other Headers 16 | #include "FIFO.h" 17 | 18 | // Forward Declarations 19 | 20 | // Public Constants 21 | 22 | // Public Datatypes 23 | 24 | // Public Data Constants 25 | 26 | 27 | namespace dkit { 28 | namespace spmc { 29 | /** 30 | * This is the main class definition 31 | */ 32 | template class LinkedFIFO : 33 | public FIFO 34 | { 35 | public: 36 | /******************************************************************* 37 | * 38 | * Constructors/Destructor 39 | * 40 | *******************************************************************/ 41 | /** 42 | * This is the default constructor that assumes NOTHING - it just 43 | * makes a simple queue ready to hold instances of T. 44 | */ 45 | LinkedFIFO() : 46 | FIFO(), 47 | _head(NULL), 48 | _tail(NULL) 49 | { 50 | // start with NOTHING in the list - not one single thing 51 | } 52 | 53 | 54 | /** 55 | * This is the standard copy constructor that needs to be in every 56 | * class to make sure that we control how many copies we have 57 | * floating around in the system. 58 | */ 59 | LinkedFIFO( const LinkedFIFO & anOther ) : 60 | FIFO(), 61 | _head(NULL), 62 | _tail(NULL) 63 | { 64 | // let the '=' operator do the heavy lifting... 65 | *this = anOther; 66 | } 67 | 68 | 69 | /** 70 | * This is the standard destructor and needs to be virtual to make 71 | * sure that if we subclass off this, the right destructor will be 72 | * called. 73 | */ 74 | virtual ~LinkedFIFO() 75 | { 76 | // simply clear out the queue and we're all cleaned up 77 | clear(); 78 | } 79 | 80 | 81 | /** 82 | * When we process the result of an equality we need to make sure 83 | * that we do this right by always having an equals operator on 84 | * all classes. 85 | * 86 | * Because of the multiple-producer, single-consumer, nature of 87 | * this class, it is IMPOSSIBLE to have the assignment operator 88 | * be thread-safe without a mutex. This defeats the entire 89 | * purpose, so what we have is a non-thread-safe assignment 90 | * operator that is still useful if care is exercised to make 91 | * sure that no use-case exists where there will be readers or 92 | * writers to this queue while it is being copied in this assignment. 93 | */ 94 | LinkedFIFO & operator=( const LinkedFIFO & anOther ) 95 | { 96 | if (this != & anOther) { 97 | /** 98 | * Assuming the argument is stable, then we're just going 99 | * to walk it, pushing on the values from it in the right 100 | * order so they are appended to us. 101 | */ 102 | for (Node *n = anOther._head; n != NULL; n = n->next) { 103 | if (!push(n->value)) { 104 | break; 105 | } 106 | } 107 | } 108 | return *this; 109 | } 110 | 111 | 112 | /******************************************************************* 113 | * 114 | * Accessor Methods 115 | * 116 | *******************************************************************/ 117 | /** 118 | * This method takes an item and places it in the queue - if it can. 119 | * If so, then it will return 'true', otherwise, it'll return 'false'. 120 | */ 121 | virtual bool push( const T & anElem ) 122 | { 123 | bool error = false; 124 | 125 | // create a new Node with the provided value 126 | Node *me = new Node(anElem); 127 | if (me == NULL) { 128 | error = true; 129 | } else { 130 | /** 131 | * We need to add the new value to the tail and then link it 132 | * back into the list. The logic for doing this is interesting 133 | * in that we need to make sure that we handle the TWO cases 134 | * of linking back into the list properly. The first is if 135 | * there is NO LIST, and in that case, we need to make this 136 | * the new head, and update the tail. If it's not empty, then 137 | * we need to make sure that the mode we're about to link 138 | * really exists. We do this by looking at the 'next' pointer. 139 | * If it's NULL, then we know it's not about to be deleted, 140 | * and we can set it. These atomic operations are really 141 | * quite handy. 142 | */ 143 | if (!__sync_bool_compare_and_swap(&_head, NULL, me)) { 144 | // only set the link on the last node that exists 145 | __sync_bool_compare_and_swap(&(_tail->next), NULL, me); 146 | } 147 | Node *oldTail = _tail; 148 | while (!__sync_bool_compare_and_swap(&_tail, oldTail, me)) { 149 | oldTail = _tail; 150 | } 151 | } 152 | 153 | return !error; 154 | } 155 | 156 | 157 | /** 158 | * This method updates the passed-in reference with the value on the 159 | * top of the queue - if it can. If so, it'll return the value and 160 | * 'true', but if it can't, as in the queue is empty, then the method 161 | * will return 'false' and the value will be untouched. 162 | */ 163 | virtual bool pop( T & anElem ) 164 | { 165 | bool error = false; 166 | 167 | Node *oldHead = NULL; 168 | // if there's no head, then the queue is empty - bad news 169 | while ((oldHead = _head) != NULL) { 170 | if (__sync_bool_compare_and_swap(&_head, oldHead, oldHead->next)) { 171 | break; 172 | } 173 | } 174 | // if we got something, then extract the value and drop the Node 175 | if (oldHead != NULL) { 176 | // extract the value from the node we've removed 177 | anElem = oldHead->value; 178 | /** 179 | * If this node is the last in the list - characterized by 180 | * the 'next' being NULL, then we need to update it's 181 | * 'next' before we delete it. Why? So that the push() 182 | * method understands that this node is about to go out of 183 | * scope, and we don't want to update it. 184 | */ 185 | __sync_bool_compare_and_swap(&(oldHead->next), NULL, (Node *)0x01L); 186 | delete oldHead; 187 | } else { 188 | // nothing to get, so return an error and no value change 189 | error = true; 190 | } 191 | 192 | return !error; 193 | } 194 | 195 | 196 | /** 197 | * This form of the pop() method will throw a std::exception 198 | * if there is nothing to pop, but otherwise, will return the 199 | * the first element on the queue. This is a slightly different 200 | * form that fits a different use-case, and so it's a handy 201 | * thing to have around at times. 202 | */ 203 | virtual T pop() 204 | { 205 | T v; 206 | if (!pop(v)) { 207 | throw std::exception(); 208 | } 209 | return v; 210 | } 211 | 212 | 213 | /** 214 | * If there is an item on the queue, this method will return a look 215 | * at that item without updating the queue. The return value will be 216 | * 'true' if there is something, but 'false' if the queue is empty. 217 | * 218 | * In a multi-consumer queue, this method is VERY dangerous because 219 | * there is no way to quarantee that another thread won't pop() off 220 | * the value returned from peek(), while the peek() is trying to do 221 | * something with it. In general, there's no way to know that the 222 | * queue is STABLE from peek() to potential pop(), and so this 223 | * method is only useful if the queue is STABLE. 224 | */ 225 | virtual bool peek( T & anElem ) 226 | { 227 | bool error = false; 228 | 229 | // if the next guy is NULL, we're empty 230 | if (__sync_bool_compare_and_swap(&_head, NULL, NULL)) { 231 | error = true; 232 | } else { 233 | // look at the next valid node to get the next value 234 | anElem = _head->value; 235 | } 236 | 237 | return !error; 238 | } 239 | 240 | 241 | /** 242 | * This form of the peek() method is very much like the non-argument 243 | * version of the pop() method. If there is something on the top of 244 | * the queue, this method will return a COPY of it. If not, it will 245 | * throw a std::exception, that needs to be caught. 246 | * 247 | * In a multi-consumer queue, this method is VERY dangerous because 248 | * there is no way to quarantee that another thread won't pop() off 249 | * the value returned from peek(), while the peek() is trying to do 250 | * something with it. In general, there's no way to know that the 251 | * queue is STABLE from peek() to potential pop(), and so this 252 | * method is only useful if the queue is STABLE. 253 | */ 254 | virtual T peek() 255 | { 256 | T v; 257 | if (!peek(v)) { 258 | throw std::exception(); 259 | } 260 | return v; 261 | } 262 | 263 | 264 | /** 265 | * This method will clear out the contents of the queue so if 266 | * you're storing pointers, then you need to be careful as this 267 | * could leak. 268 | */ 269 | virtual void clear() 270 | { 271 | // pretty simple - just pop everything off the queue 272 | T v; 273 | while (pop(v)); 274 | } 275 | 276 | 277 | /** 278 | * This method will return 'true' if there are no items in the 279 | * queue. Simple. 280 | */ 281 | virtual bool empty() 282 | { 283 | return (__sync_bool_compare_and_swap(&_head, NULL, NULL)); 284 | } 285 | 286 | 287 | /** 288 | * This method will return the total number of items in the 289 | * queue. Since it's possible that multiple threads are adding 290 | * to this queue at any one point in time, it's really at BEST 291 | * a snapshot of the size, and is only completely accurate 292 | * when the queue is stable. 293 | */ 294 | virtual size_t size() const 295 | { 296 | size_t retval = 0; 297 | for (Node *n = _head; n != NULL; n = n->next) { 298 | ++retval; 299 | } 300 | return retval; 301 | } 302 | 303 | 304 | /******************************************************************* 305 | * 306 | * Utility Methods 307 | * 308 | *******************************************************************/ 309 | /** 310 | * This method checks to see if two queues are equal in their 311 | * contents and not their pointer values. This is how you'd likely 312 | * expect equality to work. 313 | * 314 | * Once again, since it's possible that there are multiple threads 315 | * adding element to BOTH queues, this is only really going to be 316 | * accutate when BOTH queues are stable and not undergoing change. 317 | * Only then, can we be assured of the stability of the contents. 318 | */ 319 | bool operator==( const LinkedFIFO & anOther ) const 320 | { 321 | bool equals = true; 322 | 323 | // next, check the elements for equality 324 | if (equals) { 325 | Node *me = _head; 326 | Node *him = anOther._head; 327 | while (me != NULL) { 328 | if ((him == NULL) || (me->value != him->value)) { 329 | equals = false; 330 | break; 331 | } 332 | me = me->next; 333 | him = him->next; 334 | } 335 | } 336 | 337 | return equals; 338 | } 339 | 340 | 341 | /** 342 | * This method checks to see if two queues are NOT equal in their 343 | * contents and not their pointer values. This is how you'd likely 344 | * expect equality to work. 345 | */ 346 | bool operator!=( const LinkedFIFO & anOther ) const 347 | { 348 | return !operator=(anOther); 349 | } 350 | 351 | private: 352 | /** 353 | * My linked list will be made of these nodes, and I'll make them 354 | * with the one-argument constructor that just sets the value and 355 | * nulls out the next pointer. Pretty simple. 356 | */ 357 | struct Node { 358 | T value; 359 | Node *next; 360 | 361 | Node() : value(), next(NULL) { } 362 | Node( const T & aValue ) : value(aValue), next(NULL) { } 363 | ~Node() { } 364 | }; 365 | 366 | /** 367 | * We have a very simple structure - a singly-linked list of values 368 | * that I'm just going to be very careful about modifying. 369 | */ 370 | Node *volatile _head; 371 | Node *volatile _tail; 372 | }; 373 | } // end of namespace spmc 374 | } // end of namespace dkit 375 | 376 | #endif // __DKIT_SPMC_LINKEDFIFO_H 377 | -------------------------------------------------------------------------------- /src/spsc/CircularFIFO.h: -------------------------------------------------------------------------------- 1 | /** 2 | * CircularFIFO.h - this file defines the template class for a single-producer, 3 | * single-consumer, circular FIFO queue with a size initially 4 | * specified in the definition of the instance. This queue 5 | * is completely thread-safe so long as there is ONE and ONLY 6 | * ONE thread placing elements into this container, and ONE 7 | * and ONLY ONE thread removing them. The syntax is very 8 | * simple - push() will push an element, returning 'true' if 9 | * there is room, and the value has been placed in the queue. 10 | * The peek() method allows the caller to see the element on 11 | * the top of the queue, and it HAS to be the same thread as 12 | * the SINGLE consumer, but the value stays on the queue. 13 | * The pop() will return true if there's something to pop 14 | * off the queue. 15 | */ 16 | #ifndef __DKIT_SPSC_CIRCULARFIFO_H 17 | #define __DKIT_SPSC_CIRCULARFIFO_H 18 | 19 | // System Headers 20 | #include 21 | 22 | // Third-Party Headers 23 | 24 | // Other Headers 25 | #include "FIFO.h" 26 | 27 | // Forward Declarations 28 | 29 | // Public Constants 30 | 31 | // Public Datatypes 32 | 33 | // Public Data Constants 34 | 35 | 36 | namespace dkit { 37 | namespace spsc { 38 | /** 39 | * This is the main template definition for the 2^N sized FIFO queue 40 | */ 41 | template class CircularFIFO : 42 | public FIFO 43 | { 44 | public : 45 | /******************************************************** 46 | * 47 | * Constructors/Destructor 48 | * 49 | ********************************************************/ 50 | /** 51 | * This form of the constructor initializes the queue to a series 52 | * of 2^N empty T instances. If there is no default constructor 53 | * for T, this will not compile. Please make sure there's AT 54 | * LEAST a default constructor and copy construstor as well as 55 | * an assignment operator for T - it's all going to be needed. 56 | */ 57 | CircularFIFO() : 58 | FIFO(), 59 | _elements(), 60 | _head(0), 61 | _tail(0) 62 | { 63 | } 64 | 65 | 66 | /** 67 | * This is the standard copy constructor and needs to be in every 68 | * class to make sure that we don't have too many things running 69 | * around. 70 | */ 71 | CircularFIFO( const CircularFIFO & anOther ) : 72 | FIFO(), 73 | _elements(), 74 | _head(0), 75 | _tail(0) 76 | { 77 | // let the '=' operator do it 78 | *this = anOther; 79 | } 80 | 81 | 82 | /** 83 | * This is the destructor for the queue and makes sure that 84 | * everything is cleaned up before leaving. 85 | */ 86 | virtual ~CircularFIFO() 87 | { 88 | // there's not a lot to do at this point - yet 89 | } 90 | 91 | 92 | /** 93 | * When we want to process the result of an equality we need to 94 | * make sure that we do this right by always having an equals 95 | * operator on all classes. 96 | * 97 | * Because of the single-producer, single-consumer, nature of 98 | * this class, it is IMPOSSIBLE to have the assignment operator 99 | * be thread-safe without a mutex. This defeats the entire 100 | * purpose, so what we have is a non-thread-safe assignment 101 | * operator that is still useful if care is exercised to make 102 | * sure that no use-case exists where there will be readers or 103 | * writers to this queue while it is being copied in this assignment. 104 | */ 105 | CircularFIFO & operator=( const CircularFIFO & anOther ) 106 | { 107 | // make sure that we don't do this to ourselves 108 | if (this != & anOther) { 109 | // now let's copy in the elements one by one 110 | for (size_t i = 0; i < eSize; i++) { 111 | _elements[i] = anOther._elements[i]; 112 | } 113 | // now copy the pointers 114 | _head = anOther._head; 115 | _tail = anOther._tail; 116 | } 117 | 118 | return *this; 119 | } 120 | 121 | 122 | /******************************************************** 123 | * 124 | * Accessor Methods 125 | * 126 | ********************************************************/ 127 | /** 128 | * This pair of methods does what you'd expect - it returns the 129 | * length of the queue as it exists at the present time. It's 130 | * got two names because there are so many different kinds of 131 | * implementations that it's often convenient to use one or the 132 | * other to remain consistent. 133 | */ 134 | virtual size_t size() const 135 | { 136 | size_t sz = _tail - _head; 137 | // see if we wrapped around - and correct the unsigned math 138 | if (sz > eSize) { 139 | sz = (sz + eSize) & eMask; 140 | } 141 | return sz; 142 | } 143 | 144 | 145 | virtual size_t length() const 146 | { 147 | return size(); 148 | } 149 | 150 | 151 | /** 152 | * This method returns the current capacity of the vector and 153 | * is NOT the size per se. The capacity is what this queue 154 | * will hold. 155 | */ 156 | virtual size_t capacity() const 157 | { 158 | return eSize; 159 | } 160 | 161 | 162 | /******************************************************** 163 | * 164 | * Element Accessing Methods 165 | * 166 | ********************************************************/ 167 | /** 168 | * This method pushes the provided element onto the queue, and 169 | * because it's FIFO, it's going to be the placed at the 'tail' 170 | * of the queue, and the pop() will take elements off the 'head'. 171 | * This method will make sure that there is a place to put the 172 | * element BEFORE placing it in the queue, and will return 'false' 173 | * if there is no available space. Otherwise, it will place the 174 | * element, and then return 'true'. 175 | */ 176 | virtual bool push( const T & anElem ) 177 | { 178 | size_t newTail = (_tail + 1) & eMask; 179 | // if we have room, then let's save it in the spot 180 | if (newTail != _head) { 181 | _elements[_tail] = anElem; 182 | _tail = newTail; 183 | return true; 184 | } 185 | 186 | // the queue had no more space - push back! 187 | return false; 188 | } 189 | 190 | 191 | /** 192 | * This form of the pop() method takes an element reference as 193 | * an argument where the top of the queue will be placed - assuming 194 | * there's something there to get. If so, then the value will be 195 | * replaced, and a 'true' will be returned. Otherwise, a 'false' 196 | * will be returned, as there's nothing to return. 197 | */ 198 | virtual bool pop( T & anElem ) 199 | { 200 | // see if we have anything in the queue to pull out 201 | if (_head == _tail) { 202 | return false; 203 | } 204 | 205 | // OK, grab the head of the queue, and move up one 206 | anElem = _elements[_head]; 207 | _head = (_head + 1) & eMask; 208 | return true; 209 | } 210 | 211 | 212 | /** 213 | * This form of the pop() method will throw a std::exception 214 | * if there is nothing to pop, but otherwise, will return the 215 | * the first element on the queue. This is a slightly different 216 | * form that fits a different use-case, and so it's a handy 217 | * thing to have around at times. 218 | */ 219 | virtual T pop() 220 | { 221 | T v; 222 | if (!pop(v)) { 223 | throw std::exception(); 224 | } 225 | return v; 226 | } 227 | 228 | 229 | /** 230 | * This method looks at the first element on the top of the queue 231 | * and returns it in the provided reference argument. If there's 232 | * nothing there, the return value will be 'false', and the reference 233 | * will be untouched. If there's something there, the return value 234 | * will be 'true'. This is a very similar usage pattern to the 235 | * pop() method with the argument - it just doesn't advance the 236 | * 'head' pointer. 237 | */ 238 | virtual bool peek( T & anElem ) 239 | { 240 | // see if we have anything in the queue to pull out 241 | if (_head == _tail) { 242 | return false; 243 | } 244 | 245 | // OK, grab the head of the queue, but DO NOT move up one 246 | anElem = _elements[_head & eMask]; 247 | return true; 248 | } 249 | 250 | 251 | /** 252 | * This form of the peek() method is very much like the non-argument 253 | * version of the pop() method. If there is something on the top of 254 | * the queue, this method will return a COPY of it. If not, it will 255 | * throw a std::exception, that needs to be caught. 256 | */ 257 | virtual T peek() 258 | { 259 | T v; 260 | if (!peek(v)) { 261 | throw std::exception(); 262 | } 263 | return v; 264 | } 265 | 266 | 267 | /** 268 | * This method will remove all the elements from the queue by 269 | * simply popping them off one by one until they are all removed. 270 | * In order for this to be thread-safe, this action can only be 271 | * called by the CONSUMER thread, as that's the same activity as 272 | * is happening in this method. 273 | */ 274 | virtual void clear() 275 | { 276 | T v; 277 | while (pop(v)); 278 | } 279 | 280 | 281 | /** 282 | * This method returns 'true' if there are no elements in the queue. 283 | */ 284 | virtual bool empty() 285 | { 286 | return (_head == _tail); 287 | } 288 | 289 | 290 | /******************************************************** 291 | * 292 | * Utility Methods 293 | * 294 | ********************************************************/ 295 | /** 296 | * Traditionally, this operator would look at the elements in this 297 | * queue, compare them to the elements in the argument queue, and 298 | * see if they are the same. The problem with this is that in a 299 | * single-producer, single-consumer system, there's no thread that 300 | * can actually perform this operation in a thread-safe manner. 301 | * 302 | * Moreover, the complexity in doing this is far more than we want to 303 | * take on in this class. It's lightweight, and while it's certainly 304 | * possible to make a good equals operator, it's not worth the cost 305 | * at this time. This method will always return 'false'. 306 | */ 307 | bool operator==( const CircularFIFO & anOther ) const 308 | { 309 | return false; 310 | } 311 | 312 | 313 | /** 314 | * Traditionally, this operator would look at the elements in this 315 | * queue, compare them to the elements in the argument queue, and 316 | * see if they are NOT the same. The problem with this is that in a 317 | * single-producer, single-consumer system, there's no thread that 318 | * can actually perform this operation in a thread-safe manner. 319 | * 320 | * Moreover, the complexity in doing this is far more than we want to 321 | * take on in this class. It's lightweight, and while it's certainly 322 | * possible to make a good not-equals operator, it's not worth the cost 323 | * at this time. This method will always return 'true'. 324 | */ 325 | bool operator!=( const CircularFIFO & anOther ) const 326 | { 327 | return !operator==(anOther); 328 | } 329 | 330 | 331 | private: 332 | /** 333 | * Since the size of the queue is in the definition of the 334 | * instance, it's possible to make some very simple enums that 335 | * are the size and the masking bits for the index values, so that 336 | * we know before anything starts up, how big to make things and 337 | * how to "wrap around" when the time comes. 338 | */ 339 | enum { 340 | eMask = ((1 << N) - 1), 341 | eSize = (1 << N) 342 | }; 343 | 344 | /** 345 | * We need to have a linear array of the elements as well as the 346 | * head and tail of the queue. Since all these are going to be 347 | * messed with by one producer and one consumer thread, making the 348 | * ivars volatile is sufficient to ensure the thread-safety of 349 | * the container. 350 | */ 351 | volatile T _elements[eSize]; 352 | volatile size_t _head; 353 | volatile size_t _tail; 354 | }; 355 | } // end of namespace spsc 356 | } // end of namespace dkit 357 | 358 | #endif // __DKIT_SPSC_CIRCULARFIFO_H 359 | -------------------------------------------------------------------------------- /src/util/timer.h: -------------------------------------------------------------------------------- 1 | /** 2 | * timer.h - this file defines a simple timer class for DKit - something 3 | * to use to time functions and operations as well as allowing 4 | * the general user to take advantage of the same tools. 5 | */ 6 | #ifndef __DKIT_UTIL_TIMER_H 7 | #define __DKIT_UTIL_TIMER_H 8 | 9 | // System Headers 10 | #include 11 | #include 12 | #include 13 | #ifdef __MACH__ 14 | #include 15 | #include 16 | #include 17 | #include 18 | #endif 19 | #include 20 | #include 21 | 22 | // Third-Party Headers 23 | 24 | // Other Headers 25 | 26 | // Forward Declarations 27 | 28 | // Public Constants 29 | 30 | // Public Datatypes 31 | 32 | // Public Data Constants 33 | 34 | 35 | namespace dkit { 36 | namespace util { 37 | /** 38 | * This is the main class definition. 39 | */ 40 | class timer 41 | { 42 | public: 43 | /** 44 | * This method can be used to simply get the number of microseconds 45 | * since epoch at the time of the calling. It's a very handy way to 46 | * do performance measurements as it's lightweight on the process and 47 | * it's very precise. 48 | */ 49 | static inline uint64_t usecSinceEpoch() 50 | { 51 | timespec ts; 52 | /** 53 | * OS X does not have clock_gettime, use clock_get_time and then 54 | * stuff the same values into the timespec structure for a 55 | * compatible result. 56 | */ 57 | #ifdef __MACH__ 58 | clock_serv_t cclock; 59 | mach_timespec_t mts; 60 | host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock); 61 | clock_get_time(cclock, &mts); 62 | mach_port_deallocate(mach_task_self(), cclock); 63 | // now we can save the values in the timespec 64 | ts.tv_sec = mts.tv_sec; 65 | ts.tv_nsec = mts.tv_nsec; 66 | #else 67 | clock_gettime(CLOCK_REALTIME, &ts); 68 | #endif 69 | return ((uint64_t)ts.tv_sec * 1000000LL + 70 | (uint64_t)ts.tv_nsec / 1000LL); 71 | } 72 | 73 | 74 | /** 75 | * There will be times that we don't need to have the time since 76 | * epoch, but just need a very fast RELATIVE timestamp for intervals 77 | * and performance checks. In these cases, there are a few ways to 78 | * get this value faster than that which has a known reference. 79 | */ 80 | static inline uint64_t usecStamp() 81 | { 82 | uint64_t now = 0; 83 | #ifdef __MACH__ 84 | /** 85 | * For Mac OS X systems, we're going to use the MUCH 86 | * FASTER mach_absolute_time(), and the biggest downside 87 | * is that it's got no particular reference. That's why 88 | * it works here for us nicely. 89 | */ 90 | static mach_timebase_info_data_t __timebase; 91 | // get the current usec time - absolute to some reference 92 | now = mach_absolute_time()/1000; 93 | // convert to nanoseconds. 94 | if (__timebase.denom == 0) { 95 | (void) mach_timebase_info(&__timebase); 96 | } 97 | // now convert the time to real time (usec) 98 | now = (now * __timebase.numer)/__timebase.denom; 99 | #else 100 | /** 101 | * For linux systems, we'll stick with the tried and true 102 | * clock_gettime() and just leave it at that. 103 | */ 104 | timespec ts; 105 | clock_gettime(CLOCK_REALTIME, &ts); 106 | now = ((uint64_t)ts.tv_sec * 1000000LL + 107 | (uint64_t)ts.tv_nsec / 1000LL); 108 | #endif 109 | return now; 110 | } 111 | 112 | 113 | /** 114 | * This method takes a timestamp as usec since Epoch and formats 115 | * it into a nice, human-readable timestamp: '2012-02-12 11:34:15' 116 | * or '2012-02-12 11:34:15.032451' - depending on if you ask for 117 | * the usec to be displayed. This is very useful for converting 118 | * these timestamps into useful strings for logs, etc. 119 | */ 120 | static inline std::string formatTimestamp( uint64_t aTimestamp, bool inclUSec = false ) 121 | { 122 | // convert the usec since epoch to sec since epoch... 123 | time_t secs = aTimestamp / 1000000L; 124 | // ...and get the usec remaining after that 125 | uint32_t usec = (aTimestamp - secs * 1000000L); 126 | // get the struct and breack out the secs into it's components 127 | struct tm when; 128 | localtime_r(&secs, &when); 129 | // now build up the string based on all this data 130 | char buff[128]; 131 | if (inclUSec) { 132 | snprintf(buff, 127, "%4d-%02d-%02d %02d:%02d:%02d.%06d", 133 | (when.tm_year + 1900), (when.tm_mon + 1), 134 | when.tm_mday, when.tm_hour, when.tm_min, when.tm_sec, 135 | usec); 136 | } else { 137 | snprintf(buff, 127, "%4d-%02d-%02d %02d:%02d:%02d", 138 | (when.tm_year + 1900), (when.tm_mon + 1), 139 | when.tm_mday, when.tm_hour, when.tm_min, when.tm_sec); 140 | } 141 | // return what we have created. 142 | return buff; 143 | } 144 | 145 | 146 | /** 147 | * This method takes a timestamp as usec since Epoch and formats 148 | * it into a nice, human-readable date: '2012-02-12'. This totally 149 | * ignores the TIME component of the timestamp, and just looks at 150 | * the date. This is very useful for converting these timestamps 151 | * into useful strings for logs, etc. 152 | */ 153 | static inline std::string formatDate( uint64_t aTimestamp ) 154 | { 155 | // convert the usec since epoch to sec since epoch... 156 | time_t secs = aTimestamp / 1000000L; 157 | // get the struct and breack out the secs into it's components 158 | struct tm when; 159 | localtime_r(&secs, &when); 160 | // now build up the string based on all this data 161 | char buff[80]; 162 | snprintf(buff, 79, "%4d-%02d-%02d", 163 | (when.tm_year + 1900), (when.tm_mon + 1), when.tm_mday); 164 | // return what we have created. 165 | return buff; 166 | } 167 | 168 | 169 | /** 170 | * This method takes a timestamp as usec since Epoch and formats 171 | * it into a nice, human-readable time: '11:34:15' or '11:34:15.342567' 172 | * depending on if you ask for the usec to be displayed. This is very 173 | * useful for converting these timestamps into useful strings for 174 | * logs, etc. 175 | */ 176 | static inline std::string formatTime( uint64_t aTimestamp, bool inclUSec = false ) 177 | { 178 | // convert the usec since epoch to sec since epoch... 179 | time_t secs = aTimestamp / 1000000L; 180 | // ...and get the usec remaining after that 181 | uint32_t usec = (aTimestamp - secs * 1000000L); 182 | // get the struct and breack out the secs into it's components 183 | struct tm when; 184 | localtime_r(&secs, &when); 185 | // now build up the string based on all this data 186 | char buff[80]; 187 | if (inclUSec) { 188 | snprintf(buff, 79, "%02d:%02d:%02d.%06d", 189 | when.tm_hour, when.tm_min, when.tm_sec, usec); 190 | } else { 191 | snprintf(buff, 79, "%02d:%02d:%02d", 192 | when.tm_hour, when.tm_min, when.tm_sec); 193 | } 194 | // return what we have created. 195 | return buff; 196 | } 197 | }; 198 | } // end of namespace util 199 | } // end of namespace dkit 200 | 201 | #endif // __DKIT_UTIL_TIMER_H 202 | -------------------------------------------------------------------------------- /tests/.gitignore: -------------------------------------------------------------------------------- 1 | *.bak 2 | *.dSYM 3 | *.swp 4 | atomic 5 | cqueue 6 | linkedFIFO 7 | mpsc_fifo 8 | receiver 9 | sender 10 | spmc_fifo 11 | spsc_fifo 12 | pool 13 | trie 14 | udp_receiver 15 | -------------------------------------------------------------------------------- /tests/Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # Makefile for DKit tests 3 | # 4 | # We need to have a few system-level dependencies to make things cleanly 5 | # compile on a few different systems. 6 | # 7 | ifeq ($(shell uname),SunOS) 8 | CXX = g++ 9 | CXX_DEFS = -g -O3 10 | LIB_EXT = so 11 | ifeq ($(shell uname -r),5.6) 12 | OS_LIBS = -lsocket -lnsl -lposix4 13 | else 14 | OS_LIBS = -lsocket -lnsl -lrt 15 | endif 16 | SHARED = -shared 17 | endif 18 | ifeq ($(shell uname),Darwin) 19 | CXX = g++ 20 | CXX_DEFS = -D_REENTRANT -DBOOST_ASIO_HAS_STD_ATOMIC -g -O3 -arch x86_64 21 | LIB_EXT = so 22 | OS_LIBS = 23 | SHARED = -shared 24 | endif 25 | ifeq ($(shell uname),Linux) 26 | CXX = g++ 27 | ifeq ($(BOOST_ROOT),) 28 | CXX_DEFS = -g -O3 29 | LIB_EXT = so 30 | OS_LIBS = -lpthread -lrt -Wl,-rpath=../lib 31 | else 32 | CXX_DEFS = -g -O3 -I$(BOOST_ROOT)/include 33 | LIB_EXT = so 34 | OS_LIBS = -lpthread -lrt -L$(BOOST_ROOT)/lib -Wl,-rpath=../lib 35 | endif 36 | SHARED = -shared 37 | endif 38 | 39 | # 40 | # These are the locations of certain directories in the package 41 | # 42 | LIB_DIR = ../lib 43 | SRC_DIR = ../src 44 | 45 | # 46 | # This is the ultimate target we're making - the library with the entire 47 | # DKit contained within it. 48 | # 49 | LIB_FILE = $(LIB_DIR)/libDKit.$(LIB_EXT) 50 | 51 | # 52 | # These are the pretty standard C++-style defines for a makefile - the 53 | # includes, the libs, the compiler flags, etc. 54 | # 55 | INCLUDES = -I$(SRC_DIR) 56 | DEFINES = $(CXX_DEFS) 57 | CXXFLAGS = -fPIC -Wall $(INCLUDES) $(DEFINES) 58 | 59 | LIBS = -L$(LIB_DIR) $(OS_LIBS) -lboost_thread-mt -lboost_system-mt -lstdc++ \ 60 | -lDKit 61 | LDFLAGS = 62 | 63 | # 64 | # These are the main targets that we'll be making 65 | # 66 | APPS = atomic spsc_fifo mpsc_fifo linkedFIFO spmc_fifo pool sender receiver \ 67 | udp_receiver trie cqueue 68 | SRCS = $(APPS:%=%.cpp) 69 | 70 | all: $(APPS) 71 | 72 | clean: 73 | rm -f $(APPS) 74 | rm -rf *.dSYM 75 | 76 | tests: all 77 | @ echo '========= Atomic Integer Tests =========' 78 | @ ./atomic 79 | @ echo '========= SP/SC FIFO Tests =========' 80 | @ ./spsc_fifo 81 | @ echo '========= MP/SC CircularFIFO Tests =========' 82 | @ ./mpsc_fifo 83 | @ echo '========= SP/MC CircularFIFO Tests =========' 84 | @ ./spmc_fifo 85 | @ echo '========= LinkedFIFO Tests =========' 86 | @ ./linkedFIFO 87 | @ echo '========= Pool Tests =========' 88 | @ ./pool 89 | 90 | depend: 91 | makedepend -Y -o\ -- $(INCLUDES) -- $(SRCS); rm Makefile.bak 92 | 93 | .cpp.o: 94 | $(CXX) -c $(CXXFLAGS) $< -o $@ 95 | 96 | atomic: atomic.cpp $(LIB_FILE) 97 | $(CXX) $(CXXFLAGS) atomic.cpp -o atomic $(LIBS) $(LDFLAGS) 98 | 99 | cqueue: cqueue.cpp $(LIB_FILE) 100 | $(CXX) $(CXXFLAGS) cqueue.cpp -o cqueue $(LIBS) $(LDFLAGS) 101 | 102 | linkedFIFO: linkedFIFO.cpp $(LIB_FILE) 103 | $(CXX) $(CXXFLAGS) linkedFIFO.cpp -o linkedFIFO $(LIBS) $(LDFLAGS) 104 | 105 | mpsc_fifo: mpsc_fifo.cpp $(LIB_FILE) 106 | $(CXX) $(CXXFLAGS) mpsc_fifo.cpp -o mpsc_fifo $(LIBS) $(LDFLAGS) 107 | 108 | pool: pool.cpp $(LIB_FILE) 109 | $(CXX) $(CXXFLAGS) pool.cpp -o pool $(LIBS) $(LDFLAGS) 110 | 111 | trie: trie.cpp $(LIB_FILE) 112 | $(CXX) $(CXXFLAGS) trie.cpp -o trie $(LIBS) $(LDFLAGS) 113 | 114 | sender: sender.cpp $(LIB_FILE) 115 | $(CXX) $(CXXFLAGS) sender.cpp -o sender $(LIBS) $(LDFLAGS) 116 | 117 | receiver: receiver.cpp $(LIB_FILE) 118 | $(CXX) $(CXXFLAGS) receiver.cpp -o receiver $(LIBS) $(LDFLAGS) 119 | 120 | spsc_fifo: spsc_fifo.cpp $(LIB_FILE) 121 | $(CXX) $(CXXFLAGS) spsc_fifo.cpp -o spsc_fifo $(LIBS) $(LDFLAGS) 122 | 123 | spmc_fifo: spmc_fifo.cpp $(LIB_FILE) 124 | $(CXX) $(CXXFLAGS) spmc_fifo.cpp -o spmc_fifo $(LIBS) $(LDFLAGS) 125 | 126 | udp_receiver: udp_receiver.cpp $(LIB_FILE) 127 | $(CXX) $(CXXFLAGS) udp_receiver.cpp -o udp_receiver $(LIBS) $(LDFLAGS) 128 | 129 | # DO NOT DELETE 130 | 131 | atomic : ../src/atomic.h ../src/abool.h ../src/aint8.h ../src/aint16.h 132 | atomic : ../src/aint32.h ../src/aint64.h 133 | spsc_fifo : ../src/spsc/CircularFIFO.h ../src/FIFO.h ../src/util/timer.h 134 | mpsc_fifo : ../src/mpsc/CircularFIFO.h ../src/FIFO.h ../src/util/timer.h 135 | mpsc_fifo : hammer.h drain.h 136 | linkedFIFO : ../src/mpsc/LinkedFIFO.h ../src/FIFO.h ../src/spmc/LinkedFIFO.h 137 | linkedFIFO : ../src/util/timer.h hammer.h drain.h 138 | spmc_fifo : ../src/spmc/CircularFIFO.h ../src/FIFO.h ../src/util/timer.h 139 | spmc_fifo : hammer.h drain.h 140 | pool : ../src/pool.h ../src/FIFO.h ../src/spsc/CircularFIFO.h 141 | pool : ../src/mpsc/CircularFIFO.h ../src/spmc/CircularFIFO.h 142 | pool : ../src/util/timer.h 143 | udp_receiver : ../src/io/udp_receiver.h ../src/source.h ../src/abool.h 144 | udp_receiver : ../src/sink.h ../src/io/datagram.h ../src/util/timer.h 145 | udp_receiver : ../src/io/multicast_channel.h ../src/aint32.h ../src/pool.h 146 | udp_receiver : ../src/FIFO.h ../src/spsc/CircularFIFO.h 147 | udp_receiver : ../src/mpsc/CircularFIFO.h ../src/spmc/CircularFIFO.h 148 | udp_receiver : ../src/io/udp_transmitter.h ../src/adapter.h 149 | trie : ../src/trie.h ../src/abool.h ../src/util/timer.h 150 | cqueue : ../src/cqueue.h ../src/FIFO.h ../src/spsc/CircularFIFO.h 151 | cqueue : ../src/mpsc/CircularFIFO.h ../src/spmc/CircularFIFO.h ../src/trie.h 152 | cqueue : ../src/abool.h ../src/pool.h ../src/util/timer.h 153 | -------------------------------------------------------------------------------- /tests/atomic.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * This is the tests for the atomic integers and boolean values 3 | */ 4 | // System Headers 5 | #include 6 | #include 7 | 8 | // Third-Party Headers 9 | 10 | // Other Headers 11 | #include "atomic.h" 12 | 13 | int main(int argc, char *argv[]) { 14 | bool error = false; 15 | 16 | /** 17 | * Check the atomic bool - setting values, flipping values, etc. 18 | */ 19 | abool b = true; 20 | if (!error) { 21 | if (!b) { 22 | error = true; 23 | std::cout << "ERROR - the abool could not be assigned the initial value 'true'!" << std::endl; 24 | } else { 25 | std::cout << "Passed - the abool can be assigned an initial value" << std::endl; 26 | } 27 | } 28 | if (!error) { 29 | b = false; 30 | if (b) { 31 | error = true; 32 | std::cout << "ERROR - the abool could not be assigned the value 'false'!" << std::endl; 33 | } else { 34 | std::cout << "Passed - the abool can be assigned a value" << std::endl; 35 | } 36 | } 37 | if (!error) { 38 | b = true; 39 | if (++b) { 40 | error = true; 41 | std::cout << "ERROR - the abool could not be pre-incremented!" << std::endl; 42 | } else { 43 | std::cout << "Passed - the abool can be pre-incremented" << std::endl; 44 | } 45 | } 46 | if (!error) { 47 | if (b++) { 48 | error = true; 49 | std::cout << "ERROR - the abool could not be post-incremented!" << std::endl; 50 | } else { 51 | std::cout << "Passed - the abool can be post-incremented" << std::endl; 52 | } 53 | if (!b) { 54 | error = true; 55 | std::cout << "ERROR - the abool is not correct post-incremented!" << std::endl; 56 | } else { 57 | std::cout << "Passed - the abool is corrent post-incremented" << std::endl; 58 | } 59 | } 60 | if (!error) { 61 | if (--b) { 62 | error = true; 63 | std::cout << "ERROR - the abool could not be pre-decremented!" << std::endl; 64 | } else { 65 | std::cout << "Passed - the abool can be pre-decremented" << std::endl; 66 | } 67 | } 68 | if (!error) { 69 | if (b--) { 70 | error = true; 71 | std::cout << "ERROR - the abool could not be post-decremented!" << std::endl; 72 | } else { 73 | std::cout << "Passed - the abool can be post-decremented" << std::endl; 74 | } 75 | if (!b) { 76 | error = true; 77 | std::cout << "ERROR - the abool is not correct post-decremented!" << std::endl; 78 | } else { 79 | std::cout << "Passed - the abool is correct post-decremented" << std::endl; 80 | } 81 | } 82 | if (!error) { 83 | b = true; 84 | b += 3; 85 | if (b) { 86 | error = true; 87 | std::cout << "ERROR - the abool could not be incremented by 3 (to false)!" << std::endl; 88 | } else { 89 | std::cout << "Passed - the abool can be incremented by 3 (to " << b << ")" << std::endl; 90 | } 91 | } 92 | 93 | /** 94 | * Check the atomic int32_t - setting values, changing values, etc. 95 | */ 96 | aint32_t i; 97 | if (!error) { 98 | if (i != 0) { 99 | error = true; 100 | std::cout << "ERROR - the aint32_t does not have the correct default value (0)!" << std::endl; 101 | } else { 102 | std::cout << "Passed - the aint32_t has the correct default value (0)" << std::endl; 103 | } 104 | } 105 | if (!error) { 106 | i = 10; 107 | if (i != 10) { 108 | error = true; 109 | std::cout << "ERROR - the aint32_t could not be assigned the value 10!" << std::endl; 110 | } else { 111 | std::cout << "Passed - the aint32_t can be assigned a value (" << i << ")" << std::endl; 112 | } 113 | } 114 | if (!error) { 115 | if (++i != 11) { 116 | error = true; 117 | std::cout << "ERROR - the aint32_t could not be pre-incremented!" << std::endl; 118 | } else { 119 | std::cout << "Passed - the aint32_t can be pre-incremented" << std::endl; 120 | } 121 | } 122 | if (!error) { 123 | if (i++ != 11) { 124 | error = true; 125 | std::cout << "ERROR - the aint32_t could not be post-incremented!" << std::endl; 126 | } else { 127 | std::cout << "Passed - the aint32_t can be post-incremented" << std::endl; 128 | } 129 | if (i != 12) { 130 | error = true; 131 | std::cout << "ERROR - the aint32_t is not correct post-incremented!" << std::endl; 132 | } else { 133 | std::cout << "Passed - the aint32_t is correct post-incremented" << std::endl; 134 | } 135 | } 136 | if (!error) { 137 | i = 10; 138 | i += 10; 139 | if (i != 20) { 140 | error = true; 141 | std::cout << "ERROR - the aint32_t can not be incremented!" << std::endl; 142 | } else { 143 | std::cout << "Passed - the aint32_t can be incremented" << std::endl; 144 | } 145 | 146 | aint32_t j = 20; 147 | if (i != j) { 148 | error = true; 149 | std::cout << "ERROR - the aint32_t does not properly '!=' itself!" << std::endl; 150 | } else { 151 | std::cout << "Passed - the aint32_t properly handles '!='" << std::endl; 152 | } 153 | } 154 | 155 | std::cout << (error ? "FAILED!" : "SUCCESS") << std::endl; 156 | return (error ? 1 : 0); 157 | } 158 | -------------------------------------------------------------------------------- /tests/cqueue.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * This is the tests for the trie 3 | */ 4 | // System Headers 5 | #include 6 | #include 7 | 8 | // Third-Party Headers 9 | 10 | // Other Headers 11 | #include "cqueue.h" 12 | #include "util/timer.h" 13 | 14 | 15 | class blob { 16 | public: 17 | blob() : _when(0) { } 18 | blob(uint64_t aWhen) : _when(aWhen) { } 19 | virtual ~blob() { } 20 | void setValue(uint64_t aValue) { _when = aValue; } 21 | uint64_t getValue() const { return _when; } 22 | private: 23 | uint64_t _when; 24 | }; 25 | 26 | uint64_t key_value( const blob *aValue ) 27 | { 28 | return (*aValue).getValue(); 29 | } 30 | 31 | class counter : public dkit::trie::functor 32 | { 33 | public: 34 | counter() : _cnt(0) { } 35 | virtual ~counter() { } 36 | virtual bool process( volatile dkit::trie::Node & aNode ) 37 | { 38 | ++_cnt; 39 | return true; 40 | } 41 | uint64_t getCount() { return _cnt; } 42 | private: 43 | uint64_t _cnt; 44 | }; 45 | 46 | int main(int argc, char *argv[]) { 47 | bool error = false; 48 | 49 | /** 50 | * Make a conflation queue: 51 | * - holds (blob *) instances 52 | * - max 2^17 = 128k elements 53 | * - SP/SC 54 | * - 64-bit key for conflation 55 | * - max 2^5 = 32 keys in the pool 56 | */ 57 | dkit::cqueue q; 58 | std::cout << "cqueue<> has been created... pushing values..." << std::endl; 59 | 60 | uint64_t cnt = 65535; 61 | uint64_t sz = 0; 62 | if (!error) { 63 | // get the starting time 64 | uint64_t goTime = dkit::util::timer::usecStamp(); 65 | for (uint64_t i = 0; i < cnt; ++i) { 66 | blob *b = new blob(i); 67 | q.push(b); 68 | } 69 | goTime = dkit::util::timer::usecStamp() - goTime; 70 | std::cout << "pushes took " << goTime << " usec ... " 71 | << 1.0*goTime/cnt << " usec/push" << std::endl; 72 | if ((sz = q.size()) == cnt) { 73 | std::cout << "Success - the cqueue has " << sz << " elements!" << std::endl; 74 | } else { 75 | error = true; 76 | std::cout << "ERROR - the cqueue has " << sz << " elements, and it should have " << cnt << "!" << std::endl; 77 | } 78 | } 79 | 80 | if (!error) { 81 | counter worker; 82 | // get the starting time 83 | uint64_t goTime = dkit::util::timer::usecStamp(); 84 | q.apply(worker); 85 | goTime = dkit::util::timer::usecStamp() - goTime; 86 | if (worker.getCount() == cnt) { 87 | std::cout << "Success - the counter worker found: " << worker.getCount() << " elements in the trie in " << goTime/1000.0 << " msec" << std::endl; 88 | } else { 89 | error = true; 90 | std::cout << "ERROR - the counter worker found: " << worker.getCount() << " elements in the trie, and it should have found " << cnt << std::endl; 91 | } 92 | } 93 | 94 | if (!error) { 95 | // get the starting time 96 | uint64_t goTime = dkit::util::timer::usecStamp(); 97 | blob *bp = NULL; 98 | for (uint64_t i = 0; i < cnt; ++i) { 99 | if (!q.pop(bp)) { 100 | error = true; 101 | std::cout << "ERROR - failed to pop #" << i << "!" << std::endl; 102 | break; 103 | } 104 | if (bp != NULL) { 105 | delete bp; 106 | bp = NULL; 107 | } 108 | } 109 | if (!error) { 110 | goTime = dkit::util::timer::usecStamp() - goTime; 111 | std::cout << "simple pops took " << goTime << " usec ... " 112 | << 1.0*goTime/cnt << " usec/pop" << std::endl; 113 | } 114 | } 115 | 116 | // now let's test the conflation aspect 117 | if (!error) { 118 | // get the starting time 119 | uint64_t goTime = dkit::util::timer::usecStamp(); 120 | cnt = 10; 121 | sz = 10; 122 | for (uint64_t i = 0; i < cnt; ++i) { 123 | blob *b = new blob(i); 124 | q.push(b); 125 | } 126 | goTime = dkit::util::timer::usecStamp() - goTime; 127 | std::cout << "pushes took " << goTime << " usec ... " 128 | << 1.0*goTime/cnt << " usec/push" << std::endl; 129 | if ((sz = q.size()) == cnt) { 130 | std::cout << "Success - the cqueue has " << sz << " elements!" << std::endl; 131 | } else { 132 | error = true; 133 | std::cout << "ERROR - the cqueue has " << sz << " elements, and it should have " << cnt << "!" << std::endl; 134 | } 135 | 136 | goTime = dkit::util::timer::usecStamp(); 137 | for (uint64_t i = 0; i < cnt; ++i) { 138 | blob *b = new blob(i); 139 | q.push(b); 140 | } 141 | goTime = dkit::util::timer::usecStamp() - goTime; 142 | std::cout << "duplicate pushes took " << goTime << " usec ... " 143 | << 1.0*goTime/cnt << " usec/push" << std::endl; 144 | if ((sz = q.size()) == cnt) { 145 | std::cout << "Success - the cqueue has " << sz << " elements!" << std::endl; 146 | } else { 147 | error = true; 148 | std::cout << "ERROR - the cqueue has " << sz << " elements, and it should have " << cnt << "!" << std::endl; 149 | } 150 | 151 | goTime = dkit::util::timer::usecStamp(); 152 | blob *bp = NULL; 153 | for (uint64_t i = 0; i < cnt; ++i) { 154 | if (!q.pop(bp)) { 155 | error = true; 156 | std::cout << "ERROR - failed to pop #" << i << "!" << std::endl; 157 | break; 158 | } 159 | if (bp != NULL) { 160 | delete bp; 161 | bp = NULL; 162 | } 163 | } 164 | if (!error) { 165 | goTime = dkit::util::timer::usecStamp() - goTime; 166 | std::cout << "final size=" << q.size() << " ... simple pops took " 167 | << goTime << " usec ... " << 1.0*goTime/cnt 168 | << " usec/pop" << std::endl; 169 | } 170 | } 171 | 172 | std::cout << (error ? "FAILED!" : "SUCCESS") << std::endl; 173 | return (error ? 1 : 0); 174 | } 175 | -------------------------------------------------------------------------------- /tests/drain.h: -------------------------------------------------------------------------------- 1 | /** 2 | * In order to test the SC queue, we need to have a "drain" - something 3 | * that will pop() and count everything off the queue, and then 4 | * we can look at the results and see if what we have removed is equal 5 | * to all the data that all the "hammers" have been able to place on the 6 | * queue. 7 | */ 8 | #ifndef __DKIT_TEST_DRAIN_H 9 | #define __DKIT_TEST_DRAIN_H 10 | 11 | // System Headers 12 | #include 13 | #include 14 | #include 15 | 16 | // Third-Party Headers 17 | #include 18 | 19 | // Other Headers 20 | #include "FIFO.h" 21 | 22 | 23 | /** 24 | * Main class definition - and implementation... 25 | */ 26 | class Drain 27 | { 28 | public: 29 | /** 30 | * This is the standard constructor - it takes a pointer to a queue 31 | * and a count of the number of things to push onto that queue at 32 | * the proper time. This sets up this hammer to be ready to go when 33 | * the time comes, but DOES NOT start the hammering at this time. 34 | */ 35 | Drain( uint32_t anID, dkit::FIFO *aQueue ) : 36 | mID(anID), 37 | mQueue(aQueue), 38 | mThread(), 39 | mStopOnEmpty(false), 40 | mCount(0), 41 | mDone(false) 42 | { 43 | } 44 | 45 | /** 46 | * This is the standard destructor that needs to be on all classes. 47 | */ 48 | virtual ~Drain() 49 | { 50 | } 51 | 52 | /** 53 | * This method actually STARTS the hammering this class is going 54 | * to do. Clearly, if there's no queue to hammer, then there's not 55 | * a lot this guys is GOING to do, but that's just a trivial case. 56 | */ 57 | void start() 58 | { 59 | mDone = false; 60 | mStopOnEmpty = false; 61 | std::ostringstream msg; 62 | msg << "[Drain::start(" << mID << ")] - starting the drain thread..." << std::endl; 63 | std::cout << msg.str(); 64 | mThread = boost::thread(&Drain::doIt, this); 65 | } 66 | 67 | /** 68 | * This method will be called to tell the Drain that the next time 69 | * it tries to pop() something from the queue, and fails, to just 70 | * stop as the queue has no more active producers, and it's just time 71 | * to call it a day. 72 | */ 73 | void stopOnEmpty() 74 | { 75 | mStopOnEmpty = true; 76 | } 77 | 78 | /** 79 | * This method can be used to see if the hammer is done with all 80 | * the work it was assigned to do. Simple, but very effective. 81 | */ 82 | bool isDone() 83 | { 84 | return mDone; 85 | } 86 | 87 | /** 88 | * This method will return the number of SUCCESSFUL pop()s we have 89 | * done on the queue. It's a way to know if we have balanced the 90 | * pushed data with the popped data. 91 | */ 92 | uint32_t getCount() 93 | { 94 | return mCount; 95 | } 96 | 97 | private: 98 | /** 99 | * Because the default constructor doesn't allow for a queue, it's 100 | * really the trivial case of nothing to do. So let's not let anyone 101 | * get at it. This is just not something we need to support. 102 | */ 103 | Drain() : 104 | mID(0), 105 | mQueue(NULL), 106 | mThread(), 107 | mStopOnEmpty(false), 108 | mCount(0), 109 | mDone(false) 110 | { 111 | } 112 | 113 | /** 114 | * This is the method that actually pushes the elements onto the 115 | * provided queue. It's nothing much, but it makes sure that all 116 | * the push() calls are successful - or logs what's up. 117 | */ 118 | void doIt() 119 | { 120 | // if we have a queue, push on what we've been asked to 121 | if (mQueue != NULL) { 122 | int32_t val; 123 | while (true) { 124 | // pop() until we are empty... 125 | while (mQueue->pop(val)) { 126 | ++mCount; 127 | }; 128 | // ...and then wait a bit to try it all again 129 | if (!mStopOnEmpty) { 130 | usleep(100000); 131 | } else { 132 | break; 133 | } 134 | } 135 | } 136 | // now set the flag that we are done 137 | std::ostringstream msg; 138 | msg << "[Drain::doIt(" << mID << ")] - popped " << mCount << " items off the queue" << std::endl; 139 | std::cout << msg.str(); 140 | mDone = true; 141 | } 142 | 143 | // these are the ivars I'm going to need for the hammer 144 | uint32_t mID; 145 | dkit::FIFO *mQueue; 146 | // ...and this is the thread to do all the work 147 | boost::thread mThread; 148 | // this indicates if we are to stop once we are empty 149 | bool mStopOnEmpty; 150 | // this is the count of successful pop()s we have done 151 | uint32_t mCount; 152 | // when we are done, this will be 'true', which can be monitored 153 | bool mDone; 154 | }; 155 | 156 | #endif // __DKIT_TEST_DRAIN_H 157 | -------------------------------------------------------------------------------- /tests/hammer.h: -------------------------------------------------------------------------------- 1 | /** 2 | * In order to test the MP queue, we need to have a "hammer" - something 3 | * that will push() on a fixed number of "things" to the queue, and then 4 | * we can look at the results and see if all these "hammers" have been 5 | * able to place their data on the queue. 6 | */ 7 | #ifndef __DKIT_TEST_HAMMER_H 8 | #define __DKIT_TEST_HAMMER_H 9 | 10 | // System Headers 11 | #include 12 | #include 13 | #include 14 | 15 | // Third-Party Headers 16 | #include 17 | 18 | // Other Headers 19 | #include "FIFO.h" 20 | 21 | 22 | /** 23 | * Main class definition - and implementation... 24 | */ 25 | class Hammer 26 | { 27 | public: 28 | /** 29 | * This is the standard constructor - it takes a pointer to a queue 30 | * and a count of the number of things to push onto that queue at 31 | * the proper time. This sets up this hammer to be ready to go when 32 | * the time comes, but DOES NOT start the hammering at this time. 33 | */ 34 | Hammer( uint32_t anID, dkit::FIFO *aQueue, uint32_t aCount ) : 35 | mID(anID), 36 | mQueue(aQueue), 37 | mCount(aCount), 38 | mThread(), 39 | mDone(false) 40 | { 41 | } 42 | 43 | /** 44 | * This is the standard destructor that needs to be on all classes. 45 | */ 46 | virtual ~Hammer() 47 | { 48 | } 49 | 50 | /** 51 | * This method actually STARTS the hammering this class is going 52 | * to do. Clearly, if there's no queue to hammer, then there's not 53 | * a lot this guys is GOING to do, but that's just a trivial case. 54 | */ 55 | void start() 56 | { 57 | mDone = false; 58 | std::ostringstream msg; 59 | msg << "[Hammer::start(" << mID << ")] - starting the hammer thread..." << std::endl; 60 | std::cout << msg.str(); 61 | mThread = boost::thread(&Hammer::doIt, this); 62 | } 63 | 64 | /** 65 | * This method can be used to see if the hammer is done with all 66 | * the work it was assigned to do. Simple, but very effective. 67 | */ 68 | bool isDone() 69 | { 70 | return mDone; 71 | } 72 | 73 | private: 74 | /** 75 | * Because the default constructor doesn't allow for a queue, it's 76 | * really the trivial case of nothing to do. So let's not let anyone 77 | * get at it. This is just not something we need to support. 78 | */ 79 | Hammer() : 80 | mID(0), 81 | mQueue(NULL), 82 | mCount(500), 83 | mThread(), 84 | mDone(false) 85 | { 86 | } 87 | 88 | /** 89 | * This is the method that actually pushes the elements onto the 90 | * provided queue. It's nothing much, but it makes sure that all 91 | * the push() calls are successful - or logs what's up. 92 | */ 93 | void doIt() 94 | { 95 | // if we have a queue, push on what we've been asked to 96 | if (mQueue != NULL) { 97 | for (uint32_t i = 0; i < mCount; ++i) { 98 | if (!mQueue->push(i)) { 99 | std::ostringstream msg; 100 | msg << "[Hammer::doIt(" << mID << ")] - unable to push " << i << " onto queue!" << std::endl; 101 | std::cerr << msg.str(); 102 | break; 103 | } 104 | } 105 | } 106 | // now set the flag that we are done 107 | std::ostringstream msg; 108 | msg << "[Hammer::doIt(" << mID << ")] - done pushing " << mCount << " items onto queue" << std::endl; 109 | std::cout << msg.str(); 110 | mDone = true; 111 | } 112 | 113 | // these are the ivars I'm going to need for the hammer 114 | uint32_t mID; 115 | dkit::FIFO *mQueue; 116 | uint32_t mCount; 117 | // ...and this is the thread to do all the work 118 | boost::thread mThread; 119 | // when we are done, this will be 'true', which can be monitored 120 | bool mDone; 121 | }; 122 | 123 | #endif // __DKIT_TEST_HAMMER_H 124 | -------------------------------------------------------------------------------- /tests/linkedFIFO.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * This is the tests for the MPSC & SPMC LinkedFIFO 3 | */ 4 | // System Headers 5 | #include 6 | #include 7 | 8 | // Third-Party Headers 9 | #include 10 | 11 | // Other Headers 12 | #include "mpsc/LinkedFIFO.h" 13 | #include "spmc/LinkedFIFO.h" 14 | #include "util/timer.h" 15 | #include "hammer.h" 16 | #include "drain.h" 17 | 18 | 19 | /** 20 | * This is the main entry point for the testing code 21 | */ 22 | int main(int argc, char *argv[]) { 23 | bool error = false; 24 | 25 | /** 26 | * First, let's check the MP/SC Linked FIFO... 27 | */ 28 | if (!error) { 29 | // make a linked FIFO of int32_t values 30 | 31 | dkit::mpsc::LinkedFIFO q; 32 | // put 500 values on the queue - and check the size 33 | std::cout << "=== Testing speed and correctness of MP/SC LinkedFIFO ===" << std::endl; 34 | 35 | // get the starting time 36 | uint64_t goTime = dkit::util::timer::usecStamp(); 37 | 38 | // do this often enough to rotate through the size of the values 39 | int32_t trips = 25000; 40 | for (int32_t cycle = 0; cycle < trips; ++cycle) { 41 | // put 500 values on the queue - and check the size 42 | for (int32_t i = 0; i < 500; ++i) { 43 | if (!q.push(i)) { 44 | error = true; 45 | std::cout << "ERROR - could not push the value " << i << std::endl; 46 | break; 47 | } 48 | } 49 | // now check the size 50 | if (!error) { 51 | if (q.size() != 500) { 52 | error = true; 53 | std::cout << "ERROR - pushed 500 integers, but size() reports only " << q.size() << std::endl; 54 | } else { 55 | if (cycle == 0) { 56 | std::cout << "Passed - pushed on 500 integers" << std::endl; 57 | } 58 | } 59 | } 60 | // pop off 500 integers and it should be empty 61 | if (!error) { 62 | int32_t v = 0; 63 | for (int32_t i = 0; i < 500; ++i) { 64 | if (!q.pop(v) || (v != i)) { 65 | error = true; 66 | std::cout << "ERROR - could not pop the value " << i << std::endl; 67 | break; 68 | } 69 | } 70 | } 71 | // now check the size 72 | if (!error) { 73 | if (!q.empty()) { 74 | error = true; 75 | std::cout << "ERROR - popped 500 integers, but size() reports " << q.size() << std::endl; 76 | } else { 77 | if (cycle == 0) { 78 | std::cout << "Passed - popped all 500 integers" << std::endl; 79 | } 80 | } 81 | } 82 | } 83 | 84 | // get the elapsed time 85 | goTime = dkit::util::timer::usecStamp() - goTime; 86 | std::cout << "Passed - did " << (trips * 500) << " push/pop pairs in " << (goTime/1000.0) << "ms = " << ((goTime * 1000.0)/(trips * 500.0)) << "ns/op" << std::endl; 87 | 88 | /** 89 | * Make a set of Hammers and a Drain and test threading 90 | */ 91 | Hammer *src[] = { NULL, NULL, NULL, NULL }; 92 | for (uint32_t i = 0; i < 4; ++i) { 93 | if ((src[i] = new Hammer(i, &q, 5000)) == NULL) { 94 | std::cout << "PROBLEM - unable to make Hammer #" << i << "!" << std::endl; 95 | break; 96 | } 97 | } 98 | Drain dest(0, &q); 99 | // now start the drain then all the hammers 100 | dest.start(); 101 | for (uint32_t i = 0; i < 4; ++i) { 102 | if (src[i] != NULL) { 103 | src[i]->start(); 104 | } 105 | } 106 | // now let's wait for all the hammers to be done 107 | bool allSent = false; 108 | while (!allSent) { 109 | // assume done, but check for the first failure 110 | allSent = true; 111 | // now let's check all the hammers to see if they are done 112 | for (uint32_t i = 0; i < 4; ++i) { 113 | if (src[i] != NULL) { 114 | if (!src[i]->isDone()) { 115 | allSent = false; 116 | break; 117 | } 118 | } 119 | } 120 | // see if we need to wait a bit to try again 121 | if (!allSent) { 122 | usleep(250000); 123 | } 124 | } 125 | // now tell the drain to stop when the queue is empty 126 | dest.stopOnEmpty(); 127 | while (!dest.isDone()) { 128 | usleep(250000); 129 | } 130 | // now let's see what we have 131 | uint32_t cnt = dest.getCount(); 132 | if (cnt == 20000) { 133 | std::cout << "Passed - popped " << cnt << " integers, from four hammer threads" << std::endl; 134 | } else { 135 | std::cout << "ERROR - popped " << cnt << " integers, but should have popped " << 20000 << std::endl; 136 | } 137 | // finally, clean things up 138 | for (uint32_t i = 0; i < 4; ++i) { 139 | if (src[i] != NULL) { 140 | delete src[i]; 141 | src[i] = NULL; 142 | } 143 | } 144 | } 145 | 146 | /** 147 | * Next, let's check the SP/MC Linked FIFO... 148 | */ 149 | if (!error) { 150 | // make a linked FIFO of int32_t values 151 | dkit::spmc::LinkedFIFO q; 152 | // put 500 values on the queue - and check the size 153 | std::cout << "=== Testing speed and correctness of SP/MC LinkedFIFO ===" << std::endl; 154 | 155 | // get the starting time 156 | uint64_t goTime = dkit::util::timer::usecStamp(); 157 | 158 | // do this often enough to rotate through the size of the values 159 | int32_t trips = 25000; 160 | for (int32_t cycle = 0; cycle < trips; ++cycle) { 161 | // put 500 values on the queue - and check the size 162 | for (int32_t i = 0; i < 500; ++i) { 163 | if (!q.push(i)) { 164 | error = true; 165 | std::cout << "ERROR - could not push the value " << i << std::endl; 166 | break; 167 | } 168 | } 169 | // now check the size 170 | if (!error) { 171 | if (q.size() != 500) { 172 | error = true; 173 | std::cout << "ERROR - pushed 500 integers, but size() reports only " << q.size() << std::endl; 174 | } else { 175 | if (cycle == 0) { 176 | std::cout << "Passed - pushed on 500 integers" << std::endl; 177 | } 178 | } 179 | } 180 | // pop off 500 integers and it should be empty 181 | if (!error) { 182 | int32_t v = 0; 183 | for (int32_t i = 0; i < 500; ++i) { 184 | if (!q.pop(v) || (v != i)) { 185 | error = true; 186 | std::cout << "ERROR - could not pop the value " << i << std::endl; 187 | break; 188 | } 189 | } 190 | } 191 | // now check the size 192 | if (!error) { 193 | if (!q.empty()) { 194 | error = true; 195 | std::cout << "ERROR - popped 500 integers, but size() reports " << q.size() << std::endl; 196 | } else { 197 | if (cycle == 0) { 198 | std::cout << "Passed - popped all 500 integers" << std::endl; 199 | } 200 | } 201 | } 202 | } 203 | 204 | // get the elapsed time 205 | goTime = dkit::util::timer::usecStamp() - goTime; 206 | std::cout << "Passed - did " << (trips * 500) << " push/pop pairs in " << (goTime/1000.0) << "ms = " << ((goTime * 1000.0)/(trips * 500.0)) << "ns/op" << std::endl; 207 | 208 | /** 209 | * Make a Hammer and a set of Drains and test threading 210 | */ 211 | Hammer src(0, &q, 20000); 212 | Drain *dest[] = { NULL, NULL, NULL, NULL }; 213 | for (uint32_t i = 0; i < 4; ++i) { 214 | if ((dest[i] = new Drain(i, &q)) == NULL) { 215 | std::cout << "PROBLEM - unable to make Drain #" << i << "!" << std::endl; 216 | break; 217 | } 218 | } 219 | // now start the drains then the hammer 220 | for (uint32_t i = 0; i < 4; ++i) { 221 | if (dest[i] != NULL) { 222 | dest[i]->start(); 223 | } 224 | } 225 | src.start(); 226 | // now let's wait for the hammer to be done 227 | while (!src.isDone()) { 228 | usleep(250000); 229 | } 230 | // now tell the drains to stop when the queue is empty 231 | for (uint32_t i = 0; i < 4; ++i) { 232 | if (dest[i] != NULL) { 233 | dest[i]->stopOnEmpty(); 234 | } 235 | } 236 | // wait for all the drains to be done 237 | bool allDone = false; 238 | uint32_t cnt[] = { 0, 0, 0, 0 }; 239 | uint32_t total = 0; 240 | while (!allDone) { 241 | // assume done, but check for the first failure 242 | allDone = true; 243 | // now let's check all the drains to see if they are done 244 | for (uint32_t i = 0; i < 4; ++i) { 245 | if (dest[i] != NULL) { 246 | if (!dest[i]->isDone()) { 247 | allDone = false; 248 | break; 249 | } 250 | // tally up the counts 251 | cnt[i] = dest[i]->getCount(); 252 | total += cnt[i]; 253 | } 254 | } 255 | // see if we need to wait a bit to try again 256 | if (!allDone) { 257 | usleep(250000); 258 | } 259 | } 260 | // now let's see what we have 261 | if (total == 20000) { 262 | std::cout << "Passed - popped " << total << " integers (" << cnt[0] << "+" << cnt[1] << "+" << cnt[2] << "+" << cnt[3] << "), with four drain threads" << std::endl; 263 | } else { 264 | std::cout << "Error - popped " << total << " integers (" << cnt[0] << "+" << cnt[1] << "+" << cnt[2] << "+" << cnt[3] << "), with four drain threads - but should have popped 20000" << std::endl; 265 | } 266 | // finally, clean things up 267 | for (uint32_t i = 0; i < 4; ++i) { 268 | if (dest[i] != NULL) { 269 | delete dest[i]; 270 | dest[i] = NULL; 271 | } 272 | } 273 | } 274 | 275 | std::cout << (error ? "FAILED!" : "SUCCESS") << std::endl; 276 | return (error ? 1 : 0); 277 | } 278 | -------------------------------------------------------------------------------- /tests/mpsc_fifo.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * This is the tests for the MPSC CircularFIFO 3 | */ 4 | // System Headers 5 | #include 6 | #include 7 | 8 | // Third-Party Headers 9 | 10 | // Other Headers 11 | #include "mpsc/CircularFIFO.h" 12 | #include "util/timer.h" 13 | #include "hammer.h" 14 | #include "drain.h" 15 | 16 | int main(int argc, char *argv[]) { 17 | bool error = false; 18 | 19 | // make a circular FIFO of 1024 int32_t values - max 20 | dkit::mpsc::CircularFIFO q; 21 | // put 500 values on the queue - and check the size 22 | if (!error) { 23 | std::cout << "=== Testing speed and correctness of CircularFIFO ===" << std::endl; 24 | 25 | // get the starting time 26 | uint64_t goTime = dkit::util::timer::usecStamp(); 27 | 28 | // do this often enough to rotate through the size of the values 29 | int32_t trips = 100000; 30 | for (int32_t cycle = 0; cycle < trips; ++cycle) { 31 | // put 500 values on the queue - and check the size 32 | for (int32_t i = 0; i < 500; ++i) { 33 | if (!q.push(i)) { 34 | error = true; 35 | std::cout << "ERROR - could not push the value " << i << std::endl; 36 | break; 37 | } 38 | } 39 | // now check the size 40 | if (!error) { 41 | if (q.size() != 500) { 42 | error = true; 43 | std::cout << "ERROR - pushed 500 integers, but size() reports only " << q.size() << std::endl; 44 | } else { 45 | if (cycle == 0) { 46 | std::cout << "Passed - pushed on 500 integers" << std::endl; 47 | } 48 | } 49 | } 50 | // pop off 500 integers and it should be empty 51 | if (!error) { 52 | int32_t v = 0; 53 | for (int32_t i = 0; i < 500; ++i) { 54 | if (!q.pop(v) || (v != i)) { 55 | error = true; 56 | std::cout << "ERROR - could not pop the value " << i << std::endl; 57 | break; 58 | } 59 | } 60 | } 61 | // now check the size 62 | if (!error) { 63 | if (!q.empty()) { 64 | error = true; 65 | std::cout << "ERROR - popped 500 integers, but size() reports " << q.size() << std::endl; 66 | } else { 67 | if (cycle == 0) { 68 | std::cout << "Passed - popped all 500 integers" << std::endl; 69 | } 70 | } 71 | } 72 | // now make sure we can't pop() anything 73 | if (!error) { 74 | int32_t v = 0; 75 | if (!q.pop(v)) { 76 | if (cycle == 0) { 77 | std::cout << "Passed - unable to pop from an empty queue" << std::endl; 78 | } 79 | } else { 80 | error = true; 81 | std::cout << "ERROR - popped " << v << " from an empty queue - shouldn't be possible" << std::endl; 82 | } 83 | } 84 | } 85 | 86 | // get the elapsed time 87 | goTime = dkit::util::timer::usecStamp() - goTime; 88 | std::cout << "Passed - did " << (trips * 500) << " push/pop pairs in " << (goTime/1000.0) << "ms = " << ((goTime * 1000.0)/(trips * 500.0)) << "ns/op" << std::endl; 89 | } 90 | 91 | // check on how the crash recovery works 92 | if (!error) { 93 | // make sure it's all cleared out for this test 94 | q.clear(); 95 | 96 | std::cout << "=== Testing crash surviveability CircularFIFO ===" << std::endl; 97 | 98 | // push values starting at 0 up until we can't push any more 99 | int32_t v = 0; 100 | int32_t lim = 0; 101 | while (q.push(lim)) { 102 | ++lim; 103 | } 104 | std::cout << "Passed - Failed on pushing " << lim << std::endl; 105 | for (int32_t i = 0; i < lim; ++i) { 106 | if (!q.pop(v) || (v != i)) { 107 | error = true; 108 | std::cout << "ERROR - could not pop the value " << i << std::endl; 109 | break; 110 | } 111 | } 112 | if (!error) { 113 | std::cout << "Passed - after crash, still able to recover all values" << std::endl; 114 | } 115 | } 116 | 117 | /** 118 | * Make a set of Hammers and a Drain and test threading 119 | */ 120 | if (!error) { 121 | Hammer *src[] = { NULL, NULL, NULL, NULL }; 122 | for (uint32_t i = 0; i < 4; ++i) { 123 | if ((src[i] = new Hammer(i, &q, 250)) == NULL) { 124 | std::cout << "PROBLEM - unable to make Hammer #" << i << "!" << std::endl; 125 | break; 126 | } 127 | } 128 | Drain dest(0, &q); 129 | // now start the drain then all the hammers 130 | dest.start(); 131 | for (uint32_t i = 0; i < 4; ++i) { 132 | if (src[i] != NULL) { 133 | src[i]->start(); 134 | } 135 | } 136 | // now let's wait for all the hammers to be done 137 | bool allSent = false; 138 | while (!allSent) { 139 | // assume done, but check for the first failure 140 | allSent = true; 141 | // now let's check all the hammers to see if they are done 142 | for (uint32_t i = 0; i < 4; ++i) { 143 | if (src[i] != NULL) { 144 | if (!src[i]->isDone()) { 145 | allSent = false; 146 | break; 147 | } 148 | } 149 | } 150 | // see if we need to wait a bit to try again 151 | if (!allSent) { 152 | usleep(250000); 153 | } 154 | } 155 | // now tell the drain to stop when the queue is empty 156 | dest.stopOnEmpty(); 157 | while (!dest.isDone()) { 158 | usleep(250000); 159 | } 160 | // now let's see what we have 161 | uint32_t cnt = dest.getCount(); 162 | if (cnt == 1000) { 163 | std::cout << "Passed - popped " << cnt << " integers, from four hammer threads" << std::endl; 164 | } else { 165 | std::cout << "ERROR - popped " << cnt << " integers, but should have popped " << 1000 << std::endl; 166 | } 167 | // finally, clean things up 168 | for (uint32_t i = 0; i < 4; ++i) { 169 | if (src[i] != NULL) { 170 | delete src[i]; 171 | src[i] = NULL; 172 | } 173 | } 174 | } 175 | 176 | std::cout << (error ? "FAILED!" : "SUCCESS") << std::endl; 177 | return (error ? 1 : 0); 178 | } 179 | -------------------------------------------------------------------------------- /tests/pool.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * This is the tests for the pool 3 | */ 4 | // System Headers 5 | #include 6 | #include 7 | 8 | // Third-Party Headers 9 | 10 | // Other Headers 11 | #include "pool.h" 12 | #include "util/timer.h" 13 | 14 | int main(int argc, char *argv[]) { 15 | bool error = false; 16 | 17 | // make a pool of 2^5 (32) std::string pointers 18 | dkit::pool pool; 19 | // create an array to hold these guys while I'm "using" them 20 | std::string *inUse[50]; 21 | 22 | // grab a bunch and make sure they are real 23 | if (!error) { 24 | std::cout << "=== Getting 50 std::string Pointers ===" << std::endl; 25 | char buff[80]; 26 | for (uint8_t i = 0; i < 50; ++i) { 27 | if ((inUse[i] = pool.next()) == NULL) { 28 | error = true; 29 | std::cout << "ERROR - could not get std::string #" << i << std::endl; 30 | break; 31 | } 32 | // fill it in with some data 33 | snprintf(buff, 79, "string #%d", (i+1)); 34 | inUse[i]->assign(buff); 35 | } 36 | // log if we are OK to here 37 | if (!error) { 38 | std::cout << "Passed - created 50 new std::strings" << std::endl; 39 | } 40 | } 41 | 42 | // make sure they are all there 43 | if (!error) { 44 | std::cout << "=== Verifying 50 std::string Pointers ===" << std::endl; 45 | char buff[80]; 46 | for (uint8_t i = 0; i < 50; ++i) { 47 | if (inUse[i] == NULL) { 48 | error = true; 49 | std::cout << "ERROR - could not verify std::string #" << i << std::endl; 50 | break; 51 | } 52 | // check what it's loaded with 53 | snprintf(buff, 79, "string #%d", (i+1)); 54 | if (*inUse[i] != buff) { 55 | error = true; 56 | std::cout << "ERROR - std::string #" << i << " contained: '" << (*inUse[i]) << "'!" << std::endl; 57 | break; 58 | } 59 | } 60 | // log if we are OK to here 61 | if (!error) { 62 | std::cout << "Passed - verified the 50 std::strings" << std::endl; 63 | } 64 | } 65 | 66 | // check the size of the pool 67 | if (!error) { 68 | if (pool.size() == 0) { 69 | std::cout << "Passed - the pool is still empty" << std::endl; 70 | } else { 71 | error = true; 72 | std::cout << "ERROR - the pool is NOT empty! size=" << pool.size() << std::endl; 73 | } 74 | } 75 | 76 | // now start to recycle these back to the pool 77 | if (!error) { 78 | std::cout << "=== Recycling 50 std::string Pointers ===" << std::endl; 79 | for (uint8_t i = 0; i < 50; ++i) { 80 | // recycle a non-NULL emtry 81 | if (inUse[i] != NULL) { 82 | pool.recycle(inUse[i]); 83 | } 84 | // make sure it "tops out" at 31 items 85 | if (i < 31) { 86 | if (pool.size() != (i+1U)) { 87 | error = true; 88 | std::cout << "ERROR - count=" << (i+1) << " ...size=" << pool.size() << std::endl; 89 | } 90 | } else if (pool.size() != 31) { 91 | error = true; 92 | std::cout << "ERROR - capped count=" << (i+1) << " ...size=" << pool.size() << std::endl; 93 | } 94 | } 95 | } 96 | 97 | std::cout << (error ? "FAILED!" : "SUCCESS") << std::endl; 98 | return (error ? 1 : 0); 99 | } 100 | -------------------------------------------------------------------------------- /tests/receiver.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // receiver.cpp 3 | // ~~~~~~~~~~~~ 4 | // 5 | // Copyright (c) 2003-2012 Christopher M. Kohlhoff (chris at kohlhoff dot com) 6 | // 7 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 8 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 9 | // 10 | 11 | #include 12 | #include 13 | 14 | #include 15 | #include 16 | 17 | const short multicast_port = 30001; 18 | 19 | using namespace boost::asio; 20 | 21 | class receiver 22 | { 23 | public: 24 | receiver(io_service & io_service, 25 | const ip::address& listen_address, 26 | const ip::address& multicast_address) : 27 | socket_(io_service) 28 | { 29 | // Create the socket so that multiple may be bound to the same address. 30 | ip::udp::endpoint listen_endpoint(listen_address, multicast_port); 31 | socket_.open(listen_endpoint.protocol()); 32 | socket_.set_option(ip::udp::socket::reuse_address(true)); 33 | socket_.bind(listen_endpoint); 34 | 35 | // Join the multicast group. 36 | socket_.set_option(ip::multicast::join_group(multicast_address)); 37 | 38 | socket_.async_receive_from(buffer(data_, max_length), 39 | sender_endpoint_, 40 | boost::bind(&receiver::handle_receive_from, this, 41 | placeholders::error, 42 | placeholders::bytes_transferred)); 43 | } 44 | 45 | void handle_receive_from(const boost::system::error_code& error, 46 | size_t bytes_recvd) 47 | { 48 | if (!error) { 49 | std::cout.write(data_, bytes_recvd); 50 | std::cout << std::endl; 51 | socket_.async_receive_from(buffer(data_, max_length), 52 | sender_endpoint_, 53 | boost::bind(&receiver::handle_receive_from, this, 54 | placeholders::error, 55 | placeholders::bytes_transferred)); 56 | } 57 | } 58 | 59 | private: 60 | enum { 61 | max_length = 1024 62 | }; 63 | ip::udp::socket socket_; 64 | ip::udp::endpoint sender_endpoint_; 65 | char data_[max_length]; 66 | }; 67 | 68 | int main(int argc, char* argv[]) 69 | { 70 | try { 71 | if (argc != 3) { 72 | std::cerr << "Usage: receiver \n"; 73 | std::cerr << " For IPv4, try:\n"; 74 | std::cerr << " receiver 0.0.0.0 239.255.0.1\n"; 75 | std::cerr << " For IPv6, try:\n"; 76 | std::cerr << " receiver 0::0 ff31::8000:1234\n"; 77 | return 1; 78 | } 79 | 80 | io_service io_service; 81 | receiver r(io_service, ip::address::from_string(argv[1]), 82 | ip::address::from_string(argv[2])); 83 | io_service.run(); 84 | } catch (std::exception& e) { 85 | std::cerr << "Exception: " << e.what() << "\n"; 86 | } 87 | 88 | return 0; 89 | } 90 | -------------------------------------------------------------------------------- /tests/sender.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // sender.cpp 3 | // ~~~~~~~~~~ 4 | // 5 | // Copyright (c) 2003-2012 Christopher M. Kohlhoff (chris at kohlhoff dot com) 6 | // 7 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 8 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 9 | // 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | #include 16 | #include 17 | #include 18 | 19 | const short multicast_port = 30001; 20 | const int max_message_count = 10; 21 | 22 | using namespace boost::asio; 23 | 24 | class sender 25 | { 26 | public: 27 | sender(io_service & io_service, const ip::address & multicast_address) : 28 | endpoint_(multicast_address, multicast_port), 29 | socket_(io_service, endpoint_.protocol()), 30 | timer_(io_service), 31 | message_count_(0) 32 | { 33 | std::ostringstream os; 34 | os << "Message " << message_count_++; 35 | message_ = os.str(); 36 | socket_.async_send_to(buffer(message_), endpoint_, 37 | boost::bind(&sender::handle_send_to, this, 38 | placeholders::error)); 39 | } 40 | 41 | void handle_send_to(const boost::system::error_code& error) 42 | { 43 | if (!error && message_count_ < max_message_count) { 44 | timer_.expires_from_now(boost::posix_time::seconds(1)); 45 | timer_.async_wait(boost::bind(&sender::handle_timeout, this, 46 | placeholders::error)); 47 | } 48 | } 49 | 50 | void handle_timeout(const boost::system::error_code& error) 51 | { 52 | if (!error) { 53 | std::ostringstream os; 54 | os << "Message " << message_count_++; 55 | message_ = os.str(); 56 | socket_.async_send_to(buffer(message_), endpoint_, 57 | boost::bind(&sender::handle_send_to, this, 58 | placeholders::error)); 59 | } 60 | } 61 | 62 | private: 63 | boost::asio::ip::udp::endpoint endpoint_; 64 | boost::asio::ip::udp::socket socket_; 65 | boost::asio::deadline_timer timer_; 66 | int message_count_; 67 | std::string message_; 68 | }; 69 | 70 | int main(int argc, char* argv[]) 71 | { 72 | try { 73 | if (argc != 2) { 74 | std::cerr << "Usage: sender \n"; 75 | std::cerr << " For IPv4, try:\n"; 76 | std::cerr << " sender 239.255.0.1\n"; 77 | std::cerr << " For IPv6, try:\n"; 78 | std::cerr << " sender ff31::8000:1234\n"; 79 | return 1; 80 | } 81 | 82 | boost::asio::io_service io_service; 83 | sender s(io_service, boost::asio::ip::address::from_string(argv[1])); 84 | io_service.run(); 85 | } catch (std::exception& e) { 86 | std::cerr << "Exception: " << e.what() << "\n"; 87 | } 88 | 89 | return 0; 90 | } 91 | -------------------------------------------------------------------------------- /tests/spmc_fifo.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * This is the tests for the SPMC CircularFIFO 3 | */ 4 | // System Headers 5 | #include 6 | #include 7 | 8 | // Third-Party Headers 9 | 10 | // Other Headers 11 | #include "spmc/CircularFIFO.h" 12 | #include "util/timer.h" 13 | #include "hammer.h" 14 | #include "drain.h" 15 | 16 | int main(int argc, char *argv[]) { 17 | bool error = false; 18 | 19 | // make a circular FIFO of 1024 int32_t values - max 20 | dkit::spmc::CircularFIFO q; 21 | // put 500 values on the queue - and check the size 22 | if (!error) { 23 | std::cout << "=== Testing speed and correctness of CircularFIFO ===" << std::endl; 24 | 25 | // get the starting time 26 | uint64_t goTime = dkit::util::timer::usecStamp(); 27 | 28 | // do this often enough to rotate through the size of the values 29 | int32_t trips = 100000; 30 | for (int32_t cycle = 0; cycle < trips; ++cycle) { 31 | // put 500 values on the queue - and check the size 32 | for (int32_t i = 0; i < 500; ++i) { 33 | if (!q.push(i)) { 34 | error = true; 35 | std::cout << "ERROR - could not push the value " << i << std::endl; 36 | break; 37 | } 38 | } 39 | // now check the size 40 | if (!error) { 41 | if (q.size() != 500) { 42 | error = true; 43 | std::cout << "ERROR - pushed 500 integers, but size() reports only " << q.size() << std::endl; 44 | } else { 45 | if (cycle == 0) { 46 | std::cout << "Passed - pushed on 500 integers" << std::endl; 47 | } 48 | } 49 | } 50 | // pop off 500 integers and it should be empty 51 | if (!error) { 52 | int32_t v = 0; 53 | for (int32_t i = 0; i < 500; ++i) { 54 | if (!q.pop(v) || (v != i)) { 55 | error = true; 56 | std::cout << "ERROR - could not pop the value " << i << std::endl; 57 | break; 58 | } 59 | } 60 | } 61 | // now check the size 62 | if (!error) { 63 | if (!q.empty()) { 64 | error = true; 65 | std::cout << "ERROR - popped 500 integers, but size() reports " << q.size() << std::endl; 66 | } else { 67 | if (cycle == 0) { 68 | std::cout << "Passed - popped all 500 integers" << std::endl; 69 | } 70 | } 71 | } 72 | // now make sure we can't pop() anything 73 | if (!error) { 74 | int32_t v = 0; 75 | if (!q.pop(v)) { 76 | if (cycle == 0) { 77 | std::cout << "Passed - unable to pop from an empty queue" << std::endl; 78 | } 79 | } else { 80 | error = true; 81 | std::cout << "ERROR - popped " << v << " from an empty queue - shouldn't be possible" << std::endl; 82 | } 83 | } 84 | } 85 | 86 | // get the elapsed time 87 | goTime = dkit::util::timer::usecStamp() - goTime; 88 | std::cout << "Passed - did " << (trips * 500) << " push/pop pairs in " << (goTime/1000.0) << "ms = " << ((goTime * 1000.0)/(trips * 500.0)) << "ns/op" << std::endl; 89 | } 90 | 91 | // check on how the crash recovery works 92 | if (!error) { 93 | // make sure it's all cleared out for this test 94 | q.clear(); 95 | 96 | std::cout << "=== Testing crash surviveability CircularFIFO ===" << std::endl; 97 | 98 | // push values starting at 0 up until we can't push any more 99 | int32_t v = 0; 100 | int32_t lim = 0; 101 | while (q.push(lim)) { 102 | ++lim; 103 | } 104 | std::cout << "Passed - Failed on pushing " << lim << std::endl; 105 | for (int32_t i = 0; i < lim; ++i) { 106 | if (!q.pop(v) || (v != i)) { 107 | error = true; 108 | std::cout << "ERROR - could not pop the value " << i << std::endl; 109 | break; 110 | } 111 | } 112 | if (!error) { 113 | std::cout << "Passed - after crash, still able to recover all values" << std::endl; 114 | } 115 | } 116 | 117 | /** 118 | * Make a Hammer and a set of Drains and test threading 119 | */ 120 | if (!error) { 121 | Hammer src(0, &q, 1000); 122 | Drain *dest[] = { NULL, NULL, NULL, NULL }; 123 | for (uint32_t i = 0; i < 4; ++i) { 124 | if ((dest[i] = new Drain(i, &q)) == NULL) { 125 | std::cout << "PROBLEM - unable to make Drain #" << i << "!" << std::endl; 126 | break; 127 | } 128 | } 129 | // now start the drains then the hammer 130 | for (uint32_t i = 0; i < 4; ++i) { 131 | if (dest[i] != NULL) { 132 | dest[i]->start(); 133 | } 134 | } 135 | src.start(); 136 | // now let's wait for the hammer to be done 137 | while (!src.isDone()) { 138 | usleep(250000); 139 | } 140 | // now tell the drains to stop when the queue is empty 141 | for (uint32_t i = 0; i < 4; ++i) { 142 | if (dest[i] != NULL) { 143 | dest[i]->stopOnEmpty(); 144 | } 145 | } 146 | // wait for all the drains to be done 147 | bool allDone = false; 148 | uint32_t cnt[] = { 0, 0, 0, 0 }; 149 | uint32_t total = 0; 150 | while (!allDone) { 151 | // assume done, but check for the first failure 152 | allDone = true; 153 | // now let's check all the drains to see if they are done 154 | for (uint32_t i = 0; i < 4; ++i) { 155 | if (dest[i] != NULL) { 156 | if (!dest[i]->isDone()) { 157 | allDone = false; 158 | break; 159 | } 160 | // tally up the counts 161 | cnt[i] = dest[i]->getCount(); 162 | total += cnt[i]; 163 | } 164 | } 165 | // see if we need to wait a bit to try again 166 | if (!allDone) { 167 | usleep(250000); 168 | } 169 | } 170 | // now let's see what we have 171 | if (total == 1000) { 172 | std::cout << "Passed - popped " << total << " integers (" << cnt[0] << "+" << cnt[1] << "+" << cnt[2] << "+" << cnt[3] << "), with four drain threads" << std::endl; 173 | } else { 174 | std::cout << "ERROR - popped " << total << " integers (" << cnt[0] << "+" << cnt[1] << "+" << cnt[2] << "+" << cnt[3] << "), with four drain threads - but should have popped 1000" << std::endl; 175 | } 176 | // finally, clean things up 177 | for (uint32_t i = 0; i < 4; ++i) { 178 | if (dest[i] != NULL) { 179 | delete dest[i]; 180 | dest[i] = NULL; 181 | } 182 | } 183 | } 184 | 185 | std::cout << (error ? "FAILED!" : "SUCCESS") << std::endl; 186 | return (error ? 1 : 0); 187 | } 188 | -------------------------------------------------------------------------------- /tests/spsc_fifo.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * This is the tests for the SPSC CircularFIFO 3 | */ 4 | // System Headers 5 | #include 6 | #include 7 | 8 | // Third-Party Headers 9 | 10 | // Other Headers 11 | #include "spsc/CircularFIFO.h" 12 | #include "util/timer.h" 13 | 14 | int main(int argc, char *argv[]) { 15 | bool error = false; 16 | 17 | // make a circular FIFO of 1024 int32_t values - max 18 | dkit::spsc::CircularFIFO q; 19 | // put 500 values on the queue - and check the size 20 | if (!error) { 21 | std::cout << "=== Testing speed and correctness of CircularFIFO ===" << std::endl; 22 | 23 | // get the starting time 24 | uint64_t goTime = dkit::util::timer::usecStamp(); 25 | 26 | // do this often enough to rotate through the size of the values 27 | int32_t trips = 500000; 28 | for (int32_t cycle = 0; cycle < trips; ++cycle) { 29 | // put 500 values on the queue - and check the size 30 | for (int32_t i = 0; i < 500; ++i) { 31 | if (!q.push(i)) { 32 | error = true; 33 | std::cout << "ERROR - could not push the value " << i << std::endl; 34 | break; 35 | } 36 | } 37 | // now check the size 38 | if (!error) { 39 | if (q.size() != 500) { 40 | error = true; 41 | std::cout << "ERROR - pushed 500 integers, but size() reports only " << q.size() << std::endl; 42 | } else { 43 | if (cycle == 0) { 44 | std::cout << "Passed - pushed on 500 integers" << std::endl; 45 | } 46 | } 47 | } 48 | // pop off 500 integers and it should be empty 49 | if (!error) { 50 | int32_t v = 0; 51 | for (int32_t i = 0; i < 500; ++i) { 52 | if (!q.pop(v) || (v != i)) { 53 | error = true; 54 | std::cout << "ERROR - could not pop the value " << i << std::endl; 55 | break; 56 | } 57 | } 58 | } 59 | // now check the size 60 | if (!error) { 61 | if (!q.empty()) { 62 | error = true; 63 | std::cout << "ERROR - popped 500 integers, but size() reports " << q.size() << std::endl; 64 | } else { 65 | if (cycle == 0) { 66 | std::cout << "Passed - popped all 500 integers" << std::endl; 67 | } 68 | } 69 | } 70 | } 71 | 72 | // get the elapsed time 73 | goTime = dkit::util::timer::usecStamp() - goTime; 74 | std::cout << "Passed - did " << (trips * 500) << " push/pop pairs in " << (goTime/1000.0) << "ms = " << ((goTime * 1000.0)/(trips * 500.0)) << "ns/op" << std::endl; 75 | } 76 | 77 | std::cout << (error ? "FAILED!" : "SUCCESS") << std::endl; 78 | return (error ? 1 : 0); 79 | } 80 | -------------------------------------------------------------------------------- /tests/trie.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * This is the tests for the trie 3 | */ 4 | // System Headers 5 | #include 6 | #include 7 | 8 | // Third-Party Headers 9 | 10 | // Other Headers 11 | #include "trie.h" 12 | #include "util/timer.h" 13 | 14 | 15 | class blob { 16 | public: 17 | blob() : _when(0) { } 18 | blob(uint64_t aWhen) : _when(aWhen) { } 19 | virtual ~blob() { } 20 | void setValue(uint64_t aValue) { _when = aValue; } 21 | uint64_t getValue() const { return _when; } 22 | private: 23 | uint64_t _when; 24 | }; 25 | 26 | uint64_t key_value( const blob *aValue ) 27 | { 28 | return (*aValue).getValue(); 29 | } 30 | 31 | class counter : public dkit::trie::functor 32 | { 33 | public: 34 | counter() : _cnt(0) { } 35 | virtual ~counter() { } 36 | virtual bool process( volatile dkit::trie::Node & aNode ) 37 | { 38 | ++_cnt; 39 | return true; 40 | } 41 | uint64_t getCount() { return _cnt; } 42 | private: 43 | uint64_t _cnt; 44 | }; 45 | 46 | int main(int argc, char *argv[]) { 47 | bool error = false; 48 | 49 | dkit::trie m; 50 | std::cout << "trie has been created... adding values..." << std::endl; 51 | 52 | uint64_t cnt = 65535; 53 | uint64_t sz = 0; 54 | if (!error) { 55 | // get the starting time 56 | uint64_t goTime = dkit::util::timer::usecStamp(); 57 | for (uint64_t i = 0; i < cnt; ++i) { 58 | blob *b = new blob(i); 59 | m.put(b); 60 | } 61 | goTime = dkit::util::timer::usecStamp() - goTime; 62 | std::cout << "insertions took " << goTime << " usec ... " 63 | << 1.0*goTime/cnt << " usec/ins" << std::endl; 64 | if ((sz = m.size()) == cnt) { 65 | std::cout << "Success - the trie has " << sz << " elements!" << std::endl; 66 | } else { 67 | error = true; 68 | std::cout << "ERROR - the trie has " << sz << " elements, and it should have " << cnt << "!" << std::endl; 69 | } 70 | } 71 | 72 | if (!error) { 73 | for (uint16_t passes = 0; passes < 5; ++passes) { 74 | // get the starting time 75 | uint64_t goTime = dkit::util::timer::usecStamp(); 76 | blob *bp = NULL; 77 | for (uint64_t i = 0; i < cnt; ++i) { 78 | if (!m.get(i, bp)) { 79 | error = true; 80 | std::cout << "ERROR - failed to get key=" << i << "!" << std::endl; 81 | break; 82 | } 83 | } 84 | if (!error) { 85 | goTime = dkit::util::timer::usecStamp() - goTime; 86 | std::cout << "simple gets took " << goTime << " usec ... " 87 | << 1.0*goTime/cnt << " usec/get" << std::endl; 88 | } 89 | } 90 | } 91 | 92 | if (!error) { 93 | counter worker; 94 | // get the starting time 95 | uint64_t goTime = dkit::util::timer::usecStamp(); 96 | m.apply(worker); 97 | goTime = dkit::util::timer::usecStamp() - goTime; 98 | if (worker.getCount() == cnt) { 99 | std::cout << "Success - the counter worker found: " << worker.getCount() << " elements in the trie in " << goTime/1000.0 << " msec" << std::endl; 100 | } else { 101 | error = true; 102 | std::cout << "ERROR - the counter worker found: " << worker.getCount() << " elements in the trie, and it should have found " << cnt << std::endl; 103 | } 104 | } 105 | 106 | std::cout << (error ? "FAILED!" : "SUCCESS") << std::endl; 107 | return (error ? 1 : 0); 108 | } 109 | -------------------------------------------------------------------------------- /tests/udp_receiver.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * This is the tests for the udp_receiver 3 | */ 4 | // System Headers 5 | #include 6 | #include 7 | 8 | // Third-Party Headers 9 | 10 | // Other Headers 11 | #include "io/udp_receiver.h" 12 | #include "io/udp_transmitter.h" 13 | #include "sink.h" 14 | #include "adapter.h" 15 | #include "util/timer.h" 16 | 17 | using namespace dkit::io; 18 | 19 | /** 20 | * I need to have a subclass of sink that's going to handle my messages. 21 | * Because it's still a template class, I need to call out to a specialized 22 | * method. It's all still in the class, but it's required for the virtual 23 | * template class to actually work. 24 | */ 25 | template class MySink : 26 | public dkit::sink 27 | { 28 | public: 29 | MySink() : 30 | _cnt(0), 31 | _last_time(0) 32 | { } 33 | 34 | /** 35 | * This is the main receiver method that we need to call out to 36 | * a concrete method for the type we're using. It's what we have 37 | * to do to really get a virtual template class working for us. 38 | */ 39 | virtual bool recv( const T anItem ) 40 | { 41 | return onMessage(anItem); 42 | } 43 | 44 | /** 45 | * This method is called when we get a new datagram, and because 46 | * we are expecting to instantiate this template class with the 47 | * type 'T' being a , this is the method we're expecting 48 | * to get hit. It's just that simple. 49 | */ 50 | bool onMessage( const datagram *dg ) 51 | { 52 | if (dg == NULL) { 53 | std::cout << "got a NULL" << std::endl; 54 | } else { 55 | std::cout << "got: " << dg->contents() << std::endl; 56 | _last_time = dkit::util::timer::usecStamp(); 57 | ++_cnt; 58 | } 59 | return true; 60 | } 61 | 62 | /** 63 | * This method will return 'true' if we've received ANY datagrams 64 | * and if the last one was more than 5 sec ago. That's the timeout 65 | * for "no more data is coming our way." 66 | */ 67 | bool allDone() 68 | { 69 | using namespace dkit::util; 70 | static uint32_t __limit = 5 * 1000000L; 71 | return ((_cnt > 0) && ((timer::usecStamp() - _last_time) > __limit)); 72 | } 73 | 74 | private: 75 | uint32_t _cnt; 76 | uint64_t _last_time; 77 | }; 78 | 79 | 80 | /** 81 | * I need to have a subclass of adapter that's going to handle 82 | * my messages. Because it's still a template class, I need to call out to 83 | * a specialized method. It's all still in the class, but it's required for 84 | * the virtual template class to actually work. 85 | */ 86 | template class MyAdapter : 87 | public dkit::adapter 88 | { 89 | public: 90 | MyAdapter() { } 91 | 92 | /** 93 | * This is the main receiver method that we need to call out to 94 | * a concrete method for the type we're using. It's what we have 95 | * to do to really get a virtual template class working for us. 96 | */ 97 | virtual bool recv( const TIN anItem ) 98 | { 99 | return dkit::adapter::send(convert(anItem)); 100 | } 101 | 102 | /** 103 | * This method is called when we get a new datagram, and because 104 | * we are expecting to instantiate this template class with the 105 | * type 'T' being a , this is the method we're expecting 106 | * to get hit. It's just that simple. 107 | */ 108 | std::string convert( const datagram *dg ) { 109 | std::string out = ""; 110 | if (dg != NULL) { 111 | std::cout << "converting: " << dg->contents() << std::endl; 112 | out.assign(dg->what, dg->size); 113 | } 114 | return out; 115 | } 116 | }; 117 | 118 | 119 | /** 120 | * This is the main testing app where we'll listen on a specific URL for 121 | * UDP multicast data, and then process it until there's a timeout. It's 122 | * going to also use the "shared io_service" capabilities just to make 123 | * sure that the reference counting in the udp_receiver is working right. 124 | */ 125 | int main(int argc, char *argv[]) { 126 | bool error = false; 127 | 128 | /** 129 | * To wire up as a listener to the udp_receiver, we need to be a 130 | * subclass of sink... so now that it's made, construct 131 | * one with it's own io_service. 132 | */ 133 | MySink dump; 134 | udp_receiver rcvr(multicast_channel("udp://239.255.0.1:30001")); 135 | rcvr.addToListeners(&dump); 136 | rcvr.listen(); 137 | /** 138 | * At this point, make a new udp_receiver but share the io_service 139 | * thread from the one we just made. This will mean that both these 140 | * sockets are serviced on the same thread. Just a nice way to prove 141 | * that the reference counting is working. 142 | */ 143 | udp_receiver hold; 144 | hold.shareService(rcvr); 145 | hold.init(); 146 | /** 147 | * Make a simple adapter that takes the datagrams and makes std::string 148 | * instances of the data. This is just to test the adapter, and not 149 | * much else. 150 | */ 151 | MyAdapter packer; 152 | rcvr.addToListeners(&packer); 153 | /** 154 | * Just in order to show it's working, we're creating a transmitter 155 | * that will take the datagrams and ship them out on a different 156 | * UDP multicast channel. This is a simple repeater. 157 | */ 158 | udp_transmitter xmit(multicast_channel("udp://239.255.1.1:30001")); 159 | rcvr.addToListeners(&xmit); 160 | 161 | /** 162 | * Now let's stay in this loop as long as we need to... 163 | */ 164 | while (rcvr.isListening() && !dump.allDone()) { 165 | sleep(1); 166 | } 167 | std::cout << "shutting down due to inactivity..." << std::endl; 168 | rcvr.shutdown(); 169 | 170 | std::cout << (error ? "FAILED!" : "SUCCESS") << std::endl; 171 | return (error ? 1 : 0); 172 | } 173 | --------------------------------------------------------------------------------