summaryrefslogtreecommitdiffstats
path: root/jv_alloc.c
blob: 03e9a7ce763148f42f3bea1a2f9041fd2bdb55a7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
#include <stdlib.h>
#include <stdio.h>
#include "jv_alloc.h"

struct nomem_handler {
    jv_nomem_handler_f handler;
    void *data;
};

#ifndef USE_PTHREAD_KEY
#ifdef _MSC_VER
static __declspec(thread) struct nomem_handler nomem_handler;
#else
static __thread struct nomem_handler nomem_handler;
#endif

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
#ifdef HAVE_PTHREAD_KEY_CREATE
#include <pthread.h>

pthread_key_t nomem_handler_key;
pthread_once_t mem_once = PTHREAD_ONCE_INIT;

static void tsd_init(void) {
  if (pthread_key_create(&nomem_handler_key, NULL) != 0) {
    fprintf(stderr, "error: cannot create thread specific key");
    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 = calloc(1, sizeof (nomem_handler));
  if (nomem_handler == NULL) {
    handler(data);
    fprintf(stderr, "error: cannot allocate memory\n");
    abort();
  }
  nomem_handler.handler = handler;
  nomem_handler.data = data;
  if (pthread_setspecific(nomem_handler_key, nomem_handler) != 0) {
    handler(data);
    fprintf(stderr, "error: cannot set thread specific data");
    abort();
  }
}

static void memory_exhausted() {
  jv_nomem_handler_f handler;

  pthread_once(&mem_once, tsd_init);
  handler = pthread_getspecific(nomem_handler_key);
  if (handler)
    handler(); // Maybe handler() will longjmp() to safety
  // Or not
  fprintf(stderr, "error: cannot allocate memory\n");
  abort();
}

#else

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_PTHREAD_KEY */


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_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(){
  char* p = malloc(1);
  jv_mem_uninitialised = *p;
  free(p);
}
#endif