Esempio n. 1
0
/*
** Create a new PCache object.  Storage space to hold the object
** has already been allocated and is passed in as the p pointer.
*/
void sqlite3PcacheOpen(
  int szPage,                  /* Size of every page */
  int szExtra,                 /* Extra space associated with each page */
  int bPurgeable,              /* True if pages are on backing store */
  int (*xStress)(void*,PgHdr*),/* Call to try to make pages clean */
  void *pStress,               /* Argument to xStress */
  PCache *p                    /* Preallocated space for the PCache */
){
  assert( pcache_g.isInit );
  memset(p, 0, sizeof(PCache));
  p->szPage = szPage;
  p->szExtra = szExtra;
  p->bPurgeable = bPurgeable;
  p->xStress = xStress;
  p->pStress = pStress;
  p->nMax = 100;
  p->nMin = 10;

  pcacheEnterMutex();
  if( bPurgeable ){
    pcache_g.nMaxPage += p->nMax;
    pcache_g.nMinPage += p->nMin;
  }

  pcacheExitMutex();
}
Esempio n. 2
0
/*
** Drop every cache entry whose page number is greater than "pgno".
*/
void sqlite3PcacheTruncate(PCache *pCache, Pgno pgno){
  PgHdr *p, *pNext;
  PgHdr *pDirty = pCache->pDirty;
  pcacheEnterMutex();
  for(p=pCache->pClean; p||pDirty; p=pNext){
    if( !p ){
      p = pDirty;
      pDirty = 0;
    }
    pNext = p->pNext;
    if( p->pgno>pgno ){
      if( p->nRef==0 ){
        pcacheRemoveFromHash(p);
        if( p->flags&PGHDR_DIRTY ){
          pcacheRemoveFromList(&pCache->pDirty, p);
          pCache->nPinned--;
        }else{
          pcacheRemoveFromList(&pCache->pClean, p);
          pcacheRemoveFromLruList(p);
        }
        pcachePageFree(p);
      }else{
        /* If there are references to the page, it cannot be freed. In this
        ** case, zero the page content instead.
        */
        memset(p->pData, 0, pCache->szPage);
      }
    }
  }
  pcacheExitMutex();
}
Esempio n. 3
0
void *sqlite3PageMalloc(int sz){
  void *p;
  pcacheEnterMutex();
  p = pcacheMalloc(sz, 0);
  pcacheExitMutex();
  return p;
}
Esempio n. 4
0
/*
** Obtain space for a page. Try to recycle an old page if the limit on the
** number of pages has been reached. If the limit has not been reached or
** there are no pages eligible for recycling, allocate a new page.
**
** Return a pointer to the new page, or NULL if an OOM condition occurs.
*/
static int pcacheRecycleOrAlloc(PCache *pCache, PgHdr **ppPage) {
    PgHdr *p = 0;

    int szPage = pCache->szPage;
    int szExtra = pCache->szExtra;

    assert( pcache.isInit );
    assert( sqlite3_mutex_held(pcache.mutex) );

    *ppPage = 0;

    /* If we have reached the limit for pinned/dirty pages, and there is at
    ** least one dirty page, invoke the xStress callback to cause a page to
    ** become clean.
    */
    expensive_assert( pCache->nPinned==pcachePinnedCount(pCache) );
    expensive_assert( pcacheCheckSynced(pCache) );
    if( pCache->xStress
            && pCache->pDirty
            && pCache->nPinned>=(pcache.nMaxPage+pCache->nMin-pcache.nMinPage)
      ) {
        PgHdr *pPg;
        assert(pCache->pDirtyTail);

        for(pPg=pCache->pSynced;
                pPg && (pPg->nRef || (pPg->flags&PGHDR_NEED_SYNC));
                pPg=pPg->pPrev
           );
        if( !pPg ) {
            for(pPg=pCache->pDirtyTail; pPg && pPg->nRef; pPg=pPg->pPrev);
        }
        if( pPg ) {
            int rc;
            pcacheExitMutex();
            rc = pCache->xStress(pCache->pStress, pPg);
            pcacheEnterMutex();
            if( rc!=SQLITE_OK && rc!=SQLITE_BUSY ) {
                return rc;
            }
        }
    }

    /* If the global page limit has been reached, try to recycle a page. */
    if( pCache->bPurgeable && pcache.nCurrentPage>=pcache.nMaxPage ) {
        p = pcacheRecyclePage();
    }

    /* If a page has been recycled but it is the wrong size, free it. */
    if( p && (p->pCache->szPage!=szPage || p->pCache->szPage!=szExtra) ) {
        pcachePageFree(p);
        p = 0;
    }

    if( !p ) {
        p = pcachePageAlloc(pCache);
    }

    *ppPage = p;
    return (p?SQLITE_OK:SQLITE_NOMEM);
}
Esempio n. 5
0
/*
** Attempt to increase the size the hash table to contain
** at least nHash buckets.
*/
static int pcacheResizeHash(PCache *pCache, int nHash){
  PgHdr *p;
  PgHdr **pNew;
  assert( pcacheMutexHeld() );
#ifdef SQLITE_MALLOC_SOFT_LIMIT
  if( nHash*sizeof(PgHdr*)>SQLITE_MALLOC_SOFT_LIMIT ){
    nHash = SQLITE_MALLOC_SOFT_LIMIT/sizeof(PgHdr *);
  }
#endif
  pcacheExitMutex();
  pNew = (PgHdr **)sqlite3Malloc(sizeof(PgHdr*)*nHash);
  pcacheEnterMutex();
  if( !pNew ){
    return SQLITE_NOMEM;
  }
  memset(pNew, 0, sizeof(PgHdr *)*nHash);
  sqlite3_free(pCache->apHash);
  pCache->apHash = pNew;
  pCache->nHash = nHash;
  pCache->nPage = 0;
 
  for(p=pCache->pClean; p; p=p->pNext){
    pcacheAddToHash(p);
  }
  for(p=pCache->pDirty; p; p=p->pNext){
    pcacheAddToHash(p);
  }
  return SQLITE_OK;
}
Esempio n. 6
0
/*
** Allocate a page cache line.  Look in the page cache memory pool first
** and use an element from it first if available.  If nothing is available
** in the page cache memory pool, go to the general purpose memory allocator.
*/
static void *pcacheMalloc(int sz, PCache *pCache){
  assert( sqlite3_mutex_held(pcache_g.mutex) );
  if( sz<=pcache_g.szSlot && pcache_g.pFree ){
    PgFreeslot *p = pcache_g.pFree;
    pcache_g.pFree = p->pNext;
    sqlite3StatusSet(SQLITE_STATUS_PAGECACHE_SIZE, sz);
    sqlite3StatusAdd(SQLITE_STATUS_PAGECACHE_USED, 1);
    return (void*)p;
  }else{
    void *p;

    /* Allocate a new buffer using sqlite3Malloc. Before doing so, exit the
    ** global pcache mutex and unlock the pager-cache object pCache. This is 
    ** so that if the attempt to allocate a new buffer causes the the 
    ** configured soft-heap-limit to be breached, it will be possible to
    ** reclaim memory from this pager-cache.
    */
    pcacheExitMutex();
    p = sqlite3Malloc(sz);
    pcacheEnterMutex();

    if( p ){
      sz = sqlite3MallocSize(p);
      sqlite3StatusAdd(SQLITE_STATUS_PAGECACHE_OVERFLOW, sz);
    }
    return p;
  }
}
Esempio n. 7
0
/*
** Dereference a page.  When the reference count reaches zero,
** move the page to the LRU list if it is clean.
*/
void sqlite3PcacheRelease(PgHdr *p) {
    assert( p->nRef>0 );
    p->nRef--;
    if( p->nRef==0 ) {
        PCache *pCache = p->pCache;
        if( p->pCache->xDestroy ) {
            p->pCache->xDestroy(p);
        }
        pCache->nRef--;
        if( (p->flags&PGHDR_DIRTY)==0 ) {
            pCache->nPinned--;
            pcacheEnterMutex();
            if( pcache.nCurrentPage>pcache.nMaxPage ) {
                pcacheRemoveFromList(&pCache->pClean, p);
                pcacheRemoveFromHash(p);
                pcachePageFree(p);
            } else {
                pcacheAddToLruList(p);
            }
            pcacheExitMutex();
        } else {
            /* Move the page to the head of the caches dirty list. */
            pcacheRemoveFromList(&pCache->pDirty, p);
            pcacheAddToList(&pCache->pDirty, p);
        }
    }
}
Esempio n. 8
0
/*
** Make sure the page is marked as clean.  If it isn't clean already,
** make it so.
*/
void sqlite3PcacheMakeClean(PgHdr *p){
  if( (p->flags & PGHDR_DIRTY) ){
    pcacheEnterMutex();
    pcacheMakeClean(p);
    pcacheExitMutex();
  }
}
Esempio n. 9
0
/* 
** Discard the contents of the cache.
*/
int sqlite3PcacheClear(PCache *pCache){
  assert(pCache->nRef==0);
  pcacheEnterMutex();
  pcacheClear(pCache);
  pcacheExitMutex();
  return SQLITE_OK;
}
Esempio n. 10
0
/*
** Commit a change previously preserved.
*/
void sqlite3PcacheCommit(PCache *pCache, int idJournal) {
    PgHdr *p;
    pcacheEnterMutex();     /* Mutex is required to call pcacheFree() */
    for(p=pCache->pDirty; p; p=p->pNext) {
        if( p->apSave[idJournal] ) {
            pcacheFree(p->apSave[idJournal]);
            p->apSave[idJournal] = 0;
        }
    }
    pcacheExitMutex();
}
Esempio n. 11
0
/*
** Make sure the page is marked as dirty.  If it isn't dirty already,
** make it so.
*/
void sqlite3PcacheMakeDirty(PgHdr *p){
  PCache *pCache;
  p->flags &= ~PGHDR_DONT_WRITE;
  if( p->flags & PGHDR_DIRTY ) return;
  assert( (p->flags & PGHDR_DIRTY)==0 );
  assert( p->nRef>0 );
  pCache = p->pCache;
  pcacheEnterMutex();
  pcacheRemoveFromList(&pCache->pClean, p);
  pcacheAddToList(&pCache->pDirty, p);
  pcacheExitMutex();
  p->flags |= PGHDR_DIRTY;
}
Esempio n. 12
0
/*
** Drop a page from the cache. There must be exactly one reference to the
** page. This function deletes that reference, so after it returns the
** page pointed to by p is invalid.
*/
void sqlite3PcacheDrop(PgHdr *p){
  PCache *pCache;
  assert( p->nRef==1 );
  assert( 0==(p->flags&PGHDR_DIRTY) );
  pCache = p->pCache;
  pCache->nRef--;
  pCache->nPinned--;
  pcacheEnterMutex();
  pcacheRemoveFromList(&pCache->pClean, p);
  pcacheRemoveFromHash(p);
  pcachePageFree(p);
  pcacheExitMutex();
}
Esempio n. 13
0
/*
** This function is called to free superfluous dynamically allocated memory
** held by the pager system. Memory in use by any SQLite pager allocated
** by the current thread may be sqlite3_free()ed.
**
** nReq is the number of bytes of memory required. Once this much has
** been released, the function returns. The return value is the total number 
** of bytes of memory released.
*/
int sqlite3PcacheReleaseMemory(int nReq){
  int nFree = 0;
  if( pcache_g.pStart==0 ){
    PgHdr *p;
    pcacheEnterMutex();
    while( (nReq<0 || nFree<nReq) && (p=pcacheRecyclePage()) ){
      nFree += pcachePageSize(p);
      pcachePageFree(p);
    }
    pcacheExitMutex();
  }
  return nFree;
}
Esempio n. 14
0
/*
** Set the suggested cache-size value.
*/
void sqlite3PcacheSetCachesize(PCache *pCache, int mxPage){
  if( mxPage<10 ){
    mxPage = 10;
  }
  if( pCache->bPurgeable ){
    pcacheEnterMutex();
    pcache_g.nMaxPage -= pCache->nMax;
    pcache_g.nMaxPage += mxPage;
    pcacheEnforceMaxPage();
    pcacheExitMutex();
  }
  pCache->nMax = mxPage;
}
Esempio n. 15
0
/*
** Close a cache.
*/
void sqlite3PcacheClose(PCache *pCache){
  pcacheEnterMutex();

  /* Free all the pages used by this pager and remove them from the LRU list. */
  pcacheClear(pCache);
  if( pCache->bPurgeable ){
    pcache_g.nMaxPage -= pCache->nMax;
    pcache_g.nMinPage -= pCache->nMin;
    pcacheEnforceMaxPage();
  }
  sqlite3_free(pCache->apHash);
  pcacheExitMutex();
}
Esempio n. 16
0
/*
** Rollback a change previously preserved.
*/
void sqlite3PcacheRollback(PCache *pCache, int idJournal) {
    PgHdr *p;
    int sz;
    pcacheEnterMutex();     /* Mutex is required to call pcacheFree() */
    sz = pCache->szPage;
    for(p=pCache->pDirty; p; p=p->pNext) {
        if( p->apSave[idJournal] ) {
            memcpy(p->pData, p->apSave[idJournal], sz);
            pcacheFree(p->apSave[idJournal]);
            p->apSave[idJournal] = 0;
        }
    }
    pcacheExitMutex();
}
Esempio n. 17
0
/*
** Change the page number of page p to newPgno. If newPgno is 0, then the
** page object is added to the clean-list and the PGHDR_REUSE_UNLIKELY 
** flag set.
*/
void sqlite3PcacheMove(PgHdr *p, Pgno newPgno){
  assert( p->nRef>0 );
  pcacheEnterMutex();
  pcacheRemoveFromHash(p);
  p->pgno = newPgno;
  if( newPgno==0 ){
    if( (p->flags & PGHDR_DIRTY) ){
      pcacheMakeClean(p);
    }
    p->flags = PGHDR_REUSE_UNLIKELY;
  }
  pcacheAddToHash(p);
  pcacheExitMutex();
}
Esempio n. 18
0
/*
** Make every page in the cache clean.
*/
void sqlite3PcacheCleanAll(PCache *pCache){
  PgHdr *p;
  pcacheEnterMutex();
  while( (p = pCache->pDirty)!=0 ){
    pcacheRemoveFromList(&pCache->pDirty, p);
    p->flags &= ~PGHDR_DIRTY;
    pcacheAddToList(&pCache->pClean, p);
    if( p->nRef==0 ){
      pcacheAddToLruList(p);
      pCache->nPinned--;
    }
  }
  sqlite3PcacheAssertFlags(pCache, 0, PGHDR_DIRTY);
  expensive_assert( pCache->nPinned==pcachePinnedCount(pCache) );
  pcacheExitMutex();
}
Esempio n. 19
0
/* 
** Set flags on all pages in the page cache 
*/
void sqlite3PcacheClearFlags(PCache *pCache, int mask){
  PgHdr *p;

  /* Obtain the global mutex before modifying any PgHdr.flags variables 
  ** or traversing the LRU list.
  */ 
  pcacheEnterMutex();

  mask = ~mask;
  for(p=pCache->pDirty; p; p=p->pNext){
    p->flags &= mask;
  }
  for(p=pCache->pClean; p; p=p->pNext){
    p->flags &= mask;
  }

  if( 0==(mask&PGHDR_NEED_SYNC) ){
    pCache->pSynced = pCache->pDirtyTail;
    assert( !pCache->pSynced || (pCache->pSynced->flags&PGHDR_NEED_SYNC)==0 );
  }

  pcacheExitMutex();
}
Esempio n. 20
0
void sqlite3PageFree(void *p){
  pcacheEnterMutex();
  pcacheFree(p);
  pcacheExitMutex();
}
Esempio n. 21
0
/*
** Try to obtain a page from the cache.
*/
int sqlite3PcacheFetch(
  PCache *pCache,       /* Obtain the page from this cache */
  Pgno pgno,            /* Page number to obtain */
  int createFlag,       /* If true, create page if it does not exist already */
  PgHdr **ppPage        /* Write the page here */
){
  int rc = SQLITE_OK;
  PgHdr *pPage = 0;

  assert( pcache_g.isInit );
  assert( pCache!=0 );
  assert( pgno>0 );
  expensive_assert( pCache->nPinned==pcachePinnedCount(pCache) );

  pcacheEnterMutex();

  /* Search the hash table for the requested page. Exit early if it is found. */
  if( pCache->apHash ){
    u32 h = pgno % pCache->nHash;
    for(pPage=pCache->apHash[h]; pPage; pPage=pPage->pNextHash){
      if( pPage->pgno==pgno ){
        if( pPage->nRef==0 ){
          if( 0==(pPage->flags&PGHDR_DIRTY) ){
            pcacheRemoveFromLruList(pPage);
            pCache->nPinned++;
          }
          pCache->nRef++;
        }
        pPage->nRef++;
        break;
      }
    }
  }

  if( !pPage && createFlag ){
    if( pCache->nHash<=pCache->nPage ){
      rc = pcacheResizeHash(pCache, pCache->nHash<256 ? 256 : pCache->nHash*2);
    }
    if( rc==SQLITE_OK ){
      rc = pcacheRecycleOrAlloc(pCache, &pPage);
    }
    if( rc==SQLITE_OK ){
      pPage->pPager = 0;
      pPage->flags = 0;
      pPage->pDirty = 0;
      pPage->pgno = pgno;
      pPage->pCache = pCache;
      pPage->nRef = 1;
      pCache->nRef++;
      pCache->nPinned++;
      pcacheAddToList(&pCache->pClean, pPage);
      pcacheAddToHash(pPage);
    }
  }

  pcacheExitMutex();

  *ppPage = pPage;
  expensive_assert( pCache->nPinned==pcachePinnedCount(pCache) );
  assert( pPage || !createFlag || rc!=SQLITE_OK );
  return rc;
}