Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | When recycling a page, try to find one that does not require a call to xSync() on the journal file. Also simplify some of the mutex related things in pcache. (CVS 5597) |
---|---|
Downloads: | Tarball | ZIP archive |
Timelines: | family | ancestors | descendants | both | trunk |
Files: | files | file ages | folders |
SHA1: |
93dbc5427bebaa0b3d726731027caad3 |
User & Date: | danielk1977 2008-08-22 16:22:17.000 |
Context
2008-08-22
| ||
16:29 | Enhanced test coverage. (CVS 5598) (check-in: cc36b4e016 user: drh tags: trunk) | |
16:22 | When recycling a page, try to find one that does not require a call to xSync() on the journal file. Also simplify some of the mutex related things in pcache. (CVS 5597) (check-in: 93dbc5427b user: danielk1977 tags: trunk) | |
14:41 | Make sure the function context is fully initialized before invoking the function finalizer. Ticket #3326. (CVS 5596) (check-in: 8496f4a00a user: drh tags: trunk) | |
Changes
Changes to src/pager.c.
︙ | ︙ | |||
14 15 16 17 18 19 20 | ** The pager is used to access a database disk file. It implements ** atomic commit and rollback through the use of a journal file that ** is separate from the database file. The pager also implements file ** locking to prevent two processes from writing the same database ** file simultaneously, or one process from reading the database while ** another is writing. ** | | | 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | ** The pager is used to access a database disk file. It implements ** atomic commit and rollback through the use of a journal file that ** is separate from the database file. The pager also implements file ** locking to prevent two processes from writing the same database ** file simultaneously, or one process from reading the database while ** another is writing. ** ** @(#) $Id: pager.c,v 1.475 2008/08/22 16:22:17 danielk1977 Exp $ */ #ifndef SQLITE_OMIT_DISKIO #include "sqliteInt.h" /* ** Macros for troubleshooting. Normally turned off */ |
︙ | ︙ | |||
1703 1704 1705 1706 1707 1708 1709 | vfsFlags |= SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_EXCLUSIVE | SQLITE_OPEN_DELETEONCLOSE; rc = sqlite3OsOpen(pPager->pVfs, 0, pFile, vfsFlags, 0); assert( rc!=SQLITE_OK || pFile->pMethods ); return rc; } | | | 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 | vfsFlags |= SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_EXCLUSIVE | SQLITE_OPEN_DELETEONCLOSE; rc = sqlite3OsOpen(pPager->pVfs, 0, pFile, vfsFlags, 0); assert( rc!=SQLITE_OK || pFile->pMethods ); return rc; } static int pagerStress(void *,PgHdr *); /* ** Create a new page cache and put a pointer to the page cache in *ppPager. ** The file to be cached need not exist. The file is not locked until ** the first call to sqlite3PagerGet() and is only held open until the ** last page is released using sqlite3PagerUnref(). ** |
︙ | ︙ | |||
2362 2363 2364 2365 2366 2367 2368 | #endif return rc; } /* ** Given a list of pages (connected by the PgHdr.pDirty pointer) write | | > > | | 2362 2363 2364 2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379 | #endif return rc; } /* ** Given a list of pages (connected by the PgHdr.pDirty pointer) write ** every one of those pages out to the database file. No calls are made ** to the page-cache to mark the pages as clean. It is the responsibility ** of the caller to use PcacheCleanAll() or PcacheMakeClean() to mark ** the pages as clean. */ static int pager_write_pagelist(PgHdr *pList){ Pager *pPager; int rc; if( pList==0 ) return SQLITE_OK; pPager = pList->pPager; |
︙ | ︙ | |||
2429 2430 2431 2432 2433 2434 2435 | PAGERTRACE3("NOSTORE %d page %d\n", PAGERID(pPager), pList->pgno); } #endif if( rc ) return rc; #ifdef SQLITE_CHECK_PAGES pList->pageHash = pager_pagehash(pList); #endif | | | < < | | | | | | | | | | | | > | | | | | > | > | < | 2431 2432 2433 2434 2435 2436 2437 2438 2439 2440 2441 2442 2443 2444 2445 2446 2447 2448 2449 2450 2451 2452 2453 2454 2455 2456 2457 2458 2459 2460 2461 2462 2463 2464 2465 2466 2467 2468 2469 2470 2471 2472 2473 2474 2475 2476 2477 2478 2479 2480 2481 2482 2483 2484 2485 | PAGERTRACE3("NOSTORE %d page %d\n", PAGERID(pPager), pList->pgno); } #endif if( rc ) return rc; #ifdef SQLITE_CHECK_PAGES pList->pageHash = pager_pagehash(pList); #endif /* makeClean(pList); */ pList = pList->pDirty; } /* sqlite3PcacheCleanAll(pPager->pPCache); */ return SQLITE_OK; } /* ** This function is called by the pcache layer when it has reached some ** soft memory limit. The argument is a pointer to a purgeable Pager ** object. This function attempts to make a single dirty page that has no ** outstanding references (if one exists) clean so that it can be recycled ** by the pcache layer. */ static int pagerStress(void *p, PgHdr *pPg){ Pager *pPager = (Pager *)p; int rc = SQLITE_OK; assert( pPg->flags&PGHDR_DIRTY ); if( pPager->errCode==SQLITE_OK ){ if( pPg->flags&PGHDR_NEED_SYNC ){ rc = syncJournal(pPager); if( rc==SQLITE_OK && pPager->fullSync && !(sqlite3OsDeviceCharacteristics(pPager->fd)&SQLITE_IOCAP_SAFE_APPEND) ){ pPager->nRec = 0; rc = writeJournalHdr(pPager); } } if( rc==SQLITE_OK ){ pPg->pDirty = 0; rc = pager_write_pagelist(pPg); } if( rc!=SQLITE_OK ){ pager_error(pPager, rc); } } if( rc==SQLITE_OK ){ sqlite3PcacheMakeClean(pPg); } return rc; } /* ** Return 1 if there is a hot journal on the given pager. |
︙ | ︙ |
Changes to src/pcache.c.
1 2 3 4 5 6 7 8 9 10 11 12 13 | /* ** 2008 August 05 ** ** The author disclaims copyright to this source code. In place of ** a legal notice, here is a blessing: ** ** May you do good and not evil. ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** ************************************************************************* ** This file implements that page cache. ** | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | /* ** 2008 August 05 ** ** The author disclaims copyright to this source code. In place of ** a legal notice, here is a blessing: ** ** May you do good and not evil. ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** ************************************************************************* ** This file implements that page cache. ** ** @(#) $Id: pcache.c,v 1.8 2008/08/22 16:22:17 danielk1977 Exp $ */ #include "sqliteInt.h" /* ** A complete page cache is an instance of this structure. */ struct PCache { PCache *pNextAll, *pPrevAll; /* List of all page caches */ int szPage; /* Size of every page in this cache */ int szExtra; /* Size of extra space for each page */ int nHash; /* Number of slots in apHash[] */ int nPage; /* Total number of pages in apHash */ int nMax; /* Configured cache size */ PgHdr **apHash; /* Hash table for fast lookup by pgno */ int bPurgeable; /* True if pages are on backing store */ void (*xDestroy)(PgHdr*); /* Called when refcnt goes 1->0 */ int (*xStress)(void*,PgHdr*); /* Call to try make a page clean */ void *pStress; /* Argument to xStress */ PgHdr *pClean; /* List of clean pages in use */ PgHdr *pDirty; /* List of dirty pages */ int nRef; /* Number of outstanding page refs */ int iInUseMM; int iInUseDB; |
︙ | ︙ | |||
53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 | ** by the pcache.mxPage and pcache.mxPagePurgeable variables. If ** mxPage is non-zero, then the system tries to limit the number of ** cached pages stored to mxPage. In this case mxPagePurgeable is not ** used. ** ** If mxPage is zero, then the system tries to limit the number of ** pages held by purgable caches to mxPagePurgeable. */ static struct PCacheGlobal { int isInit; /* True when initialized */ sqlite3_mutex *mutex_mem2; /* static mutex MUTEX_STATIC_MEM2 */ sqlite3_mutex *mutex_lru; /* static mutex MUTEX_STATIC_LRU */ PCache *pAll; /* list of all page caches */ int nPage; /* Number of pages */ int nPurgeable; /* Number of pages in purgable caches */ int mxPage; /* Globally configured page maximum */ int mxPagePurgeable; /* Purgeable page maximum */ PgHdr *pLruHead, *pLruTail; /* Global LRU list of unused pages */ int szSlot; /* Size of each free slot */ void *pStart, *pEnd; /* Bounds of pagecache malloc range */ PgFreeslot *pFree; /* Free page blocks */ } pcache = {0}; /* ** All global variables used by this module (most of which are grouped ** together in global structure "pcache" above) except the list of all ** pager-caches starting with pcache.pAll, are protected by the static ** SQLITE_MUTEX_STATIC_LRU mutex. A pointer to this mutex is stored in ** variable "pcache.mutex_lru". ** | > > > > > > > > > > < < < | < | 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 | ** by the pcache.mxPage and pcache.mxPagePurgeable variables. If ** mxPage is non-zero, then the system tries to limit the number of ** cached pages stored to mxPage. In this case mxPagePurgeable is not ** used. ** ** If mxPage is zero, then the system tries to limit the number of ** pages held by purgable caches to mxPagePurgeable. ** ** The doubly-linked list that runs between pcache.pLruHead and ** pcache.pLruTail contains all pages in the system with a zero ** reference count. The pcache.pLruSynced variable points to the last ** (closest to pcache.pLruTail) entry in this list that does not have ** the PGHDR_NEED_SYNC flag set. This is the page that the pcacheRecycle() ** function will try to recycle. */ static struct PCacheGlobal { int isInit; /* True when initialized */ sqlite3_mutex *mutex_mem2; /* static mutex MUTEX_STATIC_MEM2 */ sqlite3_mutex *mutex_lru; /* static mutex MUTEX_STATIC_LRU */ PCache *pAll; /* list of all page caches */ int nPage; /* Number of pages */ int nPurgeable; /* Number of pages in purgable caches */ int mxPage; /* Globally configured page maximum */ int mxPagePurgeable; /* Purgeable page maximum */ PgHdr *pLruHead, *pLruTail; /* Global LRU list of unused pages */ PgHdr *pLruSynced; /* Last synced entry in LRU list */ /* Variables related to SQLITE_CONFIG_PAGECACHE settings. */ int szSlot; /* Size of each free slot */ void *pStart, *pEnd; /* Bounds of pagecache malloc range */ PgFreeslot *pFree; /* Free page blocks */ } pcache = {0}; /* ** All global variables used by this module (most of which are grouped ** together in global structure "pcache" above) except the list of all ** pager-caches starting with pcache.pAll, are protected by the static ** SQLITE_MUTEX_STATIC_LRU mutex. A pointer to this mutex is stored in ** variable "pcache.mutex_lru". ** ** Access to the contents of the individual PCache structures is not ** protected. It is the job of the caller to ensure that these structures ** are accessed in a thread-safe manner. However, this module provides the ** functions sqlite3PcacheLock() and sqlite3PcacheUnlock() that may be used ** by the caller to increment/decrement a lock-count on an individual ** pager-cache object. This module guarantees that the xStress() callback ** will not be invoked on a pager-cache with a non-zero lock-count except ** from within a call to sqlite3PcacheFetch() on the same pager. A call ** to sqlite3PcacheLock() may block if such an xStress() call is currently ** underway. ** ** Before the xStress callback of a pager-cache (PCache) is invoked, the ** SQLITE_MUTEX_STATIC_MEM2 mutex is obtained. ** ** Deadlock within the module is avoided by never blocking on the MEM2 ** mutex while the LRU mutex is held. */ #define pcacheEnterGlobal() sqlite3_mutex_enter(pcache.mutex_lru) #define pcacheExitGlobal() sqlite3_mutex_leave(pcache.mutex_lru) |
︙ | ︙ | |||
245 246 247 248 249 250 251 252 253 254 255 256 257 258 | /* ** Remove a page from the global LRU list */ static void pcacheRemoveFromLruList(PgHdr *pPage){ assert( sqlite3_mutex_held(pcache.mutex_lru) ); if( pPage->pCache->bPurgeable==0 ) return; if( pPage->pNextLru ){ pPage->pNextLru->pPrevLru = pPage->pPrevLru; }else{ assert( pcache.pLruTail==pPage ); pcache.pLruTail = pPage->pPrevLru; } if( pPage->pPrevLru ){ | > > > > > | 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 | /* ** Remove a page from the global LRU list */ static void pcacheRemoveFromLruList(PgHdr *pPage){ assert( sqlite3_mutex_held(pcache.mutex_lru) ); if( pPage->pCache->bPurgeable==0 ) return; if( pPage==pcache.pLruSynced ){ PgHdr *p; for(p=pPage->pPrevLru; p && (p->flags&PGHDR_NEED_SYNC); p=p->pPrevLru); pcache.pLruSynced = p; } if( pPage->pNextLru ){ pPage->pNextLru->pPrevLru = pPage->pPrevLru; }else{ assert( pcache.pLruTail==pPage ); pcache.pLruTail = pPage->pPrevLru; } if( pPage->pPrevLru ){ |
︙ | ︙ | |||
278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 | */ assert( pcache.pLruHead ); pPage->pNextLru = 0; pPage->pPrevLru = pcache.pLruTail; pcache.pLruTail->pNextLru = pPage; pcache.pLruTail = pPage; pPage->flags &= ~PGHDR_REUSE_UNLIKELY; }else{ /* If reuse is possible. the page goes at the beginning of the LRU ** list so that it will be the last to be recycled. */ if( pcache.pLruHead ){ pcache.pLruHead->pPrevLru = pPage; } pPage->pNextLru = pcache.pLruHead; pcache.pLruHead = pPage; pPage->pPrevLru = 0; if( pcache.pLruTail==0 ){ pcache.pLruTail = pPage; } } } /*********************************************** Memory Allocation *********** ** ** Initialize the page cache memory pool. | > > > > > > | 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 | */ assert( pcache.pLruHead ); pPage->pNextLru = 0; pPage->pPrevLru = pcache.pLruTail; pcache.pLruTail->pNextLru = pPage; pcache.pLruTail = pPage; pPage->flags &= ~PGHDR_REUSE_UNLIKELY; if( 0==(pPage->flags&PGHDR_NEED_SYNC) ){ pcache.pLruSynced = pPage; } }else{ /* If reuse is possible. the page goes at the beginning of the LRU ** list so that it will be the last to be recycled. */ if( pcache.pLruHead ){ pcache.pLruHead->pPrevLru = pPage; } pPage->pNextLru = pcache.pLruHead; pcache.pLruHead = pPage; pPage->pPrevLru = 0; if( pcache.pLruTail==0 ){ pcache.pLruTail = pPage; } if( pcache.pLruSynced==0 && 0==(pPage->flags&PGHDR_NEED_SYNC) ){ pcache.pLruSynced = pPage; } } } /*********************************************** Memory Allocation *********** ** ** Initialize the page cache memory pool. |
︙ | ︙ | |||
433 434 435 436 437 438 439 | assert( p->apSave[0]==0 ); assert( p->apSave[1]==0 ); assert( p && p->pCache ); return sqlite3MallocSize(p); } static PgHdr *pcacheRecycle(PCache *pCache){ | < > > > > > | > > < < > > > > > > > > | | > > > > | | | | < | < > | | | | | | > | > | 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 | assert( p->apSave[0]==0 ); assert( p->apSave[1]==0 ); assert( p && p->pCache ); return sqlite3MallocSize(p); } static PgHdr *pcacheRecycle(PCache *pCache){ PgHdr *p = 0; assert( pcache.isInit ); assert( sqlite3_mutex_held(pcache.mutex_lru) ); p = pcache.pLruSynced; if( !p ){ p = pcache.pLruTail; } if( p && (p->flags&PGHDR_DIRTY) ){ if( SQLITE_OK==sqlite3_mutex_try(pcache.mutex_mem2) ){ int iInUseDB; PCache *pC = p->pCache; if( pCache==pC ){ /* If trying to recycle a page from pCache, then set the iInUseDb ** flag to zero. This is done so that the sqlite3PcacheSetFlags() ** can tell that the LRU mutex is already held. ** ** It is quite safe to modify the iInUseDB variable, because we ** know no other thread will attempt to use pCache (because this ** call is being made from within a call to sqlite3PcacheFetch() ** on pCache). */ assert( pCache->iInUseDB ); iInUseDB = pCache->iInUseDB; pCache->iInUseDB = 0; } assert( pC->iInUseMM==0 ); pC->iInUseMM = 1; if( pC->xStress && pC->iInUseDB==0 ){ pC->xStress(pC->pStress, p); } if( pCache==pC ){ pCache->iInUseDB = iInUseDB; } pC->iInUseMM = 0; sqlite3_mutex_leave(pcache.mutex_mem2); } } if( p && (p->flags&PGHDR_DIRTY) ){ p = 0; } if( p ){ pcacheRemoveFromLruList(p); pcacheRemoveFromHash(p); pcacheRemoveFromList(&p->pCache->pClean, p); /* If the always-rollback flag is set on the page being recycled, set |
︙ | ︙ | |||
554 555 556 557 558 559 560 | ** 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 */ void (*xDestroy)(PgHdr*), /* Called to destroy a page */ | | < | | | 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 | ** 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 */ void (*xDestroy)(PgHdr*), /* Called to destroy a page */ 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.isInit ); memset(p, 0, sizeof(PCache)); p->szPage = szPage; p->szExtra = szExtra; p->bPurgeable = bPurgeable; p->xDestroy = xDestroy; p->xStress = xStress; p->pStress = pStress; p->nMax = 100; if( bPurgeable ){ pcacheEnterGlobal(); pcache.mxPagePurgeable += p->nMax; pcacheExitGlobal(); } /* Add the new pager-cache to the list of caches starting at pcache.pAll */ pcacheEnterGlobal(); p->pNextAll = pcache.pAll; if( pcache.pAll ){ pcache.pAll->pPrevAll = p; } p->pPrevAll = 0; pcache.pAll = p; pcacheExitGlobal(); } /* ** Change the page size for PCache object. This can only happen ** when the cache is empty. */ void sqlite3PcacheSetPageSize(PCache *pCache, int szPage){ |
︙ | ︙ | |||
615 616 617 618 619 620 621 | assert( pCache->iInUseDB || pCache->iInUseMM ); /* 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 ){ | | | 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 | assert( pCache->iInUseDB || pCache->iInUseMM ); /* 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 /* && (pPage->flags & PGHDR_DIRTY)==0 */ ){ pcacheEnterGlobal(); pcacheRemoveFromLruList(pPage); pcacheExitGlobal(); } pcacheRef(pPage, 1); *ppPage = pPage; return SQLITE_OK; |
︙ | ︙ | |||
669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 | assert( p->nRef>0 ); assert( p->pCache->iInUseDB || p->pCache->iInUseMM ); pcacheRef(p, -1); if( p->nRef!=0 ) return; if( p->pCache->xDestroy ){ p->pCache->xDestroy(p); } if( (p->flags & PGHDR_DIRTY)!=0 ) return; pcacheEnterGlobal(); pcacheAddToLruList(p); pcacheExitGlobal(); } void sqlite3PcacheRef(PgHdr *p){ assert(p->nRef>=0); | > > | 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 | assert( p->nRef>0 ); assert( p->pCache->iInUseDB || p->pCache->iInUseMM ); pcacheRef(p, -1); if( p->nRef!=0 ) return; if( p->pCache->xDestroy ){ p->pCache->xDestroy(p); } #if 0 if( (p->flags & PGHDR_DIRTY)!=0 ) return; #endif pcacheEnterGlobal(); pcacheAddToLruList(p); pcacheExitGlobal(); } void sqlite3PcacheRef(PgHdr *p){ assert(p->nRef>=0); |
︙ | ︙ | |||
727 728 729 730 731 732 733 | */ void sqlite3PcacheMakeClean(PgHdr *p){ PCache *pCache; assert( p->pCache->iInUseDB || p->pCache->iInUseMM ); if( (p->flags & PGHDR_DIRTY)==0 ) return; assert( p->apSave[0]==0 && p->apSave[1]==0 ); assert( p->flags & PGHDR_DIRTY ); | | < < < < < > < < > | | < < | 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 | */ void sqlite3PcacheMakeClean(PgHdr *p){ PCache *pCache; assert( p->pCache->iInUseDB || p->pCache->iInUseMM ); if( (p->flags & PGHDR_DIRTY)==0 ) return; assert( p->apSave[0]==0 && p->apSave[1]==0 ); assert( p->flags & PGHDR_DIRTY ); assert( p->nRef>0 || sqlite3_mutex_held(pcache.mutex_lru) ); pCache = p->pCache; pcacheRemoveFromList(&pCache->pDirty, p); pcacheAddToList(&pCache->pClean, p); p->flags &= ~PGHDR_DIRTY; } /* ** Make every page in the cache clean. */ void sqlite3PcacheCleanAll(PCache *pCache){ PgHdr *p; assert( pCache->iInUseDB ); pcacheEnterGlobal(); while( (p = pCache->pDirty)!=0 ){ assert( p->apSave[0]==0 && p->apSave[1]==0 ); pcacheRemoveFromList(&pCache->pDirty, p); pcacheAddToList(&pCache->pClean, p); p->flags &= ~PGHDR_DIRTY; } sqlite3PcacheAssertFlags(pCache, 0, PGHDR_DIRTY); pcacheExitGlobal(); } /* ** 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. */ |
︙ | ︙ | |||
802 803 804 805 806 807 808 809 810 811 812 813 814 815 | for(p=pCache->pClean; p; p=pNext){ pNext = p->pNext; pcacheRemoveFromLruList(p); pcachePageFree(p); } for(p=pCache->pDirty; p; p=pNext){ pNext = p->pNext; pcachePageFree(p); } pCache->pClean = 0; pCache->pDirty = 0; pCache->nPage = 0; memset(pCache->apHash, 0, pCache->nHash*sizeof(pCache->apHash[0])); } | > | 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 | for(p=pCache->pClean; p; p=pNext){ pNext = p->pNext; pcacheRemoveFromLruList(p); pcachePageFree(p); } for(p=pCache->pDirty; p; p=pNext){ pNext = p->pNext; pcacheRemoveFromLruList(p); pcachePageFree(p); } pCache->pClean = 0; pCache->pDirty = 0; pCache->nPage = 0; memset(pCache->apHash, 0, pCache->nHash*sizeof(pCache->apHash[0])); } |
︙ | ︙ | |||
831 832 833 834 835 836 837 | pNext = p->pNext; if( p->pgno>pgno ){ if( p->nRef==0 ){ pcacheRemoveFromHash(p); if( p->flags&PGHDR_DIRTY ){ pcacheRemoveFromList(&pCache->pDirty, p); }else{ | < > > | < < < < | < < < | > | 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 | pNext = p->pNext; if( p->pgno>pgno ){ if( p->nRef==0 ){ pcacheRemoveFromHash(p); if( p->flags&PGHDR_DIRTY ){ pcacheRemoveFromList(&pCache->pDirty, p); }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); } } } pcacheExitGlobal(); } /* ** Close a cache. */ void sqlite3PcacheClose(PCache *pCache){ assert( pCache->iInUseDB==1 ); pcacheEnterGlobal(); /* Free all the pages used by this pager and remove them from the LRU list. */ pcacheClear(pCache); if( pCache->bPurgeable ){ pcache.mxPagePurgeable -= pCache->nMax; } sqlite3_free(pCache->apHash); /* Now remove the pager-cache structure itself from the list of ** all such structures headed by pcache.pAll. */ assert(pCache==pcache.pAll || pCache->pPrevAll); assert(pCache->pNextAll==0 || pCache->pNextAll->pPrevAll==pCache); assert(pCache->pPrevAll==0 || pCache->pPrevAll->pNextAll==pCache); if( pCache->pPrevAll ){ pCache->pPrevAll->pNextAll = pCache->pNextAll; }else{ pcache.pAll = pCache->pNextAll; } if( pCache->pNextAll ){ pCache->pNextAll->pPrevAll = pCache->pPrevAll; } pcacheExitGlobal(); } /* ** Preserve the content of the page, if it has not been preserved ** already. If idJournal==0 then this is for the overall transaction. ** If idJournal==1 then this is for the statement journal. ** |
︙ | ︙ | |||
1056 1057 1058 1059 1060 1061 1062 | assert( pCache->iInUseDB ); for(p=pCache->pDirty; p; p=p->pNext){ p->pDirty = p->pNext; } return pcacheSortDirtyList(pCache->pDirty); } | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 | assert( pCache->iInUseDB ); for(p=pCache->pDirty; p; p=p->pNext){ p->pDirty = p->pNext; } return pcacheSortDirtyList(pCache->pDirty); } /* ** Return the total number of outstanding page references. */ int sqlite3PcacheRefCount(PCache *pCache){ return pCache->nRef; } |
︙ | ︙ | |||
1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 | #endif /* ** Set flags on all pages in the page cache */ void sqlite3PcacheSetFlags(PCache *pCache, int andMask, int orMask){ PgHdr *p; assert( pCache->iInUseDB || pCache->iInUseMM ); for(p=pCache->pDirty; p; p=p->pNext){ p->flags = (p->flags&andMask)|orMask; } for(p=pCache->pClean; p; p=p->pNext){ p->flags = (p->flags&andMask)|orMask; } } /* ** Set the suggested cache-size value. */ int sqlite3PcacheGetCachesize(PCache *pCache){ return pCache->nMax; | > > > > > > > > > > > > > > > > > > > > > > > | 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 | #endif /* ** Set flags on all pages in the page cache */ void sqlite3PcacheSetFlags(PCache *pCache, int andMask, int orMask){ PgHdr *p; assert( (orMask&PGHDR_NEED_SYNC)==0 ); assert( pCache->iInUseDB || pCache->iInUseMM ); /* If this call is being made from within a call to the xStress callback ** of a pager-cache (i.e. from within pagerRecycle()), then the ** PCache.iInUseDB will be set to zero. In this case, the LRU mutex is ** already held. Otherwise, obtain it before modifying any PgHdr.flags ** variables or traversing the LRU list. */ if( pCache->iInUseDB ){ pcacheEnterGlobal(); } assert( sqlite3_mutex_held(pcache.mutex_lru) ); for(p=pCache->pDirty; p; p=p->pNext){ p->flags = (p->flags&andMask)|orMask; } for(p=pCache->pClean; p; p=p->pNext){ p->flags = (p->flags&andMask)|orMask; } if( 0==(andMask&PGHDR_NEED_SYNC) ){ for(p=pcache.pLruTail; p && (p->flags&PGHDR_NEED_SYNC); p=p->pPrevLru); pcache.pLruSynced = p; } if( pCache->iInUseDB ){ pcacheExitGlobal(); } } /* ** Set the suggested cache-size value. */ int sqlite3PcacheGetCachesize(PCache *pCache){ return pCache->nMax; |
︙ | ︙ |
Changes to src/pcache.h.
︙ | ︙ | |||
8 9 10 11 12 13 14 | ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** ************************************************************************* ** This header file defines the interface that the sqlite page cache ** subsystem. ** | | | 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** ************************************************************************* ** This header file defines the interface that the sqlite page cache ** subsystem. ** ** @(#) $Id: pcache.h,v 1.4 2008/08/22 16:22:17 danielk1977 Exp $ */ #ifndef _PCACHE_H_ typedef struct PgHdr PgHdr; typedef struct PCache PCache; |
︙ | ︙ | |||
66 67 68 69 70 71 72 | void sqlite3PCacheFree(void*); /* Create a new pager cache. ** Under memory stress, invoke xStress to try to make pages clean. ** Only clean and unpinned pages can be reclaimed. */ void sqlite3PcacheOpen( | | | | | | | | | 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 | void sqlite3PCacheFree(void*); /* Create a new pager cache. ** Under memory stress, invoke xStress to try to make pages clean. ** Only clean and unpinned pages can be reclaimed. */ 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 */ void (*xDestroy)(PgHdr *), /* Called to destroy a page */ int (*xStress)(void*, PgHdr*), /* Call to try to make pages clean */ void *pStress, /* Argument to xStress */ PCache *pToInit /* Preallocated space for the PCache */ ); /* Modify the page-size after the cache has been created. */ void sqlite3PcacheSetPageSize(PCache *, int); /* Return the size in bytes of a PCache object. Used to preallocate ** storage space. |
︙ | ︙ | |||
114 115 116 117 118 119 120 | int sqlite3PcachePreserve(PgHdr*, int); /* Preserve current page content */ void sqlite3PcacheCommit(PCache*, int); /* Drop preserved copy */ void sqlite3PcacheRollback(PCache*, int); /* Rollback to preserved copy */ /* Get a list of all dirty pages in the cache, sorted by page number */ PgHdr *sqlite3PcacheDirtyList(PCache*); | < < < | 114 115 116 117 118 119 120 121 122 123 124 125 126 127 | int sqlite3PcachePreserve(PgHdr*, int); /* Preserve current page content */ void sqlite3PcacheCommit(PCache*, int); /* Drop preserved copy */ void sqlite3PcacheRollback(PCache*, int); /* Rollback to preserved copy */ /* Get a list of all dirty pages in the cache, sorted by page number */ PgHdr *sqlite3PcacheDirtyList(PCache*); /* Reset and close the cache object */ void sqlite3PcacheClose(PCache*); /* Set flags on all pages in the page cache */ void sqlite3PcacheSetFlags(PCache*, int andMask, int orMask); /* Assert flags settings on all pages. Debugging only */ |
︙ | ︙ |
Changes to test/mutex1.test.
1 2 3 4 5 6 7 8 9 10 11 | # 2008 June 17 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | # 2008 June 17 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # # $Id: mutex1.test,v 1.13 2008/08/22 16:22:17 danielk1977 Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl if {[info exists tester_do_binarylog]} { finish_test return |
︙ | ︙ | |||
92 93 94 95 96 97 98 | # * Multi-threaded mode, # * Single-threaded mode. # set enable_shared_cache [sqlite3_enable_shared_cache 1] ifcapable threadsafe { foreach {mode mutexes} { singlethread {} | | | | 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 | # * Multi-threaded mode, # * Single-threaded mode. # set enable_shared_cache [sqlite3_enable_shared_cache 1] ifcapable threadsafe { foreach {mode mutexes} { singlethread {} multithread {fast static_lru static_master static_mem static_prng } serialized {fast recursive static_lru static_master static_mem static_prng } } { do_test mutex1.2.$mode.1 { catch {db close} sqlite3_shutdown sqlite3_config $mode } SQLITE_OK |
︙ | ︙ |