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