/ Changes On Branch branch-3.5.9
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Changes In Branch branch-3.5.9 Excluding Merge-Ins

This is equivalent to a diff from b6129f4c to bb18f578

2010-01-30
23:08
The iInUseDB and iInUseMM variables do not need to be volatile. Leaf check-in: bb18f578 user: drh tags: branch-3.5.9
22:28
Always hold the MEM2 mutex when initially marking a pager as in use by its database connection. check-in: 622c1717 user: drh tags: branch-3.5.9
2010-01-29
21:23
Fix a missing mutex on page cache truncation during vacuum and auto-vacuum when SQLITE_ENABLE_MEMORY_MANAGEMENT is engaged. check-in: ea3b941a user: drh tags: branch-3.5.9
19:46
Changes to 3.5.9 proposed by Rob Stoddard. check-in: 5ed168c4 user: drh tags: branch-3.5.9
2008-05-15
08:34
Do not write pages to disk to free memory after an IO error occurs. (CVS 5132) check-in: 10ea8287 user: danielk1977 tags: trunk
2008-05-14
16:18
Version 3.5.9 (CVS 5131) check-in: b6129f4c user: drh tags: trunk
2008-05-13
19:41
On instruction from DRH, only do malloc failure tests for O/S ops on non-Windows systems. Better test fixture code will be introduced in 3.6.0 to add this coverage back in for Windows. (CVS 5130) check-in: e4aab150 user: shane tags: trunk

Changes to src/pager.c.

397
398
399
400
401
402
403
404
405


406
407
408
409
410
411
412
...
507
508
509
510
511
512
513




514
515
516
517
518

519
520
521
522
523
524

525
526
527
528
529
530
531
532
533

534
535
536

537
538
539
540
541
542
543
...
695
696
697
698
699
700
701

702
703
704
705
706
707
708
....
1269
1270
1271
1272
1273
1274
1275

1276
1277
1278
1279
1280
1281
1282
....
1412
1413
1414
1415
1416
1417
1418

1419
1420
1421
1422
1423
1424
1425
....
2369
2370
2371
2372
2373
2374
2375

2376
2377
2378
2379
2380
2381
2382
....
2633
2634
2635
2636
2637
2638
2639

2640
2641
2642
2643
2644
2645
2646
....
2703
2704
2705
2706
2707
2708
2709

2710

2711
2712
2713
2714
2715
2716
2717
2718
2719
2720
2721
2722
2723
2724
2725
2726
2727
2728
2729
2730
2731
2732
2733
2734
2735
....
2740
2741
2742
2743
2744
2745
2746




2747
2748
2749
2750
2751
2752
2753
....
2755
2756
2757
2758
2759
2760
2761

2762
2763
2764
2765
2766
2767
2768
2769
2770
2771
2772
2773
2774
2775
2776
....
2926
2927
2928
2929
2930
2931
2932

2933
2934
2935
2936
2937
2938
2939
....
3164
3165
3166
3167
3168
3169
3170
3171
3172
3173
3174
3175
3176
3177
3178
....
3275
3276
3277
3278
3279
3280
3281
3282
3283
3284
3285
3286
3287
3288
3289
....
3300
3301
3302
3303
3304
3305
3306
3307
3308
3309
3310
3311
3312

3313
3314
3315
3316
3317
3318
3319
3320
3321
....
3613
3614
3615
3616
3617
3618
3619
3620

3621
3622


3623
3624
3625
3626
3627
3628
3629
....
3649
3650
3651
3652
3653
3654
3655

3656
3657
3658
3659
3660

3661
3662
3663
3664
3665
3666
3667
....
4700
4701
4702
4703
4704
4705
4706

4707
4708
4709
4710
4711
4712
4713
....
4827
4828
4829
4830
4831
4832
4833

4834
4835
4836
4837
4838
4839
4840
  void *pCodecArg;            /* First argument to xCodec() */
#endif
  int nHash;                  /* Size of the pager hash table */
  PgHdr **aHash;              /* Hash table to map page number to PgHdr */
#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
  Pager *pNext;               /* Doubly linked list of pagers on which */
  Pager *pPrev;               /* sqlite3_release_memory() will work */
  int iInUseMM;               /* Non-zero if unavailable to MM */
  int iInUseDB;               /* Non-zero if in sqlite3_release_memory() */


#endif
  char *pTmpSpace;            /* Pager.pageSize bytes of space for tmp use */
  char dbFileVers[16];        /* Changes whenever database file changes */
};

/*
** The following global variables hold counters used for
................................................................................
** a mutex on each pager.  The mutex is recursive.
**
** This is a special-purpose mutex.  It only provides mutual exclusion
** between the Btree and the Memory Management sqlite3_release_memory()
** function.  It does not prevent, for example, two Btrees from accessing
** the same pager at the same time.  Other general-purpose mutexes in
** the btree layer handle that chore.




*/
#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
  static void pagerEnter(Pager *p){
    p->iInUseDB++;
    if( p->iInUseMM && p->iInUseDB==1 ){

#ifndef SQLITE_MUTEX_NOOP
      sqlite3_mutex *mutex;
      mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MEM2);
#endif
      p->iInUseDB = 0;
      sqlite3_mutex_enter(mutex);

      p->iInUseDB = 1;
      sqlite3_mutex_leave(mutex);
    }
    assert( p->iInUseMM==0 );
  }
  static void pagerLeave(Pager *p){
    p->iInUseDB--;
    assert( p->iInUseDB>=0 );
  }

#else
# define pagerEnter(X)
# define pagerLeave(X)

#endif

/*
** Add page pPg to the end of the linked list managed by structure
** pList (pPg becomes the last entry in the list - the most recently 
** used). Argument pLink should point to either pPg->free or pPg->gfree,
** depending on whether pPg is being added to the pager-specific or
................................................................................
  if( aHash==0 ){
    /* Failure to rehash is not an error.  It is only a performance hit. */
    return;
  }
  sqlite3_free(pPager->aHash);
  pPager->nHash = N;
  pPager->aHash = aHash;

  for(pPg=pPager->pAll; pPg; pPg=pPg->pNextAll){
    int h;
    if( pPg->pgno==0 ){
      assert( pPg->pNextHash==0 && pPg->pPrevHash==0 );
      continue;
    }
    h = pPg->pgno & (N-1);
................................................................................
** sets the state of the pager back to what it was when it was first
** opened.  Any outstanding pages are invalidated and subsequent attempts
** to access those pages will likely result in a coredump.
*/
static void pager_reset(Pager *pPager){
  PgHdr *pPg, *pNext;
  if( pPager->errCode ) return;

  for(pPg=pPager->pAll; pPg; pPg=pNext){
    IOTRACE(("PGFREE %p %d\n", pPager, pPg->pgno));
    PAGER_INCR(sqlite3_pager_pgfree_count);
    pNext = pPg->pNextAll;
    lruListRemove(pPg);
    sqlite3_free(pPg->pData);
    sqlite3_free(pPg);
................................................................................
      pPager->journalOpen = 0;
      if( rc==SQLITE_OK ){
        rc = sqlite3OsDelete(pPager->pVfs, pPager->zJournal, 0);
      }
    }
    sqlite3BitvecDestroy(pPager->pInJournal);
    pPager->pInJournal = 0;

    for(pPg=pPager->pAll; pPg; pPg=pPg->pNextAll){
      pPg->inJournal = 0;
      pPg->dirty = 0;
      pPg->needSync = 0;
      pPg->alwaysRollback = 0;
#ifdef SQLITE_CHECK_PAGES
      pPg->pageHash = pager_pagehash(pPg);
................................................................................
    pPager->pNext = sqlite3PagerList;
    if( sqlite3PagerList ){
      assert( sqlite3PagerList->pPrev==0 );
      sqlite3PagerList->pPrev = pPager;
    }
    pPager->pPrev = 0;
    sqlite3PagerList = pPager;

    sqlite3_mutex_leave(mutex);
  }
#endif
  return SQLITE_OK;
}

/*
................................................................................
** to zero it and hope that we error out sanely.
*/
static void pager_truncate_cache(Pager *pPager){
  PgHdr *pPg;
  PgHdr **ppPg;
  int dbSize = pPager->dbSize;


  ppPg = &pPager->pAll;
  while( (pPg = *ppPg)!=0 ){
    if( pPg->pgno<=dbSize ){
      ppPg = &pPg->pNextAll;
    }else if( pPg->nRef>0 ){
      memset(PGHDR_TO_DATA(pPg), 0, pPager->pageSize);
      ppPg = &pPg->pNextAll;
................................................................................
    return rc;
  }
  if( nPage>=(unsigned)pPager->dbSize ){
    return SQLITE_OK;
  }
  if( MEMDB ){
    pPager->dbSize = nPage;

    pager_truncate_cache(pPager);

    return SQLITE_OK;
  }
  pagerEnter(pPager);
  rc = syncJournal(pPager);
  pagerLeave(pPager);
  if( rc!=SQLITE_OK ){
    return rc;
  }

  /* Get an exclusive lock on the database before truncating. */
  pagerEnter(pPager);
  rc = pager_wait_on_lock(pPager, EXCLUSIVE_LOCK);
  pagerLeave(pPager);
  if( rc!=SQLITE_OK ){
    return rc;
  }

  rc = pager_truncate(pPager, nPage);
  return rc;
}

/*
** Shutdown the page cache.  Free all memory and close all files.
**
** If a transaction was in progress when this routine is called, that
................................................................................
**
** This function always succeeds. If a transaction is active an attempt
** is made to roll it back. If an error occurs during the rollback 
** a hot journal may be left in the filesystem but no error is returned
** to the caller.
*/
int sqlite3PagerClose(Pager *pPager){




#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
  if( !MEMDB ){
#ifndef SQLITE_MUTEX_NOOP
    sqlite3_mutex *mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MEM2);
#endif
    sqlite3_mutex_enter(mutex);
    if( pPager->pPrev ){
................................................................................
    }else{
      sqlite3PagerList = pPager->pNext;
    }
    if( pPager->pNext ){
      pPager->pNext->pPrev = pPager->pPrev;
    }
    sqlite3_mutex_leave(mutex);

  }
#endif

  disable_simulated_io_errors();
  sqlite3FaultBeginBenign(-1);
  pPager->errCode = 0;
  pPager->exclusiveMode = 0;
  pager_reset(pPager);
  pagerUnlockAndRollback(pPager);
  enable_simulated_io_errors();
  sqlite3FaultEndBenign(-1);
  PAGERTRACE2("CLOSE %d\n", PAGERID(pPager));
  IOTRACE(("CLOSE %p\n", pPager))
  if( pPager->journalOpen ){
    sqlite3OsClose(pPager->jfd);
................................................................................

#ifndef NDEBUG
  /* If the Pager.needSync flag is clear then the PgHdr.needSync
  ** flag must also be clear for all pages.  Verify that this
  ** invariant is true.
  */
  else{

    for(pPg=pPager->pAll; pPg; pPg=pPg->pNextAll){
      assert( pPg->needSync==0 );
    }
    assert( pPager->lru.pFirstSynced==pPager->lru.pFirst );
  }
#endif

................................................................................
*/
static int pager_recycle(Pager *pPager, PgHdr **ppPg){
  PgHdr *pPg;
  *ppPg = 0;

  /* It is illegal to call this function unless the pager object
  ** pointed to by pPager has at least one free page (page with nRef==0).
  */ 
  assert(!MEMDB);
  assert(pPager->lru.pFirst);

  /* Find a page to recycle.  Try to locate a page that does not
  ** require us to do an fsync() on the journal.
  */
  pPg = pPager->lru.pFirstSynced;
................................................................................
  */
  for(pPager=sqlite3PagerList; pPager; pPager=pPager->pNext){
     pPager->iInUseMM = 1;
  }

  while( rc==SQLITE_OK && (nReq<0 || nReleased<nReq) ){
    PgHdr *pPg;
    PgHdr *pRecycled;
 
    /* Try to find a page to recycle that does not require a sync(). If
    ** this is not possible, find one that does require a sync().
    */
    sqlite3_mutex_enter(sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_LRU));
    pPg = sqlite3LruPageList.pFirstSynced;
    while( pPg && (pPg->needSync || pPg->pPager->iInUseDB) ){
................................................................................
    /* If pPg==0, then the block above has failed to find a page to
    ** recycle. In this case return early - no further memory will
    ** be released.
    */
    if( !pPg ) break;

    pPager = pPg->pPager;
    assert(!pPg->needSync || pPg==pPager->lru.pFirst);
    assert(pPg->needSync || pPg==pPager->lru.pFirstSynced);
  
    savedBusy = pPager->pBusyHandler;
    pPager->pBusyHandler = 0;
    rc = pager_recycle(pPager, &pRecycled);

    pPager->pBusyHandler = savedBusy;
    assert(pRecycled==pPg || rc!=SQLITE_OK);
    if( rc==SQLITE_OK ){
      /* We've found a page to free. At this point the page has been 
      ** removed from the page hash-table, free-list and synced-list 
      ** (pFirstSynced). It is still in the all pages (pAll) list. 
      ** Remove it from this list before freeing.
      **
      ** Todo: Check the Pager.pStmt list to make sure this is Ok. It 
................................................................................
**          to be synced to disk or else disk syncing is currently
**          allowed.
*/
static int pagerAllocatePage(Pager *pPager, PgHdr **ppPg){
  int rc = SQLITE_OK;
  PgHdr *pPg;
  int nByteHdr;


  /* Create a new PgHdr if any of the four conditions defined 
  ** above are met: */


  if( pPager->nPage<pPager->mxPage
   || pPager->lru.pFirst==0 
   || MEMDB
   || (pPager->lru.pFirstSynced==0 && pPager->doNotSync)
  ){
    void *pData;
    if( pPager->nPage>=pPager->nHash ){
................................................................................
    if( pPg==0 ){
      rc = SQLITE_NOMEM;
      goto pager_allocate_out;
    }
    memset(pPg, 0, nByteHdr);
    pPg->pData = pData;
    pPg->pPager = pPager;

    pPg->pNextAll = pPager->pAll;
    pPager->pAll = pPg;
    pPager->nPage++;
  }else{
    /* Recycle an existing page with a zero ref-count. */

    rc = pager_recycle(pPager, &pPg);
    if( rc==SQLITE_BUSY ){
      rc = SQLITE_IOERR_BLOCKED;
    }
    if( rc!=SQLITE_OK ){
      goto pager_allocate_out;
    }
................................................................................
            if( rc!=SQLITE_OK ) goto sync_exit;
          }
        } 
      }
#endif
      rc = writeMasterJournal(pPager, zMaster);
      if( rc!=SQLITE_OK ) goto sync_exit;

      rc = syncJournal(pPager);
    }
    if( rc!=SQLITE_OK ) goto sync_exit;

#ifndef SQLITE_OMIT_AUTOVACUUM
    if( nTrunc!=0 ){
      rc = sqlite3PagerTruncate(pPager, nTrunc);
................................................................................
** SQLITE_OK is returned.
*/
int sqlite3PagerRollback(Pager *pPager){
  int rc;
  PAGERTRACE2("ROLLBACK %d\n", PAGERID(pPager));
  if( MEMDB ){
    PgHdr *p;

    for(p=pPager->pAll; p; p=p->pNextAll){
      PgHistory *pHist;
      assert( !p->alwaysRollback );
      if( !p->dirty ){
        assert( !((PgHistory *)PGHDR_TO_HIST(p, pPager))->pOrig );
        assert( !((PgHistory *)PGHDR_TO_HIST(p, pPager))->pStmt );
        continue;







<

>
>







 







>
>
>
>



|
|
>




<

>



<





>



>







 







>







 







>







 







>







 







>







 







>







 







>

>












|
|
|
|
<
<







 







>
>
>
>







 







>


<


<

<







 







>







 







|







 







<







 







|
<
<


|
>

<







 








>

|
>
>







 







>





>







 







>







 







>







397
398
399
400
401
402
403

404
405
406
407
408
409
410
411
412
413
...
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528

529
530
531
532
533

534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
...
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
....
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
....
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
....
2379
2380
2381
2382
2383
2384
2385
2386
2387
2388
2389
2390
2391
2392
2393
....
2644
2645
2646
2647
2648
2649
2650
2651
2652
2653
2654
2655
2656
2657
2658
....
2715
2716
2717
2718
2719
2720
2721
2722
2723
2724
2725
2726
2727
2728
2729
2730
2731
2732
2733
2734
2735
2736
2737
2738
2739
2740


2741
2742
2743
2744
2745
2746
2747
....
2752
2753
2754
2755
2756
2757
2758
2759
2760
2761
2762
2763
2764
2765
2766
2767
2768
2769
....
2771
2772
2773
2774
2775
2776
2777
2778
2779
2780

2781
2782

2783

2784
2785
2786
2787
2788
2789
2790
....
2940
2941
2942
2943
2944
2945
2946
2947
2948
2949
2950
2951
2952
2953
2954
....
3179
3180
3181
3182
3183
3184
3185
3186
3187
3188
3189
3190
3191
3192
3193
....
3290
3291
3292
3293
3294
3295
3296

3297
3298
3299
3300
3301
3302
3303
....
3314
3315
3316
3317
3318
3319
3320
3321


3322
3323
3324
3325
3326

3327
3328
3329
3330
3331
3332
3333
....
3625
3626
3627
3628
3629
3630
3631
3632
3633
3634
3635
3636
3637
3638
3639
3640
3641
3642
3643
3644
....
3664
3665
3666
3667
3668
3669
3670
3671
3672
3673
3674
3675
3676
3677
3678
3679
3680
3681
3682
3683
3684
....
4717
4718
4719
4720
4721
4722
4723
4724
4725
4726
4727
4728
4729
4730
4731
....
4845
4846
4847
4848
4849
4850
4851
4852
4853
4854
4855
4856
4857
4858
4859
  void *pCodecArg;            /* First argument to xCodec() */
#endif
  int nHash;                  /* Size of the pager hash table */
  PgHdr **aHash;              /* Hash table to map page number to PgHdr */
#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
  Pager *pNext;               /* Doubly linked list of pagers on which */
  Pager *pPrev;               /* sqlite3_release_memory() will work */

  int iInUseDB;               /* Non-zero if in sqlite3_release_memory() */
  u8 iInUseMM;                /* Non-zero if unavailable to MM */
  u8 onPagerList;             /* True if part of the sqlite3PagerList */
#endif
  char *pTmpSpace;            /* Pager.pageSize bytes of space for tmp use */
  char dbFileVers[16];        /* Changes whenever database file changes */
};

/*
** The following global variables hold counters used for
................................................................................
** a mutex on each pager.  The mutex is recursive.
**
** This is a special-purpose mutex.  It only provides mutual exclusion
** between the Btree and the Memory Management sqlite3_release_memory()
** function.  It does not prevent, for example, two Btrees from accessing
** the same pager at the same time.  Other general-purpose mutexes in
** the btree layer handle that chore.
**
** The pagerMutexHeld(X) macro is for sanity checking.  This macro verifies
** that the database-connection mutex is held for pager X and asserts if it
** is not.
*/
#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
  static void pagerEnter(Pager *p){
    if( p->iInUseDB>0 ){
      p->iInUseDB++;
    }else{
#ifndef SQLITE_MUTEX_NOOP
      sqlite3_mutex *mutex;
      mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MEM2);
#endif

      sqlite3_mutex_enter(mutex);
      assert( p->iInUseMM==0 );
      p->iInUseDB = 1;
      sqlite3_mutex_leave(mutex);
    }

  }
  static void pagerLeave(Pager *p){
    p->iInUseDB--;
    assert( p->iInUseDB>=0 );
  }
# define pagerMutexHeld(X)  assert( (X)->iInUseDB>0 || !(X)->onPagerList )
#else
# define pagerEnter(X)
# define pagerLeave(X)
# define pagerMutexHeld(X)
#endif

/*
** Add page pPg to the end of the linked list managed by structure
** pList (pPg becomes the last entry in the list - the most recently 
** used). Argument pLink should point to either pPg->free or pPg->gfree,
** depending on whether pPg is being added to the pager-specific or
................................................................................
  if( aHash==0 ){
    /* Failure to rehash is not an error.  It is only a performance hit. */
    return;
  }
  sqlite3_free(pPager->aHash);
  pPager->nHash = N;
  pPager->aHash = aHash;
  pagerMutexHeld(pPager);
  for(pPg=pPager->pAll; pPg; pPg=pPg->pNextAll){
    int h;
    if( pPg->pgno==0 ){
      assert( pPg->pNextHash==0 && pPg->pPrevHash==0 );
      continue;
    }
    h = pPg->pgno & (N-1);
................................................................................
** sets the state of the pager back to what it was when it was first
** opened.  Any outstanding pages are invalidated and subsequent attempts
** to access those pages will likely result in a coredump.
*/
static void pager_reset(Pager *pPager){
  PgHdr *pPg, *pNext;
  if( pPager->errCode ) return;
  pagerMutexHeld(pPager);
  for(pPg=pPager->pAll; pPg; pPg=pNext){
    IOTRACE(("PGFREE %p %d\n", pPager, pPg->pgno));
    PAGER_INCR(sqlite3_pager_pgfree_count);
    pNext = pPg->pNextAll;
    lruListRemove(pPg);
    sqlite3_free(pPg->pData);
    sqlite3_free(pPg);
................................................................................
      pPager->journalOpen = 0;
      if( rc==SQLITE_OK ){
        rc = sqlite3OsDelete(pPager->pVfs, pPager->zJournal, 0);
      }
    }
    sqlite3BitvecDestroy(pPager->pInJournal);
    pPager->pInJournal = 0;
    pagerMutexHeld(pPager);
    for(pPg=pPager->pAll; pPg; pPg=pPg->pNextAll){
      pPg->inJournal = 0;
      pPg->dirty = 0;
      pPg->needSync = 0;
      pPg->alwaysRollback = 0;
#ifdef SQLITE_CHECK_PAGES
      pPg->pageHash = pager_pagehash(pPg);
................................................................................
    pPager->pNext = sqlite3PagerList;
    if( sqlite3PagerList ){
      assert( sqlite3PagerList->pPrev==0 );
      sqlite3PagerList->pPrev = pPager;
    }
    pPager->pPrev = 0;
    sqlite3PagerList = pPager;
    pPager->onPagerList = 1;
    sqlite3_mutex_leave(mutex);
  }
#endif
  return SQLITE_OK;
}

/*
................................................................................
** to zero it and hope that we error out sanely.
*/
static void pager_truncate_cache(Pager *pPager){
  PgHdr *pPg;
  PgHdr **ppPg;
  int dbSize = pPager->dbSize;

  pagerMutexHeld(pPager);
  ppPg = &pPager->pAll;
  while( (pPg = *ppPg)!=0 ){
    if( pPg->pgno<=dbSize ){
      ppPg = &pPg->pNextAll;
    }else if( pPg->nRef>0 ){
      memset(PGHDR_TO_DATA(pPg), 0, pPager->pageSize);
      ppPg = &pPg->pNextAll;
................................................................................
    return rc;
  }
  if( nPage>=(unsigned)pPager->dbSize ){
    return SQLITE_OK;
  }
  if( MEMDB ){
    pPager->dbSize = nPage;
    pagerEnter(pPager);
    pager_truncate_cache(pPager);
    pagerLeave(pPager);
    return SQLITE_OK;
  }
  pagerEnter(pPager);
  rc = syncJournal(pPager);
  pagerLeave(pPager);
  if( rc!=SQLITE_OK ){
    return rc;
  }

  /* Get an exclusive lock on the database before truncating. */
  pagerEnter(pPager);
  rc = pager_wait_on_lock(pPager, EXCLUSIVE_LOCK);
  if( rc==SQLITE_OK ){
    rc = pager_truncate(pPager, nPage);
  }
  pagerLeave(pPager);


  return rc;
}

/*
** Shutdown the page cache.  Free all memory and close all files.
**
** If a transaction was in progress when this routine is called, that
................................................................................
**
** This function always succeeds. If a transaction is active an attempt
** is made to roll it back. If an error occurs during the rollback 
** a hot journal may be left in the filesystem but no error is returned
** to the caller.
*/
int sqlite3PagerClose(Pager *pPager){
  pagerEnter(pPager);
  pPager->errCode = 0;
  pager_reset(pPager);
  pagerLeave(pPager);
#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
  if( !MEMDB ){
#ifndef SQLITE_MUTEX_NOOP
    sqlite3_mutex *mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MEM2);
#endif
    sqlite3_mutex_enter(mutex);
    if( pPager->pPrev ){
................................................................................
    }else{
      sqlite3PagerList = pPager->pNext;
    }
    if( pPager->pNext ){
      pPager->pNext->pPrev = pPager->pPrev;
    }
    sqlite3_mutex_leave(mutex);
    pPager->onPagerList = 0;
  }
#endif

  disable_simulated_io_errors();
  sqlite3FaultBeginBenign(-1);

  pPager->exclusiveMode = 0;

  pagerUnlockAndRollback(pPager);
  enable_simulated_io_errors();
  sqlite3FaultEndBenign(-1);
  PAGERTRACE2("CLOSE %d\n", PAGERID(pPager));
  IOTRACE(("CLOSE %p\n", pPager))
  if( pPager->journalOpen ){
    sqlite3OsClose(pPager->jfd);
................................................................................

#ifndef NDEBUG
  /* If the Pager.needSync flag is clear then the PgHdr.needSync
  ** flag must also be clear for all pages.  Verify that this
  ** invariant is true.
  */
  else{
    pagerMutexHeld(pPager);
    for(pPg=pPager->pAll; pPg; pPg=pPg->pNextAll){
      assert( pPg->needSync==0 );
    }
    assert( pPager->lru.pFirstSynced==pPager->lru.pFirst );
  }
#endif

................................................................................
*/
static int pager_recycle(Pager *pPager, PgHdr **ppPg){
  PgHdr *pPg;
  *ppPg = 0;

  /* It is illegal to call this function unless the pager object
  ** pointed to by pPager has at least one free page (page with nRef==0).
  */
  assert(!MEMDB);
  assert(pPager->lru.pFirst);

  /* Find a page to recycle.  Try to locate a page that does not
  ** require us to do an fsync() on the journal.
  */
  pPg = pPager->lru.pFirstSynced;
................................................................................
  */
  for(pPager=sqlite3PagerList; pPager; pPager=pPager->pNext){
     pPager->iInUseMM = 1;
  }

  while( rc==SQLITE_OK && (nReq<0 || nReleased<nReq) ){
    PgHdr *pPg;

 
    /* Try to find a page to recycle that does not require a sync(). If
    ** this is not possible, find one that does require a sync().
    */
    sqlite3_mutex_enter(sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_LRU));
    pPg = sqlite3LruPageList.pFirstSynced;
    while( pPg && (pPg->needSync || pPg->pPager->iInUseDB) ){
................................................................................
    /* If pPg==0, then the block above has failed to find a page to
    ** recycle. In this case return early - no further memory will
    ** be released.
    */
    if( !pPg ) break;

    pPager = pPg->pPager;
    assert( pPager->iInUseDB==0 );


    savedBusy = pPager->pBusyHandler;
    pPager->pBusyHandler = 0;
    rc = pager_recycle(pPager, &pPg);
    assert( pPager->iInUseDB==0 );
    pPager->pBusyHandler = savedBusy;

    if( rc==SQLITE_OK ){
      /* We've found a page to free. At this point the page has been 
      ** removed from the page hash-table, free-list and synced-list 
      ** (pFirstSynced). It is still in the all pages (pAll) list. 
      ** Remove it from this list before freeing.
      **
      ** Todo: Check the Pager.pStmt list to make sure this is Ok. It 
................................................................................
**          to be synced to disk or else disk syncing is currently
**          allowed.
*/
static int pagerAllocatePage(Pager *pPager, PgHdr **ppPg){
  int rc = SQLITE_OK;
  PgHdr *pPg;
  int nByteHdr;


  /* Create a new PgHdr if any of the four conditions defined 
  ** above are met: 
  */
  pagerMutexHeld(pPager);
  if( pPager->nPage<pPager->mxPage
   || pPager->lru.pFirst==0 
   || MEMDB
   || (pPager->lru.pFirstSynced==0 && pPager->doNotSync)
  ){
    void *pData;
    if( pPager->nPage>=pPager->nHash ){
................................................................................
    if( pPg==0 ){
      rc = SQLITE_NOMEM;
      goto pager_allocate_out;
    }
    memset(pPg, 0, nByteHdr);
    pPg->pData = pData;
    pPg->pPager = pPager;
    pagerMutexHeld(pPager);
    pPg->pNextAll = pPager->pAll;
    pPager->pAll = pPg;
    pPager->nPage++;
  }else{
    /* Recycle an existing page with a zero ref-count. */
    pagerMutexHeld(pPager);
    rc = pager_recycle(pPager, &pPg);
    if( rc==SQLITE_BUSY ){
      rc = SQLITE_IOERR_BLOCKED;
    }
    if( rc!=SQLITE_OK ){
      goto pager_allocate_out;
    }
................................................................................
            if( rc!=SQLITE_OK ) goto sync_exit;
          }
        } 
      }
#endif
      rc = writeMasterJournal(pPager, zMaster);
      if( rc!=SQLITE_OK ) goto sync_exit;
      pagerMutexHeld(pPager);
      rc = syncJournal(pPager);
    }
    if( rc!=SQLITE_OK ) goto sync_exit;

#ifndef SQLITE_OMIT_AUTOVACUUM
    if( nTrunc!=0 ){
      rc = sqlite3PagerTruncate(pPager, nTrunc);
................................................................................
** SQLITE_OK is returned.
*/
int sqlite3PagerRollback(Pager *pPager){
  int rc;
  PAGERTRACE2("ROLLBACK %d\n", PAGERID(pPager));
  if( MEMDB ){
    PgHdr *p;
    pagerMutexHeld(pPager);
    for(p=pPager->pAll; p; p=p->pNextAll){
      PgHistory *pHist;
      assert( !p->alwaysRollback );
      if( !p->dirty ){
        assert( !((PgHistory *)PGHDR_TO_HIST(p, pPager))->pOrig );
        assert( !((PgHistory *)PGHDR_TO_HIST(p, pPager))->pStmt );
        continue;