Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Miscellaneous cleanup in the new pcache code. (CVS 5629) |
---|---|
Downloads: | Tarball | ZIP archive |
Timelines: | family | ancestors | descendants | both | trunk |
Files: | files | file ages | folders |
SHA1: |
da1777259f53c2e20c7ced06bf6f2a55 |
User & Date: | drh 2008-08-28 02:26:07.000 |
Context
2008-08-28
| ||
08:31 | Fix a threads/mutex problem in pcache.c. (CVS 5630) (check-in: 1928f15b78 user: danielk1977 tags: trunk) | |
02:26 | Miscellaneous cleanup in the new pcache code. (CVS 5629) (check-in: da1777259f user: drh tags: trunk) | |
2008-08-27
| ||
19:01 | If any error occurs during sqlite3_open(), move the database handle into "sick" state. When in the sick state the user can use sqlite3_errcode() and sqlite3_errmsg(), but not much else. (CVS 5628) (check-in: ce9c74eaab user: danielk1977 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.485 2008/08/28 02:26:07 drh Exp $ */ #ifndef SQLITE_OMIT_DISKIO #include "sqliteInt.h" /* ** Macros for troubleshooting. Normally turned off */ |
︙ | ︙ | |||
3267 3268 3269 3270 3271 3272 3273 | ** the statement journal format differs from the standard journal format ** in that it omits the checksums and the header. */ if( pPager->stmtInUse && !pageInStatement(pPg) && (int)pPg->pgno<=pPager->stmtSize ){ | | | < | 3267 3268 3269 3270 3271 3272 3273 3274 3275 3276 3277 3278 3279 3280 3281 3282 3283 3284 3285 3286 3287 3288 | ** the statement journal format differs from the standard journal format ** in that it omits the checksums and the header. */ if( pPager->stmtInUse && !pageInStatement(pPg) && (int)pPg->pgno<=pPager->stmtSize ){ assert( (pPg->flags&PGHDR_IN_JOURNAL) || (int)pPg->pgno>pPager->origDbSize ); if( MEMDB ){ rc = sqlite3PcachePreserve(pPg, 1); if( rc!=SQLITE_OK ){ return rc; } PAGERTRACE3("STMT-JOURNAL %d page %d\n", PAGERID(pPager), pPg->pgno); }else{ i64 offset = pPager->stmtNRec*(4+pPager->pageSize); char *pData2 = CODEC2(pPager, pData, pPg->pgno, 7); rc = write32bits(pPager->stfd, offset, pPg->pgno); if( rc==SQLITE_OK ){ rc = sqlite3OsWrite(pPager->stfd, pData2, pPager->pageSize, offset+4); } |
︙ | ︙ | |||
3503 3504 3505 3506 3507 3508 3509 | || pPg->pgno>pPager->origDbSize ){ return; } assert( !MEMDB ); /* For a memdb, pPager->journalOpen is always 0 */ #ifdef SQLITE_SECURE_DELETE | | | 3502 3503 3504 3505 3506 3507 3508 3509 3510 3511 3512 3513 3514 3515 3516 | || pPg->pgno>pPager->origDbSize ){ return; } assert( !MEMDB ); /* For a memdb, pPager->journalOpen is always 0 */ #ifdef SQLITE_SECURE_DELETE if( (pPg->flags & PGHDR_IN_JOURNAL)!=0 || (int)pPg->pgno>pPager->origDbSize ){ return; } #endif /* If SECURE_DELETE is disabled, then there is no way that this ** routine can be called on a page for which sqlite3PagerDontWrite() ** has not been previously called during the same transaction. |
︙ | ︙ |
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 39 40 41 42 43 44 45 46 47 48 49 50 51 52 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 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 | /* ** 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.19 2008/08/28 02:26:07 drh Exp $ */ #include "sqliteInt.h" /* ** A complete page cache is an instance of this structure. ** ** A cache may only be deleted by its owner and while holding the ** SQLITE_MUTEX_STATUS_LRU mutex. */ struct PCache { /********************************************************************* ** The first group of elements may be read or written at any time by ** the cache owner without holding the mutex. No thread other than the ** cache owner is permitted to access these elements at any time. */ PgHdr *pDirty, *pDirtyTail; /* List of dirty pages in LRU order */ PgHdr *pSynced; /* Last synced page in dirty page list */ int nRef; /* Number of pinned pages */ int nPinned; /* Number of pinned and/or dirty pages */ int nMax; /* Configured cache size */ int nMin; /* Configured minimum cache size */ /********************************************************************** ** The next group of elements are fixed when the cache is created and ** may not be changed afterwards. These elements can read at any time by ** the cache owner or by any thread holding the the mutex. Non-owner ** threads must hold the mutex when reading these elements to prevent ** the entire PCache object from being deleted during the read. */ int szPage; /* Size of every page in this cache */ int szExtra; /* Size of extra space for each page */ 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 */ /********************************************************************** ** The final group of elements can only be accessed while holding the ** mutex. Both the cache owner and any other thread must hold the mutex ** to read or write any of these elements. */ int nPage; /* Total number of pages in apHash */ int nHash; /* Number of slots in apHash[] */ PgHdr **apHash; /* Hash table for fast lookup by pgno */ PgHdr *pClean; /* List of clean pages in use */ }; /* ** Free slots in the page block allocator */ typedef struct PgFreeslot PgFreeslot; struct PgFreeslot { PgFreeslot *pNext; /* Next free slot */ }; /* ** Global data for the page cache. */ static struct PCacheGlobal { int isInit; /* True when initialized */ sqlite3_mutex *mutex; /* static mutex MUTEX_STATIC_LRU */ int nMaxPage; /* Sum of nMaxPage for purgeable caches */ int nMinPage; /* Sum of nMinPage for purgeable caches */ int nCurrentPage; /* Number of purgeable pages allocated */ PgHdr *pLruHead, *pLruTail; /* LRU list of unused clean pgs */ /* 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 (all of which are grouped ** together in global structure "pcache" above) are protected by the static ** SQLITE_MUTEX_STATIC_LRU mutex. A pointer to this mutex is stored in ** variable "pcache.mutex". ** ** Some elements of the PCache and PgHdr structures are protected by the ** SQLITE_MUTEX_STATUS_LRU mutex and other are not. The protected ** elements are grouped at the end of the structures and are clearly ** marked. ** ** Use the following macros must surround all access (read or write) ** of protected elements. The mutex is not recursive and may not be ** entered more than once. The pcacheMutexHeld() macro should only be ** used within an assert() to verify that the mutex is being held. */ #define pcacheEnterMutex() sqlite3_mutex_enter(pcache.mutex) #define pcacheExitMutex() sqlite3_mutex_leave(pcache.mutex) #define pcacheMutexHeld() sqlite3_mutex_held(pcache.mutex) /* ** Some of the assert() macros in this code are too expensive to run ** even during normal debugging. Use them only rarely on long-running ** tests. Enable the expensive asserts using the ** -DSQLITE_ENABLE_EXPENSIVE_ASSERT=1 compile-time option. */ #ifdef SQLITE_ENABLE_EXPENSIVE_ASSERT # define expensive_assert(X) assert(X) #else # define expensive_assert(X) #endif /********************************** Linked List Management ********************/ #if !defined(NDEBUG) && defined(SQLITE_ENABLE_EXPENSIVE_ASSERT) /* ** This routine verifies that the number of entries in the hash table ** is pCache->nPage. This routine is used within assert() statements ** only and is therefore disabled during production builds. */ static int pcacheCheckHashCount(PCache *pCache){ int i; int nPage = 0; for(i=0; i<pCache->nHash; i++){ PgHdr *p; for(p=pCache->apHash[i]; p; p=p->pNextHash){ nPage++; } } assert( nPage==pCache->nPage ); return 1; } #endif /* !NDEBUG && SQLITE_ENABLE_EXPENSIVE_ASSERT */ #if !defined(NDEBUG) && defined(SQLITE_ENABLE_EXPENSIVE_ASSERT) /* ** Based on the current value of PCache.nRef and the contents of the ** PCache.pDirty list, return the expected value of the PCache.nPinned ** counter. This is only used in debugging builds, as follows: ** ** expensive_assert( pCache->nPinned==pcachePinnedCount(pCache) ); */ static int pcachePinnedCount(PCache *pCache){ PgHdr *p; int nPinned = pCache->nRef; for(p=pCache->pDirty; p; p=p->pNext){ if( p->nRef==0 ){ nPinned++; } } return nPinned; } #endif /* !NDEBUG && SQLITE_ENABLE_EXPENSIVE_ASSERT */ #if !defined(NDEBUG) && defined(SQLITE_ENABLE_EXPENSIVE_ASSERT) /* ** Check that the pCache->pSynced variable is set correctly. If it ** is not, either fail an assert or return zero. Otherwise, return ** non-zero. This is only used in debugging builds, as follows: ** ** expensive_assert( pcacheCheckSynced(pCache) ); */ static int pcacheCheckSynced(PCache *pCache){ PgHdr *p = pCache->pDirtyTail; for(p=pCache->pDirtyTail; p!=pCache->pSynced; p=p->pPrev){ assert( p->nRef || (p->flags&PGHDR_NEED_SYNC) ); } return (p==0 || p->nRef || (p->flags&PGHDR_NEED_SYNC)==0); return 1; } #endif /* !NDEBUG && SQLITE_ENABLE_EXPENSIVE_ASSERT */ /* ** Remove a page from its hash table (PCache.apHash[]). */ static void pcacheRemoveFromHash(PgHdr *pPage){ /* assert( pcacheMutexHeld() ); *** FIXME ****/ if( pPage->pPrevHash ){ pPage->pPrevHash->pNextHash = pPage->pNextHash; }else{ PCache *pCache = pPage->pCache; u32 h = pPage->pgno % pCache->nHash; assert( pCache->apHash[h]==pPage ); pCache->apHash[h] = pPage->pNextHash; } if( pPage->pNextHash ){ pPage->pNextHash->pPrevHash = pPage->pPrevHash; } pPage->pCache->nPage--; expensive_assert( pcacheCheckHashCount(pPage->pCache) ); } /* ** Insert a page into the hash table ** ** The mutex must be held by the caller. */ static void pcacheAddToHash(PgHdr *pPage){ PCache *pCache = pPage->pCache; u32 h = pPage->pgno % pCache->nHash; /* assert( pcacheMutexHeld() ); *** FIXME *****/ pPage->pNextHash = pCache->apHash[h]; pPage->pPrevHash = 0; if( pCache->apHash[h] ){ pCache->apHash[h]->pPrevHash = pPage; } pCache->apHash[h] = pPage; pCache->nPage++; expensive_assert( pcacheCheckHashCount(pCache) ); } /* ** 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() ); **** FIXME *****/ #ifdef SQLITE_MALLOC_SOFT_LIMIT if( nHash*sizeof(PgHdr*)>SQLITE_MALLOC_SOFT_LIMIT ){ nHash = SQLITE_MALLOC_SOFT_LIMIT/sizeof(PgHdr *); } #endif pNew = (PgHdr **)sqlite3_malloc(sizeof(PgHdr*)*nHash); 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; } /* ** Remove a page from a linked list that is headed by *ppHead. ** *ppHead is either PCache.pClean or PCache.pDirty. */ static void pcacheRemoveFromList(PgHdr **ppHead, PgHdr *pPage){ int isDirtyList = (ppHead==&pPage->pCache->pDirty); assert( ppHead==&pPage->pCache->pClean || ppHead==&pPage->pCache->pDirty ); /* assert( pcacheMutexHeld() || ppHead!=&pPage->pCache->pClean ); *** FIXME */ if( pPage->pPrev ){ pPage->pPrev->pNext = pPage->pNext; }else{ assert( *ppHead==pPage ); *ppHead = pPage->pNext; } |
︙ | ︙ | |||
380 381 382 383 384 385 386 | }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 | | < < | | | | | 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 | }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; } } void *sqlite3PageMalloc(sz){ void *p; pcacheEnterMutex(); p = pcacheMalloc(sz, 0); pcacheExitMutex(); return p; } /* ** Release a pager memory allocation */ void pcacheFree(void *p){ |
︙ | ︙ | |||
422 423 424 425 426 427 428 | }else{ int iSize = sqlite3MallocSize(p); sqlite3StatusAdd(SQLITE_STATUS_PAGECACHE_OVERFLOW, -iSize); sqlite3_free(p); } } void sqlite3PageFree(void *p){ | | | | 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 | }else{ int iSize = sqlite3MallocSize(p); sqlite3StatusAdd(SQLITE_STATUS_PAGECACHE_OVERFLOW, -iSize); sqlite3_free(p); } } void sqlite3PageFree(void *p){ pcacheEnterMutex(); pcacheFree(p); pcacheExitMutex(); } /* ** Allocate a new page. */ static PgHdr *pcachePageAlloc(PCache *pCache){ PgHdr *p; |
︙ | ︙ | |||
515 516 517 518 519 520 521 | int szPage = pCache->szPage; int szExtra = pCache->szExtra; assert( pcache.isInit ); assert( sqlite3_mutex_notheld(pcache.mutex) ); *ppPage = 0; | | | | | | | | | 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 | int szPage = pCache->szPage; int szExtra = pCache->szExtra; assert( pcache.isInit ); assert( sqlite3_mutex_notheld(pcache.mutex) ); *ppPage = 0; pcacheEnterMutex(); /* 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); if( rc!=SQLITE_OK ){ return rc; } pcacheEnterMutex(); } } /* 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); } pcacheExitMutex(); *ppPage = p; return (p?SQLITE_OK:SQLITE_NOMEM); } /*************************************************** General Interfaces ****** ** ** Initialize and shutdown the page cache subsystem. Neither of these |
︙ | ︙ | |||
618 619 620 621 622 623 624 | p->bPurgeable = bPurgeable; p->xDestroy = xDestroy; p->xStress = xStress; p->pStress = pStress; p->nMax = 100; p->nMin = 10; | | | | 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 | p->bPurgeable = bPurgeable; p->xDestroy = xDestroy; p->xStress = xStress; p->pStress = pStress; p->nMax = 100; p->nMin = 10; pcacheEnterMutex(); if( bPurgeable ){ pcache.nMaxPage += p->nMax; pcache.nMinPage += p->nMin; } pcacheExitMutex(); } /* ** Change the page size for PCache object. This can only happen ** when the cache is empty. */ void sqlite3PcacheSetPageSize(PCache *pCache, int szPage){ |
︙ | ︙ | |||
649 650 651 652 653 654 655 | int createFlag, /* If true, create page if it does not exist already */ PgHdr **ppPage /* Write the page here */ ){ PgHdr *pPage; assert( pcache.isInit ); assert( pCache!=0 ); assert( pgno>0 ); | | | | | | 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 | int createFlag, /* If true, create page if it does not exist already */ PgHdr **ppPage /* Write the page here */ ){ PgHdr *pPage; assert( pcache.isInit ); assert( pCache!=0 ); assert( pgno>0 ); expensive_assert( pCache->nPinned==pcachePinnedCount(pCache) ); /* 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) ){ pcacheEnterMutex(); pcacheRemoveFromLruList(pPage); pcacheExitMutex(); pCache->nPinned++; } pCache->nRef++; } pPage->nRef++; *ppPage = pPage; return SQLITE_OK; } } } if( createFlag ){ int rc = SQLITE_OK; if( pCache->nHash<=pCache->nPage ){ rc = pcacheResizeHash(pCache, pCache->nHash<256 ? 256 : pCache->nHash*2); if( rc!=SQLITE_OK ){ return rc; } } rc = pcacheRecycleOrAlloc(pCache, ppPage); if( rc!=SQLITE_OK ){ |
︙ | ︙ | |||
700 701 702 703 704 705 706 | pCache->nPinned++; pcacheAddToList(&pCache->pClean, pPage); pcacheAddToHash(pPage); }else{ *ppPage = 0; } | | | | | 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 | pCache->nPinned++; pcacheAddToList(&pCache->pClean, pPage); pcacheAddToHash(pPage); }else{ *ppPage = 0; } expensive_assert( pCache->nPinned==pcachePinnedCount(pCache) ); return SQLITE_OK; } /* ** 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(); pcacheAddToLruList(p); pcacheExitMutex(); }else{ /* Move the page to the head of the caches dirty list. */ pcacheRemoveFromList(&pCache->pDirty, p); pcacheAddToList(&pCache->pDirty, p); } } } |
︙ | ︙ | |||
749 750 751 752 753 754 755 | assert( p->nRef==1 ); assert( 0==(p->flags&PGHDR_DIRTY) ); pCache = p->pCache; pCache->nRef--; pCache->nPinned--; pcacheRemoveFromList(&pCache->pClean, p); pcacheRemoveFromHash(p); | | | | 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 | assert( p->nRef==1 ); assert( 0==(p->flags&PGHDR_DIRTY) ); pCache = p->pCache; pCache->nRef--; pCache->nPinned--; pcacheRemoveFromList(&pCache->pClean, p); pcacheRemoveFromHash(p); pcacheEnterMutex(); pcachePageFree(p); pcacheExitMutex(); } /* ** Make sure the page is marked as dirty. If it isn't dirty already, ** make it so. */ void sqlite3PcacheMakeDirty(PgHdr *p){ |
︙ | ︙ | |||
781 782 783 784 785 786 787 | void sqlite3PcacheMakeClean(PgHdr *p){ PCache *pCache; if( (p->flags & PGHDR_DIRTY)==0 ) return; assert( p->apSave[0]==0 && p->apSave[1]==0 ); assert( p->flags & PGHDR_DIRTY ); pCache = p->pCache; pcacheRemoveFromList(&pCache->pDirty, p); | | | | | | | | | | 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 | void sqlite3PcacheMakeClean(PgHdr *p){ PCache *pCache; if( (p->flags & PGHDR_DIRTY)==0 ) return; assert( p->apSave[0]==0 && p->apSave[1]==0 ); assert( p->flags & PGHDR_DIRTY ); pCache = p->pCache; pcacheRemoveFromList(&pCache->pDirty, p); pcacheEnterMutex(); pcacheAddToList(&pCache->pClean, p); p->flags &= ~PGHDR_DIRTY; if( p->nRef==0 ){ pcacheAddToLruList(p); pCache->nPinned--; } expensive_assert( pCache->nPinned==pcachePinnedCount(pCache) ); pcacheExitMutex(); } /* ** Make every page in the cache clean. */ void sqlite3PcacheCleanAll(PCache *pCache){ PgHdr *p; pcacheEnterMutex(); while( (p = pCache->pDirty)!=0 ){ assert( p->apSave[0]==0 && p->apSave[1]==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(); } /* ** 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 ); pcacheRemoveFromHash(p); p->pgno = newPgno; if( newPgno==0 ){ p->flags |= PGHDR_REUSE_UNLIKELY; pcacheEnterMutex(); pcacheFree(p->apSave[0]); pcacheFree(p->apSave[1]); pcacheExitMutex(); p->apSave[0] = 0; p->apSave[1] = 0; sqlite3PcacheMakeClean(p); } pcacheAddToHash(p); } |
︙ | ︙ | |||
865 866 867 868 869 870 871 | /* ** Drop every cache entry whose page number is greater than "pgno". */ void sqlite3PcacheTruncate(PCache *pCache, Pgno pgno){ PgHdr *p, *pNext; PgHdr *pDirty = pCache->pDirty; | | | 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 | /* ** 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 ){ |
︙ | ︙ | |||
891 892 893 894 895 896 897 | /* 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); } } } | | | | | > > | | | | | | < | | | | | 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 | /* 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(); } /* ** 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.nMaxPage -= pCache->nMax; pcache.nMinPage -= pCache->nMin; } sqlite3_free(pCache->apHash); pcacheExitMutex(); } /* ** Preserve the content of the page. It is assumed that the content ** 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. ** ** This routine is used for in-memory databases only. ** ** Return SQLITE_OK or SQLITE_NOMEM if a memory allocation fails. */ int sqlite3PcachePreserve(PgHdr *p, int idJournal){ void *x; int sz; assert( p->pCache->bPurgeable==0 ); assert( p->apSave[idJournal]==0 ); sz = p->pCache->szPage; p->apSave[idJournal] = x = sqlite3PageMalloc( sz ); if( x==0 ) return SQLITE_NOMEM; memcpy(x, p->pData, sz); return SQLITE_OK; } /* ** 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(); } /* ** 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(); } /* ** Assert flags settings on all pages. Debugging only. */ void sqlite3PcacheAssertFlags(PCache *pCache, int trueMask, int falseMask){ PgHdr *p; |
︙ | ︙ | |||
987 988 989 990 991 992 993 | } /* ** Discard the contents of the cache. */ int sqlite3PcacheClear(PCache *pCache){ assert(pCache->nRef==0); | | | | 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 | } /* ** Discard the contents of the cache. */ int sqlite3PcacheClear(PCache *pCache){ assert(pCache->nRef==0); pcacheEnterMutex(); pcacheClear(pCache); pcacheExitMutex(); return SQLITE_OK; } /* ** Merge two lists of pages connected by pDirty and in pgno order. ** Do not both fixing the pPrevDirty pointers. */ |
︙ | ︙ | |||
1121 1122 1123 1124 1125 1126 1127 | PgHdr *p; assert( (orMask&PGHDR_NEED_SYNC)==0 ); /* Obtain the global mutex before modifying any PgHdr.flags variables ** or traversing the LRU list. */ | | | | | | | | 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 | PgHdr *p; assert( (orMask&PGHDR_NEED_SYNC)==0 ); /* Obtain the global mutex before modifying any PgHdr.flags variables ** or traversing the LRU list. */ pcacheEnterMutex(); 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) ){ pCache->pSynced = pCache->pDirtyTail; assert( !pCache->pSynced || (pCache->pSynced->flags&PGHDR_NEED_SYNC)==0 ); } pcacheExitMutex(); } /* ** Set the suggested cache-size value. */ int sqlite3PcacheGetCachesize(PCache *pCache){ return pCache->nMax; } /* ** Set the suggested cache-size value. */ void sqlite3PcacheSetCachesize(PCache *pCache, int mxPage){ if( mxPage<10 ){ mxPage = 10; } if( pCache->bPurgeable ){ pcacheEnterMutex(); pcache.nMaxPage -= pCache->nMax; pcache.nMaxPage += mxPage; pcacheExitMutex(); } pCache->nMax = mxPage; } #ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT /* ** 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.pStart==0 ){ PgHdr *p; pcacheEnterMutex(); while( (nReq<0 || nFree<nReq) && (p=pcacheRecyclePage()) ){ nFree += pcachePageSize(p); pcachePageFree(p); } pcacheExitMutex(); } return nFree; } #endif /* SQLITE_ENABLE_MEMORY_MANAGEMENT */ |
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 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 | ** 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.8 2008/08/28 02:26:07 drh Exp $ */ #ifndef _PCACHE_H_ typedef struct PgHdr PgHdr; typedef struct PCache PCache; /* ** Every page in the cache is controlled by an instance of the following ** structure. */ struct PgHdr { void *pData; /* Content of this page */ void *pExtra; /* Extra content */ PgHdr *pDirty; /* Transient list of dirty pages */ Pgno pgno; /* Page number for this page */ Pager *pPager; /* The pager this page is part of */ #ifdef SQLITE_CHECK_PAGES u32 pageHash; /* Hash of page content */ #endif u16 flags; /* PGHDR flags defined below */ /********************************************************************** ** Elements above are public. All that follows is private to pcache.c ** and should not be accessed by other modules. */ i16 nRef; /* Number of users of this page */ PCache *pCache; /* Cache that owns this page */ void *apSave[2]; /* Journal entries for in-memory databases */ /********************************************************************** ** Elements above are accessible at any time by the owner of the cache ** without the need for a mutex. The elements that follow can only be ** accessed while holding the SQLITE_MUTEX_STATIC_LRU mutex. */ PgHdr *pNextHash, *pPrevHash; /* Hash collision chain for PgHdr.pgno */ PgHdr *pNext, *pPrev; /* List of clean or dirty pages */ PgHdr *pNextLru, *pPrevLru; /* Part of global LRU list */ }; /* Bit values for PgHdr.flags */ #define PGHDR_IN_JOURNAL 0x001 /* Page is in rollback journal */ #define PGHDR_IN_STMTJRNL 0x002 /* Page is in the statement journal */ #define PGHDR_DIRTY 0x004 /* Page has changed */ #define PGHDR_NEED_SYNC 0x008 /* Peed to fsync this page */ |
︙ | ︙ |
Changes to src/test_func.c.
︙ | ︙ | |||
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. ** ************************************************************************* ** Code for testing all sorts of SQLite interfaces. This code ** implements new SQL functions used by the test scripts. ** | | | 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. ** ************************************************************************* ** Code for testing all sorts of SQLite interfaces. This code ** implements new SQL functions used by the test scripts. ** ** $Id: test_func.c,v 1.13 2008/08/28 02:26:07 drh Exp $ */ #include "sqlite3.h" #include "tcl.h" #include <stdlib.h> #include <string.h> #include <assert.h> |
︙ | ︙ | |||
213 214 215 216 217 218 219 | ** in a result set. */ static void counterFunc( sqlite3_context *pCtx, /* Function context */ int nArg, /* Number of function arguments */ sqlite3_value **argv /* Values for all function arguments */ ){ | < < < | | 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 | ** in a result set. */ static void counterFunc( sqlite3_context *pCtx, /* Function context */ int nArg, /* Number of function arguments */ sqlite3_value **argv /* Values for all function arguments */ ){ int *pCounter = (int*)sqlite3_get_auxdata(pCtx, 0); if( pCounter==0 ){ pCounter = sqlite3_malloc( sizeof(*pCounter) ); if( pCounter==0 ){ sqlite3_result_error_nomem(pCtx); return; } *pCounter = sqlite3_value_int(argv[0]); |
︙ | ︙ |