├── LICENCE.txt ├── README.txt ├── acu_std.c ├── acu_std.h ├── autocleanup.c ├── autocleanup.h ├── comparison_to_smartpointers_in_c++11.txt ├── example.c ├── exc_classes.c ├── exc_classes.h ├── exc_std.c ├── exc_std.h ├── exception.c ├── exception.h └── smartpointers_in_c.txt /LICENCE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014, Petteri Sevon 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 2. Redistributions in binary form must reproduce the above copyright notice, 10 | this list of conditions and the following disclaimer in the documentation 11 | and/or other materials provided with the distribution. 12 | 13 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 14 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 15 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 17 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 20 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 22 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | 24 | -------------------------------------------------------------------------------- /README.txt: -------------------------------------------------------------------------------- 1 | In the C software projects I'm involved in I grew tired of the excessive 2 | boiler plate code for error checking and releasing resources in exceptional 3 | cases, and despite all the effort there's always a leak here and there. 4 | 5 | I wanted to have an exception handling mechanism like most modern programming 6 | languages have, allowing for centralized error handling. The critical issue 7 | with exception handling, often overlooked, is releasing the acquired 8 | resources. There are two differing philosophies: 9 | - RAII (e.g., C++), in which destructors are automatically called for any 10 | objects going out of their scope. This works also in cases where functions 11 | let exceptions pass through uncatched. 12 | - finally blocks (e.g., java), which contain code that releases the resources. 13 | 14 | I strongly prefer the first option, since in my opinion finally blocks make 15 | exceptions to be little more than structured return values if non-leaky 16 | code is pursued; it practically means that exceptions must be caught in every 17 | function that does acquire some resources, which is in stark contrast with the 18 | goal of centralized error handling. There is garbage collection of course, 19 | but there's no guarantee when it's invoked and for some resources such as 20 | mutex locks that's just not good enough. 21 | 22 | There are numerous exception handling libraries available for C, most based on 23 | setjmp/longjmp, some providing finally blocks, and some even have support for 24 | local-scope RAII. But for true RAII we need more, we need smartpointers. 25 | 26 | This project intends to provide C++ like exception handling and smartpointer 27 | functionality to C programs; RAII style programming, and no explicit cleanup 28 | procedures in "finally" blocks. This project provides both unique pointers 29 | that always have a single owner, but ownership is transferrable, and shared, 30 | reference counted object with strong and weak references. 31 | 32 | The project is divided in the following library modules: 33 | 34 | - exception.{c,h} define basic exception handling functionality and macros 35 | defining TRY-CATCH-TRY_END macro brackets, and macros throw(e) and rethrow 36 | for throwing exceptions. 37 | - exc_classes.{c,h} provide definitions for some useful exception classes 38 | - exc_std.{c,h} implement wrappers for some standard C library functions that 39 | throw exceptions in case of failure 40 | 41 | - autocleanup.{c,h} provides basic smartpointer functionality by defining 42 | classes acu_unique and acu_shared (analogous to unique_ptr and shared_ptr 43 | in C++, or more precisely, acu_shared is analogous to the counter object 44 | pointed to by shared_ptr), functions for managing them, and macro brackets 45 | BEGIN..END and BEGIN_SCOPE..END_SCOPE, which should be used instead of 46 | braces at least in those parts of code that assume automatic cleanup, and 47 | macros acu_return, acu_exit_scope and acu_exit for clean exit from a scope. 48 | See file "smartpointers_in_c.txt" for a more detailed description of this 49 | library. 50 | 51 | - acu_std.{c,h} provides wrappers for some standard library constructors 52 | (such as malloc, fopen, pthread_mutex_lock, ...) that create unique 53 | pointers to the resources making them subject to automatic cleanup. 54 | 55 | See simple program "example.c" for examples on use of exceptions and 56 | smartpointers. 57 | 58 | gcc *.c -o example 59 | 60 | -------------------------------------------------------------------------------- /acu_std.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #ifdef ACU_THREAD_SAFE 7 | #include 8 | #endif 9 | 10 | #include "exception.h" 11 | #include "exc_std.h" 12 | #include "exc_classes.h" 13 | #include "autocleanup.h" 14 | #include "acu_std.h" 15 | 16 | void *acu_malloc(size_t s) 17 | { 18 | void *p = malloc(s); 19 | if (p) (void)acu_new_unique(p, free); 20 | return p; 21 | } 22 | 23 | void *acu_malloc_t(size_t s) 24 | { 25 | void *p = malloc_t(s); 26 | (void)acu_new_unique(p, free); 27 | return p; 28 | } 29 | 30 | void *acu_calloc(size_t n, size_t s) 31 | { 32 | void *p = calloc(n, s); 33 | if (p) acu_new_unique(p, free); 34 | return p; 35 | } 36 | 37 | void *acu_calloc_t(size_t n, size_t s) 38 | { 39 | void *p = calloc_t(n, s); 40 | acu_new_unique(p, free); 41 | return p; 42 | } 43 | 44 | void *acu_realloc(size_t s, acu_unique *a) 45 | { 46 | void *p = realloc(acu_get_ptr(a), s); 47 | acu_update(a, p); 48 | if (p == NULL) acu_destruct(a); 49 | return p; 50 | } 51 | 52 | void *acu_realloc_t(size_t s, acu_unique *a) 53 | { 54 | void *p = realloc(acu_get_ptr(a), s); 55 | acu_update(a, p); 56 | if (p == NULL) 57 | { 58 | acu_destruct(a); 59 | throw(new_mem_exception("acu_realloc_t", s)); 60 | } 61 | return p; 62 | } 63 | 64 | char *acu_strdup(const char *s) 65 | { 66 | char *p = strdup(s); 67 | if (p) (void)acu_new_unique(p, free); 68 | return p; 69 | } 70 | 71 | char *acu_strdup_t(const char *s) 72 | { 73 | char *p = strdup_t(s); 74 | (void)acu_new_unique(p, free); 75 | return p; 76 | } 77 | 78 | FILE *acu_fopen(const char *s, const char *m) 79 | { 80 | FILE *fi = fopen(s, m); 81 | if (fi) (void)acu_new_unique(fi, (void (*)(void *))fclose); 82 | return fi; 83 | } 84 | 85 | FILE *acu_fopen_t(const char *s, const char *m) 86 | { 87 | FILE *fi = fopen_t(s, m); 88 | (void)acu_new_unique(fi, (void (*)(void *))fclose); 89 | return fi; 90 | } 91 | 92 | static void _acu_std_close(void *fd_voidptr) 93 | { 94 | close((int)(long)fd_voidptr); 95 | } 96 | 97 | int acu_open(const char *s, int m) 98 | { 99 | int fd = open(s, m); 100 | if (fd >= 0) (void)acu_new_unique((void *)(long)fd, _acu_std_close); 101 | return fd; 102 | } 103 | 104 | int acu_open_t(const char *s, int m) 105 | { 106 | int fd = open_t(s, m); 107 | (void)acu_new_unique((void *)(long)fd, _acu_std_close); 108 | return fd; 109 | } 110 | 111 | #ifdef ACU_THREAD_SAFE 112 | acu_unique *acu_pthread_mutex_lock(pthread_mutex_t *lock) 113 | { 114 | //if (pthread_mutex_lock(lock)) ; //throw(new_io_error(errno, "", "acu_pthread_mutex_lock")); 115 | return acu_new_unique(lock, (void (*)(void *))pthread_mutex_unlock); 116 | } 117 | #endif 118 | 119 | -------------------------------------------------------------------------------- /acu_std.h: -------------------------------------------------------------------------------- 1 | #ifndef ACU_STD_H 2 | #define ACU_STD_H 3 | 4 | #include 5 | #ifdef ACU_THREAD_SAFE 6 | #include 7 | #endif 8 | #include "autocleanup.h" 9 | 10 | void *acu_malloc(size_t); 11 | void *acu_malloc_t(size_t); 12 | void *acu_calloc(size_t, size_t); 13 | void *acu_calloc_t(size_t, size_t); 14 | void *acu_realloc(size_t, acu_unique *); 15 | void *acu_realloc_t(size_t, acu_unique *); 16 | 17 | char *acu_strdup(const char *); 18 | char *acu_strdup_t(const char *); 19 | char *acu_strdup_demo(const char *); 20 | 21 | FILE *acu_fopen(const char *, const char *); 22 | FILE *acu_fopen_t(const char *, const char *); 23 | 24 | int acu_open(const char *, int); 25 | int acu_open_t(const char *, int); 26 | 27 | #ifdef ACU_THREAD_SAFE 28 | acu_unique *acu_pthread_mutex_lock(pthread_mutex_t *lock); 29 | #endif 30 | 31 | #endif 32 | 33 | -------------------------------------------------------------------------------- /autocleanup.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #ifdef ACU_THREAD_SAFE 4 | #include 5 | #include "acu_std.h" 6 | #endif 7 | #include "exception.h" 8 | #include "exc_classes.h" 9 | #include "exc_std.h" 10 | #include "autocleanup.h" 11 | 12 | 13 | /* Forward declarations */ 14 | static void _acu_del_strong_ref(void *p); 15 | static void _acu_del_weak_ref(void *p); 16 | 17 | /* Opaque classes */ 18 | 19 | /* Base class storing reference to the actual object, and its destructor function */ 20 | struct _acu_node { 21 | void *ptr; 22 | void (*del)(void *); 23 | }; 24 | 25 | #define _ACU_TRANSFERRABLE 1 26 | #define _ACU_SHAREABLE 2 27 | #define _ACU_SUBMITTABLE 4 28 | /* Class for unique object references */ 29 | struct _acu_stack_node { 30 | struct _acu_node base; 31 | acu_unique *next, *prev; 32 | long scope; 33 | int properties; 34 | }; 35 | 36 | /* Class for shared object references */ 37 | struct _acu_shared_node { 38 | struct _acu_node base; 39 | acu_unique *tail; 40 | int refcnt, weakcnt; 41 | #ifdef ACU_THREAD_SAFE 42 | int shared; 43 | pthread_mutex_t lock; 44 | #endif 45 | }; 46 | 47 | /* Global pointer to top of the main stack, and another pointer that either has the same value as _acu_stack_ptr, or NULL 48 | * _acu_latest is set to NULL at function entry, whenever it's accessed using acu_latest(), and when acu_attach or acu_destruct 49 | * is called */ 50 | __thread acu_unique *_acu_stack_ptr = NULL, *_acu_latest = NULL; 51 | __thread long _acu_scope = 0; 52 | 53 | 54 | /* Destruct a unique node without updating stack pointers. 55 | * The caller must make sure that the stack pointer will be valid after cleanup. */ 56 | static void _acu_destruct(acu_unique *u) 57 | { 58 | if (u->base.del) (u->base.del)(u->base.ptr); 59 | if (u->prev) u->prev->next = u->next; 60 | if (u->next) u->next->prev = u->prev; 61 | free(u); 62 | } 63 | 64 | /* Pop and destruct all unique nodes in a stack pointed to by *stack_ref_ptr, until node 'u' (inclusive), or 65 | * all nodes if u == NULL. However, do not touch nodes whose scope is smaller than minscope (yielded nodes). 66 | */ 67 | void _acu_cleanup(acu_unique *u, acu_unique **stack_ptr_ref, long minscope) 68 | { 69 | acu_unique **n = stack_ptr_ref, *next = NULL; 70 | if (u) u = u->prev; 71 | 72 | while (*n != u) 73 | { 74 | if ((*n)->scope > minscope) 75 | { 76 | acu_unique *c = *n; 77 | *n = c->prev; 78 | _acu_destruct(c); 79 | } 80 | else 81 | { 82 | if (n == stack_ptr_ref) (*n)->next = next; 83 | next = *n; 84 | n = &((*n)->prev); 85 | } 86 | } 87 | } 88 | 89 | /* Create a new unique node with reference to 'ptr' with destructor 'del' to stack pointed to by *tailptr. 90 | * Return pointer to the created node */ 91 | static acu_unique *_acu_new_unique(void *ptr, void (*del)(void *), acu_unique **stack_ptr_ref) 92 | { 93 | acu_unique *u = calloc_t(1, sizeof(acu_unique)); 94 | 95 | u->base.ptr = ptr; 96 | u->base.del = del; 97 | u->prev = _acu_stack_ptr; 98 | if (*stack_ptr_ref) (*stack_ptr_ref)->next = u; 99 | *stack_ptr_ref = u; 100 | u->scope = _acu_scope; 101 | u->properties = _ACU_TRANSFERRABLE | _ACU_SHAREABLE | _ACU_SUBMITTABLE; 102 | return u; 103 | } 104 | 105 | /* Create a new unique node with reference to 'ptr' with destructor 'del' to the main stack, return pointer to the created node */ 106 | acu_unique *acu_new_unique(void *ptr, void (*del)(void *)) { 107 | acu_unique *u = _acu_new_unique(ptr, del, &_acu_stack_ptr); 108 | _acu_latest = u; 109 | return u; 110 | } 111 | 112 | /* Create an empty unique node and return pointer to it */ 113 | acu_unique *acu_reserve(void) { return acu_new_unique(NULL, NULL); } 114 | 115 | 116 | /* Get pointer to the latest unique node in the main stack. Will throw an exception if 117 | * 1) no unique nodes have been created within the same function, 118 | * 2) no unique nodes have been created after last call to acu_latest(), acu_share(), acu_destruct() or acu_sumit_to(). 119 | */ 120 | acu_unique *acu_latest(void) { 121 | acu_unique *u = _acu_latest; 122 | if (u == NULL) throw(new_name_exception("acu_latest: no object available")); 123 | _acu_latest = NULL; 124 | return u; 125 | } 126 | 127 | /* Destruct unique node 'u' in the main stack. Adjust stack pointer if 'u' was on top of the stack. */ 128 | void acu_destruct(acu_unique *u) 129 | { 130 | _acu_latest = NULL; 131 | if (u == _acu_stack_ptr) _acu_stack_ptr = u->prev; 132 | _acu_destruct(u); 133 | } 134 | 135 | /* Update pointer to the base object, follow chain of forward links first */ 136 | void acu_update(acu_unique *u, void *newptr) 137 | { 138 | if (u->base.del == _acu_del_weak_ref) throw(new_name_exception("acu_update: cannot modify weakly referenced object")); 139 | struct _acu_node *p = (struct _acu_node *)u; 140 | if (p->del == _acu_del_strong_ref) p = p->ptr; 141 | p->ptr = newptr; 142 | } 143 | 144 | /* Get pointer to managed object, follow strong reference first */ 145 | void *acu_get_ptr(acu_unique *u) 146 | { 147 | if (u->base.del == _acu_del_weak_ref) throw(new_name_exception("acu_update: cannot access weakly referenced object")); 148 | struct _acu_node *p = (struct _acu_node *)u; 149 | if (p->del == _acu_del_strong_ref) p = p->ptr; 150 | return p->ptr; 151 | } 152 | 153 | /* Assign unique object reference from 'from' to 'to'. This can be used to transfer ownership of unique node to an outer scope. */ 154 | void acu_transfer(acu_unique *from, acu_unique *to) 155 | { 156 | if (from->properties & _ACU_TRANSFERRABLE == 0) throw(new_name_exception("acu_transfer: non-transferrable pointer")); 157 | to->base = from->base; 158 | to->properties = from->properties; 159 | from->base.del = NULL; // prevent the object whose ownership was transferred to 'to' from being destructed 160 | acu_destruct(from); 161 | } 162 | 163 | /* Pass unique pointer to the enclosing dynamic scope (e.g., the calling function) */ 164 | void acu_yield(acu_unique *u) 165 | { 166 | if (u->properties & _ACU_TRANSFERRABLE == 0) throw(new_name_exception("acu_yield: non-transferrable pointer")); 167 | if (u->scope > _acu_scope - 1) u->scope = _acu_scope - 1; 168 | } 169 | 170 | /* Swap the contents of two unique pointers */ 171 | void acu_swap(acu_unique *a, acu_unique *b) 172 | { 173 | if (a->properties & b->properties & _ACU_TRANSFERRABLE == 0) 174 | throw(new_name_exception("acu_swap: non-transferrable pointer")); 175 | struct _acu_node t = a->base; a->base = b->base; b->base = t; 176 | int p = a->properties; a->properties = b->properties; b->properties = p; 177 | } 178 | 179 | /* Destructor for a unique pointer with a weak reference to a shared pointer */ 180 | static void _acu_del_weak_ref(void *p) 181 | { 182 | acu_shared *s = (acu_shared *)p; 183 | if ( 184 | #ifndef ACU_THREAD_SAFE 185 | --s->weakcnt == 0 186 | #else 187 | __sync_sub_and_fetch(&(s->weakcnt), 1) == 0 188 | #endif 189 | ) 190 | { 191 | #ifdef ACU_THREAD_SAFE 192 | (void)pthread_mutex_destroy(&(s->lock)); 193 | #endif 194 | free(s); 195 | } 196 | } 197 | 198 | /* Destructor for a unique node with a strong reference to a shared node */ 199 | static void _acu_del_strong_ref(void *p) 200 | { 201 | acu_shared *s = (acu_shared *)p; 202 | 203 | if ( 204 | #ifndef ACU_THREAD_SAFE 205 | --s->refcnt == 0 206 | #else 207 | __sync_sub_and_fetch(&(s->refcnt), 1) == 0 208 | #endif 209 | ) 210 | { 211 | _acu_cleanup(NULL, &(s->tail), 0); 212 | if (s->base.del) (s->base.del)(s->base.ptr); 213 | _acu_del_weak_ref(p); 214 | } 215 | } 216 | 217 | 218 | /* Create a shared pointer pointing to the object unique pointer 'u' points to. Turn 'u' into a forward reference to the 219 | * shared node. Set reference count of the shared node to 1. Return pointer to the shared node. */ 220 | acu_shared *acu_share(acu_unique *u) 221 | { 222 | if (u->properties & _ACU_SHAREABLE == 0) throw(new_name_exception("acu_share: not shareable")); 223 | acu_shared *s = calloc_t(1, sizeof(acu_shared)); 224 | s->base = u->base; 225 | s->refcnt = s->weakcnt = 1; 226 | u->base.ptr = s; 227 | u->base.del = _acu_del_strong_ref; 228 | #ifdef ACU_THREAD_SAFE 229 | if (pthread_mutex_init(&(s->lock), NULL)) throw(new_io_exception(errno, "", "acu_share")); 230 | #endif 231 | 232 | return s; 233 | } 234 | 235 | /* Create new unique node to main stack, pointing to shared node 's'. Increase reference count of 's' by one. */ 236 | acu_unique *acu_new_reference(acu_shared *s) 237 | { 238 | acu_unique *u = acu_new_unique(s, _acu_del_strong_ref); 239 | #ifndef ACU_THREAD_SAFE 240 | if (s->refcnt == 0) s->weakcnt++; 241 | s->refcnt++; 242 | #else 243 | s->shared = 1; 244 | if (__sync_fetch_and_add(&(s->refcnt), 1) == 0) (void)__sync_fetch_and_add(&(s->weakcnt), 1); 245 | #endif 246 | return u; 247 | } 248 | 249 | 250 | /* Create new unique node with a weak reference to shared pointer 's'. */ 251 | acu_unique *acu_new_weak_reference(acu_shared *s) 252 | { 253 | acu_unique *u = acu_new_unique(s, _acu_del_weak_ref); 254 | u->properties &= ~(_ACU_SUBMITTABLE | _ACU_SHAREABLE); 255 | #ifndef ACU_THREAD_SAFE 256 | s->weakcnt++; 257 | #else 258 | s->shared = 1; 259 | (void)__sync_fetch_and_add(&(s->weakcnt), 1); 260 | #endif 261 | return u; 262 | } 263 | 264 | /* Create a strong reference copy of a weak reference. This must be done for a resource to which the caller 265 | * only owns a weak reference before actually using the resource to guarantee that it actually exists, 266 | * and it won't be destructed while being used. Returns NULL if the resource is expired. */ 267 | acu_unique *acu_lock_reference(acu_unique *weakptr) 268 | BEGIN 269 | if (!weakptr || weakptr->base.del != _acu_del_weak_ref) throw(new_name_exception("acu_get_strong_reference: argument not a weakptr")); 270 | 271 | acu_shared *s = weakptr->base.ptr; 272 | if ( 273 | #ifndef ACU_THREAD_SAFE 274 | s->refcnt++ == 0 275 | #else 276 | __sync_fetch_and_add(&(s->refcnt), 1) == 0 277 | #endif 278 | ) 279 | { 280 | s->refcnt = 0; 281 | acu_return NULL; 282 | } 283 | 284 | acu_unique *u; 285 | TRY 286 | u = acu_new_unique(s, _acu_del_strong_ref); 287 | CATCH(e) 288 | #ifndef ACU_THREAD_SAFE 289 | s->refcnt--; 290 | #else 291 | (void)__sync_fetch_and_sub(&(s->refcnt), 1); 292 | #endif 293 | rethrow; 294 | TRY_END 295 | u->properties &= ~(_ACU_SUBMITTABLE | _ACU_SHAREABLE); 296 | acu_yield(u); 297 | acu_return u; 298 | END 299 | 300 | 301 | /* Detach unique node 'u' from the main stack and push a copy of it to stack of shared object 's'. 302 | * Note that the library is designed so that user agent may not get handles to unique objects 303 | * detached from the main stack. This function is not called by other acu library functions, and 304 | * therefore it may rely on 'u' always being in the main cleanup stack */ 305 | void acu_submit_to(acu_unique *u, acu_shared *s) 306 | BEGIN 307 | if (u->properties & _ACU_SUBMITTABLE == 0) throw(new_name_exception("acu_submit_to: cannot be submitted")); 308 | /* Make a copy of a to ensure that caller will not have a handle to the object after attaching */ 309 | #ifdef ACU_THREAD_SAFE 310 | /* Take mutex lock only if acu_new_[weak_]reference(s) has been called at least once, otherwise 311 | * there cannot be a race condition. Because of this, to achieve maximal performance the creator of 312 | * the shared object should do all acu_submit_to calls before creating additonal references 313 | * to the object. */ 314 | acu_unique *lockptr; 315 | if (s->shared) lockptr = acu_pthread_mutex_lock(&(s->lock)); 316 | #endif 317 | acu_unique *b = _acu_new_unique(u->base.ptr, u->base.del, &(s->tail)); 318 | #ifdef ACU_THREAD_SAFE 319 | if (s->shared) acu_destruct(lockptr); 320 | #endif 321 | 322 | /* Unlink the previous object, do not call the destructor, since we are effectively just moving 323 | * the node to a new context. */ 324 | if (u->prev) u->prev->next = u->next; 325 | if (u->next) u->next->prev = u->prev; else _acu_stack_ptr = u->prev; 326 | _acu_latest = NULL; 327 | free(u); 328 | END 329 | 330 | void _acu_atexit_cleanup(void) { _acu_cleanup(NULL, &_acu_stack_ptr, 0); } 331 | #ifdef ACU_THREAD_SAFE 332 | void _acu_thread_cleanup(void *dummy) { _acu_cleanup(NULL, &_acu_stack_ptr, 0); } 333 | #endif 334 | 335 | -------------------------------------------------------------------------------- /autocleanup.h: -------------------------------------------------------------------------------- 1 | #ifndef AUTOCLEANUP_H 2 | #define AUTOCLEANUP_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | 9 | /* Author Petteri Sevon 10 | * 11 | * AutoCleanUp (ACU): Library for implementing RAII in C 12 | * 13 | * This library provides a mechanism for RAII style resource management; allocated 14 | * resources are objects that are automatically destructed upon leaving their scope. 15 | * This mechanism can be leveraged for existing constructors such as malloc or fopen 16 | * by using wrapper functions that, after successful construction, push a uniquely owned 17 | * smartpointer (acu_unique), pointer to the resource and its respective destructor 18 | * function, to a thread-specific cleanup stack. See acu_std.c for implementation 19 | * of such wrappers for commonly use standard C resource allocators. 20 | * 21 | * The library supports transferring ownership of an acu_unique object to another 22 | * acu_unique object in order to extend its scope. Also, an acu_unique object can be 23 | * copied into an acu_shared object. This is analogous to shared/weak_ptr in C++, but 24 | * implemented differently: there's exactly one acu_shared object for a resource, 25 | * containing strong and weak reference counters and pointers to the resource and its 26 | * destructor, and this shared pointer is referenced (strongly or weakly) by any number 27 | * of acu_unique objects or other acu_shared objects. 28 | * 29 | * The library is compatible (and intended for use) with another library, exception.h, 30 | * that implements exceptions as a macro extension. Resources allocated above the scope 31 | * catching an exception are automatically released. Using this mechanism eliminates 32 | * the need for having "finally" blocks after try-catch. Using these two libraries will 33 | * result in much less boiler plate code, less resource leaks, and more robust detection 34 | * of errors, as silently ignoring errors indicated by return values is no longer possible. 35 | * 36 | * Because accessing the resources through the unique smartpointers is not very attractive 37 | * option in C (performance hit from extra indirection, explicit casting due to lack of 38 | * templates, and lack of operator overloading), I would recommend using direct pointers 39 | * for accessing the objects and smartpointers for managing their ownership and lifespan. 40 | * Actually, the contructors in acu_std.c only return the direct pointer to the allocated 41 | * resource. This is because for the most common usage where the scope of the resource 42 | * is the current scope, the caller does not need to do anything else. If the ownership 43 | * of the resource has to be passed outside the current scope, or the resource must be 44 | * shared, it's possible to get a pointer to the acu_unique smartpointer by calling 45 | * acu_latest() immediately after the constructor call. 46 | * 47 | * The implementation defines macro brackets BEGIN ... END replacing normal curly brackets 48 | * { ... } around function definition, and optional inner context brackets BEGIN_SCOPE ... 49 | * END_SCOPE. Stack pointers are memorized at entry to any scope opened with a macro bracket, 50 | * and the closing macro bracket automatically releases any resources pushed to the 51 | * stack above the stack pointer memorized at entry to the scope. 52 | * 53 | * Since the closing macro brackets cannot catch early exits from their scope, leaving 54 | * the scope using goto, continue, break or return will not trigger the destructors. 55 | * However, the destruct operations will remain in the stack and will be done when the 56 | * cleaning up is triggered next time. Inner scope can be exited using macro acu_exit_scope(), 57 | * which will trigger cleaning up. Macro acu_return can be used to return with cleanup of 58 | * the entire function scope from anywhere within the function. 59 | * 60 | * Resources allocated using the acu_ wrappers may not be freed using the normal 61 | * function calls such as free. If they need to be explicitly released before 62 | * end of their scope, use acu_destruct(u) to release the resource. 63 | */ 64 | 65 | 66 | struct _acu_stack_node; 67 | typedef struct _acu_stack_node acu_unique; 68 | 69 | struct _acu_shared_node; 70 | typedef struct _acu_shared_node acu_shared; 71 | 72 | extern __thread acu_unique *_acu_stack_ptr, *_acu_latest; 73 | extern __thread long _acu_scope; 74 | 75 | /* Private cleanup functions, required in the header because the macros use them */ 76 | void _acu_cleanup(acu_unique *u, acu_unique **tailptr, long); 77 | void _acu_atexit_cleanup(void); 78 | #ifdef ACU_THREAD_SAFE 79 | void _acu_thread_cleanup(void *); 80 | #endif 81 | 82 | /* Create a new unique pointer to object 'ptr' with destructor 'del', return pointer to it */ 83 | acu_unique *acu_new_unique(void *ptr, void (*del)(void *)); 84 | 85 | /* Get pointer to the latest unique node. Will throw an exception if 86 | * 1) no unique nodes have been created within the same function, or 87 | * 2) no unique nodes have been created after last call to acu_latest(), acu_share(), acu_destruct() or acu_submit_to(). */ 88 | acu_unique *acu_latest(void); 89 | 90 | /* Explicit destruction of unique pointer *u. usually not needed, but may be useful for releasing critical resources 91 | * suc as locks as early as possible. */ 92 | void acu_destruct(acu_unique *u); 93 | 94 | /* Update the pointer to an object, he only motivation for having this is realloc */ 95 | void acu_update(acu_unique *u, void *p); 96 | 97 | /* Detach unique pointer 'u' from the cleanup stack and push a copy of it to cleanup stack of shared pointer 's'. */ 98 | void acu_submit_to(acu_unique *u, acu_shared *s); 99 | 100 | /* Create an empty unique node (for receiving ownership by acu_transfer) and return pointer to it */ 101 | acu_unique *acu_reserve(void); 102 | 103 | /* Create a shared pointer pointing to the same object unique pointer 'u' points to. Turn 'u' into a forward reference to the 104 | * shared node. Set reference count of the shared node to 1. Return pointer to the acu_shared object. */ 105 | acu_shared *acu_share(acu_unique *u); 106 | 107 | /* Create new acu_unique object pointing to shared node 's'. Increase reference count of 's' by one, and increase weak reference count if reference count was zero. */ 108 | acu_unique *acu_new_reference(acu_shared *s); 109 | 110 | /* Copy unique object reference from 'from' to 'to', and unlink unique pointer 'from'. This can be used to transfer ownership of unique node to an outer scope. */ 111 | void acu_transfer(acu_unique *from, acu_unique *to); 112 | 113 | /* Pass unique pointer to the enclosing dynamic scope (e.g., the calling function) */ 114 | void acu_yield(acu_unique *u); 115 | 116 | /* Swap the contents of two unique pointers */ 117 | void acu_swap(acu_unique *a, acu_unique *b); 118 | 119 | /* Get pointer to managed object, follow chain of forward links first */ 120 | void *acu_get_ptr(acu_unique *u); 121 | 122 | /* Create new acu_unique object with a weak reference to shared node 's'. Increase weak reference count of 's' by one. */ 123 | acu_unique *acu_new_weak_reference(acu_shared *s); 124 | 125 | /* Obtain a strong reference to a shared pointer from a weak reference. If the object is already destructed, return NULL. */ 126 | acu_unique *acu_lock_reference(acu_unique *weakptr); 127 | 128 | 129 | #define acu_init atexit(_acu_atexit_cleanup); 130 | #ifdef ACU_THREAD_SAFE 131 | #define acu_init_thread phthread_cleanup_push(_acu_thread_cleanup, NULL); 132 | #endif 133 | 134 | #define BEGIN { acu_unique *_acu_stack_ptr_scope = acu_reserve(), *_acu_stack_ptr_fn = _acu_stack_ptr_scope; _acu_latest = NULL; \ 135 | long _acu_current_scope = _acu_scope++; 136 | #define BEGIN_SCOPE { acu_unique *_acu_stack_ptr_scope = acu_reserve(); jmp_buf _acu_scope_context; long _acu_current_scope = _acu_scope++; \ 137 | if (setjmp(_acu_scope_context) == 0) { 138 | 139 | #define END_SCOPE } _acu_scope = _acu_current_scope; _acu_cleanup(_acu_stack_ptr_scope, &_acu_stack_ptr, _acu_current_scope); } 140 | #define END _acu_scope = _acu_current_scope; _acu_cleanup(_acu_stack_ptr_fn, &_acu_stack_ptr, _acu_current_scope); } 141 | 142 | #define acu_exit_scope longjmp(_acu_scope_context, 1) 143 | #define acu_exit(v) { _acu_cleanup(NULL, &_acu_stack_ptr, 0); exit(v); } 144 | 145 | /* This is designed to be usable in any context plain return can be used. Uses "while" instead of "if", 146 | * because "if" would break things if there's an "else" right after acu_return. */ 147 | #define acu_return while (_acu_scope = _acu_current_scope, _acu_cleanup(_acu_stack_ptr_fn, &_acu_stack_ptr, _acu_current_scope), 1) return 148 | 149 | #endif 150 | 151 | -------------------------------------------------------------------------------- /comparison_to_smartpointers_in_c++11.txt: -------------------------------------------------------------------------------- 1 | Autocleanup provides very similar functionality to the smartpointers in 2 | C++ 11, but there are differences in the usage. This document lists some 3 | of them, with examples on how specific C++ operations are done with 4 | autocleanup. 5 | 6 | 1. Dereferencing 7 | Since in C you cannot overload operators, dereferencing must be done using 8 | a function call. And since in C there are no templates, autocleanup only 9 | provides generic smartpointers that dereference to void *, hence requiring 10 | explicit casting. 11 | 12 | C++: 13 | std::unique_ptr t(); 14 | x = t->member; 15 | 16 | autocleanup: 17 | acu_unique *t = new_T(); 18 | x = (T *)acu_get_ptr(t)->member; 19 | 20 | Due to complexity and performance hit from the extra indirection (especially 21 | with shared pointers, see below) it is advisable to not use this dereferencing 22 | mechanism extensively. Instead, I recommend dual approac, where direct 23 | typed pointers are used for all actual access of the managed objects, and 24 | smart pointers only for managing their ownership and scope. 25 | 26 | 27 | 2. Move semantics 28 | acu_transfer is very close to the move constructor in C++, except that the 29 | receiving acu_unique object must exist already. Call acu_reserve() to create 30 | an empty pointer. 31 | 32 | C++: 33 | unique_ptr t = std::move(s); 34 | 35 | autocleanup: 36 | acu_unique *t = acu_reserve(); 37 | acu_transfer(s, t); 38 | 39 | In C++ the complex return value semantics allow for returning a unique_ptr 40 | and have move consructor semantics applied. With autocleanup it's possible 41 | to pass ownership of unique pointers to the enclosing dynamic scope by 42 | calling acu_yield(u). In order to pass ownership to a scope outer than 43 | the immediately enclosing one, a reference to the receiving empty unique 44 | pointer must be passed to the function, and the pointer can be passed 45 | using acu_transfer. 46 | 47 | 48 | 49 | 3. Shared and weak pointers. 50 | In C++, the shared and weak pointers for the same managed object have a shared 51 | counter object they point to, and they point to the managed object directly. In 52 | autocleanup, acu_shared is the counter object, which also points to the managed 53 | object, and is owned by one or more other unique or shared pointers with strong 54 | or weak references to it. Since all acu_shared objects are ultimately owned by 55 | acu_unique objects only, the cleanup mechanism in autocleanup is based on 56 | invoking destructors of acu_unique objects when they go out of scope. 57 | Destructor of unique pointers that own strong or weak references decrease 58 | the respective reference count and trigger destructor of the managed object 59 | if the strong reference count goes to zero, and remove the shared pointer itself 60 | if the weak count goes to zero. 61 | 62 | In autocleanup, a shared pointer cannot be created directly. Instead, first 63 | create a unique pointer, which can then be shared: 64 | 65 | acu_unique *t = new_T(); 66 | acu_shared *s = acu_share(t); 67 | 68 | After this, 't' will contain a strong reference to the shared pointer 's'. 69 | New strong references can be created using 70 | 71 | acu_unique *t2 = acu_new_refererence(s); 72 | 73 | and new weak references using 74 | 75 | acu_unique *w = acu_new_weak_reference(s); 76 | 77 | 78 | 4. Locking weak pointers 79 | Weak references can't be used for accessing the pointed object before aquiring 80 | a stong reference lock to it. This locking reference prevents the managed 81 | object from being destructed while being used. 82 | 83 | C++: 84 | shared_ptr s = w.lock(); 85 | 86 | autocleanup: 87 | acu_unique *s = acu_lock_reference(w); 88 | 89 | 90 | -------------------------------------------------------------------------------- /example.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "autocleanup.h" 4 | #include "exception.h" 5 | #include "exc_classes.h" 6 | #include "acu_std.h" 7 | 8 | /* Example program demonstrating use of RAII in C 9 | * ---------------------------------------------- 10 | * Simple program that calls main->h->g->f, constructs unique objects in differents scopes, and demonstrates 11 | * ownership transfer and sharing. */ 12 | 13 | 14 | /* unique node references can be allocated from the heap, but this doesn't mean that the scope of the node is global. Scope of 15 | * an unique node is always the scope in which it was allocated. Global variables provide one means for passing reference to the 16 | * nodes as in this example. */ 17 | acu_unique *q; 18 | 19 | 20 | /* Demo destructor that prints to stdout */ 21 | static void free_demo(char *p) 22 | { 23 | fprintf(stderr, "Destructing string '%s'\n", p); 24 | free(p); 25 | } 26 | 27 | /* Demo constructor that allocates a duplicate of a string, and prints a message to stdout */ 28 | char *acu_strdup_demo(const char *s) 29 | { 30 | char *p = strdup(s); 31 | fprintf(stderr, "Allocated string '%s'\n", p); 32 | if (p) (void)acu_new_unique(p, (void (*)(void *))free_demo); 33 | return p; 34 | } 35 | 36 | 37 | 38 | char *f(int x, acu_unique *r) 39 | BEGIN 40 | printf("...enter f\n"); 41 | /* Local scope objects: allocated object is to be destructed when going out of the 42 | * current scope. The function does not need to obtain a pointer to the actual acu_unique 43 | * node at all. */ 44 | char *s1 = acu_strdup_demo("String allocated in f"); 45 | char *s1b = acu_strdup_demo("String allocated in f but ownership passed to enclosing scope"); 46 | acu_yield(acu_latest()); 47 | 48 | /* Transfer of scope: allocate an object in this scope, and then pass ownership to another acu_unique node 49 | * received as an argument. */ 50 | char *s2 = acu_strdup_demo("String allocated in f but to be destructed at the end of h"); 51 | acu_transfer(acu_latest(), r); 52 | 53 | /* Object sharing: allocate an object in this scope, create an acu_shared node out of it, replacing the 54 | * original acu_unique node with a forward link to the shared node. */ 55 | char *s3 = acu_strdup_demo("Shared string allocated in f"); 56 | acu_shared *s = acu_share(acu_latest()); 57 | 58 | /* If thrown, the only reference to the shared object will go out of scope here, 59 | * and both the reference and the shared object will be destructed asap, 60 | * in practice at the end of the CATCH block catching the exception. */ 61 | if (x == 2) throw(new_name_exception("Got two as argument")); 62 | 63 | /* Create new reference to the shared object and pass its ownersip to object pointed by global 'q', whose scope is 64 | * the inner scope in main. */ 65 | acu_transfer(acu_new_reference(s), q); 66 | 67 | if (x == 3) throw(new_fail_exception("fake-fail-exception", -1)); 68 | 69 | printf("...exit f\n"); 70 | acu_return s2; 71 | END 72 | 73 | char *g(int x, acu_unique *r) 74 | { 75 | printf("..enter g\n"); 76 | 77 | /* Scope of this object is actually function h, since definition of g is not enclosed in BEGIN..END brackets. */ 78 | char *s = acu_strdup_demo("String allocated in g"); 79 | 80 | char *p = f(x, r); 81 | printf("..exit g\n"); 82 | 83 | /* Even if BEGIN..END brackets were used, exit by return instead of acu_return 84 | * macro would escape the automatic end-of-scope cleanup, but only until the next 85 | * proper exit from a scope, or ultimately end of program. */ 86 | return p; 87 | } 88 | 89 | char *h(int x) 90 | BEGIN 91 | /* Create an empty unique node to this scope for receiving ownership of an object created by f. */ 92 | acu_unique *r = acu_reserve(); 93 | char *s; 94 | printf(".enter h\n"); 95 | TRY 96 | s = g(x, r); 97 | printf("Function g returned %s\n", s); 98 | printf("Got handle to shared string %s\n", (char *)acu_get_ptr(q)); 99 | CATCH(e) 100 | printf("Enter catch block of h\n"); 101 | struct name_exception *n = name_exception(e); 102 | if (n) printf("Caught name exception: %s\n", n->name); 103 | else rethrow; 104 | TRY_END 105 | printf(".exit h\n"); 106 | acu_return s; 107 | END 108 | 109 | int main(int argc, char *argv[]) 110 | BEGIN 111 | acu_init; 112 | 113 | int x = (argc > 1 ? atoi(argv[1]) : -1); 114 | printf("enter main\n"); 115 | char *s = acu_strdup_demo("String allocated in main"); 116 | BEGIN_SCOPE 117 | printf("enter main/inner scope\n"); 118 | q = acu_reserve(); 119 | char *s = h(x); 120 | printf("exit main/inner scope\n"); 121 | /* unique node pointed to by 'q' goes out of scope, and will be destructed, 122 | * reference count to shared object goes to zero, and the shared object 123 | * will also be destructed */ 124 | END_SCOPE 125 | printf("exit main\n"); 126 | acu_return 0; 127 | END 128 | 129 | -------------------------------------------------------------------------------- /exc_classes.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "exception.h" 5 | #include "exc_classes.h" 6 | 7 | static void _exc_to_str_nomem(char *buf, int n) { (void)snprintf(buf, n, "Out of heap memory"); } 8 | static void _exc_del_nomem(struct exception *e) { return; } 9 | struct exception _exc_sys_nomem_g = {EXCTYPE_NOMEM, _exc_to_str_nomem, _exc_del_nomem, "", 0}; 10 | 11 | 12 | /* Exception type specific functions: each type t requires four functions defined here 13 | * - _exc_to_str_(char *buf, int n) // Create string description of the exception to buffer buf 14 | * - _exc_del_(struct exception *e) // Destructor for exception e 15 | * - new__exception(...) // Create exception of type , arguments are type-dependent 16 | * - _exception(e) // Return correctly casted pointer to e, or NULL if not correct type 17 | * and in exception.h, struct _exception */ 18 | 19 | /* NAME */ 20 | static void _exc_to_str_name(char *buf, int n) 21 | { 22 | snprintf(buf, n, "name_exception: '%s'", name_exception(_exception_ptr)->name); 23 | } 24 | 25 | static void _exc_del_name(struct exception *e) 26 | { 27 | struct name_exception *dd = name_exception(e); 28 | if (dd == NULL) return; 29 | if (dd->name) free(dd->name); 30 | } 31 | 32 | struct exception *new_name_exception(const char *name) 33 | { 34 | struct name_exception *e = malloc(sizeof(struct name_exception)); 35 | if (e == NULL) throw(&_exc_sys_nomem_g); // Global const that can be thrown even if no memory can be allocated from the heap 36 | e->name = strdup(name); // use strdup_t instead 37 | _init_exception(&(e->e), EXCTYPE_NAME, _exc_to_str_name, _exc_del_name); 38 | return &(e->e); 39 | } 40 | 41 | struct name_exception *name_exception(struct exception *e) { return (struct name_exception *)(e->type == EXCTYPE_NAME ? e : NULL); } 42 | 43 | /* IO */ 44 | static void _exc_to_str_io(char *buf, int n) 45 | { 46 | snprintf(buf, n, "io_exception: errno=%d, function '%s', filename '%s'", 47 | io_exception(_exception_ptr)->err, 48 | io_exception(_exception_ptr)->function, 49 | io_exception(_exception_ptr)->filename 50 | ); 51 | } 52 | 53 | static void _exc_del_io(struct exception *e) 54 | { 55 | struct io_exception *dd = io_exception(e); 56 | if (dd == NULL) return; 57 | if (dd->function) free(dd->function); 58 | if (dd->filename) free(dd->filename); 59 | } 60 | 61 | struct exception *new_io_exception(int err, const char *filename, const char *function) 62 | { 63 | struct io_exception *e = malloc(sizeof(struct io_exception)); 64 | if (e == NULL) throw(&_exc_sys_nomem_g); // Global const that can be thrown even if no memory can be allocated from the heap 65 | e->err = err; 66 | e->filename = strdup(filename); // use strdup_t instead 67 | e->function = strdup(function); 68 | _init_exception(&(e->e), EXCTYPE_IO, _exc_to_str_io, _exc_del_io); 69 | return &(e->e); 70 | } 71 | 72 | struct io_exception *io_exception(struct exception *e) { return (struct io_exception *)(e->type == EXCTYPE_IO ? e : NULL); } 73 | 74 | /* MEM */ 75 | static void _exc_to_str_mem(char *buf, int n) 76 | { 77 | snprintf(buf, n, "mem_exception: function '%s', size %ld", mem_exception(_exception_ptr)->function, mem_exception(_exception_ptr)->size); 78 | } 79 | 80 | static void _exc_del_mem(struct exception *e) 81 | { 82 | struct mem_exception *dd = mem_exception(e); 83 | if (dd == NULL) return; 84 | if (dd->function) free(dd->function); 85 | } 86 | 87 | struct exception *new_mem_exception(const char *function, long size) 88 | { 89 | struct mem_exception *e = malloc(sizeof(struct mem_exception)); 90 | if (e == NULL) throw(&_exc_sys_nomem_g); // Global const that can be thrown even if no memory can be allocated from the heap 91 | e->size = size; 92 | e->function = strdup(function); 93 | _init_exception(&(e->e), EXCTYPE_MEM, _exc_to_str_mem, _exc_del_mem); 94 | return &(e->e); 95 | } 96 | 97 | struct mem_exception *mem_exception(struct exception *e) { return (struct mem_exception *)(e->type == EXCTYPE_MEM ? e : NULL); } 98 | 99 | 100 | /* TRUNC */ 101 | static void _exc_to_str_trunc(char *buf, int n) 102 | { 103 | snprintf(buf, n, "trunc_exception: function '%s', bufsize %ld", trunc_exception(_exception_ptr)->function, trunc_exception(_exception_ptr)->bufsize); 104 | } 105 | 106 | static void _exc_del_trunc(struct exception *e) 107 | { 108 | struct trunc_exception *dd = trunc_exception(e); 109 | if (dd == NULL) return; 110 | if (dd->function) free(dd->function); 111 | } 112 | 113 | struct exception *new_trunc_exception(const char *function, long bufsize) 114 | { 115 | struct trunc_exception *e = malloc(sizeof(struct trunc_exception)); 116 | if (e == NULL) throw(&_exc_sys_nomem_g); // Global const that can be thrown even if no memory can be allocated from the heap 117 | e->bufsize = bufsize; 118 | e->function = strdup(function); 119 | _init_exception(&(e->e), EXCTYPE_TRUNC, _exc_to_str_trunc, _exc_del_trunc); 120 | return &(e->e); 121 | } 122 | 123 | struct trunc_exception *trunc_exception(struct exception *e) { return (struct trunc_exception *)(e->type == EXCTYPE_TRUNC ? e : NULL); } 124 | 125 | /* NULLPTR */ 126 | static void _exc_to_str_nullptr(char *buf, int n) 127 | { 128 | snprintf(buf, n, "nullptr_exception: function '%s'", nullptr_exception(_exception_ptr)->function); 129 | } 130 | 131 | static void _exc_del_nullptr(struct exception *e) 132 | { 133 | struct nullptr_exception *dd = nullptr_exception(e); 134 | if (dd == NULL) return; 135 | if (dd->function) free(dd->function); 136 | } 137 | 138 | struct exception *new_nullptr_exception(const char *function) 139 | { 140 | struct nullptr_exception *e = malloc(sizeof(struct nullptr_exception)); 141 | if (e == NULL) throw(&_exc_sys_nomem_g); // Global const that can be thrown even if no memory can be allocated from the heap 142 | e->function = strdup(function); 143 | _init_exception(&(e->e), EXCTYPE_NULLPTR, _exc_to_str_nullptr, _exc_del_nullptr); 144 | return &(e->e); 145 | } 146 | 147 | struct nullptr_exception *nullptr_exception(struct exception *e) { return (struct nullptr_exception *)(e->type == EXCTYPE_NULLPTR ? e : NULL); } 148 | 149 | /* SIG */ 150 | static void _exc_to_str_sig(char *buf, int n) 151 | { 152 | snprintf(buf, n, "sig_exception: function '%s', signal %d", sig_exception(_exception_ptr)->function, sig_exception(_exception_ptr)->signal); 153 | } 154 | 155 | static void _exc_del_sig(struct exception *e) 156 | { 157 | struct sig_exception *dd = sig_exception(e); 158 | if (dd == NULL) return; 159 | if (dd->function) free(dd->function); 160 | } 161 | 162 | struct exception *new_sig_exception(const char *function, int signal) 163 | { 164 | struct sig_exception *e = malloc(sizeof(struct sig_exception)); 165 | if (e == NULL) throw(&_exc_sys_nomem_g); // Global const that can be thrown even if no memory can be allocated from the heap 166 | e->signal = signal; 167 | e->function = strdup(function); 168 | _init_exception(&(e->e), EXCTYPE_SIG, _exc_to_str_sig, _exc_del_sig); 169 | return &(e->e); 170 | } 171 | 172 | struct sig_exception *sig_exception(struct exception *e) { return (struct sig_exception *)(e->type == EXCTYPE_SIG ? e : NULL); } 173 | 174 | /* FAIL */ 175 | static void _exc_to_str_fail(char *buf, int n) 176 | { 177 | snprintf(buf, n, "fail_exception: function '%s' returned %d", fail_exception(_exception_ptr)->function, fail_exception(_exception_ptr)->retval); 178 | } 179 | 180 | static void _exc_del_fail(struct exception *e) 181 | { 182 | struct fail_exception *dd = fail_exception(e); 183 | if (dd == NULL) return; 184 | if (dd->function) free(dd->function); 185 | } 186 | 187 | struct exception *new_fail_exception(const char *function, int retval) 188 | { 189 | struct fail_exception *e = malloc(sizeof(struct fail_exception)); 190 | if (e == NULL) throw(&_exc_sys_nomem_g); // Global const that can be thrown even if no memory can be allocated from the heap 191 | e->retval = retval; 192 | e->function = strdup(function); 193 | _init_exception(&(e->e), EXCTYPE_FAIL, _exc_to_str_fail, _exc_del_fail); 194 | return &(e->e); 195 | } 196 | 197 | struct fail_exception *fail_exception(struct exception *e) { return (struct fail_exception *)(e->type == EXCTYPE_FAIL ? e : NULL); } 198 | 199 | -------------------------------------------------------------------------------- /exc_classes.h: -------------------------------------------------------------------------------- 1 | #ifndef EXC_CLASSES_H 2 | #define EXC_CLASSES_H 3 | 4 | #include "exception.h" 5 | 6 | #define EXCTYPE_NOMEM 0 7 | #define EXCTYPE_NAME 1 8 | #define EXCTYPE_IO 2 9 | #define EXCTYPE_MEM 3 10 | #define EXCTYPE_TRUNC 4 11 | #define EXCTYPE_NULLPTR 5 12 | #define EXCTYPE_SIG 6 13 | #define EXCTYPE_FAIL 7 14 | 15 | extern struct exception _exc_sys_nomem_g; 16 | 17 | /* name_exception */ 18 | struct name_exception { 19 | struct exception e; 20 | char *name; 21 | }; 22 | 23 | struct exception *new_name_exception(const char *name); 24 | struct name_exception *name_exception(struct exception *e); 25 | 26 | /* io_exception */ 27 | struct io_exception { 28 | struct exception e; 29 | int err; 30 | char *filename; 31 | char *function; 32 | }; 33 | 34 | struct exception *new_io_exception(int err, const char *filename, const char *function); 35 | struct io_exception *io_exception(struct exception *e); 36 | 37 | /* trunc_exception */ 38 | struct trunc_exception { 39 | struct exception e; 40 | char *function; 41 | long bufsize; 42 | }; 43 | 44 | struct exception *new_trunc_exception(const char *function, long bufsize); 45 | struct trunc_exception *trunc_exception(struct exception *e); 46 | 47 | /* mem_exception */ 48 | struct mem_exception { 49 | struct exception e; 50 | char *function; 51 | long size; 52 | }; 53 | 54 | struct exception *new_mem_exception(const char *function, long size); 55 | struct mem_exception *mem_exception(struct exception *e); 56 | 57 | /* nullptr_exception */ 58 | struct nullptr_exception { 59 | struct exception e; 60 | char *function; 61 | }; 62 | 63 | struct exception *new_nullptr_exception(const char *function); 64 | struct nullptr_exception *nullptr_exception(struct exception *e); 65 | 66 | /* sig_exception */ 67 | struct sig_exception { 68 | struct exception e; 69 | char *function; 70 | int signal; 71 | }; 72 | 73 | struct exception *new_sig_exception(const char *function, int signal); 74 | struct sig_exception *sig_exception(struct exception *e); 75 | 76 | /* fail_exception */ 77 | struct fail_exception { 78 | struct exception e; 79 | char *function; 80 | int retval; 81 | }; 82 | 83 | struct exception *new_fail_exception(const char *function, int retval); 84 | struct fail_exception *fail_exception(struct exception *e); 85 | 86 | #endif 87 | -------------------------------------------------------------------------------- /exc_std.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "exception.h" 11 | #include "exc_classes.h" 12 | #include "exc_std.h" 13 | 14 | void *malloc_t(size_t s) 15 | { 16 | void *p = malloc(s); 17 | if (p == NULL) throw(new_mem_exception("malloc", s)); 18 | return p; 19 | } 20 | 21 | void *calloc_t(size_t n, size_t s) 22 | { 23 | void *p = calloc(n, s); 24 | if (p == NULL) throw(new_mem_exception("calloc", n * s)); 25 | return p; 26 | } 27 | 28 | void *realloc_t(void *ptr, size_t s) 29 | { 30 | void *p = realloc(ptr, s); 31 | if (p == NULL) throw(new_mem_exception("realloc", s)); 32 | return p; 33 | } 34 | 35 | void free_t(void *p) 36 | { 37 | if (p == NULL) throw(new_nullptr_exception("free")); 38 | free(p); 39 | } 40 | 41 | char *strdup_t(const char *s) 42 | { 43 | char *p = strdup(s); 44 | if (p == NULL) throw(new_mem_exception("strdup", strlen(s) + 1)); 45 | } 46 | 47 | FILE *fopen_t(const char *n, const char *m) 48 | { 49 | FILE *f = fopen(n, m); 50 | if (f == NULL) throw(new_io_exception(errno, n, "fopen")); 51 | return f; 52 | } 53 | 54 | int open_t(const char *n, int m) 55 | { 56 | int fd = open(n, m); 57 | if (fd < 0) throw(new_io_exception(errno, n, "open")); 58 | return fd; 59 | } 60 | 61 | char *fgets_t(char *s, int n, FILE *f) 62 | { 63 | char *p = fgets(s, n, f); 64 | if (p == NULL) throw(new_io_exception(errno, "", "fgets")); 65 | if (s[strlen(s)-1] != '\n') throw(new_trunc_exception("fgets", n)); 66 | return p; 67 | } 68 | 69 | int snprintf_t(char *s, int n, const char *fmt, ...) 70 | { 71 | va_list ap; 72 | va_start(ap, fmt); 73 | int m = vsnprintf(s, n, fmt, ap); 74 | if (m < 0) throw(new_name_exception("vsnprint error")); 75 | if (m >= n) throw(new_trunc_exception("vsnprintf", n)); 76 | return m; 77 | } 78 | 79 | int fprintf_t(FILE *f, const char *fmt, ...) 80 | { 81 | va_list ap; 82 | va_start(ap, fmt); 83 | int m = vfprintf(f, fmt, ap); 84 | if (m < 0) throw(new_io_exception(errno, "", "vfprintf")); 85 | return m; 86 | } 87 | 88 | char *strncpy_t(char *d, const char *s, int n) 89 | { 90 | char *r = strncpy(d, s, n); 91 | if (strlen(s) + 1 > n) throw(new_trunc_exception("strncpy", n)); 92 | return r; 93 | } 94 | 95 | int system_t(const char *cmd) 96 | { 97 | int v = system(cmd); 98 | if (WIFEXITED(v)) return WEXITSTATUS(v); 99 | if (WIFSIGNALED(v)) throw(new_sig_exception("system", WTERMSIG(v))); 100 | throw(new_fail_exception("system", v)); 101 | } 102 | 103 | void system_t_fail(const char *cmd) 104 | { 105 | int v = system_t(cmd); 106 | if (v) throw(new_fail_exception("system", v)); 107 | } 108 | 109 | -------------------------------------------------------------------------------- /exc_std.h: -------------------------------------------------------------------------------- 1 | #ifndef EXC_STD_H 2 | #define EXC_STD_H 3 | 4 | void *malloc_t(size_t); 5 | void *calloc_t(size_t, size_t); 6 | void *realloc_t(void *, size_t); 7 | void free_t(void *); 8 | char *strdup_t(const char *); 9 | 10 | FILE *fopen_t(const char *, const char *); 11 | int open_t(const char *, int); 12 | 13 | char *fgets_t(char *, int, FILE *); 14 | int snprintf_t(char *, int, const char *, ...); 15 | int fprintf_t(FILE *, const char *, ...); 16 | 17 | char *strncpy_t(char *, const char *, int); 18 | 19 | int system_t(const char *); 20 | void system_t_fail(const char *); 21 | #endif 22 | -------------------------------------------------------------------------------- /exception.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "exception.h" 5 | 6 | __thread struct exception *_exception_ptr = NULL; 7 | 8 | __thread jmp_buf *_exc_context = NULL; 9 | 10 | void _init_exception(struct exception *e, int type, void (*to_str)(char *, int), void (*del)(struct exception *)) 11 | { 12 | e->type = type; e->to_str = to_str; e->del = del; 13 | } 14 | 15 | void _del_exception(struct exception *e) 16 | { 17 | free(e->file); 18 | (e->del)(e); 19 | free(e); 20 | } 21 | 22 | void _exc_default_handler(void) 23 | { 24 | char buf[256]; 25 | if (_exception_ptr->to_str) (_exception_ptr->to_str)(buf, sizeof(buf)); 26 | else snprintf(buf, sizeof(buf), "type %d", _exception_ptr->type); 27 | fprintf(stderr, "Uncaught exception (%s, line %d): %s\n", _exception_ptr->file, _exception_ptr->line, buf); 28 | acu_exit(1); 29 | } 30 | 31 | void _exc_clear(void) 32 | { 33 | if (_exception_ptr == NULL) return; 34 | _del_exception(_exception_ptr); 35 | _exception_ptr = NULL; 36 | } 37 | 38 | 39 | -------------------------------------------------------------------------------- /exception.h: -------------------------------------------------------------------------------- 1 | #ifndef EXCEPTION_H 2 | #define EXCEPTION_H 3 | 4 | #include 5 | #include 6 | #include "autocleanup.h" 7 | 8 | struct exception { 9 | int type; 10 | void (*to_str)(char *, int); 11 | void (*del)(struct exception *); 12 | char *file; 13 | int line; 14 | }; 15 | 16 | extern __thread jmp_buf *_exc_context; 17 | extern __thread struct exception *_exception_ptr; 18 | 19 | void _init_exception(struct exception *, int type, void (*to_str)(char *buf, int n), void (*del)(struct exception *)); 20 | 21 | void _exc_default_handler(void); 22 | void _exc_clear(void); 23 | 24 | #define TRY BEGIN_SCOPE \ 25 | jmp_buf _new_context, *_prev_context = _exc_context; \ 26 | _exc_context = &_new_context; \ 27 | if (setjmp(_new_context) == 0) { 28 | 29 | 30 | #define CATCH(e) _exc_context = _prev_context; \ 31 | } else { \ 32 | struct exception *e = _exception_ptr; \ 33 | _exc_context = _prev_context; 34 | 35 | #define TRY_END _exc_clear(); } END_SCOPE 36 | 37 | #define throw(e) { struct exception *_e = (e); \ 38 | if (_e && _e != _exception_ptr) { \ 39 | _exc_clear(); _exception_ptr = _e; \ 40 | _e->line = __LINE__; _e->file = strdup(__FILE__); \ 41 | } \ 42 | if (_exc_context) longjmp(*_exc_context, 1); \ 43 | _exc_default_handler(); } 44 | 45 | #define rethrow throw(NULL) 46 | 47 | #endif 48 | 49 | -------------------------------------------------------------------------------- /smartpointers_in_c.txt: -------------------------------------------------------------------------------- 1 | AUTOCLEANUP IN C 2 | ================ 3 | 4 | Author: Petteri Sevon 5 | Date: 2014-04-06 6 | License: LICENSE.txt (FreeBSD) 7 | 8 | This C library provides RAII functionality similar to unique_ptr and 9 | shared_ptr smart pointers in C++. It provides two opaque classes, 10 | acu_unique and acu_shared, for automatic cleanup when the object goes 11 | out of scope, or its reference counter goes to zero. Such automatic 12 | cleanup can significantly reduce boiler plate code, especially in 13 | functions allocating many resources and having many exit points. The 14 | library works seamlessly with exception.c, a library defining exception 15 | classes and macros for throwing and catching exceptions. Automatic 16 | cleanup is particularly useful in presence of exceptions, since it 17 | eliminates the need for having "finally" blocks (for which there's no 18 | support in exception.c), and allows intermediate functions to let 19 | exceptions pass through uncaught and still have their locally 20 | allocated resources cleaned up properly. 21 | 22 | Since C does not provide any mechanism to hook any additional 23 | functionality to braces limiting a scope, the library defines macro 24 | brackets BEGIN and END for enclosing function definitions, and 25 | BEGIN_SCOPE and END_SCOPE for enclosing inner scopes (freely nestable). 26 | Also, exiting scope using return, goto, continue, break or longjmp 27 | should be avoided (although the objects will be destructed later even if 28 | cleanup is escaped due to improper scope exit). Use macros acu_return 29 | or acu_exit_scope, or function acu_exit(v) to properly exit a scope or 30 | function. 31 | 32 | The unique and shared pointers defined in this library have a slightly 33 | different role compared to their C++ counterparts, as they are primarily 34 | only intended to control the lifespan of the objects they point to, and 35 | not for accessing the objects, although functionality is provided for 36 | object access (acu_get_ptr(u)), but due to limitations of C the 37 | returned pointer must be excplicitly casted to correct type. 38 | 39 | Another major difference is that the C implementations have an extra 40 | layer of indirection. For example, constructor 41 | 42 | acu_unique *u = acu_new_unique(o, del); 43 | 44 | returns pointer to the created unique pointer managing object 'o' which 45 | has destructor function 'del'. Smart pointers are always accessed through 46 | pointers to them, never directly. The smart pointer is bound to the scope 47 | in which it's created, not the scope of 'u'. The value of 'u' can be 48 | freely assigned to any variable in any scope, but that has no effect on 49 | the scope or lifespan of the actual smartpointer. 50 | 51 | The transfer of ownership (move in C++) is done using function 52 | 53 | acu_transfer(a, b); 54 | 55 | which passes the ownership of the object managed by 'a' to another 56 | acu_unique object 'b', typically passed as an argument to the function 57 | which constructed 'a'. Unique pointer 'a' will be undefined after this call. 58 | Empty acu_unique objects required for receiving the ownership can be 59 | created using 60 | 61 | acu_unique *r = acu_reserve(); 62 | 63 | Another function for changing ownership is acu_swap(a, b), which 64 | exchanges the contens of the acu_unique objects. 65 | 66 | acu_unique object 'u' can be shared by calling 67 | 68 | acu_shared *s = acu_share(u); 69 | 70 | This creates new shared pointer 's', whose reference count is one, and 71 | whose object pointer and destructor are copied from 'u'. Contents of 72 | unique pointer 'u' are replaced with a strong reference to the new 73 | shared pointer 's', and destuctor of 'u' is function that decreases 74 | reference_count of 's' by one, and calls destructor of the managed 75 | object if it goes to zero. Note that acu_shared object actually is 76 | similar to the counter object pointed to by a shared_ptr in C++. 77 | 78 | A new acu_unique reference to a shared pointer 's' can be created by 79 | calling acu_new_reference(s) or acu_new_weak_reference(s). Before 80 | accessing an object weakly referenced by 'acu_unique u', a strong 81 | reference must be acquired first by calling acu_lock_reference(u), 82 | which returns pointer to the new acu_unique object, or NULL if the 83 | object has expired. 84 | 85 | Reference counters to a shared pointer are decreased only when a 86 | referring unique or shared pointer is destructed. When the strong 87 | reference counter goes to zero, destructor of the managed object is 88 | called. When the weak counter goes to zero, the shared pointer is 89 | destructed, too. 90 | 91 | Only unique pointers can be explicitly destructed from the client code, 92 | by calling acu_destruct(u). This is only necessary for releasing 93 | time-critical resources such as mutex locks, if the end of scope is too 94 | far away. 95 | 96 | A unique pointer can be submitted to a shared pointer so that 97 | destruction of the shared object triggers destruction of the unique 98 | object. This can be done using function 99 | 100 | void acu_submit_to(acu_unique *u, acu_shared *s); 101 | 102 | Any number of unique pointers can be submitted to the same shared 103 | pointer. Submitting unique pointers is useful for construction of 104 | compound objects whose parts have their own constructors and 105 | destructors. Unique pointers submitted to a shared pointer have no other 106 | references to them besides being linked to the shared pointers. Their 107 | only purpose is to ensure that the objects they point to become 108 | destructed together with the shared object. After the call, unique 109 | pointer referenced by 'u' becomes undefined. 110 | 111 | By default, shared pointers are not thread-safe, but they can be made 112 | such by defining ACU_THREAD_SAFE before including any of the related 113 | headers. Thread safety is achieved by mutex protecting submission of new 114 | unique pointers to a shared pointer, and atomicity of counter 115 | operations. The latter is implemented using gcc-supported 116 | __sync_sub_and_fetch and __sync_add_and_fetch builtins. Reference to a 117 | shared object may only be passed to another thread by using 118 | acu_transfer() or acu_swap(). Client code may not assume that transfer 119 | is atomic. Signalling that the receiving acu_unique is ready, and that 120 | transfer is completed, is left to client code. 121 | 122 | 123 | Remarks regarding scopes: 124 | ------------------------- 125 | 126 | A scope for autocleanup functions is a block of code enclosed in 127 | BEGIN..END or BEGIN_SCOPE..END_SCOPE macro brackets. Normal braces {..} 128 | do not limit scopes. Because of this, a function whose definition is 129 | enclosed in braces belongs to the calling function's scope. This feature 130 | is utilized in the acu-wrappers for standard C library constructors in 131 | acu_std.c. For example malloc is wrapped inside acu_malloc: 132 | 133 | void *acu_malloc(size_t s) 134 | { 135 | void *p = malloc(s); 136 | if (p) (void)acu_new_unique(p, free); 137 | return p; 138 | } 139 | 140 | If BEGIN..END and acu_return were used in the above function, the 141 | allocated memory would be destructed already upon return. Since 142 | acu_malloc does not return the acu_unique pointer it creates, the only 143 | way for client code to obtain it is to call acu_latest(), which returns 144 | pointer to the latest allocated acu_unique pointer within the function 145 | scope, if any. 146 | 147 | Another approach to creating acu-constructors is to pass a reference to 148 | the acu_unique pointer as an argument to the constructor. Malloc wrapped 149 | this way would look like this: 150 | 151 | void *acu_malloc_u(size_t s, acu_unique *r) 152 | { 153 | void *p = malloc(s); 154 | if (p) r = acu_new_unique(p, free); 155 | return p; 156 | } 157 | 158 | 159 | If the constructor needs to allocate some temporary resources, it can 160 | utilize autocleaup itself: 161 | 162 | void *acu_malloc_init(size_t s, acu_unique *r) 163 | BEGIN 164 | acu_unique *lock = acu_pthread_mutex_lock(mutex_g); 165 | 166 | // acu_malloc_t throws exception if allocation fails 167 | void *p = acu_malloc_t(s); 168 | 169 | // This may throw exception if fails, in which case the 170 | // acquired memory and lock will be automatically freed 171 | do_some_initialization(p); 172 | 173 | // Explicit destruction to keep mutexing period to the 174 | // minimum, although hardly makes a real difference here. 175 | acu_destruct(lock); 176 | 177 | if (p) acu_transfer(acu_new_unique(p, free), r); 178 | acu_return p; 179 | END 180 | 181 | This approach requires that the client creates an empty acu_unique 182 | pointer to pass as argument using acu_reserve(). 183 | 184 | --------------------------------------------------------------------------------