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