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, 2025 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 358 : GDKenvironment(const char *dbpath)
94 : {
95 358 : if (dbpath == NULL) {
96 0 : TRC_CRITICAL(GDK, "Database name missing.\n");
97 0 : return false;
98 : }
99 358 : if (strlen(dbpath) >= FILENAME_MAX) {
100 0 : TRC_CRITICAL(GDK, "Database name too long.\n");
101 0 : return false;
102 : }
103 358 : 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 478623 : GDKgetenv(const char *name)
119 : {
120 478623 : MT_lock_set(&GDKenvlock);
121 478693 : 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 478693 : MT_lock_unset(&GDKenvlock);
128 478693 : if (GDKkey && GDKval) {
129 477997 : BUN b = BUNfnd(GDKkey, name);
130 :
131 477995 : if (b != BUN_NONE) {
132 335074 : BATiter GDKenvi = bat_iterator(GDKval);
133 335076 : const char *v = BUNtvar(GDKenvi, b);
134 335076 : bat_iterator_end(&GDKenvi);
135 335076 : return v;
136 : }
137 : }
138 : return NULL;
139 : }
140 :
141 : bool
142 291479 : GDKgetenv_istext(const char *name, const char *text)
143 : {
144 291479 : const char *val = GDKgetenv(name);
145 :
146 291488 : return val && strcasecmp(val, text) == 0;
147 : }
148 :
149 : bool
150 39035 : GDKgetenv_isyes(const char *name)
151 : {
152 39035 : return GDKgetenv_istext(name, "yes");
153 : }
154 :
155 : bool
156 252453 : GDKgetenv_istrue(const char *name)
157 : {
158 252453 : return GDKgetenv_istext(name, "true");
159 : }
160 :
161 : int
162 97900 : GDKgetenv_int(const char *name, int def)
163 : {
164 97900 : const char *val = GDKgetenv(name);
165 :
166 97904 : if (val)
167 345 : return atoi(val);
168 : return def;
169 : }
170 :
171 : #define ESCAPE_CHAR '%'
172 :
173 : static bool
174 8722 : isutf8(const char *v, size_t *esclen)
175 : {
176 8722 : size_t n = 1;
177 8722 : int nutf8 = 0;
178 8722 : int m = 0;
179 165380 : for (size_t i = 0; v[i]; i++) {
180 156658 : 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 156650 : } else if ((v[i] & 0xE0) == 0xC0) {
187 0 : nutf8 = 1;
188 0 : if ((v[i] & 0x1E) == 0)
189 0 : goto badutf8;
190 156650 : } else if ((v[i] & 0xF0) == 0xE0) {
191 4 : nutf8 = 2;
192 4 : if ((v[i] & 0x0F) == 0)
193 0 : m = 0x20;
194 156646 : } else if ((v[i] & 0xF8) == 0xF0) {
195 0 : nutf8 = 3;
196 0 : if ((v[i] & 0x07) == 0)
197 0 : m = 0x30;
198 156646 : } else if ((v[i] & 0x80) != 0) {
199 0 : goto badutf8;
200 : }
201 : }
202 8722 : *esclen = 0;
203 8722 : 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 8722 : GDKsetenv(const char *name, const char *value)
217 : {
218 8722 : static const char hexdigits[] = "0123456789abcdef";
219 8722 : char *conval = NULL;
220 8722 : size_t esclen = 0;
221 8722 : 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 8722 : MT_lock_set(&GDKenvlock);
260 8722 : 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 8722 : MT_lock_unset(&GDKenvlock);
269 : }
270 8722 : BUN p = BUNfnd(GDKkey, name);
271 8722 : gdk_return rc;
272 8722 : if (p != BUN_NONE) {
273 2064 : rc = BUNreplace(GDKval, p + GDKval->hseqbase,
274 : conval ? conval : value, false);
275 : } else {
276 7690 : rc = BUNappend(GDKkey, name, false);
277 7690 : if (rc == GDK_SUCCEED) {
278 15380 : rc = BUNappend(GDKval, conval ? conval : value, false);
279 7690 : 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 8722 : assert(BATcount(GDKval) == BATcount(GDKkey));
290 8722 : GDKfree(conval);
291 8722 : return rc;
292 : }
293 :
294 : gdk_return
295 135 : GDKcopyenv(BAT **key, BAT **val, bool writable)
296 : {
297 135 : BAT *k, *v;
298 :
299 135 : if (key == NULL || val == NULL) {
300 0 : GDKerror("called incorrectly.\n");
301 0 : return GDK_FAIL;
302 : }
303 135 : k = COLcopy(GDKkey, GDKkey->ttype, writable, TRANSIENT);
304 135 : v = COLcopy(GDKval, GDKval->ttype, writable, TRANSIENT);
305 135 : if (k == NULL || v == NULL) {
306 0 : BBPreclaim(k);
307 0 : BBPreclaim(v);
308 0 : return GDK_FAIL;
309 : }
310 135 : *key = k;
311 135 : *val = v;
312 135 : 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 902 : GDKlog(FILE *lockFile, const char *format, ...)
345 : {
346 902 : va_list ap;
347 902 : char *p = 0, buf[1024];
348 902 : time_t tm = time(0);
349 : #if defined(HAVE_CTIME_R3) || defined(HAVE_CTIME_R)
350 902 : char tbuf[26];
351 : #endif
352 902 : char *ctm;
353 :
354 902 : if (MT_pagesize() == 0 || lockFile == NULL)
355 1 : return;
356 :
357 901 : va_start(ap, format);
358 901 : vsnprintf(buf, sizeof(buf), format, ap);
359 901 : va_end(ap);
360 :
361 : /* remove forbidden characters from message */
362 901 : for (p = buf; (p = strchr(p, '\n')) != NULL; *p = ' ')
363 : ;
364 901 : for (p = buf; (p = strchr(p, '@')) != NULL; *p = ' ')
365 : ;
366 :
367 901 : 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 901 : ctm = ctime_r(&tm, tbuf);
375 : #endif
376 901 : fprintf(lockFile, "USR=%d PID=%d TIME=%.24s @ %s\n", (int) getuid(), (int) getpid(), ctm, buf);
377 901 : 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 359 : BATSIGinit(void)
395 : {
396 : #ifdef HAVE_SIGACTION
397 359 : struct sigaction sa;
398 359 : sigemptyset(&sa.sa_mask);
399 359 : sa.sa_flags = 0;
400 : #ifdef SIGPIPE
401 359 : sa.sa_handler = SIG_IGN;
402 359 : sigaction(SIGPIPE, &sa, NULL);
403 : #endif
404 : #ifdef SIGHUP
405 359 : sa.sa_handler = GDKtracer_reinit_basic;
406 359 : 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 359 : }
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 360 : MT_init(void)
464 : {
465 360 : 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 360 : _MT_pagesize = (size_t)sysconf(_SC_PAGESIZE);
488 : #endif
489 360 : 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 360 : _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 360 : FILE *fc;
561 360 : char buf[1024];
562 360 : char cgr1[1024] = "/sys/fs/cgroup/memory";
563 360 : char cgr2[1024] = "/sys/fs/cgroup";
564 360 : fc = fopen("/proc/self/mountinfo", "r");
565 360 : if (fc != NULL) {
566 11880 : while (fgets(buf, (int) sizeof(buf), fc) != NULL) {
567 11520 : char *p, *cgr;
568 11520 : if ((p = strstr(buf, " - cgroup ")) != NULL &&
569 0 : strstr(p, "memory") != NULL)
570 : cgr = cgr1;
571 11520 : else if (strstr(buf, " - cgroup2 ") != NULL)
572 : cgr = cgr2;
573 : else
574 11160 : continue;
575 : /* buf points at mount ID */
576 360 : p = strchr(buf, ' ');
577 360 : if (p == NULL)
578 : break;
579 360 : p++;
580 : /* p points at parent ID */
581 360 : p = strchr(p, ' ');
582 360 : if (p == NULL)
583 : break;
584 360 : p++;
585 : /* p points at major:minor */
586 360 : p = strchr(p, ' ');
587 360 : if (p == NULL)
588 : break;
589 360 : p++;
590 : /* p points at root */
591 360 : p = strchr(p, ' ');
592 360 : if (p == NULL)
593 : break;
594 360 : p++;
595 : /* p points at mount point */
596 360 : char *dir = p;
597 360 : p = strchr(p, ' ');
598 360 : if (p == NULL)
599 : break;
600 360 : *p = 0;
601 360 : strcpy_len(cgr, dir, 1024);
602 : }
603 360 : fclose(fc);
604 : }
605 360 : fc = fopen("/proc/self/cgroup", "r");
606 360 : 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 360 : while (fgets(buf, (int) sizeof(buf), fc) != NULL) {
626 360 : char pth[1024];
627 360 : char *p, *q;
628 360 : bool success = false; /* true if we can open any file */
629 360 : FILE *f;
630 360 : uint64_t mem;
631 :
632 360 : p = strchr(buf, '\n');
633 360 : if (p == NULL)
634 : break;
635 360 : *p = 0;
636 360 : if (strncmp(buf, "0::", 3) == 0) {
637 : /* cgroup v2 entry */
638 360 : p = stpcpy(pth, cgr2);
639 360 : q = stpcpy(stpcpy(p, buf + 3), "/");
640 : /* hard limit */
641 360 : strcpy(q, "memory.max");
642 360 : f = fopen(pth, "r");
643 360 : 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 360 : if (f != NULL) {
658 360 : 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 360 : success = true;
664 : /* assume "max" if not a number */
665 360 : fclose(f);
666 : }
667 : /* soft high limit */
668 360 : strcpy(q, "memory.high");
669 360 : f = fopen(pth, "r");
670 360 : if (f != NULL) {
671 360 : 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 360 : success = true;
677 : /* assume "max" if not a number */
678 360 : 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 360 : strcpy(q, "memory.swap.max");
685 360 : f = fopen(pth, "r");
686 360 : if (f != NULL) {
687 360 : 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 360 : success = true;
693 360 : 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 360 : if (success)
790 : break;
791 : }
792 360 : fclose(fc);
793 : }
794 : #endif
795 :
796 : #if defined(HAVE_SYS_RESOURCE_H) && defined(HAVE_GETRLIMIT) && defined(RLIMIT_AS)
797 360 : struct rlimit l;
798 : /* address space (virtual memory) limit */
799 360 : if (getrlimit(RLIMIT_AS, &l) == 0
800 360 : && 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 360 : }
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 364 : GDKsetdebug(unsigned debug)
828 : {
829 364 : ATOMIC_SET(&GDKdebug, debug);
830 364 : if (debug & ACCELMASK)
831 0 : GDKtracer_set_component_level("accelerator", "debug");
832 : else
833 364 : GDKtracer_reset_component_level("accelerator");
834 364 : if (debug & ALGOMASK)
835 0 : GDKtracer_set_component_level("algo", "debug");
836 : else
837 364 : GDKtracer_reset_component_level("algo");
838 364 : if (debug & ALLOCMASK)
839 0 : GDKtracer_set_component_level("alloc", "debug");
840 : else
841 364 : GDKtracer_reset_component_level("alloc");
842 364 : if (debug & BATMASK)
843 0 : GDKtracer_set_component_level("bat", "debug");
844 : else
845 364 : GDKtracer_reset_component_level("bat");
846 364 : if (debug & CHECKMASK)
847 353 : GDKtracer_set_component_level("check", "debug");
848 : else
849 11 : GDKtracer_reset_component_level("check");
850 364 : if (debug & DELTAMASK)
851 0 : GDKtracer_set_component_level("delta", "debug");
852 : else
853 364 : GDKtracer_reset_component_level("delta");
854 364 : if (debug & HEAPMASK)
855 0 : GDKtracer_set_component_level("heap", "debug");
856 : else
857 364 : GDKtracer_reset_component_level("heap");
858 364 : if (debug & IOMASK)
859 0 : GDKtracer_set_component_level("io", "debug");
860 : else
861 364 : GDKtracer_reset_component_level("io");
862 364 : if (debug & LOADMASK)
863 0 : GDKtracer_set_component_level("mal_loader", "debug");
864 : else
865 364 : GDKtracer_reset_component_level("mal_loader");
866 364 : if (debug & PARMASK)
867 0 : GDKtracer_set_component_level("par", "debug");
868 : else
869 364 : GDKtracer_reset_component_level("par");
870 364 : if (debug & PERFMASK)
871 0 : GDKtracer_set_component_level("perf", "debug");
872 : else
873 364 : GDKtracer_reset_component_level("perf");
874 364 : if (debug & TEMMASK)
875 0 : GDKtracer_set_component_level("tem", "debug");
876 : else
877 364 : GDKtracer_reset_component_level("tem");
878 364 : if (debug & THRDMASK)
879 0 : GDKtracer_set_component_level("thrd", "debug");
880 : else
881 364 : GDKtracer_reset_component_level("thrd");
882 364 : if (debug & TMMASK)
883 0 : GDKtracer_set_component_level("tm", "debug");
884 : else
885 364 : GDKtracer_reset_component_level("tm");
886 364 : }
887 :
888 : unsigned
889 17 : GDKgetdebug(void)
890 : {
891 17 : ATOMIC_BASE_TYPE debug = ATOMIC_GET(&GDKdebug);
892 17 : const char *lvl;
893 17 : lvl = GDKtracer_get_component_level("accelerator");
894 17 : if (lvl && strcmp(lvl, "debug") == 0)
895 0 : debug |= ACCELMASK;
896 17 : lvl = GDKtracer_get_component_level("algo");
897 17 : if (lvl && strcmp(lvl, "debug") == 0)
898 0 : debug |= ALGOMASK;
899 17 : lvl = GDKtracer_get_component_level("alloc");
900 17 : if (lvl && strcmp(lvl, "debug") == 0)
901 0 : debug |= ALLOCMASK;
902 17 : lvl = GDKtracer_get_component_level("bat");
903 17 : if (lvl && strcmp(lvl, "debug") == 0)
904 0 : debug |= BATMASK;
905 17 : lvl = GDKtracer_get_component_level("check");
906 17 : if (lvl && strcmp(lvl, "debug") == 0)
907 0 : debug |= CHECKMASK;
908 17 : lvl = GDKtracer_get_component_level("delta");
909 17 : if (lvl && strcmp(lvl, "debug") == 0)
910 0 : debug |= DELTAMASK;
911 17 : lvl = GDKtracer_get_component_level("heap");
912 17 : if (lvl && strcmp(lvl, "debug") == 0)
913 0 : debug |= HEAPMASK;
914 17 : lvl = GDKtracer_get_component_level("io");
915 17 : if (lvl && strcmp(lvl, "debug") == 0)
916 0 : debug |= IOMASK;
917 17 : lvl = GDKtracer_get_component_level("mal_loader");
918 17 : if (lvl && strcmp(lvl, "debug") == 0)
919 0 : debug |= LOADMASK;
920 17 : lvl = GDKtracer_get_component_level("par");
921 17 : if (lvl && strcmp(lvl, "debug") == 0)
922 0 : debug |= PARMASK;
923 17 : lvl = GDKtracer_get_component_level("perf");
924 17 : if (lvl && strcmp(lvl, "debug") == 0)
925 0 : debug |= PERFMASK;
926 17 : lvl = GDKtracer_get_component_level("tem");
927 17 : if (lvl && strcmp(lvl, "debug") == 0)
928 0 : debug |= TEMMASK;
929 17 : lvl = GDKtracer_get_component_level("thrd");
930 17 : if (lvl && strcmp(lvl, "debug") == 0)
931 0 : debug |= THRDMASK;
932 17 : lvl = GDKtracer_get_component_level("tm");
933 17 : if (lvl && strcmp(lvl, "debug") == 0)
934 0 : debug |= TMMASK;
935 17 : return (unsigned) debug;
936 : }
937 :
938 : static bool Mbedded = true;
939 : bool
940 27901595 : GDKembedded(void)
941 : {
942 27901595 : return Mbedded;
943 : }
944 :
945 : static MT_Id mainpid;
946 :
947 : gdk_return
948 359 : GDKinit(opt *set, int setlen, bool embedded, const char *caller_revision)
949 : {
950 359 : static bool first = true;
951 359 : const char *dbpath;
952 359 : const char *dbtrace;
953 359 : const char *p;
954 359 : opt *n;
955 359 : int i, nlen = 0;
956 359 : char buf[16];
957 :
958 359 : if (caller_revision) {
959 348 : p = mercurial_revision();
960 348 : if (p && strcmp(p, caller_revision) != 0) {
961 0 : GDKerror("incompatible versions: caller is %s, GDK is %s\n", caller_revision, p);
962 0 : return GDK_FAIL;
963 : }
964 : }
965 :
966 359 : ATOMIC_SET(&GDKstopped, 0);
967 :
968 359 : if (BBPchkfarms() != GDK_SUCCEED)
969 : return GDK_FAIL;
970 :
971 359 : if (GDKinmemory(0)) {
972 : dbpath = dbtrace = NULL;
973 : } else {
974 358 : dbpath = mo_find_option(set, setlen, "gdk_dbpath");
975 358 : dbtrace = mo_find_option(set, setlen, "gdk_dbtrace");
976 : }
977 359 : Mbedded = embedded;
978 : /* some sanity checks (should also find if symbols are not defined) */
979 359 : static_assert(sizeof(int) == sizeof(int32_t),
980 : "int is not equal in size to int32_t");
981 359 : static_assert(sizeof(char) == SIZEOF_CHAR,
982 : "error in configure: bad value for SIZEOF_CHAR");
983 359 : static_assert(sizeof(short) == SIZEOF_SHORT,
984 : "error in configure: bad value for SIZEOF_SHORT");
985 359 : static_assert(sizeof(int) == SIZEOF_INT,
986 : "error in configure: bad value for SIZEOF_INT");
987 359 : static_assert(sizeof(long) == SIZEOF_LONG,
988 : "error in configure: bad value for SIZEOF_LONG");
989 359 : static_assert(sizeof(lng) == SIZEOF_LNG,
990 : "error in configure: bad value for SIZEOF_LNG");
991 : #ifdef HAVE_HGE
992 359 : static_assert(sizeof(hge) == SIZEOF_HGE,
993 : "error in configure: bad value for SIZEOF_HGE");
994 : #endif
995 359 : static_assert(sizeof(dbl) == SIZEOF_DOUBLE,
996 : "error in configure: bad value for SIZEOF_DOUBLE");
997 359 : static_assert(sizeof(oid) == SIZEOF_OID,
998 : "error in configure: bad value for SIZEOF_OID");
999 359 : static_assert(sizeof(void *) == SIZEOF_VOID_P,
1000 : "error in configure: bad value for SIZEOF_VOID_P");
1001 359 : static_assert(sizeof(size_t) == SIZEOF_SIZE_T,
1002 : "error in configure: bad value for SIZEOF_SIZE_T");
1003 359 : static_assert(SIZEOF_OID == SIZEOF_INT || SIZEOF_OID == SIZEOF_LNG,
1004 : "SIZEOF_OID should be equal to SIZEOF_INT or SIZEOF_LNG");
1005 359 : static_assert(sizeof(uuid) == 16,
1006 : "sizeof(uuid) should be equal to 16");
1007 :
1008 359 : if (first) {
1009 : /* some things are really only initialized once */
1010 349 : if (!MT_thread_init()) {
1011 0 : TRC_CRITICAL(GDK, "MT_thread_init failed\n");
1012 0 : return GDK_FAIL;
1013 : }
1014 :
1015 2859357 : for (i = 0; i <= BBP_BATMASK; i++) {
1016 2859008 : char name[MT_NAME_LEN];
1017 2859008 : snprintf(name, sizeof(name), "GDKswapLock%d", i);
1018 2859008 : MT_lock_init(&GDKbatLock[i].swap, name);
1019 2859008 : snprintf(name, sizeof(name), "GDKswapCond%d", i);
1020 2859008 : MT_cond_init(&GDKbatLock[i].cond, name);
1021 : }
1022 349 : if (mnstr_init() < 0) {
1023 0 : TRC_CRITICAL(GDK, "mnstr_init failed\n");
1024 0 : return GDK_FAIL;
1025 : }
1026 : } else {
1027 : /* BBP was locked by BBPexit() */
1028 : //BBPunlock();
1029 359 : }
1030 359 : mainpid = MT_getpid();
1031 :
1032 359 : GDKtracer_init(dbpath, dbtrace);
1033 359 : errno = 0;
1034 359 : if (!GDKinmemory(0) && !GDKenvironment(dbpath))
1035 : return GDK_FAIL;
1036 :
1037 359 : MT_init_posix();
1038 359 : if (THRinit() < 0)
1039 : return GDK_FAIL;
1040 : #ifndef NATIVE_WIN32
1041 359 : BATSIGinit();
1042 : #endif
1043 359 : MT_init();
1044 :
1045 : /* now try to lock the database: go through all farms, and if
1046 : * we see a new directory, lock it */
1047 11783 : for (int farmid = 0; farmid < MAXFARMS; farmid++) {
1048 11426 : if (BBPfarms[farmid].dirname != NULL) {
1049 1441 : bool skip = false;
1050 1441 : for (int j = 0; j < farmid; j++) {
1051 892 : if (BBPfarms[j].dirname != NULL &&
1052 892 : strcmp(BBPfarms[farmid].dirname, BBPfarms[j].dirname) == 0) {
1053 : skip = true;
1054 : break;
1055 : }
1056 : }
1057 1059 : if (!skip && GDKlockHome(farmid) != GDK_SUCCEED)
1058 : return GDK_FAIL;
1059 : }
1060 : }
1061 :
1062 : /* Mserver by default takes 80% of all memory as a default */
1063 : #if SIZEOF_SIZE_T == 4
1064 : if ((double) MT_npages() * (double) MT_pagesize() * 0.815 >= (double) GDK_VM_MAXSIZE)
1065 : GDK_mem_maxsize = GDK_VM_MAXSIZE;
1066 : else
1067 : #endif
1068 357 : GDK_mem_maxsize = (size_t) ((double) MT_npages() * (double) MT_pagesize() * 0.815);
1069 357 : const char *allow = mo_find_option(set, setlen, "allow_hge_upgrade");
1070 357 : const char *procwalxit = mo_find_option(set, setlen, "process-wal-and-exit");
1071 1063 : if (BBPinit(allow && strcmp(allow, "yes") == 0, procwalxit && strcmp(procwalxit, "yes") == 0) != GDK_SUCCEED)
1072 : return GDK_FAIL;
1073 357 : first = false;
1074 :
1075 357 : if (GDK_mem_maxsize / 16 < GDK_mmap_minsize_transient) {
1076 347 : GDK_mmap_minsize_transient = MAX(MMAP_PAGESIZE, GDK_mem_maxsize / 16);
1077 347 : if (GDK_mmap_minsize_persistent > GDK_mmap_minsize_transient)
1078 0 : GDK_mmap_minsize_persistent = GDK_mmap_minsize_transient;
1079 : }
1080 :
1081 357 : n = (opt *) malloc(setlen * sizeof(opt));
1082 357 : if (n == NULL) {
1083 0 : GDKsyserror("malloc failed\n");
1084 0 : return GDK_FAIL;
1085 : }
1086 :
1087 4322 : for (i = 0; i < setlen; i++) {
1088 17880 : bool done = false;
1089 :
1090 17880 : for (int j = 0; j < nlen; j++) {
1091 14619 : if (strcmp(n[j].name, set[i].name) == 0) {
1092 704 : if (n[j].kind < set[i].kind) {
1093 704 : n[j] = set[i];
1094 : }
1095 : done = true;
1096 : break;
1097 : }
1098 : }
1099 3261 : if (!done) {
1100 3261 : n[nlen] = set[i];
1101 3261 : nlen++;
1102 : }
1103 : }
1104 : /* check some options before creating our first BAT */
1105 3618 : for (i = 0; i < nlen; i++) {
1106 3261 : if (strcmp("gdk_mem_maxsize", n[i].name) == 0) {
1107 0 : GDK_mem_maxsize = (size_t) strtoll(n[i].value, NULL, 10);
1108 0 : GDK_mem_maxsize = MAX(1 << 26, GDK_mem_maxsize);
1109 0 : if (GDK_mem_maxsize / 16 < GDK_mmap_minsize_transient)
1110 0 : GDK_mmap_minsize_transient = MAX(MMAP_PAGESIZE, GDK_mem_maxsize / 16);
1111 0 : if (GDK_mmap_minsize_persistent > GDK_mmap_minsize_transient)
1112 0 : GDK_mmap_minsize_persistent = GDK_mmap_minsize_transient;
1113 3261 : } else if (strcmp("gdk_vm_maxsize", n[i].name) == 0) {
1114 197 : GDK_vm_maxsize = (size_t) strtoll(n[i].value, NULL, 10);
1115 197 : GDK_vm_maxsize = MAX(1 << 30, GDK_vm_maxsize);
1116 3064 : } else if (strcmp("gdk_mmap_minsize_persistent", n[i].name) == 0) {
1117 0 : GDK_mmap_minsize_persistent = (size_t) strtoll(n[i].value, NULL, 10);
1118 3064 : } else if (strcmp("gdk_mmap_minsize_transient", n[i].name) == 0) {
1119 0 : GDK_mmap_minsize_transient = (size_t) strtoll(n[i].value, NULL, 10);
1120 3064 : } else if (strcmp("gdk_mmap_pagesize", n[i].name) == 0) {
1121 0 : GDK_mmap_pagesize = (size_t) strtoll(n[i].value, NULL, 10);
1122 0 : if (GDK_mmap_pagesize < 1 << 12 ||
1123 0 : GDK_mmap_pagesize > 1 << 20 ||
1124 : /* x & (x - 1): turn off rightmost 1 bit;
1125 : * i.e. if result is zero, x is power of
1126 : * two */
1127 0 : (GDK_mmap_pagesize & (GDK_mmap_pagesize - 1)) != 0) {
1128 0 : free(n);
1129 0 : TRC_CRITICAL(GDK, "gdk_mmap_pagesize must be power of 2 between 2**12 and 2**20\n");
1130 0 : return GDK_FAIL;
1131 : }
1132 : }
1133 : }
1134 :
1135 357 : GDKkey = COLnew(0, TYPE_str, 100, TRANSIENT);
1136 357 : GDKval = COLnew(0, TYPE_str, 100, TRANSIENT);
1137 357 : if (GDKkey == NULL || GDKval == NULL) {
1138 0 : free(n);
1139 0 : TRC_CRITICAL(GDK, "Could not create environment BATs");
1140 0 : return GDK_FAIL;
1141 : }
1142 714 : if (BBPrename(GDKkey, "environment_key") != 0 ||
1143 357 : BBPrename(GDKval, "environment_val") != 0) {
1144 0 : free(n);
1145 0 : TRC_CRITICAL(GDK, "BBPrename of environment BATs failed");
1146 0 : return GDK_FAIL;
1147 : }
1148 357 : BBP_pid(GDKkey->batCacheid) = 0;
1149 357 : BBP_pid(GDKval->batCacheid) = 0;
1150 :
1151 : /* store options into environment BATs */
1152 3618 : for (i = 0; i < nlen; i++)
1153 3261 : if (GDKsetenv(n[i].name, n[i].value) != GDK_SUCCEED) {
1154 0 : TRC_CRITICAL(GDK, "GDKsetenv %s failed", n[i].name);
1155 0 : free(n);
1156 0 : return GDK_FAIL;
1157 : }
1158 357 : free(n);
1159 :
1160 357 : GDKnr_threads = GDKgetenv_int("gdk_nr_threads", 0);
1161 357 : if (GDKnr_threads == 0) {
1162 356 : GDKnr_threads = MT_check_nr_cores();
1163 356 : snprintf(buf, sizeof(buf), "%d", GDKnr_threads);
1164 356 : if (GDKsetenv("gdk_nr_threads", buf) != GDK_SUCCEED) {
1165 0 : TRC_CRITICAL(GDK, "GDKsetenv gdk_nr_threads failed");
1166 0 : return GDK_FAIL;
1167 : }
1168 : }
1169 357 : if (GDKnr_threads > THREADS)
1170 0 : GDKnr_threads = THREADS;
1171 :
1172 357 : if (!GDKinmemory(0)) {
1173 356 : if ((p = GDKgetenv("gdk_dbpath")) != NULL &&
1174 356 : (p = strrchr(p, DIR_SEP)) != NULL) {
1175 356 : if (GDKsetenv("gdk_dbname", p + 1) != GDK_SUCCEED) {
1176 0 : TRC_CRITICAL(GDK, "GDKsetenv gdk_dbname failed");
1177 0 : return GDK_FAIL;
1178 : }
1179 : #if DIR_SEP != '/' /* on Windows look for different separator */
1180 : } else if ((p = GDKgetenv("gdk_dbpath")) != NULL &&
1181 : (p = strrchr(p, '/')) != NULL) {
1182 : if (GDKsetenv("gdk_dbname", p + 1) != GDK_SUCCEED) {
1183 : TRC_CRITICAL(GDK, "GDKsetenv gdk_dbname failed");
1184 : return GDK_FAIL;
1185 : }
1186 : #endif
1187 : }
1188 1 : } else if (GDKgetenv("gdk_dbname") == NULL) {
1189 1 : if (GDKsetenv("gdk_dbname", "in-memory") != GDK_SUCCEED) {
1190 0 : TRC_CRITICAL(GDK, "GDKsetenv gdk_dbname failed");
1191 0 : return GDK_FAIL;
1192 : }
1193 : }
1194 357 : if (GDKgetenv("gdk_vm_maxsize") == NULL) {
1195 160 : snprintf(buf, sizeof(buf), "%zu", GDK_vm_maxsize);
1196 160 : if (GDKsetenv("gdk_vm_maxsize", buf) != GDK_SUCCEED) {
1197 0 : TRC_CRITICAL(GDK, "GDKsetenv gdk_vm_maxsize failed");
1198 0 : return GDK_FAIL;
1199 : }
1200 : }
1201 357 : if (GDKgetenv("gdk_mem_maxsize") == NULL) {
1202 357 : snprintf(buf, sizeof(buf), "%zu", GDK_mem_maxsize);
1203 357 : if (GDKsetenv("gdk_mem_maxsize", buf) != GDK_SUCCEED) {
1204 0 : TRC_CRITICAL(GDK, "GDKsetenv gdk_mem_maxsize failed");
1205 0 : return GDK_FAIL;
1206 : }
1207 : }
1208 357 : if (GDKgetenv("gdk_mmap_minsize_persistent") == NULL) {
1209 357 : snprintf(buf, sizeof(buf), "%zu", GDK_mmap_minsize_persistent);
1210 357 : if (GDKsetenv("gdk_mmap_minsize_persistent", buf) != GDK_SUCCEED) {
1211 0 : TRC_CRITICAL(GDK, "GDKsetenv gdk_mmap_minsize_persistent failed");
1212 0 : return GDK_FAIL;
1213 : }
1214 : }
1215 357 : if (GDKgetenv("gdk_mmap_minsize_transient") == NULL) {
1216 357 : snprintf(buf, sizeof(buf), "%zu", GDK_mmap_minsize_transient);
1217 357 : if (GDKsetenv("gdk_mmap_minsize_transient", buf) != GDK_SUCCEED) {
1218 0 : TRC_CRITICAL(GDK, "GDKsetenv gdk_mmap_minsize_transient failed");
1219 0 : return GDK_FAIL;
1220 : }
1221 : }
1222 357 : if (GDKgetenv("gdk_mmap_pagesize") == NULL) {
1223 357 : snprintf(buf, sizeof(buf), "%zu", GDK_mmap_pagesize);
1224 357 : if (GDKsetenv("gdk_mmap_pagesize", buf) != GDK_SUCCEED) {
1225 0 : TRC_CRITICAL(GDK, "GDKsetenv gdk_mmap_pagesize failed");
1226 0 : return GDK_FAIL;
1227 : }
1228 : }
1229 357 : if (GDKgetenv("monet_pid") == NULL) {
1230 357 : snprintf(buf, sizeof(buf), "%d", (int) getpid());
1231 357 : if (GDKsetenv("monet_pid", buf) != GDK_SUCCEED) {
1232 0 : TRC_CRITICAL(GDK, "GDKsetenv monet_pid failed");
1233 0 : return GDK_FAIL;
1234 : }
1235 : }
1236 357 : if (GDKsetenv("revision", mercurial_revision()) != GDK_SUCCEED) {
1237 0 : TRC_CRITICAL(GDK, "GDKsetenv revision failed");
1238 0 : return GDK_FAIL;
1239 : }
1240 357 : gdk_unique_estimate_keep_fraction = 0;
1241 357 : if ((p = GDKgetenv("gdk_unique_estimate_keep_fraction")) != NULL)
1242 0 : gdk_unique_estimate_keep_fraction = (BUN) strtoll(p, NULL, 10);
1243 357 : if (gdk_unique_estimate_keep_fraction == 0)
1244 357 : gdk_unique_estimate_keep_fraction = GDK_UNIQUE_ESTIMATE_KEEP_FRACTION;
1245 357 : hash_destroy_uniques_fraction = 0;
1246 357 : if ((p = GDKgetenv("hash_destroy_uniques_fraction")) != NULL)
1247 0 : hash_destroy_uniques_fraction = (BUN) strtoll(p, NULL, 10);
1248 357 : if (hash_destroy_uniques_fraction == 0)
1249 357 : hash_destroy_uniques_fraction = HASH_DESTROY_UNIQUES_FRACTION;
1250 357 : no_hash_select_fraction = 0;
1251 357 : if ((p = GDKgetenv("no_hash_select_fraction")) != NULL)
1252 0 : no_hash_select_fraction = (dbl) strtoll(p, NULL, 10);
1253 357 : if (no_hash_select_fraction == 0)
1254 357 : no_hash_select_fraction = NO_HASH_SELECT_FRACTION;
1255 357 : hash_destroy_chain_length = 0;
1256 357 : if ((p = GDKgetenv("hash_destroy_chain_length")) != NULL)
1257 0 : hash_destroy_chain_length = (BUN) strtoll(p, NULL, 10);
1258 357 : if (hash_destroy_chain_length == 0)
1259 357 : hash_destroy_chain_length = HASH_DESTROY_CHAIN_LENGTH;
1260 :
1261 : return GDK_SUCCEED;
1262 : }
1263 :
1264 : int GDKnr_threads = 0;
1265 : static ATOMIC_TYPE GDKnrofthreads = ATOMIC_VAR_INIT(0);
1266 :
1267 : bool
1268 29181826 : GDKexiting(void)
1269 : {
1270 29181826 : return (bool) (ATOMIC_GET(&GDKstopped) > 0);
1271 : }
1272 :
1273 : void
1274 356 : GDKprepareExit(void)
1275 : {
1276 356 : ATOMIC_ADD(&GDKstopped, 1);
1277 :
1278 356 : if (MT_getpid() == mainpid) {
1279 355 : TRC_DEBUG_IF(THRD)
1280 0 : dump_threads();
1281 355 : join_detached_threads();
1282 : }
1283 356 : }
1284 :
1285 : void
1286 355 : GDKreset(int status)
1287 : {
1288 355 : assert(GDKexiting());
1289 :
1290 355 : if (GDKembedded())
1291 : // In the case of a restarted embedded database, GDKstopped has to be reset as well.
1292 11 : ATOMIC_SET(&GDKstopped, 0);
1293 :
1294 355 : if (GDKkey) {
1295 355 : BBPunfix(GDKkey->batCacheid);
1296 355 : GDKkey = NULL;
1297 : }
1298 355 : if (GDKval) {
1299 355 : BBPunfix(GDKval->batCacheid);
1300 355 : GDKval = NULL;
1301 : }
1302 :
1303 355 : join_detached_threads();
1304 :
1305 355 : MT_lock_set(&GDKenvlock);
1306 355 : while (orig_value) {
1307 0 : struct orig_value *ov = orig_value;
1308 0 : orig_value = orig_value->next;
1309 0 : GDKfree(ov);
1310 : }
1311 355 : MT_lock_unset(&GDKenvlock);
1312 :
1313 355 : if (status == 0) {
1314 : /* they had their chance, now kill them */
1315 355 : bool killed = MT_kill_threads();
1316 : /* all threads ceased running, now we can clean up */
1317 355 : if (!killed) {
1318 : /* we can't clean up after killing threads */
1319 355 : BBPexit();
1320 : }
1321 355 : GDKlog(GET_GDKLOCK(PERSISTENT), GDKLOGOFF);
1322 :
1323 11715 : for (int farmid = 0; farmid < MAXFARMS; farmid++) {
1324 11360 : if (BBPfarms[farmid].dirname != NULL) {
1325 2094 : bool skip = false;
1326 2094 : for (int j = 0; j < farmid; j++) {
1327 1042 : if (BBPfarms[j].dirname != NULL &&
1328 0 : strcmp(BBPfarms[farmid].dirname, BBPfarms[j].dirname) == 0) {
1329 : skip = true;
1330 : break;
1331 : }
1332 : }
1333 1052 : if (!skip)
1334 1052 : GDKunlockHome(farmid);
1335 1052 : if (BBPfarms[farmid].dirname) {
1336 1052 : GDKfree((char*)BBPfarms[farmid].dirname);
1337 1052 : BBPfarms[farmid].dirname = NULL;
1338 : }
1339 : }
1340 : }
1341 :
1342 : #ifdef LOCK_STATS
1343 : TRC_DEBUG_IF(TEM) GDKlockstatistics(1);
1344 : #endif
1345 355 : ATOMIC_SET(&GDKdebug, 0);
1346 355 : GDK_mmap_minsize_persistent = MMAP_MINSIZE_PERSISTENT;
1347 355 : GDK_mmap_minsize_transient = MMAP_MINSIZE_TRANSIENT;
1348 355 : GDK_mmap_pagesize = MMAP_PAGESIZE;
1349 355 : GDK_mem_maxsize = (size_t) ((double) MT_npages() * (double) MT_pagesize() * 0.815);
1350 355 : GDK_vm_maxsize = GDK_VM_MAXSIZE;
1351 355 : GDKatomcnt = TYPE_blob + 1;
1352 :
1353 355 : if (GDK_mem_maxsize / 16 < GDK_mmap_minsize_transient) {
1354 355 : GDK_mmap_minsize_transient = GDK_mem_maxsize / 16;
1355 355 : if (GDK_mmap_minsize_persistent > GDK_mmap_minsize_transient)
1356 0 : GDK_mmap_minsize_persistent = GDK_mmap_minsize_transient;
1357 : }
1358 :
1359 355 : GDKnr_threads = 0;
1360 355 : ATOMIC_SET(&GDKnrofthreads, 0);
1361 355 : close_stream(GDKstdout);
1362 355 : close_stream(GDKstdin);
1363 355 : GDKstdout = NULL;
1364 355 : GDKstdin = NULL;
1365 :
1366 355 : gdk_bbp_reset();
1367 : }
1368 355 : ATOMunknown_clean();
1369 :
1370 : /* stop GDKtracer */
1371 355 : GDKtracer_stop();
1372 355 : }
1373 :
1374 : /*
1375 : * All semaphores used by the application should be mentioned here.
1376 : * They are initialized during system initialization.
1377 : */
1378 :
1379 : batlock_t GDKbatLock[BBP_BATMASK + 1];
1380 :
1381 : /*
1382 : * @+ Concurrency control
1383 : * Concurrency control requires actions at several levels of the
1384 : * system. First, it should be ensured that each database is
1385 : * controlled by a single server process (group). Subsequent attempts
1386 : * should be stopped. This is regulated through file locking against
1387 : * ".gdk_lock".
1388 : *
1389 : * Before the locks and threads are initiated, we cannot use the
1390 : * normal routines yet. So we have a local fatal here instead of
1391 : * GDKfatal.
1392 : */
1393 : static gdk_return
1394 549 : GDKlockHome(int farmid)
1395 : {
1396 549 : int fd;
1397 549 : struct stat st;
1398 549 : char gdklockpath[1024];
1399 549 : FILE *GDKlockFile;
1400 :
1401 549 : assert(BBPfarms[farmid].dirname != NULL);
1402 549 : assert(BBPfarms[farmid].lock_file == NULL);
1403 :
1404 549 : if (GDKfilepath(gdklockpath, sizeof(gdklockpath), farmid, NULL, GDKLOCK, NULL) != GDK_SUCCEED)
1405 : return GDK_FAIL;
1406 :
1407 : /*
1408 : * Obtain the global database lock.
1409 : */
1410 549 : if (MT_stat(BBPfarms[farmid].dirname, &st) < 0 &&
1411 0 : GDKcreatedir(gdklockpath) != GDK_SUCCEED) {
1412 0 : TRC_CRITICAL(GDK, "could not create %s\n",
1413 : BBPfarms[farmid].dirname);
1414 0 : return GDK_FAIL;
1415 : }
1416 549 : if ((fd = MT_lockf(gdklockpath, F_TLOCK)) < 0) {
1417 2 : TRC_CRITICAL(GDK, "Database lock '%s' denied\n",
1418 : gdklockpath);
1419 2 : return GDK_FAIL;
1420 : }
1421 :
1422 : /* now we have the lock on the database and are the only
1423 : * process allowed in this section */
1424 :
1425 547 : if ((GDKlockFile = fdopen(fd, "r+")) == NULL) {
1426 0 : GDKsyserror("Could not fdopen %s\n", gdklockpath);
1427 0 : close(fd);
1428 0 : return GDK_FAIL;
1429 : }
1430 :
1431 : /*
1432 : * Print the new process list in the global lock file.
1433 : */
1434 547 : if (fseek(GDKlockFile, 0, SEEK_SET) == -1) {
1435 0 : fclose(GDKlockFile);
1436 0 : TRC_CRITICAL(GDK, "Error while setting the file pointer on %s\n", gdklockpath);
1437 0 : return GDK_FAIL;
1438 : }
1439 547 : if (ftruncate(fileno(GDKlockFile), 0) < 0) {
1440 0 : fclose(GDKlockFile);
1441 0 : TRC_CRITICAL(GDK, "Could not truncate %s\n", gdklockpath);
1442 0 : return GDK_FAIL;
1443 : }
1444 547 : if (fflush(GDKlockFile) == EOF) {
1445 0 : fclose(GDKlockFile);
1446 0 : TRC_CRITICAL(GDK, "Could not flush %s\n", gdklockpath);
1447 0 : return GDK_FAIL;
1448 : }
1449 547 : GDKlog(GDKlockFile, GDKLOGON);
1450 547 : BBPfarms[farmid].lock_file = GDKlockFile;
1451 547 : return GDK_SUCCEED;
1452 : }
1453 :
1454 :
1455 : static void
1456 1052 : GDKunlockHome(int farmid)
1457 : {
1458 1052 : if (BBPfarms[farmid].lock_file) {
1459 545 : char gdklockpath[MAXPATH];
1460 :
1461 545 : if (GDKfilepath(gdklockpath, sizeof(gdklockpath), farmid, NULL, GDKLOCK, NULL) == GDK_SUCCEED)
1462 545 : MT_lockf(gdklockpath, F_ULOCK);
1463 545 : fclose(BBPfarms[farmid].lock_file);
1464 545 : BBPfarms[farmid].lock_file = NULL;
1465 : }
1466 1052 : }
1467 :
1468 : /*
1469 : * @+ Error handling
1470 : * Errors come in three flavors: warnings, non-fatal and fatal errors.
1471 : * A fatal error leaves a core dump behind after trying to safe the
1472 : * content of the relation. A non-fatal error returns a message to
1473 : * the user and aborts the current transaction. Fatal errors are also
1474 : * recorded on the system log for post-mortem analysis.
1475 : * In non-silent mode the errors are immediately sent to output, which
1476 : * makes it hard for upper layers to detect if an error was produced
1477 : * in the process. To facilitate such testing, a global error count is
1478 : * maintained on a thread basis, which can be read out by the function
1479 : * GDKerrorCount(); Furthermore, threads may have set their private
1480 : * error buffer.
1481 : */
1482 :
1483 : #define GDKERRLEN (1024+512)
1484 :
1485 : void
1486 10592500 : GDKclrerr(void)
1487 : {
1488 10592500 : char *buf;
1489 :
1490 10592500 : buf = GDKerrbuf;
1491 10614907 : if (buf)
1492 10607512 : *buf = 0;
1493 10614907 : }
1494 :
1495 : jmp_buf GDKfataljump;
1496 : str GDKfatalmsg;
1497 : bit GDKfataljumpenable = 0;
1498 :
1499 : /* coverity[+kill] */
1500 : void
1501 0 : GDKfatal(const char *format, ...)
1502 : {
1503 0 : char message[GDKERRLEN];
1504 0 : size_t len = strlen(GDKFATAL);
1505 0 : va_list ap;
1506 :
1507 0 : GDKtracer_set_component_level("io", "debug");
1508 : #ifndef NATIVE_WIN32
1509 0 : BATSIGinit();
1510 : #endif
1511 0 : if (!strncmp(format, GDKFATAL, len)) {
1512 : len = 0;
1513 : } else {
1514 0 : strcpy(message, GDKFATAL);
1515 : }
1516 0 : va_start(ap, format);
1517 0 : vsnprintf(message + len, sizeof(message) - (len + 2), format, ap);
1518 0 : va_end(ap);
1519 :
1520 : #ifndef __COVERITY__
1521 0 : if (GDKfataljumpenable) {
1522 : // in embedded mode, we really don't want to kill our host
1523 0 : GDKfatalmsg = GDKstrdup(message);
1524 0 : longjmp(GDKfataljump, 42);
1525 : } else
1526 : #endif
1527 : {
1528 0 : fputs(message, stderr);
1529 0 : fputs("\n", stderr);
1530 0 : fflush(stderr);
1531 :
1532 : /*
1533 : * Real errors should be saved in the log file for post-crash
1534 : * inspection.
1535 : */
1536 0 : if (GDKexiting()) {
1537 0 : fflush(stdout);
1538 0 : exit(1);
1539 : } else {
1540 0 : GDKlog(GET_GDKLOCK(PERSISTENT), "%s", message);
1541 : #ifdef COREDUMP
1542 : abort();
1543 : #else
1544 0 : exit(1);
1545 : #endif
1546 : }
1547 : }
1548 : }
1549 :
1550 :
1551 : lng
1552 189591313 : GDKusec(void)
1553 : {
1554 : /* Return the time in microseconds since an epoch. The epoch
1555 : * is currently midnight at the start of January 1, 1970, UTC. */
1556 : #if defined(NATIVE_WIN32)
1557 : FILETIME ft;
1558 : ULARGE_INTEGER f;
1559 : GetSystemTimeAsFileTime(&ft); /* time since Jan 1, 1601 */
1560 : f.LowPart = ft.dwLowDateTime;
1561 : f.HighPart = ft.dwHighDateTime;
1562 : /* there are 369 years, of which 89 are leap years from
1563 : * January 1, 1601 to January 1, 1970 which makes 134774 days;
1564 : * multiply that with the number of seconds in a day and the
1565 : * number of 100ns units in a second; subtract that from the
1566 : * value for the current time since January 1, 1601 to get the
1567 : * time since the Unix epoch */
1568 : f.QuadPart -= LL_CONSTANT(134774) * 24 * 60 * 60 * 10000000;
1569 : /* and convert to microseconds */
1570 : return (lng) (f.QuadPart / 10);
1571 : #elif defined(HAVE_CLOCK_GETTIME)
1572 189591313 : struct timespec ts;
1573 189591313 : (void) clock_gettime(CLOCK_REALTIME, &ts);
1574 189922463 : return (lng) (ts.tv_sec * LL_CONSTANT(1000000) + ts.tv_nsec / 1000);
1575 : #elif defined(HAVE_GETTIMEOFDAY)
1576 : struct timeval tv;
1577 : gettimeofday(&tv, NULL);
1578 : return (lng) (tv.tv_sec * LL_CONSTANT(1000000) + tv.tv_usec);
1579 : #elif defined(HAVE_FTIME)
1580 : struct timeb tb;
1581 : ftime(&tb);
1582 : return (lng) (tb.time * LL_CONSTANT(1000000) + tb.millitm * LL_CONSTANT(1000));
1583 : #else
1584 : /* last resort */
1585 : return (lng) (time(NULL) * LL_CONSTANT(1000000));
1586 : #endif
1587 : }
1588 :
1589 :
1590 : int
1591 0 : GDKms(void)
1592 : {
1593 : /* wraps around after a bit over 24 days */
1594 0 : return (int) ((GDKusec() - programepoch) / 1000);
1595 : }
1596 :
1597 :
1598 : /*
1599 : * @+ Logical Thread management
1600 : *
1601 : * All semaphores used by the application should be mentioned here.
1602 : * They are initialized during system initialization.
1603 : *
1604 : * The first action upon thread creation is to add it to the pool of
1605 : * known threads. This should be done by the thread itself.
1606 : * Note that the users should have gained exclusive access already. A
1607 : * new entry is initialized automatically when not found. Its file
1608 : * descriptors are the same as for the server and should be
1609 : * subsequently reset.
1610 : */
1611 : stream *GDKstdout;
1612 : stream *GDKstdin;
1613 :
1614 : static int
1615 359 : THRinit(void)
1616 : {
1617 359 : if ((GDKstdout = stdout_wastream()) == NULL) {
1618 0 : TRC_CRITICAL(GDK, "malloc for stdout failed\n");
1619 0 : return -1;
1620 : }
1621 359 : if ((GDKstdin = stdin_rastream()) == NULL) {
1622 0 : TRC_CRITICAL(GDK, "malloc for stdin failed\n");
1623 0 : mnstr_destroy(GDKstdout);
1624 0 : GDKstdout = NULL;
1625 0 : return -1;
1626 : }
1627 359 : struct freebats *t = MT_thread_getfreebats();
1628 359 : t->freebats = 0;
1629 359 : t->nfreebats = 0;
1630 359 : return 0;
1631 : }
1632 :
1633 : const char *
1634 346 : GDKversion(void)
1635 : {
1636 346 : return MONETDB_VERSION;
1637 : }
1638 :
1639 : const char *
1640 703 : GDKlibversion(void)
1641 : {
1642 703 : return GDK_VERSION;
1643 : }
1644 :
1645 : inline size_t
1646 182027711 : GDKmem_cursize(void)
1647 : {
1648 : /* RAM/swapmem that Monet is really using now */
1649 182027711 : return (size_t) ATOMIC_GET(&GDK_mallocedbytes_estimate);
1650 : }
1651 :
1652 : inline size_t
1653 172838564 : GDKvm_cursize(void)
1654 : {
1655 : /* current Monet VM address space usage */
1656 172838564 : return (size_t) ATOMIC_GET(&GDK_vm_cursize) + GDKmem_cursize();
1657 : }
1658 :
1659 : #define heapinc(_memdelta) \
1660 : ATOMIC_ADD(&GDK_mallocedbytes_estimate, _memdelta)
1661 : #ifndef NDEBUG
1662 : #define heapdec(_memdelta) \
1663 : do { \
1664 : ATOMIC_BASE_TYPE old = ATOMIC_SUB(&GDK_mallocedbytes_estimate, _memdelta); \
1665 : assert(old >= (ATOMIC_BASE_TYPE) _memdelta); \
1666 : } while (0)
1667 : #else
1668 : #define heapdec(_memdelta) \
1669 : ATOMIC_SUB(&GDK_mallocedbytes_estimate, _memdelta)
1670 : #endif
1671 :
1672 : #define meminc(vmdelta) \
1673 : ATOMIC_ADD(&GDK_vm_cursize, SEG_SIZE(vmdelta))
1674 : #ifndef NDEBUG
1675 : #define memdec(vmdelta) \
1676 : do { \
1677 : ssize_t diff = SEG_SIZE(vmdelta); \
1678 : ATOMIC_BASE_TYPE old = ATOMIC_SUB(&GDK_vm_cursize, diff); \
1679 : assert(old >= (ATOMIC_BASE_TYPE) diff); \
1680 : } while (0)
1681 : #else
1682 : #define memdec(vmdelta) \
1683 : ATOMIC_SUB(&GDK_vm_cursize, SEG_SIZE(vmdelta))
1684 : #endif
1685 :
1686 : /* Memory allocation
1687 : *
1688 : * The functions GDKmalloc, GDKzalloc, GDKrealloc, GDKstrdup, and
1689 : * GDKfree are used throughout to allocate and free memory. These
1690 : * functions are almost directly mapped onto the system
1691 : * malloc/realloc/free functions, but they give us some extra
1692 : * debugging hooks.
1693 : *
1694 : * When allocating memory, we allocate a bit more than was asked for.
1695 : * The extra space is added onto the front of the memory area that is
1696 : * returned, and in debug builds also some at the end. The area in
1697 : * front is used to store the actual size of the allocated area. The
1698 : * most important use is to be able to keep statistics on how much
1699 : * memory is being used. In debug builds, the size is also used to
1700 : * make sure that we don't write outside of the allocated arena. This
1701 : * is also where the extra space at the end comes in.
1702 : */
1703 :
1704 : /* we allocate extra space and return a pointer offset by this amount */
1705 : #define MALLOC_EXTRA_SPACE (2 * SIZEOF_VOID_P)
1706 :
1707 : #if defined(NDEBUG) || defined(SANITIZER)
1708 : #define DEBUG_SPACE 0
1709 : #else
1710 : #define DEBUG_SPACE 16
1711 : #endif
1712 :
1713 : /* malloc smaller than this aren't subject to the GDK_vm_maxsize test */
1714 : #define SMALL_MALLOC 256
1715 :
1716 : static void *
1717 175701027 : GDKmalloc_internal(size_t size, bool clear)
1718 : {
1719 175701027 : void *s;
1720 175701027 : size_t nsize;
1721 :
1722 175701027 : assert(size != 0);
1723 : #ifndef SIZE_CHECK_IN_HEAPS_ONLY
1724 : if (size > SMALL_MALLOC &&
1725 : GDKvm_cursize() + size >= GDK_vm_maxsize &&
1726 : !MT_thread_override_limits()) {
1727 : GDKerror("allocating too much memory\n");
1728 : return NULL;
1729 : }
1730 : #endif
1731 :
1732 : /* pad to multiple of eight bytes and add some extra space to
1733 : * write real size in front; when debugging, also allocate
1734 : * extra space for check bytes */
1735 175701027 : nsize = (size + 7) & ~7;
1736 175701027 : if (clear)
1737 28663201 : s = calloc(nsize + MALLOC_EXTRA_SPACE + DEBUG_SPACE, 1);
1738 : else
1739 147037826 : s = malloc(nsize + MALLOC_EXTRA_SPACE + DEBUG_SPACE);
1740 175701027 : if (s == NULL) {
1741 0 : GDKsyserror("malloc failed; memory requested: %zu, memory in use: %zu, virtual memory in use: %zu\n", size, GDKmem_cursize(), GDKvm_cursize());;
1742 0 : return NULL;
1743 : }
1744 175701027 : s = (void *) ((char *) s + MALLOC_EXTRA_SPACE);
1745 :
1746 175701027 : heapinc(nsize + MALLOC_EXTRA_SPACE + DEBUG_SPACE);
1747 :
1748 : /* just before the pointer that we return, write how much we
1749 : * asked of malloc */
1750 175701027 : ((size_t *) s)[-1] = nsize + MALLOC_EXTRA_SPACE + DEBUG_SPACE;
1751 : #if !defined(NDEBUG) && !defined(SANITIZER)
1752 : /* just before that, write how much was asked of us */
1753 175701027 : ((size_t *) s)[-2] = size;
1754 : /* write pattern to help find out-of-bounds writes */
1755 175701027 : memset((char *) s + size, '\xBD', nsize + DEBUG_SPACE - size);
1756 : #endif
1757 175701027 : return s;
1758 : }
1759 :
1760 : #undef GDKmalloc
1761 : void *
1762 138788433 : GDKmalloc(size_t size)
1763 : {
1764 138788433 : void *s;
1765 :
1766 138788433 : if ((s = GDKmalloc_internal(size, false)) == NULL)
1767 : return NULL;
1768 : #if !defined(NDEBUG) && !defined(SANITIZER)
1769 : /* write a pattern to help make sure all data is properly
1770 : * initialized by the caller */
1771 139017496 : DEADBEEFCHK memset(s, '\xBD', size);
1772 : #endif
1773 : return s;
1774 : }
1775 :
1776 : #undef GDKzalloc
1777 : void *
1778 28668040 : GDKzalloc(size_t size)
1779 : {
1780 28668040 : return GDKmalloc_internal(size, true);
1781 : }
1782 :
1783 : #undef GDKstrdup
1784 : char *
1785 9014396 : GDKstrdup(const char *s)
1786 : {
1787 9014396 : size_t size;
1788 9014396 : char *p;
1789 :
1790 9014396 : if (s == NULL)
1791 : return NULL;
1792 8359698 : size = strlen(s) + 1;
1793 :
1794 8359698 : if ((p = GDKmalloc_internal(size, false)) == NULL)
1795 : return NULL;
1796 8360558 : memcpy(p, s, size); /* including terminating NULL byte */
1797 8360558 : return p;
1798 : }
1799 :
1800 : #undef GDKstrndup
1801 : char *
1802 233 : GDKstrndup(const char *s, size_t size)
1803 : {
1804 233 : char *p;
1805 :
1806 233 : if (s == NULL)
1807 : return NULL;
1808 233 : if ((p = GDKmalloc_internal(size + 1, false)) == NULL)
1809 : return NULL;
1810 233 : if (size > 0)
1811 233 : memcpy(p, s, size);
1812 233 : p[size] = '\0'; /* make sure it's NULL terminated */
1813 233 : return p;
1814 : }
1815 :
1816 : #undef GDKfree
1817 : void
1818 357352685 : GDKfree(void *s)
1819 : {
1820 357352685 : size_t asize;
1821 :
1822 357352685 : if (s == NULL)
1823 : return;
1824 :
1825 175806535 : asize = ((size_t *) s)[-1]; /* how much allocated last */
1826 :
1827 : #if !defined(NDEBUG) && !defined(SANITIZER)
1828 175806535 : size_t *p = s;
1829 175806535 : assert((asize & 2) == 0); /* check against duplicate free */
1830 175806535 : size_t size = p[-2];
1831 175806535 : assert(((size + 7) & ~7) + MALLOC_EXTRA_SPACE + DEBUG_SPACE == asize);
1832 : /* check for out-of-bounds writes */
1833 3344378115 : for (size_t i = size; i < asize - MALLOC_EXTRA_SPACE; i++)
1834 3168571580 : assert(((char *) s)[i] == '\xBD');
1835 175806535 : p[-1] |= 2; /* indicate area is freed */
1836 :
1837 : /* overwrite memory that is to be freed with a pattern that
1838 : * will help us recognize access to already freed memory in
1839 : * the debugger */
1840 175806535 : DEADBEEFCHK memset(s, '\xDB', asize - MALLOC_EXTRA_SPACE);
1841 : #endif
1842 :
1843 175806535 : free((char *) s - MALLOC_EXTRA_SPACE);
1844 175806535 : heapdec((ssize_t) asize);
1845 : }
1846 :
1847 : #undef GDKrealloc
1848 : void *
1849 335704 : GDKrealloc(void *s, size_t size)
1850 : {
1851 335704 : size_t nsize, asize;
1852 : #if !defined(NDEBUG) && !defined(SANITIZER)
1853 335704 : size_t osize;
1854 : #endif
1855 335704 : size_t *os = s;
1856 :
1857 335704 : assert(size != 0);
1858 :
1859 335704 : if (s == NULL)
1860 933 : return GDKmalloc(size);
1861 :
1862 334771 : nsize = (size + 7) & ~7;
1863 334771 : asize = os[-1]; /* how much allocated last */
1864 :
1865 : #ifndef SIZE_CHECK_IN_HEAPS_ONLY
1866 : if (size > SMALL_MALLOC &&
1867 : nsize > asize &&
1868 : GDKvm_cursize() + nsize - asize >= GDK_vm_maxsize &&
1869 : !MT_thread_override_limits()) {
1870 : GDKerror("allocating too much memory\n");
1871 : return NULL;
1872 : }
1873 : #endif
1874 : #if !defined(NDEBUG) && !defined(SANITIZER)
1875 334771 : assert((asize & 2) == 0); /* check against duplicate free */
1876 : /* check for out-of-bounds writes */
1877 334771 : osize = os[-2]; /* how much asked for last */
1878 334771 : assert(((osize + 7) & ~7) + MALLOC_EXTRA_SPACE + DEBUG_SPACE == asize);
1879 5754460 : for (size_t i = osize; i < asize - MALLOC_EXTRA_SPACE; i++)
1880 5419689 : assert(((char *) s)[i] == '\xBD');
1881 : /* if shrinking, write debug pattern into to-be-freed memory */
1882 334771 : DEADBEEFCHK if (size < osize)
1883 46279 : memset((char *) s + size, '\xDB', osize - size);
1884 334771 : os[-1] |= 2; /* indicate area is freed */
1885 : #endif
1886 334771 : s = realloc((char *) s - MALLOC_EXTRA_SPACE,
1887 : nsize + MALLOC_EXTRA_SPACE + DEBUG_SPACE);
1888 334771 : if (s == NULL) {
1889 : #if !defined(NDEBUG) && !defined(SANITIZER)
1890 0 : os[-1] &= ~2; /* not freed after all */
1891 0 : assert(os[-1] == asize);
1892 0 : assert(os[-2] == osize);
1893 : #endif
1894 0 : GDKsyserror("realloc failed; memory requested: %zu, memory in use: %zu, virtual memory in use: %zu\n", size, GDKmem_cursize(), GDKvm_cursize());;
1895 0 : return NULL;
1896 : }
1897 334771 : s = (void *) ((char *) s + MALLOC_EXTRA_SPACE);
1898 : /* just before the pointer that we return, write how much we
1899 : * asked of malloc */
1900 334771 : ((size_t *) s)[-1] = nsize + MALLOC_EXTRA_SPACE + DEBUG_SPACE;
1901 : #if !defined(NDEBUG) && !defined(SANITIZER)
1902 : /* just before that, write how much was asked of us */
1903 334771 : ((size_t *) s)[-2] = size;
1904 : /* if growing, initialize new memory with debug pattern */
1905 334771 : DEADBEEFCHK if (size > osize)
1906 288435 : memset((char *) s + osize, '\xBD', size - osize);
1907 : /* write pattern to help find out-of-bounds writes */
1908 334771 : memset((char *) s + size, '\xBD', nsize + DEBUG_SPACE - size);
1909 : #endif
1910 :
1911 334771 : heapinc(nsize + MALLOC_EXTRA_SPACE + DEBUG_SPACE);
1912 334771 : heapdec((ssize_t) asize);
1913 :
1914 : return s;
1915 : }
1916 :
1917 : /* return how much memory was allocated; the argument must be a value
1918 : * returned by GDKmalloc, GDKzalloc, GDKrealloc, GDKstrdup, or
1919 : * GDKstrndup */
1920 : size_t
1921 200099 : GDKmallocated(const void *s)
1922 : {
1923 200099 : return ((const size_t *) s)[-1]; /* how much allocated last */
1924 : }
1925 :
1926 : /*
1927 : * @- virtual memory
1928 : * allocations affect only the logical VM resources.
1929 : */
1930 : #undef GDKmmap
1931 : void *
1932 2585 : GDKmmap(const char *path, int mode, size_t len)
1933 : {
1934 2585 : void *ret;
1935 :
1936 : #ifndef SIZE_CHECK_IN_HEAPS_ONLY
1937 : if (GDKvm_cursize() + len >= GDK_vm_maxsize &&
1938 : !MT_thread_override_limits()) {
1939 : GDKerror("requested too much virtual memory; memory requested: %zu, memory in use: %zu, virtual memory in use: %zu\n", len, GDKmem_cursize(), GDKvm_cursize());
1940 : return NULL;
1941 : }
1942 : #endif
1943 2585 : ret = MT_mmap(path, mode, len);
1944 2612 : if (ret != NULL) {
1945 2612 : if (mode & MMAP_COPY)
1946 0 : heapinc(len);
1947 : else
1948 2612 : meminc(len);
1949 : } else
1950 0 : GDKerror("requesting virtual memory failed; memory requested: %zu, memory in use: %zu, virtual memory in use: %zu\n", len, GDKmem_cursize(), GDKvm_cursize());
1951 2612 : return ret;
1952 : }
1953 :
1954 : #undef GDKmunmap
1955 : gdk_return
1956 2611 : GDKmunmap(void *addr, int mode, size_t size)
1957 : {
1958 2611 : int ret;
1959 :
1960 2611 : ret = MT_munmap(addr, size);
1961 2612 : if (ret == 0) {
1962 2612 : if (mode & MMAP_COPY)
1963 0 : heapdec(size);
1964 : else
1965 2612 : memdec(size);
1966 : }
1967 2612 : return ret == 0 ? GDK_SUCCEED : GDK_FAIL;
1968 : }
1969 :
1970 : #undef GDKmremap
1971 : void *
1972 453 : GDKmremap(const char *path, int mode, void *old_address, size_t old_size, size_t *new_size)
1973 : {
1974 453 : void *ret;
1975 :
1976 : #ifndef SIZE_CHECK_IN_HEAPS_ONLY
1977 : if (*new_size > old_size &&
1978 : GDKvm_cursize() + *new_size - old_size >= GDK_vm_maxsize &&
1979 : !MT_thread_override_limits()) {
1980 : 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());
1981 : return NULL;
1982 : }
1983 : #endif
1984 453 : ret = MT_mremap(path, mode, old_address, old_size, new_size);
1985 453 : if (ret != NULL) {
1986 453 : if (mode & MMAP_COPY) {
1987 0 : heapdec(old_size);
1988 0 : heapinc(*new_size);
1989 : } else {
1990 453 : memdec(old_size);
1991 453 : meminc(*new_size);
1992 : }
1993 : } else {
1994 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());
1995 : }
1996 453 : return ret;
1997 : }
1998 :
1999 : /* print some potentially interesting information */
2000 : struct prinfocb {
2001 : struct prinfocb *next;
2002 : void (*func)(void);
2003 : } *prinfocb;
2004 :
2005 : void
2006 356 : GDKprintinforegister(void (*func)(void))
2007 : {
2008 356 : struct prinfocb *p = GDKmalloc(sizeof(struct prinfocb));
2009 356 : if (p == NULL) {
2010 0 : GDKerror("cannot register USR1 printing function.\n");
2011 0 : return;
2012 : }
2013 356 : p->func = func;
2014 356 : p->next = NULL;
2015 356 : struct prinfocb **pp = &prinfocb;
2016 411 : while (*pp != NULL)
2017 55 : pp = &(*pp)->next;
2018 356 : *pp = p;
2019 : }
2020 :
2021 : void
2022 118 : GDKprintinfo(void)
2023 : {
2024 118 : size_t allocated = (size_t) ATOMIC_GET(&GDK_mallocedbytes_estimate);
2025 118 : size_t vmallocated = (size_t) ATOMIC_GET(&GDK_vm_cursize);
2026 :
2027 118 : printf("SIGUSR1 info start\n");
2028 118 : printf("Virtual memory allocated: %zu, of which %zu with malloc\n",
2029 : vmallocated + allocated, allocated);
2030 118 : printf("gdk_vm_maxsize: %zu, gdk_mem_maxsize: %zu\n",
2031 : GDK_vm_maxsize, GDK_mem_maxsize);
2032 118 : printf("gdk_mmap_minsize_persistent %zu, gdk_mmap_minsize_transient %zu\n",
2033 : GDK_mmap_minsize_persistent, GDK_mmap_minsize_transient);
2034 : #ifdef __linux__
2035 118 : int fd = open("/proc/self/statm", O_RDONLY | O_CLOEXEC);
2036 118 : if (fd >= 0) {
2037 118 : char buf[512];
2038 118 : ssize_t s = read(fd, buf, sizeof(buf) - 1);
2039 118 : close(fd);
2040 118 : if (s > 0) {
2041 118 : assert((size_t) s < sizeof(buf));
2042 118 : size_t size, resident, shared;
2043 118 : buf[s] = 0;
2044 118 : if (sscanf(buf, "%zu %zu %zu", &size, &resident, &shared) == 3) {
2045 118 : size *= MT_pagesize();
2046 118 : resident *= MT_pagesize();
2047 118 : shared *= MT_pagesize();
2048 118 : printf("Virtual size: %zu, anonymous RSS: %zu, shared RSS: %zu (together: %zu)\n",
2049 : size, resident - shared, shared, resident);
2050 : }
2051 : }
2052 : }
2053 : #endif
2054 118 : BBPprintinfo();
2055 : #ifdef LOCK_STATS
2056 : GDKlockstatistics(3);
2057 : #endif
2058 118 : dump_threads();
2059 236 : for (struct prinfocb *p = prinfocb; p; p = p->next)
2060 118 : (*p->func)();
2061 118 : printf("SIGUSR1 info end\n");
2062 118 : }
2063 :
2064 : exception_buffer *
2065 725127 : eb_init(exception_buffer *eb)
2066 : {
2067 725127 : if (eb) {
2068 725127 : eb->enabled = 0;
2069 725127 : eb->code = 0;
2070 725127 : eb->msg = NULL;
2071 : }
2072 725127 : return eb;
2073 : }
2074 :
2075 : void
2076 3 : eb_error(exception_buffer *eb, const char *msg, int val)
2077 : {
2078 3 : eb->code = val;
2079 3 : eb->msg = msg;
2080 3 : eb->enabled = 0; /* not any longer... */
2081 : #ifdef HAVE_SIGLONGJMP
2082 3 : siglongjmp(eb->state, eb->code);
2083 : #else
2084 : longjmp(eb->state, eb->code);
2085 : #endif
2086 : }
2087 :
2088 : #define SA_BLOCK (64*1024)
2089 :
2090 : typedef struct freed_t {
2091 : struct freed_t *n;
2092 : size_t sz;
2093 : } freed_t;
2094 :
2095 : static void
2096 71120 : sa_destroy_freelist(freed_t *f)
2097 : {
2098 87722 : while(f) {
2099 16602 : freed_t *n = f->n;
2100 16602 : GDKfree(f);
2101 16602 : f = n;
2102 : }
2103 : }
2104 :
2105 : static void
2106 200099 : sa_free(allocator *pa, void *blk)
2107 : {
2108 200099 : assert(!pa->pa);
2109 : size_t i;
2110 :
2111 1165050 : for(i = 0; i < pa->nr; i++) {
2112 1165050 : if (pa->blks[i] == blk)
2113 : break;
2114 : }
2115 200099 : assert (i < pa->nr);
2116 2064054 : for (; i < pa->nr-1; i++)
2117 1863955 : pa->blks[i] = pa->blks[i+1];
2118 200099 : pa->nr--;
2119 :
2120 200099 : size_t sz = GDKmallocated(blk);
2121 200099 : if (sz > (SA_BLOCK + 32)) {
2122 90 : GDKfree(blk);
2123 : } else {
2124 200009 : freed_t *f = blk;
2125 200009 : f->n = pa->freelist;
2126 200009 : f->sz = sz;
2127 :
2128 200009 : pa->freelist = f;
2129 : }
2130 200099 : }
2131 :
2132 : static void *
2133 183407 : sa_use_freed(allocator *pa, size_t sz)
2134 : {
2135 183407 : (void)sz;
2136 :
2137 183407 : freed_t *f = pa->freelist;
2138 183407 : pa->freelist = f->n;
2139 183407 : return f;
2140 : }
2141 :
2142 : allocator *
2143 282576 : sa_create(allocator *pa)
2144 : {
2145 282576 : allocator *sa = (pa)?(allocator*)sa_alloc(pa, sizeof(allocator)):(allocator*)GDKmalloc(sizeof(allocator));
2146 282582 : if (sa == NULL)
2147 : return NULL;
2148 282582 : eb_init(&sa->eb);
2149 282583 : sa->pa = pa;
2150 282583 : sa->size = 64;
2151 282583 : sa->nr = 1;
2152 282583 : sa->blks = pa?(char**)sa_alloc(pa, sizeof(char*) * sa->size):(char**)GDKmalloc(sizeof(char*) * sa->size);
2153 282581 : sa->freelist = NULL;
2154 282581 : if (sa->blks == NULL) {
2155 0 : if (!pa)
2156 0 : GDKfree(sa);
2157 0 : return NULL;
2158 : }
2159 282581 : sa->blks[0] = pa?(char*)sa_alloc(pa, SA_BLOCK):(char*)GDKmalloc(SA_BLOCK);
2160 282569 : sa->usedmem = SA_BLOCK;
2161 282569 : if (sa->blks[0] == NULL) {
2162 0 : if (!pa)
2163 0 : GDKfree(sa->blks);
2164 0 : if (!pa)
2165 0 : GDKfree(sa);
2166 0 : return NULL;
2167 : }
2168 282569 : sa->used = 0;
2169 282569 : return sa;
2170 : }
2171 :
2172 : allocator *
2173 2004962 : sa_reset(allocator *sa)
2174 : {
2175 2004962 : size_t i ;
2176 :
2177 2069371 : for (i = 1; i<sa->nr; i++) {
2178 64273 : if (!sa->pa)
2179 0 : GDKfree(sa->blks[i]);
2180 : else
2181 64273 : sa_free(sa->pa, sa->blks[i]);
2182 : }
2183 2005098 : sa->nr = 1;
2184 2005098 : sa->used = 0;
2185 2005098 : sa->usedmem = SA_BLOCK;
2186 2005098 : return sa;
2187 : }
2188 :
2189 : #undef sa_realloc
2190 : #undef sa_alloc
2191 : void *
2192 32 : sa_realloc(allocator *sa, void *p, size_t sz, size_t oldsz)
2193 : {
2194 32 : void *r = sa_alloc(sa, sz);
2195 :
2196 32 : if (r)
2197 32 : memcpy(r, p, oldsz);
2198 32 : return r;
2199 : }
2200 :
2201 : #define round16(sz) ((sz+15)&~15)
2202 : void *
2203 251699804 : sa_alloc(allocator *sa, size_t sz)
2204 : {
2205 251699804 : char *r;
2206 251699804 : sz = round16(sz);
2207 251699804 : if (sz > (SA_BLOCK-sa->used)) {
2208 358944 : if (sa->pa)
2209 64510 : r = (char*)sa_alloc(sa->pa, (sz > SA_BLOCK ? sz : SA_BLOCK));
2210 294434 : else if (sz <= SA_BLOCK && sa->freelist) {
2211 183407 : r = sa_use_freed(sa, SA_BLOCK);
2212 : } else
2213 111027 : r = GDKmalloc(sz > SA_BLOCK ? sz : SA_BLOCK);
2214 358932 : if (r == NULL) {
2215 0 : if (sa->eb.enabled)
2216 0 : eb_error(&sa->eb, "out of memory", 1000);
2217 : return NULL;
2218 : }
2219 358932 : if (sa->nr >= sa->size) {
2220 37 : char **tmp;
2221 37 : size_t osz = sa->size;
2222 37 : sa->size *=2;
2223 37 : if (sa->pa)
2224 18 : tmp = (char**)sa_realloc(sa->pa, sa->blks, sizeof(char*) * sa->size, sizeof(char*) * osz);
2225 : else
2226 19 : tmp = GDKrealloc(sa->blks, sizeof(char*) * sa->size);
2227 37 : if (tmp == NULL) {
2228 0 : sa->size /= 2; /* undo */
2229 0 : if (sa->eb.enabled)
2230 0 : eb_error(&sa->eb, "out of memory", 1000);
2231 0 : if (!sa->pa)
2232 0 : GDKfree(r);
2233 0 : return NULL;
2234 : }
2235 37 : sa->blks = tmp;
2236 : }
2237 358932 : if (sz >= SA_BLOCK) {
2238 : // The request is large so it gets its own block.
2239 : // We put it 'under' the current block because
2240 : // there may still be plenty of usable space there.
2241 276419 : sa->blks[sa->nr] = sa->blks[sa->nr-1];
2242 276419 : sa->blks[sa->nr-1] = r;
2243 276419 : sa->nr ++;
2244 276419 : sa->usedmem += sz;
2245 : } else {
2246 82513 : sa->blks[sa->nr] = r;
2247 82513 : sa->nr ++;
2248 82513 : sa->used = sz;
2249 82513 : sa->usedmem += SA_BLOCK;
2250 : }
2251 : } else {
2252 251340860 : r = sa->blks[sa->nr-1] + sa->used;
2253 251340860 : sa->used += sz;
2254 : }
2255 : return r;
2256 : }
2257 :
2258 : #undef sa_zalloc
2259 : void *
2260 10932443 : sa_zalloc(allocator *sa, size_t sz)
2261 : {
2262 10932443 : void *r = sa_alloc(sa, sz);
2263 :
2264 10932723 : if (r)
2265 10932723 : memset(r, 0, sz);
2266 10932723 : return r;
2267 : }
2268 :
2269 : void
2270 206946 : sa_destroy(allocator *sa)
2271 : {
2272 206946 : if (sa->pa) {
2273 135826 : sa_reset(sa);
2274 135826 : sa_free(sa->pa, sa->blks[0]);
2275 135826 : return;
2276 : }
2277 :
2278 71120 : sa_destroy_freelist(sa->freelist);
2279 236570 : for (size_t i = 0; i<sa->nr; i++) {
2280 165450 : GDKfree(sa->blks[i]);
2281 : }
2282 71120 : GDKfree(sa->blks);
2283 71120 : GDKfree(sa);
2284 : }
2285 :
2286 : #undef sa_strndup
2287 : char *
2288 13119881 : sa_strndup(allocator *sa, const char *s, size_t l)
2289 : {
2290 13119881 : char *r = sa_alloc(sa, l+1);
2291 :
2292 13119501 : if (r) {
2293 13119501 : memcpy(r, s, l);
2294 13119501 : r[l] = 0;
2295 : }
2296 13119501 : return r;
2297 : }
2298 :
2299 : #undef sa_strdup
2300 : char *
2301 7507754 : sa_strdup(allocator *sa, const char *s)
2302 : {
2303 7507754 : return sa_strndup(sa, s, strlen(s));
2304 : }
2305 :
2306 : char *
2307 43166 : sa_strconcat(allocator *sa, const char *s1, const char *s2)
2308 : {
2309 43166 : size_t l1 = strlen(s1);
2310 43166 : size_t l2 = strlen(s2);
2311 43166 : char *r = sa_alloc(sa, l1+l2+1);
2312 :
2313 43171 : if (l1)
2314 43173 : memcpy(r, s1, l1);
2315 43171 : if (l2)
2316 43176 : memcpy(r+l1, s2, l2);
2317 43171 : r[l1+l2] = 0;
2318 43171 : return r;
2319 : }
2320 :
2321 : size_t
2322 0 : sa_size(allocator *sa)
2323 : {
2324 0 : return sa->usedmem;
2325 : }
|