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. J. Nes
15 : * @* BAT Buffer Pool (BBP)
16 : * The BATs created and loaded are collected in a BAT buffer pool.
17 : * The Bat Buffer Pool has a number of functions:
18 : * @table @code
19 : *
20 : * @item administration and lookup
21 : * The BBP is a directory which contains status information about all
22 : * known BATs. This interface may be used very heavily, by
23 : * data-intensive applications. To eliminate all overhead, read-only
24 : * access to the BBP may be done by table-lookups. The integer index
25 : * type for these lookups is @emph{bat}, as retrieved by
26 : * @emph{b->batCacheid}. The @emph{bat} zero is reserved for the nil
27 : * bat.
28 : *
29 : * @item persistence
30 : * The BBP is made persistent by saving it to the dictionary file
31 : * called @emph{BBP.dir} in the database.
32 : *
33 : * When the number of BATs rises, having all files in one directory
34 : * becomes a bottleneck. The BBP therefore implements a scheme that
35 : * distributes all BATs in a growing directory tree with at most 64
36 : * BATs stored in one node.
37 : *
38 : * @item buffer management
39 : * The BBP is responsible for loading and saving of BATs to disk. It
40 : * also contains routines to unload BATs from memory when memory
41 : * resources get scarce. For this purpose, it administers BAT memory
42 : * reference counts (to know which BATs can be unloaded) and BAT usage
43 : * statistics (it unloads the least recently used BATs).
44 : *
45 : * @item recovery
46 : * When the database is closed or during a run-time syncpoint, the
47 : * system tables must be written to disk in a safe way, that is immune
48 : * for system failures (like disk full). To do so, the BBP implements
49 : * an atomic commit and recovery protocol: first all files to be
50 : * overwritten are moved to a BACKUP/ dir. If that succeeds, the
51 : * writes are done. If that also fully succeeds the BACKUP/ dir is
52 : * renamed to DELETE_ME/ and subsequently deleted. If not, all files
53 : * in BACKUP/ are moved back to their original location.
54 : *
55 : * @item unloading
56 : * Bats which have a logical reference (ie. a lrefs > 0) but no memory
57 : * reference (refcnt == 0) can be unloaded. Unloading dirty bats
58 : * means, moving the original (committed version) to the BACKUP/ dir
59 : * and saving the bat. This complicates the commit and recovery/abort
60 : * issues. The commit has to check if the bat is already moved. And
61 : * The recovery has to always move back the files from the BACKUP/
62 : * dir.
63 : *
64 : * @item reference counting
65 : * Bats use have two kinds of references: logical and physical
66 : * (pointer) ones. The logical references are administered by
67 : * BBPretain/BBPrelease, the physical ones by BBPfix/BBPunfix.
68 : */
69 :
70 : #include "monetdb_config.h"
71 : #include "gdk.h"
72 : #include "gdk_private.h"
73 : #include "mutils.h"
74 : #ifdef HAVE_FCNTL_H
75 : #include <fcntl.h>
76 : #endif
77 :
78 : #ifndef F_OK
79 : #define F_OK 0
80 : #endif
81 : #ifndef S_ISDIR
82 : #define S_ISDIR(mode) (((mode) & _S_IFMT) == _S_IFDIR)
83 : #endif
84 : #ifndef O_CLOEXEC
85 : #ifdef _O_NOINHERIT
86 : #define O_CLOEXEC _O_NOINHERIT /* Windows */
87 : #else
88 : #define O_CLOEXEC 0
89 : #endif
90 : #endif
91 : #ifndef O_BINARY
92 : #define O_BINARY 0
93 : #endif
94 :
95 : /*
96 : * The BBP has a fixed address, so re-allocation due to a growing BBP
97 : * caused by one thread does not disturb reads to the old entries by
98 : * another. This is implemented using anonymous virtual memory;
99 : * extensions on the same address are guaranteed because a large
100 : * non-committed VM area is requested initially. New slots in the BBP
101 : * are found in O(1) by keeping a freelist that uses the 'next' field
102 : * in the BBPrec records.
103 : */
104 : static BBPrec BBP0[BBPINIT];
105 : BBPrec *BBP[N_BBPINIT] = {[0] = BBP0}; /* fixed base VM address of BBP array */
106 : bat BBPlimit = BBPINIT; /* current committed VM BBP array */
107 : static ATOMIC_TYPE BBPsize = ATOMIC_VAR_INIT(0); /* current used size of BBP array */
108 :
109 : struct BBPfarm_t BBPfarms[MAXFARMS];
110 :
111 : #define KITTENNAP 1 /* used to suspend processing */
112 : #define BBPNONAME "." /* filler for no name in BBP.dir */
113 : /*
114 : * The hash index uses a bucket index (int array) of size mask that is
115 : * tuned for perfect hashing (1 lookup). The bucket chain uses the
116 : * 'next' field in the BBPrec records.
117 : */
118 : static MT_Lock BBPnameLock = MT_LOCK_INITIALIZER(BBPnameLock);
119 : #define BBP_mask 1023 /* number of buckets = & mask */
120 : static bat BBP_hash[BBP_mask+1]; /* BBP logical name hash buckets */
121 : static MT_Lock GDKcacheLock = MT_LOCK_INITIALIZER(GDKcacheLock);
122 : static bat BBP_free;
123 : static uint32_t BBP_nfree;
124 : #define BBP_FREE_LOWATER 10
125 : #define BBP_FREE_HIWATER 50
126 :
127 : static gdk_return BBPfree(BAT *b);
128 : static void BBPdestroy(BAT *b);
129 : static void BBPuncacheit(bat bid, bool unloaddesc);
130 : static gdk_return BBPprepare(bool subcommit);
131 : static BAT *getBBPdescriptor(bat i);
132 : static gdk_return BBPbackup(BAT *b, bool subcommit);
133 : static gdk_return BBPdir_init(void);
134 : static void BBPcallbacks(void);
135 :
136 : /* two lngs of extra info in BBP.dir */
137 : /* these two are atomic because of their use in log_new() */
138 : static ATOMIC_TYPE BBPlogno = ATOMIC_VAR_INIT(0);
139 :
140 : #define BBPtmpcheck(s) (strncmp(s, "tmp_", 4) == 0)
141 :
142 : #define BBPnamecheck(s) (BBPtmpcheck(s) ? strtol((s) + 4, NULL, 8) : 0)
143 :
144 : #define BATno_shared_heap(b) \
145 : (!VIEWtparent(b) && (ATOMIC_GET(&(b)->theap->refs) & HEAPREFS) == 1)
146 :
147 : #define BATshared(b) \
148 : ((!VIEWtparent(b) && (ATOMIC_GET(&(b)->theap->refs) & HEAPREFS) > 1) || \
149 : ((b)->tvheap && !VIEWvtparent(b) && (ATOMIC_GET(&(b)->tvheap->refs) & HEAPREFS) > 1))
150 :
151 : static void
152 24159 : BBP_insert(bat i)
153 : {
154 24159 : bat idx = (bat) (strHash(BBP_logical(i)) & BBP_mask);
155 :
156 24159 : BBP_next(i) = BBP_hash[idx];
157 24159 : BBP_hash[idx] = i;
158 24159 : }
159 :
160 : static void
161 12611 : BBP_delete(bat i)
162 : {
163 12611 : const char *s = BBP_logical(i);
164 12611 : bat idx = (bat) (strHash(s) & BBP_mask);
165 :
166 12611 : for (bat *h = &BBP_hash[idx]; (i = *h) != 0; h = &BBP_next(i)) {
167 12611 : if (strcmp(BBP_logical(i), s) == 0) {
168 12611 : *h = BBP_next(i);
169 12611 : break;
170 : }
171 : }
172 12611 : }
173 :
174 : bat
175 456601048 : getBBPsize(void)
176 : {
177 456601048 : return (bat) ATOMIC_GET(&BBPsize);
178 : }
179 :
180 : lng
181 386 : getBBPlogno(void)
182 : {
183 386 : return (lng) ATOMIC_GET(&BBPlogno);
184 : }
185 :
186 :
187 : /*
188 : * @+ BBP Consistency and Concurrency
189 : * While GDK provides the basic building blocks for an ACID system, in
190 : * itself it is not such a system, as we this would entail too much
191 : * overhead that is often not needed. Hence, some consistency control
192 : * is left to the user. The first important user constraint is that if
193 : * a user updates a BAT, (s)he himself must assure that no-one else
194 : * accesses this BAT.
195 : *
196 : * Concerning buffer management, the BBP carries out a swapping
197 : * policy. BATs are kept in memory till the memory is full. If the
198 : * memory is full, the malloc functions initiate BBP trim actions,
199 : * that unload the coldest BATs that have a zero reference count. The
200 : * second important user constraint is therefore that a user may only
201 : * manipulate live BAT data in memory if it is sure that there is at
202 : * least one reference count to that BAT.
203 : *
204 : * The main BBP array is protected by two locks:
205 : * @table @code
206 : * @item GDKcacheLock]
207 : * this lock guards the free slot management in the BBP array. The
208 : * BBP operations that allocate a new slot for a new BAT
209 : * (@emph{BBPinit},@emph{BBPcacheit}), delete the slot of a destroyed
210 : * BAT (@emph{BBPreclaim}), or rename a BAT (@emph{BBPrename}), hold
211 : * this lock. It also protects all BAT (re)naming actions include
212 : * (read and write) in the hash table with BAT names.
213 : * @item GDKswapLock
214 : * this lock guards the swap (loaded/unloaded) status of the
215 : * BATs. Hence, all BBP routines that influence the swapping policy,
216 : * or actually carry out the swapping policy itself, acquire this lock
217 : * (e.g. @emph{BBPfix},@emph{BBPunfix}). Note that this also means
218 : * that updates to the BBP_status indicator array must be protected by
219 : * GDKswapLock.
220 : *
221 : * To reduce contention GDKswapLock was split into multiple locks; it
222 : * is now an array of lock pointers which is accessed by
223 : * GDKswapLock(bat)
224 : * @end table
225 : *
226 : * Routines that need both locks should first acquire the locks in the
227 : * GDKswapLock array (in ascending order) and then GDKcacheLock (and
228 : * release them in reverse order).
229 : *
230 : * To obtain maximum speed, read operations to existing elements in
231 : * the BBP are unguarded. As said, it is the users responsibility that
232 : * the BAT that is being read is not being modified. BBP update
233 : * actions that modify the BBP data structure itself are locked by the
234 : * BBP functions themselves. Hence, multiple concurrent BBP read
235 : * operations may be ongoing while at the same time at most one BBP
236 : * write operation @strong{on a different BAT} is executing. This
237 : * holds for accesses to the public (quasi-) arrays @emph{BBPcache},
238 : * @emph{BBPstatus} and @emph{BBPrefs}.
239 : * These arrays are called quasi as now they are
240 : * actually stored together in one big BBPrec array called BBP, that
241 : * is allocated in anonymous VM space, so we can reallocate this
242 : * structure without changing the base address (a crucial feature if
243 : * read actions are to go on unlocked while other entries in the BBP
244 : * may be modified).
245 : */
246 : static volatile MT_Id locked_by = 0;
247 :
248 : /* use a lock instead of atomic instructions so that we wait for
249 : * BBPlock/BBPunlock */
250 : #define BBP_unload_inc() \
251 : do { \
252 : MT_lock_set(&GDKunloadLock); \
253 : BBPunloadCnt++; \
254 : MT_lock_unset(&GDKunloadLock); \
255 : } while (0)
256 :
257 : #define BBP_unload_dec() \
258 : do { \
259 : MT_lock_set(&GDKunloadLock); \
260 : --BBPunloadCnt; \
261 : assert(BBPunloadCnt >= 0); \
262 : MT_lock_unset(&GDKunloadLock); \
263 : } while (0)
264 :
265 : static int BBPunloadCnt = 0;
266 : static MT_Lock GDKunloadLock = MT_LOCK_INITIALIZER(GDKunloadLock);
267 :
268 : void
269 8 : BBPlock(void)
270 : {
271 8 : int i;
272 :
273 : /* wait for all pending unloads to finish */
274 8 : MT_lock_set(&GDKunloadLock);
275 8 : while (BBPunloadCnt > 0) {
276 0 : MT_lock_unset(&GDKunloadLock);
277 0 : MT_sleep_ms(1);
278 8 : MT_lock_set(&GDKunloadLock);
279 : }
280 :
281 8 : BBPtmlock();
282 8 : MT_lock_set(&GDKcacheLock);
283 65552 : for (i = 0; i <= BBP_BATMASK; i++)
284 65536 : MT_lock_set(&GDKswapLock(i));
285 8 : locked_by = MT_getpid();
286 :
287 8 : MT_lock_unset(&GDKunloadLock);
288 8 : }
289 :
290 : void
291 8 : BBPunlock(void)
292 : {
293 8 : int i;
294 :
295 65544 : for (i = BBP_BATMASK; i >= 0; i--)
296 65536 : MT_lock_unset(&GDKswapLock(i));
297 8 : MT_lock_unset(&GDKcacheLock);
298 8 : locked_by = 0;
299 8 : BBPtmunlock();
300 8 : }
301 :
302 : int
303 15355235 : BBPselectfarm(role_t role, int type, enum heaptype hptype)
304 : {
305 15355235 : int i;
306 :
307 15355235 : (void) type; /* may use in future */
308 15355235 : (void) hptype; /* may use in future */
309 :
310 15355235 : if (GDKinmemory(0))
311 : return 0;
312 :
313 30376191 : for (i = 0; i < MAXFARMS; i++)
314 30376191 : if (BBPfarms[i].roles & (1U << (int) role))
315 15346932 : return i;
316 : /* must be able to find farms for TRANSIENT and PERSISTENT */
317 0 : assert(role != TRANSIENT && role != PERSISTENT);
318 : return -1;
319 : }
320 :
321 : static gdk_return
322 355 : BBPextend(bat newsize)
323 : {
324 355 : if (newsize > N_BBPINIT * BBPINIT) {
325 0 : GDKerror("trying to extend BAT pool beyond the "
326 : "limit (%d)\n", N_BBPINIT * BBPINIT);
327 0 : return GDK_FAIL;
328 : }
329 :
330 : /* make sure the new size is at least BBPsize large */
331 358 : while (BBPlimit < newsize) {
332 3 : BUN limit = BBPlimit >> BBPINITLOG;
333 3 : assert(BBP[limit] == NULL);
334 3 : BBP[limit] = GDKzalloc(BBPINIT * sizeof(BBPrec));
335 3 : if (BBP[limit] == NULL) {
336 0 : GDKerror("failed to extend BAT pool\n");
337 0 : return GDK_FAIL;
338 : }
339 49155 : for (BUN i = 0; i < BBPINIT; i++) {
340 49152 : ATOMIC_INIT(&BBP[limit][i].status, 0);
341 49152 : BBP[limit][i].pid = ~(MT_Id)0;
342 : }
343 3 : BBPlimit += BBPINIT;
344 : }
345 :
346 : return GDK_SUCCEED;
347 : }
348 :
349 : static gdk_return
350 125 : recover_dir(int farmid, bool direxists)
351 : {
352 125 : if (direxists) {
353 : /* just try; don't care about these non-vital files */
354 0 : if (GDKunlink(farmid, BATDIR, "BBP", "bak") != GDK_SUCCEED)
355 0 : GDKwarning("unlink of BBP.bak failed\n");
356 0 : if (GDKmove(farmid, BATDIR, "BBP", "dir", BATDIR, "BBP", "bak", false) != GDK_SUCCEED)
357 0 : GDKwarning("rename of BBP.dir to BBP.bak failed\n");
358 : }
359 125 : return GDKmove(farmid, BAKDIR, "BBP", "dir", BATDIR, "BBP", "dir", true);
360 : }
361 :
362 : static inline str
363 1013944 : BBPsubdir_recursive(char *s, bat i)
364 : {
365 1013944 : i >>= 6;
366 1013944 : if (i >= 0100) {
367 248338 : s = BBPsubdir_recursive(s, i);
368 248329 : *s++ = DIR_SEP;
369 : }
370 1013935 : i &= 077;
371 1013935 : *s++ = '0' + (i >> 3);
372 1013935 : *s++ = '0' + (i & 7);
373 1013935 : return s;
374 : }
375 :
376 : static inline void
377 0 : BBPgetsubdir(char *s, bat i)
378 : {
379 0 : if (i >= 0100) {
380 0 : s = BBPsubdir_recursive(s, i);
381 : }
382 0 : *s = 0;
383 0 : }
384 :
385 : static inline void
386 788030 : BBPgetfilename(char *s, size_t len, bat i)
387 : {
388 788030 : if (i >= 0100) {
389 765995 : char *p = BBPsubdir_recursive(s, i);
390 766005 : *p++ = DIR_SEP;
391 766005 : len -= (p - s);
392 766005 : s = p;
393 : }
394 788040 : if (snprintf(s, len, "%o", i) >= (int) len)
395 0 : TRC_CRITICAL(BAT_, "impossible error\n");
396 788040 : }
397 :
398 : static gdk_return BBPrecover(int farmid);
399 : static gdk_return BBPrecover_subdir(void);
400 : static bool BBPdiskscan(const char *, size_t);
401 :
402 : static int
403 9316 : vheapinit(BAT *b, const char *buf, unsigned bbpversion, const char *filename, int lineno)
404 : {
405 9316 : int n = 0;
406 9316 : uint64_t free, size;
407 9316 : uint16_t storage;
408 :
409 9316 : (void) bbpversion; /* could be used to implement compatibility */
410 :
411 9316 : size = 0; /* for GDKLIBRARY_HSIZE case */
412 9316 : storage = STORE_INVALID; /* for GDKLIBRARY_HSIZE case */
413 18632 : if (bbpversion <= GDKLIBRARY_HSIZE ?
414 0 : sscanf(buf,
415 : " %" SCNu64 " %" SCNu64 " %" SCNu16
416 : "%n",
417 : &free, &size, &storage, &n) < 3 :
418 9316 : sscanf(buf,
419 : " %" SCNu64
420 : "%n",
421 : &free, &n) < 1) {
422 0 : TRC_CRITICAL(GDK, "invalid format for BBP.dir on line %d", lineno);
423 0 : return -1;
424 : }
425 9316 : if (b->batCount == 0)
426 2616 : free = 0;
427 9316 : if (b->ttype >= 0 &&
428 9102 : ATOMstorage(b->ttype) == TYPE_str &&
429 9048 : free < GDK_STRHASHTABLE * sizeof(stridx_t) + BATTINY * GDK_VARALIGN)
430 7787 : size = GDK_STRHASHTABLE * sizeof(stridx_t) + BATTINY * GDK_VARALIGN;
431 1529 : else if (free < 512)
432 132 : size = 512;
433 : else
434 1397 : size = free;
435 18632 : *b->tvheap = (Heap) {
436 9316 : .free = (size_t) free,
437 9316 : .size = (size_t) size,
438 : .base = NULL,
439 : .storage = STORE_INVALID,
440 : .cleanhash = true,
441 : .newstorage = STORE_INVALID,
442 : .dirty = false,
443 9316 : .parentid = b->batCacheid,
444 9316 : .farmid = BBPselectfarm(PERSISTENT, b->ttype, varheap),
445 9316 : .hasfile = free > 0,
446 : };
447 9316 : strconcat_len(b->tvheap->filename, sizeof(b->tvheap->filename),
448 : filename, ".theap", NULL);
449 9316 : return n;
450 : }
451 :
452 : static int
453 34969 : heapinit(BAT *b, const char *buf,
454 : #ifdef GDKLIBRARY_HASHASH
455 : int *hashash,
456 : #endif
457 : unsigned bbpversion, const char *filename, int lineno)
458 : {
459 34969 : int t;
460 34969 : char type[33];
461 34969 : uint16_t width;
462 34969 : uint16_t var;
463 34969 : uint16_t properties;
464 34969 : uint64_t nokey0;
465 34969 : uint64_t nokey1;
466 34969 : uint64_t nosorted;
467 34969 : uint64_t norevsorted;
468 34969 : uint64_t base;
469 34969 : uint64_t free;
470 34969 : uint64_t size;
471 34969 : uint16_t storage;
472 34969 : uint64_t minpos, maxpos;
473 34969 : int n;
474 :
475 34969 : (void) bbpversion; /* could be used to implement compatibility */
476 :
477 34969 : size = 0; /* for GDKLIBRARY_HSIZE case */
478 34969 : storage = STORE_INVALID; /* for GDKLIBRARY_HSIZE case */
479 69938 : if (bbpversion <= GDKLIBRARY_HSIZE ?
480 0 : sscanf(buf,
481 : " %10s %" SCNu16 " %" SCNu16 " %" SCNu16 " %" SCNu64
482 : " %" SCNu64 " %" SCNu64 " %" SCNu64 " %" SCNu64
483 : " %" SCNu64 " %" SCNu64 " %" SCNu16 " %" SCNu64 " %" SCNu64
484 : "%n",
485 : type, &width, &var, &properties, &nokey0,
486 : &nokey1, &nosorted, &norevsorted, &base,
487 : &free, &size, &storage, &minpos, &maxpos,
488 : &n) < 14 :
489 34969 : sscanf(buf,
490 : " %10s %" SCNu16 " %" SCNu16 " %" SCNu16 " %" SCNu64
491 : " %" SCNu64 " %" SCNu64 " %" SCNu64 " %" SCNu64
492 : " %" SCNu64 " %" SCNu64 " %" SCNu64
493 : "%n",
494 : type, &width, &var, &properties, &nokey0,
495 : &nokey1, &nosorted, &norevsorted, &base,
496 : &free, &minpos, &maxpos,
497 : &n) < 12) {
498 0 : TRC_CRITICAL(GDK, "invalid format for BBP.dir on line %d", lineno);
499 0 : return -1;
500 : }
501 :
502 34969 : if (strcmp(type, "wkba") == 0) {
503 0 : TRC_CRITICAL(GDK, "type wkba (SQL name: GeometryA) has been removed\n");
504 0 : return -1;
505 : }
506 :
507 34969 : if (properties & ~0x1F81) {
508 0 : TRC_CRITICAL(GDK, "unknown properties are set: incompatible database on line %d of BBP.dir\n", lineno);
509 0 : return -1;
510 : }
511 : #ifdef GDKLIBRARY_HASHASH
512 34969 : *hashash = var & 2;
513 : #endif
514 34969 : var &= ~2;
515 34969 : if ((t = ATOMindex(type)) < 0) {
516 241 : if ((t = ATOMunknown_find(type)) == 0) {
517 0 : TRC_CRITICAL(GDK, "no space for atom %s", type);
518 0 : return -1;
519 : }
520 43830 : } else if (var != (t == TYPE_void || BATatoms[t].atomPut != NULL)) {
521 0 : TRC_CRITICAL(GDK, "inconsistent entry in BBP.dir: tvarsized mismatch for BAT %d on line %d\n", (int) b->batCacheid, lineno);
522 0 : return -1;
523 34728 : } else if (var && t != 0 ?
524 9102 : ATOMsize(t) < width ||
525 9102 : (width != 1 && width != 2 && width != 4
526 : #if SIZEOF_VAR_T == 8
527 54 : && width != 8
528 : #endif
529 : ) :
530 25626 : ATOMsize(t) != width) {
531 0 : TRC_CRITICAL(GDK, "inconsistent entry in BBP.dir: tsize mismatch for BAT %d on line %d\n", (int) b->batCacheid, lineno);
532 0 : return -1;
533 : }
534 34969 : b->ttype = t;
535 34969 : b->twidth = width;
536 34969 : b->tshift = ATOMelmshift(width);
537 34969 : assert_shift_width(b->tshift,b->twidth);
538 34969 : b->tnokey[0] = (BUN) nokey0;
539 34969 : b->tnokey[1] = (BUN) nokey1;
540 34969 : b->tsorted = (bit) ((properties & 0x0001) != 0);
541 34969 : b->trevsorted = (bit) ((properties & 0x0080) != 0);
542 34969 : b->tkey = (properties & 0x0100) != 0;
543 34969 : b->tnonil = (properties & 0x0400) != 0;
544 34969 : b->tnil = (properties & 0x0800) != 0;
545 34969 : b->tascii = (properties & 0x1000) != 0;
546 34969 : b->tnosorted = (BUN) nosorted;
547 34969 : b->tnorevsorted = (BUN) norevsorted;
548 34969 : b->tunique_est = 0.0;
549 : /* (properties & 0x0200) is the old tdense flag */
550 34969 : b->tseqbase = (properties & 0x0200) == 0 || base >= (uint64_t) oid_nil ? oid_nil : (oid) base;
551 34969 : b->theap->free = (size_t) free;
552 34969 : b->theap->hasfile = free > 0;
553 : /* set heap size to match capacity */
554 34969 : if (b->ttype == TYPE_msk) {
555 : /* round up capacity to multiple of 32 */
556 5373 : b->batCapacity = (b->batCapacity + 31) & ~((BUN) 31);
557 5373 : b->theap->size = b->batCapacity / 8;
558 : } else {
559 29596 : b->theap->size = (size_t) b->batCapacity << b->tshift;
560 : }
561 34969 : b->theap->base = NULL;
562 34969 : settailname(b->theap, filename, t, width);
563 34969 : b->theap->storage = STORE_INVALID;
564 34969 : b->theap->newstorage = STORE_INVALID;
565 34969 : b->theap->farmid = BBPselectfarm(PERSISTENT, b->ttype, offheap);
566 34969 : b->theap->dirty = false;
567 34969 : b->theap->parentid = b->batCacheid;
568 34969 : if (minpos < b->batCount)
569 13089 : b->tminpos = (BUN) minpos;
570 : else
571 21880 : b->tminpos = BUN_NONE;
572 34969 : if (maxpos < b->batCount)
573 13153 : b->tmaxpos = (BUN) maxpos;
574 : else
575 21816 : b->tmaxpos = BUN_NONE;
576 34969 : if (t && var) {
577 9316 : t = vheapinit(b, buf + n, bbpversion, filename, lineno);
578 9316 : if (t < 0)
579 : return t;
580 9316 : n += t;
581 : } else {
582 25653 : b->tvheap = NULL;
583 : }
584 34969 : return n;
585 : }
586 :
587 : /* read a single line from the BBP.dir file (file pointer fp) and fill
588 : * in the structure pointed to by bn and extra information through the
589 : * other pointers; this function does not allocate any memory; return 0
590 : * on end of file, 1 on success, and -1 on failure */
591 : /* set to true during initialization, else always false; if false, do
592 : * not return any options (set pointer to NULL as if there aren't any);
593 : * if true and there are options, return them in freshly allocated
594 : * memory through *options */
595 : static bool return_options = false;
596 : int
597 35325 : BBPreadBBPline(FILE *fp, unsigned bbpversion, int *lineno, BAT *bn,
598 : #ifdef GDKLIBRARY_HASHASH
599 : int *hashash,
600 : #endif
601 : char *batname, char *filename, char **options)
602 : {
603 35325 : char buf[4096];
604 35325 : uint64_t batid;
605 35325 : unsigned int properties;
606 35325 : int nread, n;
607 35325 : char *s;
608 35325 : uint64_t count, base = 0;
609 :
610 35325 : if (fgets(buf, sizeof(buf), fp) == NULL) {
611 356 : if (ferror(fp)) {
612 0 : TRC_CRITICAL(GDK, "error reading BBP.dir on line %d\n", *lineno);
613 0 : return -1;
614 : }
615 : return 0; /* end of file */
616 : }
617 34969 : (*lineno)++;
618 34969 : if ((s = strpbrk(buf, "\r\n")) != NULL) {
619 34969 : if (s[0] == '\r' && s[1] != '\n') {
620 0 : TRC_CRITICAL(GDK, "invalid format for BBP.dir on line %d", *lineno);
621 0 : return -1;
622 : }
623 : /* zap the newline */
624 34969 : *s = '\0';
625 : } else {
626 0 : TRC_CRITICAL(GDK, "invalid format for BBP.dir on line %d: line too long\n", *lineno);
627 0 : return -1;
628 : }
629 :
630 69938 : if (bbpversion <= GDKLIBRARY_HSIZE ?
631 0 : sscanf(buf,
632 : "%" SCNu64 " %*u %128s %*s %u %" SCNu64 " %*u %" SCNu64
633 : "%n",
634 : &batid, batname,
635 : &properties, &count, &base,
636 : &nread) < 5 :
637 : bbpversion <= GDKLIBRARY_STATUS ?
638 2534 : sscanf(buf,
639 : "%" SCNu64 " %*u %128s %*s %u %" SCNu64 " %" SCNu64
640 : "%n",
641 : &batid, batname,
642 : &properties, &count, &base,
643 : &nread) < 5 :
644 32435 : sscanf(buf,
645 : "%" SCNu64 " %128s %u %" SCNu64 " %" SCNu64
646 : "%n",
647 : &batid, batname,
648 : &properties, &count, &base,
649 : &nread) < 5) {
650 0 : TRC_CRITICAL(GDK, "invalid format for BBP.dir on line %d", *lineno);
651 0 : return -1;
652 : }
653 :
654 34969 : if (batid >= N_BBPINIT * BBPINIT) {
655 0 : TRC_CRITICAL(GDK, "bat ID (%" PRIu64 ") too large to accommodate (max %d), on line %d.", batid, N_BBPINIT * BBPINIT - 1, *lineno);
656 0 : return -1;
657 : }
658 :
659 34969 : BBPgetfilename(filename, sizeof(BBP_physical(0)), (bat) batid);
660 :
661 34969 : bn->batCacheid = (bat) batid;
662 34969 : bn->batTransient = false;
663 34969 : bn->batCopiedtodisk = true;
664 34969 : switch ((properties & 0x06) >> 1) {
665 1028 : case 0:
666 1028 : bn->batRestricted = BAT_WRITE;
667 1028 : break;
668 33941 : case 1:
669 33941 : bn->batRestricted = BAT_READ;
670 33941 : break;
671 0 : case 2:
672 0 : bn->batRestricted = BAT_APPEND;
673 0 : break;
674 0 : default:
675 0 : TRC_CRITICAL(GDK, "incorrect batRestricted value");
676 0 : return -1;
677 : }
678 34969 : bn->batCount = (BUN) count;
679 34969 : bn->batInserted = bn->batCount;
680 : /* set capacity to at least count */
681 34969 : bn->batCapacity = (BUN) count <= BATTINY ? BATTINY : (BUN) count;
682 :
683 34969 : if (base > (uint64_t) GDK_oid_max) {
684 0 : TRC_CRITICAL(GDK, "head seqbase out of range (ID = %" PRIu64 ", seq = %" PRIu64 ") on line %d.", batid, base, *lineno);
685 0 : return -1;
686 : }
687 34969 : bn->hseqbase = (oid) base;
688 34969 : n = heapinit(bn, buf + nread,
689 : #ifdef GDKLIBRARY_HASHASH
690 : hashash,
691 : #endif
692 : bbpversion, filename, *lineno);
693 34969 : if (n < 0) {
694 : return -1;
695 : }
696 34969 : nread += n;
697 :
698 34969 : if (nread >= (int) sizeof(buf) || (buf[nread] != '\0' && buf[nread] != ' ')) {
699 0 : TRC_CRITICAL(GDK, "invalid format for BBP.dir on line %d", *lineno);
700 0 : return -1;
701 : }
702 34969 : if (options) {
703 34969 : if (return_options && buf[nread] == ' ') {
704 0 : if ((*options = GDKstrdup(buf + nread + 1)) == NULL) {
705 0 : TRC_CRITICAL(GDK, "GDKstrdup failed\n");
706 0 : return -1;
707 : }
708 : } else {
709 34969 : *options = NULL;
710 : }
711 : }
712 : return 1;
713 : }
714 :
715 : static gdk_return
716 351 : BBPreadEntries(FILE *fp, unsigned bbpversion, int lineno
717 : #ifdef GDKLIBRARY_HASHASH
718 : , bat **hashbats, bat *nhashbats
719 : #endif
720 : )
721 : {
722 : #ifdef GDKLIBRARY_HASHASH
723 351 : bat *hbats = NULL;
724 351 : bat nhbats = 0;
725 : #endif
726 :
727 : /* read the BBP.dir and insert the BATs into the BBP */
728 351 : return_options = true;
729 351 : MT_lock_set(&BBPnameLock);
730 33704 : for (;;) {
731 34055 : BAT b;
732 34055 : Heap h;
733 34055 : Heap vh;
734 34055 : vh = h = (Heap) {
735 : .free = 0,
736 : };
737 34055 : b = (BAT) {
738 : .theap = &h,
739 : .tvheap = &vh,
740 : };
741 34055 : char *options;
742 34055 : char headname[129];
743 34055 : char filename[sizeof(BBP_physical(0))];
744 34055 : char logical[1024];
745 : #ifdef GDKLIBRARY_HASHASH
746 34055 : int Thashash;
747 : #endif
748 :
749 34055 : switch (BBPreadBBPline(fp, bbpversion, &lineno, &b,
750 : #ifdef GDKLIBRARY_HASHASH
751 : &Thashash,
752 : #endif
753 : headname, filename, &options)) {
754 351 : case 0:
755 : /* end of file */
756 : #ifdef GDKLIBRARY_HASHASH
757 351 : *hashbats = hbats;
758 351 : *nhashbats = nhbats;
759 : #endif
760 351 : return_options = false;
761 351 : MT_lock_unset(&BBPnameLock);
762 351 : return GDK_SUCCEED;
763 : case 1:
764 : /* successfully read an entry */
765 33704 : break;
766 0 : default:
767 : /* error */
768 0 : goto bailout;
769 : }
770 :
771 33704 : if (b.batCacheid >= N_BBPINIT * BBPINIT) {
772 0 : GDKfree(options);
773 0 : TRC_CRITICAL(GDK, "bat ID (%d) too large to accommodate (max %d), on line %d.", b.batCacheid, N_BBPINIT * BBPINIT - 1, lineno);
774 0 : goto bailout;
775 : }
776 :
777 33704 : if (b.batCacheid >= (bat) ATOMIC_GET(&BBPsize)) {
778 0 : if ((bat) ATOMIC_GET(&BBPsize) + 1 >= BBPlimit &&
779 0 : BBPextend(b.batCacheid + 1) != GDK_SUCCEED) {
780 0 : GDKfree(options);
781 0 : goto bailout;
782 : }
783 0 : ATOMIC_SET(&BBPsize, b.batCacheid + 1);
784 : }
785 33704 : BAT *bn = BBP_desc(b.batCacheid);
786 33704 : if (bn->batCacheid != 0) {
787 0 : GDKfree(options);
788 0 : TRC_CRITICAL(GDK, "duplicate entry in BBP.dir (ID = "
789 : "%d) on line %d.", b.batCacheid, lineno);
790 0 : goto bailout;
791 : }
792 :
793 : #ifdef GDKLIBRARY_HASHASH
794 33704 : if (Thashash) {
795 0 : assert(bbpversion <= GDKLIBRARY_HASHASH);
796 0 : bat *sb = GDKrealloc(hbats, ++nhbats * sizeof(bat));
797 0 : if (sb == NULL) {
798 0 : GDKfree(options);
799 0 : goto bailout;
800 : }
801 0 : hbats = sb;
802 0 : hbats[nhbats - 1] = b.batCacheid;
803 : }
804 : #endif
805 :
806 33704 : Heap *hn;
807 33704 : if ((hn = GDKmalloc(sizeof(Heap))) == NULL) {
808 0 : GDKfree(options);
809 0 : TRC_CRITICAL(GDK, "cannot allocate memory for BAT.");
810 0 : goto bailout;
811 : }
812 33704 : *bn = b;
813 33704 : *hn = h;
814 33704 : bn->theap = hn;
815 33704 : if (b.tvheap) {
816 8976 : Heap *vhn;
817 8976 : assert(b.tvheap == &vh);
818 8976 : if ((vhn = GDKmalloc(sizeof(Heap))) == NULL) {
819 0 : GDKfree(hn);
820 0 : GDKfree(options);
821 0 : TRC_CRITICAL(GDK, "cannot allocate memory for BAT.");
822 0 : goto bailout;
823 : }
824 8976 : *vhn = vh;
825 8976 : bn->tvheap = vhn;
826 8976 : ATOMIC_INIT(&bn->tvheap->refs, 1);
827 : }
828 :
829 33704 : char name[MT_NAME_LEN];
830 33704 : snprintf(name, sizeof(name), "heaplock%d", bn->batCacheid); /* fits */
831 33704 : MT_lock_init(&bn->theaplock, name);
832 33704 : snprintf(name, sizeof(name), "BATlock%d", bn->batCacheid); /* fits */
833 33704 : MT_lock_init(&bn->batIdxLock, name);
834 33704 : snprintf(name, sizeof(name), "hashlock%d", bn->batCacheid); /* fits */
835 33704 : MT_rwlock_init(&bn->thashlock, name);
836 33704 : ATOMIC_INIT(&bn->theap->refs, 1);
837 :
838 33704 : if (snprintf(BBP_bak(b.batCacheid), sizeof(BBP_bak(b.batCacheid)), "tmp_%o", (unsigned) b.batCacheid) >= (int) sizeof(BBP_bak(b.batCacheid))) {
839 0 : BATdestroy(bn);
840 0 : GDKfree(options);
841 0 : TRC_CRITICAL(GDK, "BBP logical filename directory is too large, on line %d\n", lineno);
842 0 : goto bailout;
843 : }
844 33704 : char *s;
845 33704 : if ((s = strchr(headname, '~')) != NULL && s == headname) {
846 : /* sizeof(logical) > sizeof(BBP_bak(b.batCacheid)), so
847 : * this fits */
848 0 : strcpy(logical, BBP_bak(b.batCacheid));
849 : } else {
850 0 : if (s)
851 0 : *s = 0;
852 33704 : strcpy_len(logical, headname, sizeof(logical));
853 : }
854 33704 : if (strcmp(logical, BBP_bak(b.batCacheid)) == 0) {
855 32890 : BBP_logical(b.batCacheid) = BBP_bak(b.batCacheid);
856 : } else {
857 814 : BBP_logical(b.batCacheid) = GDKstrdup(logical);
858 814 : if (BBP_logical(b.batCacheid) == NULL) {
859 0 : BATdestroy(bn);
860 0 : GDKfree(options);
861 0 : TRC_CRITICAL(GDK, "GDKstrdup failed\n");
862 0 : goto bailout;
863 : }
864 : }
865 33704 : strcpy_len(BBP_physical(b.batCacheid), filename, sizeof(BBP_physical(b.batCacheid)));
866 : #ifdef __COVERITY__
867 : /* help coverity */
868 : BBP_physical(b.batCacheid)[sizeof(BBP_physical(b.batCacheid)) - 1] = 0;
869 : #endif
870 33704 : BBP_options(b.batCacheid) = options;
871 33704 : BBP_refs(b.batCacheid) = 0;
872 33704 : BBP_lrefs(b.batCacheid) = 1; /* any BAT we encounter here is persistent, so has a logical reference */
873 33704 : BBP_pid(b.batCacheid) = 0;
874 33704 : BBP_status_set(b.batCacheid, BBPEXISTING);
875 33704 : if (BBPnamecheck(BBP_logical(b.batCacheid)) == 0)
876 814 : BBP_insert(b.batCacheid);
877 : }
878 :
879 0 : bailout:
880 0 : MT_lock_unset(&BBPnameLock);
881 0 : return_options = false;
882 : #ifdef GDKLIBRARY_HASHASH
883 0 : GDKfree(hbats);
884 : #endif
885 0 : return GDK_FAIL;
886 : }
887 :
888 : /* check that the necessary files for all BATs exist and are large
889 : * enough */
890 : static gdk_return
891 352 : BBPcheckbats(unsigned bbpversion)
892 : {
893 352 : (void) bbpversion;
894 83179 : for (bat bid = 1, size = (bat) ATOMIC_GET(&BBPsize); bid < size; bid++) {
895 82827 : struct stat statb;
896 82827 : BAT *b;
897 82827 : char path[MAXPATH];
898 :
899 82827 : b = BBP_desc(bid);
900 82827 : if (b->batCacheid == 0 || b->ttype == TYPE_void) {
901 : /* no files needed */
902 49123 : continue;
903 : }
904 33704 : if (b->theap->free > 0) {
905 22361 : if (GDKfilepath(path, sizeof(path), 0, BATDIR, b->theap->filename, NULL) != GDK_SUCCEED)
906 0 : return GDK_FAIL;
907 : /* first check string offset heap with width,
908 : * then without */
909 22361 : if (MT_stat(path, &statb) < 0) {
910 0 : GDKsyserror("cannot stat file %s (expected size %zu)\n",
911 : path, b->theap->free);
912 0 : return GDK_FAIL;
913 : }
914 22361 : if ((size_t) statb.st_size < b->theap->free) {
915 0 : GDKerror("file %s too small (expected %zu, actual %zu)\n", path, b->theap->free, (size_t) statb.st_size);
916 0 : return GDK_FAIL;
917 : }
918 22361 : size_t hfree = b->theap->free;
919 22361 : hfree = (hfree + GDK_mmap_pagesize - 1) & ~(GDK_mmap_pagesize - 1);
920 22361 : if (hfree == 0)
921 0 : hfree = GDK_mmap_pagesize;
922 22361 : if (statb.st_size > (off_t) hfree) {
923 9 : int fd;
924 9 : if ((fd = MT_open(path, O_RDWR | O_CLOEXEC | O_BINARY)) >= 0) {
925 9 : if (ftruncate(fd, hfree) == -1)
926 0 : perror("ftruncate");
927 9 : (void) close(fd);
928 : }
929 : }
930 : }
931 33704 : if (b->tvheap != NULL && b->tvheap->free > 0) {
932 6465 : if (GDKfilepath(path, sizeof(path), 0, BATDIR, BBP_physical(b->batCacheid), "theap") != GDK_SUCCEED)
933 : return GDK_FAIL;
934 6465 : if (MT_stat(path, &statb) < 0) {
935 0 : GDKsyserror("cannot stat file %s\n",
936 : path);
937 0 : return GDK_FAIL;
938 : }
939 6465 : if ((size_t) statb.st_size < b->tvheap->free) {
940 0 : GDKerror("file %s too small (expected %zu, actual %zu)\n", path, b->tvheap->free, (size_t) statb.st_size);
941 0 : return GDK_FAIL;
942 : }
943 6465 : size_t hfree = b->tvheap->free;
944 6465 : hfree = (hfree + GDK_mmap_pagesize - 1) & ~(GDK_mmap_pagesize - 1);
945 6465 : if (hfree == 0)
946 0 : hfree = GDK_mmap_pagesize;
947 6465 : if (statb.st_size > (off_t) hfree) {
948 8 : int fd;
949 8 : if ((fd = MT_open(path, O_RDWR | O_CLOEXEC | O_BINARY)) >= 0) {
950 8 : if (ftruncate(fd, hfree) == -1)
951 0 : perror("ftruncate");
952 8 : (void) close(fd);
953 : }
954 : }
955 : }
956 : }
957 : return GDK_SUCCEED;
958 : }
959 :
960 : #ifdef HAVE_HGE
961 : #define SIZEOF_MAX_INT SIZEOF_HGE
962 : #else
963 : #define SIZEOF_MAX_INT SIZEOF_LNG
964 : #endif
965 :
966 : unsigned
967 356 : BBPheader(FILE *fp, int *lineno, bat *bbpsize, lng *logno, bool allow_hge_upgrade)
968 : {
969 356 : char buf[BUFSIZ];
970 356 : int sz, ptrsize, oidsize, intsize;
971 356 : unsigned bbpversion;
972 :
973 356 : if (fgets(buf, sizeof(buf), fp) == NULL) {
974 0 : TRC_CRITICAL(GDK, "BBP.dir is empty");
975 0 : return 0;
976 : }
977 356 : ++*lineno;
978 356 : if (sscanf(buf, "BBP.dir, GDKversion %u\n", &bbpversion) != 1) {
979 0 : GDKerror("old BBP without version number; "
980 : "dump the database using a compatible version, "
981 : "then restore into new database using this version.\n");
982 0 : return 0;
983 : }
984 356 : if (bbpversion != GDKLIBRARY &&
985 : bbpversion != GDKLIBRARY_STATUS &&
986 : bbpversion != GDKLIBRARY_JSON &&
987 356 : bbpversion != GDKLIBRARY_HSIZE &&
988 : bbpversion != GDKLIBRARY_HASHASH) {
989 0 : TRC_CRITICAL(GDK, "incompatible BBP version: expected 0%o, got 0%o. "
990 : "This database was probably created by a %s version of MonetDB.",
991 : GDKLIBRARY, bbpversion,
992 : bbpversion > GDKLIBRARY ? "newer" : "too old");
993 0 : return 0;
994 : }
995 356 : if (fgets(buf, sizeof(buf), fp) == NULL) {
996 0 : TRC_CRITICAL(GDK, "short BBP");
997 0 : return 0;
998 : }
999 356 : ++*lineno;
1000 356 : if (sscanf(buf, "%d %d %d", &ptrsize, &oidsize, &intsize) != 3) {
1001 0 : TRC_CRITICAL(GDK, "BBP.dir has incompatible format: pointer, OID, and max. integer sizes are missing on line %d", *lineno);
1002 0 : return 0;
1003 : }
1004 356 : if (ptrsize != SIZEOF_SIZE_T || oidsize != SIZEOF_OID) {
1005 0 : TRC_CRITICAL(GDK, "database created with incompatible server: "
1006 : "expected pointer size %d, got %d, expected OID size %d, got %d.",
1007 : SIZEOF_SIZE_T, ptrsize, SIZEOF_OID, oidsize);
1008 0 : return 0;
1009 : }
1010 356 : if (intsize > SIZEOF_MAX_INT) {
1011 0 : TRC_CRITICAL(GDK, "database created with incompatible server: "
1012 : "expected max. integer size %d, got %d.",
1013 : SIZEOF_MAX_INT, intsize);
1014 0 : return 0;
1015 : }
1016 356 : if (intsize < SIZEOF_MAX_INT && !allow_hge_upgrade) {
1017 0 : TRC_CRITICAL(GDK, "database created with incompatible server: "
1018 : "expected max. integer size %d, got %d; "
1019 : "use --set allow_hge_upgrade=yes to upgrade.",
1020 : SIZEOF_MAX_INT, intsize);
1021 0 : return 0;
1022 : }
1023 356 : if (fgets(buf, sizeof(buf), fp) == NULL) {
1024 0 : TRC_CRITICAL(GDK, "short BBP");
1025 0 : return 0;
1026 : }
1027 356 : ++*lineno;
1028 356 : if (sscanf(buf, "BBPsize=%d", &sz) != 1) {
1029 0 : TRC_CRITICAL(GDK, "no BBPsize value found\n");
1030 0 : return 0;
1031 : }
1032 356 : if (sz > *bbpsize)
1033 130 : *bbpsize = sz;
1034 356 : if (fgets(buf, sizeof(buf), fp) == NULL) {
1035 0 : TRC_CRITICAL(GDK, "short BBP");
1036 0 : return 0;
1037 : }
1038 712 : if (bbpversion <= GDKLIBRARY_STATUS ?
1039 8 : sscanf(buf, "BBPinfo=" LLSCN " %*d", logno) != 1 :
1040 348 : sscanf(buf, "BBPinfo=" LLSCN, logno) != 1) {
1041 0 : TRC_CRITICAL(GDK, "no info value found\n");
1042 0 : return 0;
1043 : }
1044 356 : return bbpversion;
1045 : }
1046 :
1047 : bool
1048 92954935 : GDKinmemory(int farmid)
1049 : {
1050 92954935 : if (farmid == NOFARM)
1051 : farmid = 0;
1052 91744635 : assert(farmid >= 0 && farmid < MAXFARMS);
1053 92954935 : return BBPfarms[farmid].dirname == NULL;
1054 : }
1055 :
1056 : /* all errors are fatal */
1057 : gdk_return
1058 1050 : BBPaddfarm(const char *dirname, uint32_t rolemask, bool logerror)
1059 : {
1060 1050 : struct stat st;
1061 1050 : int i;
1062 :
1063 1050 : if (dirname == NULL) {
1064 1 : assert(BBPfarms[0].dirname == NULL);
1065 1 : assert(rolemask & 1);
1066 1 : assert(BBPfarms[0].roles == 0);
1067 1 : BBPfarms[0].roles = rolemask;
1068 1 : return GDK_SUCCEED;
1069 : }
1070 1049 : if (strchr(dirname, '\n') != NULL) {
1071 0 : if (logerror)
1072 0 : GDKerror("no newline allowed in directory name\n");
1073 0 : return GDK_FAIL;
1074 : }
1075 1049 : if (rolemask == 0 || (rolemask & 1 && BBPfarms[0].roles != 0)) {
1076 0 : if (logerror)
1077 0 : GDKerror("bad rolemask\n");
1078 0 : return GDK_FAIL;
1079 : }
1080 1049 : if (strcmp(dirname, "in-memory") == 0 ||
1081 1048 : /* backward compatibility: */ strcmp(dirname, ":memory:") == 0) {
1082 : dirname = NULL;
1083 1048 : } else if (MT_mkdir(dirname) < 0) {
1084 960 : if (errno == EEXIST) {
1085 960 : if (MT_stat(dirname, &st) == -1 || !S_ISDIR(st.st_mode)) {
1086 0 : if (logerror)
1087 0 : GDKerror("%s: not a directory\n", dirname);
1088 0 : return GDK_FAIL;
1089 : }
1090 : } else {
1091 0 : if (logerror)
1092 0 : GDKsyserror("%s: cannot create directory\n", dirname);
1093 0 : return GDK_FAIL;
1094 : }
1095 : }
1096 2087 : for (i = 0; i < MAXFARMS; i++) {
1097 2087 : if (BBPfarms[i].roles == 0) {
1098 1049 : if (dirname) {
1099 1048 : BBPfarms[i].dirname = GDKstrdup(dirname);
1100 1048 : if (BBPfarms[i].dirname == NULL)
1101 : return GDK_FAIL;
1102 : }
1103 1049 : BBPfarms[i].roles = rolemask;
1104 1049 : if ((rolemask & 1) == 0 && dirname != NULL) {
1105 : char bbpdir[MAXPATH];
1106 : int j;
1107 :
1108 1067 : for (j = 0; j < i; j++)
1109 881 : if (BBPfarms[j].dirname != NULL &&
1110 881 : strcmp(BBPfarms[i].dirname,
1111 : BBPfarms[j].dirname) == 0)
1112 509 : return GDK_SUCCEED;
1113 : /* if an extra farm, make sure we
1114 : * don't find a BBP.dir there that
1115 : * might belong to an existing
1116 : * database */
1117 186 : if (GDKfilepath(bbpdir, sizeof(bbpdir), i, BATDIR, "BBP", "dir") != GDK_SUCCEED) {
1118 : return GDK_FAIL;
1119 : }
1120 186 : if (MT_stat(bbpdir, &st) != -1 || errno != ENOENT) {
1121 0 : if (logerror)
1122 0 : GDKerror("%s is a database\n", dirname);
1123 0 : return GDK_FAIL;
1124 : }
1125 186 : if (GDKfilepath(bbpdir, sizeof(bbpdir), i, BAKDIR, "BBP", "dir") != GDK_SUCCEED) {
1126 : return GDK_FAIL;
1127 : }
1128 186 : if (MT_stat(bbpdir, &st) != -1 || errno != ENOENT) {
1129 0 : if (logerror)
1130 0 : GDKerror("%s is a database\n", dirname);
1131 0 : return GDK_FAIL;
1132 : }
1133 : }
1134 540 : return GDK_SUCCEED;
1135 : }
1136 : }
1137 0 : if (logerror)
1138 0 : GDKerror("too many farms\n");
1139 : return GDK_FAIL;
1140 : }
1141 :
1142 : gdk_return
1143 354 : BBPchkfarms(void)
1144 : {
1145 354 : const char *dir = NULL;
1146 354 : uint32_t rolemask = 0;
1147 354 : if ((BBPfarms[0].roles & 1) == 0) {
1148 0 : GDKerror("Must call BBPaddfarms at least once for persistent data\n");
1149 0 : return GDK_FAIL;
1150 : }
1151 11682 : for (int i = 0; i < MAXFARMS; i++) {
1152 11328 : if (BBPfarms[i].roles != 0) {
1153 696 : dir = BBPfarms[i].dirname;
1154 696 : rolemask |= BBPfarms[i].roles;
1155 : }
1156 : }
1157 354 : if (dir == NULL)
1158 1 : dir = "in-memory";
1159 354 : if ((rolemask & (1U << TRANSIENT)) == 0) {
1160 0 : gdk_return rc = BBPaddfarm(dir, 1U << TRANSIENT, true);
1161 0 : if (rc != GDK_SUCCEED)
1162 : return rc;
1163 : }
1164 354 : if ((rolemask & (1U << SYSTRANS)) == 0) {
1165 354 : gdk_return rc = BBPaddfarm(dir, 1U << SYSTRANS, true);
1166 354 : if (rc != GDK_SUCCEED)
1167 : return rc;
1168 : }
1169 : return GDK_SUCCEED;
1170 : }
1171 :
1172 : #ifdef GDKLIBRARY_HASHASH
1173 : static gdk_return
1174 0 : fixhashashbat(BAT *b)
1175 : {
1176 0 : const char *nme = BBP_physical(b->batCacheid);
1177 0 : char srcdir[MAXPATH];
1178 :
1179 0 : if (GDKfilepath(srcdir, sizeof(srcdir), NOFARM, BATDIR, nme, NULL) != GDK_SUCCEED) {
1180 0 : TRC_CRITICAL(GDK, "GDKfilepath failed\n");
1181 0 : return GDK_FAIL;
1182 : }
1183 0 : char *s;
1184 0 : if ((s = strrchr(srcdir, DIR_SEP)) != NULL)
1185 0 : *s = 0;
1186 0 : const char *bnme;
1187 0 : if ((bnme = strrchr(nme, DIR_SEP)) != NULL)
1188 0 : bnme++;
1189 : else
1190 : bnme = nme;
1191 0 : long_str filename;
1192 0 : snprintf(filename, sizeof(filename), "BACKUP%c%s", DIR_SEP, bnme);
1193 :
1194 : /* we don't maintain index structures */
1195 0 : HASHdestroy(b);
1196 0 : OIDXdestroy(b);
1197 0 : PROPdestroy(b);
1198 0 : STRMPdestroy(b);
1199 0 : RTREEdestroy(b);
1200 :
1201 : /* make backup of heaps */
1202 0 : const char *t;
1203 0 : if (GDKmove(b->theap->farmid, srcdir, bnme, "tail1",
1204 : BAKDIR, bnme, "tail1", false) == GDK_SUCCEED)
1205 : t = "tail1";
1206 0 : else if (GDKmove(b->theap->farmid, srcdir, bnme, "tail2",
1207 : BAKDIR, bnme, "tail2", false) == GDK_SUCCEED)
1208 : t = "tail2";
1209 : #if SIZEOF_VAR_T == 8
1210 0 : else if (GDKmove(b->theap->farmid, srcdir, bnme, "tail4",
1211 : BAKDIR, bnme, "tail4", false) == GDK_SUCCEED)
1212 : t = "tail4";
1213 : #endif
1214 0 : else if (GDKmove(b->theap->farmid, srcdir, bnme, "tail",
1215 : BAKDIR, bnme, "tail", true) == GDK_SUCCEED)
1216 : t = "tail";
1217 : else {
1218 0 : TRC_CRITICAL(GDK, "cannot make backup of %s.tail\n", nme);
1219 0 : return GDK_FAIL;
1220 : }
1221 0 : GDKclrerr();
1222 0 : if (GDKmove(b->theap->farmid, srcdir, bnme, "theap",
1223 : BAKDIR, bnme, "theap", true) != GDK_SUCCEED) {
1224 0 : TRC_CRITICAL(GDK, "cannot make backup of %s.theap\n", nme);
1225 0 : return GDK_FAIL;
1226 : }
1227 : /* load old heaps */
1228 0 : Heap h1 = *b->theap; /* old heap */
1229 0 : h1.base = NULL;
1230 0 : h1.dirty = false;
1231 0 : strconcat_len(h1.filename, sizeof(h1.filename), filename, ".", t, NULL);
1232 0 : if (HEAPload(&h1, filename, t, false) != GDK_SUCCEED) {
1233 0 : TRC_CRITICAL(GDK, "loading old tail heap "
1234 : "for BAT %d failed\n", b->batCacheid);
1235 0 : return GDK_FAIL;
1236 : }
1237 0 : Heap vh1 = *b->tvheap; /* old heap */
1238 0 : vh1.base = NULL;
1239 0 : vh1.dirty = false;
1240 0 : strconcat_len(vh1.filename, sizeof(vh1.filename), filename, ".theap", NULL);
1241 0 : if (HEAPload(&vh1, filename, "theap", false) != GDK_SUCCEED) {
1242 0 : HEAPfree(&h1, false);
1243 0 : TRC_CRITICAL(GDK, "loading old string heap "
1244 : "for BAT %d failed\n", b->batCacheid);
1245 0 : return GDK_FAIL;
1246 : }
1247 :
1248 : /* create new heaps */
1249 0 : Heap *h2 = GDKmalloc(sizeof(Heap));
1250 0 : Heap *vh2 = GDKmalloc(sizeof(Heap));
1251 0 : if (h2 == NULL || vh2 == NULL) {
1252 0 : GDKfree(h2);
1253 0 : GDKfree(vh2);
1254 0 : HEAPfree(&h1, false);
1255 0 : HEAPfree(&vh1, false);
1256 0 : TRC_CRITICAL(GDK, "allocating new heaps "
1257 : "for BAT %d failed\n", b->batCacheid);
1258 0 : return GDK_FAIL;
1259 : }
1260 0 : *h2 = *b->theap;
1261 0 : h2->base = NULL;
1262 0 : if (HEAPalloc(h2, b->batCapacity, b->twidth) != GDK_SUCCEED) {
1263 0 : GDKfree(h2);
1264 0 : GDKfree(vh2);
1265 0 : HEAPfree(&h1, false);
1266 0 : HEAPfree(&vh1, false);
1267 0 : TRC_CRITICAL(GDK, "allocating new tail heap "
1268 : "for BAT %d failed\n", b->batCacheid);
1269 0 : return GDK_FAIL;
1270 : }
1271 0 : h2->dirty = true;
1272 0 : h2->free = h1.free;
1273 :
1274 0 : *vh2 = *b->tvheap;
1275 0 : strconcat_len(vh2->filename, sizeof(vh2->filename), nme, ".theap", NULL);
1276 0 : strHeap(vh2, b->batCapacity);
1277 0 : if (vh2->base == NULL) {
1278 0 : HEAPfree(&h1, false);
1279 0 : HEAPfree(&vh1, false);
1280 0 : HEAPfree(h2, false);
1281 0 : GDKfree(h2);
1282 0 : GDKfree(vh2);
1283 0 : TRC_CRITICAL(GDK, "allocating new string heap "
1284 : "for BAT %d failed\n", b->batCacheid);
1285 0 : return GDK_FAIL;
1286 : }
1287 0 : vh2->dirty = true;
1288 0 : ATOMIC_INIT(&h2->refs, 1);
1289 0 : ATOMIC_INIT(&vh2->refs, 1);
1290 0 : Heap *ovh = b->tvheap;
1291 0 : b->tvheap = vh2;
1292 0 : vh2 = NULL; /* no longer needed */
1293 0 : for (BUN i = 0; i < b->batCount; i++) {
1294 0 : var_t o;
1295 0 : switch (b->twidth) {
1296 0 : case 1:
1297 0 : o = (var_t) ((uint8_t *) h1.base)[i] + GDK_VAROFFSET;
1298 0 : break;
1299 0 : case 2:
1300 0 : o = (var_t) ((uint16_t *) h1.base)[i] + GDK_VAROFFSET;
1301 0 : break;
1302 : #if SIZEOF_VAR_T == 8
1303 0 : case 4:
1304 0 : o = (var_t) ((uint32_t *) h1.base)[i];
1305 0 : break;
1306 : #endif
1307 0 : default:
1308 0 : o = ((var_t *) h1.base)[i];
1309 0 : break;
1310 : }
1311 0 : const char *s = vh1.base + o;
1312 0 : var_t no = strPut(b, &o, s);
1313 0 : if (no == 0) {
1314 0 : HEAPfree(&h1, false);
1315 0 : HEAPfree(&vh1, false);
1316 0 : HEAPdecref(h2, false);
1317 0 : HEAPdecref(b->tvheap, false);
1318 0 : b->tvheap = ovh;
1319 0 : TRC_CRITICAL(GDK, "storing string value "
1320 : "for BAT %d failed\n", b->batCacheid);
1321 0 : return GDK_FAIL;
1322 : }
1323 0 : assert(no >= GDK_VAROFFSET);
1324 0 : switch (b->twidth) {
1325 0 : case 1:
1326 0 : no -= GDK_VAROFFSET;
1327 0 : assert(no <= 0xFF);
1328 0 : ((uint8_t *) h2->base)[i] = (uint8_t) no;
1329 0 : break;
1330 0 : case 2:
1331 0 : no -= GDK_VAROFFSET;
1332 0 : assert(no <= 0xFFFF);
1333 0 : ((uint16_t *) h2->base)[i] = (uint16_t) no;
1334 0 : break;
1335 : #if SIZEOF_VAR_T == 8
1336 0 : case 4:
1337 0 : assert(no <= 0xFFFFFFFF);
1338 0 : ((uint32_t *) h2->base)[i] = (uint32_t) no;
1339 0 : break;
1340 : #endif
1341 0 : default:
1342 0 : ((var_t *) h2->base)[i] = no;
1343 0 : break;
1344 : }
1345 : }
1346 :
1347 : /* cleanup */
1348 0 : HEAPfree(&h1, false);
1349 0 : HEAPfree(&vh1, false);
1350 0 : if (HEAPsave(h2, nme, BATtailname(b), true, h2->free, NULL) != GDK_SUCCEED) {
1351 0 : HEAPdecref(h2, false);
1352 0 : HEAPdecref(b->tvheap, false);
1353 0 : b->tvheap = ovh;
1354 0 : TRC_CRITICAL(GDK, "saving heap failed\n");
1355 0 : return GDK_FAIL;
1356 : }
1357 0 : if (HEAPsave(b->tvheap, nme, "theap", true, b->tvheap->free, &b->theaplock) != GDK_SUCCEED) {
1358 0 : HEAPfree(b->tvheap, false);
1359 0 : b->tvheap = ovh;
1360 0 : TRC_CRITICAL(GDK, "saving string heap failed\n");
1361 0 : return GDK_FAIL;
1362 : }
1363 0 : HEAPdecref(b->theap, false);
1364 0 : b->theap = h2;
1365 0 : HEAPfree(h2, false);
1366 0 : HEAPdecref(ovh, false);
1367 0 : HEAPfree(b->tvheap, false);
1368 0 : return GDK_SUCCEED;
1369 : }
1370 :
1371 : static gdk_return
1372 0 : fixhashash(bat *hashbats, bat nhashbats)
1373 : {
1374 0 : for (bat i = 0; i < nhashbats; i++) {
1375 0 : bat bid = hashbats[i];
1376 0 : BAT *b = BBP_desc(bid);
1377 0 : if (b->batCacheid == 0) {
1378 : /* not a valid BAT (shouldn't happen) */
1379 0 : continue;
1380 : }
1381 0 : if (fixhashashbat(b) != GDK_SUCCEED)
1382 : return GDK_FAIL;
1383 : }
1384 : return GDK_SUCCEED;
1385 : }
1386 : #endif
1387 :
1388 : #ifdef GDKLIBRARY_JSON
1389 : static gdk_return
1390 0 : jsonupgradebat(BAT *b, json_storage_conversion fixJSONStorage)
1391 : {
1392 0 : const char *nme = BBP_physical(b->batCacheid);
1393 0 : char srcdir[MAXPATH];
1394 :
1395 0 : if (GDKfilepath(srcdir, sizeof(srcdir), NOFARM, BATDIR, nme, NULL) != GDK_SUCCEED) {
1396 0 : TRC_CRITICAL(GDK, "GDKfilepath failed\n");
1397 0 : return GDK_FAIL;
1398 : }
1399 :
1400 0 : char *s;
1401 0 : if ((s = strrchr(srcdir, DIR_SEP)) != NULL)
1402 0 : *s = 0;
1403 0 : const char *bnme;
1404 0 : if ((bnme = strrchr(nme, DIR_SEP)) != NULL) {
1405 0 : bnme++;
1406 : } else {
1407 : bnme = nme;
1408 : }
1409 :
1410 0 : long_str filename;
1411 0 : snprintf(filename, sizeof(filename), "BACKUP%c%s", DIR_SEP, bnme);
1412 :
1413 : /* A json column should not normally have any index structures */
1414 0 : HASHdestroy(b);
1415 0 : OIDXdestroy(b);
1416 0 : PROPdestroy(b);
1417 0 : STRMPdestroy(b);
1418 0 : RTREEdestroy(b);
1419 :
1420 : /* backup the current heaps */
1421 0 : if (GDKmove(b->theap->farmid, srcdir, bnme, "tail",
1422 : BAKDIR, bnme, "tail", false) != GDK_SUCCEED) {
1423 0 : TRC_CRITICAL(GDK, "cannot make backup of %s.tail\n", nme);
1424 0 : return GDK_FAIL;
1425 : }
1426 0 : if (GDKmove(b->theap->farmid, srcdir, bnme, "theap",
1427 : BAKDIR, bnme, "theap", true) != GDK_SUCCEED) {
1428 0 : TRC_CRITICAL(GDK, "cannot make backup of %s.theap\n", nme);
1429 0 : return GDK_FAIL;
1430 : }
1431 :
1432 : /* load the old heaps */
1433 0 : Heap h1 = *b->theap;
1434 0 : h1.base = NULL;
1435 0 : h1.dirty = false;
1436 0 : strconcat_len(h1.filename, sizeof(h1.filename), filename, ".tail", NULL);
1437 0 : if (HEAPload(&h1, filename, "tail", false) != GDK_SUCCEED) {
1438 0 : TRC_CRITICAL(GDK, "loading old tail heap "
1439 : "for BAT %d failed\n", b->batCacheid);
1440 0 : return GDK_FAIL;
1441 : }
1442 :
1443 0 : Heap vh1 = *b->tvheap;
1444 0 : vh1.base = NULL;
1445 0 : vh1.dirty = false;
1446 0 : strconcat_len(vh1.filename, sizeof(vh1.filename), filename, ".theap", NULL);
1447 0 : if (HEAPload(&vh1, filename, "theap", false) != GDK_SUCCEED) {
1448 0 : HEAPfree(&h1, false);
1449 0 : TRC_CRITICAL(GDK, "loading old string heap "
1450 : "for BAT %d failed\n", b->batCacheid);
1451 0 : return GDK_FAIL;
1452 : }
1453 :
1454 : /* create the new heaps */
1455 0 : Heap *h2 = GDKmalloc(sizeof(Heap));
1456 0 : Heap *vh2 = GDKmalloc(sizeof(Heap));
1457 0 : if (h2 == NULL || vh2 == NULL) {
1458 0 : GDKfree(h2);
1459 0 : GDKfree(vh2);
1460 0 : HEAPfree(&h1, false);
1461 0 : HEAPfree(&vh1, false);
1462 0 : TRC_CRITICAL(GDK, "allocating new heaps "
1463 : "for BAT %d failed\n", b->batCacheid);
1464 0 : return GDK_FAIL;
1465 : }
1466 0 : *h2 = *b->theap;
1467 0 : h2->base = NULL;
1468 0 : if (HEAPalloc(h2, b->batCapacity, b->twidth) != GDK_SUCCEED) {
1469 0 : GDKfree(h2);
1470 0 : GDKfree(vh2);
1471 0 : HEAPfree(&h1, false);
1472 0 : HEAPfree(&vh1, false);
1473 0 : TRC_CRITICAL(GDK, "allocating new tail heap "
1474 : "for BAT %d failed\n", b->batCacheid);
1475 0 : return GDK_FAIL;
1476 :
1477 : }
1478 0 : h2->dirty = true;
1479 0 : h2->free = h1.free;
1480 :
1481 0 : *vh2 = *b->tvheap;
1482 0 : strconcat_len(vh2->filename, sizeof(vh2->filename), nme, ".theap", NULL);
1483 0 : strHeap(vh2, b->batCapacity);
1484 0 : if (vh2->base == NULL) {
1485 0 : HEAPfree(&h1, false);
1486 0 : HEAPfree(&vh1, false);
1487 0 : HEAPfree(h2, false);
1488 0 : GDKfree(h2);
1489 0 : GDKfree(vh2);
1490 0 : TRC_CRITICAL(GDK, "allocating new string heap "
1491 : "for BAT %d failed\n", b->batCacheid);
1492 0 : return GDK_FAIL;
1493 : }
1494 0 : vh2->dirty = true;
1495 0 : ATOMIC_INIT(&h2->refs, 1);
1496 0 : ATOMIC_INIT(&vh2->refs, 1);
1497 0 : Heap *ovh = b->tvheap;
1498 0 : b->tvheap = vh2;
1499 0 : vh2 = NULL;
1500 :
1501 0 : for (BUN i = 0; i < b->batCount; i++) {
1502 0 : var_t o = ((var_t *) h1.base)[i];
1503 0 : const char *s = vh1.base + o;
1504 0 : char *ns;
1505 0 : if (fixJSONStorage(&ns, &s) != GDK_SUCCEED) {
1506 0 : HEAPfree(&h1, false);
1507 0 : HEAPfree(&vh1, false);
1508 0 : HEAPdecref(h2, false);
1509 0 : HEAPdecref(b->tvheap, false);
1510 0 : b->tvheap = ovh;
1511 0 : TRC_CRITICAL(GDK, "converting value "
1512 : "in BAT %d failed\n", b->batCacheid);
1513 0 : return GDK_FAIL;
1514 : }
1515 0 : var_t no = strPut(b, &o, ns);
1516 0 : GDKfree(ns);
1517 0 : if (no == 0) {
1518 0 : HEAPfree(&h1, false);
1519 0 : HEAPfree(&vh1, false);
1520 0 : HEAPdecref(h2, false);
1521 0 : HEAPdecref(b->tvheap, false);
1522 0 : b->tvheap = ovh;
1523 0 : TRC_CRITICAL(GDK, "storing new value "
1524 : "in BAT %d failed\n", b->batCacheid);
1525 0 : return GDK_FAIL;
1526 :
1527 : }
1528 0 : ((var_t *)h2->base)[i] = no;
1529 : }
1530 :
1531 : /* cleanup */
1532 0 : HEAPfree(&h1, false);
1533 0 : HEAPfree(&vh1, false);
1534 0 : if (HEAPsave(h2, nme, BATtailname(b), true, h2->free, NULL) !=
1535 : GDK_SUCCEED) {
1536 0 : HEAPdecref(h2, false);
1537 0 : HEAPdecref(b->tvheap, false);
1538 0 : b->tvheap = ovh;
1539 0 : TRC_CRITICAL(GDK, "saving heap failed\n");
1540 0 : return GDK_FAIL;
1541 : }
1542 :
1543 0 : if (HEAPsave(b->tvheap, nme, "theap", true, b->tvheap->free,
1544 : &b->theaplock) != GDK_SUCCEED) {
1545 0 : HEAPfree(b->tvheap, false);
1546 0 : b->tvheap = ovh;
1547 0 : TRC_CRITICAL(GDK, "saving string failed\n");
1548 0 : return GDK_FAIL;
1549 : }
1550 :
1551 0 : HEAPdecref(b->theap, false);
1552 0 : b->theap = h2;
1553 0 : HEAPfree(h2, false);
1554 0 : HEAPdecref(ovh, false);
1555 0 : HEAPfree(b->tvheap, false);
1556 :
1557 0 : return GDK_SUCCEED;
1558 : }
1559 :
1560 : gdk_return
1561 0 : BBPjson_upgrade(json_storage_conversion fixJSONStorage)
1562 : {
1563 0 : bat bid;
1564 0 : int JSON_type = ATOMindex("json");
1565 0 : bat nbat = (bat) ATOMIC_GET(&BBPsize);
1566 0 : bat *upd = GDKmalloc(sizeof(bat) * (size_t) nbat);
1567 0 : int nupd = 0;
1568 :
1569 0 : if (upd == NULL) {
1570 0 : TRC_CRITICAL(GDK, "could not create bat\n");
1571 0 : return GDK_FAIL;
1572 : }
1573 0 : upd[nupd++] = 0; /* first entry unused */
1574 :
1575 0 : BBPlock();
1576 :
1577 0 : for (bid = 1; bid < nbat; bid++) {
1578 0 : BAT *b = BBP_desc(bid);
1579 0 : if (b->batCacheid == 0) {
1580 : /* not a valid BAT */
1581 0 : continue;
1582 : }
1583 :
1584 0 : if (b->ttype < 0) {
1585 0 : const char *nme;
1586 :
1587 0 : nme = ATOMunknown_name(b->ttype);
1588 0 : int tt = ATOMindex(nme);
1589 0 : if (tt >= 0)
1590 0 : b->ttype = tt;
1591 0 : if (strcmp(nme, "json") != 0)
1592 0 : continue;
1593 0 : } else if (b->ttype != JSON_type) {
1594 0 : continue;
1595 : }
1596 0 : fprintf(stderr, "Upgrading json bat %d\n", bid);
1597 0 : if (jsonupgradebat(b, fixJSONStorage) != GDK_SUCCEED) {
1598 0 : BBPunlock();
1599 0 : GDKfree(upd);
1600 0 : return GDK_FAIL;
1601 : }
1602 0 : upd[nupd++] = bid;
1603 : }
1604 0 : BBPunlock();
1605 0 : if (nupd > 1 &&
1606 0 : TMsubcommit_list(upd, NULL, nupd, -1) != GDK_SUCCEED) {
1607 0 : TRC_CRITICAL(GDK, "failed to commit changes\n");
1608 0 : GDKfree(upd);
1609 0 : return GDK_FAIL;
1610 : }
1611 0 : GDKfree(upd);
1612 0 : return GDK_SUCCEED;
1613 : }
1614 : #endif
1615 :
1616 : static bool
1617 49 : BBPtrim(bool aggressive, bat nbat)
1618 : {
1619 49 : int n = 0;
1620 49 : int waitctr = 0;
1621 49 : bool changed = false;
1622 49 : unsigned flag = BBPUNLOADING | BBPSYNCING | BBPSAVING;
1623 49 : if (!aggressive)
1624 49 : flag |= BBPHOT;
1625 49 : lng t0 = GDKusec();
1626 69109 : for (bat bid = 1; bid < nbat && !GDKexiting(); bid++) {
1627 : /* quick check to see if we might possibly have to do
1628 : * work (includes free bats) */
1629 69060 : if ((BBP_status(bid) & BBPLOADED) == 0)
1630 14926 : continue;
1631 : /* don't do this during a (sub)commit */
1632 54134 : BBPtmlock();
1633 54134 : MT_lock_set(&GDKswapLock(bid));
1634 54134 : BAT *b = NULL;
1635 54134 : bool swap = false;
1636 54134 : if ((BBP_status(bid) & (flag | BBPLOADED)) == BBPLOADED &&
1637 13962 : BBP_refs(bid) == 0 &&
1638 13962 : BBP_lrefs(bid) != 0 &&
1639 13962 : (b = BBP_desc(bid))->batCacheid != 0) {
1640 13962 : MT_lock_set(&b->theaplock);
1641 13962 : if (!BATshared(b) &&
1642 13962 : !isVIEW(b) &&
1643 13938 : (!BATdirty(b) ||
1644 0 : (aggressive &&
1645 0 : b->theap->storage == STORE_MMAP &&
1646 0 : (b->tvheap == NULL ||
1647 0 : b->tvheap->storage == STORE_MMAP)) ||
1648 11513 : (b->batRole == PERSISTENT &&
1649 11508 : BBP_lrefs(bid) <= 2))) {
1650 10105 : BBP_status_on(bid, BBPUNLOADING);
1651 10105 : swap = true;
1652 17785 : waitctr += BATdirty(b) ? 9 : 1;
1653 : }
1654 13962 : MT_lock_unset(&b->theaplock);
1655 : }
1656 54134 : MT_lock_unset(&GDKswapLock(bid));
1657 54134 : if (swap) {
1658 10105 : TRC_DEBUG(BAT_, "unload and free bat %d\n", bid);
1659 10105 : if (BBPfree(b) != GDK_SUCCEED)
1660 0 : GDKerror("unload failed for bat %d", bid);
1661 10105 : n++;
1662 10105 : changed = true;
1663 : }
1664 54134 : BBPtmunlock();
1665 : /* every once in a while, give others a chance */
1666 54134 : if (++waitctr >= 1000) {
1667 111 : waitctr = 0;
1668 111 : MT_sleep_ms(2);
1669 : }
1670 : }
1671 49 : if (n > 0)
1672 22 : TRC_INFO(BAT_, "unloaded %d bats in "LLFMT" usec%s\n", n, GDKusec() - t0, aggressive ? " (also hot)" : "");
1673 49 : return changed;
1674 : }
1675 :
1676 : static void
1677 351 : BBPmanager(void *dummy)
1678 : {
1679 351 : (void) dummy;
1680 351 : bool changed = true;
1681 :
1682 400 : for (;;) {
1683 400 : int n = 0;
1684 400 : bat nbat = (bat) ATOMIC_GET(&BBPsize);
1685 400 : MT_thread_setworking("clearing HOT bits");
1686 292377 : for (bat bid = 1; bid < nbat; bid++) {
1687 291977 : MT_lock_set(&GDKswapLock(bid));
1688 291977 : if (BBP_refs(bid) == 0 && BBP_lrefs(bid) != 0) {
1689 108700 : n += (BBP_status(bid) & BBPHOT) != 0;
1690 108700 : BBP_status_off(bid, BBPHOT);
1691 : }
1692 291977 : MT_lock_unset(&GDKswapLock(bid));
1693 : }
1694 400 : TRC_DEBUG(BAT_, "cleared HOT bit from %d bats\n", n);
1695 400 : size_t cur = GDKvm_cursize();
1696 400 : MT_thread_setworking("sleeping");
1697 10871 : for (int i = 0, n = changed && cur > GDK_vm_maxsize / 2 ? 1 : cur > GDK_vm_maxsize / 4 ? 10 : 100; i < n; i++) {
1698 10422 : MT_sleep_ms(100);
1699 10420 : if (GDKexiting())
1700 : return;
1701 : }
1702 49 : MT_thread_setworking("BBPtrim");
1703 49 : changed = BBPtrim(false, nbat);
1704 49 : MT_thread_setworking("BBPcallbacks");
1705 49 : BBPcallbacks();
1706 49 : if (GDKexiting())
1707 : return;
1708 : }
1709 : }
1710 :
1711 : static MT_Id manager;
1712 :
1713 : gdk_return
1714 352 : BBPinit(bool allow_hge_upgrade, bool no_manager)
1715 : {
1716 352 : FILE *fp = NULL;
1717 352 : struct stat st;
1718 352 : unsigned bbpversion = 0;
1719 352 : int i;
1720 352 : int lineno = 0;
1721 : #ifdef GDKLIBRARY_HASHASH
1722 352 : bat *hashbats = NULL;
1723 352 : bat nhashbats = 0;
1724 352 : gdk_return res = GDK_SUCCEED;
1725 : #endif
1726 352 : ATOMIC_BASE_TYPE dbg = ATOMIC_GET(&GDKdebug);
1727 :
1728 352 : ATOMIC_AND(&GDKdebug, ~TAILCHKMASK);
1729 :
1730 : /* the maximum number of BATs allowed in the system and the
1731 : * size of the "physical" array are linked in a complicated
1732 : * manner. The expression below shows the relationship */
1733 352 : static_assert((uint64_t) N_BBPINIT * BBPINIT < (UINT64_C(1) << (3 * ((sizeof(BBP[0][0].physical) + 2) * 2 / 5))), "\"physical\" array in BBPrec is too small");
1734 : /* similarly, the maximum number of BATs allowed also has a
1735 : * (somewhat simpler) relation with the size of the "bak"
1736 : * array */
1737 352 : static_assert((uint64_t) N_BBPINIT * BBPINIT < (UINT64_C(1) << (3 * (sizeof(BBP[0][0].bak) - 5))), "\"bak\" array in BBPrec is too small");
1738 :
1739 352 : if (!GDKinmemory(0)) {
1740 351 : char bbpdirstr[MAXPATH], backupbbpdirstr[MAXPATH];
1741 :
1742 351 : BBPtmlock();
1743 :
1744 351 : if (GDKfilepath(bbpdirstr, sizeof(bbpdirstr), 0, BATDIR, "BBP", "dir") != GDK_SUCCEED) {
1745 0 : TRC_CRITICAL(GDK, "GDKmalloc failed\n");
1746 0 : BBPtmunlock();
1747 0 : ATOMIC_SET(&GDKdebug, dbg);
1748 0 : return GDK_FAIL;
1749 : }
1750 :
1751 351 : if (GDKfilepath(backupbbpdirstr, sizeof(backupbbpdirstr), 0, BAKDIR, "BBP", "dir") != GDK_SUCCEED) {
1752 0 : TRC_CRITICAL(GDK, "GDKmalloc failed\n");
1753 0 : BBPtmunlock();
1754 0 : ATOMIC_SET(&GDKdebug, dbg);
1755 0 : return GDK_FAIL;
1756 : }
1757 :
1758 351 : if (GDKremovedir(0, TEMPDIR) != GDK_SUCCEED) {
1759 0 : TRC_CRITICAL(GDK, "cannot remove directory %s\n", TEMPDIR);
1760 0 : BBPtmunlock();
1761 0 : ATOMIC_SET(&GDKdebug, dbg);
1762 0 : return GDK_FAIL;
1763 : }
1764 :
1765 351 : if (GDKremovedir(0, DELDIR) != GDK_SUCCEED) {
1766 0 : TRC_CRITICAL(GDK, "cannot remove directory %s\n", DELDIR);
1767 0 : BBPtmunlock();
1768 0 : ATOMIC_SET(&GDKdebug, dbg);
1769 0 : return GDK_FAIL;
1770 : }
1771 :
1772 : /* first move everything from SUBDIR to BAKDIR (its parent) */
1773 351 : if (BBPrecover_subdir() != GDK_SUCCEED) {
1774 0 : TRC_CRITICAL(GDK, "cannot properly recover_subdir process %s.", SUBDIR);
1775 0 : BBPtmunlock();
1776 0 : ATOMIC_SET(&GDKdebug, dbg);
1777 0 : return GDK_FAIL;
1778 : }
1779 :
1780 : /* try to obtain a BBP.dir from bakdir */
1781 351 : if (MT_stat(backupbbpdirstr, &st) == 0) {
1782 : /* backup exists; *must* use it */
1783 125 : if (recover_dir(0, MT_stat(bbpdirstr, &st) == 0) != GDK_SUCCEED) {
1784 0 : BBPtmunlock();
1785 0 : goto bailout;
1786 : }
1787 125 : if ((fp = GDKfilelocate(0, "BBP", "r", "dir")) == NULL) {
1788 0 : TRC_CRITICAL(GDK, "cannot open recovered BBP.dir.");
1789 0 : BBPtmunlock();
1790 0 : ATOMIC_SET(&GDKdebug, dbg);
1791 0 : return GDK_FAIL;
1792 : }
1793 226 : } else if ((fp = GDKfilelocate(0, "BBP", "r", "dir")) == NULL) {
1794 : /* there was no BBP.dir either. Panic! try to use a
1795 : * BBP.bak */
1796 226 : if (MT_stat(backupbbpdirstr, &st) < 0) {
1797 : /* no BBP.bak (nor BBP.dir or BACKUP/BBP.dir):
1798 : * create a new one */
1799 226 : TRC_DEBUG(IO_, "initializing BBP.\n");
1800 226 : if (BBPdir_init() != GDK_SUCCEED) {
1801 0 : BBPtmunlock();
1802 0 : goto bailout;
1803 : }
1804 0 : } else if (GDKmove(0, BATDIR, "BBP", "bak", BATDIR, "BBP", "dir", true) == GDK_SUCCEED)
1805 0 : TRC_DEBUG(IO_, "reverting to dir saved in BBP.bak.\n");
1806 :
1807 226 : if ((fp = GDKfilelocate(0, "BBP", "r", "dir")) == NULL) {
1808 0 : GDKsyserror("cannot open BBP.dir");
1809 0 : BBPtmunlock();
1810 0 : goto bailout;
1811 : }
1812 : }
1813 : assert(fp != NULL);
1814 351 : BBPtmunlock();
1815 : }
1816 :
1817 : /* scan the BBP.dir to obtain current size */
1818 352 : BBPlimit = BBPINIT;
1819 352 : memset(BBP0, 0, sizeof(BBP0));
1820 352 : memset(BBP, 0, sizeof(BBP));
1821 352 : BBP[0] = BBP0;
1822 :
1823 352 : bat bbpsize;
1824 352 : bbpsize = 1;
1825 352 : if (GDKinmemory(0)) {
1826 : bbpversion = GDKLIBRARY;
1827 : } else {
1828 351 : lng logno;
1829 351 : bbpversion = BBPheader(fp, &lineno, &bbpsize, &logno, allow_hge_upgrade);
1830 351 : if (bbpversion == 0) {
1831 0 : ATOMIC_SET(&GDKdebug, dbg);
1832 0 : return GDK_FAIL;
1833 : }
1834 351 : ATOMIC_SET(&BBPlogno, logno);
1835 : }
1836 :
1837 : /* allocate BBP records */
1838 352 : if (BBPextend(bbpsize) != GDK_SUCCEED) {
1839 0 : ATOMIC_SET(&GDKdebug, dbg);
1840 0 : return GDK_FAIL;
1841 : }
1842 352 : ATOMIC_SET(&BBPsize, bbpsize);
1843 :
1844 352 : if (!GDKinmemory(0)) {
1845 351 : if (BBPreadEntries(fp, bbpversion, lineno
1846 : #ifdef GDKLIBRARY_HASHASH
1847 : , &hashbats, &nhashbats
1848 : #endif
1849 : ) != GDK_SUCCEED) {
1850 0 : ATOMIC_SET(&GDKdebug, dbg);
1851 0 : return GDK_FAIL;
1852 : }
1853 351 : fclose(fp);
1854 : }
1855 :
1856 : /* remove trailing free bats from potential free list (they will
1857 : * get added when needed) */
1858 57720 : for (bat i = (bat) ATOMIC_GET(&BBPsize) - 1; i > 0; i--) {
1859 57493 : if (BBP_desc(i)->batCacheid != 0)
1860 : break;
1861 57368 : bbpsize--;
1862 : }
1863 352 : ATOMIC_SET(&BBPsize, bbpsize);
1864 :
1865 : /* add free bats to free list in such a way that low numbered
1866 : * ones are at the head of the list */
1867 83179 : for (bat i = (bat) ATOMIC_GET(&BBPsize) - 1; i > 0; i--) {
1868 82827 : if (BBP_desc(i)->batCacheid == 0) {
1869 49123 : BBP_next(i) = BBP_free;
1870 49123 : BBP_free = i;
1871 49123 : BBP_nfree++;
1872 : }
1873 : }
1874 :
1875 : /* will call BBPrecover if needed */
1876 352 : if (!GDKinmemory(0)) {
1877 351 : BBPtmlock();
1878 351 : gdk_return rc = BBPprepare(false);
1879 351 : BBPtmunlock();
1880 351 : if (rc != GDK_SUCCEED) {
1881 : #ifdef GDKLIBRARY_HASHASH
1882 0 : GDKfree(hashbats);
1883 : #endif
1884 0 : TRC_CRITICAL(GDK, "cannot properly prepare process %s.", BAKDIR);
1885 0 : ATOMIC_SET(&GDKdebug, dbg);
1886 0 : return rc;
1887 : }
1888 : }
1889 :
1890 352 : if (BBPcheckbats(bbpversion) != GDK_SUCCEED) {
1891 : #ifdef GDKLIBRARY_HASHASH
1892 0 : GDKfree(hashbats);
1893 : #endif
1894 0 : ATOMIC_SET(&GDKdebug, dbg);
1895 0 : return GDK_FAIL;
1896 : }
1897 :
1898 : #ifdef GDKLIBRARY_HASHASH
1899 352 : if (nhashbats > 0)
1900 0 : res = fixhashash(hashbats, nhashbats);
1901 352 : GDKfree(hashbats);
1902 352 : if (res != GDK_SUCCEED)
1903 : return res;
1904 : #endif
1905 :
1906 : #ifdef GDKLIBRARY_JSON
1907 352 : if (bbpversion <= GDKLIBRARY_JSON && !GDKinmemory(0)) {
1908 0 : char jsonupgradestr[MAXPATH];
1909 :
1910 0 : if (GDKfilepath(jsonupgradestr, sizeof(jsonupgradestr), 0, BATDIR, "jsonupgradeneeded", NULL) != GDK_SUCCEED) {
1911 0 : TRC_CRITICAL(GDK, "GDKfilepath failed\n");
1912 0 : ATOMIC_SET(&GDKdebug, dbg);
1913 0 : return GDK_FAIL;
1914 : }
1915 :
1916 : /* create signal file that we need to upgrade stored
1917 : * json strings. This will be performed by an upgrade
1918 : * function in the GDK that will be called at the end of
1919 : * the json module initialization with a callback that
1920 : * actually knows how to perform the upgrade. */
1921 0 : int fd = MT_open(jsonupgradestr, O_WRONLY | O_CREAT);
1922 0 : if (fd < 0) {
1923 0 : TRC_CRITICAL(GDK, "cannot create signal file jsonupgradeneeded");
1924 0 : ATOMIC_SET(&GDKdebug, dbg);
1925 0 : return GDK_FAIL;
1926 : }
1927 :
1928 0 : close(fd);
1929 : }
1930 : #endif
1931 :
1932 352 : if (bbpversion < GDKLIBRARY && TMcommit() != GDK_SUCCEED) {
1933 0 : TRC_CRITICAL(GDK, "TMcommit failed\n");
1934 0 : ATOMIC_SET(&GDKdebug, dbg);
1935 0 : return GDK_FAIL;
1936 : }
1937 :
1938 352 : ATOMIC_SET(&GDKdebug, dbg);
1939 :
1940 : /* cleanup any leftovers (must be done after BBPrecover) */
1941 1394 : for (i = 0; i < MAXFARMS && BBPfarms[i].dirname != NULL; i++) {
1942 : int j;
1943 1414 : for (j = 0; j < i; j++) {
1944 : /* don't clean a directory twice */
1945 877 : if (BBPfarms[j].dirname &&
1946 877 : strcmp(BBPfarms[i].dirname,
1947 : BBPfarms[j].dirname) == 0)
1948 : break;
1949 : }
1950 1042 : if (j == i) {
1951 537 : char d[MAXPATH];
1952 :
1953 537 : if (GDKfilepath(d, sizeof(d), i, NULL, BATDIR, NULL) != GDK_SUCCEED) {
1954 0 : return GDK_FAIL;
1955 : }
1956 537 : BBPdiskscan(d, strlen(d) - strlen(BATDIR));
1957 : }
1958 : }
1959 :
1960 352 : if (!GDKinmemory(0) && !no_manager && MT_create_thread(&manager, BBPmanager, NULL, MT_THR_DETACHED, "BBPmanager") < 0) {
1961 0 : TRC_CRITICAL(GDK, "Could not start BBPmanager thread.");
1962 0 : return GDK_FAIL;
1963 : }
1964 : return GDK_SUCCEED;
1965 :
1966 0 : bailout:
1967 : /* now it is time for real panic */
1968 0 : TRC_CRITICAL(GDK, "could not write %s%cBBP.dir.", BATDIR, DIR_SEP);
1969 0 : return GDK_FAIL;
1970 : }
1971 :
1972 : /*
1973 : * During the exit phase all non-persistent BATs are removed. Upon
1974 : * exit the status of the BBP tables is saved on disk. This function
1975 : * is called once and during the shutdown of the server. Since
1976 : * shutdown may be issued from any thread (dangerous) it may lead to
1977 : * interference in a parallel session.
1978 : */
1979 :
1980 : static int backup_files = 0, backup_dir = 0, backup_subdir = 0;
1981 :
1982 : void
1983 350 : BBPexit(void)
1984 : {
1985 350 : bat i;
1986 350 : bool skipped;
1987 :
1988 : //BBPlock(); /* stop all threads ever touching more descriptors */
1989 :
1990 : /* free all memory (just for leak-checking in Purify) */
1991 350 : do {
1992 350 : skipped = false;
1993 803019 : for (i = 0; i < (bat) ATOMIC_GET(&BBPsize); i++) {
1994 802669 : if (BBPvalid(i)) {
1995 563190 : BAT *b = BBP_desc(i);
1996 :
1997 563190 : if (b->batCacheid != 0) {
1998 563190 : if (BATshared(b)) {
1999 0 : skipped = true;
2000 0 : continue;
2001 : }
2002 563190 : MT_lock_set(&b->theaplock);
2003 563190 : bat tp = VIEWtparent(b);
2004 0 : if (tp != 0) {
2005 0 : --BBP_lrefs(tp);
2006 0 : HEAPdecref(b->theap, false);
2007 0 : b->theap = NULL;
2008 : }
2009 563190 : tp = VIEWvtparent(b);
2010 0 : if (tp != 0) {
2011 0 : --BBP_lrefs(tp);
2012 0 : HEAPdecref(b->tvheap, false);
2013 0 : b->tvheap = NULL;
2014 : }
2015 563190 : if (b->oldtail) {
2016 6 : Heap *h = b->oldtail;
2017 6 : b->oldtail = NULL;
2018 6 : ATOMIC_AND(&h->refs, ~DELAYEDREMOVE);
2019 6 : HEAPdecref(h, false);
2020 : }
2021 563190 : PROPdestroy_nolock(b);
2022 563190 : MT_lock_unset(&b->theaplock);
2023 563190 : BATfree(b);
2024 : }
2025 563190 : BBP_pid(i) = 0;
2026 563190 : BBPuncacheit(i, true);
2027 563190 : if (BBP_logical(i) != BBP_bak(i))
2028 11480 : GDKfree(BBP_logical(i));
2029 563190 : BBP_logical(i) = NULL;
2030 : }
2031 : }
2032 350 : } while (skipped);
2033 : /* these need to be NULL, otherwise no new ones get created */
2034 350 : memset(BBP_hash, 0, sizeof(BBP_hash));
2035 350 : backup_files = 0;
2036 350 : backup_dir = 0;
2037 350 : backup_subdir = 0;
2038 350 : }
2039 :
2040 : /*
2041 : * The routine BBPdir creates the BAT pool dictionary file. It
2042 : * includes some information about the current state of affair in the
2043 : * pool. The location in the buffer pool is saved for later use as
2044 : * well. This is merely done for ease of debugging and of no
2045 : * importance to front-ends. The tail of non-used entries is
2046 : * reclaimed as well.
2047 : */
2048 : static inline int
2049 1714814 : heap_entry(FILE *fp, BATiter *bi, BUN size)
2050 : {
2051 1714814 : size_t free = bi->hfree;
2052 1714814 : if (size < BUN_NONE) {
2053 1714814 : if ((bi->type >= 0 && ATOMstorage(bi->type) == TYPE_msk))
2054 269535 : free = ((size + 31) / 32) * 4;
2055 1445279 : else if (bi->width > 0)
2056 1445279 : free = size << bi->shift;
2057 : else
2058 : free = 0;
2059 : }
2060 :
2061 5508351 : return fprintf(fp, " %s %d %d %d " BUNFMT " " BUNFMT " " BUNFMT " "
2062 : BUNFMT " " OIDFMT " %zu %" PRIu64" %" PRIu64,
2063 1714814 : bi->type >= 0 ? BATatoms[bi->type].name : ATOMunknown_name(bi->type),
2064 1714814 : bi->width,
2065 1714814 : bi->type == TYPE_void || bi->vh != NULL,
2066 1714814 : (unsigned short) bi->sorted |
2067 1714814 : ((unsigned short) bi->revsorted << 7) |
2068 3429628 : ((unsigned short) bi->key << 8) |
2069 1714814 : ((unsigned short) BATtdensebi(bi) << 9) |
2070 1714814 : ((unsigned short) bi->nonil << 10) |
2071 1714814 : ((unsigned short) bi->nil << 11) |
2072 1714814 : ((unsigned short) bi->ascii << 12),
2073 1025989 : bi->nokey[0] >= size || bi->nokey[1] >= size ? 0 : bi->nokey[0],
2074 1714814 : bi->nokey[0] >= size || bi->nokey[1] >= size ? 0 : bi->nokey[1],
2075 1714814 : bi->nosorted >= size ? 0 : bi->nosorted,
2076 1714814 : bi->norevsorted >= size ? 0 : bi->norevsorted,
2077 : bi->tseq,
2078 : free,
2079 1714814 : bi->minpos < size ? (uint64_t) bi->minpos : (uint64_t) oid_nil,
2080 1714814 : bi->maxpos < size ? (uint64_t) bi->maxpos : (uint64_t) oid_nil);
2081 : }
2082 :
2083 : static inline int
2084 1714814 : vheap_entry(FILE *fp, BATiter *bi, BUN size)
2085 : {
2086 1714814 : (void) size;
2087 1714814 : if (bi->vh == NULL)
2088 : return 0;
2089 404014 : return fprintf(fp, " %zu", size == 0 ? 0 : bi->vhfree);
2090 : }
2091 :
2092 : static gdk_return
2093 1714814 : new_bbpentry(FILE *fp, bat i, BUN size, BATiter *bi)
2094 : {
2095 : #ifndef NDEBUG
2096 1714814 : assert(i > 0);
2097 1714814 : assert(i < (bat) ATOMIC_GET(&BBPsize));
2098 1714814 : assert(bi->b);
2099 1714814 : assert(bi->b->batCacheid == i);
2100 1714814 : assert(bi->b->batRole == PERSISTENT);
2101 1714814 : assert(0 <= bi->h->farmid && bi->h->farmid < MAXFARMS);
2102 1714814 : assert(BBPfarms[bi->h->farmid].roles & (1U << PERSISTENT));
2103 1714814 : if (bi->vh) {
2104 404014 : assert(0 <= bi->vh->farmid && bi->vh->farmid < MAXFARMS);
2105 404014 : assert(BBPfarms[bi->vh->farmid].roles & (1U << PERSISTENT));
2106 : }
2107 1714814 : assert(size <= bi->count || size == BUN_NONE);
2108 1714814 : assert(BBP_options(i) == NULL || strpbrk(BBP_options(i), "\r\n") == NULL);
2109 : #endif
2110 :
2111 1714814 : if (BBP_options(i) != NULL && strpbrk(BBP_options(i), "\r\n") != NULL) {
2112 0 : GDKerror("options for bat %d contains a newline\n", i);
2113 0 : return GDK_FAIL;
2114 : }
2115 1714814 : if (size > bi->count)
2116 : size = bi->count;
2117 1714814 : if (fprintf(fp, "%d %s %d " BUNFMT " " OIDFMT,
2118 : /* BAT info */
2119 : (int) i,
2120 : BBP_logical(i),
2121 1714814 : (unsigned) bi->restricted << 1,
2122 : size,
2123 1714814 : bi->b->hseqbase) < 0 ||
2124 3429628 : heap_entry(fp, bi, size) < 0 ||
2125 1714814 : vheap_entry(fp, bi, size) < 0 ||
2126 3429628 : (BBP_options(i) && fprintf(fp, " %s", BBP_options(i)) < 0) ||
2127 1714814 : fprintf(fp, "\n") < 0) {
2128 0 : GDKsyserror("new_bbpentry: Writing BBP.dir entry failed\n");
2129 0 : return GDK_FAIL;
2130 : }
2131 :
2132 : return GDK_SUCCEED;
2133 : }
2134 :
2135 : static gdk_return
2136 12274 : BBPdir_header(FILE *f, int n, lng logno)
2137 : {
2138 12274 : if (fprintf(f, "BBP.dir, GDKversion %u\n%d %d %d\nBBPsize=%d\nBBPinfo=" LLFMT "\n",
2139 : GDKLIBRARY, SIZEOF_SIZE_T, SIZEOF_OID,
2140 : #ifdef HAVE_HGE
2141 : SIZEOF_HGE
2142 : #else
2143 : SIZEOF_LNG
2144 : #endif
2145 12274 : , n, logno) < 0 ||
2146 12274 : ferror(f)) {
2147 0 : GDKsyserror("Writing BBP.dir header failed\n");
2148 0 : return GDK_FAIL;
2149 : }
2150 : return GDK_SUCCEED;
2151 : }
2152 :
2153 : static gdk_return
2154 12274 : BBPdir_first(bool subcommit, lng logno, FILE **obbpfp, FILE **nbbpfp)
2155 : {
2156 12274 : FILE *obbpf = NULL, *nbbpf = NULL;
2157 12274 : int n = 0;
2158 12274 : lng ologno;
2159 :
2160 12274 : if (obbpfp)
2161 12048 : *obbpfp = NULL;
2162 12274 : *nbbpfp = NULL;
2163 :
2164 12274 : if ((nbbpf = GDKfilelocate(0, "BBP", "w", "dir")) == NULL) {
2165 : return GDK_FAIL;
2166 : }
2167 :
2168 12274 : if (subcommit) {
2169 12040 : char buf[512];
2170 :
2171 12040 : assert(obbpfp != NULL);
2172 : /* we need to copy the backup BBP.dir to the new, but
2173 : * replacing the entries for the subcommitted bats */
2174 12040 : if ((obbpf = GDKfileopen(0, SUBDIR, "BBP", "dir", "r")) == NULL &&
2175 0 : (obbpf = GDKfileopen(0, BAKDIR, "BBP", "dir", "r")) == NULL) {
2176 0 : GDKsyserror("subcommit attempted without backup BBP.dir");
2177 0 : goto bailout;
2178 : }
2179 : /* read first three lines */
2180 24080 : if (fgets(buf, sizeof(buf), obbpf) == NULL || /* BBP.dir, GDKversion %d */
2181 24080 : fgets(buf, sizeof(buf), obbpf) == NULL || /* SIZEOF_SIZE_T SIZEOF_OID SIZEOF_MAX_INT */
2182 12040 : fgets(buf, sizeof(buf), obbpf) == NULL) { /* BBPsize=%d */
2183 0 : GDKerror("subcommit attempted with invalid backup BBP.dir.");
2184 0 : goto bailout;
2185 : }
2186 : /* third line contains BBPsize */
2187 12040 : if (sscanf(buf, "BBPsize=%d", &n) != 1) {
2188 0 : GDKerror("cannot read BBPsize in backup BBP.dir.");
2189 0 : goto bailout;
2190 : }
2191 : /* fourth line contains BBPinfo */
2192 12040 : if (fgets(buf, sizeof(buf), obbpf) == NULL ||
2193 12040 : sscanf(buf, "BBPinfo=" LLSCN, &ologno) != 1) {
2194 0 : GDKerror("cannot read BBPinfo in backup BBP.dir.");
2195 0 : goto bailout;
2196 : }
2197 : }
2198 :
2199 12274 : if (n < (bat) ATOMIC_GET(&BBPsize))
2200 2678 : n = (bat) ATOMIC_GET(&BBPsize);
2201 :
2202 12274 : TRC_DEBUG(IO_, "writing BBP.dir (%d bats).\n", n);
2203 :
2204 12274 : if (BBPdir_header(nbbpf, n, logno) != GDK_SUCCEED) {
2205 0 : goto bailout;
2206 : }
2207 :
2208 12274 : if (obbpfp)
2209 12048 : *obbpfp = obbpf;
2210 12274 : *nbbpfp = nbbpf;
2211 :
2212 12274 : return GDK_SUCCEED;
2213 :
2214 0 : bailout:
2215 0 : if (obbpf != NULL)
2216 0 : fclose(obbpf);
2217 0 : if (nbbpf != NULL)
2218 0 : fclose(nbbpf);
2219 0 : return GDK_FAIL;
2220 : }
2221 :
2222 : static bat
2223 1907764 : BBPdir_step(bat bid, BUN size, int n, char *buf, size_t bufsize,
2224 : FILE **obbpfp, FILE *nbbpf, BATiter *bi)
2225 : {
2226 1907764 : if (n < -1) /* safety catch */
2227 : return n;
2228 4951500 : while (n >= 0 && n < bid) {
2229 3043736 : if (n > 0) {
2230 1389262 : if (fputs(buf, nbbpf) == EOF) {
2231 0 : GDKerror("Writing BBP.dir file failed.\n");
2232 0 : goto bailout;
2233 : }
2234 : }
2235 3043736 : if (fgets(buf, (int) bufsize, *obbpfp) == NULL) {
2236 3000 : if (ferror(*obbpfp)) {
2237 0 : GDKerror("error reading backup BBP.dir.");
2238 0 : goto bailout;
2239 : }
2240 3000 : n = -1;
2241 3000 : if (fclose(*obbpfp) == EOF) {
2242 0 : GDKsyserror("Closing backup BBP.dir file failed\n");
2243 0 : GDKclrerr(); /* ignore error */
2244 : }
2245 3000 : *obbpfp = NULL;
2246 : } else {
2247 3040736 : if (sscanf(buf, "%d", &n) != 1 || n <= 0 || n >= N_BBPINIT * BBPINIT) {
2248 0 : GDKerror("subcommit attempted with invalid backup BBP.dir.");
2249 0 : goto bailout;
2250 : }
2251 : }
2252 : }
2253 1907764 : if (bi) {
2254 1714814 : assert(BBP_status(bid) & BBPPERSISTENT);
2255 1714814 : if (new_bbpentry(nbbpf, bid, size, bi) != GDK_SUCCEED)
2256 0 : goto bailout;
2257 : }
2258 1907764 : return n == -1 ? -1 : n == bid ? 0 : n;
2259 :
2260 0 : bailout:
2261 0 : if (*obbpfp)
2262 0 : fclose(*obbpfp);
2263 0 : fclose(nbbpf);
2264 0 : return -2;
2265 : }
2266 :
2267 : static gdk_return
2268 12274 : BBPdir_last(int n, char *buf, size_t bufsize, FILE *obbpf, FILE *nbbpf)
2269 : {
2270 12274 : if (n > 0 && fputs(buf, nbbpf) == EOF) {
2271 0 : GDKerror("Writing BBP.dir file failed.\n");
2272 0 : goto bailout;
2273 : }
2274 317936 : while (obbpf) {
2275 314702 : if (fgets(buf, (int) bufsize, obbpf) == NULL) {
2276 9040 : if (ferror(obbpf)) {
2277 0 : GDKerror("error reading backup BBP.dir.");
2278 0 : goto bailout;
2279 : }
2280 9040 : if (fclose(obbpf) == EOF) {
2281 0 : GDKsyserror("Closing backup BBP.dir file failed\n");
2282 0 : GDKclrerr(); /* ignore error */
2283 : }
2284 : obbpf = NULL;
2285 : } else {
2286 305662 : if (fputs(buf, nbbpf) == EOF) {
2287 0 : GDKerror("Writing BBP.dir file failed.\n");
2288 0 : goto bailout;
2289 : }
2290 : }
2291 : }
2292 12274 : if (fflush(nbbpf) == EOF ||
2293 12274 : (!(ATOMIC_GET(&GDKdebug) & NOSYNCMASK)
2294 : #if defined(NATIVE_WIN32)
2295 : && _commit(_fileno(nbbpf)) < 0
2296 : #elif defined(HAVE_FDATASYNC)
2297 5 : && fdatasync(fileno(nbbpf)) < 0
2298 : #elif defined(HAVE_FSYNC)
2299 : && fsync(fileno(nbbpf)) < 0
2300 : #endif
2301 : )) {
2302 0 : GDKsyserror("Syncing BBP.dir file failed\n");
2303 0 : goto bailout;
2304 : }
2305 12274 : if (fclose(nbbpf) == EOF) {
2306 0 : GDKsyserror("Closing BBP.dir file failed\n");
2307 0 : nbbpf = NULL; /* can't close again */
2308 0 : goto bailout;
2309 : }
2310 :
2311 12274 : TRC_DEBUG(IO_, "end\n");
2312 :
2313 : return GDK_SUCCEED;
2314 :
2315 0 : bailout:
2316 0 : if (obbpf != NULL)
2317 0 : fclose(obbpf);
2318 0 : if (nbbpf != NULL)
2319 0 : fclose(nbbpf);
2320 : return GDK_FAIL;
2321 : }
2322 :
2323 : gdk_return
2324 226 : BBPdir_init(void)
2325 : {
2326 226 : FILE *fp;
2327 226 : gdk_return rc;
2328 :
2329 226 : rc = BBPdir_first(false, 0, NULL, &fp);
2330 226 : if (rc == GDK_SUCCEED)
2331 226 : rc = BBPdir_last(-1, NULL, 0, NULL, fp);
2332 226 : return rc;
2333 : }
2334 :
2335 : /* function used for debugging */
2336 : void
2337 0 : BBPdump(void)
2338 : {
2339 0 : size_t mem = 0, vm = 0;
2340 0 : int n = 0;
2341 :
2342 0 : for (bat i = 0; i < (bat) ATOMIC_GET(&BBPsize); i++) {
2343 0 : if (BBP_refs(i) == 0 && BBP_lrefs(i) == 0)
2344 0 : continue;
2345 0 : BAT *b = BBP_desc(i);
2346 0 : unsigned status = BBP_status(i);
2347 0 : printf("# %d: " ALGOOPTBATFMT " refs=%d lrefs=%d status=%u%s",
2348 : i,
2349 0 : ALGOOPTBATPAR(b),
2350 : BBP_refs(i),
2351 0 : BBP_lrefs(i),
2352 : status,
2353 0 : status & BBPLOADED ? "" : " not cached");
2354 0 : if (b->batCacheid == 0) {
2355 0 : printf(", no descriptor\n");
2356 0 : continue;
2357 : }
2358 0 : if (b->theap) {
2359 0 : if (b->theap->parentid != b->batCacheid) {
2360 0 : printf(" Theap -> %d", b->theap->parentid);
2361 : } else {
2362 0 : printf(" Theap=[%zu,%zu,f=%d]%s%s",
2363 : b->theap->free,
2364 : b->theap->size,
2365 0 : b->theap->farmid,
2366 0 : b->theap->base == NULL ? "X" : b->theap->storage == STORE_MMAP ? "M" : "",
2367 0 : status & BBPSWAPPED ? "(Swapped)" : b->theap->dirty ? "(Dirty)" : "");
2368 0 : mem += HEAPmemsize(b->theap);
2369 0 : vm += HEAPvmsize(b->theap);
2370 0 : n++;
2371 : }
2372 : }
2373 0 : if (b->tvheap) {
2374 0 : if (b->tvheap->parentid != b->batCacheid) {
2375 0 : printf(" Tvheap -> %d",
2376 : b->tvheap->parentid);
2377 : } else {
2378 0 : printf(" Tvheap=[%zu,%zu,f=%d]%s%s",
2379 : b->tvheap->free,
2380 : b->tvheap->size,
2381 0 : b->tvheap->farmid,
2382 0 : b->tvheap->base == NULL ? "X" : b->tvheap->storage == STORE_MMAP ? "M" : "",
2383 0 : b->tvheap->dirty ? "(Dirty)" : "");
2384 0 : mem += HEAPmemsize(b->tvheap);
2385 0 : vm += HEAPvmsize(b->tvheap);
2386 : }
2387 : }
2388 0 : if (MT_rwlock_rdtry(&b->thashlock)) {
2389 0 : if (b->thash && b->thash != (Hash *) 1) {
2390 0 : size_t m = HEAPmemsize(&b->thash->heaplink) + HEAPmemsize(&b->thash->heapbckt);
2391 0 : size_t v = HEAPvmsize(&b->thash->heaplink) + HEAPvmsize(&b->thash->heapbckt);
2392 0 : printf(" Thash=[%zu,%zu,f=%d/%d]", m, v,
2393 0 : b->thash->heaplink.farmid,
2394 0 : b->thash->heapbckt.farmid);
2395 0 : mem += m;
2396 0 : vm += v;
2397 : }
2398 0 : MT_rwlock_rdunlock(&b->thashlock);
2399 : }
2400 0 : printf(" role: %s\n",
2401 0 : b->batRole == PERSISTENT ? "persistent" : "transient");
2402 : }
2403 0 : printf("# %d bats: mem=%zu, vm=%zu\n", n, mem, vm);
2404 0 : fflush(stdout);
2405 0 : }
2406 :
2407 : /*
2408 : * @+ BBP Readonly Interface
2409 : *
2410 : * These interface functions do not change the BBP tables. If they
2411 : * only access one specific BAT, the caller must have ensured that no
2412 : * other thread is modifying that BAT, therefore such functions do not
2413 : * need locking.
2414 : *
2415 : * BBP index lookup by BAT name:
2416 : */
2417 : static inline bat
2418 36217 : BBP_find(const char *nme, bool lock)
2419 : {
2420 36217 : bat i = BBPnamecheck(nme);
2421 :
2422 11201 : if (i != 0) {
2423 : /* for tmp_X BATs, we already know X */
2424 11201 : const char *s;
2425 :
2426 11201 : if (i >= (bat) ATOMIC_GET(&BBPsize) || (s = BBP_logical(i)) == NULL || strcmp(s, nme)) {
2427 11201 : i = 0;
2428 : }
2429 25016 : } else if (*nme != '.') {
2430 : /* must lock since hash-lookup traverses other BATs */
2431 25016 : if (lock)
2432 1670 : MT_lock_set(&BBPnameLock);
2433 25044 : for (i = BBP_hash[strHash(nme) & BBP_mask]; i; i = BBP_next(i)) {
2434 907 : if (strcmp(BBP_logical(i), nme) == 0)
2435 : break;
2436 : }
2437 25016 : if (lock)
2438 1670 : MT_lock_unset(&BBPnameLock);
2439 : }
2440 36217 : return i;
2441 : }
2442 :
2443 : bat
2444 1670 : BBPindex(const char *nme)
2445 : {
2446 1670 : return BBP_find(nme, true);
2447 : }
2448 :
2449 : /*
2450 : * @+ BBP Update Interface
2451 : * Operations to insert, delete, clear, and modify BBP entries.
2452 : * Our policy for the BBP is to provide unlocked BBP access for
2453 : * speed, but still write operations have to be locked.
2454 : * #ifdef DEBUG_THREADLOCAL_BATS
2455 : * Create the shadow version (reversed) of a bat.
2456 : *
2457 : * An existing BAT is inserted into the BBP
2458 : */
2459 :
2460 : /* The free list is empty. We create a new entry by either just
2461 : * increasing BBPsize (up to BBPlimit) or extending the BBP (which
2462 : * increases BBPlimit).
2463 : *
2464 : * Note that this is the only place in normal, multi-threaded operation
2465 : * where BBPsize is assigned a value (never decreasing) and that the
2466 : * assignment happens after any necessary memory was allocated and
2467 : * initialized. */
2468 : static gdk_return
2469 71995 : maybeextend(void)
2470 : {
2471 71995 : bat size = (bat) ATOMIC_GET(&BBPsize);
2472 71998 : if (size + BBP_FREE_LOWATER > BBPlimit &&
2473 3 : BBPextend(size + BBP_FREE_LOWATER) != GDK_SUCCEED) {
2474 : /* nothing available */
2475 : return GDK_FAIL;
2476 : }
2477 71995 : ATOMIC_SET(&BBPsize, size + BBP_FREE_LOWATER);
2478 71995 : assert(BBP_free == 0);
2479 71995 : BBP_free = size;
2480 719950 : for (int i = 1; i < BBP_FREE_LOWATER; i++) {
2481 647955 : bat sz = size;
2482 647955 : BBP_next(sz) = ++size;
2483 : }
2484 71995 : BBP_next(size) = 0;
2485 71995 : BBP_nfree += BBP_FREE_LOWATER;
2486 71995 : return GDK_SUCCEED;
2487 : }
2488 :
2489 : /* return new BAT id (> 0); return 0 on failure */
2490 : bat
2491 24218296 : BBPallocbat(int tt)
2492 : {
2493 24218296 : MT_Id pid = MT_getpid();
2494 24233375 : bool lock = locked_by == 0 || locked_by != pid;
2495 24233375 : bat i;
2496 24233375 : struct freebats *t = MT_thread_getfreebats();
2497 :
2498 24268510 : if (t->freebats == 0) {
2499 : /* critical section: get a new BBP entry */
2500 253143 : assert(t->nfreebats == 0);
2501 253143 : if (lock) {
2502 253143 : MT_lock_set(&GDKcacheLock);
2503 : }
2504 :
2505 : /* get a global bat, perhaps extend */
2506 253320 : if (BBP_free <= 0) {
2507 : /* we need to extend the BBP */
2508 71995 : gdk_return r;
2509 71995 : r = maybeextend();
2510 71995 : if (r != GDK_SUCCEED) {
2511 0 : if (lock) {
2512 0 : MT_lock_unset(&GDKcacheLock);
2513 : }
2514 : /* failed */
2515 0 : return 0;
2516 : }
2517 : }
2518 253320 : t->freebats = i = BBP_free;
2519 253320 : bat l = 0;
2520 2751256 : for (int x = 0; x < BBP_FREE_LOWATER && i; x++) {
2521 2497936 : assert(BBP_next(i) == 0 || BBP_next(i) > i);
2522 2497936 : t->nfreebats++;
2523 2497936 : BBP_nfree--;
2524 2497936 : l = i;
2525 2497936 : i = BBP_next(i);
2526 : }
2527 253320 : BBP_next(l) = 0;
2528 253320 : BBP_free = i;
2529 :
2530 253320 : if (lock) {
2531 253320 : MT_lock_unset(&GDKcacheLock);
2532 : }
2533 : /* rest of the work outside the lock */
2534 : }
2535 24268687 : if (t->nfreebats > 0) {
2536 24268687 : assert(t->freebats > 0);
2537 24268687 : i = t->freebats;
2538 24268687 : t->freebats = BBP_next(i);
2539 24268687 : assert(t->freebats == 0 || t->freebats > i);
2540 24268687 : BBP_next(i) = 0;
2541 24268687 : t->nfreebats--;
2542 : } else {
2543 0 : assert(t->nfreebats == 0);
2544 0 : assert(t->freebats == 0);
2545 : return 0;
2546 : }
2547 :
2548 : /* fill in basic BBP fields for the new bat */
2549 :
2550 24268687 : MT_lock_set(&GDKswapLock(i));
2551 24305795 : BBP_status_set(i, BBPDELETING|BBPHOT);
2552 24305795 : BBP_refs(i) = 1; /* new bats have 1 pin */
2553 24305795 : BBP_lrefs(i) = 0; /* ie. no logical refs */
2554 24305795 : BBP_pid(i) = pid;
2555 24305795 : MT_lock_unset(&GDKswapLock(i));
2556 :
2557 24359340 : if (*BBP_bak(i) == 0) {
2558 755217 : int len;
2559 755217 : len = snprintf(BBP_bak(i), sizeof(BBP_bak(i)), "tmp_%o", (unsigned) i);
2560 755217 : if (len == -1 || (size_t) len >= sizeof(BBP_bak(i))) {
2561 0 : GDKerror("impossible error\n");
2562 0 : return 0;
2563 : }
2564 : }
2565 24359340 : BBP_logical(i) = BBP_bak(i);
2566 :
2567 : /* Keep the physical location around forever */
2568 24359340 : if (!GDKinmemory(0) && *BBP_physical(i) == 0) {
2569 753620 : BBPgetfilename(BBP_physical(i), sizeof(BBP_physical(i)), i);
2570 753771 : TRC_DEBUG(BAT_, "%d = new %s(%s)\n", (int) i, BBP_logical(i), ATOMname(tt));
2571 : }
2572 :
2573 : return i;
2574 : }
2575 :
2576 : gdk_return
2577 24312653 : BBPcacheit(BAT *bn, bool lock)
2578 : {
2579 24312653 : bat i = bn->batCacheid;
2580 24312653 : unsigned mode;
2581 :
2582 24312653 : if (lock)
2583 48547980 : lock = locked_by == 0 || locked_by != MT_getpid();
2584 :
2585 24312653 : assert(i > 0);
2586 :
2587 24312653 : if (lock)
2588 24303473 : MT_lock_set(&GDKswapLock(i));
2589 24307704 : mode = (BBP_status(i) | BBPLOADED) & ~(BBPLOADING | BBPDELETING | BBPSWAPPED);
2590 :
2591 : /* cache it! */
2592 24307704 : BBP_status_set(i, mode);
2593 :
2594 24307704 : if (lock)
2595 24356068 : MT_lock_unset(&GDKswapLock(i));
2596 24299014 : return GDK_SUCCEED;
2597 : }
2598 :
2599 : /*
2600 : * BBPuncacheit changes the BBP status to swapped out. Currently only
2601 : * used in BBPfree (bat swapped out) and BBPclear (bat destroyed
2602 : * forever).
2603 : */
2604 :
2605 : static void
2606 24285629 : BBPuncacheit(bat i, bool unloaddesc)
2607 : {
2608 24285629 : if (i < 0)
2609 : i = -i;
2610 24285629 : if (BBPcheck(i)) {
2611 24283954 : BAT *b = BBP_desc(i);
2612 :
2613 24283954 : assert(unloaddesc || BBP_refs(i) == 0);
2614 :
2615 24283954 : if (BBP_status(i) & BBPLOADED) {
2616 24256425 : TRC_DEBUG(BAT_, "uncache %d (%s)\n", (int) i, BBP_logical(i));
2617 :
2618 : /* clearing bits can be done without the lock */
2619 24256425 : BBP_status_off(i, BBPLOADED);
2620 : }
2621 24283954 : if (unloaddesc) {
2622 24400626 : BATdestroy(b);
2623 : }
2624 : }
2625 24156399 : }
2626 :
2627 : /*
2628 : * @- BBPclear
2629 : * BBPclear removes a BAT from the BBP directory forever.
2630 : */
2631 : static inline void
2632 78268 : BBPhandover(struct freebats *t, uint32_t n)
2633 : {
2634 78268 : bat *p, bid;
2635 : /* take one bat from our private free list and hand it over to
2636 : * the global free list */
2637 78268 : if (n >= t->nfreebats) {
2638 42625 : bid = t->freebats;
2639 42625 : t->freebats = 0;
2640 42625 : BBP_nfree += t->nfreebats;
2641 42625 : t->nfreebats = 0;
2642 : } else {
2643 35643 : p = &t->freebats;
2644 392073 : for (uint32_t i = n; i < t->nfreebats; i++)
2645 356430 : p = &BBP_next(*p);
2646 35643 : bid = *p;
2647 35643 : *p = 0;
2648 35643 : BBP_nfree += n;
2649 35643 : t->nfreebats -= n;
2650 : }
2651 : p = &BBP_free;
2652 2041650 : while (bid != 0) {
2653 33340024 : while (*p && *p < bid)
2654 31376642 : p = &BBP_next(*p);
2655 1963382 : bat i = BBP_next(bid);
2656 1963382 : BBP_next(bid) = *p;
2657 1963382 : *p = bid;
2658 1963382 : bid = i;
2659 : }
2660 78268 : }
2661 :
2662 : #ifndef NDEBUG
2663 : extern void printlist(bat bid) __attribute__((__cold__));
2664 : /* print a bat free list, pass start of free list as argument
2665 : * to be used from the debugger */
2666 : void
2667 0 : printlist(bat bid)
2668 : {
2669 0 : int n = 0;
2670 0 : while (bid) {
2671 0 : printf("%d ", bid);
2672 0 : bid = BBP_next(bid);
2673 0 : n++;
2674 : }
2675 0 : printf("(%d)\n", n);
2676 0 : }
2677 : #endif
2678 :
2679 : static inline void
2680 23710275 : bbpclear(bat i, bool lock)
2681 : {
2682 23710275 : struct freebats *t = MT_thread_getfreebats();
2683 :
2684 23710402 : TRC_DEBUG(BAT_, "clear %d (%s)\n", (int) i, BBP_logical(i));
2685 23710402 : BBPuncacheit(i, true);
2686 23575381 : TRC_DEBUG(BAT_, "set to unloading %d\n", i);
2687 23575381 : if (lock) {
2688 23656394 : MT_lock_set(&GDKswapLock(i));
2689 : }
2690 :
2691 23687552 : BBP_status_set(i, BBPUNLOADING);
2692 23687552 : BBP_refs(i) = 0;
2693 23687552 : BBP_lrefs(i) = 0;
2694 23687552 : if (lock)
2695 23823628 : MT_lock_unset(&GDKswapLock(i));
2696 23695583 : if (!BBPtmpcheck(BBP_logical(i))) {
2697 1410 : MT_lock_set(&BBPnameLock);
2698 1410 : BBP_delete(i);
2699 1410 : MT_lock_unset(&BBPnameLock);
2700 : }
2701 23695583 : if (BBP_logical(i) != BBP_bak(i))
2702 1410 : GDKfree(BBP_logical(i));
2703 23837261 : BBP_status_set(i, 0);
2704 23837261 : BBP_logical(i) = NULL;
2705 23837261 : bat *p;
2706 72970926 : for (p = &t->freebats; *p && *p < i; p = &BBP_next(*p))
2707 : ;
2708 23837261 : BBP_next(i) = *p;
2709 23837261 : *p = i;
2710 23837261 : t->nfreebats++;
2711 23837261 : BBP_pid(i) = ~(MT_Id)0; /* not zero, not a valid thread id */
2712 23837261 : if (t->nfreebats > BBP_FREE_HIWATER) {
2713 35639 : if (lock)
2714 35639 : MT_lock_set(&GDKcacheLock);
2715 35643 : BBPhandover(t, t->nfreebats - BBP_FREE_LOWATER);
2716 35643 : if (lock)
2717 35643 : MT_lock_unset(&GDKcacheLock);
2718 : }
2719 23837265 : }
2720 :
2721 : void
2722 23727370 : BBPclear(bat i)
2723 : {
2724 23727370 : if (BBPcheck(i)) {
2725 23709251 : bool lock = locked_by == 0 || locked_by != MT_getpid();
2726 23709251 : bbpclear(i, lock);
2727 : }
2728 23810585 : }
2729 :
2730 : void
2731 46940 : BBPrelinquishbats(void)
2732 : {
2733 46940 : struct freebats *t = MT_thread_getfreebats();
2734 46989 : if (t == NULL || t->nfreebats == 0)
2735 : return;
2736 42565 : MT_lock_set(&GDKcacheLock);
2737 85250 : while (t->nfreebats > 0) {
2738 42625 : BBPhandover(t, t->nfreebats);
2739 : }
2740 42625 : MT_lock_unset(&GDKcacheLock);
2741 : }
2742 :
2743 : /*
2744 : * @- BBP rename
2745 : *
2746 : * Each BAT has a logical name that is globally unique.
2747 : * The batId is the same as the logical BAT name.
2748 : *
2749 : * The default logical name of a BAT is tmp_X, where X is the
2750 : * batCacheid. Apart from being globally unique, new logical bat
2751 : * names cannot be of the form tmp_X, unless X is the batCacheid.
2752 : *
2753 : * Physical names consist of a directory name followed by a logical
2754 : * name suffix. The directory name is derived from the batCacheid,
2755 : * and is currently organized in a hierarchy that puts max 64 bats in
2756 : * each directory (see BBPgetsubdir).
2757 : *
2758 : * Concerning the physical suffix: it is almost always bat_X. This
2759 : * saves us a whole lot of trouble, as bat_X is always unique and no
2760 : * conflicts can occur. Other suffixes are only supported in order
2761 : * just for backward compatibility with old repositories (you won't
2762 : * see them anymore in new repositories).
2763 : */
2764 : int
2765 34547 : BBPrename(BAT *b, const char *nme)
2766 : {
2767 34547 : if (b == NULL)
2768 : return 0;
2769 :
2770 34547 : bat bid = b->batCacheid;
2771 34547 : bat tmpid = 0, i;
2772 :
2773 34547 : if (nme == NULL) {
2774 11201 : if (BBP_bak(bid)[0] == 0 &&
2775 0 : snprintf(BBP_bak(bid), sizeof(BBP_bak(bid)), "tmp_%o", (unsigned) bid) >= (int) sizeof(BBP_bak(bid))) {
2776 : /* cannot happen */
2777 0 : TRC_CRITICAL(GDK, "BBP default filename too long\n");
2778 0 : return BBPRENAME_LONG;
2779 : }
2780 11201 : nme = BBP_bak(bid);
2781 : }
2782 :
2783 : /* If name stays same, do nothing */
2784 34547 : if (BBP_logical(bid) && strcmp(BBP_logical(bid), nme) == 0)
2785 : return 0;
2786 :
2787 34547 : if ((tmpid = BBPnamecheck(nme)) && tmpid != bid) {
2788 0 : GDKerror("illegal temporary name: '%s'\n", nme);
2789 0 : return BBPRENAME_ILLEGAL;
2790 : }
2791 34547 : if (strLen(nme) >= IDLENGTH) {
2792 0 : GDKerror("illegal temporary name: '%s'\n", nme);
2793 0 : return BBPRENAME_LONG;
2794 : }
2795 :
2796 34547 : MT_lock_set(&BBPnameLock);
2797 34547 : i = BBP_find(nme, false);
2798 34547 : if (i != 0) {
2799 1 : MT_lock_unset(&BBPnameLock);
2800 1 : GDKerror("name is in use: '%s'.\n", nme);
2801 1 : return BBPRENAME_ALREADY;
2802 : }
2803 :
2804 34546 : char *nnme;
2805 34546 : if (nme == BBP_bak(bid) || strcmp(nme, BBP_bak(bid)) == 0) {
2806 34546 : nnme = BBP_bak(bid);
2807 : } else {
2808 23345 : nnme = GDKstrdup(nme);
2809 23345 : if (nnme == NULL) {
2810 0 : MT_lock_unset(&BBPnameLock);
2811 0 : return BBPRENAME_MEMORY;
2812 : }
2813 : }
2814 :
2815 : /* carry through the name change */
2816 34546 : if (BBP_logical(bid) && !BBPtmpcheck(BBP_logical(bid))) {
2817 11201 : BBP_delete(bid);
2818 : }
2819 34546 : if (BBP_logical(bid) != BBP_bak(bid))
2820 11201 : GDKfree(BBP_logical(bid));
2821 34546 : BBP_logical(bid) = nnme;
2822 34546 : if (tmpid == 0) {
2823 23345 : BBP_insert(bid);
2824 : }
2825 34546 : MT_lock_set(&b->theaplock);
2826 34546 : bool transient = b->batTransient;
2827 34546 : MT_lock_unset(&b->theaplock);
2828 34546 : if (!transient) {
2829 8365 : bool lock = locked_by == 0 || locked_by != MT_getpid();
2830 :
2831 8365 : if (lock)
2832 8365 : MT_lock_set(&GDKswapLock(i));
2833 8365 : BBP_status_on(bid, BBPRENAMED);
2834 8365 : if (lock)
2835 8365 : MT_lock_unset(&GDKswapLock(i));
2836 : }
2837 34546 : MT_lock_unset(&BBPnameLock);
2838 34546 : return 0;
2839 : }
2840 :
2841 : /*
2842 : * @+ BBP swapping Policy
2843 : * The BAT can be moved back to disk using the routine BBPfree. It
2844 : * frees the storage for other BATs. After this call BAT* references
2845 : * maintained for the BAT are wrong. We should keep track of dirty
2846 : * unloaded BATs. They may have to be committed later on, which may
2847 : * include reading them in again.
2848 : *
2849 : * BBPswappable: may this bat be unloaded? Only real bats without
2850 : * memory references can be unloaded.
2851 : */
2852 : static inline void
2853 3993548 : BBPspin(bat i, const char *s, unsigned event)
2854 : {
2855 3993548 : if (BBPcheck(i) && (BBP_status(i) & event)) {
2856 : lng spin = LL_CONSTANT(0);
2857 :
2858 49 : do {
2859 49 : MT_sleep_ms(KITTENNAP);
2860 49 : spin++;
2861 49 : } while (BBP_status(i) & event);
2862 2 : TRC_DEBUG(BAT_, "%d,%s,%u: " LLFMT " loops\n", (int) i, s, event, spin);
2863 : }
2864 3993248 : }
2865 :
2866 : void
2867 12075750 : BBPcold(bat i)
2868 : {
2869 12075750 : if (!is_bat_nil(i)) {
2870 12089037 : BAT *b = BBP_desc(i);
2871 12089037 : if (b->batRole == PERSISTENT)
2872 699 : BBP_status_off(i, BBPHOT);
2873 : }
2874 12075750 : }
2875 :
2876 : /* This function can fail if the input parameter (i) is incorrect
2877 : * (unlikely). */
2878 : static inline int
2879 138228892 : incref(bat i, bool logical, bool lock)
2880 : {
2881 138228892 : int refs;
2882 138228892 : BAT *b;
2883 :
2884 138228892 : if (!BBPcheck(i))
2885 : return 0;
2886 :
2887 138383236 : if (lock) {
2888 42664459 : for (;;) {
2889 42664459 : MT_lock_set(&GDKswapLock(i));
2890 43953485 : if (!(BBP_status(i) & (BBPUNSTABLE|BBPLOADING)))
2891 : break;
2892 : /* the BATs is "unstable", try again */
2893 0 : MT_lock_unset(&GDKswapLock(i));
2894 0 : BBPspin(i, __func__, BBPUNSTABLE|BBPLOADING);
2895 : }
2896 : }
2897 : /* we have the lock */
2898 :
2899 139672262 : b = BBP_desc(i);
2900 139672262 : if (b->batCacheid == 0) {
2901 : /* should not have happened */
2902 0 : if (lock)
2903 0 : MT_lock_unset(&GDKswapLock(i));
2904 0 : return 0;
2905 : }
2906 :
2907 139672262 : assert(BBP_refs(i) + BBP_lrefs(i) ||
2908 : BBP_status(i) & (BBPDELETED | BBPSWAPPED));
2909 139672262 : if (logical) {
2910 42833452 : refs = ++BBP_lrefs(i);
2911 42833452 : BBP_pid(i) = 0;
2912 : } else {
2913 96838810 : refs = ++BBP_refs(i);
2914 96838810 : BBP_status_on(i, BBPHOT);
2915 : }
2916 139672262 : if (lock)
2917 42825342 : MT_lock_unset(&GDKswapLock(i));
2918 :
2919 : return refs;
2920 : }
2921 :
2922 : /* increment the physical reference counter for the given bat
2923 : * returns the new reference count
2924 : * also increments the physical reference count of the parent bat(s) (if
2925 : * any) */
2926 : int
2927 115191 : BBPfix(bat i)
2928 : {
2929 115191 : return BATdescriptor(i) ? 1 : 0;
2930 : }
2931 :
2932 : /* increment the logical reference count for the given bat
2933 : * returns the new reference count */
2934 : int
2935 33924078 : BBPretain(bat i)
2936 : {
2937 33924078 : bool lock = locked_by == 0 || locked_by != MT_getpid();
2938 :
2939 33924078 : return incref(i, true, lock);
2940 : }
2941 :
2942 : static inline int
2943 174056378 : decref(bat i, bool logical, bool lock, const char *func)
2944 : {
2945 174056378 : int refs = 0, lrefs;
2946 174056378 : bool swap = false;
2947 174056378 : bool locked = false;
2948 174056378 : int farmid = 0;
2949 174056378 : BAT *b;
2950 :
2951 174056378 : if (is_bat_nil(i))
2952 : return -1;
2953 160927589 : assert(i > 0);
2954 160927589 : if (BBPcheck(i) == 0)
2955 : return -1;
2956 :
2957 160824615 : if (lock)
2958 160780106 : MT_lock_set(&GDKswapLock(i));
2959 :
2960 161852802 : while (BBP_status(i) & BBPUNLOADING) {
2961 0 : if (lock)
2962 0 : MT_lock_unset(&GDKswapLock(i));
2963 0 : BBPspin(i, func, BBPUNLOADING);
2964 0 : if (lock)
2965 161852802 : MT_lock_set(&GDKswapLock(i));
2966 : }
2967 :
2968 162385529 : b = (BBP_status(i) & BBPLOADED) ? BBP_desc(i) : NULL;
2969 :
2970 : /* decrement references by one */
2971 162385529 : if (logical) {
2972 42797598 : if (BBP_lrefs(i) == 0) {
2973 0 : GDKerror("%s: %s does not have logical references.\n", func, BBP_logical(i));
2974 0 : assert(0);
2975 : } else {
2976 42797598 : refs = --BBP_lrefs(i);
2977 : }
2978 : /* cannot release last logical ref if still shared */
2979 : // but we could still have a bat iterator on it
2980 : //assert(!BATshared(BBP_desc(i)) || refs > 0);
2981 : } else {
2982 119587931 : if (BBP_refs(i) == 0) {
2983 0 : GDKerror("%s: %s does not have pointer fixes.\n", func, BBP_logical(i));
2984 0 : assert(0);
2985 : } else {
2986 119587931 : refs = --BBP_refs(i);
2987 119587931 : if (b && refs == 0) {
2988 97290914 : MT_lock_set(&b->theaplock);
2989 97603568 : locked = true;
2990 97603568 : if (VIEWtparent(b) || VIEWvtparent(b))
2991 19155386 : BBP_status_on(i, BBPHOT);
2992 : }
2993 : }
2994 : }
2995 162698183 : if (b) {
2996 161623898 : if (!locked) {
2997 65151951 : MT_lock_set(&b->theaplock);
2998 65348645 : locked = true;
2999 : }
3000 : #if 0
3001 : if (b->batCount > b->batInserted && !isVIEW(b)) {
3002 : /* if batCount is larger than batInserted and
3003 : * the dirty bits are off, it may be that a
3004 : * (sub)commit happened in parallel to an
3005 : * update; we must undo the turning off of the
3006 : * dirty bits */
3007 : if (b->theap && b->theap->parentid == i)
3008 : b->theap->dirty = true;
3009 : if (b->tvheap && b->tvheap->parentid == i)
3010 : b->tvheap->dirty = true;
3011 : }
3012 : #endif
3013 161640652 : if (b->theap)
3014 161640652 : farmid = b->theap->farmid;
3015 : }
3016 :
3017 : /* we destroy transients asap and unload persistent bats only
3018 : * if they have been made cold or are not dirty */
3019 162714937 : unsigned chkflag = BBPSYNCING;
3020 162714937 : bool swapdirty = false;
3021 162714937 : if (b) {
3022 161706700 : size_t cursize;
3023 161706700 : if ((cursize = GDKvm_cursize()) < (size_t) (GDK_vm_maxsize * 0.75)) {
3024 161675749 : if (!locked) {
3025 0 : MT_lock_set(&b->theaplock);
3026 0 : locked = true;
3027 : }
3028 161675749 : if (((b->theap ? b->theap->size : 0) + (b->tvheap ? b->tvheap->size : 0)) < (GDK_vm_maxsize - cursize) / 32)
3029 161777076 : chkflag |= BBPHOT;
3030 0 : } else if (cursize > (size_t) (GDK_vm_maxsize * 0.85))
3031 162683986 : swapdirty = true;
3032 : }
3033 : /* only consider unloading if refs is 0; if, in addition, lrefs
3034 : * is 0, we can definitely unload, else only if some more
3035 : * conditions are met */
3036 276462617 : if (BBP_refs(i) == 0 &&
3037 137579349 : (BBP_lrefs(i) == 0 ||
3038 113748434 : (b != NULL && b->theap != NULL
3039 113753545 : ? ((swapdirty || !BATdirty(b)) &&
3040 13488442 : !(BBP_status(i) & chkflag) &&
3041 10755 : (BBP_status(i) & BBPPERSISTENT) &&
3042 : /* cannot unload in-memory data */
3043 5318 : !GDKinmemory(farmid) &&
3044 : /* do not unload views or parents of views */
3045 5317 : !BATshared(b) &&
3046 113753292 : b->batCacheid == b->theap->parentid &&
3047 4858 : (b->tvheap == NULL || b->batCacheid == b->tvheap->parentid))
3048 30197 : : (BBP_status(i) & BBPTMP)))) {
3049 : /* bat will be unloaded now. set the UNLOADING bit
3050 : * while locked so no other thread thinks it's
3051 : * available anymore */
3052 23809532 : assert((BBP_status(i) & BBPUNLOADING) == 0);
3053 23809532 : TRC_DEBUG(BAT_, "%s set to unloading BAT %d (status %u, lrefs %d)\n", func, i, BBP_status(i), BBP_lrefs(i));
3054 23809532 : BBP_status_on(i, BBPUNLOADING);
3055 23809532 : swap = true;
3056 : } /* else: bat cannot be swapped out */
3057 162683986 : lrefs = BBP_lrefs(i);
3058 162683986 : if (locked)
3059 161823845 : MT_lock_unset(&b->theaplock);
3060 :
3061 : /* unlock before re-locking in unload; as saving a dirty
3062 : * persistent bat may take a long time */
3063 163477278 : if (lock)
3064 162705944 : MT_lock_unset(&GDKswapLock(i));
3065 :
3066 163754908 : if (swap) {
3067 23838065 : if (b != NULL) {
3068 23822605 : if (lrefs == 0 && (BBP_status(i) & BBPDELETED) == 0) {
3069 : /* free memory (if loaded) and delete from
3070 : * disk (if transient but saved) */
3071 23822458 : BBPdestroy(b);
3072 : } else {
3073 147 : TRC_DEBUG(BAT_, "%s unload and free bat %d\n", func, i);
3074 : /* free memory of transient */
3075 147 : if (BBPfree(b) != GDK_SUCCEED)
3076 : return -1; /* indicate failure */
3077 : }
3078 15460 : } else if (lrefs == 0 && (BBP_status(i) & BBPDELETED) == 0) {
3079 11504 : BATdelete(BBP_desc(i));
3080 11504 : BBPclear(i);
3081 : } else {
3082 3956 : BBP_status_off(i, BBPUNLOADING);
3083 : }
3084 : }
3085 : return refs;
3086 : }
3087 :
3088 : int
3089 113258453 : BBPunfix(bat i)
3090 : {
3091 113258453 : return decref(i, false, true, __func__);
3092 : }
3093 :
3094 : int
3095 55794509 : BBPrelease(bat i)
3096 : {
3097 55794509 : return decref(i, true, true, __func__);
3098 : }
3099 :
3100 : void
3101 8898658 : BBPkeepref(BAT *b)
3102 : {
3103 8898658 : assert(b != NULL);
3104 8898658 : bool lock = locked_by == 0 || locked_by != MT_getpid();
3105 8898658 : int i = b->batCacheid;
3106 8898658 : int refs = incref(i, true, lock);
3107 8964231 : if (refs == 1) {
3108 8626476 : MT_lock_set(&b->theaplock);
3109 8607116 : BATsettrivprop(b);
3110 8595794 : MT_lock_unset(&b->theaplock);
3111 : }
3112 8966735 : if (ATOMIC_GET(&GDKdebug) & CHECKMASK)
3113 8964690 : BATassertProps(b);
3114 8956363 : if (BATsetaccess(b, BAT_READ) == NULL)
3115 : return; /* already decreffed */
3116 :
3117 6536035 : refs = decref(i, false, lock, __func__);
3118 6525908 : (void) refs;
3119 6525908 : assert(refs >= 0);
3120 : }
3121 :
3122 : BAT *
3123 96582204 : BATdescriptor(bat i)
3124 : {
3125 96582204 : BAT *b = NULL;
3126 :
3127 96582204 : if (BBPcheck(i)) {
3128 96566613 : bool lock = locked_by == 0 || locked_by != MT_getpid();
3129 : if (lock) {
3130 96566613 : for (;;) {
3131 96566613 : MT_lock_set(&GDKswapLock(i));
3132 96758934 : if (!(BBP_status(i) & (BBPUNSTABLE|BBPLOADING)))
3133 : break;
3134 : /* the BATs is "unstable", try again */
3135 0 : MT_lock_unset(&GDKswapLock(i));
3136 0 : BBPspin(i, __func__, BBPUNSTABLE|BBPLOADING);
3137 : }
3138 : }
3139 96758934 : if (incref(i, false, false) > 0) {
3140 97303522 : if ((BBP_status(i) & BBPLOADED) == 0) {
3141 22898 : b = getBBPdescriptor(i);
3142 22898 : if (b == NULL) {
3143 : /* if loading failed, we need to
3144 : * compensate for the incref */
3145 0 : decref(i, false, false, __func__);
3146 : }
3147 : } else {
3148 97280624 : b = BBP_desc(i);
3149 : }
3150 : }
3151 97303522 : if (lock)
3152 97263768 : MT_lock_unset(&GDKswapLock(i));
3153 : }
3154 97371715 : return b;
3155 : }
3156 :
3157 : /*
3158 : * BBPdescriptor checks whether BAT needs loading and does so if
3159 : * necessary. You must have at least one fix on the BAT before calling
3160 : * this.
3161 : */
3162 : static BAT *
3163 22898 : getBBPdescriptor(bat i)
3164 : {
3165 22898 : bool load = false;
3166 22898 : BAT *b = NULL;
3167 :
3168 22898 : assert(i > 0);
3169 22898 : if (!BBPcheck(i)) {
3170 0 : GDKerror("BBPcheck failed for bat id %d\n", i);
3171 0 : return NULL;
3172 : }
3173 22898 : assert(BBP_refs(i));
3174 22898 : unsigned status = BBP_status(i);
3175 22898 : b = BBP_desc(i);
3176 22898 : if ((status & BBPLOADED) == 0 || status & BBPWAITING) {
3177 22898 : while (BBP_status(i) & BBPWAITING) { /* wait for bat to be loaded by other thread */
3178 0 : MT_lock_unset(&GDKswapLock(i));
3179 0 : BBPspin(i, __func__, BBPWAITING);
3180 22898 : MT_lock_set(&GDKswapLock(i));
3181 : }
3182 22898 : if (BBPvalid(i)) {
3183 22898 : if ((BBP_status(i) & BBPLOADED) == 0) {
3184 22898 : load = true;
3185 22898 : TRC_DEBUG(BAT_, "set to loading BAT %d\n", i);
3186 22898 : BBP_status_on(i, BBPLOADING);
3187 : }
3188 : }
3189 : }
3190 22898 : if (load) {
3191 22898 : TRC_DEBUG(IO_, "load %s\n", BBP_logical(i));
3192 :
3193 22898 : b = BATload_intern(i, false);
3194 :
3195 22898 : BBP_status_off(i, BBPLOADING);
3196 22898 : CHECKDEBUG if (b != NULL)
3197 21361 : BATassertProps(b);
3198 : }
3199 : return b;
3200 : }
3201 :
3202 : /*
3203 : * In BBPsave executes unlocked; it just marks the BBP_status of the
3204 : * BAT to BBPsaving, so others that want to save or unload this BAT
3205 : * must spin lock on the BBP_status field.
3206 : */
3207 : gdk_return
3208 15112 : BBPsave(BAT *b)
3209 : {
3210 15112 : bool lock = locked_by == 0 || locked_by != MT_getpid();
3211 15112 : bat bid = b->batCacheid;
3212 15112 : gdk_return ret = GDK_SUCCEED;
3213 :
3214 15112 : MT_lock_set(&b->theaplock);
3215 15112 : if (BBP_lrefs(bid) == 0 || isVIEW(b) || !BATdirty(b)) {
3216 : /* do nothing */
3217 7432 : MT_lock_unset(&b->theaplock);
3218 7432 : MT_rwlock_rdlock(&b->thashlock);
3219 7432 : if (b->thash && b->thash != (Hash *) 1 &&
3220 201 : (b->thash->heaplink.dirty || b->thash->heapbckt.dirty))
3221 123 : BAThashsave(b, (BBP_status(bid) & BBPPERSISTENT) != 0);
3222 7432 : MT_rwlock_rdunlock(&b->thashlock);
3223 7432 : return GDK_SUCCEED;
3224 : }
3225 7680 : MT_lock_unset(&b->theaplock);
3226 7680 : if (lock)
3227 7680 : MT_lock_set(&GDKswapLock(bid));
3228 :
3229 7680 : if (BBP_status(bid) & BBPSAVING) {
3230 : /* wait until save in other thread completes */
3231 0 : if (lock)
3232 0 : MT_lock_unset(&GDKswapLock(bid));
3233 0 : BBPspin(bid, __func__, BBPSAVING);
3234 : } else {
3235 : /* save it */
3236 7680 : unsigned flags = BBPSAVING;
3237 :
3238 7680 : MT_lock_set(&b->theaplock);
3239 7680 : if (DELTAdirty(b)) {
3240 59 : flags |= BBPSWAPPED;
3241 : }
3242 7680 : if (b->batTransient) {
3243 7677 : flags |= BBPTMP;
3244 : }
3245 7680 : MT_lock_unset(&b->theaplock);
3246 7680 : BBP_status_on(bid, flags);
3247 7680 : if (lock)
3248 7680 : MT_lock_unset(&GDKswapLock(bid));
3249 :
3250 7680 : TRC_DEBUG(IO_, "save " ALGOBATFMT "\n", ALGOBATPAR(b));
3251 :
3252 : /* do the time-consuming work unlocked */
3253 7680 : if (BBP_status(bid) & BBPEXISTING && b->batInserted > 0)
3254 0 : ret = BBPbackup(b, false);
3255 0 : if (ret == GDK_SUCCEED) {
3256 7680 : ret = BATsave(b);
3257 : }
3258 : /* clearing bits can be done without the lock */
3259 7680 : BBP_status_off(bid, BBPSAVING);
3260 : }
3261 : return ret;
3262 : }
3263 :
3264 : /*
3265 : * TODO merge BBPfree with BATfree? Its function is to prepare a BAT
3266 : * for being unloaded (or even destroyed, if the BAT is not
3267 : * persistent).
3268 : */
3269 : static void
3270 23808712 : BBPdestroy(BAT *b)
3271 : {
3272 23808712 : bat tp = VIEWtparent(b);
3273 23808712 : bat vtp = VIEWvtparent(b);
3274 :
3275 23808712 : if (b->theap) {
3276 23814383 : HEAPdecref(b->theap, tp == 0);
3277 23792444 : b->theap = NULL;
3278 23792444 : if (tp != 0)
3279 12034285 : BBPrelease(tp);
3280 : }
3281 23785324 : if (b->tvheap) {
3282 3811555 : HEAPdecref(b->tvheap, vtp == 0);
3283 3811887 : b->tvheap = NULL;
3284 3811887 : if (vtp != 0)
3285 2625692 : BBPrelease(vtp);
3286 : }
3287 23785474 : if (b->oldtail) {
3288 2 : ATOMIC_AND(&b->oldtail->refs, ~DELAYEDREMOVE);
3289 2 : HEAPdecref(b->oldtail, true);
3290 2 : b->oldtail = NULL;
3291 : }
3292 23785474 : BATdelete(b);
3293 :
3294 23782003 : BBPclear(b->batCacheid); /* if destroyed; de-register from BBP */
3295 23803790 : }
3296 :
3297 : static gdk_return
3298 15112 : BBPfree(BAT *b)
3299 : {
3300 15112 : bat bid = b->batCacheid;
3301 15112 : gdk_return ret;
3302 :
3303 15112 : assert(bid > 0);
3304 15112 : assert(BBPswappable(b));
3305 15112 : assert(!isVIEW(b));
3306 :
3307 15112 : BBP_unload_inc();
3308 : /* write dirty BATs before unloading */
3309 15112 : ret = BBPsave(b);
3310 15111 : if (ret == GDK_SUCCEED) {
3311 15112 : if (BBP_status(bid) & BBPLOADED)
3312 15111 : BATfree(b); /* free memory */
3313 15113 : BBPuncacheit(bid, false);
3314 : }
3315 15111 : TRC_DEBUG(BAT_, "turn off unloading %d\n", bid);
3316 15111 : BBP_status_off(bid, BBPUNLOADING);
3317 15111 : BBP_unload_dec();
3318 15112 : return ret;
3319 : }
3320 :
3321 : /*
3322 : * BBPquickdesc loads a BAT descriptor without loading the entire BAT,
3323 : * of which the result be used only for a *limited* number of
3324 : * purposes. Specifically, during the global sync/commit, we do not
3325 : * want to load any BATs that are not already loaded, both because
3326 : * this costs performance, and because getting into memory shortage
3327 : * during a commit is extremely dangerous. Loading a BAT tends not to
3328 : * be required, since the commit actions mostly involve moving some
3329 : * pointers in the BAT descriptor.
3330 : */
3331 : BAT *
3332 2094432 : BBPquickdesc(bat bid)
3333 : {
3334 2094432 : BAT *b;
3335 :
3336 2094432 : if (!BBPcheck(bid)) {
3337 175 : if (!is_bat_nil(bid)) {
3338 0 : GDKerror("called with invalid batid.\n");
3339 0 : assert(0);
3340 : }
3341 : return NULL;
3342 : }
3343 2094151 : BBPspin(bid, __func__, BBPWAITING);
3344 2094795 : b = BBP_desc(bid);
3345 2094795 : if (b->ttype < 0) {
3346 241 : const char *aname = ATOMunknown_name(b->ttype);
3347 241 : int tt = ATOMindex(aname);
3348 241 : if (tt < 0) {
3349 0 : GDKwarning("atom '%s' unknown in bat '%s'.\n",
3350 : aname, BBP_physical(bid));
3351 : } else {
3352 241 : b->ttype = tt;
3353 : }
3354 : }
3355 : return b;
3356 : }
3357 :
3358 : /*
3359 : * @+ Global Commit
3360 : */
3361 : static BAT *
3362 1907764 : dirty_bat(bat *i, bool subcommit)
3363 : {
3364 1907764 : if (BBPvalid(*i)) {
3365 1899239 : BAT *b;
3366 1899239 : BBPspin(*i, __func__, BBPSAVING);
3367 1899239 : if (BBP_status(*i) & BBPLOADED) {
3368 1821535 : b = BBP_desc(*i);
3369 1821535 : MT_lock_set(&b->theaplock);
3370 1920699 : if ((BBP_status(*i) & BBPNEW) &&
3371 99164 : BATcheckmodes(b, false) != GDK_SUCCEED) /* check mmap modes */
3372 0 : *i = -*i; /* error */
3373 1821535 : else if ((BBP_status(*i) & BBPPERSISTENT) &&
3374 0 : (subcommit || BATdirty(b))) {
3375 1641201 : MT_lock_unset(&b->theaplock);
3376 1641201 : return b; /* the bat is loaded, persistent and dirty */
3377 : }
3378 180334 : MT_lock_unset(&b->theaplock);
3379 77704 : } else if (subcommit)
3380 75170 : return BBP_desc(*i);
3381 : }
3382 : return NULL;
3383 : }
3384 :
3385 : /*
3386 : * @- backup-bat
3387 : * Backup-bat moves all files of a BAT to a backup directory. Only
3388 : * after this succeeds, it may be saved. If some failure occurs
3389 : * halfway saving, we can thus always roll back.
3390 : */
3391 : static gdk_return
3392 223251 : file_move(int farmid, const char *srcdir, const char *dstdir, const char *name, const char *ext)
3393 : {
3394 223251 : if (GDKmove(farmid, srcdir, name, ext, dstdir, name, ext, false) == GDK_SUCCEED) {
3395 : return GDK_SUCCEED;
3396 : } else {
3397 0 : char path[MAXPATH];
3398 0 : struct stat st;
3399 :
3400 0 : if (GDKfilepath(path, sizeof(path), farmid, srcdir, name, ext) != GDK_SUCCEED)
3401 0 : return GDK_FAIL;
3402 0 : if (MT_stat(path, &st)) {
3403 : /* source file does not exist; the best
3404 : * recovery is to give an error but continue
3405 : * by considering the BAT as not saved; making
3406 : * sure that this time it does get saved.
3407 : */
3408 0 : GDKsyserror("file_move: cannot stat %s\n", path);
3409 0 : return GDK_FAIL; /* fishy, but not fatal */
3410 : }
3411 : }
3412 0 : return GDK_FAIL;
3413 : }
3414 :
3415 : /* returns true if the file exists */
3416 : static bool
3417 3083993 : file_exists(int farmid, const char *dir, const char *name, const char *ext)
3418 : {
3419 3083993 : char path[MAXPATH];
3420 3083993 : struct stat st;
3421 3083993 : int ret = -1;
3422 :
3423 3083993 : if (GDKfilepath(path, sizeof(path), farmid, dir, name, ext) == GDK_SUCCEED) {
3424 3083993 : ret = MT_stat(path, &st);
3425 3083993 : TRC_DEBUG(IO_, "stat(%s) = %d\n", path, ret);
3426 : }
3427 3083993 : return (ret == 0);
3428 : }
3429 :
3430 : static gdk_return
3431 223251 : heap_move(Heap *hp, const char *srcdir, const char *dstdir, const char *nme, const char *ext)
3432 : {
3433 : /* see doc at BATsetaccess()/gdk_bat.c for an expose on mmap
3434 : * heap modes */
3435 223251 : if (file_exists(hp->farmid, dstdir, nme, ext)) {
3436 : /* dont overwrite heap with the committed state
3437 : * already in dstdir */
3438 : return GDK_SUCCEED;
3439 223251 : } else if (hp->newstorage == STORE_PRIV &&
3440 0 : !file_exists(hp->farmid, srcdir, nme, ext)) {
3441 :
3442 : /* In order to prevent half-saved X.new files
3443 : * surviving a recover we create a dummy file in the
3444 : * BACKUP(dstdir) whose presence will trigger
3445 : * BBPrecover to remove them. Thus, X will prevail
3446 : * where it otherwise wouldn't have. If X already has
3447 : * a saved X.new, that one is backed up as normal.
3448 : */
3449 :
3450 0 : FILE *fp;
3451 0 : long_str kill_ext;
3452 0 : char path[MAXPATH];
3453 :
3454 0 : strconcat_len(kill_ext, sizeof(kill_ext), ext, ".kill", NULL);
3455 0 : if (GDKfilepath(path, sizeof(path), hp->farmid, dstdir, nme, kill_ext) != GDK_SUCCEED)
3456 : return GDK_FAIL;
3457 0 : fp = MT_fopen(path, "w");
3458 0 : if (fp == NULL)
3459 0 : GDKsyserror("heap_move: cannot open file %s\n", path);
3460 0 : TRC_DEBUG(IO_, "open %s = %d\n", path, fp ? 0 : -1);
3461 :
3462 0 : if (fp != NULL) {
3463 0 : fclose(fp);
3464 0 : return GDK_SUCCEED;
3465 : } else {
3466 : return GDK_FAIL;
3467 : }
3468 : }
3469 223251 : return file_move(hp->farmid, srcdir, dstdir, nme, ext);
3470 : }
3471 :
3472 : /*
3473 : * @- BBPprepare
3474 : *
3475 : * this routine makes sure there is a BAKDIR/, and initiates one if
3476 : * not. For subcommits, it does the same with SUBDIR.
3477 : *
3478 : * It is now locked, to get proper file counters, and also to prevent
3479 : * concurrent BBPrecovers, etc.
3480 : *
3481 : * backup_dir == 0 => no backup BBP.dir
3482 : * backup_dir == 1 => BBP.dir saved in BACKUP/
3483 : * backup_dir == 2 => BBP.dir saved in SUBCOMMIT/
3484 : */
3485 :
3486 : static gdk_return
3487 24447 : BBPprepare(bool subcommit)
3488 : {
3489 24447 : bool start_subcommit;
3490 24447 : int set = 1 + subcommit;
3491 24447 : gdk_return ret = GDK_SUCCEED;
3492 :
3493 24447 : start_subcommit = (subcommit && backup_subdir == 0);
3494 12040 : if (start_subcommit) {
3495 : /* starting a subcommit. Make sure SUBDIR and DELDIR
3496 : * are clean */
3497 12040 : ret = BBPrecover_subdir();
3498 12040 : if (ret != GDK_SUCCEED)
3499 : return ret;
3500 : }
3501 24447 : if (backup_files == 0) {
3502 359 : backup_dir = 0;
3503 359 : ret = BBPrecover(0);
3504 359 : if (ret != GDK_SUCCEED)
3505 0 : return ret;
3506 359 : char bakdirpath[MAXPATH];
3507 :
3508 359 : if (GDKfilepath(bakdirpath, sizeof(bakdirpath), 0, NULL, BAKDIR, NULL) != GDK_SUCCEED) {
3509 : return GDK_FAIL;
3510 : }
3511 :
3512 359 : if (MT_mkdir(bakdirpath) < 0 && errno != EEXIST) {
3513 0 : GDKsyserror("cannot create directory %s\n", bakdirpath);
3514 0 : return GDK_FAIL;
3515 : }
3516 : /* if BAKDIR already exists, don't signal error */
3517 359 : TRC_DEBUG(IO_, "mkdir %s = %d\n", bakdirpath, (int) ret);
3518 : }
3519 24447 : if (start_subcommit) {
3520 : /* make a new SUBDIR (subdir of BAKDIR) */
3521 12040 : char subdirpath[MAXPATH];
3522 :
3523 12040 : if (GDKfilepath(subdirpath, sizeof(subdirpath), 0, NULL, SUBDIR, NULL) != GDK_SUCCEED) {
3524 0 : return GDK_FAIL;
3525 : }
3526 :
3527 12040 : if (MT_mkdir(subdirpath) < 0) {
3528 0 : GDKsyserror("cannot create directory %s\n", subdirpath);
3529 0 : return GDK_FAIL;
3530 : }
3531 12040 : TRC_DEBUG(IO_, "mkdir %s\n", subdirpath);
3532 : }
3533 24447 : if (backup_dir != set) {
3534 : /* a valid backup dir *must* at least contain BBP.dir */
3535 49237 : if ((ret = GDKmove(0, backup_dir ? BAKDIR : BATDIR, "BBP", "dir", subcommit ? SUBDIR : BAKDIR, "BBP", "dir", true)) != GDK_SUCCEED)
3536 : return ret;
3537 24439 : backup_dir = set;
3538 : }
3539 : /* increase counters */
3540 24447 : backup_subdir += subcommit;
3541 24447 : backup_files++;
3542 :
3543 24447 : return ret;
3544 : }
3545 :
3546 : static gdk_return
3547 1217271 : do_backup(Heap *h, bool dirty, bool subcommit)
3548 : {
3549 1217271 : gdk_return ret = GDK_SUCCEED;
3550 1217271 : char extnew[16];
3551 :
3552 1217271 : if (h->wasempty) {
3553 : return GDK_SUCCEED;
3554 : }
3555 :
3556 : /* direct mmap is unprotected (readonly usage, or has WAL
3557 : * protection) */
3558 1217271 : if (h->storage != STORE_MMAP) {
3559 : /* STORE_PRIV saves into X.new files. Two cases could
3560 : * happen. The first is when a valid X.new exists
3561 : * because of an access change or a previous
3562 : * commit. This X.new should be backed up as
3563 : * usual. The second case is when X.new doesn't
3564 : * exist. In that case we could have half written
3565 : * X.new files (after a crash). To protect against
3566 : * these we write X.new.kill files in the backup
3567 : * directory (see heap_move). */
3568 1207120 : gdk_return mvret = GDK_SUCCEED;
3569 1207120 : char srcdir[1025];
3570 :
3571 1207120 : if (GDKfilepath(srcdir, sizeof(srcdir), NOFARM, BATDIR, h->filename, NULL) != GDK_SUCCEED)
3572 0 : return GDK_FAIL;
3573 1207120 : char *nme = strrchr(srcdir, DIR_SEP);
3574 1207120 : assert(nme != NULL);
3575 1207120 : *nme++ = '\0';
3576 1207120 : char *ext = strchr(nme, '.');
3577 1207120 : assert(ext != NULL);
3578 1207120 : *ext++ = '\0';
3579 :
3580 1207120 : strconcat_len(extnew, sizeof(extnew), ext, ".new", NULL);
3581 1430371 : if (dirty &&
3582 446502 : !file_exists(h->farmid, BAKDIR, nme, extnew) &&
3583 223251 : !file_exists(h->farmid, BAKDIR, nme, ext)) {
3584 : /* if the heap is dirty and there is no heap
3585 : * file (with or without .new extension) in
3586 : * the BAKDIR, move the heap (preferably with
3587 : * .new extension) to the correct backup
3588 : * directory */
3589 223251 : if (file_exists(h->farmid, srcdir, nme, extnew)) {
3590 0 : mvret = heap_move(h, srcdir,
3591 : subcommit ? SUBDIR : BAKDIR,
3592 : nme, extnew);
3593 223251 : } else if (file_exists(h->farmid, srcdir, nme, ext)) {
3594 223251 : mvret = heap_move(h, srcdir,
3595 : subcommit ? SUBDIR : BAKDIR,
3596 : nme, ext);
3597 223251 : if (mvret == GDK_SUCCEED) {
3598 : /* file no longer in "standard"
3599 : * location */
3600 223251 : h->hasfile = false;
3601 : }
3602 : }
3603 983869 : } else if (subcommit) {
3604 : /* if subcommit, we may need to move an
3605 : * already made backup from BAKDIR to
3606 : * SUBDIR */
3607 983869 : if (file_exists(h->farmid, BAKDIR, nme, extnew))
3608 0 : mvret = file_move(h->farmid, BAKDIR, SUBDIR, nme, extnew);
3609 983869 : else if (file_exists(h->farmid, BAKDIR, nme, ext))
3610 0 : mvret = file_move(h->farmid, BAKDIR, SUBDIR, nme, ext);
3611 : }
3612 : /* there is a situation where the move may fail,
3613 : * namely if this heap was not supposed to be existing
3614 : * before, i.e. after a BATmaterialize on a persistent
3615 : * bat; as a workaround, do not complain about move
3616 : * failure if the source file is nonexistent
3617 : */
3618 223251 : if (mvret != GDK_SUCCEED && file_exists(h->farmid, srcdir, nme, ext)) {
3619 1207120 : ret = GDK_FAIL;
3620 : }
3621 1207120 : if (subcommit &&
3622 1207120 : (h->storage == STORE_PRIV || h->newstorage == STORE_PRIV)) {
3623 0 : long_str kill_ext;
3624 :
3625 0 : strconcat_len(kill_ext, sizeof(kill_ext),
3626 : ext, ".new.kill", NULL);
3627 0 : if (file_exists(h->farmid, BAKDIR, nme, kill_ext) &&
3628 0 : file_move(h->farmid, BAKDIR, SUBDIR, nme, kill_ext) != GDK_SUCCEED) {
3629 0 : ret = GDK_FAIL;
3630 : }
3631 : }
3632 : }
3633 : return ret;
3634 : }
3635 :
3636 : static gdk_return
3637 957611 : BBPbackup(BAT *b, bool subcommit)
3638 : {
3639 957611 : gdk_return rc = GDK_SUCCEED;
3640 :
3641 957611 : MT_lock_set(&b->theaplock);
3642 957611 : BATiter bi = bat_iterator_nolock(b);
3643 957611 : if (!bi.copiedtodisk || bi.transient) {
3644 1 : MT_lock_unset(&b->theaplock);
3645 1 : return GDK_SUCCEED;
3646 : }
3647 957610 : assert(b->theap->parentid == b->batCacheid);
3648 957610 : if (b->oldtail && b->oldtail != (Heap *) 1) {
3649 1719 : bi.h = b->oldtail;
3650 1719 : bi.hdirty = b->oldtail->dirty;
3651 : }
3652 957610 : bat_iterator_incref(&bi);
3653 957610 : MT_lock_unset(&b->theaplock);
3654 :
3655 : /* determine location dir and physical suffix */
3656 957610 : if (bi.type != TYPE_void) {
3657 957610 : rc = do_backup(bi.h, bi.hdirty, subcommit);
3658 957610 : if (rc == GDK_SUCCEED && bi.vh != NULL)
3659 259661 : rc = do_backup(bi.vh, bi.vhdirty, subcommit);
3660 : }
3661 957610 : bat_iterator_end(&bi);
3662 957610 : return rc;
3663 : }
3664 :
3665 : static inline void
3666 0 : BBPcheckHeap(Heap *h)
3667 : {
3668 0 : struct stat statb;
3669 0 : char path[MAXPATH];
3670 :
3671 0 : char *s = strrchr(h->filename, DIR_SEP);
3672 0 : if (s)
3673 0 : s++;
3674 : else
3675 : s = h->filename;
3676 0 : if (GDKfilepath(path, sizeof(path), 0, BAKDIR, s, NULL) != GDK_SUCCEED)
3677 0 : return;
3678 0 : if (MT_stat(path, &statb) < 0) {
3679 0 : if (GDKfilepath(path, sizeof(path), 0, BATDIR, h->filename, NULL) != GDK_SUCCEED)
3680 : return;
3681 0 : if (MT_stat(path, &statb) < 0) {
3682 0 : GDKsyserror("cannot stat file %s (expected size %zu)\n",
3683 : path, h->free);
3684 0 : assert(0);
3685 : return;
3686 : }
3687 : }
3688 0 : assert((statb.st_mode & S_IFMT) == S_IFREG);
3689 0 : assert((size_t) statb.st_size >= h->free);
3690 0 : if ((size_t) statb.st_size < h->free) {
3691 : GDKerror("file %s too small (expected %zu, actual %zu)\n", path, h->free, (size_t) statb.st_size);
3692 : return;
3693 : }
3694 : }
3695 :
3696 : static void
3697 0 : BBPcheckBBPdir(void)
3698 : {
3699 0 : FILE *fp;
3700 0 : int lineno = 0;
3701 0 : bat bbpsize = 0;
3702 0 : unsigned bbpversion;
3703 0 : lng logno;
3704 :
3705 0 : fp = GDKfileopen(0, BAKDIR, "BBP", "dir", "r");
3706 0 : assert(fp != NULL);
3707 0 : if (fp == NULL) {
3708 : fp = GDKfileopen(0, BATDIR, "BBP", "dir", "r");
3709 : assert(fp != NULL);
3710 : if (fp == NULL)
3711 : return;
3712 : }
3713 0 : bbpversion = BBPheader(fp, &lineno, &bbpsize, &logno, false);
3714 0 : if (bbpversion == 0) {
3715 0 : fclose(fp);
3716 0 : return; /* error reading file */
3717 : }
3718 0 : assert(bbpversion == GDKLIBRARY);
3719 :
3720 0 : for (;;) {
3721 0 : BAT b;
3722 0 : Heap h;
3723 0 : Heap vh;
3724 0 : vh = h = (Heap) {
3725 : .free = 0,
3726 : };
3727 0 : b = (BAT) {
3728 : .theap = &h,
3729 : .tvheap = &vh,
3730 : };
3731 0 : char filename[sizeof(BBP_physical(0))];
3732 0 : char batname[129];
3733 : #ifdef GDKLIBRARY_HASHASH
3734 0 : int hashash;
3735 : #endif
3736 :
3737 0 : switch (BBPreadBBPline(fp, bbpversion, &lineno, &b,
3738 : #ifdef GDKLIBRARY_HASHASH
3739 : &hashash,
3740 : #endif
3741 : batname, filename, NULL)) {
3742 0 : case 0:
3743 : /* end of file */
3744 0 : fclose(fp);
3745 : /* don't leak errors, this is just debug code */
3746 0 : GDKclrerr();
3747 0 : return;
3748 : case 1:
3749 : /* successfully read an entry */
3750 0 : break;
3751 0 : default:
3752 : /* error */
3753 0 : fclose(fp);
3754 0 : return;
3755 : }
3756 : #ifdef GDKLIBRARY_HASHASH
3757 0 : assert(hashash == 0);
3758 : #endif
3759 0 : assert(b.batCacheid < (bat) ATOMIC_GET(&BBPsize));
3760 0 : assert(b.hseqbase <= GDK_oid_max);
3761 0 : if (b.ttype == TYPE_void) {
3762 : /* no files needed */
3763 0 : continue;
3764 : }
3765 0 : if (b.theap->free > 0)
3766 0 : BBPcheckHeap(b.theap);
3767 0 : if (b.tvheap != NULL && b.tvheap->free > 0)
3768 0 : BBPcheckHeap(b.tvheap);
3769 : }
3770 : }
3771 :
3772 : /*
3773 : * @+ Atomic Write
3774 : * The atomic BBPsync() function first safeguards the old images of
3775 : * all files to be written in BAKDIR. It then saves all files. If that
3776 : * succeeds fully, BAKDIR is renamed to DELDIR. The rename is
3777 : * considered an atomic action. If it succeeds, the DELDIR is removed.
3778 : * If something fails, the pre-sync status can be obtained by moving
3779 : * back all backed up files; this is done by BBPrecover().
3780 : *
3781 : * The BBP.dir is also moved into the BAKDIR.
3782 : */
3783 : gdk_return
3784 12048 : BBPsync(int cnt, bat *restrict subcommit, BUN *restrict sizes, lng logno)
3785 : {
3786 12048 : gdk_return ret = GDK_SUCCEED;
3787 12048 : lng t0 = 0, t1 = 0;
3788 12048 : char bakdir[MAXPATH], deldir[MAXPATH];
3789 12048 : const bool lock = locked_by == 0 || locked_by != MT_getpid();
3790 12048 : char buf[3000];
3791 12048 : int n = subcommit ? 0 : -1;
3792 8 : FILE *obbpf, *nbbpf;
3793 :
3794 24096 : if (GDKfilepath(bakdir, sizeof(bakdir), 0, NULL, subcommit ? SUBDIR : BAKDIR, NULL) != GDK_SUCCEED ||
3795 12048 : GDKfilepath(deldir, sizeof(deldir), 0, NULL, DELDIR, NULL) != GDK_SUCCEED)
3796 0 : return GDK_FAIL;
3797 :
3798 12048 : TRC_DEBUG_IF(PERF) t0 = t1 = GDKusec();
3799 :
3800 12048 : if ((ATOMIC_GET(&GDKdebug) & TAILCHKMASK) && !GDKinmemory(0))
3801 0 : BBPcheckBBPdir();
3802 :
3803 12048 : ret = BBPprepare(subcommit != NULL);
3804 :
3805 12048 : if (ret == GDK_SUCCEED) {
3806 12048 : ret = BBPdir_first(subcommit != NULL, logno, &obbpf, &nbbpf);
3807 : }
3808 :
3809 1919812 : for (int idx = 1; ret == GDK_SUCCEED && idx < cnt; idx++) {
3810 1907764 : bat i = subcommit ? subcommit[idx] : idx;
3811 1907764 : BUN size = sizes ? sizes[idx] : BUN_NONE;
3812 1907764 : BATiter bi, *bip;
3813 :
3814 1907764 : const bat bid = i;
3815 1907764 : if (lock)
3816 1896705 : MT_lock_set(&GDKswapLock(bid));
3817 : /* set flag that we're syncing, i.e. that we'll
3818 : * be between moving heap to backup dir and
3819 : * saving the new version, in other words, the
3820 : * heap may not exist in the usual location */
3821 1907764 : BBP_status_on(bid, BBPSYNCING);
3822 : /* wait until unloading is finished before
3823 : * attempting to make a backup */
3824 1907764 : while (BBP_status(bid) & BBPUNLOADING) {
3825 0 : if (lock)
3826 0 : MT_lock_unset(&GDKswapLock(bid));
3827 0 : BBPspin(bid, __func__, BBPUNLOADING);
3828 0 : if (lock)
3829 1907764 : MT_lock_set(&GDKswapLock(bid));
3830 : }
3831 1907764 : BAT *b = BBP_desc(bid);
3832 1907764 : if (subcommit && b->ttype != TYPE_void) {
3833 : /* move any tail/theap files we find for this bat that
3834 : * are in the BACKUP directory to the SUBCOMMIT
3835 : * directory */
3836 1896705 : assert(b->ttype > 0); /* no unknown types allowed */
3837 1896705 : char fname[16]; /* plenty big enough */
3838 1896705 : if (snprintf(fname, sizeof(fname), "%o", (unsigned) bid) < 16) {
3839 : /* the snprintf never fails, any of the
3840 : * below may fail */
3841 1896705 : uint8_t stpe = ATOMstorage(b->ttype);
3842 3387368 : if ((b->ttype != TYPE_str || b->twidth >= 8) &&
3843 1490663 : GDKmove(0, BAKDIR, fname, "tail", SUBDIR, fname, "tail", false) == GDK_SUCCEED)
3844 0 : TRC_DEBUG(IO_, "moved %s.tail from %s to %s\n",
3845 : fname, BAKDIR, SUBDIR);
3846 2303064 : if (stpe == TYPE_str &&
3847 406359 : GDKmove(0, BAKDIR, fname, "tail1", SUBDIR, fname, "tail1", false) == GDK_SUCCEED)
3848 0 : TRC_DEBUG(IO_, "moved %s.tail1 from %s to %s\n",
3849 : fname, BAKDIR, SUBDIR);
3850 514319 : if (stpe == TYPE_str && b->twidth >= 2 &&
3851 107960 : GDKmove(0, BAKDIR, fname, "tail2", SUBDIR, fname, "tail2", false) == GDK_SUCCEED)
3852 0 : TRC_DEBUG(IO_, "moved %s.tail2 from %s to %s\n",
3853 : fname, BAKDIR, SUBDIR);
3854 : #if SIZEOF_VAR_T == 8
3855 423771 : if (stpe == TYPE_str && b->twidth >= 4 &&
3856 17412 : GDKmove(0, BAKDIR, fname, "tail4", SUBDIR, fname, "tail4", false) == GDK_SUCCEED)
3857 0 : TRC_DEBUG(IO_, "moved %s.tail4 from %s to %s\n",
3858 : fname, BAKDIR, SUBDIR);
3859 : #endif
3860 2304153 : if (ATOMvarsized(b->ttype) &&
3861 407448 : GDKmove(0, BAKDIR, fname, "theap", SUBDIR, fname, "theap", false) == GDK_SUCCEED)
3862 1896705 : TRC_DEBUG(IO_, "moved %s.theap from %s to %s\n",
3863 : fname, BAKDIR, SUBDIR);
3864 : }
3865 : }
3866 1907764 : b = dirty_bat(&i, subcommit != NULL);
3867 1907764 : if (i <= 0)
3868 : ret = GDK_FAIL;
3869 1907764 : else if (BBP_status(bid) & BBPEXISTING &&
3870 1613116 : b != NULL &&
3871 1613116 : b->batInserted > 0)
3872 957611 : ret = BBPbackup(b, subcommit != NULL);
3873 :
3874 1907764 : if (lock)
3875 1896705 : MT_lock_unset(&GDKswapLock(bid));
3876 :
3877 1907764 : if (ret != GDK_SUCCEED)
3878 : break;
3879 :
3880 1907764 : if (BBP_status(i) & BBPPERSISTENT) {
3881 1714814 : MT_lock_set(&BBP_desc(i)->theaplock);
3882 1714814 : bi = bat_iterator_nolock(BBP_desc(i));
3883 1714814 : bat_iterator_incref(&bi);
3884 1714814 : assert(sizes == NULL || size <= bi.count);
3885 1711177 : assert(sizes == NULL || bi.width == 0 || (bi.type == TYPE_msk ? ((size + 31) / 32) * 4 : size << bi.shift) <= bi.hfree);
3886 1714814 : if (size > bi.count) /* includes sizes==NULL */
3887 : size = bi.count;
3888 1714814 : bi.b->batInserted = size;
3889 1714814 : if (bi.b->ttype >= 0 && ATOMvarsized(bi.b->ttype)) {
3890 : /* see epilogue() for other part of this */
3891 : /* remember the tail we're saving */
3892 403964 : if (BATsetprop_nolock(bi.b, (enum prop_t) 20, TYPE_ptr, &bi.h) == NULL) {
3893 0 : GDKerror("setprop failed\n");
3894 0 : ret = GDK_FAIL;
3895 : } else {
3896 403964 : if (bi.b->oldtail == NULL)
3897 401871 : bi.b->oldtail = (Heap *) 1;
3898 403964 : HEAPincref(bi.h);
3899 : }
3900 : }
3901 1714814 : MT_lock_unset(&bi.b->theaplock);
3902 1714814 : if (ret == GDK_SUCCEED && b && size != 0) {
3903 : /* wait for BBPSAVING so that we
3904 : * can set it, wait for
3905 : * BBPUNLOADING before
3906 : * attempting to save */
3907 1024159 : for (;;) {
3908 1024159 : if (lock)
3909 1024159 : MT_lock_set(&GDKswapLock(i));
3910 1024159 : if (!(BBP_status(i) & (BBPSAVING|BBPUNLOADING)))
3911 : break;
3912 0 : if (lock)
3913 0 : MT_lock_unset(&GDKswapLock(i));
3914 0 : BBPspin(i, __func__, BBPSAVING|BBPUNLOADING);
3915 : }
3916 1024159 : BBP_status_on(i, BBPSAVING);
3917 1024159 : if (lock)
3918 1024159 : MT_lock_unset(&GDKswapLock(i));
3919 1024159 : ret = BATsave_iter(b, &bi, size);
3920 1024159 : BBP_status_off(i, BBPSAVING);
3921 : }
3922 : bip = &bi;
3923 : } else {
3924 : bip = NULL;
3925 : }
3926 1907764 : if (ret == GDK_SUCCEED) {
3927 1907764 : n = BBPdir_step(i, size, n, buf, sizeof(buf), &obbpf, nbbpf, bip);
3928 1907764 : if (n < -1)
3929 0 : ret = GDK_FAIL;
3930 : }
3931 1907764 : if (bip)
3932 1714814 : bat_iterator_end(bip);
3933 : /* we once again have a saved heap */
3934 : }
3935 :
3936 12048 : TRC_DEBUG(PERF, "write time "LLFMT" usec\n", (t0 = GDKusec()) - t1);
3937 :
3938 12048 : if (ret == GDK_SUCCEED) {
3939 12048 : ret = BBPdir_last(n, buf, sizeof(buf), obbpf, nbbpf);
3940 : }
3941 :
3942 12048 : TRC_DEBUG(PERF, "dir time "LLFMT" usec, %d bats\n", (t1 = GDKusec()) - t0, (bat) ATOMIC_GET(&BBPsize));
3943 :
3944 12048 : if (ret == GDK_SUCCEED) {
3945 : /* atomic switchover */
3946 : /* this is the big one: this call determines
3947 : * whether the operation of this function
3948 : * succeeded, so no changing of ret after this
3949 : * call anymore */
3950 :
3951 12048 : if (MT_rename(bakdir, deldir) < 0 &&
3952 : /* maybe there was an old deldir, so remove and try again */
3953 0 : (GDKremovedir(0, DELDIR) != GDK_SUCCEED ||
3954 0 : MT_rename(bakdir, deldir) < 0))
3955 0 : ret = GDK_FAIL;
3956 0 : if (ret != GDK_SUCCEED)
3957 0 : GDKsyserror("rename(%s,%s) failed\n", bakdir, deldir);
3958 12048 : TRC_DEBUG(IO_, "rename %s %s = %d\n", bakdir, deldir, (int) ret);
3959 : }
3960 :
3961 : /* AFTERMATH */
3962 12048 : if (ret == GDK_SUCCEED) {
3963 12048 : ATOMIC_SET(&BBPlogno, logno); /* the new value */
3964 12048 : backup_files = subcommit ? (backup_files - backup_subdir) : 0;
3965 12048 : backup_dir = backup_subdir = 0;
3966 12048 : if (GDKremovedir(0, DELDIR) != GDK_SUCCEED)
3967 0 : fprintf(stderr, "#BBPsync: cannot remove directory %s\n", DELDIR);
3968 12048 : (void) BBPprepare(false); /* (try to) remove DELDIR and set up new BAKDIR */
3969 12048 : if (backup_files > 1) {
3970 12040 : TRC_DEBUG(PERF, "backup_files %d > 1\n", backup_files);
3971 12040 : backup_files = 1;
3972 : }
3973 : }
3974 12048 : TRC_DEBUG(PERF, "%s (ready time "LLFMT" usec)\n",
3975 : ret == GDK_SUCCEED ? "" : " failed",
3976 : (t0 = GDKusec()) - t1);
3977 :
3978 12048 : if (ret != GDK_SUCCEED) {
3979 : /* clean up extra refs we created */
3980 0 : for (int idx = 1; idx < cnt; idx++) {
3981 0 : bat i = subcommit ? subcommit[idx] : idx;
3982 0 : BAT *b = BBP_desc(i);
3983 0 : if (ATOMvarsized(b->ttype)) {
3984 0 : MT_lock_set(&b->theaplock);
3985 0 : ValPtr p = BATgetprop_nolock(b, (enum prop_t) 20);
3986 0 : if (p != NULL) {
3987 0 : HEAPdecref(p->val.pval, false);
3988 0 : BATrmprop_nolock(b, (enum prop_t) 20);
3989 : }
3990 0 : MT_lock_unset(&b->theaplock);
3991 : }
3992 : }
3993 : }
3994 :
3995 : /* turn off the BBPSYNCING bits for all bats, even when things
3996 : * didn't go according to plan (i.e., don't check for ret ==
3997 : * GDK_SUCCEED) */
3998 1919812 : for (int idx = 1; idx < cnt; idx++) {
3999 1907764 : bat i = subcommit ? subcommit[idx] : idx;
4000 1907764 : BBP_status_off(i, BBPSYNCING);
4001 : }
4002 :
4003 : return ret;
4004 : }
4005 :
4006 : /*
4007 : * Recovery just moves all files back to their original location. this
4008 : * is an incremental process: if something fails, just stop with still
4009 : * files left for moving in BACKUP/. The recovery process can resume
4010 : * later with the left over files.
4011 : */
4012 : static gdk_return
4013 0 : force_move(int farmid, const char *srcdir, const char *dstdir, const char *name)
4014 : {
4015 0 : const char *p;
4016 0 : char dstpath[MAXPATH], path2[MAXPATH];
4017 0 : gdk_return ret = GDK_SUCCEED;
4018 :
4019 0 : if ((p = strrchr(name, '.')) != NULL && strcmp(p, ".kill") == 0) {
4020 : /* Found a X.new.kill file, ie remove the X.new file */
4021 0 : ptrdiff_t len = p - name;
4022 0 : long_str srcpath;
4023 :
4024 0 : strncpy(srcpath, name, len);
4025 0 : srcpath[len] = '\0';
4026 0 : if (GDKfilepath(dstpath, sizeof(dstpath), farmid, dstdir, srcpath, NULL) != GDK_SUCCEED) {
4027 : return GDK_FAIL;
4028 : }
4029 :
4030 : /* step 1: remove the X.new file that is going to be
4031 : * overridden by X */
4032 0 : if (MT_remove(dstpath) != 0 && errno != ENOENT) {
4033 : /* if it exists and cannot be removed, all
4034 : * this is going to fail */
4035 0 : GDKsyserror("force_move: remove(%s)\n", dstpath);
4036 0 : return GDK_FAIL;
4037 : }
4038 :
4039 : /* step 2: now remove the .kill file. This one is
4040 : * crucial, otherwise we'll never finish recovering */
4041 0 : if (GDKfilepath(path2, sizeof(path2), farmid, srcdir, name, NULL) != GDK_SUCCEED) {
4042 : return GDK_FAIL;
4043 : }
4044 0 : if (MT_remove(path2) != 0) {
4045 0 : ret = GDK_FAIL;
4046 0 : GDKsyserror("force_move: remove(%s)\n", path2);
4047 : }
4048 0 : return ret;
4049 : }
4050 : /* try to rename it */
4051 0 : ret = GDKmove(farmid, srcdir, name, NULL, dstdir, name, NULL, false);
4052 :
4053 0 : if (ret != GDK_SUCCEED) {
4054 0 : GDKclrerr();
4055 : /* two legal possible causes: file exists or dir
4056 : * doesn't exist */
4057 0 : if (GDKfilepath(dstpath, sizeof(dstpath), farmid, dstdir, name, NULL) != GDK_SUCCEED ||
4058 0 : GDKfilepath(path2, sizeof(path2), farmid, srcdir, name, NULL) != GDK_SUCCEED) {
4059 0 : return GDK_FAIL;
4060 : }
4061 0 : if (MT_remove(dstpath) != 0) /* clear destination */
4062 0 : ret = GDK_FAIL;
4063 0 : TRC_DEBUG(IO_, "remove %s = %d\n", dstpath, (int) ret);
4064 :
4065 0 : (void) GDKcreatedir(dstdir); /* if fails, move will fail */
4066 0 : ret = GDKmove(farmid, srcdir, name, NULL, dstdir, name, NULL, true);
4067 0 : TRC_DEBUG(IO_, "link %s %s = %d\n", path2, dstpath, (int) ret);
4068 : }
4069 : return ret;
4070 : }
4071 :
4072 : gdk_return
4073 359 : BBPrecover(int farmid)
4074 : {
4075 359 : char bakdirpath[MAXPATH];
4076 359 : char leftdirpath[MAXPATH];
4077 359 : DIR *dirp;
4078 359 : struct dirent *dent;
4079 359 : long_str path, dstpath;
4080 359 : bat i;
4081 359 : size_t j = strlen(BATDIR);
4082 359 : gdk_return ret = GDK_SUCCEED;
4083 359 : bool dirseen = false;
4084 359 : str dstdir;
4085 :
4086 718 : if (GDKfilepath(bakdirpath, sizeof(bakdirpath), farmid, NULL, BAKDIR, NULL) != GDK_SUCCEED ||
4087 359 : GDKfilepath(leftdirpath, sizeof(leftdirpath), farmid, NULL, LEFTDIR, NULL) != GDK_SUCCEED) {
4088 0 : return GDK_FAIL;
4089 : }
4090 359 : dirp = opendir(bakdirpath);
4091 359 : if (dirp == NULL) {
4092 234 : if (errno != ENOENT)
4093 0 : GDKsyserror("cannot open directory %s\n", bakdirpath);
4094 234 : return GDK_SUCCEED; /* nothing to do */
4095 : }
4096 125 : memcpy(dstpath, BATDIR, j);
4097 125 : dstpath[j] = DIR_SEP;
4098 125 : dstpath[++j] = 0;
4099 125 : dstdir = dstpath + j;
4100 125 : TRC_DEBUG(IO_, "start\n");
4101 :
4102 125 : if (MT_mkdir(leftdirpath) < 0 && errno != EEXIST) {
4103 0 : GDKsyserror("cannot create directory %s\n", leftdirpath);
4104 0 : closedir(dirp);
4105 0 : return GDK_FAIL;
4106 : }
4107 :
4108 : /* move back all files */
4109 : char fn[MAXPATH];
4110 375 : while ((dent = readdir(dirp)) != NULL) {
4111 250 : const char *q = strchr(dent->d_name, '.');
4112 :
4113 250 : if (q == dent->d_name) {
4114 250 : if (strcmp(dent->d_name, ".") == 0 ||
4115 125 : strcmp(dent->d_name, "..") == 0)
4116 250 : continue;
4117 0 : if (GDKfilepath(fn, sizeof(fn), farmid, BAKDIR, dent->d_name, NULL) == GDK_SUCCEED) {
4118 0 : int uret = MT_remove(fn);
4119 0 : TRC_DEBUG(IO_, "remove %s = %d\n",
4120 : fn, uret);
4121 : }
4122 0 : continue;
4123 0 : } else if (strcmp(dent->d_name, "BBP.dir") == 0) {
4124 0 : dirseen = true;
4125 0 : continue;
4126 : }
4127 0 : if (q == NULL)
4128 0 : q = dent->d_name + strlen(dent->d_name);
4129 0 : if ((j = q - dent->d_name) + 1 > sizeof(path)) {
4130 : /* name too long: ignore */
4131 0 : continue;
4132 : }
4133 0 : strncpy(path, dent->d_name, j);
4134 0 : path[j] = 0;
4135 0 : if (GDKisdigit(*path)) {
4136 0 : i = strtol(path, NULL, 8);
4137 : } else {
4138 0 : i = BBP_find(path, false);
4139 0 : if (i < 0)
4140 0 : i = -i;
4141 : }
4142 0 : if (i == 0 || i >= (bat) ATOMIC_GET(&BBPsize) || !BBPvalid(i)) {
4143 0 : force_move(farmid, BAKDIR, LEFTDIR, dent->d_name);
4144 : } else {
4145 0 : BBPgetsubdir(dstdir, i);
4146 0 : if (force_move(farmid, BAKDIR, dstpath, dent->d_name) != GDK_SUCCEED) {
4147 : ret = GDK_FAIL;
4148 : break;
4149 : }
4150 : /* don't trust index files after recovery */
4151 0 : GDKunlink(farmid, dstpath, path, "thashl");
4152 0 : GDKunlink(farmid, dstpath, path, "thashb");
4153 0 : GDKunlink(farmid, dstpath, path, "torderidx");
4154 0 : GDKunlink(farmid, dstpath, path, "tstrimps");
4155 : }
4156 : }
4157 125 : closedir(dirp);
4158 125 : if (dirseen && ret == GDK_SUCCEED) { /* we have a saved BBP.dir; it should be moved back!! */
4159 0 : struct stat st;
4160 :
4161 0 : if (GDKfilepath(fn, sizeof(fn), farmid, BATDIR, "BBP", "dir") != GDK_SUCCEED) {
4162 : ret = GDK_FAIL;
4163 : } else {
4164 0 : ret = recover_dir(farmid, MT_stat(fn, &st) == 0);
4165 : }
4166 : }
4167 :
4168 125 : if (ret == GDK_SUCCEED) {
4169 125 : if (MT_rmdir(bakdirpath) < 0) {
4170 0 : GDKsyserror("cannot remove directory %s\n", bakdirpath);
4171 0 : ret = GDK_FAIL;
4172 : }
4173 125 : TRC_DEBUG(IO_, "rmdir %s = %d\n", bakdirpath, (int) ret);
4174 : }
4175 125 : if (ret != GDK_SUCCEED)
4176 0 : GDKerror("recovery failed.\n");
4177 :
4178 125 : TRC_DEBUG(IO_, "end\n");
4179 : return ret;
4180 : }
4181 :
4182 : /*
4183 : * SUBDIR recovery is quite mindlessly moving all files back to the
4184 : * parent (BAKDIR). We do recognize moving back BBP.dir and set
4185 : * backed_up_subdir accordingly.
4186 : */
4187 : gdk_return
4188 12391 : BBPrecover_subdir(void)
4189 : {
4190 12391 : char subdirpath[MAXPATH];
4191 12391 : DIR *dirp;
4192 12391 : struct dirent *dent;
4193 12391 : gdk_return ret = GDK_SUCCEED;
4194 :
4195 12391 : if (GDKfilepath(subdirpath, sizeof(subdirpath), 0, NULL, SUBDIR, NULL) != GDK_SUCCEED)
4196 : return GDK_FAIL;
4197 12391 : dirp = opendir(subdirpath);
4198 12391 : if (dirp == NULL && errno != ENOENT)
4199 0 : GDKsyserror("cannot open directory %s\n", subdirpath);
4200 12391 : if (dirp == NULL) {
4201 12391 : return GDK_SUCCEED; /* nothing to do */
4202 : }
4203 0 : TRC_DEBUG(IO_, "start\n");
4204 :
4205 : /* move back all files */
4206 0 : while ((dent = readdir(dirp)) != NULL) {
4207 0 : if (dent->d_name[0] == '.')
4208 0 : continue;
4209 0 : ret = GDKmove(0, SUBDIR, dent->d_name, NULL, BAKDIR, dent->d_name, NULL, true);
4210 0 : if (ret != GDK_SUCCEED)
4211 : break;
4212 0 : if (strcmp(dent->d_name, "BBP.dir") == 0)
4213 0 : backup_dir = 1;
4214 : }
4215 0 : closedir(dirp);
4216 :
4217 : /* delete the directory */
4218 0 : if (ret == GDK_SUCCEED) {
4219 0 : ret = GDKremovedir(0, SUBDIR);
4220 0 : if (backup_dir == 2) {
4221 0 : TRC_DEBUG(IO_, "%s%cBBP.dir had disappeared!\n", SUBDIR, DIR_SEP);
4222 0 : backup_dir = 0;
4223 : }
4224 : }
4225 0 : TRC_DEBUG(IO_, "end = %d\n", (int) ret);
4226 :
4227 0 : if (ret != GDK_SUCCEED)
4228 0 : GDKerror("recovery failed.\n");
4229 : return ret;
4230 : }
4231 :
4232 : /*
4233 : * @- The diskscan
4234 : * The BBPdiskscan routine walks through the BAT dir, cleans up
4235 : * leftovers, and measures disk occupancy. Leftovers are files that
4236 : * cannot belong to a BAT. in order to establish this for [ht]heap
4237 : * files, the BAT descriptor is loaded in order to determine whether
4238 : * these files are still required.
4239 : *
4240 : * The routine gathers all bat sizes in a bat that contains bat-ids
4241 : * and bytesizes. The return value is the number of bytes of space
4242 : * freed.
4243 : */
4244 : static bool
4245 30009 : persistent_bat(bat bid)
4246 : {
4247 30009 : if (bid >= 0 && bid < (bat) ATOMIC_GET(&BBPsize) && BBPvalid(bid)) {
4248 30009 : BAT *b = BBP_desc(bid);
4249 30009 : if ((BBP_status(bid) & BBPLOADED) == 0 || b->batCopiedtodisk) {
4250 : return true;
4251 : }
4252 : }
4253 : return false;
4254 : }
4255 :
4256 : static BAT *
4257 30009 : getdesc(bat bid)
4258 : {
4259 30009 : BAT *b = NULL;
4260 :
4261 30009 : if (is_bat_nil(bid))
4262 : return NULL;
4263 30009 : assert(bid > 0);
4264 30009 : if (bid < (bat) ATOMIC_GET(&BBPsize) && BBP_logical(bid))
4265 30009 : b = BBP_desc(bid);
4266 30009 : if (b == NULL)
4267 0 : BBPclear(bid);
4268 : return b;
4269 : }
4270 :
4271 : static bool
4272 1946 : BBPdiskscan(const char *parent, size_t baseoff)
4273 : {
4274 1946 : DIR *dirp = opendir(parent);
4275 1946 : struct dirent *dent;
4276 1946 : char fullname[FILENAME_MAX];
4277 1946 : str dst;
4278 1946 : size_t dstlen;
4279 1946 : const char *src = parent;
4280 :
4281 1946 : if (dirp == NULL) {
4282 186 : if (errno != ENOENT)
4283 0 : GDKsyserror("cannot open directory %s\n", parent);
4284 186 : return true; /* nothing to do */
4285 : }
4286 :
4287 1760 : dst = stpcpy(fullname, src);
4288 1760 : if (dst > fullname && dst[-1] != DIR_SEP)
4289 1760 : *dst++ = DIR_SEP;
4290 1760 : dstlen = sizeof(fullname) - (dst - fullname);
4291 :
4292 38809 : while ((dent = readdir(dirp)) != NULL) {
4293 35289 : const char *p;
4294 35289 : bat bid;
4295 35289 : bool ok, delete;
4296 :
4297 35289 : if (dent->d_name[0] == '.')
4298 3520 : continue; /* ignore .dot files and directories (. ..) */
4299 :
4300 : #ifdef GDKLIBRARY_JSON
4301 31769 : if (strcmp(dent->d_name, "jsonupgradeneeded") == 0) {
4302 0 : continue; /* ignore json upgrade signal file */
4303 : }
4304 : #endif
4305 :
4306 31769 : if (strncmp(dent->d_name, "BBP.", 4) == 0 &&
4307 351 : (strcmp(parent + baseoff, BATDIR) == 0 ||
4308 351 : strncmp(parent + baseoff, BAKDIR, strlen(BAKDIR)) == 0 ||
4309 0 : strncmp(parent + baseoff, SUBDIR, strlen(SUBDIR)) == 0))
4310 351 : continue;
4311 :
4312 31418 : p = strchr(dent->d_name, '.');
4313 :
4314 31418 : if (strlen(dent->d_name) >= dstlen) {
4315 : /* found a file with too long a name
4316 : (i.e. unknown); stop pruning in this
4317 : subdir */
4318 0 : fprintf(stderr, "unexpected file %s, leaving %s.\n", dent->d_name, parent);
4319 0 : break;
4320 : }
4321 31418 : strncpy(dst, dent->d_name, dstlen);
4322 31418 : fullname[sizeof(fullname) - 1] = 0;
4323 :
4324 31418 : if (p == NULL && !BBPdiskscan(fullname, baseoff)) {
4325 : /* it was a directory */
4326 1409 : continue;
4327 : }
4328 :
4329 30009 : if (p && strcmp(p + 1, "tmp") == 0) {
4330 : delete = true;
4331 : ok = true;
4332 30009 : bid = 0;
4333 : } else {
4334 30009 : bid = strtol(dent->d_name, NULL, 8);
4335 30009 : ok = p && bid;
4336 30009 : delete = false;
4337 :
4338 30009 : if (!ok || !persistent_bat(bid)) {
4339 : delete = true;
4340 30009 : } else if (strncmp(p + 1, "tail", 4) == 0) {
4341 22363 : BAT *b = getdesc(bid);
4342 22363 : delete = (b == NULL || !b->ttype || !b->batCopiedtodisk || b->batCount == 0);
4343 22363 : assert(b == NULL || b->batCount > 0 || b->theap->free == 0);
4344 22363 : if (!delete) {
4345 22361 : if (b->ttype == TYPE_str) {
4346 6197 : switch (b->twidth) {
4347 3760 : case 1:
4348 3760 : delete = strcmp(p + 1, "tail1") != 0;
4349 3760 : break;
4350 2009 : case 2:
4351 2009 : delete = strcmp(p + 1, "tail2") != 0;
4352 2009 : break;
4353 : #if SIZEOF_VAR_T == 8
4354 428 : case 4:
4355 428 : delete = strcmp(p + 1, "tail4") != 0;
4356 428 : break;
4357 : #endif
4358 0 : default:
4359 0 : delete = strcmp(p + 1, "tail") != 0;
4360 0 : break;
4361 : }
4362 : } else {
4363 16164 : delete = strcmp(p + 1, "tail") != 0;
4364 : }
4365 : }
4366 7646 : } else if (strncmp(p + 1, "theap", 5) == 0) {
4367 6465 : BAT *b = getdesc(bid);
4368 6465 : delete = (b == NULL || !b->tvheap || !b->batCopiedtodisk || b->tvheap->free == 0);
4369 1181 : } else if (strncmp(p + 1, "thashl", 6) == 0 ||
4370 591 : strncmp(p + 1, "thashb", 6) == 0) {
4371 1180 : BAT *b = getdesc(bid);
4372 1180 : delete = b == NULL;
4373 1180 : if (!delete)
4374 1180 : b->thash = (Hash *) 1;
4375 1 : } else if (strncmp(p + 1, "thash", 5) == 0) {
4376 : /* older versions used .thash which we
4377 : * can simply ignore */
4378 : delete = true;
4379 1 : } else if (strncmp(p + 1, "thsh", 4) == 0) {
4380 : /* temporary hash files which we can
4381 : * simply ignore */
4382 : delete = true;
4383 1 : } else if (strncmp(p + 1, "timprints", 9) == 0) {
4384 : /* imprints have been removed */
4385 : delete = true;
4386 1 : } else if (strncmp(p + 1, "torderidx", 9) == 0) {
4387 0 : BAT *b = getdesc(bid);
4388 0 : delete = b == NULL;
4389 0 : if (!delete)
4390 0 : b->torderidx = (Heap *) 1;
4391 1 : } else if (strncmp(p + 1, "tstrimps", 8) == 0) {
4392 1 : BAT *b = getdesc(bid);
4393 1 : delete = b == NULL;
4394 1 : if (!delete)
4395 1 : b->tstrimps = (Strimps *)1;
4396 0 : } else if (strncmp(p + 1, "new", 3) != 0) {
4397 30009 : ok = false;
4398 : }
4399 : }
4400 30009 : if (!ok) {
4401 : /* found an unknown file; stop pruning in this
4402 : * subdir */
4403 0 : fprintf(stderr, "unexpected file %s, leaving %s.\n", dent->d_name, parent);
4404 0 : break;
4405 : }
4406 30009 : if (delete) {
4407 2 : if (MT_remove(fullname) != 0 && errno != ENOENT) {
4408 0 : GDKsyserror("remove(%s)", fullname);
4409 0 : continue;
4410 : }
4411 37051 : TRC_DEBUG(IO_, "remove(%s) = 0\n", fullname);
4412 : }
4413 : }
4414 1760 : closedir(dirp);
4415 1760 : return false;
4416 : }
4417 :
4418 : void
4419 350 : gdk_bbp_reset(void)
4420 : {
4421 350 : int i;
4422 :
4423 350 : BBP_free = 0;
4424 350 : BBP_nfree = 0;
4425 353 : while (BBPlimit > BBPINIT) {
4426 3 : BBPlimit -= BBPINIT;
4427 3 : assert(BBPlimit >= 0);
4428 3 : GDKfree(BBP[BBPlimit >> BBPINITLOG]);
4429 3 : BBP[BBPlimit >> BBPINITLOG] = NULL;
4430 : }
4431 350 : ATOMIC_SET(&BBPsize, 0);
4432 350 : memset(BBP0, 0, sizeof(BBP0));
4433 11550 : for (i = 0; i < MAXFARMS; i++)
4434 11200 : GDKfree((void *) BBPfarms[i].dirname); /* loose "const" */
4435 350 : memset(BBPfarms, 0, sizeof(BBPfarms));
4436 350 : memset(BBP_hash, 0, sizeof(BBP_hash));
4437 :
4438 350 : locked_by = 0;
4439 350 : BBPunloadCnt = 0;
4440 350 : backup_files = 0;
4441 350 : backup_dir = 0;
4442 350 : backup_subdir = 0;
4443 350 : }
4444 :
4445 : static MT_Lock GDKCallbackListLock = MT_LOCK_INITIALIZER(GDKCallbackListLock);
4446 :
4447 : static struct {
4448 : int cnt;
4449 : gdk_callback *head;
4450 : } callback_list = {
4451 : .cnt = 0,
4452 : .head = NULL,
4453 : };
4454 :
4455 : /*
4456 : * @- Add a callback
4457 : * Adds new callback to the callback list.
4458 : */
4459 : gdk_return
4460 0 : gdk_add_callback(const char *name, gdk_callback_func *f, int argc, void *argv[], int
4461 : interval)
4462 : {
4463 :
4464 0 : gdk_callback *callback = NULL;
4465 :
4466 0 : if (!(callback = GDKmalloc(sizeof(gdk_callback) + sizeof(void *) * argc))) {
4467 0 : TRC_CRITICAL(GDK, "Failed to allocate memory!");
4468 0 : return GDK_FAIL;
4469 : }
4470 :
4471 0 : *callback = (gdk_callback) {
4472 : .name = name,
4473 : .argc = argc,
4474 : .interval = interval,
4475 : .func = f,
4476 : };
4477 :
4478 0 : for (int i=0; i < argc; i++) {
4479 0 : callback->argv[i] = argv[i];
4480 : }
4481 :
4482 0 : MT_lock_set(&GDKCallbackListLock);
4483 0 : gdk_callback *p = callback_list.head;
4484 0 : if (p) {
4485 : int cnt = 1;
4486 0 : do {
4487 : /* check if already added */
4488 0 : if (strcmp(callback->name, p->name) == 0) {
4489 0 : MT_lock_unset(&GDKCallbackListLock);
4490 0 : GDKfree(callback);
4491 0 : return GDK_FAIL;
4492 : }
4493 0 : if (p->next == NULL) {
4494 0 : p->next = callback;
4495 0 : p = callback->next;
4496 : } else {
4497 : p = p->next;
4498 : }
4499 0 : cnt += 1;
4500 0 : } while(p);
4501 0 : callback_list.cnt = cnt;
4502 : } else {
4503 0 : callback_list.cnt = 1;
4504 0 : callback_list.head = callback;
4505 : }
4506 0 : MT_lock_unset(&GDKCallbackListLock);
4507 0 : return GDK_SUCCEED;
4508 : }
4509 :
4510 : /*
4511 : * @- Remove a callback
4512 : * Removes a callback from the callback list with a given name as an argument.
4513 : */
4514 : gdk_return
4515 0 : gdk_remove_callback(const char *cb_name, gdk_callback_func *argsfree)
4516 : {
4517 0 : gdk_callback *prev = NULL;
4518 0 : gdk_return res = GDK_FAIL;
4519 :
4520 0 : MT_lock_set(&GDKCallbackListLock);
4521 0 : gdk_callback *curr = callback_list.head;
4522 0 : while(curr) {
4523 0 : if (strcmp(cb_name, curr->name) == 0) {
4524 0 : if (curr == callback_list.head && prev == NULL) {
4525 0 : callback_list.head = curr->next;
4526 : } else {
4527 0 : prev->next = curr->next;
4528 : }
4529 0 : if (argsfree)
4530 0 : argsfree(curr->argc, curr->argv);
4531 0 : GDKfree(curr);
4532 0 : curr = NULL;
4533 0 : callback_list.cnt -=1;
4534 0 : res = GDK_SUCCEED;
4535 : } else {
4536 0 : prev = curr;
4537 0 : curr = curr->next;
4538 : }
4539 : }
4540 0 : MT_lock_unset(&GDKCallbackListLock);
4541 0 : return res;
4542 : }
4543 :
4544 : static gdk_return
4545 0 : do_callback(gdk_callback *cb)
4546 : {
4547 0 : cb->last_called = GDKusec();
4548 0 : return cb->func(cb->argc, cb->argv);
4549 : }
4550 :
4551 : static bool
4552 0 : should_call(gdk_callback *cb)
4553 : {
4554 0 : if (cb->last_called && cb->interval) {
4555 0 : return (cb->last_called + cb->interval * 1000 * 1000) <
4556 0 : GDKusec();
4557 : }
4558 : return true;
4559 : }
4560 :
4561 : static void
4562 49 : BBPcallbacks(void)
4563 : {
4564 49 : MT_lock_set(&GDKCallbackListLock);
4565 49 : gdk_callback *next = callback_list.head;
4566 :
4567 49 : while (next) {
4568 0 : if(should_call(next))
4569 0 : do_callback(next);
4570 0 : next = next->next;
4571 : }
4572 49 : MT_lock_unset(&GDKCallbackListLock);
4573 49 : }
4574 :
4575 : /* GDKtmLock protects all accesses and changes to BAKDIR and SUBDIR.
4576 : * MUST use BBPtmlock()/BBPtmunlock() to set/unset the lock.
4577 : *
4578 : * This is at the end of the file on purpose: we don't want people to
4579 : * accidentally use GDKtmLock directly. */
4580 : static MT_Lock GDKtmLock = MT_LOCK_INITIALIZER(GDKtmLock);
4581 : static int lockfd = -1;
4582 : static char lockfile[MAXPATH];
4583 :
4584 : static void
4585 67005 : BBPtmlockFinish(void)
4586 : {
4587 134010 : if (!GDKinmemory(0) &&
4588 : /* also use an external lock file to synchronize with
4589 : * external programs */
4590 67005 : GDKfilepath(lockfile, sizeof(lockfile), 0, NULL, ".tm_lock", NULL) == GDK_SUCCEED) {
4591 67005 : lockfd = MT_lockf(lockfile, F_LOCK);
4592 : }
4593 67005 : }
4594 :
4595 : void
4596 66889 : BBPtmlock(void)
4597 : {
4598 66889 : MT_lock_set(&GDKtmLock);
4599 66889 : BBPtmlockFinish();
4600 66889 : }
4601 :
4602 : static bool
4603 116 : BBPtrytmlock(int ms)
4604 : {
4605 116 : if (!MT_lock_trytime(&GDKtmLock, ms))
4606 0 : return false;
4607 116 : BBPtmlockFinish();
4608 116 : return true;
4609 : }
4610 :
4611 : void
4612 67005 : BBPtmunlock(void)
4613 : {
4614 67005 : if (lockfd >= 0) {
4615 67005 : assert(!GDKinmemory(0));
4616 67005 : MT_lockf(lockfile, F_ULOCK);
4617 67005 : close(lockfd);
4618 67005 : lockfd = -1;
4619 : }
4620 67005 : MT_lock_unset(&GDKtmLock);
4621 67005 : }
4622 :
4623 : void
4624 116 : BBPprintinfo(void)
4625 : {
4626 : /* 32 categories for the bats, not all are expected to be filled */
4627 116 : struct counters {
4628 : size_t sz;
4629 : size_t vmsz;
4630 : int nr;
4631 116 : } bats[2][2][2][2][2] = {0};
4632 116 : int nbats = 0;
4633 116 : int nskip = 0;
4634 :
4635 116 : if (!BBPtrytmlock(1000)) {
4636 0 : printf("BBP is currently locked, so no BAT information\n");
4637 0 : return;
4638 : }
4639 116 : bat sz = (bat) ATOMIC_GET(&BBPsize);
4640 394126 : for (bat i = 1; i < sz; i++) {
4641 394010 : if (!MT_lock_trytime(&GDKswapLock(i), 1000)) {
4642 0 : nskip++;
4643 0 : continue;
4644 : }
4645 394010 : int r;
4646 394010 : if ((r = BBP_refs(i)) > 0 || BBP_lrefs(i) > 0) {
4647 344053 : BAT *b = BBP_desc(i);
4648 344053 : if (!MT_lock_trytime(&b->theaplock, 1000)) {
4649 0 : nskip++;
4650 0 : b = NULL;
4651 : }
4652 0 : if (b != NULL) {
4653 344053 : nbats++;
4654 344053 : ATOMIC_BASE_TYPE status = BBP_status(i);
4655 344053 : struct counters *bt = &bats[r > 0][BATdirty(b)][(status & BBPPERSISTENT) != 0][(status & BBPLOADED) != 0][(status & BBPHOT) != 0];
4656 344053 : bt->nr++;
4657 344053 : if (b->theap && b->batCacheid == b->theap->parentid) {
4658 344053 : bt->sz += HEAPmemsize(b->theap);
4659 344053 : bt->vmsz += HEAPvmsize(b->theap);
4660 : }
4661 344053 : if (b->tvheap && b->batCacheid == b->tvheap->parentid) {
4662 11125 : bt->sz += HEAPmemsize(b->tvheap);
4663 11125 : bt->vmsz += HEAPvmsize(b->tvheap);
4664 : }
4665 344053 : MT_lock_unset(&b->theaplock);
4666 : }
4667 : }
4668 394010 : MT_lock_unset(&GDKswapLock(i));
4669 : }
4670 116 : uint32_t nfree = BBP_nfree;
4671 116 : BBPtmunlock();
4672 116 : printf("BATs:\n");
4673 116 : if (bats[1][1][1][1][1].nr > 0)
4674 116 : printf("fix, dirty, persistent, loaded, hot: %d bats, %zu virtual, %zu malloc\n", bats[1][1][1][1][1].nr, bats[1][1][1][1][1].vmsz, bats[1][1][1][1][1].sz);
4675 116 : if (bats[1][1][1][1][0].nr > 0)
4676 0 : printf("fix, dirty, persistent, not loaded, hot: %d bats, %zu virtual, %zu malloc\n", bats[1][1][1][1][0].nr, bats[1][1][1][1][0].vmsz, bats[1][1][1][1][0].sz);
4677 116 : if (bats[1][1][1][0][1].nr > 0)
4678 0 : printf("fix, dirty, persistent, loaded, cold: %d bats, %zu virtual, %zu malloc\n", bats[1][1][1][0][1].nr, bats[1][1][1][0][1].vmsz, bats[1][1][1][0][1].sz);
4679 116 : if (bats[1][1][1][0][0].nr > 0)
4680 0 : printf("fix, dirty, persistent, not loaded, cold: %d bats, %zu virtual, %zu malloc\n", bats[1][1][1][0][0].nr, bats[1][1][1][0][0].vmsz, bats[1][1][1][0][0].sz);
4681 116 : if (bats[1][1][0][1][1].nr > 0)
4682 116 : printf("fix, dirty, transient, loaded, hot: %d bats, %zu virtual, %zu malloc\n", bats[1][1][0][1][1].nr, bats[1][1][0][1][1].vmsz, bats[1][1][0][1][1].sz);
4683 116 : if (bats[1][1][0][1][0].nr > 0)
4684 1 : printf("fix, dirty, transient, not loaded, hot: %d bats, %zu virtual, %zu malloc\n", bats[1][1][0][1][0].nr, bats[1][1][0][1][0].vmsz, bats[1][1][0][1][0].sz);
4685 116 : if (bats[1][1][0][0][1].nr > 0)
4686 0 : printf("fix, dirty, transient, loaded, cold: %d bats, %zu virtual, %zu malloc\n", bats[1][1][0][0][1].nr, bats[1][1][0][0][1].vmsz, bats[1][1][0][0][1].sz);
4687 116 : if (bats[1][1][0][0][0].nr > 0)
4688 0 : printf("fix, dirty, transient, not loaded, cold: %d bats, %zu virtual, %zu malloc\n", bats[1][1][0][0][0].nr, bats[1][1][0][0][0].vmsz, bats[1][1][0][0][0].sz);
4689 116 : if (bats[1][0][1][1][1].nr > 0)
4690 106 : printf("fix, clean, persistent, loaded, hot: %d bats, %zu virtual, %zu malloc\n", bats[1][0][1][1][1].nr, bats[1][0][1][1][1].vmsz, bats[1][0][1][1][1].sz);
4691 116 : if (bats[1][0][1][1][0].nr > 0)
4692 0 : printf("fix, clean, persistent, not loaded, hot: %d bats, %zu virtual, %zu malloc\n", bats[1][0][1][1][0].nr, bats[1][0][1][1][0].vmsz, bats[1][0][1][1][0].sz);
4693 116 : if (bats[1][0][1][0][1].nr > 0)
4694 0 : printf("fix, clean, persistent, loaded, cold: %d bats, %zu virtual, %zu malloc\n", bats[1][0][1][0][1].nr, bats[1][0][1][0][1].vmsz, bats[1][0][1][0][1].sz);
4695 116 : if (bats[1][0][1][0][0].nr > 0)
4696 0 : printf("fix, clean, persistent, not loaded, cold: %d bats, %zu virtual, %zu malloc\n", bats[1][0][1][0][0].nr, bats[1][0][1][0][0].vmsz, bats[1][0][1][0][0].sz);
4697 116 : if (bats[1][0][0][1][1].nr > 0)
4698 0 : printf("fix, clean, transient, loaded, hot: %d bats, %zu virtual, %zu malloc\n", bats[1][0][0][1][1].nr, bats[1][0][0][1][1].vmsz, bats[1][0][0][1][1].sz);
4699 116 : if (bats[1][0][0][1][0].nr > 0)
4700 0 : printf("fix, clean, transient, not loaded, hot: %d bats, %zu virtual, %zu malloc\n", bats[1][0][0][1][0].nr, bats[1][0][0][1][0].vmsz, bats[1][0][0][1][0].sz);
4701 116 : if (bats[1][0][0][0][1].nr > 0)
4702 0 : printf("fix, clean, transient, loaded, cold: %d bats, %zu virtual, %zu malloc\n", bats[1][0][0][0][1].nr, bats[1][0][0][0][1].vmsz, bats[1][0][0][0][1].sz);
4703 116 : if (bats[1][0][0][0][0].nr > 0)
4704 0 : printf("fix, clean, transient, not loaded, cold: %d bats, %zu virtual, %zu malloc\n", bats[1][0][0][0][0].nr, bats[1][0][0][0][0].vmsz, bats[1][0][0][0][0].sz);
4705 116 : if (bats[0][1][1][1][1].nr > 0)
4706 115 : printf("no fix, dirty, persistent, loaded, hot: %d bats, %zu virtual, %zu malloc\n", bats[0][1][1][1][1].nr, bats[0][1][1][1][1].vmsz, bats[0][1][1][1][1].sz);
4707 116 : if (bats[0][1][1][1][0].nr > 0)
4708 16 : printf("no fix, dirty, persistent, not loaded, hot: %d bats, %zu virtual, %zu malloc\n", bats[0][1][1][1][0].nr, bats[0][1][1][1][0].vmsz, bats[0][1][1][1][0].sz);
4709 116 : if (bats[0][1][1][0][1].nr > 0)
4710 0 : printf("no fix, dirty, persistent, loaded, cold: %d bats, %zu virtual, %zu malloc\n", bats[0][1][1][0][1].nr, bats[0][1][1][0][1].vmsz, bats[0][1][1][0][1].sz);
4711 116 : if (bats[0][1][1][0][0].nr > 0)
4712 0 : printf("no fix, dirty, persistent, not loaded, cold: %d bats, %zu virtual, %zu malloc\n", bats[0][1][1][0][0].nr, bats[0][1][1][0][0].vmsz, bats[0][1][1][0][0].sz);
4713 116 : if (bats[0][1][0][1][1].nr > 0)
4714 106 : printf("no fix, dirty, transient, loaded, hot: %d bats, %zu virtual, %zu malloc\n", bats[0][1][0][1][1].nr, bats[0][1][0][1][1].vmsz, bats[0][1][0][1][1].sz);
4715 116 : if (bats[0][1][0][1][0].nr > 0)
4716 11 : printf("no fix, dirty, transient, not loaded, hot: %d bats, %zu virtual, %zu malloc\n", bats[0][1][0][1][0].nr, bats[0][1][0][1][0].vmsz, bats[0][1][0][1][0].sz);
4717 116 : if (bats[0][1][0][0][1].nr > 0)
4718 0 : printf("no fix, dirty, transient, loaded, cold: %d bats, %zu virtual, %zu malloc\n", bats[0][1][0][0][1].nr, bats[0][1][0][0][1].vmsz, bats[0][1][0][0][1].sz);
4719 116 : if (bats[0][1][0][0][0].nr > 0)
4720 6 : printf("no fix, dirty, transient, not loaded, cold: %d bats, %zu virtual, %zu malloc\n", bats[0][1][0][0][0].nr, bats[0][1][0][0][0].vmsz, bats[0][1][0][0][0].sz);
4721 116 : if (bats[0][0][1][1][1].nr > 0)
4722 116 : printf("no fix, clean, persistent, loaded, hot: %d bats, %zu virtual, %zu malloc\n", bats[0][0][1][1][1].nr, bats[0][0][1][1][1].vmsz, bats[0][0][1][1][1].sz);
4723 116 : if (bats[0][0][1][1][0].nr > 0)
4724 12 : printf("no fix, clean, persistent, not loaded, hot: %d bats, %zu virtual, %zu malloc\n", bats[0][0][1][1][0].nr, bats[0][0][1][1][0].vmsz, bats[0][0][1][1][0].sz);
4725 116 : if (bats[0][0][1][0][1].nr > 0)
4726 1 : printf("no fix, clean, persistent, loaded, cold: %d bats, %zu virtual, %zu malloc\n", bats[0][0][1][0][1].nr, bats[0][0][1][0][1].vmsz, bats[0][0][1][0][1].sz);
4727 116 : if (bats[0][0][1][0][0].nr > 0)
4728 7 : printf("no fix, clean, persistent, not loaded, cold: %d bats, %zu virtual, %zu malloc\n", bats[0][0][1][0][0].nr, bats[0][0][1][0][0].vmsz, bats[0][0][1][0][0].sz);
4729 116 : if (bats[0][0][0][1][1].nr > 0)
4730 3 : printf("no fix, clean, transient, loaded, hot: %d bats, %zu virtual, %zu malloc\n", bats[0][0][0][1][1].nr, bats[0][0][0][1][1].vmsz, bats[0][0][0][1][1].sz);
4731 116 : if (bats[0][0][0][1][0].nr > 0)
4732 0 : printf("no fix, clean, transient, not loaded, hot: %d bats, %zu virtual, %zu malloc\n", bats[0][0][0][1][0].nr, bats[0][0][0][1][0].vmsz, bats[0][0][0][1][0].sz);
4733 116 : if (bats[0][0][0][0][1].nr > 0)
4734 0 : printf("no fix, clean, transient, loaded, cold: %d bats, %zu virtual, %zu malloc\n", bats[0][0][0][0][1].nr, bats[0][0][0][0][1].vmsz, bats[0][0][0][0][1].sz);
4735 116 : if (bats[0][0][0][0][0].nr > 0)
4736 0 : printf("no fix, clean, transient, not loaded, cold: %d bats, %zu virtual, %zu malloc\n", bats[0][0][0][0][0].nr, bats[0][0][0][0][0].vmsz, bats[0][0][0][0][0].sz);
4737 :
4738 116 : printf("%d bats total, %d in use, %"PRIu32" free bats in common shared list\n",
4739 : sz - 1, nbats, nfree);
4740 116 : if (nskip > 0)
4741 0 : printf("%d bat slots unaccounted for because of locking\n", nskip);
4742 : }
|