Line data Source code
1 : /*
2 : * SPDX-License-Identifier: MPL-2.0
3 : *
4 : * This Source Code Form is subject to the terms of the Mozilla Public
5 : * License, v. 2.0. If a copy of the MPL was not distributed with this
6 : * file, You can obtain one at http://mozilla.org/MPL/2.0/.
7 : *
8 : * Copyright 2024 MonetDB Foundation;
9 : * Copyright August 2008 - 2023 MonetDB B.V.;
10 : * Copyright 1997 - July 2008 CWI.
11 : */
12 :
13 : /*
14 : * @a M. L. Kersten, P. Boncz, N. Nes
15 : *
16 : * @* Utilities
17 : * The utility section contains functions to initialize the Monet
18 : * database system, memory allocation details, and a basic system
19 : * logging scheme.
20 : */
21 : #include "monetdb_config.h"
22 : #include "monet_options.h"
23 :
24 : #include "gdk.h"
25 : #include "gdk_private.h"
26 : #include "mutils.h"
27 :
28 : static BAT *GDKkey = NULL;
29 : static BAT *GDKval = NULL;
30 : ATOMIC_TYPE GDKdebug = ATOMIC_VAR_INIT(0);
31 :
32 : #include <signal.h>
33 :
34 : #ifdef HAVE_FCNTL_H
35 : #include <fcntl.h>
36 : #endif
37 :
38 : #ifdef HAVE_PWD_H
39 : # include <pwd.h>
40 : #endif
41 :
42 : #ifdef HAVE_SYS_PARAM_H
43 : # include <sys/param.h> /* prerequisite of sys/sysctl on OpenBSD */
44 : #endif
45 : #ifdef BSD /* BSD macro is defined in sys/param.h */
46 : # include <sys/sysctl.h>
47 : #endif
48 : #if defined(HAVE_SYS_RESOURCE_H) && defined(HAVE_GETRLIMIT)
49 : #include <sys/resource.h>
50 : #endif
51 :
52 : #ifdef __CYGWIN__
53 : #include <sysinfoapi.h>
54 : #endif
55 :
56 : static ATOMIC_TYPE GDKstopped = ATOMIC_VAR_INIT(0);
57 : static void GDKunlockHome(int farmid);
58 :
59 : #undef malloc
60 : #undef calloc
61 : #undef realloc
62 : #undef free
63 :
64 : /* when the number of updates to a BAT is less than 1 in this number, we
65 : * keep the unique_est property */
66 : BUN gdk_unique_estimate_keep_fraction = GDK_UNIQUE_ESTIMATE_KEEP_FRACTION; /* should become a define once */
67 : /* if the number of unique values is less than 1 in this number, we
68 : * destroy the hash rather than update it in HASH{append,insert,delete} */
69 : BUN hash_destroy_uniques_fraction = HASH_DESTROY_UNIQUES_FRACTION; /* likewise */
70 : /* if the estimated number of unique values is less than 1 in this
71 : * number, don't build a hash table to do a hashselect */
72 : dbl no_hash_select_fraction = NO_HASH_SELECT_FRACTION; /* same here */
73 : /* if the hash chain is longer than this number, we delete the hash
74 : * rather than maintaining it in HASHdelete */
75 : BUN hash_destroy_chain_length = HASH_DESTROY_CHAIN_LENGTH;
76 :
77 : /*
78 : * @+ Monet configuration file
79 : * Parse a possible MonetDB config file (if specified by command line
80 : * option -c/--config) to extract pre-settings of system variables.
81 : * Un-recognized parameters are simply skipped, because they may be
82 : * picked up by other components of the system. The consequence is
83 : * that making a typing error in the configuration file may be
84 : * unnoticed for a long time. Syntax errors are immediately flagged,
85 : * though.
86 : *
87 : * Since the GDK kernel moves into the database directory, we need to
88 : * keep the absolute path to the MonetDB config file for top-levels to
89 : * access its information.
90 : */
91 :
92 : static bool
93 342 : GDKenvironment(const char *dbpath)
94 : {
95 342 : if (dbpath == NULL) {
96 0 : TRC_CRITICAL(GDK, "Database name missing.\n");
97 0 : return false;
98 : }
99 342 : if (strlen(dbpath) >= FILENAME_MAX) {
100 0 : TRC_CRITICAL(GDK, "Database name too long.\n");
101 0 : return false;
102 : }
103 342 : if (!GDKembedded() && !MT_path_absolute(dbpath)) {
104 0 : TRC_CRITICAL(GDK, "Directory not an absolute path: %s.\n", dbpath);
105 0 : return false;
106 : }
107 : return true;
108 : }
109 :
110 : static struct orig_value {
111 : struct orig_value *next;
112 : char *value;
113 : char key[];
114 : } *orig_value;
115 : static MT_Lock GDKenvlock = MT_LOCK_INITIALIZER(GDKenvlock);
116 :
117 : const char *
118 461693 : GDKgetenv(const char *name)
119 : {
120 461693 : MT_lock_set(&GDKenvlock);
121 461773 : for (struct orig_value *ov = orig_value; ov; ov = ov->next) {
122 0 : if (strcmp(ov->key, name) == 0) {
123 0 : MT_lock_unset(&GDKenvlock);
124 0 : return ov->value;
125 : }
126 : }
127 461773 : MT_lock_unset(&GDKenvlock);
128 461773 : if (GDKkey && GDKval) {
129 461109 : BUN b = BUNfnd(GDKkey, name);
130 :
131 461108 : if (b != BUN_NONE) {
132 324271 : BATiter GDKenvi = bat_iterator(GDKval);
133 324272 : const char *v = BUNtvar(GDKenvi, b);
134 324272 : bat_iterator_end(&GDKenvi);
135 324272 : return v;
136 : }
137 : }
138 : return NULL;
139 : }
140 :
141 : bool
142 280198 : GDKgetenv_istext(const char *name, const char *text)
143 : {
144 280198 : const char *val = GDKgetenv(name);
145 :
146 280204 : return val && strcasecmp(val, text) == 0;
147 : }
148 :
149 : bool
150 38697 : GDKgetenv_isyes(const char *name)
151 : {
152 38697 : return GDKgetenv_istext(name, "yes");
153 : }
154 :
155 : bool
156 241505 : GDKgetenv_istrue(const char *name)
157 : {
158 241505 : return GDKgetenv_istext(name, "true");
159 : }
160 :
161 : int
162 92427 : GDKgetenv_int(const char *name, int def)
163 : {
164 92427 : const char *val = GDKgetenv(name);
165 :
166 92431 : if (val)
167 329 : return atoi(val);
168 : return def;
169 : }
170 :
171 : #define ESCAPE_CHAR '%'
172 :
173 : static bool
174 8328 : isutf8(const char *v, size_t *esclen)
175 : {
176 8328 : size_t n = 1;
177 8328 : int nutf8 = 0;
178 8328 : int m = 0;
179 155848 : for (size_t i = 0; v[i]; i++) {
180 147520 : if (nutf8 > 0) {
181 8 : if ((v[i] & 0xC0) != 0x80 ||
182 0 : (m != 0 && (v[i] & m) == 0))
183 0 : goto badutf8;
184 8 : m = 0;
185 8 : nutf8--;
186 147512 : } else if ((v[i] & 0xE0) == 0xC0) {
187 0 : nutf8 = 1;
188 0 : if ((v[i] & 0x1E) == 0)
189 0 : goto badutf8;
190 147512 : } else if ((v[i] & 0xF0) == 0xE0) {
191 4 : nutf8 = 2;
192 4 : if ((v[i] & 0x0F) == 0)
193 0 : m = 0x20;
194 147508 : } else if ((v[i] & 0xF8) == 0xF0) {
195 0 : nutf8 = 3;
196 0 : if ((v[i] & 0x07) == 0)
197 0 : m = 0x30;
198 147508 : } else if ((v[i] & 0x80) != 0) {
199 0 : goto badutf8;
200 : }
201 : }
202 8328 : *esclen = 0;
203 8328 : return true;
204 0 : badutf8:
205 0 : for (size_t i = 0; v[i]; i++) {
206 0 : if (v[i] & 0x80 || v[i] == ESCAPE_CHAR)
207 0 : n += 3;
208 : else
209 0 : n++;
210 : }
211 0 : *esclen = n;
212 0 : return false;
213 : }
214 :
215 : gdk_return
216 8328 : GDKsetenv(const char *name, const char *value)
217 : {
218 8328 : static const char hexdigits[] = "0123456789abcdef";
219 8328 : char *conval = NULL;
220 8328 : size_t esclen = 0;
221 8328 : if (!isutf8(value, &esclen)) {
222 0 : size_t j = strlen(name) + 1;
223 0 : struct orig_value *ov = GDKmalloc(offsetof(struct orig_value, key) + j + strlen(value) + 1);
224 0 : if (ov == NULL)
225 : return GDK_FAIL;
226 0 : strcpy(ov->key, name);
227 0 : ov->value = ov->key + j;
228 0 : strcpy(ov->value, value);
229 0 : conval = GDKmalloc(esclen);
230 0 : if (conval == NULL) {
231 0 : GDKfree(ov);
232 0 : return GDK_FAIL;
233 : }
234 : j = 0;
235 0 : for (size_t i = 0; value[i]; i++) {
236 0 : if (value[i] & 0x80 || value[i] == ESCAPE_CHAR) {
237 0 : conval[j++] = ESCAPE_CHAR;
238 0 : conval[j++] = hexdigits[(unsigned char) value[i] >> 4];
239 0 : conval[j++] = hexdigits[(unsigned char) value[i] & 0xF];
240 : } else {
241 0 : conval[j++] = value[i];
242 : }
243 : }
244 0 : conval[j] = 0;
245 0 : MT_lock_set(&GDKenvlock);
246 0 : ov->next = orig_value;
247 0 : orig_value = ov;
248 : /* remove previous value if present (later in list) */
249 0 : for (ov = orig_value; ov->next; ov = ov->next) {
250 0 : if (strcmp(ov->next->key, name) == 0) {
251 0 : struct orig_value *ovn = ov->next;
252 0 : ov->next = ovn->next;
253 0 : GDKfree(ovn);
254 : }
255 : }
256 0 : MT_lock_unset(&GDKenvlock);
257 : } else {
258 : /* remove previous value if present */
259 8328 : MT_lock_set(&GDKenvlock);
260 8328 : for (struct orig_value **ovp = &orig_value; *ovp; ovp = &(*ovp)->next) {
261 0 : if (strcmp((*ovp)->key, name) == 0) {
262 0 : struct orig_value *ov = *ovp;
263 0 : *ovp = ov->next;
264 0 : GDKfree(ov);
265 0 : break;
266 : }
267 : }
268 8328 : MT_lock_unset(&GDKenvlock);
269 : }
270 8328 : BUN p = BUNfnd(GDKkey, name);
271 8328 : gdk_return rc;
272 8328 : if (p != BUN_NONE) {
273 1968 : rc = BUNreplace(GDKval, p + GDKval->hseqbase,
274 : conval ? conval : value, false);
275 : } else {
276 7344 : rc = BUNappend(GDKkey, name, false);
277 7344 : if (rc == GDK_SUCCEED) {
278 14688 : rc = BUNappend(GDKval, conval ? conval : value, false);
279 7344 : if (rc != GDK_SUCCEED) {
280 : /* undo earlier successful append to
281 : * keep bats aligned (this can't really
282 : * fail, but we must check the result
283 : * anyway) */
284 0 : if (BUNdelete(GDKkey, GDKkey->hseqbase + GDKkey->batCount - 1) != GDK_SUCCEED)
285 0 : GDKerror("deleting key failed after failed value append");
286 : }
287 : }
288 : }
289 8328 : assert(BATcount(GDKval) == BATcount(GDKkey));
290 8328 : GDKfree(conval);
291 8328 : return rc;
292 : }
293 :
294 : gdk_return
295 33 : GDKcopyenv(BAT **key, BAT **val, bool writable)
296 : {
297 33 : BAT *k, *v;
298 :
299 33 : if (key == NULL || val == NULL) {
300 0 : GDKerror("called incorrectly.\n");
301 0 : return GDK_FAIL;
302 : }
303 33 : k = COLcopy(GDKkey, GDKkey->ttype, writable, TRANSIENT);
304 33 : v = COLcopy(GDKval, GDKval->ttype, writable, TRANSIENT);
305 33 : if (k == NULL || v == NULL) {
306 0 : BBPreclaim(k);
307 0 : BBPreclaim(v);
308 0 : return GDK_FAIL;
309 : }
310 33 : *key = k;
311 33 : *val = v;
312 33 : return GDK_SUCCEED;
313 : }
314 :
315 :
316 : /*
317 : * @+ System logging
318 : * Per database a log file can be maintained for collection of system
319 : * management information. Its contents is driven by the upper layers,
320 : * which encode information such as who logged on and how long the
321 : * session went on. The lower layers merely store error information
322 : * on the file. It should not be used for crash recovery, because
323 : * this should be dealt with on a per client basis.
324 : *
325 : * A system log can be maintained in the database to keep track of
326 : * session and crash information. It should regularly be refreshed to
327 : * avoid disk overflow.
328 : */
329 : #define GDKLOCK ".gdk_lock"
330 :
331 : #define GET_GDKLOCK(x) BBPfarms[BBPselectfarm((x), 0, offheap)].lock_file
332 :
333 : #define GDKLOGOFF "LOGOFF"
334 : #define GDKFOUNDDEAD "FOUND DEAD"
335 : #define GDKLOGON "LOGON"
336 : #define GDKCRASH "CRASH"
337 :
338 : /*
339 : * Single-lined comments can now be logged safely, together with
340 : * process, thread and user ID, and the current time.
341 : */
342 : static void __attribute__((__format__(__printf__, 2, 3)))
343 855 : GDKlog(FILE *lockFile, const char *format, ...)
344 : {
345 855 : va_list ap;
346 855 : char *p = 0, buf[1024];
347 855 : time_t tm = time(0);
348 : #if defined(HAVE_CTIME_R3) || defined(HAVE_CTIME_R)
349 855 : char tbuf[26];
350 : #endif
351 855 : char *ctm;
352 :
353 855 : if (MT_pagesize() == 0 || lockFile == NULL)
354 1 : return;
355 :
356 854 : va_start(ap, format);
357 854 : vsnprintf(buf, sizeof(buf), format, ap);
358 854 : va_end(ap);
359 :
360 : /* remove forbidden characters from message */
361 854 : for (p = buf; (p = strchr(p, '\n')) != NULL; *p = ' ')
362 : ;
363 854 : for (p = buf; (p = strchr(p, '@')) != NULL; *p = ' ')
364 : ;
365 :
366 854 : fseek(lockFile, 0, SEEK_END);
367 : #ifndef HAVE_GETUID
368 : #define getuid() 0
369 : #endif
370 : #ifdef HAVE_CTIME_R3
371 : ctm = ctime_r(&tm, tbuf, sizeof(tbuf));
372 : #else
373 854 : ctm = ctime_r(&tm, tbuf);
374 : #endif
375 854 : fprintf(lockFile, "USR=%d PID=%d TIME=%.24s @ %s\n", (int) getuid(), (int) getpid(), ctm, buf);
376 854 : fflush(lockFile);
377 : }
378 :
379 : /*
380 : * @+ Interrupt handling
381 : * The current version simply catches signals and prints a warning.
382 : * It should be extended to cope with the specifics of the interrupt
383 : * received.
384 : */
385 : #if 0 /* these are unused */
386 : static void
387 : BATSIGignore(int nr)
388 : {
389 : (void) nr;
390 : GDKsyserror("! ERROR signal %d caught by thread %zu\n", nr, (size_t) MT_getpid());
391 : }
392 : #endif
393 :
394 : #ifdef WIN32
395 : static void
396 : BATSIGabort(int nr)
397 : {
398 : (void) nr;
399 : _Exit(3); /* emulate Windows exit code without pop-up */
400 : }
401 : #endif
402 :
403 : #ifndef NATIVE_WIN32
404 : static void
405 343 : BATSIGinit(void)
406 : {
407 : #ifdef HAVE_SIGACTION
408 343 : struct sigaction sa;
409 343 : sigemptyset(&sa.sa_mask);
410 343 : sa.sa_flags = 0;
411 : #ifdef SIGPIPE
412 343 : sa.sa_handler = SIG_IGN;
413 343 : sigaction(SIGPIPE, &sa, NULL);
414 : #endif
415 : #ifdef SIGHUP
416 343 : sa.sa_handler = GDKtracer_reinit_basic;
417 343 : sigaction(SIGHUP, &sa, NULL);
418 : #endif
419 : #ifdef WIN32
420 : sa.sa_handler = BATSIGabort;
421 : sigaction(SIGABRT, &sa, NULL);
422 : #endif
423 : #else
424 : #ifdef SIGPIPE
425 : (void) signal(SIGPIPE, SIG_IGN);
426 : #endif
427 : #ifdef SIGHUP
428 : // Register signal to GDKtracer (logrotate)
429 : (void) signal(SIGHUP, GDKtracer_reinit_basic);
430 : #endif
431 : #ifdef WIN32
432 : (void) signal(SIGABRT, BATSIGabort);
433 : #endif
434 : #endif
435 : #if defined(WIN32) && !defined(__MINGW32__) && !defined(__CYGWIN__)
436 : _set_abort_behavior(0, _CALL_REPORTFAULT | _WRITE_ABORT_MSG);
437 : _set_error_mode(_OUT_TO_STDERR);
438 : #endif
439 343 : }
440 : #endif /* NATIVE_WIN32 */
441 :
442 : /* memory thresholds; these values some "sane" constants only, really
443 : * set in GDKinit() */
444 : #define MMAP_MINSIZE_PERSISTENT ((size_t) 1 << 18)
445 : #if SIZEOF_SIZE_T == 4
446 : #define MMAP_MINSIZE_TRANSIENT ((size_t) 1 << 20)
447 : #else
448 : #define MMAP_MINSIZE_TRANSIENT ((size_t) 1 << 32)
449 : #endif
450 : #define MMAP_PAGESIZE ((size_t) 1 << 16)
451 : size_t GDK_mmap_minsize_persistent = MMAP_MINSIZE_PERSISTENT;
452 : size_t GDK_mmap_minsize_transient = MMAP_MINSIZE_TRANSIENT;
453 : size_t GDK_mmap_pagesize = MMAP_PAGESIZE; /* mmap granularity */
454 : size_t GDK_mem_maxsize = GDK_VM_MAXSIZE;
455 : size_t GDK_vm_maxsize = GDK_VM_MAXSIZE;
456 :
457 : #define SEG_SIZE(x) ((ssize_t) (((x) + _MT_pagesize - 1) & ~(_MT_pagesize - 1)))
458 :
459 : /* This block is to provide atomic addition and subtraction to select
460 : * variables. We use intrinsic functions (recognized and inlined by
461 : * the compiler) for both the GNU C compiler and Microsoft Visual
462 : * Studio. By doing this, we avoid locking overhead. There is also a
463 : * fall-back for other compilers. */
464 : #include "matomic.h"
465 : static ATOMIC_TYPE GDK_mallocedbytes_estimate = ATOMIC_VAR_INIT(0);
466 : #ifndef NDEBUG
467 : static volatile lng GDK_malloc_success_count = -1;
468 : #endif
469 : static ATOMIC_TYPE GDK_vm_cursize = ATOMIC_VAR_INIT(0);
470 :
471 : size_t _MT_pagesize = 0; /* variable holding page size */
472 : size_t _MT_npages = 0; /* variable holding memory size in pages */
473 :
474 : static lng programepoch;
475 :
476 : void
477 344 : MT_init(void)
478 : {
479 344 : programepoch = GDKusec();
480 : #ifdef _MSC_VER
481 : {
482 : SYSTEM_INFO sysInfo;
483 :
484 : GetSystemInfo(&sysInfo);
485 : _MT_pagesize = sysInfo.dwPageSize;
486 : }
487 : #elif defined(BSD) && defined(HW_PAGESIZE)
488 : {
489 : int size;
490 : size_t len = sizeof(int);
491 : int mib[2];
492 :
493 : /* Everyone should have permission to make this call,
494 : * if we get a failure something is really wrong. */
495 : mib[0] = CTL_HW;
496 : mib[1] = HW_PAGESIZE;
497 : sysctl(mib, 2, &size, &len, NULL, 0);
498 : _MT_pagesize = size;
499 : }
500 : #elif defined(HAVE_SYSCONF) && defined(_SC_PAGESIZE)
501 344 : _MT_pagesize = (size_t)sysconf(_SC_PAGESIZE);
502 : #endif
503 344 : if (_MT_pagesize <= 0)
504 0 : _MT_pagesize = 4096; /* default */
505 :
506 : #ifdef WIN32
507 : {
508 : MEMORYSTATUSEX memStatEx;
509 :
510 : memStatEx.dwLength = sizeof(memStatEx);
511 : if (GlobalMemoryStatusEx(&memStatEx))
512 : _MT_npages = (size_t) (memStatEx.ullTotalPhys / _MT_pagesize);
513 : }
514 : #elif defined(BSD) && defined(HW_MEMSIZE) && SIZEOF_SIZE_T == SIZEOF_LNG
515 : /* Darwin, 64-bits */
516 : {
517 : uint64_t size = 0;
518 : size_t len = sizeof(size);
519 : int mib[2];
520 :
521 : /* Everyone should have permission to make this call,
522 : * if we get a failure something is really wrong. */
523 : mib[0] = CTL_HW;
524 : mib[1] = HW_MEMSIZE;
525 : sysctl(mib, 2, &size, &len, NULL, 0);
526 : _MT_npages = size / _MT_pagesize;
527 : }
528 : #elif defined(BSD) && defined (HW_PHYSMEM64) && SIZEOF_SIZE_T == SIZEOF_LNG
529 : /* OpenBSD, 64-bits */
530 : {
531 : int64_t size = 0;
532 : size_t len = sizeof(size);
533 : int mib[2];
534 :
535 : /* Everyone should have permission to make this call,
536 : * if we get a failure something is really wrong. */
537 : mib[0] = CTL_HW;
538 : mib[1] = HW_PHYSMEM64;
539 : sysctl(mib, 2, &size, &len, NULL, 0);
540 : _MT_npages = size / _MT_pagesize;
541 : }
542 : #elif defined(BSD) && defined(HW_PHYSMEM)
543 : /* NetBSD, OpenBSD, Darwin, 32-bits; FreeBSD 32 & 64-bits */
544 : {
545 : # ifdef __FreeBSD__
546 : unsigned long size = 0; /* type long required by sysctl() (?) */
547 : # else
548 : int size = 0;
549 : # endif
550 : size_t len = sizeof(size);
551 : int mib[2];
552 :
553 : /* Everyone should have permission to make this call,
554 : * if we get a failure something is really wrong. */
555 : mib[0] = CTL_HW;
556 : mib[1] = HW_PHYSMEM;
557 : sysctl(mib, 2, &size, &len, NULL, 0);
558 : _MT_npages = size / _MT_pagesize;
559 : }
560 : #elif defined(HAVE_SYSCONF) && defined(_SC_PHYS_PAGES)
561 344 : _MT_npages = (size_t)sysconf(_SC_PHYS_PAGES);
562 : # if SIZEOF_SIZE_T == SIZEOF_INT
563 : /* Bug #2935: the value returned here can be more than what can be
564 : * addressed on Solaris, so cap the value */
565 : if (UINT_MAX / _MT_pagesize < _MT_npages)
566 : _MT_npages = UINT_MAX / _MT_pagesize;
567 : # endif
568 : #else
569 : # error "don't know how to get the amount of physical memory for your OS"
570 : #endif
571 :
572 : #ifdef __linux__
573 : /* limit values to whatever cgroups gives us */
574 344 : FILE *fc;
575 344 : char buf[1024];
576 344 : char cgr1[1024] = "/sys/fs/cgroup/memory";
577 344 : char cgr2[1024] = "/sys/fs/cgroup";
578 344 : fc = fopen("/proc/self/mountinfo", "r");
579 344 : if (fc != NULL) {
580 11008 : while (fgets(buf, (int) sizeof(buf), fc) != NULL) {
581 10664 : char *p, *cgr;
582 10664 : if ((p = strstr(buf, " - cgroup ")) != NULL &&
583 0 : strstr(p, "memory") != NULL)
584 : cgr = cgr1;
585 10664 : else if (strstr(buf, " - cgroup2 ") != NULL)
586 : cgr = cgr2;
587 : else
588 10320 : continue;
589 : /* buf points at mount ID */
590 344 : p = strchr(buf, ' ');
591 344 : if (p == NULL)
592 : break;
593 344 : p++;
594 : /* p points at parent ID */
595 344 : p = strchr(p, ' ');
596 344 : if (p == NULL)
597 : break;
598 344 : p++;
599 : /* p points at major:minor */
600 344 : p = strchr(p, ' ');
601 344 : if (p == NULL)
602 : break;
603 344 : p++;
604 : /* p points at root */
605 344 : p = strchr(p, ' ');
606 344 : if (p == NULL)
607 : break;
608 344 : p++;
609 : /* p points at mount point */
610 344 : char *dir = p;
611 344 : p = strchr(p, ' ');
612 344 : if (p == NULL)
613 : break;
614 344 : *p = 0;
615 344 : strcpy_len(cgr, dir, 1024);
616 : }
617 344 : fclose(fc);
618 : }
619 344 : fc = fopen("/proc/self/cgroup", "r");
620 344 : if (fc != NULL) {
621 : /* each line is of the form:
622 : * hierarchy-ID:controller-list:cgroup-path
623 : *
624 : * For cgroup v1, the hierarchy-ID refers to the
625 : * second column in /proc/cgroups (which we ignore)
626 : * and the controller-list is a comma-separated list
627 : * of the controllers bound to the hierarchy. We look
628 : * for the "memory" controller and use its
629 : * cgroup-path. We ignore the other lines.
630 : *
631 : * For cgroup v2, the hierarchy-ID is 0 and the
632 : * controller-list is empty. We just use the
633 : * cgroup-path.
634 : *
635 : * We use the first line that we can match (either v1
636 : * or v2) and for which we can open any of the files
637 : * that we are looking for.
638 : */
639 344 : while (fgets(buf, (int) sizeof(buf), fc) != NULL) {
640 344 : char pth[1024];
641 344 : char *p, *q;
642 344 : bool success = false; /* true if we can open any file */
643 344 : FILE *f;
644 344 : uint64_t mem;
645 :
646 344 : p = strchr(buf, '\n');
647 344 : if (p == NULL)
648 : break;
649 344 : *p = 0;
650 344 : if (strncmp(buf, "0::", 3) == 0) {
651 : /* cgroup v2 entry */
652 344 : p = stpcpy(pth, cgr2);
653 344 : q = stpcpy(stpcpy(p, buf + 3), "/");
654 : /* hard limit */
655 344 : strcpy(q, "memory.max");
656 344 : f = fopen(pth, "r");
657 344 : while (f == NULL && q > p) {
658 : /* go up the hierarchy until we
659 : * find the file or the
660 : * hierarchy runs out */
661 0 : *--q = 0; /* zap the slash */
662 0 : q = strrchr(p, '/');
663 0 : if (q == NULL || q == p) {
664 : /* position after the slash */
665 0 : q = p + 1;
666 0 : break;
667 : }
668 0 : strcpy(++q, "memory.max");
669 0 : f = fopen(pth, "r");
670 : }
671 344 : if (f != NULL) {
672 344 : if (fscanf(f, "%" SCNu64, &mem) == 1
673 0 : && mem > 0
674 0 : && mem < (uint64_t) _MT_pagesize * _MT_npages) {
675 0 : _MT_npages = (size_t) (mem / _MT_pagesize);
676 : }
677 344 : success = true;
678 : /* assume "max" if not a number */
679 344 : fclose(f);
680 : }
681 : /* soft high limit */
682 344 : strcpy(q, "memory.high");
683 344 : f = fopen(pth, "r");
684 344 : if (f != NULL) {
685 344 : if (fscanf(f, "%" SCNu64, &mem) == 1
686 0 : && mem > 0
687 0 : && mem < (uint64_t) _MT_pagesize * _MT_npages) {
688 0 : _MT_npages = (size_t) (mem / _MT_pagesize);
689 : }
690 344 : success = true;
691 : /* assume "max" if not a number */
692 344 : fclose(f);
693 : }
694 : /* limit of swap usage, hard limit
695 : * we use this, together with
696 : * memory.high, as maximum virtual
697 : * memory size */
698 344 : strcpy(q, "memory.swap.max");
699 344 : f = fopen(pth, "r");
700 344 : if (f != NULL) {
701 344 : if (fscanf(f, "%" SCNu64, &mem) == 1
702 0 : && mem > 0
703 0 : && (mem += _MT_npages * _MT_pagesize) < (uint64_t) GDK_vm_maxsize) {
704 0 : GDK_vm_maxsize = (size_t) mem;
705 : }
706 344 : success = true;
707 344 : fclose(f);
708 : }
709 : #if 0 /* not sure about using this one */
710 : /* limit of swap usage, soft limit */
711 : strcpy(q, "memory.swap.high");
712 : f = fopen(pth, "r");
713 : if (f != NULL) {
714 : if (fscanf(f, "%" SCNu64, &mem) == 1
715 : && mem > 0
716 : && (mem += _MT_npages * _MT_pagesize) < (uint64_t) GDK_vm_maxsize) {
717 : GDK_vm_maxsize = (size_t) mem;
718 : }
719 : success = true;
720 : fclose(f);
721 : }
722 : #endif
723 : } else {
724 : /* cgroup v1 entry */
725 0 : p = strchr(buf, ':');
726 0 : if (p == NULL)
727 : break;
728 0 : q = p + 1;
729 0 : p = strchr(q, ':');
730 0 : if (p == NULL)
731 : break;
732 0 : *p++ = 0;
733 0 : if (strstr(q, "memory") == NULL)
734 0 : continue;
735 : /* limit of memory usage */
736 0 : strconcat_len(pth, sizeof(pth),
737 : cgr1, p,
738 : "/memory.limit_in_bytes",
739 : NULL);
740 0 : f = fopen(pth, "r");
741 0 : if (f == NULL) {
742 0 : strconcat_len(pth, sizeof(pth),
743 : cgr1,
744 : "/memory.limit_in_bytes",
745 : NULL);
746 0 : f = fopen(pth, "r");
747 : }
748 0 : if (f != NULL) {
749 0 : if (fscanf(f, "%" SCNu64, &mem) == 1
750 0 : && mem > 0
751 0 : && mem < (uint64_t) _MT_pagesize * _MT_npages) {
752 0 : _MT_npages = (size_t) (mem / _MT_pagesize);
753 : }
754 0 : success = true;
755 0 : fclose(f);
756 : }
757 : /* soft limit of memory usage */
758 0 : strconcat_len(pth, sizeof(pth),
759 : cgr1, p,
760 : "/memory.soft_limit_in_bytes",
761 : NULL);
762 0 : f = fopen(pth, "r");
763 0 : if (f == NULL) {
764 0 : strconcat_len(pth, sizeof(pth),
765 : cgr1,
766 : "/memory.soft_limit_in_bytes",
767 : NULL);
768 0 : f = fopen(pth, "r");
769 : }
770 0 : if (f != NULL) {
771 0 : if (fscanf(f, "%" SCNu64, &mem) == 1
772 0 : && mem > 0
773 0 : && mem < (uint64_t) _MT_pagesize * _MT_npages) {
774 0 : _MT_npages = (size_t) (mem / _MT_pagesize);
775 : }
776 0 : success = true;
777 0 : fclose(f);
778 : }
779 : /* limit of memory+swap usage
780 : * we use this as maximum virtual memory size */
781 0 : strconcat_len(pth, sizeof(pth),
782 : cgr1, p,
783 : "/memory.memsw.limit_in_bytes",
784 : NULL);
785 0 : f = fopen(pth, "r");
786 0 : if (f == NULL) {
787 0 : strconcat_len(pth, sizeof(pth),
788 : cgr1,
789 : "/memory.memsw.limit_in_bytes",
790 : NULL);
791 0 : f = fopen(pth, "r");
792 : }
793 0 : if (f != NULL) {
794 0 : if (fscanf(f, "%" SCNu64, &mem) == 1
795 0 : && mem > 0
796 0 : && mem < (uint64_t) GDK_vm_maxsize) {
797 0 : GDK_vm_maxsize = (size_t) mem;
798 : }
799 0 : success = true;
800 0 : fclose(f);
801 : }
802 : }
803 344 : if (success)
804 : break;
805 : }
806 344 : fclose(fc);
807 : }
808 : #endif
809 :
810 : #if defined(HAVE_SYS_RESOURCE_H) && defined(HAVE_GETRLIMIT) && defined(RLIMIT_AS)
811 344 : struct rlimit l;
812 : /* address space (virtual memory) limit */
813 344 : if (getrlimit(RLIMIT_AS, &l) == 0
814 344 : && l.rlim_cur != (rlim_t)RLIM_INFINITY
815 0 : && (size_t)l.rlim_cur < GDK_vm_maxsize) {
816 0 : GDK_vm_maxsize = l.rlim_cur;
817 : }
818 : #endif
819 344 : }
820 :
821 : /*
822 : * @+ Session Initialization
823 : * The interface code to the operating system is highly dependent on
824 : * the processing environment. It can be filtered away with
825 : * compile-time flags. Suicide is necessary due to some system
826 : * implementation errors.
827 : *
828 : * The kernel requires file descriptors for I/O with the user. They
829 : * are thread specific and should be obtained by a function.
830 : *
831 : * The arguments relevant for the kernel are extracted from the list.
832 : * Their value is turned into a blanc space.
833 : */
834 :
835 : #define CATNAP 50 /* time to sleep in ms for catnaps */
836 :
837 : static int THRinit(void);
838 : static gdk_return GDKlockHome(int farmid);
839 :
840 : #ifndef __COVERITY__
841 : #ifndef NDEBUG
842 : static MT_Lock mallocsuccesslock = MT_LOCK_INITIALIZER(mallocsuccesslock);
843 : #endif
844 : #endif
845 :
846 : void
847 350 : GDKsetdebug(unsigned debug)
848 : {
849 350 : ATOMIC_SET(&GDKdebug, debug);
850 350 : if (debug & ACCELMASK)
851 0 : GDKtracer_set_component_level("accelerator", "debug");
852 : else
853 350 : GDKtracer_reset_component_level("accelerator");
854 350 : if (debug & ALGOMASK)
855 0 : GDKtracer_set_component_level("algo", "debug");
856 : else
857 350 : GDKtracer_reset_component_level("algo");
858 350 : if (debug & ALLOCMASK)
859 0 : GDKtracer_set_component_level("alloc", "debug");
860 : else
861 350 : GDKtracer_reset_component_level("alloc");
862 350 : if (debug & BATMASK)
863 0 : GDKtracer_set_component_level("bat", "debug");
864 : else
865 350 : GDKtracer_reset_component_level("bat");
866 350 : if (debug & CHECKMASK)
867 349 : GDKtracer_set_component_level("check", "debug");
868 : else
869 1 : GDKtracer_reset_component_level("check");
870 350 : if (debug & DELTAMASK)
871 0 : GDKtracer_set_component_level("delta", "debug");
872 : else
873 350 : GDKtracer_reset_component_level("delta");
874 350 : if (debug & HEAPMASK)
875 0 : GDKtracer_set_component_level("heap", "debug");
876 : else
877 350 : GDKtracer_reset_component_level("heap");
878 350 : if (debug & IOMASK)
879 0 : GDKtracer_set_component_level("io", "debug");
880 : else
881 350 : GDKtracer_reset_component_level("io");
882 350 : if (debug & PARMASK)
883 0 : GDKtracer_set_component_level("par", "debug");
884 : else
885 350 : GDKtracer_reset_component_level("par");
886 350 : if (debug & PERFMASK)
887 0 : GDKtracer_set_component_level("perf", "debug");
888 : else
889 350 : GDKtracer_reset_component_level("perf");
890 350 : if (debug & TEMMASK)
891 0 : GDKtracer_set_component_level("tem", "debug");
892 : else
893 350 : GDKtracer_reset_component_level("tem");
894 350 : if (debug & THRDMASK)
895 0 : GDKtracer_set_component_level("thrd", "debug");
896 : else
897 350 : GDKtracer_reset_component_level("thrd");
898 350 : }
899 :
900 : unsigned
901 19 : GDKgetdebug(void)
902 : {
903 19 : ATOMIC_BASE_TYPE debug = ATOMIC_GET(&GDKdebug);
904 19 : const char *lvl;
905 19 : lvl = GDKtracer_get_component_level("accelerator");
906 19 : if (lvl && strcmp(lvl, "debug") == 0)
907 0 : debug |= ACCELMASK;
908 19 : lvl = GDKtracer_get_component_level("algo");
909 19 : if (lvl && strcmp(lvl, "debug") == 0)
910 0 : debug |= ALGOMASK;
911 19 : lvl = GDKtracer_get_component_level("alloc");
912 19 : if (lvl && strcmp(lvl, "debug") == 0)
913 0 : debug |= ALLOCMASK;
914 19 : lvl = GDKtracer_get_component_level("bat");
915 19 : if (lvl && strcmp(lvl, "debug") == 0)
916 0 : debug |= BATMASK;
917 19 : lvl = GDKtracer_get_component_level("check");
918 19 : if (lvl && strcmp(lvl, "debug") == 0)
919 0 : debug |= CHECKMASK;
920 19 : lvl = GDKtracer_get_component_level("delta");
921 19 : if (lvl && strcmp(lvl, "debug") == 0)
922 0 : debug |= DELTAMASK;
923 19 : lvl = GDKtracer_get_component_level("heap");
924 19 : if (lvl && strcmp(lvl, "debug") == 0)
925 0 : debug |= HEAPMASK;
926 19 : lvl = GDKtracer_get_component_level("io");
927 19 : if (lvl && strcmp(lvl, "debug") == 0)
928 0 : debug |= IOMASK;
929 19 : lvl = GDKtracer_get_component_level("par");
930 19 : if (lvl && strcmp(lvl, "debug") == 0)
931 0 : debug |= PARMASK;
932 19 : lvl = GDKtracer_get_component_level("perf");
933 19 : if (lvl && strcmp(lvl, "debug") == 0)
934 0 : debug |= PERFMASK;
935 19 : lvl = GDKtracer_get_component_level("tem");
936 19 : if (lvl && strcmp(lvl, "debug") == 0)
937 0 : debug |= TEMMASK;
938 19 : lvl = GDKtracer_get_component_level("thrd");
939 19 : if (lvl && strcmp(lvl, "debug") == 0)
940 0 : debug |= THRDMASK;
941 19 : return (unsigned) debug;
942 : }
943 :
944 : static bool Mbedded = true;
945 : bool
946 19540203 : GDKembedded(void)
947 : {
948 19540203 : return Mbedded;
949 : }
950 :
951 : static MT_Id mainpid;
952 :
953 : gdk_return
954 343 : GDKinit(opt *set, int setlen, bool embedded, const char *caller_revision)
955 : {
956 343 : static bool first = true;
957 343 : const char *dbpath;
958 343 : const char *dbtrace;
959 343 : const char *p;
960 343 : opt *n;
961 343 : int i, nlen = 0;
962 343 : char buf[16];
963 :
964 343 : if (caller_revision) {
965 332 : p = mercurial_revision();
966 332 : if (p && strcmp(p, caller_revision) != 0) {
967 0 : GDKerror("incompatible versions: caller is %s, GDK is %s\n", caller_revision, p);
968 0 : return GDK_FAIL;
969 : }
970 : }
971 :
972 343 : ATOMIC_SET(&GDKstopped, 0);
973 :
974 343 : if (BBPchkfarms() != GDK_SUCCEED)
975 : return GDK_FAIL;
976 :
977 343 : if (GDKinmemory(0)) {
978 : dbpath = dbtrace = NULL;
979 : } else {
980 342 : dbpath = mo_find_option(set, setlen, "gdk_dbpath");
981 342 : dbtrace = mo_find_option(set, setlen, "gdk_dbtrace");
982 : }
983 343 : Mbedded = embedded;
984 : /* some sanity checks (should also find if symbols are not defined) */
985 343 : static_assert(sizeof(int) == sizeof(int32_t),
986 : "int is not equal in size to int32_t");
987 343 : static_assert(sizeof(char) == SIZEOF_CHAR,
988 : "error in configure: bad value for SIZEOF_CHAR");
989 343 : static_assert(sizeof(short) == SIZEOF_SHORT,
990 : "error in configure: bad value for SIZEOF_SHORT");
991 343 : static_assert(sizeof(int) == SIZEOF_INT,
992 : "error in configure: bad value for SIZEOF_INT");
993 343 : static_assert(sizeof(long) == SIZEOF_LONG,
994 : "error in configure: bad value for SIZEOF_LONG");
995 343 : static_assert(sizeof(lng) == SIZEOF_LNG,
996 : "error in configure: bad value for SIZEOF_LNG");
997 : #ifdef HAVE_HGE
998 343 : static_assert(sizeof(hge) == SIZEOF_HGE,
999 : "error in configure: bad value for SIZEOF_HGE");
1000 : #endif
1001 343 : static_assert(sizeof(dbl) == SIZEOF_DOUBLE,
1002 : "error in configure: bad value for SIZEOF_DOUBLE");
1003 343 : static_assert(sizeof(oid) == SIZEOF_OID,
1004 : "error in configure: bad value for SIZEOF_OID");
1005 343 : static_assert(sizeof(void *) == SIZEOF_VOID_P,
1006 : "error in configure: bad value for SIZEOF_VOID_P");
1007 343 : static_assert(sizeof(size_t) == SIZEOF_SIZE_T,
1008 : "error in configure: bad value for SIZEOF_SIZE_T");
1009 343 : static_assert(SIZEOF_OID == SIZEOF_INT || SIZEOF_OID == SIZEOF_LNG,
1010 : "SIZEOF_OID should be equal to SIZEOF_INT or SIZEOF_LNG");
1011 343 : static_assert(sizeof(uuid) == 16,
1012 : "sizeof(uuid) should be equal to 16");
1013 :
1014 343 : if (first) {
1015 : /* some things are really only initialized once */
1016 333 : if (!MT_thread_init()) {
1017 0 : TRC_CRITICAL(GDK, "MT_thread_init failed\n");
1018 0 : return GDK_FAIL;
1019 : }
1020 :
1021 2728269 : for (i = 0; i <= BBP_BATMASK; i++) {
1022 2727936 : char name[MT_NAME_LEN];
1023 2727936 : snprintf(name, sizeof(name), "GDKswapLock%d", i);
1024 2727936 : MT_lock_init(&GDKbatLock[i].swap, name);
1025 : }
1026 333 : if (mnstr_init() < 0) {
1027 0 : TRC_CRITICAL(GDK, "mnstr_init failed\n");
1028 0 : return GDK_FAIL;
1029 : }
1030 : } else {
1031 : /* BBP was locked by BBPexit() */
1032 : //BBPunlock();
1033 343 : }
1034 343 : mainpid = MT_getpid();
1035 :
1036 343 : GDKtracer_init(dbpath, dbtrace);
1037 343 : errno = 0;
1038 343 : if (!GDKinmemory(0) && !GDKenvironment(dbpath))
1039 : return GDK_FAIL;
1040 :
1041 343 : MT_init_posix();
1042 343 : if (THRinit() < 0)
1043 : return GDK_FAIL;
1044 : #ifndef NATIVE_WIN32
1045 343 : BATSIGinit();
1046 : #endif
1047 343 : MT_init();
1048 :
1049 : /* now try to lock the database: go through all farms, and if
1050 : * we see a new directory, lock it */
1051 11255 : for (int farmid = 0; farmid < MAXFARMS; farmid++) {
1052 10914 : if (BBPfarms[farmid].dirname != NULL) {
1053 1363 : bool skip = false;
1054 1363 : for (int j = 0; j < farmid; j++) {
1055 845 : if (BBPfarms[j].dirname != NULL &&
1056 845 : strcmp(BBPfarms[farmid].dirname, BBPfarms[j].dirname) == 0) {
1057 : skip = true;
1058 : break;
1059 : }
1060 : }
1061 1011 : if (!skip && GDKlockHome(farmid) != GDK_SUCCEED)
1062 : return GDK_FAIL;
1063 : }
1064 : }
1065 :
1066 : /* Mserver by default takes 80% of all memory as a default */
1067 : #if SIZEOF_SIZE_T == 4
1068 : if ((double) MT_npages() * (double) MT_pagesize() * 0.815 >= (double) GDK_VM_MAXSIZE)
1069 : GDK_mem_maxsize = GDK_VM_MAXSIZE;
1070 : else
1071 : #endif
1072 341 : GDK_mem_maxsize = (size_t) ((double) MT_npages() * (double) MT_pagesize() * 0.815);
1073 341 : const char *allow = mo_find_option(set, setlen, "allow_hge_upgrade");
1074 674 : if (BBPinit(allow && strcmp(allow, "yes") == 0) != GDK_SUCCEED)
1075 : return GDK_FAIL;
1076 341 : first = false;
1077 :
1078 341 : if (GDK_mem_maxsize / 16 < GDK_mmap_minsize_transient) {
1079 331 : GDK_mmap_minsize_transient = MAX(MMAP_PAGESIZE, GDK_mem_maxsize / 16);
1080 331 : if (GDK_mmap_minsize_persistent > GDK_mmap_minsize_transient)
1081 0 : GDK_mmap_minsize_persistent = GDK_mmap_minsize_transient;
1082 : }
1083 :
1084 341 : n = (opt *) malloc(setlen * sizeof(opt));
1085 341 : if (n == NULL) {
1086 0 : GDKsyserror("malloc failed\n");
1087 0 : return GDK_FAIL;
1088 : }
1089 :
1090 4109 : for (i = 0; i < setlen; i++) {
1091 16899 : bool done = false;
1092 :
1093 16899 : for (int j = 0; j < nlen; j++) {
1094 13803 : if (strcmp(n[j].name, set[i].name) == 0) {
1095 672 : if (n[j].kind < set[i].kind) {
1096 672 : n[j] = set[i];
1097 : }
1098 : done = true;
1099 : break;
1100 : }
1101 : }
1102 3096 : if (!done) {
1103 3096 : n[nlen] = set[i];
1104 3096 : nlen++;
1105 : }
1106 : }
1107 : /* check some options before creating our first BAT */
1108 3437 : for (i = 0; i < nlen; i++) {
1109 3096 : if (strcmp("gdk_mem_maxsize", n[i].name) == 0) {
1110 0 : GDK_mem_maxsize = (size_t) strtoll(n[i].value, NULL, 10);
1111 0 : GDK_mem_maxsize = MAX(1 << 26, GDK_mem_maxsize);
1112 0 : if (GDK_mem_maxsize / 16 < GDK_mmap_minsize_transient)
1113 0 : GDK_mmap_minsize_transient = MAX(MMAP_PAGESIZE, GDK_mem_maxsize / 16);
1114 0 : if (GDK_mmap_minsize_persistent > GDK_mmap_minsize_transient)
1115 0 : GDK_mmap_minsize_persistent = GDK_mmap_minsize_transient;
1116 3096 : } else if (strcmp("gdk_vm_maxsize", n[i].name) == 0) {
1117 182 : GDK_vm_maxsize = (size_t) strtoll(n[i].value, NULL, 10);
1118 182 : GDK_vm_maxsize = MAX(1 << 30, GDK_vm_maxsize);
1119 2914 : } else if (strcmp("gdk_mmap_minsize_persistent", n[i].name) == 0) {
1120 0 : GDK_mmap_minsize_persistent = (size_t) strtoll(n[i].value, NULL, 10);
1121 2914 : } else if (strcmp("gdk_mmap_minsize_transient", n[i].name) == 0) {
1122 0 : GDK_mmap_minsize_transient = (size_t) strtoll(n[i].value, NULL, 10);
1123 2914 : } else if (strcmp("gdk_mmap_pagesize", n[i].name) == 0) {
1124 0 : GDK_mmap_pagesize = (size_t) strtoll(n[i].value, NULL, 10);
1125 0 : if (GDK_mmap_pagesize < 1 << 12 ||
1126 0 : GDK_mmap_pagesize > 1 << 20 ||
1127 : /* x & (x - 1): turn off rightmost 1 bit;
1128 : * i.e. if result is zero, x is power of
1129 : * two */
1130 0 : (GDK_mmap_pagesize & (GDK_mmap_pagesize - 1)) != 0) {
1131 0 : free(n);
1132 0 : TRC_CRITICAL(GDK, "gdk_mmap_pagesize must be power of 2 between 2**12 and 2**20\n");
1133 0 : return GDK_FAIL;
1134 : }
1135 : }
1136 : }
1137 :
1138 341 : GDKkey = COLnew(0, TYPE_str, 100, TRANSIENT);
1139 341 : GDKval = COLnew(0, TYPE_str, 100, TRANSIENT);
1140 341 : if (GDKkey == NULL || GDKval == NULL) {
1141 0 : free(n);
1142 0 : TRC_CRITICAL(GDK, "Could not create environment BATs");
1143 0 : return GDK_FAIL;
1144 : }
1145 682 : if (BBPrename(GDKkey, "environment_key") != 0 ||
1146 341 : BBPrename(GDKval, "environment_val") != 0) {
1147 0 : free(n);
1148 0 : TRC_CRITICAL(GDK, "BBPrename of environment BATs failed");
1149 0 : return GDK_FAIL;
1150 : }
1151 341 : BBP_pid(GDKkey->batCacheid) = 0;
1152 341 : BBP_pid(GDKval->batCacheid) = 0;
1153 :
1154 : /* store options into environment BATs */
1155 3437 : for (i = 0; i < nlen; i++)
1156 3096 : if (GDKsetenv(n[i].name, n[i].value) != GDK_SUCCEED) {
1157 0 : TRC_CRITICAL(GDK, "GDKsetenv %s failed", n[i].name);
1158 0 : free(n);
1159 0 : return GDK_FAIL;
1160 : }
1161 341 : free(n);
1162 :
1163 341 : GDKnr_threads = GDKgetenv_int("gdk_nr_threads", 0);
1164 341 : if (GDKnr_threads == 0) {
1165 340 : GDKnr_threads = MT_check_nr_cores();
1166 340 : snprintf(buf, sizeof(buf), "%d", GDKnr_threads);
1167 340 : if (GDKsetenv("gdk_nr_threads", buf) != GDK_SUCCEED) {
1168 0 : TRC_CRITICAL(GDK, "GDKsetenv gdk_nr_threads failed");
1169 0 : return GDK_FAIL;
1170 : }
1171 : }
1172 341 : if (GDKnr_threads > THREADS)
1173 0 : GDKnr_threads = THREADS;
1174 :
1175 341 : if (!GDKinmemory(0)) {
1176 340 : if ((p = GDKgetenv("gdk_dbpath")) != NULL &&
1177 340 : (p = strrchr(p, DIR_SEP)) != NULL) {
1178 340 : if (GDKsetenv("gdk_dbname", p + 1) != GDK_SUCCEED) {
1179 0 : TRC_CRITICAL(GDK, "GDKsetenv gdk_dbname failed");
1180 0 : return GDK_FAIL;
1181 : }
1182 : #if DIR_SEP != '/' /* on Windows look for different separator */
1183 : } else if ((p = GDKgetenv("gdk_dbpath")) != NULL &&
1184 : (p = strrchr(p, '/')) != NULL) {
1185 : if (GDKsetenv("gdk_dbname", p + 1) != GDK_SUCCEED) {
1186 : TRC_CRITICAL(GDK, "GDKsetenv gdk_dbname failed");
1187 : return GDK_FAIL;
1188 : }
1189 : #endif
1190 : }
1191 1 : } else if (GDKgetenv("gdk_dbname") == NULL) {
1192 1 : if (GDKsetenv("gdk_dbname", "in-memory") != GDK_SUCCEED) {
1193 0 : TRC_CRITICAL(GDK, "GDKsetenv gdk_dbname failed");
1194 0 : return GDK_FAIL;
1195 : }
1196 : }
1197 341 : if (GDKgetenv("gdk_vm_maxsize") == NULL) {
1198 159 : snprintf(buf, sizeof(buf), "%zu", GDK_vm_maxsize);
1199 159 : if (GDKsetenv("gdk_vm_maxsize", buf) != GDK_SUCCEED) {
1200 0 : TRC_CRITICAL(GDK, "GDKsetenv gdk_vm_maxsize failed");
1201 0 : return GDK_FAIL;
1202 : }
1203 : }
1204 341 : if (GDKgetenv("gdk_mem_maxsize") == NULL) {
1205 341 : snprintf(buf, sizeof(buf), "%zu", GDK_mem_maxsize);
1206 341 : if (GDKsetenv("gdk_mem_maxsize", buf) != GDK_SUCCEED) {
1207 0 : TRC_CRITICAL(GDK, "GDKsetenv gdk_mem_maxsize failed");
1208 0 : return GDK_FAIL;
1209 : }
1210 : }
1211 341 : if (GDKgetenv("gdk_mmap_minsize_persistent") == NULL) {
1212 341 : snprintf(buf, sizeof(buf), "%zu", GDK_mmap_minsize_persistent);
1213 341 : if (GDKsetenv("gdk_mmap_minsize_persistent", buf) != GDK_SUCCEED) {
1214 0 : TRC_CRITICAL(GDK, "GDKsetenv gdk_mmap_minsize_persistent failed");
1215 0 : return GDK_FAIL;
1216 : }
1217 : }
1218 341 : if (GDKgetenv("gdk_mmap_minsize_transient") == NULL) {
1219 341 : snprintf(buf, sizeof(buf), "%zu", GDK_mmap_minsize_transient);
1220 341 : if (GDKsetenv("gdk_mmap_minsize_transient", buf) != GDK_SUCCEED) {
1221 0 : TRC_CRITICAL(GDK, "GDKsetenv gdk_mmap_minsize_transient failed");
1222 0 : return GDK_FAIL;
1223 : }
1224 : }
1225 341 : if (GDKgetenv("gdk_mmap_pagesize") == NULL) {
1226 341 : snprintf(buf, sizeof(buf), "%zu", GDK_mmap_pagesize);
1227 341 : if (GDKsetenv("gdk_mmap_pagesize", buf) != GDK_SUCCEED) {
1228 0 : TRC_CRITICAL(GDK, "GDKsetenv gdk_mmap_pagesize failed");
1229 0 : return GDK_FAIL;
1230 : }
1231 : }
1232 341 : if (GDKgetenv("monet_pid") == NULL) {
1233 341 : snprintf(buf, sizeof(buf), "%d", (int) getpid());
1234 341 : if (GDKsetenv("monet_pid", buf) != GDK_SUCCEED) {
1235 0 : TRC_CRITICAL(GDK, "GDKsetenv monet_pid failed");
1236 0 : return GDK_FAIL;
1237 : }
1238 : }
1239 341 : if (GDKsetenv("revision", mercurial_revision()) != GDK_SUCCEED) {
1240 0 : TRC_CRITICAL(GDK, "GDKsetenv revision failed");
1241 0 : return GDK_FAIL;
1242 : }
1243 341 : gdk_unique_estimate_keep_fraction = 0;
1244 341 : if ((p = GDKgetenv("gdk_unique_estimate_keep_fraction")) != NULL)
1245 0 : gdk_unique_estimate_keep_fraction = (BUN) strtoll(p, NULL, 10);
1246 341 : if (gdk_unique_estimate_keep_fraction == 0)
1247 341 : gdk_unique_estimate_keep_fraction = GDK_UNIQUE_ESTIMATE_KEEP_FRACTION;
1248 341 : hash_destroy_uniques_fraction = 0;
1249 341 : if ((p = GDKgetenv("hash_destroy_uniques_fraction")) != NULL)
1250 0 : hash_destroy_uniques_fraction = (BUN) strtoll(p, NULL, 10);
1251 341 : if (hash_destroy_uniques_fraction == 0)
1252 341 : hash_destroy_uniques_fraction = HASH_DESTROY_UNIQUES_FRACTION;
1253 341 : no_hash_select_fraction = 0;
1254 341 : if ((p = GDKgetenv("no_hash_select_fraction")) != NULL)
1255 0 : no_hash_select_fraction = (dbl) strtoll(p, NULL, 10);
1256 341 : if (no_hash_select_fraction == 0)
1257 341 : no_hash_select_fraction = NO_HASH_SELECT_FRACTION;
1258 341 : hash_destroy_chain_length = 0;
1259 341 : if ((p = GDKgetenv("hash_destroy_chain_length")) != NULL)
1260 0 : hash_destroy_chain_length = (BUN) strtoll(p, NULL, 10);
1261 341 : if (hash_destroy_chain_length == 0)
1262 341 : hash_destroy_chain_length = HASH_DESTROY_CHAIN_LENGTH;
1263 :
1264 : return GDK_SUCCEED;
1265 : }
1266 :
1267 : int GDKnr_threads = 0;
1268 : static ATOMIC_TYPE GDKnrofthreads = ATOMIC_VAR_INIT(0);
1269 :
1270 : bool
1271 20256308 : GDKexiting(void)
1272 : {
1273 20256308 : return (bool) (ATOMIC_GET(&GDKstopped) > 0);
1274 : }
1275 :
1276 : void
1277 340 : GDKprepareExit(void)
1278 : {
1279 340 : ATOMIC_ADD(&GDKstopped, 1);
1280 :
1281 340 : if (MT_getpid() == mainpid) {
1282 339 : TRC_DEBUG_IF(THRD)
1283 0 : dump_threads();
1284 339 : join_detached_threads();
1285 : }
1286 340 : }
1287 :
1288 : void
1289 339 : GDKreset(int status)
1290 : {
1291 339 : assert(GDKexiting());
1292 :
1293 339 : if (GDKembedded())
1294 : // In the case of a restarted embedded database, GDKstopped has to be reset as well.
1295 11 : ATOMIC_SET(&GDKstopped, 0);
1296 :
1297 339 : if (GDKkey) {
1298 339 : BBPunfix(GDKkey->batCacheid);
1299 339 : GDKkey = NULL;
1300 : }
1301 339 : if (GDKval) {
1302 339 : BBPunfix(GDKval->batCacheid);
1303 339 : GDKval = NULL;
1304 : }
1305 :
1306 339 : join_detached_threads();
1307 :
1308 339 : MT_lock_set(&GDKenvlock);
1309 339 : while (orig_value) {
1310 0 : struct orig_value *ov = orig_value;
1311 0 : orig_value = orig_value->next;
1312 0 : GDKfree(ov);
1313 : }
1314 339 : MT_lock_unset(&GDKenvlock);
1315 :
1316 339 : if (status == 0) {
1317 : /* they had their chance, now kill them */
1318 339 : bool killed = MT_kill_threads();
1319 : /* all threads ceased running, now we can clean up */
1320 339 : if (!killed) {
1321 : /* we can't clean up after killing threads */
1322 339 : BBPexit();
1323 : }
1324 339 : GDKlog(GET_GDKLOCK(PERSISTENT), GDKLOGOFF);
1325 :
1326 11187 : for (int farmid = 0; farmid < MAXFARMS; farmid++) {
1327 10848 : if (BBPfarms[farmid].dirname != NULL) {
1328 1998 : bool skip = false;
1329 1998 : for (int j = 0; j < farmid; j++) {
1330 994 : if (BBPfarms[j].dirname != NULL &&
1331 0 : strcmp(BBPfarms[farmid].dirname, BBPfarms[j].dirname) == 0) {
1332 : skip = true;
1333 : break;
1334 : }
1335 : }
1336 1004 : if (!skip)
1337 1004 : GDKunlockHome(farmid);
1338 1004 : if (BBPfarms[farmid].dirname) {
1339 1004 : GDKfree((char*)BBPfarms[farmid].dirname);
1340 1004 : BBPfarms[farmid].dirname = NULL;
1341 : }
1342 : }
1343 : }
1344 :
1345 : #ifdef LOCK_STATS
1346 : TRC_DEBUG_IF(TEM) GDKlockstatistics(1);
1347 : #endif
1348 339 : ATOMIC_SET(&GDKdebug, 0);
1349 339 : GDK_mmap_minsize_persistent = MMAP_MINSIZE_PERSISTENT;
1350 339 : GDK_mmap_minsize_transient = MMAP_MINSIZE_TRANSIENT;
1351 339 : GDK_mmap_pagesize = MMAP_PAGESIZE;
1352 339 : GDK_mem_maxsize = (size_t) ((double) MT_npages() * (double) MT_pagesize() * 0.815);
1353 339 : GDK_vm_maxsize = GDK_VM_MAXSIZE;
1354 339 : GDKatomcnt = TYPE_blob + 1;
1355 :
1356 339 : if (GDK_mem_maxsize / 16 < GDK_mmap_minsize_transient) {
1357 339 : GDK_mmap_minsize_transient = GDK_mem_maxsize / 16;
1358 339 : if (GDK_mmap_minsize_persistent > GDK_mmap_minsize_transient)
1359 0 : GDK_mmap_minsize_persistent = GDK_mmap_minsize_transient;
1360 : }
1361 :
1362 339 : GDKnr_threads = 0;
1363 339 : ATOMIC_SET(&GDKnrofthreads, 0);
1364 339 : close_stream(GDKstdout);
1365 339 : close_stream(GDKstdin);
1366 339 : GDKstdout = NULL;
1367 339 : GDKstdin = NULL;
1368 :
1369 339 : gdk_bbp_reset();
1370 : }
1371 339 : ATOMunknown_clean();
1372 :
1373 : /* stop GDKtracer */
1374 339 : GDKtracer_stop();
1375 339 : }
1376 :
1377 : /*
1378 : * All semaphores used by the application should be mentioned here.
1379 : * They are initialized during system initialization.
1380 : */
1381 :
1382 : batlock_t GDKbatLock[BBP_BATMASK + 1];
1383 :
1384 : /*
1385 : * @+ Concurrency control
1386 : * Concurrency control requires actions at several levels of the
1387 : * system. First, it should be ensured that each database is
1388 : * controlled by a single server process (group). Subsequent attempts
1389 : * should be stopped. This is regulated through file locking against
1390 : * ".gdk_lock".
1391 : *
1392 : * Before the locks and threads are initiated, we cannot use the
1393 : * normal routines yet. So we have a local fatal here instead of
1394 : * GDKfatal.
1395 : */
1396 : static gdk_return
1397 518 : GDKlockHome(int farmid)
1398 : {
1399 518 : int fd;
1400 518 : struct stat st;
1401 518 : char *gdklockpath;
1402 518 : FILE *GDKlockFile;
1403 :
1404 518 : assert(BBPfarms[farmid].dirname != NULL);
1405 518 : assert(BBPfarms[farmid].lock_file == NULL);
1406 :
1407 518 : if ((gdklockpath = GDKfilepath(farmid, NULL, GDKLOCK, NULL)) == NULL) {
1408 : return GDK_FAIL;
1409 : }
1410 :
1411 : /*
1412 : * Obtain the global database lock.
1413 : */
1414 518 : if (MT_stat(BBPfarms[farmid].dirname, &st) < 0 &&
1415 0 : GDKcreatedir(gdklockpath) != GDK_SUCCEED) {
1416 0 : TRC_CRITICAL(GDK, "could not create %s\n",
1417 : BBPfarms[farmid].dirname);
1418 0 : GDKfree(gdklockpath);
1419 0 : return GDK_FAIL;
1420 : }
1421 518 : if ((fd = MT_lockf(gdklockpath, F_TLOCK)) < 0) {
1422 2 : TRC_CRITICAL(GDK, "Database lock '%s' denied\n",
1423 : gdklockpath);
1424 2 : GDKfree(gdklockpath);
1425 2 : return GDK_FAIL;
1426 : }
1427 :
1428 : /* now we have the lock on the database and are the only
1429 : * process allowed in this section */
1430 :
1431 516 : if ((GDKlockFile = fdopen(fd, "r+")) == NULL) {
1432 0 : GDKsyserror("Could not fdopen %s\n", gdklockpath);
1433 0 : close(fd);
1434 0 : GDKfree(gdklockpath);
1435 0 : return GDK_FAIL;
1436 : }
1437 :
1438 : /*
1439 : * Print the new process list in the global lock file.
1440 : */
1441 516 : if (fseek(GDKlockFile, 0, SEEK_SET) == -1) {
1442 0 : fclose(GDKlockFile);
1443 0 : TRC_CRITICAL(GDK, "Error while setting the file pointer on %s\n", gdklockpath);
1444 0 : GDKfree(gdklockpath);
1445 0 : return GDK_FAIL;
1446 : }
1447 516 : if (ftruncate(fileno(GDKlockFile), 0) < 0) {
1448 0 : fclose(GDKlockFile);
1449 0 : TRC_CRITICAL(GDK, "Could not truncate %s\n", gdklockpath);
1450 0 : GDKfree(gdklockpath);
1451 0 : return GDK_FAIL;
1452 : }
1453 516 : if (fflush(GDKlockFile) == EOF) {
1454 0 : fclose(GDKlockFile);
1455 0 : TRC_CRITICAL(GDK, "Could not flush %s\n", gdklockpath);
1456 0 : GDKfree(gdklockpath);
1457 0 : return GDK_FAIL;
1458 : }
1459 516 : GDKlog(GDKlockFile, GDKLOGON);
1460 516 : GDKfree(gdklockpath);
1461 516 : BBPfarms[farmid].lock_file = GDKlockFile;
1462 516 : return GDK_SUCCEED;
1463 : }
1464 :
1465 :
1466 : static void
1467 1004 : GDKunlockHome(int farmid)
1468 : {
1469 1004 : if (BBPfarms[farmid].lock_file) {
1470 514 : char *gdklockpath = GDKfilepath(farmid, NULL, GDKLOCK, NULL);
1471 514 : if (gdklockpath)
1472 514 : MT_lockf(gdklockpath, F_ULOCK);
1473 514 : fclose(BBPfarms[farmid].lock_file);
1474 514 : BBPfarms[farmid].lock_file = NULL;
1475 514 : GDKfree(gdklockpath);
1476 : }
1477 1004 : }
1478 :
1479 : /*
1480 : * @+ Error handling
1481 : * Errors come in three flavors: warnings, non-fatal and fatal errors.
1482 : * A fatal error leaves a core dump behind after trying to safe the
1483 : * content of the relation. A non-fatal error returns a message to
1484 : * the user and aborts the current transaction. Fatal errors are also
1485 : * recorded on the system log for post-mortem analysis.
1486 : * In non-silent mode the errors are immediately sent to output, which
1487 : * makes it hard for upper layers to detect if an error was produced
1488 : * in the process. To facilitate such testing, a global error count is
1489 : * maintained on a thread basis, which can be read out by the function
1490 : * GDKerrorCount(); Furthermore, threads may have set their private
1491 : * error buffer.
1492 : */
1493 :
1494 : #define GDKERRLEN (1024+512)
1495 :
1496 : void
1497 9247217 : GDKclrerr(void)
1498 : {
1499 9247217 : char *buf;
1500 :
1501 9247217 : buf = GDKerrbuf;
1502 9255305 : if (buf)
1503 9205879 : *buf = 0;
1504 9255305 : }
1505 :
1506 : jmp_buf GDKfataljump;
1507 : str GDKfatalmsg;
1508 : bit GDKfataljumpenable = 0;
1509 :
1510 : /* coverity[+kill] */
1511 : void
1512 0 : GDKfatal(const char *format, ...)
1513 : {
1514 0 : char message[GDKERRLEN];
1515 0 : size_t len = strlen(GDKFATAL);
1516 0 : va_list ap;
1517 :
1518 0 : GDKtracer_set_component_level("io", "debug");
1519 : #ifndef NATIVE_WIN32
1520 0 : BATSIGinit();
1521 : #endif
1522 0 : if (!strncmp(format, GDKFATAL, len)) {
1523 : len = 0;
1524 : } else {
1525 0 : strcpy(message, GDKFATAL);
1526 : }
1527 0 : va_start(ap, format);
1528 0 : vsnprintf(message + len, sizeof(message) - (len + 2), format, ap);
1529 0 : va_end(ap);
1530 :
1531 : #ifndef __COVERITY__
1532 0 : if (GDKfataljumpenable) {
1533 : // in embedded mode, we really don't want to kill our host
1534 0 : GDKfatalmsg = GDKstrdup(message);
1535 0 : longjmp(GDKfataljump, 42);
1536 : } else
1537 : #endif
1538 : {
1539 0 : fputs(message, stderr);
1540 0 : fputs("\n", stderr);
1541 0 : fflush(stderr);
1542 :
1543 : /*
1544 : * Real errors should be saved in the log file for post-crash
1545 : * inspection.
1546 : */
1547 0 : if (GDKexiting()) {
1548 0 : fflush(stdout);
1549 0 : exit(1);
1550 : } else {
1551 0 : GDKlog(GET_GDKLOCK(PERSISTENT), "%s", message);
1552 : #ifdef COREDUMP
1553 : abort();
1554 : #else
1555 0 : exit(1);
1556 : #endif
1557 : }
1558 : }
1559 : }
1560 :
1561 :
1562 : lng
1563 160028970 : GDKusec(void)
1564 : {
1565 : /* Return the time in microseconds since an epoch. The epoch
1566 : * is currently midnight at the start of January 1, 1970, UTC. */
1567 : #if defined(NATIVE_WIN32)
1568 : FILETIME ft;
1569 : ULARGE_INTEGER f;
1570 : GetSystemTimeAsFileTime(&ft); /* time since Jan 1, 1601 */
1571 : f.LowPart = ft.dwLowDateTime;
1572 : f.HighPart = ft.dwHighDateTime;
1573 : /* there are 369 years, of which 89 are leap years from
1574 : * January 1, 1601 to January 1, 1970 which makes 134774 days;
1575 : * multiply that with the number of seconds in a day and the
1576 : * number of 100ns units in a second; subtract that from the
1577 : * value for the current time since January 1, 1601 to get the
1578 : * time since the Unix epoch */
1579 : f.QuadPart -= LL_CONSTANT(134774) * 24 * 60 * 60 * 10000000;
1580 : /* and convert to microseconds */
1581 : return (lng) (f.QuadPart / 10);
1582 : #elif defined(HAVE_CLOCK_GETTIME)
1583 160028970 : struct timespec ts;
1584 160028970 : (void) clock_gettime(CLOCK_REALTIME, &ts);
1585 160335831 : return (lng) (ts.tv_sec * LL_CONSTANT(1000000) + ts.tv_nsec / 1000);
1586 : #elif defined(HAVE_GETTIMEOFDAY)
1587 : struct timeval tv;
1588 : gettimeofday(&tv, NULL);
1589 : return (lng) (tv.tv_sec * LL_CONSTANT(1000000) + tv.tv_usec);
1590 : #elif defined(HAVE_FTIME)
1591 : struct timeb tb;
1592 : ftime(&tb);
1593 : return (lng) (tb.time * LL_CONSTANT(1000000) + tb.millitm * LL_CONSTANT(1000));
1594 : #else
1595 : /* last resort */
1596 : return (lng) (time(NULL) * LL_CONSTANT(1000000));
1597 : #endif
1598 : }
1599 :
1600 :
1601 : int
1602 0 : GDKms(void)
1603 : {
1604 : /* wraps around after a bit over 24 days */
1605 0 : return (int) ((GDKusec() - programepoch) / 1000);
1606 : }
1607 :
1608 :
1609 : /*
1610 : * @+ Logical Thread management
1611 : *
1612 : * All semaphores used by the application should be mentioned here.
1613 : * They are initialized during system initialization.
1614 : *
1615 : * The first action upon thread creation is to add it to the pool of
1616 : * known threads. This should be done by the thread itself.
1617 : * Note that the users should have gained exclusive access already. A
1618 : * new entry is initialized automatically when not found. Its file
1619 : * descriptors are the same as for the server and should be
1620 : * subsequently reset.
1621 : */
1622 : stream *GDKstdout;
1623 : stream *GDKstdin;
1624 :
1625 : static int
1626 343 : THRinit(void)
1627 : {
1628 343 : if ((GDKstdout = stdout_wastream()) == NULL) {
1629 0 : TRC_CRITICAL(GDK, "malloc for stdout failed\n");
1630 0 : return -1;
1631 : }
1632 343 : if ((GDKstdin = stdin_rastream()) == NULL) {
1633 0 : TRC_CRITICAL(GDK, "malloc for stdin failed\n");
1634 0 : mnstr_destroy(GDKstdout);
1635 0 : GDKstdout = NULL;
1636 0 : return -1;
1637 : }
1638 343 : struct freebats *t = MT_thread_getfreebats();
1639 343 : t->freebats = 0;
1640 343 : t->nfreebats = 0;
1641 343 : return 0;
1642 : }
1643 :
1644 : const char *
1645 330 : GDKversion(void)
1646 : {
1647 330 : return MONETDB_VERSION;
1648 : }
1649 :
1650 : const char *
1651 671 : GDKlibversion(void)
1652 : {
1653 671 : return GDK_VERSION;
1654 : }
1655 :
1656 : inline size_t
1657 182550176 : GDKmem_cursize(void)
1658 : {
1659 : /* RAM/swapmem that Monet is really using now */
1660 182550176 : return (size_t) ATOMIC_GET(&GDK_mallocedbytes_estimate);
1661 : }
1662 :
1663 : inline size_t
1664 174607859 : GDKvm_cursize(void)
1665 : {
1666 : /* current Monet VM address space usage */
1667 174607859 : return (size_t) ATOMIC_GET(&GDK_vm_cursize) + GDKmem_cursize();
1668 : }
1669 :
1670 : #define heapinc(_memdelta) \
1671 : ATOMIC_ADD(&GDK_mallocedbytes_estimate, _memdelta)
1672 : #ifndef NDEBUG
1673 : #define heapdec(_memdelta) \
1674 : do { \
1675 : ATOMIC_BASE_TYPE old = ATOMIC_SUB(&GDK_mallocedbytes_estimate, _memdelta); \
1676 : assert(old >= (ATOMIC_BASE_TYPE) _memdelta); \
1677 : } while (0)
1678 : #else
1679 : #define heapdec(_memdelta) \
1680 : ATOMIC_SUB(&GDK_mallocedbytes_estimate, _memdelta)
1681 : #endif
1682 :
1683 : #define meminc(vmdelta) \
1684 : ATOMIC_ADD(&GDK_vm_cursize, SEG_SIZE(vmdelta))
1685 : #ifndef NDEBUG
1686 : #define memdec(vmdelta) \
1687 : do { \
1688 : ssize_t diff = SEG_SIZE(vmdelta); \
1689 : ATOMIC_BASE_TYPE old = ATOMIC_SUB(&GDK_vm_cursize, diff); \
1690 : assert(old >= (ATOMIC_BASE_TYPE) diff); \
1691 : } while (0)
1692 : #else
1693 : #define memdec(vmdelta) \
1694 : ATOMIC_SUB(&GDK_vm_cursize, SEG_SIZE(vmdelta))
1695 : #endif
1696 :
1697 : /* Memory allocation
1698 : *
1699 : * The functions GDKmalloc, GDKzalloc, GDKrealloc, GDKstrdup, and
1700 : * GDKfree are used throughout to allocate and free memory. These
1701 : * functions are almost directly mapped onto the system
1702 : * malloc/realloc/free functions, but they give us some extra
1703 : * debugging hooks.
1704 : *
1705 : * When allocating memory, we allocate a bit more than was asked for.
1706 : * The extra space is added onto the front of the memory area that is
1707 : * returned, and in debug builds also some at the end. The area in
1708 : * front is used to store the actual size of the allocated area. The
1709 : * most important use is to be able to keep statistics on how much
1710 : * memory is being used. In debug builds, the size is also used to
1711 : * make sure that we don't write outside of the allocated arena. This
1712 : * is also where the extra space at the end comes in.
1713 : */
1714 :
1715 : /* we allocate extra space and return a pointer offset by this amount */
1716 : #define MALLOC_EXTRA_SPACE (2 * SIZEOF_VOID_P)
1717 :
1718 : #if defined(NDEBUG) || defined(SANITIZER)
1719 : #define DEBUG_SPACE 0
1720 : #else
1721 : #define DEBUG_SPACE 16
1722 : #endif
1723 :
1724 : /* malloc smaller than this aren't subject to the GDK_vm_maxsize test */
1725 : #define SMALL_MALLOC 256
1726 :
1727 : static void *
1728 169029436 : GDKmalloc_internal(size_t size, bool clear)
1729 : {
1730 169029436 : void *s;
1731 169029436 : size_t nsize;
1732 :
1733 169029436 : assert(size != 0);
1734 : #ifndef NDEBUG
1735 : /* fail malloc for testing purposes depending on set limit */
1736 169029436 : if (GDK_malloc_success_count > 0) {
1737 0 : MT_lock_set(&mallocsuccesslock);
1738 0 : if (GDK_malloc_success_count > 0)
1739 0 : GDK_malloc_success_count--;
1740 0 : MT_lock_unset(&mallocsuccesslock);
1741 : }
1742 169029436 : if (GDK_malloc_success_count == 0) {
1743 0 : GDKerror("allocation failed because of testing limit\n");
1744 0 : return NULL;
1745 : }
1746 : #endif
1747 : #ifndef SIZE_CHECK_IN_HEAPS_ONLY
1748 : if (size > SMALL_MALLOC &&
1749 : GDKvm_cursize() + size >= GDK_vm_maxsize &&
1750 : !MT_thread_override_limits()) {
1751 : GDKerror("allocating too much memory\n");
1752 : return NULL;
1753 : }
1754 : #endif
1755 :
1756 : /* pad to multiple of eight bytes and add some extra space to
1757 : * write real size in front; when debugging, also allocate
1758 : * extra space for check bytes */
1759 169029436 : nsize = (size + 7) & ~7;
1760 169029436 : if (clear)
1761 29605393 : s = calloc(nsize + MALLOC_EXTRA_SPACE + DEBUG_SPACE, 1);
1762 : else
1763 139424043 : s = malloc(nsize + MALLOC_EXTRA_SPACE + DEBUG_SPACE);
1764 169029436 : if (s == NULL) {
1765 0 : GDKsyserror("malloc failed; memory requested: %zu, memory in use: %zu, virtual memory in use: %zu\n", size, GDKmem_cursize(), GDKvm_cursize());;
1766 0 : return NULL;
1767 : }
1768 169029436 : s = (void *) ((char *) s + MALLOC_EXTRA_SPACE);
1769 :
1770 169029436 : heapinc(nsize + MALLOC_EXTRA_SPACE + DEBUG_SPACE);
1771 :
1772 : /* just before the pointer that we return, write how much we
1773 : * asked of malloc */
1774 169029436 : ((size_t *) s)[-1] = nsize + MALLOC_EXTRA_SPACE + DEBUG_SPACE;
1775 : #if !defined(NDEBUG) && !defined(SANITIZER)
1776 : /* just before that, write how much was asked of us */
1777 169029436 : ((size_t *) s)[-2] = size;
1778 : /* write pattern to help find out-of-bounds writes */
1779 169029436 : memset((char *) s + size, '\xBD', nsize + DEBUG_SPACE - size);
1780 : #endif
1781 169029436 : return s;
1782 : }
1783 :
1784 : #undef GDKmalloc
1785 : void *
1786 131839464 : GDKmalloc(size_t size)
1787 : {
1788 131839464 : void *s;
1789 :
1790 131839464 : if ((s = GDKmalloc_internal(size, false)) == NULL)
1791 : return NULL;
1792 : #if !defined(NDEBUG) && !defined(SANITIZER)
1793 : /* write a pattern to help make sure all data is properly
1794 : * initialized by the caller */
1795 132125555 : DEADBEEFCHK memset(s, '\xBD', size);
1796 : #endif
1797 : return s;
1798 : }
1799 :
1800 : #undef GDKzalloc
1801 : void *
1802 29608161 : GDKzalloc(size_t size)
1803 : {
1804 29608161 : return GDKmalloc_internal(size, true);
1805 : }
1806 :
1807 : #undef GDKstrdup
1808 : char *
1809 8268594 : GDKstrdup(const char *s)
1810 : {
1811 8268594 : size_t size;
1812 8268594 : char *p;
1813 :
1814 8268594 : if (s == NULL)
1815 : return NULL;
1816 7649168 : size = strlen(s) + 1;
1817 :
1818 7649168 : if ((p = GDKmalloc_internal(size, false)) == NULL)
1819 : return NULL;
1820 7649689 : memcpy(p, s, size); /* including terminating NULL byte */
1821 7649689 : return p;
1822 : }
1823 :
1824 : #undef GDKstrndup
1825 : char *
1826 126 : GDKstrndup(const char *s, size_t size)
1827 : {
1828 126 : char *p;
1829 :
1830 126 : if (s == NULL)
1831 : return NULL;
1832 126 : if ((p = GDKmalloc_internal(size + 1, false)) == NULL)
1833 : return NULL;
1834 126 : if (size > 0)
1835 126 : memcpy(p, s, size);
1836 126 : p[size] = '\0'; /* make sure it's NULL terminated */
1837 126 : return p;
1838 : }
1839 :
1840 : #undef GDKfree
1841 : void
1842 323161815 : GDKfree(void *s)
1843 : {
1844 323161815 : size_t asize;
1845 :
1846 323161815 : if (s == NULL)
1847 : return;
1848 :
1849 169144259 : asize = ((size_t *) s)[-1]; /* how much allocated last */
1850 :
1851 : #if !defined(NDEBUG) && !defined(SANITIZER)
1852 169144259 : size_t *p = s;
1853 169144259 : assert((asize & 2) == 0); /* check against duplicate free */
1854 169144259 : size_t size = p[-2];
1855 169144259 : assert(((size + 7) & ~7) + MALLOC_EXTRA_SPACE + DEBUG_SPACE == asize);
1856 : /* check for out-of-bounds writes */
1857 3245654229 : for (size_t i = size; i < asize - MALLOC_EXTRA_SPACE; i++)
1858 3076509970 : assert(((char *) s)[i] == '\xBD');
1859 169144259 : p[-1] |= 2; /* indicate area is freed */
1860 :
1861 : /* overwrite memory that is to be freed with a pattern that
1862 : * will help us recognize access to already freed memory in
1863 : * the debugger */
1864 169144259 : DEADBEEFCHK memset(s, '\xDB', asize - MALLOC_EXTRA_SPACE);
1865 : #endif
1866 :
1867 169144259 : free((char *) s - MALLOC_EXTRA_SPACE);
1868 169144259 : heapdec((ssize_t) asize);
1869 : }
1870 :
1871 : #undef GDKrealloc
1872 : void *
1873 278741 : GDKrealloc(void *s, size_t size)
1874 : {
1875 278741 : size_t nsize, asize;
1876 : #if !defined(NDEBUG) && !defined(SANITIZER)
1877 278741 : size_t osize;
1878 : #endif
1879 278741 : size_t *os = s;
1880 :
1881 278741 : assert(size != 0);
1882 :
1883 278741 : if (s == NULL)
1884 957 : return GDKmalloc(size);
1885 :
1886 277784 : nsize = (size + 7) & ~7;
1887 277784 : asize = os[-1]; /* how much allocated last */
1888 :
1889 : #ifndef SIZE_CHECK_IN_HEAPS_ONLY
1890 : if (size > SMALL_MALLOC &&
1891 : nsize > asize &&
1892 : GDKvm_cursize() + nsize - asize >= GDK_vm_maxsize &&
1893 : !MT_thread_override_limits()) {
1894 : GDKerror("allocating too much memory\n");
1895 : return NULL;
1896 : }
1897 : #endif
1898 : #if !defined(NDEBUG) && !defined(SANITIZER)
1899 277784 : assert((asize & 2) == 0); /* check against duplicate free */
1900 : /* check for out-of-bounds writes */
1901 277784 : osize = os[-2]; /* how much asked for last */
1902 277784 : assert(((osize + 7) & ~7) + MALLOC_EXTRA_SPACE + DEBUG_SPACE == asize);
1903 4770897 : for (size_t i = osize; i < asize - MALLOC_EXTRA_SPACE; i++)
1904 4493113 : assert(((char *) s)[i] == '\xBD');
1905 : /* if shrinking, write debug pattern into to-be-freed memory */
1906 277784 : DEADBEEFCHK if (size < osize)
1907 40483 : memset((char *) s + size, '\xDB', osize - size);
1908 277784 : os[-1] |= 2; /* indicate area is freed */
1909 : #endif
1910 277784 : s = realloc((char *) s - MALLOC_EXTRA_SPACE,
1911 : nsize + MALLOC_EXTRA_SPACE + DEBUG_SPACE);
1912 277784 : if (s == NULL) {
1913 : #if !defined(NDEBUG) && !defined(SANITIZER)
1914 0 : os[-1] &= ~2; /* not freed after all */
1915 0 : assert(os[-1] == asize);
1916 0 : assert(os[-2] == osize);
1917 : #endif
1918 0 : GDKsyserror("realloc failed; memory requested: %zu, memory in use: %zu, virtual memory in use: %zu\n", size, GDKmem_cursize(), GDKvm_cursize());;
1919 0 : return NULL;
1920 : }
1921 277784 : s = (void *) ((char *) s + MALLOC_EXTRA_SPACE);
1922 : /* just before the pointer that we return, write how much we
1923 : * asked of malloc */
1924 277784 : ((size_t *) s)[-1] = nsize + MALLOC_EXTRA_SPACE + DEBUG_SPACE;
1925 : #if !defined(NDEBUG) && !defined(SANITIZER)
1926 : /* just before that, write how much was asked of us */
1927 277784 : ((size_t *) s)[-2] = size;
1928 : /* if growing, initialize new memory with debug pattern */
1929 277784 : DEADBEEFCHK if (size > osize)
1930 237253 : memset((char *) s + osize, '\xBD', size - osize);
1931 : /* write pattern to help find out-of-bounds writes */
1932 277784 : memset((char *) s + size, '\xBD', nsize + DEBUG_SPACE - size);
1933 : #endif
1934 :
1935 277784 : heapinc(nsize + MALLOC_EXTRA_SPACE + DEBUG_SPACE);
1936 277784 : heapdec((ssize_t) asize);
1937 :
1938 : return s;
1939 : }
1940 :
1941 : /* return how much memory was allocated; the argument must be a value
1942 : * returned by GDKmalloc, GDKzalloc, GDKrealloc, GDKstrdup, or
1943 : * GDKstrndup */
1944 : size_t
1945 180574 : GDKmallocated(const void *s)
1946 : {
1947 180574 : return ((const size_t *) s)[-1]; /* how much allocated last */
1948 : }
1949 :
1950 : void
1951 39061 : GDKsetmallocsuccesscount(lng count)
1952 : {
1953 39061 : (void) count;
1954 : #ifndef NDEBUG
1955 39061 : GDK_malloc_success_count = count;
1956 : #endif
1957 39061 : }
1958 :
1959 : /*
1960 : * @- virtual memory
1961 : * allocations affect only the logical VM resources.
1962 : */
1963 : #undef GDKmmap
1964 : void *
1965 2086 : GDKmmap(const char *path, int mode, size_t len)
1966 : {
1967 2086 : void *ret;
1968 :
1969 : #ifndef SIZE_CHECK_IN_HEAPS_ONLY
1970 : if (GDKvm_cursize() + len >= GDK_vm_maxsize &&
1971 : !MT_thread_override_limits()) {
1972 : GDKerror("requested too much virtual memory; memory requested: %zu, memory in use: %zu, virtual memory in use: %zu\n", len, GDKmem_cursize(), GDKvm_cursize());
1973 : return NULL;
1974 : }
1975 : #endif
1976 2086 : ret = MT_mmap(path, mode, len);
1977 2114 : if (ret != NULL) {
1978 2114 : if (mode & MMAP_COPY)
1979 0 : heapinc(len);
1980 : else
1981 2114 : meminc(len);
1982 : } else
1983 0 : GDKerror("requesting virtual memory failed; memory requested: %zu, memory in use: %zu, virtual memory in use: %zu\n", len, GDKmem_cursize(), GDKvm_cursize());
1984 2114 : return ret;
1985 : }
1986 :
1987 : #undef GDKmunmap
1988 : gdk_return
1989 2110 : GDKmunmap(void *addr, int mode, size_t size)
1990 : {
1991 2110 : int ret;
1992 :
1993 2110 : ret = MT_munmap(addr, size);
1994 2114 : if (ret == 0) {
1995 2114 : if (mode & MMAP_COPY)
1996 0 : heapdec(size);
1997 : else
1998 2114 : memdec(size);
1999 : }
2000 2114 : return ret == 0 ? GDK_SUCCEED : GDK_FAIL;
2001 : }
2002 :
2003 : #undef GDKmremap
2004 : void *
2005 465 : GDKmremap(const char *path, int mode, void *old_address, size_t old_size, size_t *new_size)
2006 : {
2007 465 : void *ret;
2008 :
2009 : #ifndef SIZE_CHECK_IN_HEAPS_ONLY
2010 : if (*new_size > old_size &&
2011 : GDKvm_cursize() + *new_size - old_size >= GDK_vm_maxsize &&
2012 : !MT_thread_override_limits()) {
2013 : GDKerror("requested too much virtual memory; memory requested: %zu, memory in use: %zu, virtual memory in use: %zu\n", *new_size, GDKmem_cursize(), GDKvm_cursize());
2014 : return NULL;
2015 : }
2016 : #endif
2017 465 : ret = MT_mremap(path, mode, old_address, old_size, new_size);
2018 465 : if (ret != NULL) {
2019 465 : if (mode & MMAP_COPY) {
2020 0 : heapdec(old_size);
2021 0 : heapinc(*new_size);
2022 : } else {
2023 465 : memdec(old_size);
2024 465 : meminc(*new_size);
2025 : }
2026 : } else {
2027 0 : GDKerror("requesting virtual memory failed; memory requested: %zu, memory in use: %zu, virtual memory in use: %zu\n", *new_size, GDKmem_cursize(), GDKvm_cursize());
2028 : }
2029 465 : return ret;
2030 : }
2031 :
2032 : /* print some potentially interesting information */
2033 : struct prinfocb {
2034 : struct prinfocb *next;
2035 : void (*func)(void);
2036 : } *prinfocb;
2037 :
2038 : void
2039 680 : GDKprintinforegister(void (*func)(void))
2040 : {
2041 680 : struct prinfocb *p = GDKmalloc(sizeof(struct prinfocb));
2042 680 : if (p == NULL) {
2043 0 : GDKerror("cannot register USR1 printing function.\n");
2044 0 : return;
2045 : }
2046 680 : p->func = func;
2047 680 : p->next = NULL;
2048 680 : struct prinfocb **pp = &prinfocb;
2049 1239 : while (*pp != NULL)
2050 559 : pp = &(*pp)->next;
2051 680 : *pp = p;
2052 : }
2053 :
2054 : void
2055 114 : GDKprintinfo(void)
2056 : {
2057 114 : size_t allocated = (size_t) ATOMIC_GET(&GDK_mallocedbytes_estimate);
2058 114 : size_t vmallocated = (size_t) ATOMIC_GET(&GDK_vm_cursize);
2059 :
2060 114 : printf("SIGUSR1 info start\n");
2061 114 : printf("Virtual memory allocated: %zu, of which %zu with malloc\n",
2062 : vmallocated + allocated, allocated);
2063 114 : printf("gdk_vm_maxsize: %zu, gdk_mem_maxsize: %zu\n",
2064 : GDK_vm_maxsize, GDK_mem_maxsize);
2065 114 : printf("gdk_mmap_minsize_persistent %zu, gdk_mmap_minsize_transient %zu\n",
2066 : GDK_mmap_minsize_persistent, GDK_mmap_minsize_transient);
2067 : #ifdef __linux__
2068 114 : int fd = open("/proc/self/statm", O_RDONLY | O_CLOEXEC);
2069 114 : if (fd >= 0) {
2070 114 : char buf[512];
2071 114 : ssize_t s = read(fd, buf, sizeof(buf) - 1);
2072 114 : close(fd);
2073 114 : if (s > 0) {
2074 114 : assert((size_t) s < sizeof(buf));
2075 114 : size_t size, resident, shared;
2076 114 : buf[s] = 0;
2077 114 : if (sscanf(buf, "%zu %zu %zu", &size, &resident, &shared) == 3) {
2078 114 : size *= MT_pagesize();
2079 114 : resident *= MT_pagesize();
2080 114 : shared *= MT_pagesize();
2081 114 : printf("Virtual size: %zu, anonymous RSS: %zu, shared RSS: %zu (together: %zu)\n",
2082 : size, resident - shared, shared, resident);
2083 : }
2084 : }
2085 : }
2086 : #endif
2087 114 : BBPprintinfo();
2088 : #ifdef LOCK_STATS
2089 : GDKlockstatistics(3);
2090 : #endif
2091 114 : dump_threads();
2092 342 : for (struct prinfocb *p = prinfocb; p; p = p->next)
2093 228 : (*p->func)();
2094 114 : printf("SIGUSR1 info end\n");
2095 114 : }
2096 :
2097 : exception_buffer *
2098 674048 : eb_init(exception_buffer *eb)
2099 : {
2100 674048 : if (eb) {
2101 674048 : eb->enabled = 0;
2102 674048 : eb->code = 0;
2103 674048 : eb->msg = NULL;
2104 : }
2105 674048 : return eb;
2106 : }
2107 :
2108 : void
2109 2 : eb_error( exception_buffer *eb, char *msg, int val )
2110 : {
2111 2 : eb->code = val;
2112 2 : eb->msg = msg;
2113 2 : eb->enabled = 0; /* not any longer... */
2114 : #ifdef HAVE_SIGLONGJMP
2115 2 : siglongjmp(eb->state, eb->code);
2116 : #else
2117 : longjmp(eb->state, eb->code);
2118 : #endif
2119 : }
2120 :
2121 : #define SA_BLOCK (64*1024)
2122 :
2123 : typedef struct freed_t {
2124 : struct freed_t *n;
2125 : size_t sz;
2126 : } freed_t;
2127 :
2128 : static void
2129 69765 : sa_destroy_freelist( freed_t *f )
2130 : {
2131 76822 : while(f) {
2132 7057 : freed_t *n = f->n;
2133 7057 : GDKfree(f);
2134 7057 : f = n;
2135 : }
2136 : }
2137 :
2138 : static void
2139 180574 : sa_free(allocator *pa, void *blk)
2140 : {
2141 180574 : assert(!pa->pa);
2142 : size_t i;
2143 :
2144 46106308 : for(i = 0; i < pa->nr; i++) {
2145 46106308 : if (pa->blks[i] == blk)
2146 : break;
2147 : }
2148 180574 : assert (i < pa->nr);
2149 1480220 : for (; i < pa->nr-1; i++)
2150 1299646 : pa->blks[i] = pa->blks[i+1];
2151 180574 : pa->nr--;
2152 :
2153 180574 : size_t sz = GDKmallocated(blk);
2154 180574 : if (sz > (SA_BLOCK + 32)) {
2155 116 : GDKfree(blk);
2156 : } else {
2157 180458 : freed_t *f = blk;
2158 180458 : f->n = pa->freelist;
2159 180458 : f->sz = sz;
2160 :
2161 180458 : pa->freelist = f;
2162 : }
2163 180574 : }
2164 :
2165 : static void *
2166 173401 : sa_use_freed(allocator *pa, size_t sz)
2167 : {
2168 173401 : (void)sz;
2169 :
2170 173401 : freed_t *f = pa->freelist;
2171 173401 : pa->freelist = f->n;
2172 173401 : return f;
2173 : }
2174 :
2175 : allocator *
2176 275384 : sa_create(allocator *pa)
2177 : {
2178 275384 : allocator *sa = (pa)?(allocator*)sa_alloc(pa, sizeof(allocator)):(allocator*)GDKmalloc(sizeof(allocator));
2179 275379 : if (sa == NULL)
2180 : return NULL;
2181 275379 : eb_init(&sa->eb);
2182 275376 : sa->pa = pa;
2183 275376 : sa->size = 64;
2184 275376 : sa->nr = 1;
2185 275376 : sa->blks = pa?(char**)sa_alloc(pa, sizeof(char*) * sa->size):(char**)GDKmalloc(sizeof(char*) * sa->size);
2186 275376 : sa->freelist = NULL;
2187 275376 : if (sa->blks == NULL) {
2188 0 : if (!pa)
2189 0 : GDKfree(sa);
2190 0 : return NULL;
2191 : }
2192 275376 : sa->blks[0] = pa?(char*)sa_alloc(pa, SA_BLOCK):(char*)GDKmalloc(SA_BLOCK);
2193 275376 : sa->usedmem = SA_BLOCK;
2194 275376 : if (sa->blks[0] == NULL) {
2195 0 : if (!pa)
2196 0 : GDKfree(sa->blks);
2197 0 : if (!pa)
2198 0 : GDKfree(sa);
2199 0 : return NULL;
2200 : }
2201 275376 : sa->used = 0;
2202 275376 : return sa;
2203 : }
2204 :
2205 1845660 : allocator *sa_reset( allocator *sa )
2206 : {
2207 1845660 : size_t i ;
2208 :
2209 1897262 : for (i = 1; i<sa->nr; i++) {
2210 51349 : if (!sa->pa)
2211 0 : GDKfree(sa->blks[i]);
2212 : else
2213 51349 : sa_free(sa->pa, sa->blks[i]);
2214 : }
2215 1845913 : sa->nr = 1;
2216 1845913 : sa->used = 0;
2217 1845913 : sa->usedmem = SA_BLOCK;
2218 1845913 : return sa;
2219 : }
2220 :
2221 : #undef sa_realloc
2222 : #undef sa_alloc
2223 : void *
2224 25 : sa_realloc( allocator *sa, void *p, size_t sz, size_t oldsz )
2225 : {
2226 25 : void *r = sa_alloc(sa, sz);
2227 :
2228 25 : if (r)
2229 25 : memcpy(r, p, oldsz);
2230 25 : return r;
2231 : }
2232 :
2233 : #define round16(sz) ((sz+15)&~15)
2234 : void *
2235 232602869 : sa_alloc( allocator *sa, size_t sz )
2236 : {
2237 232602869 : char *r;
2238 232602869 : sz = round16(sz);
2239 232602869 : if (sz > (SA_BLOCK-sa->used)) {
2240 491690 : if (sa->pa)
2241 51578 : r = (char*)sa_alloc(sa->pa, (sz > SA_BLOCK ? sz : SA_BLOCK));
2242 440112 : else if (sz <= SA_BLOCK && sa->freelist) {
2243 173401 : r = sa_use_freed(sa, SA_BLOCK);
2244 : } else
2245 266711 : r = GDKmalloc(sz > SA_BLOCK ? sz : SA_BLOCK);
2246 491680 : if (r == NULL) {
2247 0 : if (sa->eb.enabled)
2248 0 : eb_error(&sa->eb, "out of memory", 1000);
2249 : return NULL;
2250 : }
2251 491680 : if (sa->nr >= sa->size) {
2252 931 : char **tmp;
2253 931 : size_t osz = sa->size;
2254 931 : sa->size *=2;
2255 931 : if (sa->pa)
2256 11 : tmp = (char**)sa_realloc(sa->pa, sa->blks, sizeof(char*) * sa->size, sizeof(char*) * osz);
2257 : else
2258 920 : tmp = GDKrealloc(sa->blks, sizeof(char*) * sa->size);
2259 931 : if (tmp == NULL) {
2260 0 : sa->size /= 2; /* undo */
2261 0 : if (sa->eb.enabled)
2262 0 : eb_error(&sa->eb, "out of memory", 1000);
2263 0 : if (!sa->pa)
2264 0 : GDKfree(r);
2265 0 : return NULL;
2266 : }
2267 931 : sa->blks = tmp;
2268 : }
2269 491680 : if (sz > SA_BLOCK) {
2270 588 : sa->blks[sa->nr] = sa->blks[sa->nr-1];
2271 588 : sa->blks[sa->nr-1] = r;
2272 588 : sa->nr ++;
2273 588 : sa->usedmem += sz;
2274 : } else {
2275 491092 : sa->blks[sa->nr] = r;
2276 491092 : sa->nr ++;
2277 491092 : sa->used = sz;
2278 491092 : sa->usedmem += SA_BLOCK;
2279 : }
2280 : } else {
2281 232111179 : r = sa->blks[sa->nr-1] + sa->used;
2282 232111179 : sa->used += sz;
2283 : }
2284 : return r;
2285 : }
2286 :
2287 : #undef sa_zalloc
2288 10796623 : void *sa_zalloc( allocator *sa, size_t sz )
2289 : {
2290 10796623 : void *r = sa_alloc(sa, sz);
2291 :
2292 10796578 : if (r)
2293 10796578 : memset(r, 0, sz);
2294 10796578 : return r;
2295 : }
2296 :
2297 198990 : void sa_destroy( allocator *sa )
2298 : {
2299 198990 : if (sa->pa) {
2300 129225 : sa_reset(sa);
2301 129225 : sa_free(sa->pa, sa->blks[0]);
2302 129225 : return;
2303 : }
2304 :
2305 69765 : sa_destroy_freelist(sa->freelist);
2306 399081 : for (size_t i = 0; i<sa->nr; i++) {
2307 329316 : GDKfree(sa->blks[i]);
2308 : }
2309 69765 : GDKfree(sa->blks);
2310 69765 : GDKfree(sa);
2311 : }
2312 :
2313 : #undef sa_strndup
2314 14492289 : char *sa_strndup( allocator *sa, const char *s, size_t l)
2315 : {
2316 14492289 : char *r = sa_alloc(sa, l+1);
2317 :
2318 14491961 : if (r) {
2319 14491961 : memcpy(r, s, l);
2320 14491961 : r[l] = 0;
2321 : }
2322 14491961 : return r;
2323 : }
2324 :
2325 : #undef sa_strdup
2326 9232698 : char *sa_strdup( allocator *sa, const char *s )
2327 : {
2328 9232698 : return sa_strndup( sa, s, strlen(s));
2329 : }
2330 :
2331 41897 : char *sa_strconcat( allocator *sa, const char *s1, const char *s2 )
2332 : {
2333 41897 : size_t l1 = strlen(s1);
2334 41897 : size_t l2 = strlen(s2);
2335 41897 : char *r = sa_alloc(sa, l1+l2+1);
2336 :
2337 41894 : if (l1)
2338 41896 : memcpy(r, s1, l1);
2339 41894 : if (l2)
2340 41898 : memcpy(r+l1, s2, l2);
2341 41894 : r[l1+l2] = 0;
2342 41894 : return r;
2343 : }
2344 :
2345 0 : size_t sa_size( allocator *sa )
2346 : {
2347 0 : return sa->usedmem;
2348 : }
|