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