Guest User

Untitled

a guest
Jul 22nd, 2018
98
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 9.92 KB | None | 0 0
  1. /**
  2. * RingFIFO.h - this file defines a multi-producer/single-consumer circular
  3. * FIFO queue and is using the compare-and-swap to achieve
  4. * the lock-free goal. The size of this buffer is given as a
  5. * power of 2 - so saying:
  6. * RingFIFO<int, 10> a;
  7. * makes a ring buffer of 2^10 = 1024 ints.
  8. */
  9. #ifndef __RINGFIFO_H
  10. #define __RINGFIFO_H
  11.  
  12. // System Headers
  13. #include <stdint.h>
  14. #include <ostream>
  15. #include <string>
  16.  
  17. // Third-Party Headers
  18. #include <log4cpp/Category.hh>
  19.  
  20. // Other Headers
  21.  
  22. // Forward Declarations
  23.  
  24. // Public Constants
  25.  
  26. // Public Datatypes
  27.  
  28. // Public Data Constants
  29.  
  30.  
  31.  
  32. /**
  33. * This is the main class definition
  34. */
  35. template <class T, size_t aPowOf2Size> class RingFIFO
  36. {
  37. public:
  38. /*******************************************************************
  39. *
  40. * Constructors/Destructor
  41. *
  42. *******************************************************************/
  43. /**
  44. * This is the default constructor that assumes NOTHING - it just
  45. * makes a simple queue ready to hold things.
  46. */
  47. RingFIFO() :
  48. mData(),
  49. mHead(0),
  50. mTail(0),
  51. mInsPt(0),
  52. mSleepDelay(),
  53. mHowSleepy(0)
  54. {
  55. // clear out the sleep timer values
  56. mSleepDelay.tv_sec = 0;
  57. }
  58.  
  59.  
  60. /**
  61. * This is the standard copy constructor that needs to be in every
  62. * class to make sure that we control how many copies we have
  63. * floating around in the system.
  64. */
  65. RingFIFO( const RingFIFO<T, aPowOf2Size> & anOther ) :
  66. mData(),
  67. mHead(0),
  68. mTail(0),
  69. mInsPt(0),
  70. mSleepDelay(),
  71. mHowSleepy(0)
  72. {
  73. // let the '=' operator do the heavy lifting...
  74. *this = anOther;
  75. }
  76.  
  77.  
  78. /**
  79. * This is the standard destructor and needs to be virtual to make
  80. * sure that if we subclass off this, the right destructor will be
  81. * called.
  82. */
  83. virtual ~RingFIFO()
  84. {
  85. clear();
  86. }
  87.  
  88.  
  89. /**
  90. * When we process the result of an equality we need to make sure
  91. * that we do this right by always having an equals operator on
  92. * all classes.
  93. */
  94. RingFIFO & operator=( RingFIFO<T, aPowOf2Size> & 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 (size_t i = anOther.mHead; i <= anOther.mTail; ++i) {
  103. if (!push(const_cast<const T &>(anOther.mData[i]))) {
  104. break;
  105. }
  106. }
  107. }
  108. return *this;
  109. }
  110.  
  111.  
  112. RingFIFO & operator=( const RingFIFO<T, aPowOf2Size> & anOther )
  113. {
  114. return operator=((RingFIFO<T, aPowOf2Size> &)anOther);
  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. bool push( T & anElem )
  128. {
  129. bool error = false;
  130.  
  131. // move the insertion point and get the old value for me
  132. size_t insPt = __sync_fetch_and_add(&mInsPt, 1);
  133. // see if we've crashed the queue all the way around...
  134. if (__sync_bool_compare_and_swap(&mHead, (insPt + 1), mHead)) {
  135. error = true;
  136. cLog.error("[push] the queue is full! Can't push this item");
  137. __sync_fetch_and_sub(&mInsPt, 1);
  138. } else {
  139. // save the data in the right spot for the size
  140. const_cast<T &>(mData[insPt & eBitMask]) = anElem;
  141. // ...and finally update the 'tail' for a new item
  142. __sync_fetch_and_add(&mTail, 1);
  143. }
  144.  
  145. return !error;
  146. }
  147.  
  148.  
  149. bool push( const T & anElem )
  150. {
  151. return push(*const_cast<T *>(&anElem));
  152. }
  153.  
  154.  
  155. bool push( volatile T & anElem )
  156. {
  157. return push(*const_cast<T *>(&anElem));
  158. }
  159.  
  160.  
  161. /**
  162. * This method updates the passed-in reference with the value on the
  163. * top of the queue - if it can. If so, it'll return the value and
  164. * 'true', but if it can't, as in the queue is empty, then the method
  165. * will return 'false' and the value will be untouched.
  166. */
  167. bool pop( T & anElem )
  168. {
  169. bool error = false;
  170.  
  171. // see if we have an empty queue...
  172. if (__sync_bool_compare_and_swap(&mHead, mTail, mHead)) {
  173. error = true;
  174. } else {
  175. // move the head, mask it for the location and copy the value
  176. anElem = const_cast<T &>(mData[__sync_fetch_and_add(&mHead, 1) & eBitMask]);
  177. // don't forget to tell the sleep() that we got a hit
  178. mHowSleepy = 0;
  179. }
  180.  
  181. return !error;
  182. }
  183.  
  184.  
  185. /**
  186. * If there is an item on the queue, this method will return a look
  187. * at that item without updating the queue. The return value will be
  188. * 'true' if there is something, but 'false' if the queue is empty.
  189. */
  190. bool peek( T & anElem )
  191. {
  192. bool error = false;
  193.  
  194. // see if we have an empty queue...
  195. if (__sync_bool_compare_and_swap(&mHead, mTail, mHead)) {
  196. error = true;
  197. } else {
  198. anElem = const_cast<T &>(mData[mHead & eBitMask]);
  199. }
  200.  
  201. return !error;
  202. }
  203.  
  204.  
  205. /**
  206. * This method will clear out the contents of the queue so if
  207. * you're storing pointers, then you need to be careful as this
  208. * could leak.
  209. */
  210. void clear()
  211. {
  212. T v;
  213. while (pop(v));
  214. }
  215.  
  216.  
  217. /**
  218. * This method will return 'true' if there are no items in the
  219. * queue. Simple.
  220. */
  221. bool empty()
  222. {
  223. return (mHead == mTail);
  224. }
  225.  
  226.  
  227. /**
  228. * This method will return the total number of items in the
  229. * queue.
  230. */
  231. size_t size() const
  232. {
  233. size_t retval = mTail - mHead;
  234. // see if it's wrapped around - and correct the unsigned math
  235. if (retval > eCapacity) {
  236. retval = (retval + eCapacity) & eBitMask;
  237. }
  238. return retval;
  239. }
  240.  
  241.  
  242. /*******************************************************************
  243. *
  244. * Utility Methods
  245. *
  246. *******************************************************************/
  247. /**
  248. * This method will check to see if the queue is empty, and if it
  249. * is, it'll sleep a "little bit" and then return to the caller.
  250. * The reason for all this is that the queue does not have a blocking
  251. * mode, so the only way to service the queue is to poll it. Too
  252. * agressive and you're wasing CPU cycles, too passive and you're
  253. * wasting time. This tries to strike a nice balance.
  254. */
  255. void sleep( bool aForcedSleep = false )
  256. {
  257. /**
  258. * If we are empty, and we have a non-zero delay to sleep for,
  259. * then do it, but make sure to cap the sleepy index at the size
  260. * of the array of sleepy values.
  261. */
  262. if ((aForcedSleep || empty()) &&
  263. ((mSleepDelay.tv_nsec = cSleepy[mHowSleepy++]) != 0)) {
  264. nanosleep(&mSleepDelay, NULL);
  265. mHowSleepy = (mHowSleepy > cSleepyMax ? cSleepyMax : mHowSleepy);
  266. }
  267. }
  268.  
  269.  
  270. /**
  271. * Because there are plenty of times that it's really useful to have
  272. * a human-readable version of this instance's data, we're going to
  273. * have a common method to give us just that - and this is it.
  274. */
  275. virtual std::string toString() const
  276. {
  277. return "<RingFIFO>";
  278. }
  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. bool operator==( RingFIFO<T, aPowOf2Size> & anOther ) const
  287. {
  288. bool equals = true;
  289.  
  290. // check to see if he's as sleepy as me :)
  291. if (equals) {
  292. equals = (mHowSleepy == anOther.mHowSleepy);
  293. }
  294.  
  295. // next, check the elements for equality
  296. if (equals) {
  297. for (size_t i = mHead, j = anOther.mHead; i <= mTail; ++i, ++j) {
  298. if (const_cast<T &>(mData[i]) != const_cast<T &>(anOther.mData[j])) {
  299. equals = false;
  300. break;
  301. }
  302. }
  303. }
  304.  
  305. return equals;
  306. }
  307.  
  308.  
  309. /**
  310. * This method checks to see if two queues are NOT equal in their
  311. * contents and not their pointer values. This is how you'd likely
  312. * expect equality to work.
  313. */
  314. bool operator!=( RingFIFO<T, aPowOf2Size> & anOther ) const
  315. {
  316. return !operator=(anOther);
  317. }
  318.  
  319.  
  320. private:
  321. /**
  322. * We need a few 'computed' values from the power of 2 size - the
  323. * first is the real capacity and the second is the bit mask for
  324. * locating the index of the array. These will be used in the code
  325. * to make it a lot faster overall.
  326. */
  327. enum {
  328. eBitMask = ((1 << aPowOf2Size) - 1),
  329. eCapacity = (1 << aPowOf2Size)
  330. };
  331.  
  332. /**
  333. * We have a very simple structure - an array of values of a fixed
  334. * size and a simple head and tail.
  335. */
  336. volatile T mData[eCapacity];
  337. volatile size_t mHead;
  338. volatile size_t mTail;
  339. volatile size_t mInsPt;
  340.  
  341. /**
  342. * Because this queue will not block until something is there,
  343. * we need to make it a little easier on the users of this class
  344. * by having a simple sleep() method. Simple, but clever under
  345. * the covers. In order to implement this sleeper I need a few
  346. * things, and these are it.
  347. */
  348. struct timespec mSleepDelay;
  349. uint8_t mHowSleepy;
  350. static int32_t cSleepy[];
  351. static uint8_t cSleepyMax;
  352.  
  353. /**
  354. * This is the class reference to the logger for this class.
  355. * It's pthread-aware, so we should be OK to use it by all the
  356. * instances of this class.
  357. */
  358. static log4cpp::Category & cLog;
  359. };
  360. template <class T, size_t aPowOf2Size> int32_t RingFIFO<T, aPowOf2Size>::cSleepy[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 250000, 250000, 250000, 250000, 250000, 250000, 250000, 250000, 250000, 250000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 750000, 750000, 750000, 750000, 750000, 750000, 750000, 750000, 750000, 750000, 1000000, 1000000, 1000000, 1000000, 1000000, 1000000, 1000000, 1000000, 1000000, 1000000, 2500000, 2500000, 2500000, 2500000, 2500000, 2500000, 2500000, 2500000, 2500000, 2500000, 5000000, 5000000, 5000000, 5000000, 5000000, 10000000, 10000000, 10000000, 10000000, 10000000, 50000000, 100000000 };
  361. template <class T, size_t aPowOf2Size> uint8_t RingFIFO<T, aPowOf2Size>::cSleepyMax = 41;
  362. template <class T, size_t aPowOf2Size> log4cpp::Category & RingFIFO<T, aPowOf2Size>::cLog = log4cpp::Category::getInstance("RingFIFO<T, aPowOf2Size>");
  363.  
  364. #endif // __RINGFIFO_H
  365. // vim: set ts=4:
Add Comment
Please, Sign In to add comment