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