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 359 : GDKenvironment(const char *dbpath)
94 : {
95 359 : if (dbpath == NULL) {
96 0 : TRC_CRITICAL(GDK, "Database name missing.\n");
97 0 : return false;
98 : }
99 359 : if (strlen(dbpath) >= FILENAME_MAX) {
100 0 : TRC_CRITICAL(GDK, "Database name too long.\n");
101 0 : return false;
102 : }
103 359 : 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 480174 : GDKgetenv(const char *name)
119 : {
120 480174 : MT_lock_set(&GDKenvlock);
121 480245 : 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 480245 : MT_lock_unset(&GDKenvlock);
128 480244 : if (GDKkey && GDKval) {
129 479546 : BUN b = BUNfnd(GDKkey, name);
130 :
131 479546 : if (b != BUN_NONE) {
132 337016 : BATiter GDKenvi = bat_iterator(GDKval);
133 337017 : const char *v = BUNtvar(GDKenvi, b);
134 337017 : bat_iterator_end(&GDKenvi);
135 337017 : return v;
136 : }
137 : }
138 : return NULL;
139 : }
140 :
141 : bool
142 293060 : GDKgetenv_istext(const char *name, const char *text)
143 : {
144 293060 : const char *val = GDKgetenv(name);
145 :
146 293066 : return val && strcasecmp(val, text) == 0;
147 : }
148 :
149 : bool
150 39037 : GDKgetenv_isyes(const char *name)
151 : {
152 39037 : return GDKgetenv_istext(name, "yes");
153 : }
154 :
155 : bool
156 254029 : GDKgetenv_istrue(const char *name)
157 : {
158 254029 : return GDKgetenv_istext(name, "true");
159 : }
160 :
161 : int
162 97489 : GDKgetenv_int(const char *name, int def)
163 : {
164 97489 : const char *val = GDKgetenv(name);
165 :
166 97494 : if (val)
167 346 : return atoi(val);
168 : return def;
169 : }
170 :
171 : #define ESCAPE_CHAR '%'
172 :
173 : static bool
174 8747 : isutf8(const char *v, size_t *esclen)
175 : {
176 8747 : size_t n = 1;
177 8747 : int nutf8 = 0;
178 8747 : int m = 0;
179 165894 : for (size_t i = 0; v[i]; i++) {
180 157147 : 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 157139 : } else if ((v[i] & 0xE0) == 0xC0) {
187 0 : nutf8 = 1;
188 0 : if ((v[i] & 0x1E) == 0)
189 0 : goto badutf8;
190 157139 : } else if ((v[i] & 0xF0) == 0xE0) {
191 4 : nutf8 = 2;
192 4 : if ((v[i] & 0x0F) == 0)
193 0 : m = 0x20;
194 157135 : } else if ((v[i] & 0xF8) == 0xF0) {
195 0 : nutf8 = 3;
196 0 : if ((v[i] & 0x07) == 0)
197 0 : m = 0x30;
198 157135 : } else if ((v[i] & 0x80) != 0) {
199 0 : goto badutf8;
200 : }
201 : }
202 8747 : *esclen = 0;
203 8747 : 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 8747 : GDKsetenv(const char *name, const char *value)
217 : {
218 8747 : static const char hexdigits[] = "0123456789abcdef";
219 8747 : char *conval = NULL;
220 8747 : size_t esclen = 0;
221 8747 : 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 8747 : MT_lock_set(&GDKenvlock);
260 8747 : 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 8747 : MT_lock_unset(&GDKenvlock);
269 : }
270 8747 : BUN p = BUNfnd(GDKkey, name);
271 8747 : gdk_return rc;
272 8747 : if (p != BUN_NONE) {
273 2070 : rc = BUNreplace(GDKval, p + GDKval->hseqbase,
274 : conval ? conval : value, false);
275 : } else {
276 7712 : rc = BUNappend(GDKkey, name, false);
277 7712 : if (rc == GDK_SUCCEED) {
278 15424 : rc = BUNappend(GDKval, conval ? conval : value, false);
279 7712 : 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 8747 : assert(BATcount(GDKval) == BATcount(GDKkey));
290 8747 : GDKfree(conval);
291 8747 : 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 905 : GDKlog(FILE *lockFile, const char *format, ...)
345 : {
346 905 : va_list ap;
347 905 : char *p = 0, buf[1024];
348 905 : time_t tm = time(0);
349 : #if defined(HAVE_CTIME_R3) || defined(HAVE_CTIME_R)
350 905 : char tbuf[26];
351 : #endif
352 905 : char *ctm;
353 :
354 905 : if (MT_pagesize() == 0 || lockFile == NULL)
355 1 : return;
356 :
357 904 : va_start(ap, format);
358 904 : vsnprintf(buf, sizeof(buf), format, ap);
359 904 : va_end(ap);
360 :
361 : /* remove forbidden characters from message */
362 904 : for (p = buf; (p = strchr(p, '\n')) != NULL; *p = ' ')
363 : ;
364 904 : for (p = buf; (p = strchr(p, '@')) != NULL; *p = ' ')
365 : ;
366 :
367 904 : 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 904 : ctm = ctime_r(&tm, tbuf);
375 : #endif
376 904 : fprintf(lockFile, "USR=%d PID=%d TIME=%.24s @ %s\n", (int) getuid(), (int) getpid(), ctm, buf);
377 904 : 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 360 : BATSIGinit(void)
395 : {
396 : #ifdef HAVE_SIGACTION
397 360 : struct sigaction sa;
398 360 : sigemptyset(&sa.sa_mask);
399 360 : sa.sa_flags = 0;
400 : #ifdef SIGPIPE
401 360 : sa.sa_handler = SIG_IGN;
402 360 : sigaction(SIGPIPE, &sa, NULL);
403 : #endif
404 : #ifdef SIGHUP
405 360 : sa.sa_handler = GDKtracer_reinit_basic;
406 360 : 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 360 : }
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 361 : MT_init(void)
464 : {
465 361 : 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 361 : _MT_pagesize = (size_t)sysconf(_SC_PAGESIZE);
488 : #endif
489 361 : 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 361 : _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 361 : FILE *fc;
561 361 : char buf[1024];
562 361 : char cgr1[1024] = "/sys/fs/cgroup/memory";
563 361 : char cgr2[1024] = "/sys/fs/cgroup";
564 361 : fc = fopen("/proc/self/mountinfo", "r");
565 361 : if (fc != NULL) {
566 11913 : while (fgets(buf, (int) sizeof(buf), fc) != NULL) {
567 11552 : char *p, *cgr;
568 11552 : if ((p = strstr(buf, " - cgroup ")) != NULL &&
569 0 : strstr(p, "memory") != NULL)
570 : cgr = cgr1;
571 11552 : else if (strstr(buf, " - cgroup2 ") != NULL)
572 : cgr = cgr2;
573 : else
574 11191 : continue;
575 : /* buf points at mount ID */
576 361 : p = strchr(buf, ' ');
577 361 : if (p == NULL)
578 : break;
579 361 : p++;
580 : /* p points at parent ID */
581 361 : p = strchr(p, ' ');
582 361 : if (p == NULL)
583 : break;
584 361 : p++;
585 : /* p points at major:minor */
586 361 : p = strchr(p, ' ');
587 361 : if (p == NULL)
588 : break;
589 361 : p++;
590 : /* p points at root */
591 361 : p = strchr(p, ' ');
592 361 : if (p == NULL)
593 : break;
594 361 : p++;
595 : /* p points at mount point */
596 361 : char *dir = p;
597 361 : p = strchr(p, ' ');
598 361 : if (p == NULL)
599 : break;
600 361 : *p = 0;
601 361 : strcpy_len(cgr, dir, 1024);
602 : }
603 361 : fclose(fc);
604 : }
605 361 : fc = fopen("/proc/self/cgroup", "r");
606 361 : 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 361 : while (fgets(buf, (int) sizeof(buf), fc) != NULL) {
626 361 : char pth[1024];
627 361 : char *p, *q;
628 361 : bool success = false; /* true if we can open any file */
629 361 : FILE *f;
630 361 : uint64_t mem;
631 :
632 361 : p = strchr(buf, '\n');
633 361 : if (p == NULL)
634 : break;
635 361 : *p = 0;
636 361 : if (strncmp(buf, "0::", 3) == 0) {
637 : /* cgroup v2 entry */
638 361 : p = stpcpy(pth, cgr2);
639 361 : q = stpcpy(stpcpy(p, buf + 3), "/");
640 : /* hard limit */
641 361 : strcpy(q, "memory.max");
642 361 : f = fopen(pth, "r");
643 361 : 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 361 : if (f != NULL) {
658 361 : 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 361 : success = true;
664 : /* assume "max" if not a number */
665 361 : fclose(f);
666 : }
667 : /* soft high limit */
668 361 : strcpy(q, "memory.high");
669 361 : f = fopen(pth, "r");
670 361 : if (f != NULL) {
671 361 : 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 361 : success = true;
677 : /* assume "max" if not a number */
678 361 : 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 361 : strcpy(q, "memory.swap.max");
685 361 : f = fopen(pth, "r");
686 361 : if (f != NULL) {
687 361 : 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 361 : success = true;
693 361 : 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 361 : if (success)
790 : break;
791 : }
792 361 : fclose(fc);
793 : }
794 : #endif
795 :
796 : #if defined(HAVE_SYS_RESOURCE_H) && defined(HAVE_GETRLIMIT) && defined(RLIMIT_AS)
797 361 : struct rlimit l;
798 : /* address space (virtual memory) limit */
799 361 : if (getrlimit(RLIMIT_AS, &l) == 0
800 361 : && 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 361 : }
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 365 : GDKsetdebug(unsigned debug)
828 : {
829 365 : ATOMIC_SET(&GDKdebug, debug);
830 365 : if (debug & ACCELMASK)
831 0 : GDKtracer_set_component_level("accelerator", "debug");
832 : else
833 365 : GDKtracer_reset_component_level("accelerator");
834 365 : if (debug & ALGOMASK)
835 0 : GDKtracer_set_component_level("algo", "debug");
836 : else
837 365 : GDKtracer_reset_component_level("algo");
838 365 : if (debug & ALLOCMASK)
839 0 : GDKtracer_set_component_level("alloc", "debug");
840 : else
841 365 : GDKtracer_reset_component_level("alloc");
842 365 : if (debug & BATMASK)
843 0 : GDKtracer_set_component_level("bat", "debug");
844 : else
845 365 : GDKtracer_reset_component_level("bat");
846 365 : if (debug & CHECKMASK)
847 354 : GDKtracer_set_component_level("check", "debug");
848 : else
849 11 : GDKtracer_reset_component_level("check");
850 365 : if (debug & DELTAMASK)
851 0 : GDKtracer_set_component_level("delta", "debug");
852 : else
853 365 : GDKtracer_reset_component_level("delta");
854 365 : if (debug & HEAPMASK)
855 0 : GDKtracer_set_component_level("heap", "debug");
856 : else
857 365 : GDKtracer_reset_component_level("heap");
858 365 : if (debug & IOMASK)
859 0 : GDKtracer_set_component_level("io", "debug");
860 : else
861 365 : GDKtracer_reset_component_level("io");
862 365 : if (debug & LOADMASK)
863 0 : GDKtracer_set_component_level("mal_loader", "debug");
864 : else
865 365 : GDKtracer_reset_component_level("mal_loader");
866 365 : if (debug & PARMASK)
867 0 : GDKtracer_set_component_level("par", "debug");
868 : else
869 365 : GDKtracer_reset_component_level("par");
870 365 : if (debug & PERFMASK)
871 0 : GDKtracer_set_component_level("perf", "debug");
872 : else
873 365 : GDKtracer_reset_component_level("perf");
874 365 : if (debug & TEMMASK)
875 0 : GDKtracer_set_component_level("tem", "debug");
876 : else
877 365 : GDKtracer_reset_component_level("tem");
878 365 : if (debug & THRDMASK)
879 0 : GDKtracer_set_component_level("thrd", "debug");
880 : else
881 365 : GDKtracer_reset_component_level("thrd");
882 365 : if (debug & TMMASK)
883 0 : GDKtracer_set_component_level("tm", "debug");
884 : else
885 365 : GDKtracer_reset_component_level("tm");
886 365 : }
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 28327341 : GDKembedded(void)
941 : {
942 28327341 : return Mbedded;
943 : }
944 :
945 : static MT_Id mainpid;
946 :
947 : gdk_return
948 360 : GDKinit(opt *set, int setlen, bool embedded, const char *caller_revision)
949 : {
950 360 : static bool first = true;
951 360 : const char *dbpath;
952 360 : const char *dbtrace;
953 360 : const char *p;
954 360 : opt *n;
955 360 : int i, nlen = 0;
956 360 : char buf[16];
957 :
958 360 : if (caller_revision) {
959 349 : p = mercurial_revision();
960 349 : 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 360 : ATOMIC_SET(&GDKstopped, 0);
967 :
968 360 : if (BBPchkfarms() != GDK_SUCCEED)
969 : return GDK_FAIL;
970 :
971 360 : if (GDKinmemory(0)) {
972 : dbpath = dbtrace = NULL;
973 : } else {
974 359 : dbpath = mo_find_option(set, setlen, "gdk_dbpath");
975 359 : dbtrace = mo_find_option(set, setlen, "gdk_dbtrace");
976 : }
977 360 : Mbedded = embedded;
978 : /* some sanity checks (should also find if symbols are not defined) */
979 360 : static_assert(sizeof(int) == sizeof(int32_t),
980 : "int is not equal in size to int32_t");
981 360 : static_assert(sizeof(char) == SIZEOF_CHAR,
982 : "error in configure: bad value for SIZEOF_CHAR");
983 360 : static_assert(sizeof(short) == SIZEOF_SHORT,
984 : "error in configure: bad value for SIZEOF_SHORT");
985 360 : static_assert(sizeof(int) == SIZEOF_INT,
986 : "error in configure: bad value for SIZEOF_INT");
987 360 : static_assert(sizeof(long) == SIZEOF_LONG,
988 : "error in configure: bad value for SIZEOF_LONG");
989 360 : static_assert(sizeof(lng) == SIZEOF_LNG,
990 : "error in configure: bad value for SIZEOF_LNG");
991 : #ifdef HAVE_HGE
992 360 : static_assert(sizeof(hge) == SIZEOF_HGE,
993 : "error in configure: bad value for SIZEOF_HGE");
994 : #endif
995 360 : static_assert(sizeof(dbl) == SIZEOF_DOUBLE,
996 : "error in configure: bad value for SIZEOF_DOUBLE");
997 360 : static_assert(sizeof(oid) == SIZEOF_OID,
998 : "error in configure: bad value for SIZEOF_OID");
999 360 : static_assert(sizeof(void *) == SIZEOF_VOID_P,
1000 : "error in configure: bad value for SIZEOF_VOID_P");
1001 360 : static_assert(sizeof(size_t) == SIZEOF_SIZE_T,
1002 : "error in configure: bad value for SIZEOF_SIZE_T");
1003 360 : static_assert(SIZEOF_OID == SIZEOF_INT || SIZEOF_OID == SIZEOF_LNG,
1004 : "SIZEOF_OID should be equal to SIZEOF_INT or SIZEOF_LNG");
1005 360 : static_assert(sizeof(uuid) == 16,
1006 : "sizeof(uuid) should be equal to 16");
1007 :
1008 360 : if (first) {
1009 : /* some things are really only initialized once */
1010 350 : if (!MT_thread_init()) {
1011 0 : TRC_CRITICAL(GDK, "MT_thread_init failed\n");
1012 0 : return GDK_FAIL;
1013 : }
1014 :
1015 2867550 : for (i = 0; i <= BBP_BATMASK; i++) {
1016 2867200 : char name[MT_NAME_LEN];
1017 2867200 : snprintf(name, sizeof(name), "GDKswapLock%d", i);
1018 2867200 : MT_lock_init(&GDKbatLock[i].swap, name);
1019 2867200 : snprintf(name, sizeof(name), "GDKswapCond%d", i);
1020 2867200 : MT_cond_init(&GDKbatLock[i].cond, name);
1021 : }
1022 350 : 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 360 : }
1030 360 : mainpid = MT_getpid();
1031 :
1032 360 : GDKtracer_init(dbpath, dbtrace);
1033 360 : errno = 0;
1034 360 : if (!GDKinmemory(0) && !GDKenvironment(dbpath))
1035 : return GDK_FAIL;
1036 :
1037 360 : MT_init_posix();
1038 360 : if (THRinit() < 0)
1039 : return GDK_FAIL;
1040 : #ifndef NATIVE_WIN32
1041 360 : BATSIGinit();
1042 : #endif
1043 360 : 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 11816 : for (int farmid = 0; farmid < MAXFARMS; farmid++) {
1048 11458 : if (BBPfarms[farmid].dirname != NULL) {
1049 1446 : bool skip = false;
1050 1446 : for (int j = 0; j < farmid; j++) {
1051 895 : if (BBPfarms[j].dirname != NULL &&
1052 895 : strcmp(BBPfarms[farmid].dirname, BBPfarms[j].dirname) == 0) {
1053 : skip = true;
1054 : break;
1055 : }
1056 : }
1057 1062 : 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 358 : GDK_mem_maxsize = (size_t) ((double) MT_npages() * (double) MT_pagesize() * 0.815);
1069 358 : const char *allow = mo_find_option(set, setlen, "allow_hge_upgrade");
1070 358 : const char *procwalxit = mo_find_option(set, setlen, "process-wal-and-exit");
1071 1066 : if (BBPinit(allow && strcmp(allow, "yes") == 0, procwalxit && strcmp(procwalxit, "yes") == 0) != GDK_SUCCEED)
1072 : return GDK_FAIL;
1073 358 : first = false;
1074 :
1075 358 : if (GDK_mem_maxsize / 16 < GDK_mmap_minsize_transient) {
1076 348 : GDK_mmap_minsize_transient = MAX(MMAP_PAGESIZE, GDK_mem_maxsize / 16);
1077 348 : if (GDK_mmap_minsize_persistent > GDK_mmap_minsize_transient)
1078 0 : GDK_mmap_minsize_persistent = GDK_mmap_minsize_transient;
1079 : }
1080 :
1081 358 : n = (opt *) malloc(setlen * sizeof(opt));
1082 358 : if (n == NULL) {
1083 0 : GDKsyserror("malloc failed\n");
1084 0 : return GDK_FAIL;
1085 : }
1086 :
1087 4335 : for (i = 0; i < setlen; i++) {
1088 17938 : bool done = false;
1089 :
1090 17938 : for (int j = 0; j < nlen; j++) {
1091 14667 : if (strcmp(n[j].name, set[i].name) == 0) {
1092 706 : if (n[j].kind < set[i].kind) {
1093 706 : n[j] = set[i];
1094 : }
1095 : done = true;
1096 : break;
1097 : }
1098 : }
1099 3271 : if (!done) {
1100 3271 : n[nlen] = set[i];
1101 3271 : nlen++;
1102 : }
1103 : }
1104 : /* check some options before creating our first BAT */
1105 3629 : for (i = 0; i < nlen; i++) {
1106 3271 : 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 3271 : } else if (strcmp("gdk_vm_maxsize", n[i].name) == 0) {
1114 198 : GDK_vm_maxsize = (size_t) strtoll(n[i].value, NULL, 10);
1115 198 : GDK_vm_maxsize = MAX(1 << 30, GDK_vm_maxsize);
1116 3073 : } 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 3073 : } 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 3073 : } 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 358 : GDKkey = COLnew(0, TYPE_str, 100, TRANSIENT);
1136 358 : GDKval = COLnew(0, TYPE_str, 100, TRANSIENT);
1137 358 : 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 716 : if (BBPrename(GDKkey, "environment_key") != 0 ||
1143 358 : 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 358 : BBP_pid(GDKkey->batCacheid) = 0;
1149 358 : BBP_pid(GDKval->batCacheid) = 0;
1150 :
1151 : /* store options into environment BATs */
1152 3629 : for (i = 0; i < nlen; i++)
1153 3271 : 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 358 : free(n);
1159 :
1160 358 : GDKnr_threads = GDKgetenv_int("gdk_nr_threads", 0);
1161 358 : if (GDKnr_threads == 0) {
1162 357 : GDKnr_threads = MT_check_nr_cores();
1163 357 : snprintf(buf, sizeof(buf), "%d", GDKnr_threads);
1164 357 : 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 358 : if (GDKnr_threads > THREADS)
1170 0 : GDKnr_threads = THREADS;
1171 :
1172 358 : if (!GDKinmemory(0)) {
1173 357 : if ((p = GDKgetenv("gdk_dbpath")) != NULL &&
1174 357 : (p = strrchr(p, DIR_SEP)) != NULL) {
1175 357 : 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 358 : 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 358 : if (GDKgetenv("gdk_mem_maxsize") == NULL) {
1202 358 : snprintf(buf, sizeof(buf), "%zu", GDK_mem_maxsize);
1203 358 : 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 358 : if (GDKgetenv("gdk_mmap_minsize_persistent") == NULL) {
1209 358 : snprintf(buf, sizeof(buf), "%zu", GDK_mmap_minsize_persistent);
1210 358 : 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 358 : if (GDKgetenv("gdk_mmap_minsize_transient") == NULL) {
1216 358 : snprintf(buf, sizeof(buf), "%zu", GDK_mmap_minsize_transient);
1217 358 : 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 358 : if (GDKgetenv("gdk_mmap_pagesize") == NULL) {
1223 358 : snprintf(buf, sizeof(buf), "%zu", GDK_mmap_pagesize);
1224 358 : 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 358 : if (GDKgetenv("monet_pid") == NULL) {
1230 358 : snprintf(buf, sizeof(buf), "%d", (int) getpid());
1231 358 : if (GDKsetenv("monet_pid", buf) != GDK_SUCCEED) {
1232 0 : TRC_CRITICAL(GDK, "GDKsetenv monet_pid failed");
1233 0 : return GDK_FAIL;
1234 : }
1235 : }
1236 358 : if (GDKsetenv("revision", mercurial_revision()) != GDK_SUCCEED) {
1237 0 : TRC_CRITICAL(GDK, "GDKsetenv revision failed");
1238 0 : return GDK_FAIL;
1239 : }
1240 358 : gdk_unique_estimate_keep_fraction = 0;
1241 358 : if ((p = GDKgetenv("gdk_unique_estimate_keep_fraction")) != NULL)
1242 0 : gdk_unique_estimate_keep_fraction = (BUN) strtoll(p, NULL, 10);
1243 358 : if (gdk_unique_estimate_keep_fraction == 0)
1244 358 : gdk_unique_estimate_keep_fraction = GDK_UNIQUE_ESTIMATE_KEEP_FRACTION;
1245 358 : hash_destroy_uniques_fraction = 0;
1246 358 : if ((p = GDKgetenv("hash_destroy_uniques_fraction")) != NULL)
1247 0 : hash_destroy_uniques_fraction = (BUN) strtoll(p, NULL, 10);
1248 358 : if (hash_destroy_uniques_fraction == 0)
1249 358 : hash_destroy_uniques_fraction = HASH_DESTROY_UNIQUES_FRACTION;
1250 358 : no_hash_select_fraction = 0;
1251 358 : if ((p = GDKgetenv("no_hash_select_fraction")) != NULL)
1252 0 : no_hash_select_fraction = (dbl) strtoll(p, NULL, 10);
1253 358 : if (no_hash_select_fraction == 0)
1254 358 : no_hash_select_fraction = NO_HASH_SELECT_FRACTION;
1255 358 : hash_destroy_chain_length = 0;
1256 358 : if ((p = GDKgetenv("hash_destroy_chain_length")) != NULL)
1257 0 : hash_destroy_chain_length = (BUN) strtoll(p, NULL, 10);
1258 358 : if (hash_destroy_chain_length == 0)
1259 358 : 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 29260506 : GDKexiting(void)
1269 : {
1270 29260506 : return (bool) (ATOMIC_GET(&GDKstopped) > 0);
1271 : }
1272 :
1273 : void
1274 357 : GDKprepareExit(void)
1275 : {
1276 357 : ATOMIC_ADD(&GDKstopped, 1);
1277 :
1278 357 : if (MT_getpid() == mainpid) {
1279 356 : TRC_DEBUG_IF(THRD)
1280 0 : dump_threads();
1281 356 : join_detached_threads();
1282 : }
1283 357 : }
1284 :
1285 : void
1286 356 : GDKreset(int status)
1287 : {
1288 356 : assert(GDKexiting());
1289 :
1290 356 : 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 356 : if (GDKkey) {
1295 356 : BBPunfix(GDKkey->batCacheid);
1296 356 : GDKkey = NULL;
1297 : }
1298 356 : if (GDKval) {
1299 356 : BBPunfix(GDKval->batCacheid);
1300 356 : GDKval = NULL;
1301 : }
1302 :
1303 356 : join_detached_threads();
1304 :
1305 356 : MT_lock_set(&GDKenvlock);
1306 356 : while (orig_value) {
1307 0 : struct orig_value *ov = orig_value;
1308 0 : orig_value = orig_value->next;
1309 0 : GDKfree(ov);
1310 : }
1311 356 : MT_lock_unset(&GDKenvlock);
1312 :
1313 356 : if (status == 0) {
1314 : /* they had their chance, now kill them */
1315 356 : bool killed = MT_kill_threads();
1316 : /* all threads ceased running, now we can clean up */
1317 356 : if (!killed) {
1318 : /* we can't clean up after killing threads */
1319 356 : BBPexit();
1320 : }
1321 356 : GDKlog(GET_GDKLOCK(PERSISTENT), GDKLOGOFF);
1322 :
1323 11748 : for (int farmid = 0; farmid < MAXFARMS; farmid++) {
1324 11392 : if (BBPfarms[farmid].dirname != NULL) {
1325 2100 : bool skip = false;
1326 2100 : for (int j = 0; j < farmid; j++) {
1327 1045 : if (BBPfarms[j].dirname != NULL &&
1328 0 : strcmp(BBPfarms[farmid].dirname, BBPfarms[j].dirname) == 0) {
1329 : skip = true;
1330 : break;
1331 : }
1332 : }
1333 1055 : if (!skip)
1334 1055 : GDKunlockHome(farmid);
1335 1055 : if (BBPfarms[farmid].dirname) {
1336 1055 : GDKfree((char*)BBPfarms[farmid].dirname);
1337 1055 : BBPfarms[farmid].dirname = NULL;
1338 : }
1339 : }
1340 : }
1341 :
1342 : #ifdef LOCK_STATS
1343 : TRC_DEBUG_IF(TEM) GDKlockstatistics(1);
1344 : #endif
1345 356 : ATOMIC_SET(&GDKdebug, 0);
1346 356 : GDK_mmap_minsize_persistent = MMAP_MINSIZE_PERSISTENT;
1347 356 : GDK_mmap_minsize_transient = MMAP_MINSIZE_TRANSIENT;
1348 356 : GDK_mmap_pagesize = MMAP_PAGESIZE;
1349 356 : GDK_mem_maxsize = (size_t) ((double) MT_npages() * (double) MT_pagesize() * 0.815);
1350 356 : GDK_vm_maxsize = GDK_VM_MAXSIZE;
1351 356 : GDKatomcnt = TYPE_blob + 1;
1352 :
1353 356 : if (GDK_mem_maxsize / 16 < GDK_mmap_minsize_transient) {
1354 356 : GDK_mmap_minsize_transient = GDK_mem_maxsize / 16;
1355 356 : if (GDK_mmap_minsize_persistent > GDK_mmap_minsize_transient)
1356 0 : GDK_mmap_minsize_persistent = GDK_mmap_minsize_transient;
1357 : }
1358 :
1359 356 : GDKnr_threads = 0;
1360 356 : ATOMIC_SET(&GDKnrofthreads, 0);
1361 356 : close_stream(GDKstdout);
1362 356 : close_stream(GDKstdin);
1363 356 : GDKstdout = NULL;
1364 356 : GDKstdin = NULL;
1365 :
1366 356 : gdk_bbp_reset();
1367 : }
1368 356 : ATOMunknown_clean();
1369 :
1370 : /* stop GDKtracer */
1371 356 : GDKtracer_stop();
1372 356 : }
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 551 : GDKlockHome(int farmid)
1395 : {
1396 551 : int fd;
1397 551 : struct stat st;
1398 551 : char gdklockpath[1024];
1399 551 : FILE *GDKlockFile;
1400 :
1401 551 : assert(BBPfarms[farmid].dirname != NULL);
1402 551 : assert(BBPfarms[farmid].lock_file == NULL);
1403 :
1404 551 : 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 551 : 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 551 : 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 549 : 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 549 : 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 549 : 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 549 : 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 549 : GDKlog(GDKlockFile, GDKLOGON);
1450 549 : BBPfarms[farmid].lock_file = GDKlockFile;
1451 549 : return GDK_SUCCEED;
1452 : }
1453 :
1454 :
1455 : static void
1456 1055 : GDKunlockHome(int farmid)
1457 : {
1458 1055 : if (BBPfarms[farmid].lock_file) {
1459 547 : char gdklockpath[MAXPATH];
1460 :
1461 547 : if (GDKfilepath(gdklockpath, sizeof(gdklockpath), farmid, NULL, GDKLOCK, NULL) == GDK_SUCCEED)
1462 547 : MT_lockf(gdklockpath, F_ULOCK);
1463 547 : fclose(BBPfarms[farmid].lock_file);
1464 547 : BBPfarms[farmid].lock_file = NULL;
1465 : }
1466 1055 : }
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 10662016 : GDKclrerr(void)
1487 : {
1488 10662016 : char *buf;
1489 :
1490 10662016 : buf = GDKerrbuf;
1491 10684923 : if (buf)
1492 10677536 : *buf = 0;
1493 10684923 : }
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 190271604 : 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 190271604 : struct timespec ts;
1573 190271604 : (void) clock_gettime(CLOCK_REALTIME, &ts);
1574 190601078 : 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 360 : THRinit(void)
1616 : {
1617 360 : if ((GDKstdout = stdout_wastream()) == NULL) {
1618 0 : TRC_CRITICAL(GDK, "malloc for stdout failed\n");
1619 0 : return -1;
1620 : }
1621 360 : 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 360 : struct freebats *t = MT_thread_getfreebats();
1628 360 : t->freebats = 0;
1629 360 : t->nfreebats = 0;
1630 360 : return 0;
1631 : }
1632 :
1633 : const char *
1634 347 : GDKversion(void)
1635 : {
1636 347 : return MONETDB_VERSION;
1637 : }
1638 :
1639 : const char *
1640 705 : GDKlibversion(void)
1641 : {
1642 705 : return GDK_VERSION;
1643 : }
1644 :
1645 : inline size_t
1646 185759838 : GDKmem_cursize(void)
1647 : {
1648 : /* RAM/swapmem that Monet is really using now */
1649 185759838 : return (size_t) ATOMIC_GET(&GDK_mallocedbytes_estimate);
1650 : }
1651 :
1652 : inline size_t
1653 176522988 : GDKvm_cursize(void)
1654 : {
1655 : /* current Monet VM address space usage */
1656 176522988 : 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 176794974 : GDKmalloc_internal(size_t size, bool clear)
1718 : {
1719 176794974 : void *s;
1720 176794974 : size_t nsize;
1721 :
1722 176794974 : 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 176794974 : nsize = (size + 7) & ~7;
1736 176794974 : if (clear)
1737 28765519 : s = calloc(nsize + MALLOC_EXTRA_SPACE + DEBUG_SPACE, 1);
1738 : else
1739 148029455 : s = malloc(nsize + MALLOC_EXTRA_SPACE + DEBUG_SPACE);
1740 176794974 : 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 176794974 : s = (void *) ((char *) s + MALLOC_EXTRA_SPACE);
1745 :
1746 176794974 : 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 176794974 : ((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 176794974 : ((size_t *) s)[-2] = size;
1754 : /* write pattern to help find out-of-bounds writes */
1755 176794974 : memset((char *) s + size, '\xBD', nsize + DEBUG_SPACE - size);
1756 : #endif
1757 176794974 : return s;
1758 : }
1759 :
1760 : #undef GDKmalloc
1761 : void *
1762 139744329 : GDKmalloc(size_t size)
1763 : {
1764 139744329 : void *s;
1765 :
1766 139744329 : 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 139978606 : DEADBEEFCHK memset(s, '\xBD', size);
1772 : #endif
1773 : return s;
1774 : }
1775 :
1776 : #undef GDKzalloc
1777 : void *
1778 28770176 : GDKzalloc(size_t size)
1779 : {
1780 28770176 : return GDKmalloc_internal(size, true);
1781 : }
1782 :
1783 : #undef GDKstrdup
1784 : char *
1785 9054203 : GDKstrdup(const char *s)
1786 : {
1787 9054203 : size_t size;
1788 9054203 : char *p;
1789 :
1790 9054203 : if (s == NULL)
1791 : return NULL;
1792 8396309 : size = strlen(s) + 1;
1793 :
1794 8396309 : if ((p = GDKmalloc_internal(size, false)) == NULL)
1795 : return NULL;
1796 8397109 : memcpy(p, s, size); /* including terminating NULL byte */
1797 8397109 : 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 359145032 : GDKfree(void *s)
1819 : {
1820 359145032 : size_t asize;
1821 :
1822 359145032 : if (s == NULL)
1823 : return;
1824 :
1825 176918859 : asize = ((size_t *) s)[-1]; /* how much allocated last */
1826 :
1827 : #if !defined(NDEBUG) && !defined(SANITIZER)
1828 176918859 : size_t *p = s;
1829 176918859 : assert((asize & 2) == 0); /* check against duplicate free */
1830 176918859 : size_t size = p[-2];
1831 176918859 : assert(((size + 7) & ~7) + MALLOC_EXTRA_SPACE + DEBUG_SPACE == asize);
1832 : /* check for out-of-bounds writes */
1833 3366811830 : for (size_t i = size; i < asize - MALLOC_EXTRA_SPACE; i++)
1834 3189892971 : assert(((char *) s)[i] == '\xBD');
1835 176918859 : 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 176918859 : DEADBEEFCHK memset(s, '\xDB', asize - MALLOC_EXTRA_SPACE);
1841 : #endif
1842 :
1843 176918859 : free((char *) s - MALLOC_EXTRA_SPACE);
1844 176918859 : heapdec((ssize_t) asize);
1845 : }
1846 :
1847 : #undef GDKrealloc
1848 : void *
1849 402154 : GDKrealloc(void *s, size_t size)
1850 : {
1851 402154 : size_t nsize, asize;
1852 : #if !defined(NDEBUG) && !defined(SANITIZER)
1853 402154 : size_t osize;
1854 : #endif
1855 402154 : size_t *os = s;
1856 :
1857 402154 : assert(size != 0);
1858 :
1859 402154 : if (s == NULL)
1860 934 : return GDKmalloc(size);
1861 :
1862 401220 : nsize = (size + 7) & ~7;
1863 401220 : 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 401220 : assert((asize & 2) == 0); /* check against duplicate free */
1876 : /* check for out-of-bounds writes */
1877 401220 : osize = os[-2]; /* how much asked for last */
1878 401220 : assert(((osize + 7) & ~7) + MALLOC_EXTRA_SPACE + DEBUG_SPACE == asize);
1879 7128219 : for (size_t i = osize; i < asize - MALLOC_EXTRA_SPACE; i++)
1880 6726999 : assert(((char *) s)[i] == '\xBD');
1881 : /* if shrinking, write debug pattern into to-be-freed memory */
1882 401220 : DEADBEEFCHK if (size < osize)
1883 46818 : memset((char *) s + size, '\xDB', osize - size);
1884 401220 : os[-1] |= 2; /* indicate area is freed */
1885 : #endif
1886 401220 : s = realloc((char *) s - MALLOC_EXTRA_SPACE,
1887 : nsize + MALLOC_EXTRA_SPACE + DEBUG_SPACE);
1888 401220 : 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 401220 : s = (void *) ((char *) s + MALLOC_EXTRA_SPACE);
1898 : /* just before the pointer that we return, write how much we
1899 : * asked of malloc */
1900 401220 : ((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 401220 : ((size_t *) s)[-2] = size;
1904 : /* if growing, initialize new memory with debug pattern */
1905 401220 : DEADBEEFCHK if (size > osize)
1906 354372 : memset((char *) s + osize, '\xBD', size - osize);
1907 : /* write pattern to help find out-of-bounds writes */
1908 401220 : memset((char *) s + size, '\xBD', nsize + DEBUG_SPACE - size);
1909 : #endif
1910 :
1911 401220 : heapinc(nsize + MALLOC_EXTRA_SPACE + DEBUG_SPACE);
1912 401220 : 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 210550 : GDKmallocated(const void *s)
1922 : {
1923 210550 : 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 2619 : GDKmmap(const char *path, int mode, size_t len)
1933 : {
1934 2619 : 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 2619 : ret = MT_mmap(path, mode, len);
1944 2645 : if (ret != NULL) {
1945 2645 : if (mode & MMAP_COPY)
1946 0 : heapinc(len);
1947 : else
1948 2645 : 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 2645 : return ret;
1952 : }
1953 :
1954 : #undef GDKmunmap
1955 : gdk_return
1956 2643 : GDKmunmap(void *addr, int mode, size_t size)
1957 : {
1958 2643 : int ret;
1959 :
1960 2643 : ret = MT_munmap(addr, size);
1961 2645 : if (ret == 0) {
1962 2645 : if (mode & MMAP_COPY)
1963 0 : heapdec(size);
1964 : else
1965 2645 : memdec(size);
1966 : }
1967 2645 : return ret == 0 ? GDK_SUCCEED : GDK_FAIL;
1968 : }
1969 :
1970 : #undef GDKmremap
1971 : void *
1972 465 : GDKmremap(const char *path, int mode, void *old_address, size_t old_size, size_t *new_size)
1973 : {
1974 465 : 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 465 : ret = MT_mremap(path, mode, old_address, old_size, new_size);
1985 465 : if (ret != NULL) {
1986 465 : if (mode & MMAP_COPY) {
1987 0 : heapdec(old_size);
1988 0 : heapinc(*new_size);
1989 : } else {
1990 465 : memdec(old_size);
1991 465 : 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 465 : 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 357 : GDKprintinforegister(void (*func)(void))
2007 : {
2008 357 : struct prinfocb *p = GDKmalloc(sizeof(struct prinfocb));
2009 357 : if (p == NULL) {
2010 0 : GDKerror("cannot register USR1 printing function.\n");
2011 0 : return;
2012 : }
2013 357 : p->func = func;
2014 357 : p->next = NULL;
2015 357 : struct prinfocb **pp = &prinfocb;
2016 412 : while (*pp != NULL)
2017 55 : pp = &(*pp)->next;
2018 357 : *pp = p;
2019 : }
2020 :
2021 : void
2022 119 : GDKprintinfo(void)
2023 : {
2024 119 : size_t allocated = (size_t) ATOMIC_GET(&GDK_mallocedbytes_estimate);
2025 119 : size_t vmallocated = (size_t) ATOMIC_GET(&GDK_vm_cursize);
2026 :
2027 119 : printf("SIGUSR1 info start\n");
2028 119 : printf("Virtual memory allocated: %zu, of which %zu with malloc\n",
2029 : vmallocated + allocated, allocated);
2030 119 : printf("gdk_vm_maxsize: %zu, gdk_mem_maxsize: %zu\n",
2031 : GDK_vm_maxsize, GDK_mem_maxsize);
2032 119 : 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 119 : int fd = open("/proc/self/statm", O_RDONLY | O_CLOEXEC);
2036 119 : if (fd >= 0) {
2037 119 : char buf[512];
2038 119 : ssize_t s = read(fd, buf, sizeof(buf) - 1);
2039 119 : close(fd);
2040 119 : if (s > 0) {
2041 119 : assert((size_t) s < sizeof(buf));
2042 119 : size_t size, resident, shared;
2043 119 : buf[s] = 0;
2044 119 : if (sscanf(buf, "%zu %zu %zu", &size, &resident, &shared) == 3) {
2045 119 : size *= MT_pagesize();
2046 119 : resident *= MT_pagesize();
2047 119 : shared *= MT_pagesize();
2048 119 : 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 119 : BBPprintinfo();
2055 : #ifdef LOCK_STATS
2056 : GDKlockstatistics(3);
2057 : #endif
2058 119 : dump_threads();
2059 238 : for (struct prinfocb *p = prinfocb; p; p = p->next)
2060 119 : (*p->func)();
2061 119 : printf("SIGUSR1 info end\n");
2062 119 : }
2063 :
2064 : exception_buffer *
2065 726704 : eb_init(exception_buffer *eb)
2066 : {
2067 726704 : if (eb) {
2068 726704 : eb->enabled = 0;
2069 726704 : eb->code = 0;
2070 726704 : eb->msg = NULL;
2071 : }
2072 726704 : 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 71270 : sa_destroy_freelist(freed_t *f)
2097 : {
2098 90098 : while(f) {
2099 18828 : freed_t *n = f->n;
2100 18828 : GDKfree(f);
2101 18828 : f = n;
2102 : }
2103 : }
2104 :
2105 : static void
2106 210550 : sa_free(allocator *pa, void *blk)
2107 : {
2108 210550 : assert(!pa->pa);
2109 : size_t i;
2110 :
2111 1238716 : for(i = 0; i < pa->nr; i++) {
2112 1238716 : if (pa->blks[i] == blk)
2113 : break;
2114 : }
2115 210550 : assert (i < pa->nr);
2116 2401504 : for (; i < pa->nr-1; i++)
2117 2190954 : pa->blks[i] = pa->blks[i+1];
2118 210550 : pa->nr--;
2119 :
2120 210550 : size_t sz = GDKmallocated(blk);
2121 210550 : if (sz > (SA_BLOCK + 32)) {
2122 90 : GDKfree(blk);
2123 : } else {
2124 210460 : freed_t *f = blk;
2125 210460 : f->n = pa->freelist;
2126 210460 : f->sz = sz;
2127 :
2128 210460 : pa->freelist = f;
2129 : }
2130 210550 : }
2131 :
2132 : static void *
2133 191632 : sa_use_freed(allocator *pa, size_t sz)
2134 : {
2135 191632 : (void)sz;
2136 :
2137 191632 : freed_t *f = pa->freelist;
2138 191632 : pa->freelist = f->n;
2139 191632 : return f;
2140 : }
2141 :
2142 : allocator *
2143 283768 : sa_create(allocator *pa)
2144 : {
2145 283768 : allocator *sa = (pa)?(allocator*)sa_alloc(pa, sizeof(allocator)):(allocator*)GDKmalloc(sizeof(allocator));
2146 283768 : if (sa == NULL)
2147 : return NULL;
2148 283768 : eb_init(&sa->eb);
2149 283767 : sa->pa = pa;
2150 283767 : sa->size = 64;
2151 283767 : sa->nr = 1;
2152 283767 : sa->blks = pa?(char**)sa_alloc(pa, sizeof(char*) * sa->size):(char**)GDKmalloc(sizeof(char*) * sa->size);
2153 283764 : sa->freelist = NULL;
2154 283764 : if (sa->blks == NULL) {
2155 0 : if (!pa)
2156 0 : GDKfree(sa);
2157 0 : return NULL;
2158 : }
2159 283764 : sa->blks[0] = pa?(char*)sa_alloc(pa, SA_BLOCK):(char*)GDKmalloc(SA_BLOCK);
2160 283753 : sa->usedmem = SA_BLOCK;
2161 283753 : 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 283753 : sa->used = 0;
2169 283753 : return sa;
2170 : }
2171 :
2172 : allocator *
2173 2010117 : sa_reset(allocator *sa)
2174 : {
2175 2010117 : size_t i ;
2176 :
2177 2083937 : for (i = 1; i<sa->nr; i++) {
2178 73689 : if (!sa->pa)
2179 0 : GDKfree(sa->blks[i]);
2180 : else
2181 73689 : sa_free(sa->pa, sa->blks[i]);
2182 : }
2183 2010248 : sa->nr = 1;
2184 2010248 : sa->used = 0;
2185 2010248 : sa->usedmem = SA_BLOCK;
2186 2010248 : return sa;
2187 : }
2188 :
2189 : #undef sa_realloc
2190 : #undef sa_alloc
2191 : void *
2192 35 : sa_realloc(allocator *sa, void *p, size_t sz, size_t oldsz)
2193 : {
2194 35 : void *r = sa_alloc(sa, sz);
2195 :
2196 35 : if (r)
2197 35 : memcpy(r, p, oldsz);
2198 35 : return r;
2199 : }
2200 :
2201 : #define round16(sz) ((sz+15)&~15)
2202 : void *
2203 259901742 : sa_alloc(allocator *sa, size_t sz)
2204 : {
2205 259901742 : char *r;
2206 259901742 : sz = round16(sz);
2207 259901742 : if (sz > (SA_BLOCK-sa->used)) {
2208 381469 : if (sa->pa)
2209 73935 : r = (char*)sa_alloc(sa->pa, (sz > SA_BLOCK ? sz : SA_BLOCK));
2210 307534 : else if (sz <= SA_BLOCK && sa->freelist) {
2211 191632 : r = sa_use_freed(sa, SA_BLOCK);
2212 : } else
2213 115902 : r = GDKmalloc(sz > SA_BLOCK ? sz : SA_BLOCK);
2214 381456 : if (r == NULL) {
2215 0 : if (sa->eb.enabled)
2216 0 : eb_error(&sa->eb, "out of memory", 1000);
2217 : return NULL;
2218 : }
2219 381456 : if (sa->nr >= sa->size) {
2220 43 : char **tmp;
2221 43 : size_t osz = sa->size;
2222 43 : sa->size *=2;
2223 43 : if (sa->pa)
2224 21 : tmp = (char**)sa_realloc(sa->pa, sa->blks, sizeof(char*) * sa->size, sizeof(char*) * osz);
2225 : else
2226 22 : tmp = GDKrealloc(sa->blks, sizeof(char*) * sa->size);
2227 43 : 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 43 : sa->blks = tmp;
2236 : }
2237 381456 : 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 286879 : sa->blks[sa->nr] = sa->blks[sa->nr-1];
2242 286879 : sa->blks[sa->nr-1] = r;
2243 286879 : sa->nr ++;
2244 286879 : sa->usedmem += sz;
2245 : } else {
2246 94577 : sa->blks[sa->nr] = r;
2247 94577 : sa->nr ++;
2248 94577 : sa->used = sz;
2249 94577 : sa->usedmem += SA_BLOCK;
2250 : }
2251 : } else {
2252 259520273 : r = sa->blks[sa->nr-1] + sa->used;
2253 259520273 : sa->used += sz;
2254 : }
2255 : return r;
2256 : }
2257 :
2258 : #undef sa_zalloc
2259 : void *
2260 10986695 : sa_zalloc(allocator *sa, size_t sz)
2261 : {
2262 10986695 : void *r = sa_alloc(sa, sz);
2263 :
2264 10986994 : if (r)
2265 10986994 : memset(r, 0, sz);
2266 10986994 : return r;
2267 : }
2268 :
2269 : void
2270 208131 : sa_destroy(allocator *sa)
2271 : {
2272 208131 : if (sa->pa) {
2273 136861 : sa_reset(sa);
2274 136861 : sa_free(sa->pa, sa->blks[0]);
2275 136861 : return;
2276 : }
2277 :
2278 71270 : sa_destroy_freelist(sa->freelist);
2279 239517 : for (size_t i = 0; i<sa->nr; i++) {
2280 168247 : GDKfree(sa->blks[i]);
2281 : }
2282 71270 : GDKfree(sa->blks);
2283 71270 : GDKfree(sa);
2284 : }
2285 :
2286 : #undef sa_strndup
2287 : char *
2288 12954665 : sa_strndup(allocator *sa, const char *s, size_t l)
2289 : {
2290 12954665 : char *r = sa_alloc(sa, l+1);
2291 :
2292 12954297 : if (r) {
2293 12954297 : memcpy(r, s, l);
2294 12954297 : r[l] = 0;
2295 : }
2296 12954297 : return r;
2297 : }
2298 :
2299 : #undef sa_strdup
2300 : char *
2301 7315533 : sa_strdup(allocator *sa, const char *s)
2302 : {
2303 7315533 : return sa_strndup(sa, s, strlen(s));
2304 : }
2305 :
2306 : char *
2307 43755 : sa_strconcat(allocator *sa, const char *s1, const char *s2)
2308 : {
2309 43755 : size_t l1 = strlen(s1);
2310 43755 : size_t l2 = strlen(s2);
2311 43755 : char *r = sa_alloc(sa, l1+l2+1);
2312 :
2313 43757 : if (l1)
2314 43762 : memcpy(r, s1, l1);
2315 43757 : if (l2)
2316 43763 : memcpy(r+l1, s2, l2);
2317 43757 : r[l1+l2] = 0;
2318 43757 : return r;
2319 : }
2320 :
2321 : size_t
2322 0 : sa_size(allocator *sa)
2323 : {
2324 0 : return sa->usedmem;
2325 : }
|