LCOV - code coverage report
Current view: top level - gdk - gdk_bbp.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 1591 2613 60.9 %
Date: 2024-12-20 21:24:02 Functions: 78 92 84.8 %

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

Generated by: LCOV version 1.14