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

Generated by: LCOV version 1.14