Fix issues in cbuff when building without threads with GCC
authorArran Cudbard-Bell <a.cudbardb@freeradius.org>
Thu, 19 Dec 2013 23:48:59 +0000 (23:48 +0000)
committerArran Cudbard-Bell <a.cudbardb@freeradius.org>
Thu, 19 Dec 2013 23:51:21 +0000 (23:51 +0000)
src/lib/cbuff.c [new file with mode: 0644]

diff --git a/src/lib/cbuff.c b/src/lib/cbuff.c
new file mode 100644 (file)
index 0000000..3b85f94
--- /dev/null
@@ -0,0 +1,154 @@
+/*
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+/**
+ * @file cbuff.c
+ * @brief Implementation of a ring buffer
+ *
+ * @copyright 2013  The FreeRADIUS server project
+ * @copyright 2013  Arran Cudbard-Bell <a.cudbardb@freeradius.org>
+ */
+RCSID("$Id$")
+
+#include <freeradius-devel/libradius.h>
+
+#ifdef HAVE_PTHREAD_H
+#  define PTHREAD_MUTEX_LOCK(_x) if (_x->lock) pthread_mutex_lock(&((_x)->mutex))
+#  define PTHREAD_MUTEX_UNLOCK(_x) if (_x->lock) pthread_mutex_unlock(&((_x)->mutex))
+#else
+#  define PTHREAD_MUTEX_LOCK(_x)
+#  define PTHREAD_MUTEX_UNLOCK(_x)
+#endif
+
+/** Standard thread safe circular buffer
+ *
+ */
+struct fr_cbuff {
+       void const              *end;                   //!< End of allocated memory
+
+       uint32_t                size;
+       uint32_t                in;                     //!< Write index
+       uint32_t                out;                    //!< Read index
+
+       void                    **elem;                 //!< Ring buffer data
+
+       bool                    lock;                   //!< Perform thread synchronisation
+       pthread_mutex_t         mutex;                  //!< Thread synchronisation mutex
+};
+
+/** Initialise a new circular buffer
+ *
+ * @param ctx to allocate the buffer in.
+ * @param size of buffer to allocate.
+ * @param lock If true, insert and next operations will lock the buffer.
+ * @return new cbuff, or NULL on error.
+ */
+#ifdef HAVE_PTHREAD_H
+fr_cbuff_t *fr_cbuff_alloc(TALLOC_CTX *ctx, uint32_t size, bool lock)
+#else
+fr_cbuff_t *fr_cbuff_alloc(TALLOC_CTX *ctx, uint32_t size, UNUSED bool lock)
+#endif
+{
+       fr_cbuff_t *cbuff;
+
+       uint32_t pow;
+
+       /*
+        *      Find the nearest power of 2 (rounding up)
+        */
+       for (pow = 0x00000001;
+            pow < size;
+            pow <<= 1);
+       size = pow;
+       size--;
+
+       cbuff = talloc_zero(ctx, fr_cbuff_t);
+       if (!cbuff) {
+               return NULL;
+       }
+       cbuff->elem = talloc_zero_array(cbuff, void *, size);
+       if (!cbuff->elem) {
+               return NULL;
+       }
+       cbuff->size = size;
+
+#ifdef HAVE_PTHREAD_H
+       if (lock) {
+               cbuff->lock = true;
+               pthread_mutex_init(&cbuff->mutex, NULL);
+       }
+#endif
+       return cbuff;
+}
+
+/** Insert a new element into the buffer, and steal it from it's original context
+ *
+ * cbuff will steal obj and insert it into it's own context.
+ *
+ * @param cbuff to insert element into
+ * @param obj to insert, must of been allocated with talloc
+ */
+void fr_cbuff_rp_insert(fr_cbuff_t *cbuff, void *obj)
+{
+       if (cbuff->lock) PTHREAD_MUTEX_LOCK(cbuff);
+
+       if (cbuff->elem[cbuff->in]) {
+               TALLOC_FREE(cbuff->elem[cbuff->in]);
+       }
+
+       cbuff->elem[cbuff->in] = obj;
+       talloc_steal(cbuff, obj);
+
+       cbuff->in = (cbuff->in + 1) & cbuff->size;
+
+       /* overwrite - out is advanced ahead of in */
+       if (cbuff->in == cbuff->out) {
+               cbuff->out = (cbuff->out + 1) & cbuff->size;
+       }
+
+#ifdef HAVE_PTHREAD_H
+       if (cbuff->lock) {
+               PTHREAD_MUTEX_UNLOCK(cbuff);
+       }
+#endif
+}
+
+/** Remove an item from the buffer, and reparent to ctx
+ *
+ * @param cbuff to remove element from
+ * @param ctx to hang obj off.
+ * @return NULL if no elements in the buffer, else an element from the buffer reparented to ctx.
+ */
+void *fr_cbuff_rp_next(fr_cbuff_t *cbuff, TALLOC_CTX *ctx)
+{
+       void *obj = NULL;
+
+       if (cbuff->lock) PTHREAD_MUTEX_LOCK(cbuff);
+
+       /* Buffer is empty */
+       if (cbuff->out == cbuff->in) goto done;
+
+       obj = talloc_steal(ctx, cbuff->elem[cbuff->out]);
+       cbuff->out = (cbuff->out + 1) & cbuff->size;
+
+done:
+#ifdef HAVE_PTHREAD_H
+       if (cbuff->lock) {
+               PTHREAD_MUTEX_UNLOCK(cbuff);
+       }
+#endif
+       return obj;
+}