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