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

Generated by: LCOV version 1.14