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