LCOV - code coverage report
Current view: top level - gdk - gdk_bbp.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 1612 2720 59.3 %
Date: 2024-11-14 20:04:02 Functions: 78 92 84.8 %

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

Generated by: LCOV version 1.14