1/*
2 * librd - Rapid Development C library
3 *
4 * Copyright (c) 2012, Magnus Edenhill
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are met:
9 *
10 * 1. Redistributions of source code must retain the above copyright notice,
11 * this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright notice,
13 * this list of conditions and the following disclaimer in the documentation
14 * and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29
30#ifndef _RD_H_
31#define _RD_H_
32
33#ifndef _MSC_VER
34#ifndef _GNU_SOURCE
35#define _GNU_SOURCE /* for strndup() */
36#endif
37#define __need_IOV_MAX
38#ifndef _POSIX_C_SOURCE
39#define _POSIX_C_SOURCE 200809L /* for timespec on solaris */
40#endif
41#endif
42
43#include <stdio.h>
44#include <stdlib.h>
45#include <string.h>
46#include <errno.h>
47#include <time.h>
48#include <assert.h>
49#include <limits.h>
50
51#include "tinycthread.h"
52#include "rdsysqueue.h"
53
54#ifdef _MSC_VER
55/* Visual Studio */
56#include "win32_config.h"
57#else
58/* POSIX / UNIX based systems */
59#include "../config.h" /* mklove output */
60#endif
61
62#ifdef _MSC_VER
63/* Win32/Visual Studio */
64#include "rdwin32.h"
65
66#else
67/* POSIX / UNIX based systems */
68#include "rdposix.h"
69#endif
70
71#include "rdtypes.h"
72
73
74/* Debug assert, only enabled with --enable-devel */
75#if ENABLE_DEVEL == 1
76#define rd_dassert(cond) rd_assert(cond)
77#else
78#define rd_dassert(cond) do {} while (0)
79#endif
80
81
82/** Assert if reached */
83#define RD_NOTREACHED() rd_assert(!*"/* NOTREACHED */ violated")
84
85
86
87/**
88* Allocator wrappers.
89* We serve under the premise that if a (small) memory
90* allocation fails all hope is lost and the application
91* will fail anyway, so no need to handle it handsomely.
92*/
93static RD_INLINE RD_UNUSED void *rd_calloc(size_t num, size_t sz) {
94 void *p = calloc(num, sz);
95 rd_assert(p);
96 return p;
97}
98
99static RD_INLINE RD_UNUSED void *rd_malloc(size_t sz) {
100 void *p = malloc(sz);
101 rd_assert(p);
102 return p;
103}
104
105static RD_INLINE RD_UNUSED void *rd_realloc(void *ptr, size_t sz) {
106 void *p = realloc(ptr, sz);
107 rd_assert(p);
108 return p;
109}
110
111static RD_INLINE RD_UNUSED void rd_free(void *ptr) {
112 free(ptr);
113}
114
115static RD_INLINE RD_UNUSED char *rd_strdup(const char *s) {
116#ifndef _MSC_VER
117 char *n = strdup(s);
118#else
119 char *n = _strdup(s);
120#endif
121 rd_assert(n);
122 return n;
123}
124
125static RD_INLINE RD_UNUSED char *rd_strndup(const char *s, size_t len) {
126#if HAVE_STRNDUP
127 char *n = strndup(s, len);
128 rd_assert(n);
129#else
130 char *n = malloc(len + 1);
131 rd_assert(n);
132 memcpy(n, s, len);
133 n[len] = '\0';
134#endif
135 return n;
136}
137
138
139
140/*
141 * Portability
142 */
143
144#ifdef strndupa
145#define rd_strndupa(DESTPTR,PTR,LEN) (*(DESTPTR) = strndupa(PTR,LEN))
146#else
147#define rd_strndupa(DESTPTR,PTR,LEN) do { \
148 const char *_src = (PTR); \
149 size_t _srclen = (LEN); \
150 char *_dst = rd_alloca(_srclen + 1); \
151 memcpy(_dst, _src, _srclen); \
152 _dst[_srclen] = '\0'; \
153 *(DESTPTR) = _dst; \
154 } while (0)
155#endif
156
157#ifdef strdupa
158#define rd_strdupa(DESTPTR,PTR) (*(DESTPTR) = strdupa(PTR))
159#else
160#define rd_strdupa(DESTPTR,PTR) do { \
161 const char *_src1 = (PTR); \
162 size_t _srclen1 = strlen(_src1); \
163 rd_strndupa(DESTPTR, _src1, _srclen1); \
164 } while (0)
165#endif
166
167#ifndef IOV_MAX
168#ifdef __APPLE__
169/* Some versions of MacOSX dont have IOV_MAX */
170#define IOV_MAX 1024
171#elif defined(_MSC_VER) || defined(__GNU__)
172/* There is no IOV_MAX on MSVC or GNU but it is used internally in librdkafka */
173#define IOV_MAX 1024
174#else
175#error "IOV_MAX not defined"
176#endif
177#endif
178
179
180/* Round/align X upwards to STRIDE, which must be power of 2. */
181#define RD_ROUNDUP(X,STRIDE) (((X) + ((STRIDE) - 1)) & ~(STRIDE-1))
182
183#define RD_ARRAY_SIZE(A) (sizeof((A)) / sizeof(*(A)))
184#define RD_ARRAYSIZE(A) RD_ARRAY_SIZE(A)
185#define RD_SIZEOF(TYPE,MEMBER) sizeof(((TYPE *)NULL)->MEMBER)
186#define RD_OFFSETOF(TYPE,MEMBER) ((size_t) &(((TYPE *)NULL)->MEMBER))
187
188/**
189 * Returns the 'I'th array element from static sized array 'A'
190 * or NULL if 'I' is out of range.
191 * var-args is an optional prefix to provide the correct return type.
192 */
193#define RD_ARRAY_ELEM(A,I,...) \
194 ((unsigned int)(I) < RD_ARRAY_SIZE(A) ? __VA_ARGS__ (A)[(I)] : NULL)
195
196
197#define RD_STRINGIFY(X) # X
198
199
200
201#define RD_MIN(a,b) ((a) < (b) ? (a) : (b))
202#define RD_MAX(a,b) ((a) > (b) ? (a) : (b))
203
204
205/**
206 * Cap an integer (of any type) to reside within the defined limit.
207 */
208#define RD_INT_CAP(val,low,hi) \
209 ((val) < (low) ? low : ((val) > (hi) ? (hi) : (val)))
210
211
212
213/**
214 * Allocate 'size' bytes, copy 'src', return pointer to new memory.
215 *
216 * Use rd_free() to free the returned pointer.
217*/
218static RD_INLINE RD_UNUSED void *rd_memdup (const void *src, size_t size) {
219 void *dst = rd_malloc(size);
220 memcpy(dst, src, size);
221 return dst;
222}
223
224/**
225 * @brief Memset &OBJ to 0, does automatic sizeof(OBJ).
226 */
227#define RD_MEMZERO(OBJ) memset(&(OBJ), 0, sizeof(OBJ))
228
229
230/**
231 * Generic refcnt interface
232 */
233
234#if !HAVE_ATOMICS_32
235#define RD_REFCNT_USE_LOCKS 1
236#endif
237
238#ifdef RD_REFCNT_USE_LOCKS
239typedef struct rd_refcnt_t {
240 mtx_t lock;
241 int v;
242} rd_refcnt_t;
243#else
244typedef rd_atomic32_t rd_refcnt_t;
245#endif
246
247#ifdef RD_REFCNT_USE_LOCKS
248static RD_INLINE RD_UNUSED int rd_refcnt_init (rd_refcnt_t *R, int v) {
249 int r;
250 mtx_init(&R->lock, mtx_plain);
251 mtx_lock(&R->lock);
252 r = R->v = v;
253 mtx_unlock(&R->lock);
254 return r;
255}
256#else
257#define rd_refcnt_init(R,v) rd_atomic32_init(R, v)
258#endif
259
260#ifdef RD_REFCNT_USE_LOCKS
261static RD_INLINE RD_UNUSED void rd_refcnt_destroy (rd_refcnt_t *R) {
262 mtx_lock(&R->lock);
263 rd_assert(R->v == 0);
264 mtx_unlock(&R->lock);
265
266 mtx_destroy(&R->lock);
267}
268#else
269#define rd_refcnt_destroy(R) do { } while (0)
270#endif
271
272
273#ifdef RD_REFCNT_USE_LOCKS
274static RD_INLINE RD_UNUSED int rd_refcnt_set (rd_refcnt_t *R, int v) {
275 int r;
276 mtx_lock(&R->lock);
277 r = R->v = v;
278 mtx_unlock(&R->lock);
279 return r;
280}
281#else
282#define rd_refcnt_set(R,v) rd_atomic32_set(R, v)
283#endif
284
285
286#ifdef RD_REFCNT_USE_LOCKS
287static RD_INLINE RD_UNUSED int rd_refcnt_add0 (rd_refcnt_t *R) {
288 int r;
289 mtx_lock(&R->lock);
290 r = ++(R->v);
291 mtx_unlock(&R->lock);
292 return r;
293}
294#else
295#define rd_refcnt_add0(R) rd_atomic32_add(R, 1)
296#endif
297
298static RD_INLINE RD_UNUSED int rd_refcnt_sub0 (rd_refcnt_t *R) {
299 int r;
300#ifdef RD_REFCNT_USE_LOCKS
301 mtx_lock(&R->lock);
302 r = --(R->v);
303 mtx_unlock(&R->lock);
304#else
305 r = rd_atomic32_sub(R, 1);
306#endif
307 if (r < 0)
308 rd_assert(!*"refcnt sub-zero");
309 return r;
310}
311
312#ifdef RD_REFCNT_USE_LOCKS
313static RD_INLINE RD_UNUSED int rd_refcnt_get (rd_refcnt_t *R) {
314 int r;
315 mtx_lock(&R->lock);
316 r = R->v;
317 mtx_unlock(&R->lock);
318 return r;
319}
320#else
321#define rd_refcnt_get(R) rd_atomic32_get(R)
322#endif
323
324/**
325 * A wrapper for decreasing refcount and calling a destroy function
326 * when refcnt reaches 0.
327 */
328#define rd_refcnt_destroywrapper(REFCNT,DESTROY_CALL) do { \
329 if (rd_refcnt_sub(REFCNT) > 0) \
330 break; \
331 DESTROY_CALL; \
332 } while (0)
333
334
335#define rd_refcnt_destroywrapper2(REFCNT,WHAT,DESTROY_CALL) do { \
336 if (rd_refcnt_sub2(REFCNT,WHAT) > 0) \
337 break; \
338 DESTROY_CALL; \
339 } while (0)
340
341#if ENABLE_REFCNT_DEBUG
342#define rd_refcnt_add(R) \
343 ( \
344 printf("REFCNT DEBUG: %-35s %d +1: %16p: %s:%d\n", \
345 #R, rd_refcnt_get(R), (R), __FUNCTION__,__LINE__), \
346 rd_refcnt_add0(R) \
347 )
348
349#define rd_refcnt_add2(R,WHAT) do { \
350 printf("REFCNT DEBUG: %-35s %d +1: %16p: %16s: %s:%d\n", \
351 #R, rd_refcnt_get(R), (R), WHAT, __FUNCTION__,__LINE__), \
352 rd_refcnt_add0(R); \
353 } while (0)
354
355
356#define rd_refcnt_sub2(R,WHAT) ( \
357 printf("REFCNT DEBUG: %-35s %d -1: %16p: %16s: %s:%d\n", \
358 #R, rd_refcnt_get(R), (R), WHAT, __FUNCTION__,__LINE__), \
359 rd_refcnt_sub0(R) )
360
361#define rd_refcnt_sub(R) ( \
362 printf("REFCNT DEBUG: %-35s %d -1: %16p: %s:%d\n", \
363 #R, rd_refcnt_get(R), (R), __FUNCTION__,__LINE__), \
364 rd_refcnt_sub0(R) )
365
366#else
367#define rd_refcnt_add(R) rd_refcnt_add0(R)
368#define rd_refcnt_sub(R) rd_refcnt_sub0(R)
369#endif
370
371
372
373#if !ENABLE_SHAREDPTR_DEBUG
374
375/**
376 * The non-debug version of shared_ptr is simply a reference counting interface
377 * without any additional costs and no indirections.
378 */
379
380#define RD_SHARED_PTR_TYPE(STRUCT_NAME,WRAPPED_TYPE) WRAPPED_TYPE
381
382
383#define rd_shared_ptr_get_src(FUNC,LINE,OBJ,REFCNT,SPTR_TYPE) \
384 (rd_refcnt_add(REFCNT), (OBJ))
385#define rd_shared_ptr_get(OBJ,REFCNT,SPTR_TYPE) \
386 (rd_refcnt_add(REFCNT), (OBJ))
387
388#define rd_shared_ptr_obj(SPTR) (SPTR)
389
390#define rd_shared_ptr_put(SPTR,REF,DESTRUCTOR) \
391 rd_refcnt_destroywrapper(REF,DESTRUCTOR)
392
393
394#else
395
396#define RD_SHARED_PTR_TYPE(STRUCT_NAME, WRAPPED_TYPE) \
397 struct STRUCT_NAME { \
398 LIST_ENTRY(rd_shptr0_s) link; \
399 WRAPPED_TYPE *obj; \
400 rd_refcnt_t *ref; \
401 const char *typename; \
402 const char *func; \
403 int line; \
404 }
405
406
407
408/* Common backing struct compatible with RD_SHARED_PTR_TYPE() types */
409typedef RD_SHARED_PTR_TYPE(rd_shptr0_s, void) rd_shptr0_t;
410
411LIST_HEAD(rd_shptr0_head, rd_shptr0_s);
412extern struct rd_shptr0_head rd_shared_ptr_debug_list;
413extern mtx_t rd_shared_ptr_debug_mtx;
414
415static RD_INLINE RD_UNUSED RD_WARN_UNUSED_RESULT __attribute__((warn_unused_result))
416rd_shptr0_t *rd_shared_ptr_get0 (const char *func, int line,
417 const char *typename,
418 rd_refcnt_t *ref, void *obj) {
419 rd_shptr0_t *sptr = rd_calloc(1, sizeof(*sptr));
420 sptr->obj = obj;
421 sptr->ref = ref;
422 sptr->typename = typename;
423 sptr->func = func;
424 sptr->line = line;
425
426 mtx_lock(&rd_shared_ptr_debug_mtx);
427 LIST_INSERT_HEAD(&rd_shared_ptr_debug_list, sptr, link);
428 mtx_unlock(&rd_shared_ptr_debug_mtx);
429 return sptr;
430}
431
432#define rd_shared_ptr_get_src(FUNC,LINE,OBJ,REF,SPTR_TYPE) \
433 (rd_refcnt_add(REF), \
434 (SPTR_TYPE *)rd_shared_ptr_get0(FUNC,LINE, #SPTR_TYPE,REF,OBJ))
435#define rd_shared_ptr_get(OBJ,REF,SPTR_TYPE) \
436 rd_shared_ptr_get_src(__FUNCTION__, __LINE__, OBJ, REF, SPTR_TYPE)
437
438
439
440#define rd_shared_ptr_obj(SPTR) (SPTR)->obj
441
442#define rd_shared_ptr_put(SPTR,REF,DESTRUCTOR) do { \
443 if (rd_refcnt_sub(REF) == 0) \
444 DESTRUCTOR; \
445 mtx_lock(&rd_shared_ptr_debug_mtx); \
446 LIST_REMOVE(SPTR, link); \
447 mtx_unlock(&rd_shared_ptr_debug_mtx); \
448 rd_free(SPTR); \
449 } while (0)
450
451void rd_shared_ptrs_dump (void);
452#endif
453
454
455#define RD_IF_FREE(PTR,FUNC) do { if ((PTR)) FUNC(PTR); } while (0)
456
457
458/**
459 * @brief Utility types to hold memory,size tuple.
460 */
461
462typedef struct rd_chariov_s {
463 char *ptr;
464 size_t size;
465} rd_chariov_t;
466
467#endif /* _RD_H_ */
468