LCOV - code coverage report
Current view: top level - gdk - gdk_bbp.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 1402 2063 68.0 %
Date: 2021-10-13 02:24:04 Functions: 79 84 94.0 %

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

Generated by: LCOV version 1.14