1/*
2 * This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5 *
6 * Copyright 1997 - July 2008 CWI, August 2008 - 2019 MonetDB B.V.
7 */
8
9/*
10 * @a M. L. Kersten, P. Boncz, N. Nes
11 *
12 * @* Utilities
13 * The utility section contains functions to initialize the Monet
14 * database system, memory allocation details, and a basic system
15 * logging scheme.
16 */
17#include "monetdb_config.h"
18#include "monet_options.h"
19
20#include "gdk.h"
21#include "gdk_private.h"
22#include "mutils.h"
23
24static BAT *GDKkey = NULL;
25static BAT *GDKval = NULL;
26int GDKdebug = 0;
27int GDKverbose = 0;
28
29#include <signal.h>
30
31#ifdef HAVE_FCNTL_H
32#include <fcntl.h>
33#endif
34
35#ifdef HAVE_PWD_H
36# include <pwd.h>
37#endif
38
39#ifdef HAVE_SYS_PARAM_H
40# include <sys/param.h> /* prerequisite of sys/sysctl on OpenBSD */
41#endif
42#ifdef BSD /* BSD macro is defined in sys/param.h */
43# include <sys/sysctl.h>
44#endif
45#if defined(HAVE_SYS_RESOURCE_H) && defined(HAVE_GETRLIMIT)
46#include <sys/resource.h>
47#endif
48
49#ifdef __CYGWIN__
50#include <sysinfoapi.h>
51#endif
52
53#ifdef NATIVE_WIN32
54#define chdir _chdir
55#endif
56
57static ATOMIC_TYPE GDKstopped = ATOMIC_VAR_INIT(0);
58static void GDKunlockHome(int farmid);
59
60#undef malloc
61#undef calloc
62#undef realloc
63#undef free
64
65/*
66 * @+ Monet configuration file
67 * Parse a possible MonetDB config file (if specified by command line
68 * option -c/--config) to extract pre-settings of system variables.
69 * Un-recognized parameters are simply skipped, because they may be
70 * picked up by other components of the system. The consequence is
71 * that making a typing error in the configuration file may be
72 * unnoticed for a long time. Syntax errors are immediately flagged,
73 * though.
74 *
75 * Since the GDK kernel moves into the database directory, we need to
76 * keep the absolute path to the MonetDB config file for top-levels to
77 * access its information.
78 */
79
80static bool
81GDKenvironment(const char *dbpath)
82{
83 if (dbpath == NULL) {
84 fprintf(stderr, "!GDKenvironment: database name missing.\n");
85 return false;
86 }
87 if (strlen(dbpath) >= FILENAME_MAX) {
88 fprintf(stderr, "!GDKenvironment: database name too long.\n");
89 return false;
90 }
91 if (!MT_path_absolute(dbpath)) {
92 fprintf(stderr, "!GDKenvironment: directory not an absolute path: %s.\n", dbpath);
93 return false;
94 }
95 return true;
96}
97
98const char *
99GDKgetenv(const char *name)
100{
101 if (GDKkey && GDKval) {
102 BUN b = BUNfnd(GDKkey, (ptr) name);
103
104 if (b != BUN_NONE) {
105 BATiter GDKenvi = bat_iterator(GDKval);
106 return BUNtvar(GDKenvi, b);
107 }
108 }
109 return NULL;
110}
111
112bool
113GDKgetenv_istext(const char *name, const char *text)
114{
115 const char *val = GDKgetenv(name);
116
117 return val && strcasecmp(val, text) == 0;
118}
119
120bool
121GDKgetenv_isyes(const char *name)
122{
123 return GDKgetenv_istext(name, "yes");
124}
125
126bool
127GDKgetenv_istrue(const char *name)
128{
129 return GDKgetenv_istext(name, "true");
130}
131
132int
133GDKgetenv_int(const char *name, int def)
134{
135 const char *val = GDKgetenv(name);
136
137 if (val)
138 return atoi(val);
139 return def;
140}
141
142gdk_return
143GDKsetenv(const char *name, const char *value)
144{
145 if (BUNappend(GDKkey, name, false) != GDK_SUCCEED ||
146 BUNappend(GDKval, value, false) != GDK_SUCCEED)
147 return GDK_FAIL;
148 return GDK_SUCCEED;
149}
150
151gdk_return
152GDKcopyenv(BAT **key, BAT **val, bool writable)
153{
154 BAT *k, *v;
155
156 if (key == NULL || val == NULL) {
157 GDKerror("GDKcopyenv: called incorrectly.\n");
158 return GDK_FAIL;
159 }
160 k = COLcopy(GDKkey, GDKkey->ttype, writable, TRANSIENT);
161 v = COLcopy(GDKval, GDKval->ttype, writable, TRANSIENT);
162 if (k == NULL || v == NULL) {
163 BBPreclaim(k);
164 BBPreclaim(v);
165 return GDK_FAIL;
166 }
167 *key = k;
168 *val = v;
169 return GDK_SUCCEED;
170}
171
172
173/*
174 * @+ System logging
175 * Per database a log file can be maintained for collection of system
176 * management information. Its contents is driven by the upper layers,
177 * which encode information such as who logged on and how long the
178 * session went on. The lower layers merely store error information
179 * on the file. It should not be used for crash recovery, because
180 * this should be dealt with on a per client basis.
181 *
182 * A system log can be maintained in the database to keep track of
183 * session and crash information. It should regularly be refreshed to
184 * avoid disk overflow.
185 */
186#define GDKLOCK ".gdk_lock"
187
188#define GET_GDKLOCK(x) BBPfarms[BBPselectfarm((x), 0, offheap)].lock_file
189
190#define GDKLOGOFF "LOGOFF"
191#define GDKFOUNDDEAD "FOUND DEAD"
192#define GDKLOGON "LOGON"
193#define GDKCRASH "CRASH"
194
195/*
196 * Single-lined comments can now be logged safely, together with
197 * process, thread and user ID, and the current time.
198 */
199void
200GDKlog(FILE *lockFile, const char *format, ...)
201{
202 va_list ap;
203 char *p = 0, buf[1024];
204 time_t tm = time(0);
205#if defined(HAVE_CTIME_R3) || defined(HAVE_CTIME_R)
206 char tbuf[26];
207#endif
208 char *ctm;
209
210 if (MT_pagesize() == 0 || lockFile == NULL)
211 return;
212
213 va_start(ap, format);
214 vsprintf(buf, format, ap);
215 va_end(ap);
216
217 /* remove forbidden characters from message */
218 for (p = buf; (p = strchr(p, '\n')) != NULL; *p = ' ')
219 ;
220 for (p = buf; (p = strchr(p, '@')) != NULL; *p = ' ')
221 ;
222
223 fseek(lockFile, 0, SEEK_END);
224#ifndef HAVE_GETUID
225#define getuid() 0
226#endif
227#ifdef HAVE_CTIME_R3
228 ctm = ctime_r(&tm, tbuf, sizeof(tbuf));
229#else
230#ifdef HAVE_CTIME_R
231 ctm = ctime_r(&tm, tbuf);
232#else
233 ctm = ctime(&tm);
234#endif
235#endif
236 fprintf(lockFile, "USR=%d PID=%d TIME=%.24s @ %s\n", (int) getuid(), (int) getpid(), ctm, buf);
237 fflush(lockFile);
238}
239
240/*
241 * @+ Interrupt handling
242 * The current version simply catches signals and prints a warning.
243 * It should be extended to cope with the specifics of the interrupt
244 * received.
245 */
246#if 0 /* these are unused */
247static void
248BATSIGignore(int nr)
249{
250 (void) nr;
251 GDKsyserror("! ERROR signal %d caught by thread %zu\n", nr, (size_t) MT_getpid());
252}
253#endif
254
255#ifdef WIN32
256static void
257BATSIGabort(int nr)
258{
259 (void) nr;
260 _Exit(3); /* emulate Windows exit code without pop-up */
261}
262#endif
263
264#ifndef NATIVE_WIN32
265static int
266BATSIGinit(void)
267{
268#ifdef SIGPIPE
269 (void) signal(SIGPIPE, SIG_IGN);
270#endif
271 return 0;
272}
273#endif /* NATIVE_WIN32 */
274
275/* memory thresholds; these values some "sane" constants only, really
276 * set in GDKinit() */
277#define MMAP_MINSIZE_PERSISTENT ((size_t) 1 << 18)
278#if SIZEOF_SIZE_T == 4
279#define MMAP_MINSIZE_TRANSIENT ((size_t) 1 << 20)
280#else
281#define MMAP_MINSIZE_TRANSIENT ((size_t) 1 << 32)
282#endif
283#define MMAP_PAGESIZE ((size_t) 1 << 16)
284size_t GDK_mmap_minsize_persistent = MMAP_MINSIZE_PERSISTENT;
285size_t GDK_mmap_minsize_transient = MMAP_MINSIZE_TRANSIENT;
286size_t GDK_mmap_pagesize = MMAP_PAGESIZE; /* mmap granularity */
287size_t GDK_mem_maxsize = GDK_VM_MAXSIZE;
288size_t GDK_vm_maxsize = GDK_VM_MAXSIZE;
289
290#define SEG_SIZE(x,y) ((x)+(((x)&((1<<(y))-1))?(1<<(y))-((x)&((1<<(y))-1)):0))
291
292/* This block is to provide atomic addition and subtraction to select
293 * variables. We use intrinsic functions (recognized and inlined by
294 * the compiler) for both the GNU C compiler and Microsoft Visual
295 * Studio. By doing this, we avoid locking overhead. There is also a
296 * fall-back for other compilers. */
297#include "matomic.h"
298static ATOMIC_TYPE GDK_mallocedbytes_estimate = ATOMIC_VAR_INIT(0);
299#ifndef NDEBUG
300static volatile lng GDK_malloc_success_count = -1;
301#endif
302static ATOMIC_TYPE GDK_vm_cursize = ATOMIC_VAR_INIT(0);
303
304size_t _MT_pagesize = 0; /* variable holding page size */
305size_t _MT_npages = 0; /* variable holding memory size in pages */
306
307static lng programepoch;
308
309void
310MT_init(void)
311{
312 programepoch = GDKusec();
313#ifdef _MSC_VER
314 {
315 SYSTEM_INFO sysInfo;
316
317 GetSystemInfo(&sysInfo);
318 _MT_pagesize = sysInfo.dwPageSize;
319 }
320#elif defined(BSD) && defined(HW_PAGESIZE)
321 {
322 int size;
323 size_t len = sizeof(int);
324 int mib[2];
325
326 /* Everyone should have permission to make this call,
327 * if we get a failure something is really wrong. */
328 mib[0] = CTL_HW;
329 mib[1] = HW_PAGESIZE;
330 sysctl(mib, 2, &size, &len, NULL, 0);
331 _MT_pagesize = size;
332 }
333#elif defined(HAVE_SYSCONF) && defined(_SC_PAGESIZE)
334 _MT_pagesize = (size_t)sysconf(_SC_PAGESIZE);
335#endif
336 if (_MT_pagesize <= 0)
337 _MT_pagesize = 4096; /* default */
338
339#ifdef WIN32
340 {
341 MEMORYSTATUSEX memStatEx;
342
343 memStatEx.dwLength = sizeof(memStatEx);
344 if (GlobalMemoryStatusEx(&memStatEx))
345 _MT_npages = (size_t) (memStatEx.ullTotalPhys / _MT_pagesize);
346 }
347#elif defined(BSD) && defined(HW_MEMSIZE) && SIZEOF_SIZE_T == SIZEOF_LNG
348 /* Darwin, 64-bits */
349 {
350 uint64_t size = 0;
351 size_t len = sizeof(size);
352 int mib[2];
353
354 /* Everyone should have permission to make this call,
355 * if we get a failure something is really wrong. */
356 mib[0] = CTL_HW;
357 mib[1] = HW_MEMSIZE;
358 sysctl(mib, 2, &size, &len, NULL, 0);
359 _MT_npages = size / _MT_pagesize;
360 }
361#elif defined(BSD) && defined (HW_PHYSMEM64) && SIZEOF_SIZE_T == SIZEOF_LNG
362 /* OpenBSD, 64-bits */
363 {
364 int64_t size = 0;
365 size_t len = sizeof(size);
366 int mib[2];
367
368 /* Everyone should have permission to make this call,
369 * if we get a failure something is really wrong. */
370 mib[0] = CTL_HW;
371 mib[1] = HW_PHYSMEM64;
372 sysctl(mib, 2, &size, &len, NULL, 0);
373 _MT_npages = size / _MT_pagesize;
374 }
375#elif defined(BSD) && defined(HW_PHYSMEM)
376 /* NetBSD, OpenBSD, Darwin, 32-bits; FreeBSD 32 & 64-bits */
377 {
378# ifdef __FreeBSD__
379 unsigned long size = 0; /* type long required by sysctl() (?) */
380# else
381 int size = 0;
382# endif
383 size_t len = sizeof(size);
384 int mib[2];
385
386 /* Everyone should have permission to make this call,
387 * if we get a failure something is really wrong. */
388 mib[0] = CTL_HW;
389 mib[1] = HW_PHYSMEM;
390 sysctl(mib, 2, &size, &len, NULL, 0);
391 _MT_npages = size / _MT_pagesize;
392 }
393#elif defined(HAVE_SYSCONF) && defined(_SC_PHYS_PAGES)
394 _MT_npages = (size_t)sysconf(_SC_PHYS_PAGES);
395# if SIZEOF_SIZE_T == SIZEOF_INT
396 /* Bug #2935: the value returned here can be more than what can be
397 * addressed on Solaris, so cap the value */
398 if (UINT_MAX / _MT_pagesize < _MT_npages)
399 _MT_npages = UINT_MAX / _MT_pagesize;
400# endif
401#else
402# error "don't know how to get the amount of physical memory for your OS"
403#endif
404
405#ifdef __linux__
406 /* limit values to whatever cgroups gives us */
407 FILE *fc;
408 fc = fopen("/proc/self/cgroup", "r");
409 if (fc != NULL) {
410 char buf[1024];
411 /* each line is of the form:
412 * hierarchy-ID:controller-list:cgroup-path
413 *
414 * For cgroup v1, the hierarchy-ID refers to the
415 * second column in /proc/cgroups (which we ignore)
416 * and the controller-list is a comma-separated list
417 * of the controllers bound to the hierarchy. We look
418 * for the "memory" controller and use its
419 * cgroup-path. We ignore the other lines.
420 *
421 * For cgroup v2, the hierarchy-ID is 0 and the
422 * controller-list is empty. We just use the
423 * cgroup-path.
424 *
425 * We use the first line that we can match (either v1
426 * or v2) and for which we can open any of the files
427 * that we are looking for.
428 */
429 while (fgets(buf, (int) sizeof(buf), fc) != NULL) {
430 char pth[1024];
431 char *p, *q;
432 bool success = false; /* true if we can open any file */
433 FILE *f;
434 uint64_t mem;
435 size_t l;
436
437 p = strchr(buf, '\n');
438 if (p == NULL)
439 break;
440 *p = 0;
441 if (strncmp(buf, "0::", 3) == 0) {
442 /* cgroup v2 entry */
443 l = strconcat_len(pth, sizeof(pth),
444 "/sys/fs/cgroup",
445 buf + 3, "/", NULL);
446 /* hard limit */
447 strcpy(pth + l, "memory.max");
448 f = fopen(pth, "r");
449 if (f != NULL) {
450 if (fscanf(f, "%" SCNu64, &mem) == 1 && mem < (uint64_t) _MT_pagesize * _MT_npages) {
451 _MT_npages = (size_t) (mem / _MT_pagesize);
452 }
453 success = true;
454 /* assume "max" if not a number */
455 fclose(f);
456 }
457 /* soft limit */
458 strcpy(pth + l, "memory.high");
459 f = fopen(pth, "r");
460 if (f != NULL) {
461 if (fscanf(f, "%" SCNu64, &mem) == 1 && mem < (uint64_t) _MT_pagesize * _MT_npages) {
462 _MT_npages = (size_t) (mem / _MT_pagesize);
463 }
464 success = true;
465 /* assume "max" if not a number */
466 fclose(f);
467 }
468 /* limit of memory+swap usage
469 * we use this as maximum virtual memory size */
470 strcpy(pth + l, "memory.swap.max");
471 f = fopen(pth, "r");
472 if (f != NULL) {
473 if (fscanf(f, "%" SCNu64, &mem) == 1
474 && mem < (uint64_t) GDK_vm_maxsize) {
475 GDK_vm_maxsize = (size_t) mem;
476 }
477 success = true;
478 fclose(f);
479 }
480 } else {
481 /* cgroup v1 entry */
482 p = strchr(buf, ':');
483 if (p == NULL)
484 break;
485 q = p + 1;
486 p = strchr(q, ':');
487 if (p == NULL)
488 break;
489 *p++ = 0;
490 if (strstr(q, "memory") == NULL)
491 continue;
492 l = strconcat_len(pth, sizeof(pth),
493 "/sys/fs/cgroup/", q,
494 p, "/", NULL);
495 /* limit of memory usage */
496 strcpy(pth + l, "memory.limit_in_bytes");
497 f = fopen(pth, "r");
498 if (f != NULL) {
499 if (fscanf(f, "%" SCNu64, &mem) == 1
500 && mem < (uint64_t) _MT_pagesize * _MT_npages) {
501 _MT_npages = (size_t) (mem / _MT_pagesize);
502 }
503 success = true;
504 fclose(f);
505 }
506 /* soft limit of memory usage */
507 strcpy(pth + l, "memory.soft_limit_in_bytes");
508 f = fopen(pth, "r");
509 if (f != NULL) {
510 if (fscanf(f, "%" SCNu64, &mem) == 1
511 && mem < (uint64_t) _MT_pagesize * _MT_npages) {
512 _MT_npages = (size_t) (mem / _MT_pagesize);
513 }
514 success = true;
515 fclose(f);
516 }
517 /* limit of memory+swap usage
518 * we use this as maximum virtual memory size */
519 strcpy(pth + l, "memory.memsw.limit_in_bytes");
520 f = fopen(pth, "r");
521 if (f != NULL) {
522 if (fscanf(f, "%" SCNu64, &mem) == 1
523 && mem < (uint64_t) GDK_vm_maxsize) {
524 GDK_vm_maxsize = (size_t) mem;
525 }
526 success = true;
527 fclose(f);
528 }
529 }
530 if (success)
531 break;
532 }
533 fclose(fc);
534 }
535#endif
536
537#if defined(HAVE_SYS_RESOURCE_H) && defined(HAVE_GETRLIMIT) && defined(RLIMIT_AS)
538 struct rlimit l;
539 /* address space (virtual memory) limit */
540 if (getrlimit(RLIMIT_AS, &l) == 0
541 && l.rlim_cur != RLIM_INFINITY
542 && l.rlim_cur < GDK_vm_maxsize) {
543 GDK_vm_maxsize = l.rlim_cur;
544 }
545#endif
546}
547
548/*
549 * @+ Session Initialization
550 * The interface code to the operating system is highly dependent on
551 * the processing environment. It can be filtered away with
552 * compile-time flags. Suicide is necessary due to some system
553 * implementation errors.
554 *
555 * The kernel requires file descriptors for I/O with the user. They
556 * are thread specific and should be obtained by a function.
557 *
558 * The arguments relevant for the kernel are extracted from the list.
559 * Their value is turned into a blanc space.
560 */
561
562#define CATNAP 50 /* time to sleep in ms for catnaps */
563
564static int THRinit(void);
565static gdk_return GDKlockHome(int farmid);
566
567#ifndef STATIC_CODE_ANALYSIS
568#ifndef NDEBUG
569static MT_Lock mallocsuccesslock = MT_LOCK_INITIALIZER("mallocsuccesslk");
570#endif
571#endif
572
573void
574GDKsetdebug(int debug)
575{
576 GDKdebug = debug;
577}
578
579void
580GDKsetverbose(int verbose)
581{
582 GDKverbose = verbose;
583}
584
585gdk_return
586GDKinit(opt *set, int setlen)
587{
588 static bool first = true;
589 char *dbpath = mo_find_option(set, setlen, "gdk_dbpath");
590 const char *p;
591 opt *n;
592 int i, nlen = 0;
593 char buf[16];
594
595 /* some sanity checks (should also find if symbols are not defined) */
596 static_assert(sizeof(char) == SIZEOF_CHAR,
597 "error in configure: bad value for SIZEOF_CHAR");
598 static_assert(sizeof(short) == SIZEOF_SHORT,
599 "error in configure: bad value for SIZEOF_SHORT");
600 static_assert(sizeof(int) == SIZEOF_INT,
601 "error in configure: bad value for SIZEOF_INT");
602 static_assert(sizeof(long) == SIZEOF_LONG,
603 "error in configure: bad value for SIZEOF_LONG");
604 static_assert(sizeof(lng) == SIZEOF_LNG,
605 "error in configure: bad value for SIZEOF_LNG");
606#ifdef HAVE_HGE
607 static_assert(sizeof(hge) == SIZEOF_HGE,
608 "error in configure: bad value for SIZEOF_HGE");
609#endif
610 static_assert(sizeof(oid) == SIZEOF_OID,
611 "error in configure: bad value for SIZEOF_OID");
612 static_assert(sizeof(void *) == SIZEOF_VOID_P,
613 "error in configure: bad value for SIZEOF_VOID_P");
614 static_assert(sizeof(size_t) == SIZEOF_SIZE_T,
615 "error in configure: bad value for SIZEOF_SIZE_T");
616 static_assert(SIZEOF_OID == SIZEOF_INT || SIZEOF_OID == SIZEOF_LNG,
617 "SIZEOF_OID should be equal to SIZEOF_INT or SIZEOF_LNG");
618
619 if (first) {
620 /* some things are really only initialized once */
621 if (!MT_thread_init())
622 return GDK_FAIL;
623
624 for (i = 0; i <= BBP_BATMASK; i++) {
625 char name[16];
626 snprintf(name, sizeof(name), "GDKswapLock%d", i);
627 MT_lock_init(&GDKbatLock[i].swap, name);
628 }
629 for (i = 0; i <= BBP_THREADMASK; i++) {
630 char name[16];
631 snprintf(name, sizeof(name), "GDKcacheLock%d", i);
632 MT_lock_init(&GDKbbpLock[i].cache, name);
633 snprintf(name, sizeof(name), "GDKtrimLock%d", i);
634 MT_lock_init(&GDKbbpLock[i].trim, name);
635 GDKbbpLock[i].free = 0;
636 }
637 if (mnstr_init() < 0)
638 return GDK_FAIL;
639 first = false;
640 } else {
641 /* BBP was locked by BBPexit() */
642 BBPunlock();
643 }
644 errno = 0;
645 if (!GDKinmemory() && !GDKenvironment(dbpath))
646 return GDK_FAIL;
647
648 MT_init_posix();
649 if (THRinit() < 0)
650 return GDK_FAIL;
651#ifndef NATIVE_WIN32
652 if (BATSIGinit() < 0)
653 return GDK_FAIL;
654#endif
655#ifdef WIN32
656 (void) signal(SIGABRT, BATSIGabort);
657#if !defined(__MINGW32__) && !defined(__CYGWIN__)
658 _set_abort_behavior(0, _CALL_REPORTFAULT | _WRITE_ABORT_MSG);
659 _set_error_mode(_OUT_TO_STDERR);
660#endif
661#endif
662 MT_init();
663
664 /* now try to lock the database: go through all farms, and if
665 * we see a new directory, lock it */
666 for (int farmid = 0; farmid < MAXFARMS; farmid++) {
667 if (BBPfarms[farmid].dirname != NULL) {
668 bool skip = false;
669 for (int j = 0; j < farmid; j++) {
670 if (BBPfarms[j].dirname != NULL &&
671 strcmp(BBPfarms[farmid].dirname, BBPfarms[j].dirname) == 0) {
672 skip = true;
673 break;
674 }
675 }
676 if (!skip && GDKlockHome(farmid) != GDK_SUCCEED)
677 return GDK_FAIL;
678 }
679 }
680
681 /* Mserver by default takes 80% of all memory as a default */
682 GDK_mem_maxsize = (size_t) ((double) MT_npages() * (double) MT_pagesize() * 0.815);
683 if (BBPinit() != GDK_SUCCEED)
684 return GDK_FAIL;
685
686 if (GDK_mem_maxsize / 16 < GDK_mmap_minsize_transient) {
687 GDK_mmap_minsize_transient = GDK_mem_maxsize / 16;
688 if (GDK_mmap_minsize_persistent > GDK_mmap_minsize_transient)
689 GDK_mmap_minsize_persistent = GDK_mmap_minsize_transient;
690 }
691
692 n = (opt *) malloc(setlen * sizeof(opt));
693 if (n == NULL)
694 return GDK_FAIL;
695
696 for (i = 0; i < setlen; i++) {
697 bool done = false;
698
699 for (int j = 0; j < nlen; j++) {
700 if (strcmp(n[j].name, set[i].name) == 0) {
701 if (n[j].kind < set[i].kind) {
702 n[j] = set[i];
703 }
704 done = true;
705 break;
706 }
707 }
708 if (!done) {
709 n[nlen] = set[i];
710 nlen++;
711 }
712 }
713 /* check some options before creating our first BAT */
714 for (i = 0; i < nlen; i++) {
715 if (strcmp("gdk_mem_maxsize", n[i].name) == 0) {
716 GDK_mem_maxsize = (size_t) strtoll(n[i].value, NULL, 10);
717 GDK_mem_maxsize = MAX(1 << 26, GDK_mem_maxsize);
718 } else if (strcmp("gdk_vm_maxsize", n[i].name) == 0) {
719 GDK_vm_maxsize = (size_t) strtoll(n[i].value, NULL, 10);
720 GDK_vm_maxsize = MAX(1 << 30, GDK_vm_maxsize);
721 if (GDK_vm_maxsize < GDK_mmap_minsize_persistent / 4)
722 GDK_mmap_minsize_persistent = GDK_vm_maxsize / 4;
723 if (GDK_vm_maxsize < GDK_mmap_minsize_transient / 4)
724 GDK_mmap_minsize_transient = GDK_vm_maxsize / 4;
725 } else if (strcmp("gdk_mmap_minsize_persistent", n[i].name) == 0) {
726 GDK_mmap_minsize_persistent = (size_t) strtoll(n[i].value, NULL, 10);
727 } else if (strcmp("gdk_mmap_minsize_transient", n[i].name) == 0) {
728 GDK_mmap_minsize_transient = (size_t) strtoll(n[i].value, NULL, 10);
729 } else if (strcmp("gdk_mmap_pagesize", n[i].name) == 0) {
730 GDK_mmap_pagesize = (size_t) strtoll(n[i].value, NULL, 10);
731 if (GDK_mmap_pagesize < 1 << 12 ||
732 GDK_mmap_pagesize > 1 << 20 ||
733 /* x & (x - 1): turn off rightmost 1 bit;
734 * i.e. if result is zero, x is power of
735 * two */
736 (GDK_mmap_pagesize & (GDK_mmap_pagesize - 1)) != 0) {
737 free(n);
738 GDKerror("GDKinit: gdk_mmap_pagesize must be power of 2 between 2**12 and 2**20\n");
739 return GDK_FAIL;
740 }
741 }
742 }
743
744 GDKkey = COLnew(0, TYPE_str, 100, TRANSIENT);
745 GDKval = COLnew(0, TYPE_str, 100, TRANSIENT);
746 if (GDKkey == NULL || GDKval == NULL) {
747 free(n);
748 GDKerror("GDKinit: Could not create environment BAT");
749 return GDK_FAIL;
750 }
751 if (BBPrename(GDKkey->batCacheid, "environment_key") != 0 ||
752 BBPrename(GDKval->batCacheid, "environment_val") != 0) {
753 free(n);
754 GDKerror("GDKinit: BBPrename failed");
755 return GDK_FAIL;
756 }
757
758 /* store options into environment BATs */
759 for (i = 0; i < nlen; i++)
760 if (GDKsetenv(n[i].name, n[i].value) != GDK_SUCCEED) {
761 free(n);
762 GDKerror("GDKinit: GDKsetenv failed");
763 return GDK_FAIL;
764 }
765 free(n);
766
767 GDKnr_threads = GDKgetenv_int("gdk_nr_threads", 0);
768 if (GDKnr_threads == 0)
769 GDKnr_threads = MT_check_nr_cores();
770
771 if (!GDKinmemory()) {
772 if ((p = GDKgetenv("gdk_dbpath")) != NULL &&
773 (p = strrchr(p, DIR_SEP)) != NULL) {
774 if (GDKsetenv("gdk_dbname", p + 1) != GDK_SUCCEED) {
775 GDKerror("GDKinit: GDKsetenv failed");
776 return GDK_FAIL;
777 }
778#if DIR_SEP != '/' /* on Windows look for different separator */
779 } else if ((p = GDKgetenv("gdk_dbpath")) != NULL &&
780 (p = strrchr(p, '/')) != NULL) {
781 if (GDKsetenv("gdk_dbname", p + 1) != GDK_SUCCEED) {
782 GDKerror("GDKinit: GDKsetenv failed");
783 return GDK_FAIL;
784 }
785#endif
786 }
787 } else {
788 if (GDKsetenv("gdk_dbname", ":inmemory") != GDK_SUCCEED) {
789 GDKerror("GDKinit: GDKsetenv failed");
790 return GDK_FAIL;
791 }
792 }
793 if (GDKgetenv("gdk_vm_maxsize") == NULL) {
794 snprintf(buf, sizeof(buf), "%zu", GDK_vm_maxsize);
795 if (GDKsetenv("gdk_vm_maxsize", buf) != GDK_SUCCEED) {
796 GDKerror("GDKinit: GDKsetenv failed");
797 return GDK_FAIL;
798 }
799 }
800 if (GDKgetenv("gdk_mem_maxsize") == NULL) {
801 snprintf(buf, sizeof(buf), "%zu", GDK_mem_maxsize);
802 if (GDKsetenv("gdk_mem_maxsize", buf) != GDK_SUCCEED) {
803 GDKerror("GDKinit: GDKsetenv failed");
804 return GDK_FAIL;
805 }
806 }
807 if (GDKgetenv("gdk_mmap_minsize_persistent") == NULL) {
808 snprintf(buf, sizeof(buf), "%zu", GDK_mmap_minsize_persistent);
809 if (GDKsetenv("gdk_mmap_minsize_persistent", buf) != GDK_SUCCEED) {
810 GDKerror("GDKinit: GDKsetenv failed");
811 return GDK_FAIL;
812 }
813 }
814 if (GDKgetenv("gdk_mmap_minsize_transient") == NULL) {
815 snprintf(buf, sizeof(buf), "%zu", GDK_mmap_minsize_transient);
816 if (GDKsetenv("gdk_mmap_minsize_transient", buf) != GDK_SUCCEED) {
817 GDKerror("GDKinit: GDKsetenv failed");
818 return GDK_FAIL;
819 }
820 }
821 if (GDKgetenv("gdk_mmap_pagesize") == NULL) {
822 snprintf(buf, sizeof(buf), "%zu", GDK_mmap_pagesize);
823 if (GDKsetenv("gdk_mmap_pagesize", buf) != GDK_SUCCEED) {
824 GDKerror("GDKinit: GDKsetenv failed");
825 return GDK_FAIL;
826 }
827 }
828 if (GDKgetenv("monet_pid") == NULL) {
829 snprintf(buf, sizeof(buf), "%d", (int) getpid());
830 if (GDKsetenv("monet_pid", buf) != GDK_SUCCEED) {
831 GDKerror("GDKinit: GDKsetenv failed");
832 return GDK_FAIL;
833 }
834 }
835 if (GDKsetenv("revision", mercurial_revision()) != GDK_SUCCEED) {
836 GDKerror("GDKinit: GDKsetenv failed");
837 return GDK_FAIL;
838 }
839
840 return GDK_SUCCEED;
841}
842
843int GDKnr_threads = 0;
844static ATOMIC_TYPE GDKnrofthreads = ATOMIC_VAR_INIT(0);
845static ThreadRec GDKthreads[THREADS];
846
847bool
848GDKexiting(void)
849{
850 return (bool) (ATOMIC_GET(&GDKstopped) > 0);
851}
852
853void
854GDKprepareExit(void)
855{
856 if (ATOMIC_ADD(&GDKstopped, 1) > 0)
857 return;
858
859 THRDDEBUG dump_threads();
860 join_detached_threads();
861}
862
863void
864GDKreset(int status)
865{
866 MT_Id pid = MT_getpid();
867
868 assert(GDKexiting());
869
870 if (GDKkey) {
871 BBPunfix(GDKkey->batCacheid);
872 GDKkey = NULL;
873 }
874 if (GDKval) {
875 BBPunfix(GDKval->batCacheid);
876 GDKval = NULL;
877 }
878
879 join_detached_threads();
880
881 if (status == 0) {
882 /* they had their chance, now kill them */
883 bool killed = false;
884 MT_lock_set(&GDKthreadLock);
885 for (Thread t = GDKthreads; t < GDKthreads + THREADS; t++) {
886 MT_Id victim;
887 if ((victim = (MT_Id) ATOMIC_GET(&t->pid)) != 0) {
888 if (victim != pid) {
889 int e;
890
891 killed = true;
892 e = MT_kill_thread(victim);
893 fprintf(stderr, "#GDKexit: killing thread %d\n", e);
894 (void) ATOMIC_DEC(&GDKnrofthreads);
895 }
896 GDKfree(t->name);
897 t->name = NULL;
898 ATOMIC_SET(&t->pid, 0);
899 }
900 }
901 assert(ATOMIC_GET(&GDKnrofthreads) <= 1);
902 /* all threads ceased running, now we can clean up */
903 if (!killed) {
904 /* we can't clean up after killing threads */
905 BBPexit();
906 }
907 GDKlog(GET_GDKLOCK(PERSISTENT), GDKLOGOFF);
908
909 for (int farmid = 0; farmid < MAXFARMS; farmid++) {
910 if (BBPfarms[farmid].dirname != NULL) {
911 bool skip = false;
912 for (int j = 0; j < farmid; j++) {
913 if (BBPfarms[j].dirname != NULL &&
914 strcmp(BBPfarms[farmid].dirname, BBPfarms[j].dirname) == 0) {
915 skip = true;
916 break;
917 }
918 }
919 if (!skip)
920 GDKunlockHome(farmid);
921 }
922 }
923
924#ifdef LOCK_STATS
925 TEMDEBUG GDKlockstatistics(1);
926#endif
927 GDKdebug = 0;
928 GDK_mmap_minsize_persistent = MMAP_MINSIZE_PERSISTENT;
929 GDK_mmap_minsize_transient = MMAP_MINSIZE_TRANSIENT;
930 GDK_mmap_pagesize = MMAP_PAGESIZE;
931 GDK_mem_maxsize = (size_t) ((double) MT_npages() * (double) MT_pagesize() * 0.815);
932 GDK_vm_maxsize = GDK_VM_MAXSIZE;
933 GDKatomcnt = TYPE_str + 1;
934
935 if (GDK_mem_maxsize / 16 < GDK_mmap_minsize_transient) {
936 GDK_mmap_minsize_transient = GDK_mem_maxsize / 16;
937 if (GDK_mmap_minsize_persistent > GDK_mmap_minsize_transient)
938 GDK_mmap_minsize_persistent = GDK_mmap_minsize_transient;
939 }
940
941 GDKnr_threads = 0;
942 ATOMIC_SET(&GDKnrofthreads, 0);
943 close_stream((stream *) THRdata[0]);
944 close_stream((stream *) THRdata[1]);
945 for (int i = 0; i <= BBP_THREADMASK; i++) {
946 GDKbbpLock[i].free = 0;
947 }
948
949 memset(THRdata, 0, sizeof(THRdata));
950 gdk_bbp_reset();
951 MT_lock_unset(&GDKthreadLock);
952 }
953 ATOMunknown_clean();
954}
955
956/* coverity[+kill] */
957void
958GDKexit(int status)
959{
960 if (!GDKinmemory() && GET_GDKLOCK(PERSISTENT) == NULL) {
961#ifdef HAVE_EMBEDDED
962 return;
963#else
964 /* no database lock, so no threads, so exit now */
965 exit(status);
966#endif
967 }
968 GDKprepareExit();
969 GDKreset(status);
970#ifndef HAVE_EMBEDDED
971 exit(status);
972#endif
973}
974
975/*
976 * All semaphores used by the application should be mentioned here.
977 * They are initialized during system initialization.
978 */
979
980batlock_t GDKbatLock[BBP_BATMASK + 1];
981bbplock_t GDKbbpLock[BBP_THREADMASK + 1];
982MT_Lock GDKnameLock = MT_LOCK_INITIALIZER("GDKnameLock");
983MT_Lock GDKthreadLock = MT_LOCK_INITIALIZER("GDKthreadLock");
984MT_Lock GDKtmLock = MT_LOCK_INITIALIZER("GDKtmLock");
985
986/*
987 * @+ Concurrency control
988 * Concurrency control requires actions at several levels of the
989 * system. First, it should be ensured that each database is
990 * controlled by a single server process (group). Subsequent attempts
991 * should be stopped. This is regulated through file locking against
992 * ".gdk_lock".
993 *
994 * Before the locks and threads are initiated, we cannot use the
995 * normal routines yet. So we have a local fatal here instead of
996 * GDKfatal.
997 */
998static gdk_return
999GDKlockHome(int farmid)
1000{
1001 int fd;
1002 struct stat st;
1003 char *gdklockpath;
1004 FILE *GDKlockFile;
1005
1006 assert(BBPfarms[farmid].dirname != NULL);
1007 assert(BBPfarms[farmid].lock_file == NULL);
1008
1009 if(!(gdklockpath = GDKfilepath(farmid, NULL, GDKLOCK, NULL))) {
1010 GDKerror("GDKlockHome: malloc failure\n");
1011 return GDK_FAIL;
1012 }
1013
1014 /*
1015 * Obtain the global database lock.
1016 */
1017 if (stat(BBPfarms[farmid].dirname, &st) < 0 &&
1018 GDKcreatedir(gdklockpath) != GDK_SUCCEED) {
1019 GDKerror("GDKlockHome: could not create %s\n",
1020 BBPfarms[farmid].dirname);
1021 return GDK_FAIL;
1022 }
1023 if ((fd = MT_lockf(gdklockpath, F_TLOCK, 4, 1)) < 0) {
1024 GDKerror("GDKlockHome: Database lock '%s' denied\n",
1025 gdklockpath);
1026 return GDK_FAIL;
1027 }
1028
1029 /* now we have the lock on the database and are the only
1030 * process allowed in this section */
1031
1032 if ((GDKlockFile = fdopen(fd, "r+")) == NULL) {
1033 close(fd);
1034 GDKerror("GDKlockHome: Could not fdopen %s\n", gdklockpath);
1035 return GDK_FAIL;
1036 }
1037
1038 /*
1039 * Print the new process list in the global lock file.
1040 */
1041 if (fseek(GDKlockFile, 0, SEEK_SET) == -1) {
1042 fclose(GDKlockFile);
1043 GDKerror("GDKlockHome: Error while setting the file pointer on %s\n", gdklockpath);
1044 return GDK_FAIL;
1045 }
1046 if (ftruncate(fileno(GDKlockFile), 0) < 0) {
1047 fclose(GDKlockFile);
1048 GDKerror("GDKlockHome: Could not truncate %s\n", gdklockpath);
1049 return GDK_FAIL;
1050 }
1051 if (fflush(GDKlockFile) == EOF) {
1052 fclose(GDKlockFile);
1053 GDKerror("GDKlockHome: Could not flush %s\n", gdklockpath);
1054 return GDK_FAIL;
1055 }
1056 GDKlog(GDKlockFile, GDKLOGON);
1057 GDKfree(gdklockpath);
1058 BBPfarms[farmid].lock_file = GDKlockFile;
1059 return GDK_SUCCEED;
1060}
1061
1062
1063static void
1064GDKunlockHome(int farmid)
1065{
1066 if (BBPfarms[farmid].lock_file) {
1067 char *gdklockpath = GDKfilepath(farmid, NULL, GDKLOCK, NULL);
1068 MT_lockf(gdklockpath, F_ULOCK, 4, 1);
1069 fclose(BBPfarms[farmid].lock_file);
1070 BBPfarms[farmid].lock_file = NULL;
1071 GDKfree(gdklockpath);
1072 }
1073}
1074
1075/*
1076 * @+ Error handling
1077 * Errors come in three flavors: warnings, non-fatal and fatal errors.
1078 * A fatal error leaves a core dump behind after trying to safe the
1079 * content of the relation. A non-fatal error returns a message to
1080 * the user and aborts the current transaction. Fatal errors are also
1081 * recorded on the system log for post-mortem analysis.
1082 * In non-silent mode the errors are immediately sent to output, which
1083 * makes it hard for upper layers to detect if an error was produced
1084 * in the process. To facilitate such testing, a global error count is
1085 * maintained on a thread basis, which can be read out by the function
1086 * GDKerrorCount(); Furthermore, threads may have set their private
1087 * error buffer.
1088 */
1089
1090/* do the real work for GDKaddbuf below. */
1091static void
1092doGDKaddbuf(const char *prefix, const char *message, size_t messagelen, const char *suffix)
1093{
1094 char *buf;
1095
1096 buf = GDKerrbuf;
1097 if (buf) {
1098 char *dst = buf + strlen(buf);
1099 size_t maxlen = GDKMAXERRLEN - (dst - buf) - 1;
1100
1101 if (*prefix && dst < buf + GDKMAXERRLEN) {
1102 size_t preflen;
1103
1104 strncpy(dst, prefix, maxlen);
1105 dst[maxlen] = '\0';
1106 preflen = strlen(dst);
1107 maxlen -= preflen;
1108 dst += preflen;
1109 }
1110 if (maxlen > messagelen)
1111 maxlen = messagelen;
1112 strncpy(dst, message, maxlen);
1113 dst += maxlen;
1114 if (*suffix && dst < buf + GDKMAXERRLEN) {
1115 size_t sufflen;
1116
1117 maxlen = buf + GDKMAXERRLEN - dst - 1;
1118 strncpy(dst, suffix, maxlen);
1119 dst[maxlen] = '\0';
1120 sufflen = strlen(dst);
1121 maxlen -= sufflen;
1122 dst += sufflen;
1123 }
1124 *dst = '\0';
1125 } else {
1126 fprintf(stderr, "%s%.*s%s", prefix, (int) messagelen, message, suffix);
1127 }
1128 fprintf(stderr, "#%s:%s%.*s%s",
1129 MT_thread_getname(),
1130 prefix[0] == '#' ? prefix + 1 : prefix,
1131 (int) messagelen, message, suffix);
1132}
1133
1134/* print an error or warning message, making sure the message ends in
1135 * a newline, and also that every line in the message (if there are
1136 * multiple), starts with an exclamation point.
1137 * One of the problems complicating this whole issue is that each line
1138 * should be printed using a single call to mnstr_printf, and moreover,
1139 * the format string should start with a "!".
1140 * Another problem is that we're religious about bounds checking. It
1141 * would probably also not be quite as bad if we could write in the
1142 * message buffer.
1143 */
1144static void
1145GDKaddbuf(const char *message)
1146{
1147 const char *p, *q;
1148 char prefix[16];
1149
1150 if (message == NULL || *message == '\0') /* empty message, nothing to do */
1151 return;
1152 /* filter out duplicate messages */
1153 if (GDKerrbuf && strstr(GDKerrbuf , message))
1154 return;
1155 p = message;
1156 strcpy(prefix, "!"); /* default prefix */
1157 while (p && *p) {
1158 if (*p == '!') {
1159 size_t preflen;
1160
1161 /* remember last ! prefix (e.g. "!ERROR: ")
1162 * for any subsequent lines that start without
1163 * ! */
1164 message = p;
1165 /* A prefix consists of a ! immediately
1166 * followed by some text, followed by a : and
1167 * a space. Anything else results in no
1168 * prefix being remembered */
1169 while (*++p && *p != ':' && *p != '\n' && *p != ' ')
1170 ;
1171 if (*p == ':' && *++p == ' ') {
1172 /* found prefix, now remember it */
1173 preflen = (size_t) (p - message) + 1;
1174 if (preflen > sizeof(prefix) - 1)
1175 preflen = sizeof(prefix) - 1;
1176 strncpy(prefix, message, preflen);
1177 prefix[preflen] = 0;
1178 } else {
1179 /* there is a ! but no proper prefix */
1180 strcpy(prefix, "!");
1181 preflen = 1;
1182 }
1183 p = message + preflen;
1184 }
1185
1186 /* find end of line */
1187 q = strchr(p, '\n');
1188 if (q) {
1189 /* print line including newline */
1190 q++;
1191 doGDKaddbuf(prefix, p, (size_t) (q - p), "");
1192 } else {
1193 /* no newline at end of buffer: print all the
1194 * rest and add a newline */
1195 doGDKaddbuf(prefix, p, strlen(p), "\n");
1196 /* we're done since there were no more newlines */
1197 break;
1198 }
1199 p = q;
1200 }
1201}
1202
1203#define GDKERRLEN (1024+512)
1204
1205void
1206GDKerror(const char *format, ...)
1207{
1208 char message[GDKERRLEN];
1209 size_t len = strlen(GDKERROR);
1210 va_list ap;
1211
1212 if (!strncmp(format, GDKERROR, len)) {
1213 len = 0;
1214 } else {
1215 strcpy(message, GDKERROR);
1216 }
1217 va_start(ap, format);
1218 if (vsnprintf(message + len, sizeof(message) - (len + 2), format, ap) < 0){
1219 fprintf(stderr,GDKERROR "an error occurred within GDKerror.\n");
1220 strcpy(message, GDKERROR "an error occurred within GDKerror.\n");
1221 }
1222 va_end(ap);
1223
1224 GDKaddbuf(message);
1225}
1226
1227void
1228GDKsyserror(const char *format, ...)
1229{
1230 int err = errno;
1231 char message[GDKERRLEN];
1232 size_t len = strlen(GDKERROR);
1233 va_list ap;
1234
1235 if (strncmp(format, GDKERROR, len) == 0) {
1236 len = 0;
1237 } else {
1238 strncpy(message, GDKERROR, sizeof(message));
1239 }
1240 va_start(ap, format);
1241 vsnprintf(message + len, sizeof(message) - (len + 2), format, ap);
1242 va_end(ap);
1243 if (err > 0 && err < 1024) {
1244 size_t len1;
1245 size_t len2;
1246 size_t len3;
1247 char *osmsg;
1248 osmsg = strerror(err);
1249 len1 = strlen(message);
1250 len2 = len1 + strlen(GDKMESSAGE);
1251 len3 = len2 + strlen(osmsg);
1252
1253 if (len3 + 2 < sizeof(message)) {
1254 strcpy(message + len1, GDKMESSAGE);
1255 strcpy(message + len2, osmsg);
1256 if (len3 > 0 && message[len3 - 1] != '\n') {
1257 message[len3] = '\n';
1258 message[len3 + 1] = 0;
1259 }
1260 }
1261 }
1262 GDKaddbuf(message);
1263
1264 errno = 0;
1265}
1266
1267#ifdef NATIVE_WIN32
1268void
1269GDKwinerror(const char *format, ...)
1270{
1271 int err = GetLastError();
1272 char message[GDKERRLEN];
1273 size_t len = strlen(GDKERROR);
1274 va_list ap;
1275
1276 if (strncmp(format, GDKERROR, len) == 0) {
1277 len = 0;
1278 } else {
1279 strncpy(message, GDKERROR, sizeof(message));
1280 }
1281 va_start(ap, format);
1282 vsnprintf(message + len, sizeof(message) - (len + 2), format, ap);
1283 va_end(ap);
1284
1285 size_t len1;
1286 size_t len2;
1287 size_t len3;
1288 char *osmsg;
1289 char osmsgbuf[256];
1290 osmsg = osmsgbuf;
1291 FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, err,
1292 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
1293 (LPTSTR) osmsgbuf, sizeof(osmsgbuf), NULL);
1294 len1 = strlen(message);
1295 len2 = len1 + strlen(GDKMESSAGE);
1296 len3 = len2 + strlen(osmsg);
1297
1298 if (len3 + 2 < sizeof(message)) {
1299 strcpy(message + len1, GDKMESSAGE);
1300 strcpy(message + len2, osmsg);
1301 if (len3 > 0 && message[len3 - 1] != '\n') {
1302 message[len3] = '\n';
1303 message[len3 + 1] = 0;
1304 }
1305 }
1306 GDKaddbuf(message);
1307
1308 SetLastError(0);
1309}
1310#endif
1311
1312void
1313GDKclrerr(void)
1314{
1315 char *buf;
1316
1317 buf = GDKerrbuf;
1318 if (buf)
1319 *buf = 0;
1320}
1321
1322jmp_buf GDKfataljump;
1323str GDKfatalmsg;
1324bit GDKfataljumpenable = 0;
1325
1326/* coverity[+kill] */
1327void
1328GDKfatal(const char *format, ...)
1329{
1330 char message[GDKERRLEN];
1331 size_t len = strlen(GDKFATAL);
1332 va_list ap;
1333
1334 GDKdebug |= IOMASK;
1335#ifndef NATIVE_WIN32
1336 BATSIGinit();
1337#endif
1338 if (!strncmp(format, GDKFATAL, len)) {
1339 len = 0;
1340 } else {
1341 strcpy(message, GDKFATAL);
1342 }
1343 va_start(ap, format);
1344 vsnprintf(message + len, sizeof(message) - (len + 2), format, ap);
1345 va_end(ap);
1346
1347#ifndef STATIC_CODE_ANALYSIS
1348 if (GDKfataljumpenable) {
1349 // in embedded mode, we really don't want to kill our host
1350 GDKfatalmsg = GDKstrdup(message);
1351 longjmp(GDKfataljump, 42);
1352 } else
1353#endif
1354 {
1355 fputs(message, stderr);
1356 fputs("\n", stderr);
1357 fflush(stderr);
1358
1359 /*
1360 * Real errors should be saved in the log file for post-crash
1361 * inspection.
1362 */
1363 if (GDKexiting()) {
1364 fflush(stdout);
1365 exit(1);
1366 } else {
1367 GDKlog(GET_GDKLOCK(PERSISTENT), "%s", message);
1368#ifdef COREDUMP
1369 abort();
1370#else
1371 GDKexit(1);
1372#endif
1373 }
1374 }
1375}
1376
1377
1378lng
1379GDKusec(void)
1380{
1381 /* Return the time in microseconds since an epoch. The epoch
1382 * is currently midnight at the start of January 1, 1970, UTC. */
1383#if defined(NATIVE_WIN32)
1384 FILETIME ft;
1385 ULARGE_INTEGER f;
1386 GetSystemTimeAsFileTime(&ft); /* time since Jan 1, 1601 */
1387 f.LowPart = ft.dwLowDateTime;
1388 f.HighPart = ft.dwHighDateTime;
1389 /* there are 369 years, of which 89 are leap years from
1390 * January 1, 1601 to January 1, 1970 which makes 134774 days;
1391 * multiply that with the number of seconds in a day and the
1392 * number of 100ns units in a second; subtract that from the
1393 * value for the current time since January 1, 1601 to get the
1394 * time since the Unix epoch */
1395 f.QuadPart -= LL_CONSTANT(134774) * 24 * 60 * 60 * 10000000;
1396 /* and convert to microseconds */
1397 return (lng) (f.QuadPart / 10);
1398#elif defined(HAVE_CLOCK_GETTIME)
1399 struct timespec ts;
1400 clock_gettime(CLOCK_REALTIME, &ts);
1401 return (lng) (ts.tv_sec * LL_CONSTANT(1000000) + ts.tv_nsec / 1000);
1402#elif defined(HAVE_GETTIMEOFDAY)
1403 struct timeval tv;
1404 gettimeofday(&tv, NULL);
1405 return (lng) (tv.tv_sec * LL_CONSTANT(1000000) + tv.tv_usec);
1406#elif defined(HAVE_FTIME)
1407 struct timeb tb;
1408 ftime(&tb);
1409 return (lng) (tb.time * LL_CONSTANT(1000000) + tb.millitm * LL_CONSTANT(1000));
1410#else
1411 /* last resort */
1412 return (lng) (time(NULL) * LL_CONSTANT(1000000));
1413#endif
1414}
1415
1416
1417int
1418GDKms(void)
1419{
1420 /* wraps around after a bit over 24 days */
1421 return (int) ((GDKusec() - programepoch) / 1000);
1422}
1423
1424
1425/*
1426 * @+ Logical Thread management
1427 *
1428 * All semaphores used by the application should be mentioned here.
1429 * They are initialized during system initialization.
1430 *
1431 * The first action upon thread creation is to add it to the pool of
1432 * known threads. This should be done by the thread itself.
1433 * Subsequently, the thread descriptor can be obtained using THRget.
1434 * Note that the users should have gained exclusive access already. A
1435 * new entry is initialized automatically when not found. Its file
1436 * descriptors are the same as for the server and should be
1437 * subsequently reset.
1438 */
1439void *THRdata[THREADDATA] = { 0 };
1440
1441Thread
1442THRget(int tid)
1443{
1444 assert(0 < tid && tid <= THREADS);
1445 return &GDKthreads[tid - 1];
1446}
1447
1448#if defined(_MSC_VER) && _MSC_VER >= 1900
1449#pragma warning(disable : 4172)
1450#endif
1451static inline uintptr_t
1452THRsp(void)
1453{
1454 int l = 0;
1455 uintptr_t sp = (uintptr_t) (&l);
1456
1457 return sp;
1458}
1459
1460static inline Thread
1461GDK_find_self(void)
1462{
1463 return (Thread) MT_thread_getdata();
1464}
1465
1466static Thread
1467THRnew(const char *name, MT_Id pid)
1468{
1469 char *nme = GDKstrdup(name);
1470
1471 if (nme == NULL) {
1472 IODEBUG fprintf(stderr, "#THRnew: malloc failure\n");
1473 GDKerror("THRnew: malloc failure\n");
1474 return NULL;
1475 }
1476 for (Thread s = GDKthreads; s < GDKthreads + THREADS; s++) {
1477 ATOMIC_BASE_TYPE npid = 0;
1478 if (ATOMIC_CAS(&s->pid, &npid, pid)) {
1479 /* successfully allocated, fill in rest */
1480 s->data[0] = THRdata[0];
1481 s->data[1] = THRdata[1];
1482 s->sp = THRsp();
1483 s->name = nme;
1484 PARDEBUG fprintf(stderr, "#%x %zu sp = %zu\n",
1485 (unsigned) s->tid,
1486 (size_t) ATOMIC_GET(&s->pid),
1487 (size_t) s->sp);
1488 PARDEBUG fprintf(stderr, "#nrofthreads %d\n",
1489 (int) ATOMIC_GET(&GDKnrofthreads) + 1);
1490 return s;
1491 }
1492 }
1493 GDKfree(nme);
1494 IODEBUG fprintf(stderr, "#THRnew: too many threads\n");
1495 GDKerror("THRnew: too many threads\n");
1496 return NULL;
1497}
1498
1499struct THRstart {
1500 void (*func) (void *);
1501 void *arg;
1502 MT_Sema sem;
1503 Thread thr;
1504};
1505
1506static void
1507THRstarter(void *a)
1508{
1509 struct THRstart *t = a;
1510 void (*func) (void *) = t->func;
1511 void *arg = t->arg;
1512
1513 MT_sema_down(&t->sem);
1514 t->thr->sp = THRsp();
1515 MT_thread_setdata(t->thr);
1516 (*func)(arg);
1517 THRdel(t->thr);
1518 MT_sema_destroy(&t->sem);
1519 GDKfree(a);
1520}
1521
1522MT_Id
1523THRcreate(void (*f) (void *), void *arg, enum MT_thr_detach d, const char *name)
1524{
1525 MT_Id pid;
1526 Thread s;
1527 struct THRstart *t;
1528 static ATOMIC_TYPE ctr = ATOMIC_VAR_INIT(0);
1529 char semname[16];
1530 int len;
1531
1532 if ((t = GDKmalloc(sizeof(*t))) == NULL)
1533 return 0;
1534 if ((s = THRnew(name, ~(MT_Id)0)) == NULL) {
1535 GDKfree(t);
1536 return 0;
1537 }
1538 *t = (struct THRstart) {
1539 .func = f,
1540 .arg = arg,
1541 .thr = s,
1542 };
1543 len = snprintf(semname, sizeof(semname), "THRcreate%" PRIu64, (uint64_t) ATOMIC_INC(&ctr));
1544 if (len == -1 || len > (int) sizeof(semname)) {
1545 IODEBUG fprintf(stderr, "#THRcreate: semaphore name is too large\n");
1546 GDKerror("THRcreate: semaphore name is too large\n");
1547 GDKfree(t);
1548 GDKfree(s->name);
1549 s->name = NULL;
1550 ATOMIC_SET(&s->pid, 0); /* deallocate */
1551 return 0;
1552 }
1553 MT_sema_init(&t->sem, 0, semname);
1554 if (MT_create_thread(&pid, THRstarter, t, d, name) != 0) {
1555 GDKerror("THRcreate: could not start thread\n");
1556 MT_sema_destroy(&t->sem);
1557 GDKfree(t);
1558 GDKfree(s->name);
1559 s->name = NULL;
1560 ATOMIC_SET(&s->pid, 0); /* deallocate */
1561 return 0;
1562 }
1563 /* must not fail after this: the thread has been started */
1564 (void) ATOMIC_INC(&GDKnrofthreads);
1565 ATOMIC_SET(&s->pid, pid);
1566 /* send new thread on its way */
1567 MT_sema_up(&t->sem);
1568 return pid;
1569}
1570
1571void
1572THRdel(Thread t)
1573{
1574 assert(GDKthreads <= t && t < GDKthreads + THREADS);
1575 MT_thread_setdata(NULL);
1576 PARDEBUG fprintf(stderr, "#pid = %zu, disconnected, %d left\n",
1577 (size_t) ATOMIC_GET(&t->pid),
1578 (int) ATOMIC_GET(&GDKnrofthreads));
1579
1580 GDKfree(t->name);
1581 t->name = NULL;
1582 for (int i = 0; i < THREADDATA; i++)
1583 t->data[i] = NULL;
1584 t->sp = 0;
1585 ATOMIC_SET(&t->pid, 0); /* deallocate */
1586 (void) ATOMIC_DEC(&GDKnrofthreads);
1587}
1588
1589int
1590THRhighwater(void)
1591{
1592 uintptr_t c;
1593 Thread s;
1594 size_t diff;
1595 int rc = 0;
1596
1597 s = GDK_find_self();
1598 if (s != NULL) {
1599 c = THRsp();
1600 diff = c < s->sp ? s->sp - c : c - s->sp;
1601 if (diff > THREAD_STACK_SIZE - 80 * 1024)
1602 rc = 1;
1603 }
1604 return rc;
1605}
1606
1607/*
1608 * I/O is organized per thread, because users may gain access through
1609 * the network. The code below should be improved to gain speed.
1610 */
1611
1612static int
1613THRinit(void)
1614{
1615 int i = 0;
1616 Thread s;
1617 static bool first = true;
1618
1619 if ((THRdata[0] = (void *) file_wastream(stdout, "stdout")) == NULL)
1620 return -1;
1621 if ((THRdata[1] = (void *) file_rastream(stdin, "stdin")) == NULL) {
1622 mnstr_destroy(THRdata[0]);
1623 THRdata[0] = NULL;
1624 return -1;
1625 }
1626 if (first) {
1627 for (i = 0; i < THREADS; i++) {
1628 GDKthreads[i].tid = i + 1;
1629 ATOMIC_INIT(&GDKthreads[i].pid, 0);
1630 }
1631 first = false;
1632 }
1633 if ((s = THRnew("main thread", MT_getpid())) == NULL) {
1634 mnstr_destroy(THRdata[0]);
1635 THRdata[0] = NULL;
1636 mnstr_destroy(THRdata[1]);
1637 THRdata[1] = NULL;
1638 return -1;
1639 }
1640 (void) ATOMIC_INC(&GDKnrofthreads);
1641 MT_thread_setdata(s);
1642 return 0;
1643}
1644
1645void
1646THRsetdata(int n, ptr val)
1647{
1648 Thread s;
1649
1650 s = GDK_find_self();
1651 if (s) {
1652 assert(val == NULL || s->data[n] == NULL);
1653 s->data[n] = val;
1654 }
1655}
1656
1657void *
1658THRgetdata(int n)
1659{
1660 Thread s;
1661 void *d;
1662
1663 s = GDK_find_self();
1664 d = s ? s->data[n] : THRdata[n];
1665 return d;
1666}
1667
1668int
1669THRgettid(void)
1670{
1671 Thread s;
1672 int t;
1673
1674 s = GDK_find_self();
1675 t = s ? s->tid : 1;
1676 return t;
1677}
1678
1679static const char *_gdk_version_string = VERSION;
1680/**
1681 * Returns the GDK version as internally allocated string. Hence the
1682 * string does not have to (and should not) be freed. Do not inline
1683 * this function or the wrong VERSION will be used.
1684 */
1685const char *
1686GDKversion(void)
1687{
1688 return (_gdk_version_string);
1689}
1690
1691size_t
1692GDKmem_cursize(void)
1693{
1694 /* RAM/swapmem that Monet is really using now */
1695 return (size_t) ATOMIC_GET(&GDK_mallocedbytes_estimate);
1696}
1697
1698size_t
1699GDKvm_cursize(void)
1700{
1701 /* current Monet VM address space usage */
1702 return (size_t) ATOMIC_GET(&GDK_vm_cursize) + GDKmem_cursize();
1703}
1704
1705#define heapinc(_memdelta) \
1706 (void) ATOMIC_ADD(&GDK_mallocedbytes_estimate, _memdelta)
1707#define heapdec(_memdelta) \
1708 (void) ATOMIC_SUB(&GDK_mallocedbytes_estimate, _memdelta)
1709
1710#define meminc(vmdelta) \
1711 (void) ATOMIC_ADD(&GDK_vm_cursize, (ssize_t) SEG_SIZE((vmdelta), MT_VMUNITLOG))
1712#define memdec(vmdelta) \
1713 (void) ATOMIC_SUB(&GDK_vm_cursize, (ssize_t) SEG_SIZE((vmdelta), MT_VMUNITLOG))
1714
1715#ifndef STATIC_CODE_ANALYSIS
1716
1717static void
1718GDKmemfail(const char *s, size_t len)
1719{
1720 /* bumped your nose against the wall; try to prevent
1721 * repetition by adjusting maxsizes
1722 if (memtarget < 0.3 * GDKmem_cursize()) {
1723 size_t newmax = (size_t) (0.7 * (double) GDKmem_cursize());
1724
1725 if (newmax < GDK_mem_maxsize)
1726 GDK_mem_maxsize = newmax;
1727 }
1728 if (vmtarget < 0.3 * GDKvm_cursize()) {
1729 size_t newmax = (size_t) (0.7 * (double) GDKvm_cursize());
1730
1731 if (newmax < GDK_vm_maxsize)
1732 GDK_vm_maxsize = newmax;
1733 }
1734 */
1735
1736 fprintf(stderr, "#%s(%zu) fails, try to free up space [memory in use=%zu,virtual memory in use=%zu]\n", s, len, GDKmem_cursize(), GDKvm_cursize());
1737}
1738
1739/* Memory allocation
1740 *
1741 * The functions GDKmalloc, GDKzalloc, GDKrealloc, GDKstrdup, and
1742 * GDKfree are used throughout to allocate and free memory. These
1743 * functions are almost directly mapped onto the system
1744 * malloc/realloc/free functions, but they give us some extra
1745 * debugging hooks.
1746 *
1747 * When allocating memory, we allocate a bit more than was asked for.
1748 * The extra space is added onto the front of the memory area that is
1749 * returned, and in debug builds also some at the end. The area in
1750 * front is used to store the actual size of the allocated area. The
1751 * most important use is to be able to keep statistics on how much
1752 * memory is being used. In debug builds, the size is also used to
1753 * make sure that we don't write outside of the allocated arena. This
1754 * is also where the extra space at the end comes in.
1755 */
1756
1757/* we allocate extra space and return a pointer offset by this amount */
1758#define MALLOC_EXTRA_SPACE (2 * SIZEOF_VOID_P)
1759
1760#ifdef NDEBUG
1761#define DEBUG_SPACE 0
1762#else
1763#define DEBUG_SPACE 16
1764#endif
1765
1766static void *
1767GDKmalloc_internal(size_t size)
1768{
1769 void *s;
1770 size_t nsize;
1771
1772 assert(size != 0);
1773#ifndef NDEBUG
1774 /* fail malloc for testing purposes depending on set limit */
1775 if (GDK_malloc_success_count > 0) {
1776 MT_lock_set(&mallocsuccesslock);
1777 if (GDK_malloc_success_count > 0)
1778 GDK_malloc_success_count--;
1779 MT_lock_unset(&mallocsuccesslock);
1780 }
1781 if (GDK_malloc_success_count == 0) {
1782 return NULL;
1783 }
1784#endif
1785 if (GDKvm_cursize() + size >= GDK_vm_maxsize) {
1786 GDKerror("allocating too much memory\n");
1787 return NULL;
1788 }
1789
1790 /* pad to multiple of eight bytes and add some extra space to
1791 * write real size in front; when debugging, also allocate
1792 * extra space for check bytes */
1793 nsize = (size + 7) & ~7;
1794 if ((s = malloc(nsize + MALLOC_EXTRA_SPACE + DEBUG_SPACE)) == NULL) {
1795 GDKmemfail("GDKmalloc", size);
1796 GDKerror("GDKmalloc_internal: failed for %zu bytes", size);
1797 return NULL;
1798 }
1799 s = (void *) ((char *) s + MALLOC_EXTRA_SPACE);
1800
1801 heapinc(nsize + MALLOC_EXTRA_SPACE + DEBUG_SPACE);
1802
1803 /* just before the pointer that we return, write how much we
1804 * asked of malloc */
1805 ((size_t *) s)[-1] = nsize + MALLOC_EXTRA_SPACE + DEBUG_SPACE;
1806#ifndef NDEBUG
1807 /* just before that, write how much was asked of us */
1808 ((size_t *) s)[-2] = size;
1809 /* write pattern to help find out-of-bounds writes */
1810 memset((char *) s + size, '\xBD', nsize + DEBUG_SPACE - size);
1811#endif
1812 return s;
1813}
1814
1815#undef GDKmalloc
1816void *
1817GDKmalloc(size_t size)
1818{
1819 void *s;
1820
1821 if ((s = GDKmalloc_internal(size)) == NULL)
1822 return NULL;
1823#ifndef NDEBUG
1824 /* write a pattern to help make sure all data is properly
1825 * initialized by the caller */
1826 DEADBEEFCHK memset(s, '\xBD', size);
1827#endif
1828 return s;
1829}
1830
1831#undef GDKzalloc
1832void *
1833GDKzalloc(size_t size)
1834{
1835 void *s;
1836
1837 if ((s = GDKmalloc_internal(size)) == NULL)
1838 return NULL;
1839 memset(s, 0, size);
1840 return s;
1841}
1842
1843#undef GDKstrdup
1844char *
1845GDKstrdup(const char *s)
1846{
1847 size_t size;
1848 char *p;
1849
1850 if (s == NULL)
1851 return NULL;
1852 size = strlen(s) + 1;
1853
1854 if ((p = GDKmalloc_internal(size)) == NULL)
1855 return NULL;
1856 memcpy(p, s, size); /* including terminating NULL byte */
1857 return p;
1858}
1859
1860#undef GDKstrndup
1861char *
1862GDKstrndup(const char *s, size_t size)
1863{
1864 char *p;
1865
1866 if (s == NULL)
1867 return NULL;
1868 if ((p = GDKmalloc_internal(size + 1)) == NULL)
1869 return NULL;
1870 if (size > 0)
1871 memcpy(p, s, size);
1872 p[size] = '\0'; /* make sure it's NULL terminated */
1873 return p;
1874}
1875
1876#undef GDKfree
1877void
1878GDKfree(void *s)
1879{
1880 size_t asize;
1881
1882 if (s == NULL)
1883 return;
1884
1885 asize = ((size_t *) s)[-1]; /* how much allocated last */
1886
1887#ifndef NDEBUG
1888 assert((asize & 2) == 0); /* check against duplicate free */
1889 /* check for out-of-bounds writes */
1890 {
1891 size_t i = ((size_t *) s)[-2]; /* how much asked for last */
1892 for (; i < asize - MALLOC_EXTRA_SPACE; i++)
1893 assert(((char *) s)[i] == '\xBD');
1894 }
1895 ((size_t *) s)[-1] |= 2; /* indicate area is freed */
1896#endif
1897
1898#ifndef NDEBUG
1899 /* overwrite memory that is to be freed with a pattern that
1900 * will help us recognize access to already freed memory in
1901 * the debugger */
1902 DEADBEEFCHK memset(s, '\xDB', asize - MALLOC_EXTRA_SPACE);
1903#endif
1904
1905 free((char *) s - MALLOC_EXTRA_SPACE);
1906 heapdec((ssize_t) asize);
1907}
1908
1909#undef GDKrealloc
1910void *
1911GDKrealloc(void *s, size_t size)
1912{
1913 size_t nsize, asize;
1914#ifndef NDEBUG
1915 size_t osize;
1916 size_t *os;
1917#endif
1918
1919 assert(size != 0);
1920
1921 if (s == NULL)
1922 return GDKmalloc(size);
1923
1924 nsize = (size + 7) & ~7;
1925 asize = ((size_t *) s)[-1]; /* how much allocated last */
1926
1927 if (nsize > asize &&
1928 GDKvm_cursize() + nsize - asize >= GDK_vm_maxsize) {
1929 GDKerror("allocating too much memory\n");
1930 return NULL;
1931 }
1932#ifndef NDEBUG
1933 assert((asize & 2) == 0); /* check against duplicate free */
1934 /* check for out-of-bounds writes */
1935 osize = ((size_t *) s)[-2]; /* how much asked for last */
1936 {
1937 size_t i;
1938 for (i = osize; i < asize - MALLOC_EXTRA_SPACE; i++)
1939 assert(((char *) s)[i] == '\xBD');
1940 }
1941 /* if shrinking, write debug pattern into to-be-freed memory */
1942 DEADBEEFCHK if (size < osize)
1943 memset((char *) s + size, '\xDB', osize - size);
1944 os = s;
1945 os[-1] |= 2; /* indicate area is freed */
1946#endif
1947 s = realloc((char *) s - MALLOC_EXTRA_SPACE,
1948 nsize + MALLOC_EXTRA_SPACE + DEBUG_SPACE);
1949 if (s == NULL) {
1950#ifndef NDEBUG
1951 os[-1] &= ~2; /* not freed after all */
1952#endif
1953 GDKmemfail("GDKrealloc", size);
1954 GDKerror("GDKrealloc: failed for %zu bytes", size);
1955 return NULL;
1956 }
1957 s = (void *) ((char *) s + MALLOC_EXTRA_SPACE);
1958 /* just before the pointer that we return, write how much we
1959 * asked of malloc */
1960 ((size_t *) s)[-1] = nsize + MALLOC_EXTRA_SPACE + DEBUG_SPACE;
1961#ifndef NDEBUG
1962 /* just before that, write how much was asked of us */
1963 ((size_t *) s)[-2] = size;
1964 /* if growing, initialize new memory with debug pattern */
1965 DEADBEEFCHK if (size > osize)
1966 memset((char *) s + osize, '\xBD', size - osize);
1967 /* write pattern to help find out-of-bounds writes */
1968 memset((char *) s + size, '\xBD', nsize + DEBUG_SPACE - size);
1969#endif
1970
1971 heapinc(nsize + MALLOC_EXTRA_SPACE + DEBUG_SPACE);
1972 heapdec((ssize_t) asize);
1973
1974 return s;
1975}
1976
1977#else
1978
1979#define GDKmemfail(s, len) /* nothing */
1980
1981void *
1982GDKmalloc(size_t size)
1983{
1984 void *p = malloc(size);
1985 if (p == NULL)
1986 GDKerror("GDKmalloc: failed for %zu bytes", size);
1987 return p;
1988}
1989
1990void
1991GDKfree(void *ptr)
1992{
1993 if (ptr)
1994 free(ptr);
1995}
1996
1997void *
1998GDKzalloc(size_t size)
1999{
2000 void *ptr = calloc(size, 1);
2001 if (ptr == NULL)
2002 GDKerror("GDKzalloc: failed for %zu bytes", size);
2003 return ptr;
2004}
2005
2006void *
2007GDKrealloc(void *ptr, size_t size)
2008{
2009 void *p = realloc(ptr, size);
2010 if (p == NULL)
2011 GDKerror("GDKrealloc: failed for %zu bytes", size);
2012 return p;
2013}
2014
2015char *
2016GDKstrdup(const char *s)
2017{
2018 char *p = strdup(s);
2019 if (p == NULL)
2020 GDKerror("GDKstrdup failed for %s\n", s);
2021 return p;
2022}
2023
2024char *
2025GDKstrndup(const char *s, size_t size)
2026{
2027 char *p = malloc(size + 1);
2028 if (p == NULL) {
2029 GDKerror("GDKstrdup failed for %s\n", s);
2030 return NULL;
2031 }
2032 memcpy(p, s, size);
2033 p[size] = 0;
2034 return p;
2035}
2036
2037#endif /* STATIC_CODE_ANALYSIS */
2038
2039void
2040GDKsetmallocsuccesscount(lng count)
2041{
2042 (void) count;
2043#ifndef NDEBUG
2044 GDK_malloc_success_count = count;
2045#endif
2046}
2047
2048/*
2049 * @- virtual memory
2050 * allocations affect only the logical VM resources.
2051 */
2052#undef GDKmmap
2053void *
2054GDKmmap(const char *path, int mode, size_t len)
2055{
2056 void *ret;
2057
2058 if (GDKvm_cursize() + len >= GDK_vm_maxsize) {
2059 GDKmemfail("GDKmmap", len);
2060 GDKerror("allocating too much virtual address space\n");
2061 return NULL;
2062 }
2063 ret = MT_mmap(path, mode, len);
2064 if (ret == NULL) {
2065 GDKmemfail("GDKmmap", len);
2066 }
2067 if (ret != NULL) {
2068 meminc(len);
2069 }
2070 return ret;
2071}
2072
2073#undef GDKmunmap
2074gdk_return
2075GDKmunmap(void *addr, size_t size)
2076{
2077 int ret;
2078
2079 ret = MT_munmap(addr, size);
2080 if (ret == 0)
2081 memdec(size);
2082 return ret == 0 ? GDK_SUCCEED : GDK_FAIL;
2083}
2084
2085#undef GDKmremap
2086void *
2087GDKmremap(const char *path, int mode, void *old_address, size_t old_size, size_t *new_size)
2088{
2089 void *ret;
2090
2091 if (*new_size > old_size &&
2092 GDKvm_cursize() + *new_size - old_size >= GDK_vm_maxsize) {
2093 GDKmemfail("GDKmmap", *new_size);
2094 GDKerror("allocating too much virtual address space\n");
2095 return NULL;
2096 }
2097 ret = MT_mremap(path, mode, old_address, old_size, new_size);
2098 if (ret == NULL) {
2099 GDKmemfail("GDKmremap", *new_size);
2100 }
2101 if (ret != NULL) {
2102 memdec(old_size);
2103 meminc(*new_size);
2104 }
2105 return ret;
2106}
2107