#include #include #include #include "jv_alloc.h" struct nomem_handler { jv_nomem_handler_f handler; void *data; }; #if !defined(HAVE_PTHREAD_KEY_CREATE) || \ !defined(HAVE_PTHREAD_ONCE) || \ !defined(HAVE_ATEXIT) /* Try thread-local storage? */ #ifdef _MSC_VER /* Visual C++: yes */ static __declspec(thread) struct nomem_handler nomem_handler; #define USE_TLS #else #ifdef HAVE___THREAD /* GCC and friends: yes */ static __thread struct nomem_handler nomem_handler; #define USE_TLS #endif /* HAVE___THREAD */ #endif /* _MSC_VER */ #endif /* !HAVE_PTHREAD_KEY_CREATE */ #ifdef USE_TLS void jv_nomem_handler(jv_nomem_handler_f handler, void *data) { nomem_handler.handler = handler; } static void memory_exhausted() { if (nomem_handler.handler) nomem_handler.handler(nomem_handler.data); // Maybe handler() will longjmp() to safety // Or not fprintf(stderr, "error: cannot allocate memory\n"); abort(); } #else /* USE_TLS */ #ifdef HAVE_PTHREAD_KEY_CREATE #include pthread_key_t nomem_handler_key; pthread_once_t mem_once = PTHREAD_ONCE_INIT; static void tsd_fini(void) { struct nomem_handler *nomem_handler; nomem_handler = pthread_getspecific(nomem_handler_key); if (nomem_handler) { (void) pthread_setspecific(nomem_handler_key, NULL); free(nomem_handler); } } static void tsd_init(void) { if (pthread_key_create(&nomem_handler_key, NULL) != 0) { fprintf(stderr, "error: cannot create thread specific key"); abort(); } if (atexit(tsd_fini) != 0) { fprintf(stderr, "error: cannot set an exit handler"); abort(); } struct nomem_handler *nomem_handler = calloc(1, sizeof(struct nomem_handler)); if (pthread_setspecific(nomem_handler_key, nomem_handler) != 0) { fprintf(stderr, "error: cannot set thread specific data"); abort(); } } void jv_nomem_handler(jv_nomem_handler_f handler, void *data) { pthread_once(&mem_once, tsd_init); // cannot fail struct nomem_handler *nomem_handler; nomem_handler = pthread_getspecific(nomem_handler_key); if (nomem_handler == NULL) { handler(data); fprintf(stderr, "error: cannot allocate memory\n"); abort(); } nomem_handler->handler = handler; nomem_handler->data = data; } static void memory_exhausted() { struct nomem_handler *nomem_handler; pthread_once(&mem_once, tsd_init); nomem_handler = pthread_getspecific(nomem_handler_key); if (nomem_handler) nomem_handler->handler(nomem_handler->data); // Maybe handler() will longjmp() to safety // Or not fprintf(stderr, "error: cannot allocate memory\n"); abort(); } #else /* No thread-local storage of any kind that we know how to handle */ static struct nomem_handler nomem_handler; void jv_nomem_handler(jv_nomem_handler_f handler, void *data) { nomem_handler.handler = handler; nomem_handler.data = data; } static void memory_exhausted() { fprintf(stderr, "error: cannot allocate memory\n"); abort(); } #endif /* HAVE_PTHREAD_KEY_CREATE */ #endif /* USE_TLS */ void* jv_mem_alloc(size_t sz) { void* p = malloc(sz); if (!p) { memory_exhausted(); } return p; } void* jv_mem_alloc_unguarded(size_t sz) { return malloc(sz); } void* jv_mem_calloc(size_t nemb, size_t sz) { void* p = calloc(nemb, sz); if (!p) { memory_exhausted(); } return p; } void* jv_mem_calloc_unguarded(size_t nemb, size_t sz) { return calloc(nemb, sz); } char* jv_mem_strdup(const char *s) { char *p = strdup(s); if (!p) { memory_exhausted(); } return p; } char* jv_mem_strdup_unguarded(const char *s) { return strdup(s); } void jv_mem_free(void* p) { free(p); } void* jv_mem_realloc(void* p, size_t sz) { p = realloc(p, sz); if (!p) { memory_exhausted(); } return p; } #ifndef NDEBUG volatile char jv_mem_uninitialised; __attribute__((constructor)) void jv_mem_uninit_setup(){ // ignore warning that this reads uninitialized memory - that's the point! #ifndef __clang_analyzer__ char* p = malloc(1); jv_mem_uninitialised = *p; free(p); #endif } #endif