ni: adds global references to Nit objects from C code
[nit.git] / clib / nit_main.c
1 /* This file is part of NIT ( http://www.nitlanguage.org ).
2 *
3 * Copyright 2006-2009 Jean Privat <jean@pryen.org>
4 *
5 * This file is free software, which comes along with NIT. This software is
6 * distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
7 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
8 * PARTICULAR PURPOSE. You can modify it is you want, provided this header
9 * is kept unaltered, and a notification of the changes is added.
10 * You are allowed to redistribute it and sell it, alone or is a part of
11 * another product.
12 */
13
14 #include "nit_common.h"
15 #include <signal.h>
16 #include <stdarg.h>
17 #include "gc.h"
18
19 bigint object_id_counter = 1000000;
20 enum gc_option { large, gc_opt_malloc, boehm, nitgc } gc_option;
21
22 #ifdef WITH_LIBGC
23 #define GC_DEBUG
24 #include <gc/gc.h>
25 #endif
26
27 void *raw_alloc(size_t s0)
28 {
29 switch (gc_option) {
30 case nitgc: return malloc(s0);
31 case gc_opt_malloc: return malloc(s0);
32 default: return alloc(s0);
33 }
34 }
35
36 void register_static_object(val_t *o)
37 {
38 switch (gc_option) {
39 case nitgc: GC_add_static_object(o); break;
40 default: break;
41 }
42 return;
43 }
44
45 void *large_alloc(size_t s0)
46 {
47 static char * alloc_pos = NULL;
48 static size_t alloc_size = 0;
49 void * res;
50 size_t s = ((s0+3)/4)*4;
51 if(alloc_size < s) {
52 alloc_size = s + 1024*1024;
53 alloc_pos = (char *)calloc(alloc_size, 1);
54 }
55 res = alloc_pos;
56 alloc_size -= s;
57 alloc_pos += s;
58 return res;
59 }
60
61 void * alloc(size_t s0)
62 {
63 switch (gc_option) {
64 #ifdef WITH_LIBGC
65 case boehm: return GC_MALLOC(s0);
66 #endif
67 case nitgc: return Nit_gc_malloc(s0);
68 case gc_opt_malloc: return calloc(1, s0);
69 case large:
70 default: return large_alloc(s0);
71 }
72 }
73
74 int glob_argc;
75 char **glob_argv;
76 val_t G_sys;
77
78 void exithandler(int s) {
79 fprintf(stderr, "Recieved signal %d\n", s);
80 nit_exit(1);
81 }
82 void initialize_gc_option(void) {
83 /* GC default */
84 char *def;
85 #ifdef WITH_LIBGC
86 gc_option = boehm;
87 def = "boehm";
88 #else
89 gc_option = nitgc;
90 def = "nitgc";
91 #endif
92
93 /* Process GC runtime selection */
94 if (getenv("NIT_GC_OPTION") != NULL) {
95 char *opt=getenv("NIT_GC_OPTION");
96 if (strcmp(opt, "boehm")==0) {
97 #ifdef WITH_LIBGC
98 gc_option = boehm;
99 #else
100 fprintf(stderr, "Compiled without Boehm GC support. Using default '%s'.\n", def);
101 #endif
102 } else if (strcmp(opt, "nitgc")==0) {
103 gc_option = nitgc;
104 } else if (strcmp(opt, "malloc")==0) {
105 gc_option = gc_opt_malloc;
106 } else if (strcmp(opt, "large")==0) {
107 gc_option = large;
108 } else if (strcmp(opt, "help")==0) {
109 fprintf(stderr, "NIT_GC_OPTION accepts 'nitgc'"
110 #ifdef WITH_LIBGC
111 ", 'boehm'"
112 #endif
113 ", 'large', 'malloc'. Default is '%s'.\n", def);
114 exit(1);
115 } else {
116 fprintf(stderr, "Invalid GC option in NIT_GC_OPTION environment variable. Using default '%s'.\n", def);
117 }
118 }
119
120 /* Initialize GC (if needed) */
121 switch(gc_option) {
122 #ifdef WITH_LIBGC
123 case boehm: GC_INIT(); break;
124 #endif
125 case nitgc: Nit_gc_init(); break;
126 default: break; /* Nothing */
127 }
128
129 /* Initialize global references list */
130 nitni_global_ref_list_init();
131 }
132 void prepare_signals(void) {
133 initialize_gc_option();
134
135 signal(SIGINT,exithandler);
136 signal(SIGABRT,exithandler);
137 signal(SIGSEGV,exithandler);
138 signal(SIGILL, exithandler);
139 signal(SIGFPE, exithandler);
140 signal(SIGTERM,exithandler);
141 signal(SIGBUS, exithandler);
142 }
143 struct stack_frame_t *stack_frame_head = NULL;
144 void nit_exit(int i) {
145 char *opt=getenv("NIT_NO_STACK");
146 if (opt == NULL || strcmp(opt, "0")==0) {
147 fprintf(stderr, ",---- Stack trace -- - - -\n");
148 while(stack_frame_head != NULL) {
149 fprintf(stderr, "| %s (%s:%d)\n", stack_frame_head->meth, stack_frame_head->file, stack_frame_head->line);
150 if (stack_frame_head == stack_frame_head->prev) break;
151 stack_frame_head = stack_frame_head->prev;
152 }
153 fprintf(stderr, "`------------------- - - -\n");
154 }
155 exit(i);
156 }
157
158 void nit_abort(const char* s, const char* msg, const char* loc, int line) {
159 fprintf(stderr, s, msg);
160 fprintf(stderr, " (%s", loc);
161 if (line != 0) fprintf(stderr, ":%d", line);
162 fprintf(stderr, ")\n");
163 nit_exit(1);
164 }
165
166 /* Register reference to Nit object with the latest extern method called. */
167 void nitni_local_ref_add( struct nitni_ref *ref ) {
168 struct nitni_ref_array_link **link_p;
169 struct nitni_ref_array_link * link = NULL;
170
171 /* find usable link or link to create */
172 link_p = &( stack_frame_head->nitni_local_ref_head );
173 while ( (*link_p) != NULL &&
174 (*link_p)->count >= NITNI_REF_ARRAY_LINK_SIZE ) {
175 link_p = &((*link_p)->next);
176 }
177
178 /* create link if needed */
179 if ( *link_p == NULL ) {
180 link = malloc( sizeof(struct nitni_ref_array_link) );
181 link->count = 0;
182 link->next = NULL;
183
184 (*link_p) = link;
185 } else {
186 link = *link_p;
187 }
188
189 /* add to link */
190 link->reg[ link->count ] = ref;
191 link->count ++;
192 }
193
194 /* Clean all references associated to the current (but returning) extern method. */
195 void nitni_local_ref_clean( void ) {
196 struct nitni_ref_array_link * link,
197 * last_link;
198 int i;
199
200 link = stack_frame_head->nitni_local_ref_head;
201 while ( link != NULL )
202 {
203 for ( i = 0; i < link->count; i ++ ) {
204 if ( link->reg[i]->count == 0 ) { /* not registered globally */
205 free( link->reg[ i ] );
206 }
207 }
208
209 last_link = link;
210 if ( link->count == NITNI_REF_ARRAY_LINK_SIZE )
211 link = link->next;
212 else
213 link = NULL;
214
215 free(last_link);
216 }
217
218 stack_frame_head->nitni_local_ref_head = NULL;
219 }
220
221 struct nitni_global_ref_list_t *nitni_global_ref_list;
222 void nitni_global_ref_list_init() {
223 nitni_global_ref_list = (struct nitni_global_ref_list_t*)malloc(sizeof(struct nitni_global_ref_list_t));
224 nitni_global_ref_list->head = NULL;
225 nitni_global_ref_list->tail = NULL;
226 }
227
228 void nitni_global_ref_add( struct nitni_ref *ref ) {
229 if ( nitni_global_ref_list->head == NULL ) {
230 nitni_global_ref_list->head = ref;
231 nitni_global_ref_list->tail = ref;
232
233 ref->prev = NULL;
234 } else {
235 nitni_global_ref_list->tail->next = ref;
236 ref->prev = nitni_global_ref_list->tail;
237 }
238
239 ref->next = NULL;
240 }
241
242 void nitni_global_ref_remove( struct nitni_ref *ref ) {
243 if ( ref->prev == NULL ) {
244 nitni_global_ref_list->head = ref->next;
245 } else {
246 ref->prev->next = ref->next;
247 }
248
249 if ( ref->next == NULL ) {
250 nitni_global_ref_list->tail = ref->prev;
251 } else {
252 ref->next->prev = ref->prev;
253 }
254 }
255
256 extern void nitni_global_ref_incr( struct nitni_ref *ref ) {
257 if ( ref->count == 0 ) /* not registered */
258 {
259 /* add to list */
260 nitni_global_ref_add( ref );
261 }
262
263 ref->count ++;
264 }
265
266 extern void nitni_global_ref_decr( struct nitni_ref *ref ) {
267 if ( ref->count == 1 ) /* was last reference */
268 {
269 /* remove from list */
270 nitni_global_ref_remove( ref );
271 }
272
273 ref->count --;
274 }