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 478849 : GDKgetenv(const char *name)
119 : {
120 478849 : MT_lock_set(&GDKenvlock);
121 478851 : 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 478851 : MT_lock_unset(&GDKenvlock);
128 478851 : if (GDKkey && GDKval) {
129 478155 : BUN b = BUNfnd(GDKkey, name);
130 :
131 478155 : if (b != BUN_NONE) {
132 335078 : BATiter GDKenvi = bat_iterator(GDKval);
133 335078 : const char *v = BUNtvar(GDKenvi, b);
134 335078 : bat_iterator_end(&GDKenvi);
135 335078 : return v;
136 : }
137 : }
138 : return NULL;
139 : }
140 :
141 : bool
142 291490 : GDKgetenv_istext(const char *name, const char *text)
143 : {
144 291490 : const char *val = GDKgetenv(name);
145 :
146 291490 : 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 252455 : GDKgetenv_istrue(const char *name)
157 : {
158 252455 : return GDKgetenv_istext(name, "true");
159 : }
160 :
161 : int
162 98060 : GDKgetenv_int(const char *name, int def)
163 : {
164 98060 : const char *val = GDKgetenv(name);
165 :
166 98060 : 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 165023 : for (size_t i = 0; v[i]; i++) {
180 156301 : 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 156293 : } else if ((v[i] & 0xE0) == 0xC0) {
187 0 : nutf8 = 1;
188 0 : if ((v[i] & 0x1E) == 0)
189 0 : goto badutf8;
190 156293 : } else if ((v[i] & 0xF0) == 0xE0) {
191 4 : nutf8 = 2;
192 4 : if ((v[i] & 0x0F) == 0)
193 0 : m = 0x20;
194 156289 : } else if ((v[i] & 0xF8) == 0xF0) {
195 0 : nutf8 = 3;
196 0 : if ((v[i] & 0x07) == 0)
197 0 : m = 0x30;
198 156289 : } 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 12240 : while (fgets(buf, (int) sizeof(buf), fc) != NULL) {
567 11880 : char *p, *cgr;
568 11880 : if ((p = strstr(buf, " - cgroup ")) != NULL &&
569 0 : strstr(p, "memory") != NULL)
570 : cgr = cgr1;
571 11880 : else if (strstr(buf, " - cgroup2 ") != NULL)
572 : cgr = cgr2;
573 : else
574 11520 : 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 25779794 : GDKembedded(void)
941 : {
942 25779794 : 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 : }
1020 349 : if (mnstr_init() < 0) {
1021 0 : TRC_CRITICAL(GDK, "mnstr_init failed\n");
1022 0 : return GDK_FAIL;
1023 : }
1024 : } else {
1025 : /* BBP was locked by BBPexit() */
1026 : //BBPunlock();
1027 359 : }
1028 359 : mainpid = MT_getpid();
1029 :
1030 359 : GDKtracer_init(dbpath, dbtrace);
1031 359 : errno = 0;
1032 359 : if (!GDKinmemory(0) && !GDKenvironment(dbpath))
1033 : return GDK_FAIL;
1034 :
1035 359 : MT_init_posix();
1036 359 : if (THRinit() < 0)
1037 : return GDK_FAIL;
1038 : #ifndef NATIVE_WIN32
1039 359 : BATSIGinit();
1040 : #endif
1041 359 : MT_init();
1042 :
1043 : /* now try to lock the database: go through all farms, and if
1044 : * we see a new directory, lock it */
1045 11783 : for (int farmid = 0; farmid < MAXFARMS; farmid++) {
1046 11426 : if (BBPfarms[farmid].dirname != NULL) {
1047 1441 : bool skip = false;
1048 1441 : for (int j = 0; j < farmid; j++) {
1049 892 : if (BBPfarms[j].dirname != NULL &&
1050 892 : strcmp(BBPfarms[farmid].dirname, BBPfarms[j].dirname) == 0) {
1051 : skip = true;
1052 : break;
1053 : }
1054 : }
1055 1059 : if (!skip && GDKlockHome(farmid) != GDK_SUCCEED)
1056 : return GDK_FAIL;
1057 : }
1058 : }
1059 :
1060 : /* Mserver by default takes 80% of all memory as a default */
1061 : #if SIZEOF_SIZE_T == 4
1062 : if ((double) MT_npages() * (double) MT_pagesize() * 0.815 >= (double) GDK_VM_MAXSIZE)
1063 : GDK_mem_maxsize = GDK_VM_MAXSIZE;
1064 : else
1065 : #endif
1066 357 : GDK_mem_maxsize = (size_t) ((double) MT_npages() * (double) MT_pagesize() * 0.815);
1067 357 : const char *allow = mo_find_option(set, setlen, "allow_hge_upgrade");
1068 357 : const char *procwalxit = mo_find_option(set, setlen, "process-wal-and-exit");
1069 1063 : if (BBPinit(allow && strcmp(allow, "yes") == 0, procwalxit && strcmp(procwalxit, "yes") == 0) != GDK_SUCCEED)
1070 : return GDK_FAIL;
1071 357 : first = false;
1072 :
1073 357 : if (GDK_mem_maxsize / 16 < GDK_mmap_minsize_transient) {
1074 347 : GDK_mmap_minsize_transient = MAX(MMAP_PAGESIZE, GDK_mem_maxsize / 16);
1075 347 : if (GDK_mmap_minsize_persistent > GDK_mmap_minsize_transient)
1076 0 : GDK_mmap_minsize_persistent = GDK_mmap_minsize_transient;
1077 : }
1078 :
1079 357 : n = (opt *) malloc(setlen * sizeof(opt));
1080 357 : if (n == NULL) {
1081 0 : GDKsyserror("malloc failed\n");
1082 0 : return GDK_FAIL;
1083 : }
1084 :
1085 4322 : for (i = 0; i < setlen; i++) {
1086 17880 : bool done = false;
1087 :
1088 17880 : for (int j = 0; j < nlen; j++) {
1089 14619 : if (strcmp(n[j].name, set[i].name) == 0) {
1090 704 : if (n[j].kind < set[i].kind) {
1091 704 : n[j] = set[i];
1092 : }
1093 : done = true;
1094 : break;
1095 : }
1096 : }
1097 3261 : if (!done) {
1098 3261 : n[nlen] = set[i];
1099 3261 : nlen++;
1100 : }
1101 : }
1102 : /* check some options before creating our first BAT */
1103 3618 : for (i = 0; i < nlen; i++) {
1104 3261 : if (strcmp("gdk_mem_maxsize", n[i].name) == 0) {
1105 0 : GDK_mem_maxsize = (size_t) strtoll(n[i].value, NULL, 10);
1106 0 : GDK_mem_maxsize = MAX(1 << 26, GDK_mem_maxsize);
1107 0 : if (GDK_mem_maxsize / 16 < GDK_mmap_minsize_transient)
1108 0 : GDK_mmap_minsize_transient = MAX(MMAP_PAGESIZE, GDK_mem_maxsize / 16);
1109 0 : if (GDK_mmap_minsize_persistent > GDK_mmap_minsize_transient)
1110 0 : GDK_mmap_minsize_persistent = GDK_mmap_minsize_transient;
1111 3261 : } else if (strcmp("gdk_vm_maxsize", n[i].name) == 0) {
1112 197 : GDK_vm_maxsize = (size_t) strtoll(n[i].value, NULL, 10);
1113 197 : GDK_vm_maxsize = MAX(1 << 30, GDK_vm_maxsize);
1114 3064 : } else if (strcmp("gdk_mmap_minsize_persistent", n[i].name) == 0) {
1115 0 : GDK_mmap_minsize_persistent = (size_t) strtoll(n[i].value, NULL, 10);
1116 3064 : } else if (strcmp("gdk_mmap_minsize_transient", n[i].name) == 0) {
1117 0 : GDK_mmap_minsize_transient = (size_t) strtoll(n[i].value, NULL, 10);
1118 3064 : } else if (strcmp("gdk_mmap_pagesize", n[i].name) == 0) {
1119 0 : GDK_mmap_pagesize = (size_t) strtoll(n[i].value, NULL, 10);
1120 0 : if (GDK_mmap_pagesize < 1 << 12 ||
1121 0 : GDK_mmap_pagesize > 1 << 20 ||
1122 : /* x & (x - 1): turn off rightmost 1 bit;
1123 : * i.e. if result is zero, x is power of
1124 : * two */
1125 0 : (GDK_mmap_pagesize & (GDK_mmap_pagesize - 1)) != 0) {
1126 0 : free(n);
1127 0 : TRC_CRITICAL(GDK, "gdk_mmap_pagesize must be power of 2 between 2**12 and 2**20\n");
1128 0 : return GDK_FAIL;
1129 : }
1130 : }
1131 : }
1132 :
1133 357 : GDKkey = COLnew(0, TYPE_str, 100, TRANSIENT);
1134 357 : GDKval = COLnew(0, TYPE_str, 100, TRANSIENT);
1135 357 : if (GDKkey == NULL || GDKval == NULL) {
1136 0 : free(n);
1137 0 : TRC_CRITICAL(GDK, "Could not create environment BATs");
1138 0 : return GDK_FAIL;
1139 : }
1140 714 : if (BBPrename(GDKkey, "environment_key") != 0 ||
1141 357 : BBPrename(GDKval, "environment_val") != 0) {
1142 0 : free(n);
1143 0 : TRC_CRITICAL(GDK, "BBPrename of environment BATs failed");
1144 0 : return GDK_FAIL;
1145 : }
1146 357 : BBP_pid(GDKkey->batCacheid) = 0;
1147 357 : BBP_pid(GDKval->batCacheid) = 0;
1148 :
1149 : /* store options into environment BATs */
1150 3618 : for (i = 0; i < nlen; i++)
1151 3261 : if (GDKsetenv(n[i].name, n[i].value) != GDK_SUCCEED) {
1152 0 : TRC_CRITICAL(GDK, "GDKsetenv %s failed", n[i].name);
1153 0 : free(n);
1154 0 : return GDK_FAIL;
1155 : }
1156 357 : free(n);
1157 :
1158 357 : GDKnr_threads = GDKgetenv_int("gdk_nr_threads", 0);
1159 357 : if (GDKnr_threads == 0) {
1160 356 : GDKnr_threads = MT_check_nr_cores();
1161 356 : snprintf(buf, sizeof(buf), "%d", GDKnr_threads);
1162 356 : if (GDKsetenv("gdk_nr_threads", buf) != GDK_SUCCEED) {
1163 0 : TRC_CRITICAL(GDK, "GDKsetenv gdk_nr_threads failed");
1164 0 : return GDK_FAIL;
1165 : }
1166 : }
1167 357 : if (GDKnr_threads > THREADS)
1168 0 : GDKnr_threads = THREADS;
1169 :
1170 357 : if (!GDKinmemory(0)) {
1171 356 : if ((p = GDKgetenv("gdk_dbpath")) != NULL &&
1172 356 : (p = strrchr(p, DIR_SEP)) != NULL) {
1173 356 : if (GDKsetenv("gdk_dbname", p + 1) != GDK_SUCCEED) {
1174 0 : TRC_CRITICAL(GDK, "GDKsetenv gdk_dbname failed");
1175 0 : return GDK_FAIL;
1176 : }
1177 : #if DIR_SEP != '/' /* on Windows look for different separator */
1178 : } else if ((p = GDKgetenv("gdk_dbpath")) != NULL &&
1179 : (p = strrchr(p, '/')) != NULL) {
1180 : if (GDKsetenv("gdk_dbname", p + 1) != GDK_SUCCEED) {
1181 : TRC_CRITICAL(GDK, "GDKsetenv gdk_dbname failed");
1182 : return GDK_FAIL;
1183 : }
1184 : #endif
1185 : }
1186 1 : } else if (GDKgetenv("gdk_dbname") == NULL) {
1187 1 : if (GDKsetenv("gdk_dbname", "in-memory") != GDK_SUCCEED) {
1188 0 : TRC_CRITICAL(GDK, "GDKsetenv gdk_dbname failed");
1189 0 : return GDK_FAIL;
1190 : }
1191 : }
1192 357 : if (GDKgetenv("gdk_vm_maxsize") == NULL) {
1193 160 : snprintf(buf, sizeof(buf), "%zu", GDK_vm_maxsize);
1194 160 : if (GDKsetenv("gdk_vm_maxsize", buf) != GDK_SUCCEED) {
1195 0 : TRC_CRITICAL(GDK, "GDKsetenv gdk_vm_maxsize failed");
1196 0 : return GDK_FAIL;
1197 : }
1198 : }
1199 357 : if (GDKgetenv("gdk_mem_maxsize") == NULL) {
1200 357 : snprintf(buf, sizeof(buf), "%zu", GDK_mem_maxsize);
1201 357 : if (GDKsetenv("gdk_mem_maxsize", buf) != GDK_SUCCEED) {
1202 0 : TRC_CRITICAL(GDK, "GDKsetenv gdk_mem_maxsize failed");
1203 0 : return GDK_FAIL;
1204 : }
1205 : }
1206 357 : if (GDKgetenv("gdk_mmap_minsize_persistent") == NULL) {
1207 357 : snprintf(buf, sizeof(buf), "%zu", GDK_mmap_minsize_persistent);
1208 357 : if (GDKsetenv("gdk_mmap_minsize_persistent", buf) != GDK_SUCCEED) {
1209 0 : TRC_CRITICAL(GDK, "GDKsetenv gdk_mmap_minsize_persistent failed");
1210 0 : return GDK_FAIL;
1211 : }
1212 : }
1213 357 : if (GDKgetenv("gdk_mmap_minsize_transient") == NULL) {
1214 357 : snprintf(buf, sizeof(buf), "%zu", GDK_mmap_minsize_transient);
1215 357 : if (GDKsetenv("gdk_mmap_minsize_transient", buf) != GDK_SUCCEED) {
1216 0 : TRC_CRITICAL(GDK, "GDKsetenv gdk_mmap_minsize_transient failed");
1217 0 : return GDK_FAIL;
1218 : }
1219 : }
1220 357 : if (GDKgetenv("gdk_mmap_pagesize") == NULL) {
1221 357 : snprintf(buf, sizeof(buf), "%zu", GDK_mmap_pagesize);
1222 357 : if (GDKsetenv("gdk_mmap_pagesize", buf) != GDK_SUCCEED) {
1223 0 : TRC_CRITICAL(GDK, "GDKsetenv gdk_mmap_pagesize failed");
1224 0 : return GDK_FAIL;
1225 : }
1226 : }
1227 357 : if (GDKgetenv("monet_pid") == NULL) {
1228 357 : snprintf(buf, sizeof(buf), "%d", (int) getpid());
1229 357 : if (GDKsetenv("monet_pid", buf) != GDK_SUCCEED) {
1230 0 : TRC_CRITICAL(GDK, "GDKsetenv monet_pid failed");
1231 0 : return GDK_FAIL;
1232 : }
1233 : }
1234 357 : if (GDKsetenv("revision", mercurial_revision()) != GDK_SUCCEED) {
1235 0 : TRC_CRITICAL(GDK, "GDKsetenv revision failed");
1236 0 : return GDK_FAIL;
1237 : }
1238 357 : gdk_unique_estimate_keep_fraction = 0;
1239 357 : if ((p = GDKgetenv("gdk_unique_estimate_keep_fraction")) != NULL)
1240 0 : gdk_unique_estimate_keep_fraction = (BUN) strtoll(p, NULL, 10);
1241 357 : if (gdk_unique_estimate_keep_fraction == 0)
1242 357 : gdk_unique_estimate_keep_fraction = GDK_UNIQUE_ESTIMATE_KEEP_FRACTION;
1243 357 : hash_destroy_uniques_fraction = 0;
1244 357 : if ((p = GDKgetenv("hash_destroy_uniques_fraction")) != NULL)
1245 0 : hash_destroy_uniques_fraction = (BUN) strtoll(p, NULL, 10);
1246 357 : if (hash_destroy_uniques_fraction == 0)
1247 357 : hash_destroy_uniques_fraction = HASH_DESTROY_UNIQUES_FRACTION;
1248 357 : no_hash_select_fraction = 0;
1249 357 : if ((p = GDKgetenv("no_hash_select_fraction")) != NULL)
1250 0 : no_hash_select_fraction = (dbl) strtoll(p, NULL, 10);
1251 357 : if (no_hash_select_fraction == 0)
1252 357 : no_hash_select_fraction = NO_HASH_SELECT_FRACTION;
1253 357 : hash_destroy_chain_length = 0;
1254 357 : if ((p = GDKgetenv("hash_destroy_chain_length")) != NULL)
1255 0 : hash_destroy_chain_length = (BUN) strtoll(p, NULL, 10);
1256 357 : if (hash_destroy_chain_length == 0)
1257 357 : hash_destroy_chain_length = HASH_DESTROY_CHAIN_LENGTH;
1258 :
1259 : return GDK_SUCCEED;
1260 : }
1261 :
1262 : int GDKnr_threads = 0;
1263 : static ATOMIC_TYPE GDKnrofthreads = ATOMIC_VAR_INIT(0);
1264 :
1265 : bool
1266 29045426 : GDKexiting(void)
1267 : {
1268 29045426 : return (bool) (ATOMIC_GET(&GDKstopped) > 0);
1269 : }
1270 :
1271 : void
1272 356 : GDKprepareExit(void)
1273 : {
1274 356 : ATOMIC_ADD(&GDKstopped, 1);
1275 :
1276 356 : if (MT_getpid() == mainpid) {
1277 355 : TRC_DEBUG_IF(THRD)
1278 0 : dump_threads();
1279 355 : join_detached_threads();
1280 : }
1281 356 : }
1282 :
1283 : void
1284 355 : GDKreset(int status)
1285 : {
1286 355 : assert(GDKexiting());
1287 :
1288 355 : if (GDKembedded())
1289 : // In the case of a restarted embedded database, GDKstopped has to be reset as well.
1290 11 : ATOMIC_SET(&GDKstopped, 0);
1291 :
1292 355 : if (GDKkey) {
1293 355 : BBPunfix(GDKkey->batCacheid);
1294 355 : GDKkey = NULL;
1295 : }
1296 355 : if (GDKval) {
1297 355 : BBPunfix(GDKval->batCacheid);
1298 355 : GDKval = NULL;
1299 : }
1300 :
1301 355 : join_detached_threads();
1302 :
1303 355 : MT_lock_set(&GDKenvlock);
1304 355 : while (orig_value) {
1305 0 : struct orig_value *ov = orig_value;
1306 0 : orig_value = orig_value->next;
1307 0 : GDKfree(ov);
1308 : }
1309 355 : MT_lock_unset(&GDKenvlock);
1310 :
1311 355 : if (status == 0) {
1312 : /* they had their chance, now kill them */
1313 355 : bool killed = MT_kill_threads();
1314 : /* all threads ceased running, now we can clean up */
1315 355 : if (!killed) {
1316 : /* we can't clean up after killing threads */
1317 355 : BBPexit();
1318 : }
1319 355 : GDKlog(GET_GDKLOCK(PERSISTENT), GDKLOGOFF);
1320 :
1321 11715 : for (int farmid = 0; farmid < MAXFARMS; farmid++) {
1322 11360 : if (BBPfarms[farmid].dirname != NULL) {
1323 2094 : bool skip = false;
1324 2094 : for (int j = 0; j < farmid; j++) {
1325 1042 : if (BBPfarms[j].dirname != NULL &&
1326 0 : strcmp(BBPfarms[farmid].dirname, BBPfarms[j].dirname) == 0) {
1327 : skip = true;
1328 : break;
1329 : }
1330 : }
1331 1052 : if (!skip)
1332 1052 : GDKunlockHome(farmid);
1333 1052 : if (BBPfarms[farmid].dirname) {
1334 1052 : GDKfree((char*)BBPfarms[farmid].dirname);
1335 1052 : BBPfarms[farmid].dirname = NULL;
1336 : }
1337 : }
1338 : }
1339 :
1340 : #ifdef LOCK_STATS
1341 : TRC_DEBUG_IF(TEM) GDKlockstatistics(1);
1342 : #endif
1343 355 : ATOMIC_SET(&GDKdebug, 0);
1344 355 : GDK_mmap_minsize_persistent = MMAP_MINSIZE_PERSISTENT;
1345 355 : GDK_mmap_minsize_transient = MMAP_MINSIZE_TRANSIENT;
1346 355 : GDK_mmap_pagesize = MMAP_PAGESIZE;
1347 355 : GDK_mem_maxsize = (size_t) ((double) MT_npages() * (double) MT_pagesize() * 0.815);
1348 355 : GDK_vm_maxsize = GDK_VM_MAXSIZE;
1349 355 : GDKatomcnt = TYPE_blob + 1;
1350 :
1351 355 : if (GDK_mem_maxsize / 16 < GDK_mmap_minsize_transient) {
1352 355 : GDK_mmap_minsize_transient = GDK_mem_maxsize / 16;
1353 355 : if (GDK_mmap_minsize_persistent > GDK_mmap_minsize_transient)
1354 0 : GDK_mmap_minsize_persistent = GDK_mmap_minsize_transient;
1355 : }
1356 :
1357 355 : GDKnr_threads = 0;
1358 355 : ATOMIC_SET(&GDKnrofthreads, 0);
1359 355 : close_stream(GDKstdout);
1360 355 : close_stream(GDKstdin);
1361 355 : GDKstdout = NULL;
1362 355 : GDKstdin = NULL;
1363 :
1364 355 : gdk_bbp_reset();
1365 : }
1366 355 : ATOMunknown_clean();
1367 :
1368 : /* stop GDKtracer */
1369 355 : GDKtracer_stop();
1370 355 : }
1371 :
1372 : /*
1373 : * All semaphores used by the application should be mentioned here.
1374 : * They are initialized during system initialization.
1375 : */
1376 :
1377 : batlock_t GDKbatLock[BBP_BATMASK + 1];
1378 :
1379 : /*
1380 : * @+ Concurrency control
1381 : * Concurrency control requires actions at several levels of the
1382 : * system. First, it should be ensured that each database is
1383 : * controlled by a single server process (group). Subsequent attempts
1384 : * should be stopped. This is regulated through file locking against
1385 : * ".gdk_lock".
1386 : *
1387 : * Before the locks and threads are initiated, we cannot use the
1388 : * normal routines yet. So we have a local fatal here instead of
1389 : * GDKfatal.
1390 : */
1391 : static gdk_return
1392 549 : GDKlockHome(int farmid)
1393 : {
1394 549 : int fd;
1395 549 : struct stat st;
1396 549 : char gdklockpath[1024];
1397 549 : FILE *GDKlockFile;
1398 :
1399 549 : assert(BBPfarms[farmid].dirname != NULL);
1400 549 : assert(BBPfarms[farmid].lock_file == NULL);
1401 :
1402 549 : if (GDKfilepath(gdklockpath, sizeof(gdklockpath), farmid, NULL, GDKLOCK, NULL) != GDK_SUCCEED)
1403 : return GDK_FAIL;
1404 :
1405 : /*
1406 : * Obtain the global database lock.
1407 : */
1408 549 : if (MT_stat(BBPfarms[farmid].dirname, &st) < 0 &&
1409 0 : GDKcreatedir(gdklockpath) != GDK_SUCCEED) {
1410 0 : TRC_CRITICAL(GDK, "could not create %s\n",
1411 : BBPfarms[farmid].dirname);
1412 0 : return GDK_FAIL;
1413 : }
1414 549 : if ((fd = MT_lockf(gdklockpath, F_TLOCK)) < 0) {
1415 2 : TRC_CRITICAL(GDK, "Database lock '%s' denied\n",
1416 : gdklockpath);
1417 2 : return GDK_FAIL;
1418 : }
1419 :
1420 : /* now we have the lock on the database and are the only
1421 : * process allowed in this section */
1422 :
1423 547 : if ((GDKlockFile = fdopen(fd, "r+")) == NULL) {
1424 0 : GDKsyserror("Could not fdopen %s\n", gdklockpath);
1425 0 : close(fd);
1426 0 : return GDK_FAIL;
1427 : }
1428 :
1429 : /*
1430 : * Print the new process list in the global lock file.
1431 : */
1432 547 : if (fseek(GDKlockFile, 0, SEEK_SET) == -1) {
1433 0 : fclose(GDKlockFile);
1434 0 : TRC_CRITICAL(GDK, "Error while setting the file pointer on %s\n", gdklockpath);
1435 0 : return GDK_FAIL;
1436 : }
1437 547 : if (ftruncate(fileno(GDKlockFile), 0) < 0) {
1438 0 : fclose(GDKlockFile);
1439 0 : TRC_CRITICAL(GDK, "Could not truncate %s\n", gdklockpath);
1440 0 : return GDK_FAIL;
1441 : }
1442 547 : if (fflush(GDKlockFile) == EOF) {
1443 0 : fclose(GDKlockFile);
1444 0 : TRC_CRITICAL(GDK, "Could not flush %s\n", gdklockpath);
1445 0 : return GDK_FAIL;
1446 : }
1447 547 : GDKlog(GDKlockFile, GDKLOGON);
1448 547 : BBPfarms[farmid].lock_file = GDKlockFile;
1449 547 : return GDK_SUCCEED;
1450 : }
1451 :
1452 :
1453 : static void
1454 1052 : GDKunlockHome(int farmid)
1455 : {
1456 1052 : if (BBPfarms[farmid].lock_file) {
1457 545 : char gdklockpath[MAXPATH];
1458 :
1459 545 : if (GDKfilepath(gdklockpath, sizeof(gdklockpath), farmid, NULL, GDKLOCK, NULL) == GDK_SUCCEED)
1460 545 : MT_lockf(gdklockpath, F_ULOCK);
1461 545 : fclose(BBPfarms[farmid].lock_file);
1462 545 : BBPfarms[farmid].lock_file = NULL;
1463 : }
1464 1052 : }
1465 :
1466 : /*
1467 : * @+ Error handling
1468 : * Errors come in three flavors: warnings, non-fatal and fatal errors.
1469 : * A fatal error leaves a core dump behind after trying to safe the
1470 : * content of the relation. A non-fatal error returns a message to
1471 : * the user and aborts the current transaction. Fatal errors are also
1472 : * recorded on the system log for post-mortem analysis.
1473 : * In non-silent mode the errors are immediately sent to output, which
1474 : * makes it hard for upper layers to detect if an error was produced
1475 : * in the process. To facilitate such testing, a global error count is
1476 : * maintained on a thread basis, which can be read out by the function
1477 : * GDKerrorCount(); Furthermore, threads may have set their private
1478 : * error buffer.
1479 : */
1480 :
1481 : #define GDKERRLEN (1024+512)
1482 :
1483 : void
1484 7217191 : GDKclrerr(void)
1485 : {
1486 7217191 : char *buf;
1487 :
1488 7217191 : buf = GDKerrbuf;
1489 7216853 : if (buf)
1490 7208187 : *buf = 0;
1491 7216853 : }
1492 :
1493 : jmp_buf GDKfataljump;
1494 : str GDKfatalmsg;
1495 : bit GDKfataljumpenable = 0;
1496 :
1497 : /* coverity[+kill] */
1498 : void
1499 0 : GDKfatal(const char *format, ...)
1500 : {
1501 0 : char message[GDKERRLEN];
1502 0 : size_t len = strlen(GDKFATAL);
1503 0 : va_list ap;
1504 :
1505 0 : GDKtracer_set_component_level("io", "debug");
1506 : #ifndef NATIVE_WIN32
1507 0 : BATSIGinit();
1508 : #endif
1509 0 : if (!strncmp(format, GDKFATAL, len)) {
1510 : len = 0;
1511 : } else {
1512 0 : strcpy(message, GDKFATAL);
1513 : }
1514 0 : va_start(ap, format);
1515 0 : vsnprintf(message + len, sizeof(message) - (len + 2), format, ap);
1516 0 : va_end(ap);
1517 :
1518 : #ifndef __COVERITY__
1519 0 : if (GDKfataljumpenable) {
1520 : // in embedded mode, we really don't want to kill our host
1521 0 : GDKfatalmsg = GDKstrdup(message);
1522 0 : longjmp(GDKfataljump, 42);
1523 : } else
1524 : #endif
1525 : {
1526 0 : fputs(message, stderr);
1527 0 : fputs("\n", stderr);
1528 0 : fflush(stderr);
1529 :
1530 : /*
1531 : * Real errors should be saved in the log file for post-crash
1532 : * inspection.
1533 : */
1534 0 : if (GDKexiting()) {
1535 0 : fflush(stdout);
1536 0 : exit(1);
1537 : } else {
1538 0 : GDKlog(GET_GDKLOCK(PERSISTENT), "%s", message);
1539 : #ifdef COREDUMP
1540 : abort();
1541 : #else
1542 0 : exit(1);
1543 : #endif
1544 : }
1545 : }
1546 : }
1547 :
1548 :
1549 : lng
1550 179226018 : GDKusec(void)
1551 : {
1552 : /* Return the time in microseconds since an epoch. The epoch
1553 : * is currently midnight at the start of January 1, 1970, UTC. */
1554 : #if defined(NATIVE_WIN32)
1555 : FILETIME ft;
1556 : ULARGE_INTEGER f;
1557 : GetSystemTimeAsFileTime(&ft); /* time since Jan 1, 1601 */
1558 : f.LowPart = ft.dwLowDateTime;
1559 : f.HighPart = ft.dwHighDateTime;
1560 : /* there are 369 years, of which 89 are leap years from
1561 : * January 1, 1601 to January 1, 1970 which makes 134774 days;
1562 : * multiply that with the number of seconds in a day and the
1563 : * number of 100ns units in a second; subtract that from the
1564 : * value for the current time since January 1, 1601 to get the
1565 : * time since the Unix epoch */
1566 : f.QuadPart -= LL_CONSTANT(134774) * 24 * 60 * 60 * 10000000;
1567 : /* and convert to microseconds */
1568 : return (lng) (f.QuadPart / 10);
1569 : #elif defined(HAVE_CLOCK_GETTIME)
1570 179226018 : struct timespec ts;
1571 179226018 : (void) clock_gettime(CLOCK_REALTIME, &ts);
1572 179226900 : return (lng) (ts.tv_sec * LL_CONSTANT(1000000) + ts.tv_nsec / 1000);
1573 : #elif defined(HAVE_GETTIMEOFDAY)
1574 : struct timeval tv;
1575 : gettimeofday(&tv, NULL);
1576 : return (lng) (tv.tv_sec * LL_CONSTANT(1000000) + tv.tv_usec);
1577 : #elif defined(HAVE_FTIME)
1578 : struct timeb tb;
1579 : ftime(&tb);
1580 : return (lng) (tb.time * LL_CONSTANT(1000000) + tb.millitm * LL_CONSTANT(1000));
1581 : #else
1582 : /* last resort */
1583 : return (lng) (time(NULL) * LL_CONSTANT(1000000));
1584 : #endif
1585 : }
1586 :
1587 :
1588 : int
1589 0 : GDKms(void)
1590 : {
1591 : /* wraps around after a bit over 24 days */
1592 0 : return (int) ((GDKusec() - programepoch) / 1000);
1593 : }
1594 :
1595 :
1596 : /*
1597 : * @+ Logical Thread management
1598 : *
1599 : * All semaphores used by the application should be mentioned here.
1600 : * They are initialized during system initialization.
1601 : *
1602 : * The first action upon thread creation is to add it to the pool of
1603 : * known threads. This should be done by the thread itself.
1604 : * Note that the users should have gained exclusive access already. A
1605 : * new entry is initialized automatically when not found. Its file
1606 : * descriptors are the same as for the server and should be
1607 : * subsequently reset.
1608 : */
1609 : stream *GDKstdout;
1610 : stream *GDKstdin;
1611 :
1612 : static int
1613 359 : THRinit(void)
1614 : {
1615 359 : if ((GDKstdout = stdout_wastream()) == NULL) {
1616 0 : TRC_CRITICAL(GDK, "malloc for stdout failed\n");
1617 0 : return -1;
1618 : }
1619 359 : if ((GDKstdin = stdin_rastream()) == NULL) {
1620 0 : TRC_CRITICAL(GDK, "malloc for stdin failed\n");
1621 0 : mnstr_destroy(GDKstdout);
1622 0 : GDKstdout = NULL;
1623 0 : return -1;
1624 : }
1625 359 : struct freebats *t = MT_thread_getfreebats();
1626 359 : t->freebats = 0;
1627 359 : t->nfreebats = 0;
1628 359 : return 0;
1629 : }
1630 :
1631 : const char *
1632 346 : GDKversion(void)
1633 : {
1634 346 : return MONETDB_VERSION;
1635 : }
1636 :
1637 : const char *
1638 703 : GDKlibversion(void)
1639 : {
1640 703 : return GDK_VERSION;
1641 : }
1642 :
1643 : inline size_t
1644 156103688 : GDKmem_cursize(void)
1645 : {
1646 : /* RAM/swapmem that Monet is really using now */
1647 156103688 : return (size_t) ATOMIC_GET(&GDK_mallocedbytes_estimate);
1648 : }
1649 :
1650 : inline size_t
1651 148449658 : GDKvm_cursize(void)
1652 : {
1653 : /* current Monet VM address space usage */
1654 148449658 : return (size_t) ATOMIC_GET(&GDK_vm_cursize) + GDKmem_cursize();
1655 : }
1656 :
1657 : #define heapinc(_memdelta) \
1658 : ATOMIC_ADD(&GDK_mallocedbytes_estimate, _memdelta)
1659 : #ifndef NDEBUG
1660 : #define heapdec(_memdelta) \
1661 : do { \
1662 : ATOMIC_BASE_TYPE old = ATOMIC_SUB(&GDK_mallocedbytes_estimate, _memdelta); \
1663 : assert(old >= (ATOMIC_BASE_TYPE) _memdelta); \
1664 : } while (0)
1665 : #else
1666 : #define heapdec(_memdelta) \
1667 : ATOMIC_SUB(&GDK_mallocedbytes_estimate, _memdelta)
1668 : #endif
1669 :
1670 : #define meminc(vmdelta) \
1671 : ATOMIC_ADD(&GDK_vm_cursize, SEG_SIZE(vmdelta))
1672 : #ifndef NDEBUG
1673 : #define memdec(vmdelta) \
1674 : do { \
1675 : ssize_t diff = SEG_SIZE(vmdelta); \
1676 : ATOMIC_BASE_TYPE old = ATOMIC_SUB(&GDK_vm_cursize, diff); \
1677 : assert(old >= (ATOMIC_BASE_TYPE) diff); \
1678 : } while (0)
1679 : #else
1680 : #define memdec(vmdelta) \
1681 : ATOMIC_SUB(&GDK_vm_cursize, SEG_SIZE(vmdelta))
1682 : #endif
1683 :
1684 : /* Memory allocation
1685 : *
1686 : * The functions GDKmalloc, GDKzalloc, GDKrealloc, GDKstrdup, and
1687 : * GDKfree are used throughout to allocate and free memory. These
1688 : * functions are almost directly mapped onto the system
1689 : * malloc/realloc/free functions, but they give us some extra
1690 : * debugging hooks.
1691 : *
1692 : * When allocating memory, we allocate a bit more than was asked for.
1693 : * The extra space is added onto the front of the memory area that is
1694 : * returned, and in debug builds also some at the end. The area in
1695 : * front is used to store the actual size of the allocated area. The
1696 : * most important use is to be able to keep statistics on how much
1697 : * memory is being used. In debug builds, the size is also used to
1698 : * make sure that we don't write outside of the allocated arena. This
1699 : * is also where the extra space at the end comes in.
1700 : */
1701 :
1702 : /* we allocate extra space and return a pointer offset by this amount */
1703 : #define MALLOC_EXTRA_SPACE (2 * SIZEOF_VOID_P)
1704 :
1705 : #if defined(NDEBUG) || defined(SANITIZER)
1706 : #define DEBUG_SPACE 0
1707 : #else
1708 : #define DEBUG_SPACE 16
1709 : #endif
1710 :
1711 : /* malloc smaller than this aren't subject to the GDK_vm_maxsize test */
1712 : #define SMALL_MALLOC 256
1713 :
1714 : static void *
1715 158521650 : GDKmalloc_internal(size_t size, bool clear)
1716 : {
1717 158521650 : void *s;
1718 158521650 : size_t nsize;
1719 :
1720 158521650 : assert(size != 0);
1721 : #ifndef SIZE_CHECK_IN_HEAPS_ONLY
1722 : if (size > SMALL_MALLOC &&
1723 : GDKvm_cursize() + size >= GDK_vm_maxsize &&
1724 : !MT_thread_override_limits()) {
1725 : GDKerror("allocating too much memory\n");
1726 : return NULL;
1727 : }
1728 : #endif
1729 :
1730 : /* pad to multiple of eight bytes and add some extra space to
1731 : * write real size in front; when debugging, also allocate
1732 : * extra space for check bytes */
1733 158521650 : nsize = (size + 7) & ~7;
1734 158521650 : if (clear)
1735 28153191 : s = calloc(nsize + MALLOC_EXTRA_SPACE + DEBUG_SPACE, 1);
1736 : else
1737 130368459 : s = malloc(nsize + MALLOC_EXTRA_SPACE + DEBUG_SPACE);
1738 158521650 : if (s == NULL) {
1739 0 : GDKsyserror("malloc failed; memory requested: %zu, memory in use: %zu, virtual memory in use: %zu\n", size, GDKmem_cursize(), GDKvm_cursize());;
1740 0 : return NULL;
1741 : }
1742 158521650 : s = (void *) ((char *) s + MALLOC_EXTRA_SPACE);
1743 :
1744 158521650 : heapinc(nsize + MALLOC_EXTRA_SPACE + DEBUG_SPACE);
1745 :
1746 : /* just before the pointer that we return, write how much we
1747 : * asked of malloc */
1748 158521650 : ((size_t *) s)[-1] = nsize + MALLOC_EXTRA_SPACE + DEBUG_SPACE;
1749 : #if !defined(NDEBUG) && !defined(SANITIZER)
1750 : /* just before that, write how much was asked of us */
1751 158521650 : ((size_t *) s)[-2] = size;
1752 : /* write pattern to help find out-of-bounds writes */
1753 158521650 : memset((char *) s + size, '\xBD', nsize + DEBUG_SPACE - size);
1754 : #endif
1755 158521650 : return s;
1756 : }
1757 :
1758 : #undef GDKmalloc
1759 : void *
1760 122034296 : GDKmalloc(size_t size)
1761 : {
1762 122034296 : void *s;
1763 :
1764 122034296 : if ((s = GDKmalloc_internal(size, false)) == NULL)
1765 : return NULL;
1766 : #if !defined(NDEBUG) && !defined(SANITIZER)
1767 : /* write a pattern to help make sure all data is properly
1768 : * initialized by the caller */
1769 122021774 : DEADBEEFCHK memset(s, '\xBD', size);
1770 : #endif
1771 : return s;
1772 : }
1773 :
1774 : #undef GDKzalloc
1775 : void *
1776 28153573 : GDKzalloc(size_t size)
1777 : {
1778 28153573 : return GDKmalloc_internal(size, true);
1779 : }
1780 :
1781 : #undef GDKstrdup
1782 : char *
1783 8998775 : GDKstrdup(const char *s)
1784 : {
1785 8998775 : size_t size;
1786 8998775 : char *p;
1787 :
1788 8998775 : if (s == NULL)
1789 : return NULL;
1790 8344083 : size = strlen(s) + 1;
1791 :
1792 8344083 : if ((p = GDKmalloc_internal(size, false)) == NULL)
1793 : return NULL;
1794 8344144 : memcpy(p, s, size); /* including terminating NULL byte */
1795 8344144 : return p;
1796 : }
1797 :
1798 : #undef GDKstrndup
1799 : char *
1800 233 : GDKstrndup(const char *s, size_t size)
1801 : {
1802 233 : char *p;
1803 :
1804 233 : if (s == NULL)
1805 : return NULL;
1806 233 : if ((p = GDKmalloc_internal(size + 1, false)) == NULL)
1807 : return NULL;
1808 233 : if (size > 0)
1809 233 : memcpy(p, s, size);
1810 233 : p[size] = '\0'; /* make sure it's NULL terminated */
1811 233 : return p;
1812 : }
1813 :
1814 : #undef GDKfree
1815 : void
1816 339416652 : GDKfree(void *s)
1817 : {
1818 339416652 : size_t asize;
1819 :
1820 339416652 : if (s == NULL)
1821 : return;
1822 :
1823 158396710 : asize = ((size_t *) s)[-1]; /* how much allocated last */
1824 :
1825 : #if !defined(NDEBUG) && !defined(SANITIZER)
1826 158396710 : size_t *p = s;
1827 158396710 : assert((asize & 2) == 0); /* check against duplicate free */
1828 158396710 : size_t size = p[-2];
1829 158396710 : assert(((size + 7) & ~7) + MALLOC_EXTRA_SPACE + DEBUG_SPACE == asize);
1830 : /* check for out-of-bounds writes */
1831 3020599366 : for (size_t i = size; i < asize - MALLOC_EXTRA_SPACE; i++)
1832 2862202656 : assert(((char *) s)[i] == '\xBD');
1833 158396710 : p[-1] |= 2; /* indicate area is freed */
1834 :
1835 : /* overwrite memory that is to be freed with a pattern that
1836 : * will help us recognize access to already freed memory in
1837 : * the debugger */
1838 158396710 : DEADBEEFCHK memset(s, '\xDB', asize - MALLOC_EXTRA_SPACE);
1839 : #endif
1840 :
1841 158396710 : free((char *) s - MALLOC_EXTRA_SPACE);
1842 158396710 : heapdec((ssize_t) asize);
1843 : }
1844 :
1845 : #undef GDKrealloc
1846 : void *
1847 245164 : GDKrealloc(void *s, size_t size)
1848 : {
1849 245164 : size_t nsize, asize;
1850 : #if !defined(NDEBUG) && !defined(SANITIZER)
1851 245164 : size_t osize;
1852 : #endif
1853 245164 : size_t *os = s;
1854 :
1855 245164 : assert(size != 0);
1856 :
1857 245164 : if (s == NULL)
1858 843 : return GDKmalloc(size);
1859 :
1860 244321 : nsize = (size + 7) & ~7;
1861 244321 : asize = os[-1]; /* how much allocated last */
1862 :
1863 : #ifndef SIZE_CHECK_IN_HEAPS_ONLY
1864 : if (size > SMALL_MALLOC &&
1865 : nsize > asize &&
1866 : GDKvm_cursize() + nsize - asize >= GDK_vm_maxsize &&
1867 : !MT_thread_override_limits()) {
1868 : GDKerror("allocating too much memory\n");
1869 : return NULL;
1870 : }
1871 : #endif
1872 : #if !defined(NDEBUG) && !defined(SANITIZER)
1873 244321 : assert((asize & 2) == 0); /* check against duplicate free */
1874 : /* check for out-of-bounds writes */
1875 244321 : osize = os[-2]; /* how much asked for last */
1876 244321 : assert(((osize + 7) & ~7) + MALLOC_EXTRA_SPACE + DEBUG_SPACE == asize);
1877 4225720 : for (size_t i = osize; i < asize - MALLOC_EXTRA_SPACE; i++)
1878 3981399 : assert(((char *) s)[i] == '\xBD');
1879 : /* if shrinking, write debug pattern into to-be-freed memory */
1880 244321 : DEADBEEFCHK if (size < osize)
1881 35267 : memset((char *) s + size, '\xDB', osize - size);
1882 244321 : os[-1] |= 2; /* indicate area is freed */
1883 : #endif
1884 244321 : s = realloc((char *) s - MALLOC_EXTRA_SPACE,
1885 : nsize + MALLOC_EXTRA_SPACE + DEBUG_SPACE);
1886 244321 : if (s == NULL) {
1887 : #if !defined(NDEBUG) && !defined(SANITIZER)
1888 0 : os[-1] &= ~2; /* not freed after all */
1889 0 : assert(os[-1] == asize);
1890 0 : assert(os[-2] == osize);
1891 : #endif
1892 0 : GDKsyserror("realloc failed; memory requested: %zu, memory in use: %zu, virtual memory in use: %zu\n", size, GDKmem_cursize(), GDKvm_cursize());;
1893 0 : return NULL;
1894 : }
1895 244321 : s = (void *) ((char *) s + MALLOC_EXTRA_SPACE);
1896 : /* just before the pointer that we return, write how much we
1897 : * asked of malloc */
1898 244321 : ((size_t *) s)[-1] = nsize + MALLOC_EXTRA_SPACE + DEBUG_SPACE;
1899 : #if !defined(NDEBUG) && !defined(SANITIZER)
1900 : /* just before that, write how much was asked of us */
1901 244321 : ((size_t *) s)[-2] = size;
1902 : /* if growing, initialize new memory with debug pattern */
1903 244321 : DEADBEEFCHK if (size > osize)
1904 208982 : memset((char *) s + osize, '\xBD', size - osize);
1905 : /* write pattern to help find out-of-bounds writes */
1906 244321 : memset((char *) s + size, '\xBD', nsize + DEBUG_SPACE - size);
1907 : #endif
1908 :
1909 244321 : heapinc(nsize + MALLOC_EXTRA_SPACE + DEBUG_SPACE);
1910 244321 : heapdec((ssize_t) asize);
1911 :
1912 : return s;
1913 : }
1914 :
1915 : /* return how much memory was allocated; the argument must be a value
1916 : * returned by GDKmalloc, GDKzalloc, GDKrealloc, GDKstrdup, or
1917 : * GDKstrndup */
1918 : size_t
1919 200199 : GDKmallocated(const void *s)
1920 : {
1921 200199 : return ((const size_t *) s)[-1]; /* how much allocated last */
1922 : }
1923 :
1924 : /*
1925 : * @- virtual memory
1926 : * allocations affect only the logical VM resources.
1927 : */
1928 : #undef GDKmmap
1929 : void *
1930 3020 : GDKmmap(const char *path, int mode, size_t len)
1931 : {
1932 3020 : void *ret;
1933 :
1934 : #ifndef SIZE_CHECK_IN_HEAPS_ONLY
1935 : if (GDKvm_cursize() + len >= GDK_vm_maxsize &&
1936 : !MT_thread_override_limits()) {
1937 : GDKerror("requested too much virtual memory; memory requested: %zu, memory in use: %zu, virtual memory in use: %zu\n", len, GDKmem_cursize(), GDKvm_cursize());
1938 : return NULL;
1939 : }
1940 : #endif
1941 3020 : ret = MT_mmap(path, mode, len);
1942 3020 : if (ret != NULL) {
1943 3020 : if (mode & MMAP_COPY)
1944 0 : heapinc(len);
1945 : else
1946 3020 : meminc(len);
1947 : } else
1948 0 : GDKerror("requesting virtual memory failed; memory requested: %zu, memory in use: %zu, virtual memory in use: %zu\n", len, GDKmem_cursize(), GDKvm_cursize());
1949 3020 : return ret;
1950 : }
1951 :
1952 : #undef GDKmunmap
1953 : gdk_return
1954 3020 : GDKmunmap(void *addr, int mode, size_t size)
1955 : {
1956 3020 : int ret;
1957 :
1958 3020 : ret = MT_munmap(addr, size);
1959 3020 : if (ret == 0) {
1960 3020 : if (mode & MMAP_COPY)
1961 0 : heapdec(size);
1962 : else
1963 3020 : memdec(size);
1964 : }
1965 3020 : return ret == 0 ? GDK_SUCCEED : GDK_FAIL;
1966 : }
1967 :
1968 : #undef GDKmremap
1969 : void *
1970 560 : GDKmremap(const char *path, int mode, void *old_address, size_t old_size, size_t *new_size)
1971 : {
1972 560 : void *ret;
1973 :
1974 : #ifndef SIZE_CHECK_IN_HEAPS_ONLY
1975 : if (*new_size > old_size &&
1976 : GDKvm_cursize() + *new_size - old_size >= GDK_vm_maxsize &&
1977 : !MT_thread_override_limits()) {
1978 : 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());
1979 : return NULL;
1980 : }
1981 : #endif
1982 560 : ret = MT_mremap(path, mode, old_address, old_size, new_size);
1983 560 : if (ret != NULL) {
1984 560 : if (mode & MMAP_COPY) {
1985 0 : heapdec(old_size);
1986 0 : heapinc(*new_size);
1987 : } else {
1988 560 : memdec(old_size);
1989 560 : meminc(*new_size);
1990 : }
1991 : } else {
1992 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());
1993 : }
1994 560 : return ret;
1995 : }
1996 :
1997 : /* print some potentially interesting information */
1998 : struct prinfocb {
1999 : struct prinfocb *next;
2000 : void (*func)(void);
2001 : } *prinfocb;
2002 :
2003 : void
2004 356 : GDKprintinforegister(void (*func)(void))
2005 : {
2006 356 : struct prinfocb *p = GDKmalloc(sizeof(struct prinfocb));
2007 356 : if (p == NULL) {
2008 0 : GDKerror("cannot register USR1 printing function.\n");
2009 0 : return;
2010 : }
2011 356 : p->func = func;
2012 356 : p->next = NULL;
2013 356 : struct prinfocb **pp = &prinfocb;
2014 411 : while (*pp != NULL)
2015 55 : pp = &(*pp)->next;
2016 356 : *pp = p;
2017 : }
2018 :
2019 : void
2020 118 : GDKprintinfo(void)
2021 : {
2022 118 : size_t allocated = (size_t) ATOMIC_GET(&GDK_mallocedbytes_estimate);
2023 118 : size_t vmallocated = (size_t) ATOMIC_GET(&GDK_vm_cursize);
2024 :
2025 118 : printf("SIGUSR1 info start\n");
2026 118 : printf("Virtual memory allocated: %zu, of which %zu with malloc\n",
2027 : vmallocated + allocated, allocated);
2028 118 : printf("gdk_vm_maxsize: %zu, gdk_mem_maxsize: %zu\n",
2029 : GDK_vm_maxsize, GDK_mem_maxsize);
2030 118 : printf("gdk_mmap_minsize_persistent %zu, gdk_mmap_minsize_transient %zu\n",
2031 : GDK_mmap_minsize_persistent, GDK_mmap_minsize_transient);
2032 : #ifdef __linux__
2033 118 : int fd = open("/proc/self/statm", O_RDONLY | O_CLOEXEC);
2034 118 : if (fd >= 0) {
2035 118 : char buf[512];
2036 118 : ssize_t s = read(fd, buf, sizeof(buf) - 1);
2037 118 : close(fd);
2038 118 : if (s > 0) {
2039 118 : assert((size_t) s < sizeof(buf));
2040 118 : size_t size, resident, shared;
2041 118 : buf[s] = 0;
2042 118 : if (sscanf(buf, "%zu %zu %zu", &size, &resident, &shared) == 3) {
2043 118 : size *= MT_pagesize();
2044 118 : resident *= MT_pagesize();
2045 118 : shared *= MT_pagesize();
2046 118 : printf("Virtual size: %zu, anonymous RSS: %zu, shared RSS: %zu (together: %zu)\n",
2047 : size, resident - shared, shared, resident);
2048 : }
2049 : }
2050 : }
2051 : #endif
2052 118 : BBPprintinfo();
2053 : #ifdef LOCK_STATS
2054 : GDKlockstatistics(3);
2055 : #endif
2056 118 : dump_threads();
2057 236 : for (struct prinfocb *p = prinfocb; p; p = p->next)
2058 118 : (*p->func)();
2059 118 : printf("SIGUSR1 info end\n");
2060 118 : }
2061 :
2062 : exception_buffer *
2063 724181 : eb_init(exception_buffer *eb)
2064 : {
2065 724181 : if (eb) {
2066 724181 : eb->enabled = 0;
2067 724181 : eb->code = 0;
2068 724181 : eb->msg = NULL;
2069 : }
2070 724181 : return eb;
2071 : }
2072 :
2073 : void
2074 3 : eb_error(exception_buffer *eb, const char *msg, int val)
2075 : {
2076 3 : eb->code = val;
2077 3 : eb->msg = msg;
2078 3 : eb->enabled = 0; /* not any longer... */
2079 : #ifdef HAVE_SIGLONGJMP
2080 3 : siglongjmp(eb->state, eb->code);
2081 : #else
2082 : longjmp(eb->state, eb->code);
2083 : #endif
2084 : }
2085 :
2086 : #define SA_BLOCK (64*1024)
2087 :
2088 : typedef struct freed_t {
2089 : struct freed_t *n;
2090 : size_t sz;
2091 : } freed_t;
2092 :
2093 : static void
2094 71120 : sa_destroy_freelist(freed_t *f)
2095 : {
2096 87359 : while(f) {
2097 16239 : freed_t *n = f->n;
2098 16239 : GDKfree(f);
2099 16239 : f = n;
2100 : }
2101 : }
2102 :
2103 : static void
2104 200199 : sa_free(allocator *pa, void *blk)
2105 : {
2106 200199 : assert(!pa->pa);
2107 : size_t i;
2108 :
2109 1167496 : for(i = 0; i < pa->nr; i++) {
2110 1167496 : if (pa->blks[i] == blk)
2111 : break;
2112 : }
2113 200199 : assert (i < pa->nr);
2114 2044809 : for (; i < pa->nr-1; i++)
2115 1844610 : pa->blks[i] = pa->blks[i+1];
2116 200199 : pa->nr--;
2117 :
2118 200199 : size_t sz = GDKmallocated(blk);
2119 200199 : if (sz > (SA_BLOCK + 32)) {
2120 90 : GDKfree(blk);
2121 : } else {
2122 200109 : freed_t *f = blk;
2123 200109 : f->n = pa->freelist;
2124 200109 : f->sz = sz;
2125 :
2126 200109 : pa->freelist = f;
2127 : }
2128 200199 : }
2129 :
2130 : static void *
2131 183870 : sa_use_freed(allocator *pa, size_t sz)
2132 : {
2133 183870 : (void)sz;
2134 :
2135 183870 : freed_t *f = pa->freelist;
2136 183870 : pa->freelist = f->n;
2137 183870 : return f;
2138 : }
2139 :
2140 : allocator *
2141 282585 : sa_create(allocator *pa)
2142 : {
2143 282585 : allocator *sa = (pa)?(allocator*)sa_alloc(pa, sizeof(allocator)):(allocator*)GDKmalloc(sizeof(allocator));
2144 282584 : if (sa == NULL)
2145 : return NULL;
2146 282584 : eb_init(&sa->eb);
2147 282584 : sa->pa = pa;
2148 282584 : sa->size = 64;
2149 282584 : sa->nr = 1;
2150 282584 : sa->blks = pa?(char**)sa_alloc(pa, sizeof(char*) * sa->size):(char**)GDKmalloc(sizeof(char*) * sa->size);
2151 282584 : sa->freelist = NULL;
2152 282584 : if (sa->blks == NULL) {
2153 0 : if (!pa)
2154 0 : GDKfree(sa);
2155 0 : return NULL;
2156 : }
2157 282584 : sa->blks[0] = pa?(char*)sa_alloc(pa, SA_BLOCK):(char*)GDKmalloc(SA_BLOCK);
2158 282582 : sa->usedmem = SA_BLOCK;
2159 282582 : if (sa->blks[0] == NULL) {
2160 0 : if (!pa)
2161 0 : GDKfree(sa->blks);
2162 0 : if (!pa)
2163 0 : GDKfree(sa);
2164 0 : return NULL;
2165 : }
2166 282582 : sa->used = 0;
2167 282582 : return sa;
2168 : }
2169 :
2170 : allocator *
2171 1999237 : sa_reset(allocator *sa)
2172 : {
2173 1999237 : size_t i ;
2174 :
2175 2063606 : for (i = 1; i<sa->nr; i++) {
2176 64373 : if (!sa->pa)
2177 0 : GDKfree(sa->blks[i]);
2178 : else
2179 64373 : sa_free(sa->pa, sa->blks[i]);
2180 : }
2181 1999233 : sa->nr = 1;
2182 1999233 : sa->used = 0;
2183 1999233 : sa->usedmem = SA_BLOCK;
2184 1999233 : return sa;
2185 : }
2186 :
2187 : #undef sa_realloc
2188 : #undef sa_alloc
2189 : void *
2190 33 : sa_realloc(allocator *sa, void *p, size_t sz, size_t oldsz)
2191 : {
2192 33 : void *r = sa_alloc(sa, sz);
2193 :
2194 33 : if (r)
2195 33 : memcpy(r, p, oldsz);
2196 33 : return r;
2197 : }
2198 :
2199 : #define round16(sz) ((sz+15)&~15)
2200 : void *
2201 252186241 : sa_alloc(allocator *sa, size_t sz)
2202 : {
2203 252186241 : char *r;
2204 252186241 : sz = round16(sz);
2205 252186241 : if (sz > (SA_BLOCK-sa->used)) {
2206 359145 : if (sa->pa)
2207 64610 : r = (char*)sa_alloc(sa->pa, (sz > SA_BLOCK ? sz : SA_BLOCK));
2208 294535 : else if (sz <= SA_BLOCK && sa->freelist) {
2209 183870 : r = sa_use_freed(sa, SA_BLOCK);
2210 : } else
2211 110665 : r = GDKmalloc(sz > SA_BLOCK ? sz : SA_BLOCK);
2212 359145 : if (r == NULL) {
2213 0 : if (sa->eb.enabled)
2214 0 : eb_error(&sa->eb, "out of memory", 1000);
2215 : return NULL;
2216 : }
2217 359145 : if (sa->nr >= sa->size) {
2218 40 : char **tmp;
2219 40 : size_t osz = sa->size;
2220 40 : sa->size *=2;
2221 40 : if (sa->pa)
2222 19 : tmp = (char**)sa_realloc(sa->pa, sa->blks, sizeof(char*) * sa->size, sizeof(char*) * osz);
2223 : else
2224 21 : tmp = GDKrealloc(sa->blks, sizeof(char*) * sa->size);
2225 40 : if (tmp == NULL) {
2226 0 : sa->size /= 2; /* undo */
2227 0 : if (sa->eb.enabled)
2228 0 : eb_error(&sa->eb, "out of memory", 1000);
2229 0 : if (!sa->pa)
2230 0 : GDKfree(r);
2231 0 : return NULL;
2232 : }
2233 40 : sa->blks = tmp;
2234 : }
2235 359145 : if (sz >= SA_BLOCK) {
2236 : // The request is large so it gets its own block.
2237 : // We put it 'under' the current block because
2238 : // there may still be plenty of usable space there.
2239 276533 : sa->blks[sa->nr] = sa->blks[sa->nr-1];
2240 276533 : sa->blks[sa->nr-1] = r;
2241 276533 : sa->nr ++;
2242 276533 : sa->usedmem += sz;
2243 : } else {
2244 82612 : sa->blks[sa->nr] = r;
2245 82612 : sa->nr ++;
2246 82612 : sa->used = sz;
2247 82612 : sa->usedmem += SA_BLOCK;
2248 : }
2249 : } else {
2250 251827096 : r = sa->blks[sa->nr-1] + sa->used;
2251 251827096 : sa->used += sz;
2252 : }
2253 : return r;
2254 : }
2255 :
2256 : #undef sa_zalloc
2257 : void *
2258 10938961 : sa_zalloc(allocator *sa, size_t sz)
2259 : {
2260 10938961 : void *r = sa_alloc(sa, sz);
2261 :
2262 10938914 : if (r)
2263 10938914 : memset(r, 0, sz);
2264 10938914 : return r;
2265 : }
2266 :
2267 : void
2268 206946 : sa_destroy(allocator *sa)
2269 : {
2270 206946 : if (sa->pa) {
2271 135826 : sa_reset(sa);
2272 135826 : sa_free(sa->pa, sa->blks[0]);
2273 135826 : return;
2274 : }
2275 :
2276 71120 : sa_destroy_freelist(sa->freelist);
2277 236569 : for (size_t i = 0; i<sa->nr; i++) {
2278 165449 : GDKfree(sa->blks[i]);
2279 : }
2280 71120 : GDKfree(sa->blks);
2281 71120 : GDKfree(sa);
2282 : }
2283 :
2284 : #undef sa_strndup
2285 : char *
2286 13107742 : sa_strndup(allocator *sa, const char *s, size_t l)
2287 : {
2288 13107742 : char *r = sa_alloc(sa, l+1);
2289 :
2290 13107676 : if (r) {
2291 13107676 : memcpy(r, s, l);
2292 13107676 : r[l] = 0;
2293 : }
2294 13107676 : return r;
2295 : }
2296 :
2297 : #undef sa_strdup
2298 : char *
2299 7505510 : sa_strdup(allocator *sa, const char *s)
2300 : {
2301 7505510 : return sa_strndup(sa, s, strlen(s));
2302 : }
2303 :
2304 : char *
2305 43169 : sa_strconcat(allocator *sa, const char *s1, const char *s2)
2306 : {
2307 43169 : size_t l1 = strlen(s1);
2308 43169 : size_t l2 = strlen(s2);
2309 43169 : char *r = sa_alloc(sa, l1+l2+1);
2310 :
2311 43169 : if (l1)
2312 43169 : memcpy(r, s1, l1);
2313 43169 : if (l2)
2314 43169 : memcpy(r+l1, s2, l2);
2315 43169 : r[l1+l2] = 0;
2316 43169 : return r;
2317 : }
2318 :
2319 : size_t
2320 0 : sa_size(allocator *sa)
2321 : {
2322 0 : return sa->usedmem;
2323 : }
|