SQLite

Check-in [473c09fac2]
Login

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

Overview
Comment:Enable the disabled asserts added by (5629). Add extra tests to thread003.test. And the required modifications to pcache.c. (CVS 5631)
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 473c09fac22ed2f56ea86150a60b9f0f2263c889
User & Date: danielk1977 2008-08-28 10:21:17.000
Context
2008-08-28
11:12
Fix mutex related bug in pcache.c to do with handling IO errors. (CVS 5632) (check-in: 5e304fed27 user: danielk1977 tags: trunk)
10:21
Enable the disabled asserts added by (5629). Add extra tests to thread003.test. And the required modifications to pcache.c. (CVS 5631) (check-in: 473c09fac2 user: danielk1977 tags: trunk)
08:31
Fix a threads/mutex problem in pcache.c. (CVS 5630) (check-in: 1928f15b78 user: danielk1977 tags: trunk)
Changes
Unified Diff Ignore Whitespace Patch
Changes to src/pcache.c.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/*
** 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.20 2008/08/28 08:31:48 danielk1977 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













|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/*
** 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.21 2008/08/28 10:21:17 danielk1977 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
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
*/
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;







<









|







169
170
171
172
173
174
175

176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
*/
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);

}
#endif /* !NDEBUG && SQLITE_ENABLE_EXPENSIVE_ASSERT */



/*
** Remove a page from its hash table (PCache.apHash[]).
*/
static void pcacheRemoveFromHash(PgHdr *pPage){
  assert( pcacheMutexHeld() );
  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;
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
** 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;







|

















|





>
|
>







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
** 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() );
  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() );
#ifdef SQLITE_MALLOC_SOFT_LIMIT
  if( nHash*sizeof(PgHdr*)>SQLITE_MALLOC_SOFT_LIMIT ){
    nHash = SQLITE_MALLOC_SOFT_LIMIT/sizeof(PgHdr *);
  }
#endif
  pcacheExitMutex();
  pNew = (PgHdr **)sqlite3Malloc(sizeof(PgHdr*)*nHash);
  pcacheEnterMutex();
  if( !pNew ){
    return SQLITE_NOMEM;
  }
  memset(pNew, 0, sizeof(PgHdr *)*nHash);
  sqlite3_free(pCache->apHash);
  pCache->apHash = pNew;
  pCache->nHash = nHash;
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
/*
** 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;
  }







|







254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
/*
** 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 );

  if( pPage->pPrev ){
    pPage->pPrev->pNext = pPage->pNext;
  }else{
    assert( *ppHead==pPage );
    *ppHead = pPage->pNext;
  }
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
static int pcacheRecycleOrAlloc(PCache *pCache, PgHdr **ppPage){
  PgHdr *p = 0;

  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) );







|


<







540
541
542
543
544
545
546
547
548
549

550
551
552
553
554
555
556
static int pcacheRecycleOrAlloc(PCache *pCache, PgHdr **ppPage){
  PgHdr *p = 0;

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

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

  *ppPage = 0;


  /* If we have reached the limit for pinned/dirty pages, and there is at
  ** least one dirty page, invoke the xStress callback to cause a page to
  ** become clean.
  */
  expensive_assert( pCache->nPinned==pcachePinnedCount(pCache) );
  expensive_assert( pcacheCheckSynced(pCache) );
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
    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 







<







590
591
592
593
594
595
596

597
598
599
600
601
602
603
    p = 0;
  }

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


  *ppPage = p;
  return (p?SQLITE_OK:SQLITE_NOMEM);
}

/*************************************************** General Interfaces ******
**
** Initialize and shutdown the page cache subsystem. Neither of these 
672
673
674
675
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
717
718
719
720

721
722
723
724
725
726
727
728
729
730
731
732
733
734




735

736
737
738
739
740
741
742
743
*/
int sqlite3PcacheFetch(
  PCache *pCache,       /* Obtain the page from this cache */
  Pgno pgno,            /* Page number to obtain */
  int createFlag,       /* If true, create page if it does not exist already */
  PgHdr **ppPage        /* Write the page here */
){

  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;
    pcacheEnterMutex();
    for(pPage=pCache->apHash[h]; pPage; pPage=pPage->pNextHash){
      if( pPage->pgno==pgno ){
        if( pPage->nRef==0 ){
          if( 0==(pPage->flags&PGHDR_DIRTY) ){
            pcacheRemoveFromLruList(pPage);
            pCache->nPinned++;
          }
          pCache->nRef++;
        }
        pPage->nRef++;
        *ppPage = pPage;
        pcacheExitMutex();
        return SQLITE_OK;
      }
    }
    pcacheExitMutex();
  }

  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 ){
      return rc;
    }
    pPage = *ppPage;

    pPage->pPager = 0;
    pPage->flags = 0;
    pPage->pDirty = 0;
    pPage->pgno = pgno;
    pPage->pCache = pCache;
    pPage->nRef = 1;
    pCache->nRef++;
    pCache->nPinned++;
    pcacheAddToList(&pCache->pClean, pPage);
    pcacheAddToHash(pPage);
  }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){







>
|
>




>
>




<










<
<
|


<


|
<


<
<
|
<
|
|
<
<

<
>
|
|
|
|
|
|
|
|
|
|
<
<
|
|
>
>
>
>

>
|







671
672
673
674
675
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
717
718
719
720
721
722
723


724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
*/
int sqlite3PcacheFetch(
  PCache *pCache,       /* Obtain the page from this cache */
  Pgno pgno,            /* Page number to obtain */
  int createFlag,       /* If true, create page if it does not exist already */
  PgHdr **ppPage        /* Write the page here */
){
  int rc = SQLITE_OK;
  PgHdr *pPage = 0;

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

  pcacheEnterMutex();

  /* Search the hash table for the requested page. Exit early if it is found. */
  if( pCache->apHash ){
    u32 h = pgno % pCache->nHash;

    for(pPage=pCache->apHash[h]; pPage; pPage=pPage->pNextHash){
      if( pPage->pgno==pgno ){
        if( pPage->nRef==0 ){
          if( 0==(pPage->flags&PGHDR_DIRTY) ){
            pcacheRemoveFromLruList(pPage);
            pCache->nPinned++;
          }
          pCache->nRef++;
        }
        pPage->nRef++;


        break;
      }
    }

  }

  if( !pPage && createFlag ){

    if( pCache->nHash<=pCache->nPage ){
      rc = pcacheResizeHash(pCache, pCache->nHash<256 ? 256 : pCache->nHash*2);


    }

    if( rc==SQLITE_OK ){
      rc = pcacheRecycleOrAlloc(pCache, &pPage);


    }

    if( rc==SQLITE_OK ){
      pPage->pPager = 0;
      pPage->flags = 0;
      pPage->pDirty = 0;
      pPage->pgno = pgno;
      pPage->pCache = pCache;
      pPage->nRef = 1;
      pCache->nRef++;
      pCache->nPinned++;
      pcacheAddToList(&pCache->pClean, pPage);
      pcacheAddToHash(pPage);


    }
  }

  pcacheExitMutex();

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

/*
** Dereference a page.  When the reference count reaches zero,
** move the page to the LRU list if it is clean.
*/
void sqlite3PcacheRelease(PgHdr *p){
775
776
777
778
779
780
781

782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799

800
801

802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823










824

825
826
827
828
829
830
831
void sqlite3PcacheDrop(PgHdr *p){
  PCache *pCache;
  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){
  PCache *pCache;
  p->flags &= ~PGHDR_DONT_WRITE;
  if( p->flags & PGHDR_DIRTY ) return;
  assert( (p->flags & PGHDR_DIRTY)==0 );
  assert( p->nRef>0 );
  pCache = p->pCache;

  pcacheRemoveFromList(&pCache->pClean, p);
  pcacheAddToList(&pCache->pDirty, p);

  p->flags |= PGHDR_DIRTY;
}

/*
** Make sure the page is marked as clean.  If it isn't clean already,
** make it so.
*/
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;







>


<















>


>



<
<
<
<
|
|
<


<

<







>
>
>
>
>
>
>
>
>
>
|
>







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
798
799
800
801
802




803
804

805
806

807

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
void sqlite3PcacheDrop(PgHdr *p){
  PCache *pCache;
  assert( p->nRef==1 );
  assert( 0==(p->flags&PGHDR_DIRTY) );
  pCache = p->pCache;
  pCache->nRef--;
  pCache->nPinned--;
  pcacheEnterMutex();
  pcacheRemoveFromList(&pCache->pClean, p);
  pcacheRemoveFromHash(p);

  pcachePageFree(p);
  pcacheExitMutex();
}

/*
** Make sure the page is marked as dirty.  If it isn't dirty already,
** make it so.
*/
void sqlite3PcacheMakeDirty(PgHdr *p){
  PCache *pCache;
  p->flags &= ~PGHDR_DONT_WRITE;
  if( p->flags & PGHDR_DIRTY ) return;
  assert( (p->flags & PGHDR_DIRTY)==0 );
  assert( p->nRef>0 );
  pCache = p->pCache;
  pcacheEnterMutex();
  pcacheRemoveFromList(&pCache->pClean, p);
  pcacheAddToList(&pCache->pDirty, p);
  pcacheExitMutex();
  p->flags |= PGHDR_DIRTY;
}





void pcacheMakeClean(PgHdr *p){
  PCache *pCache = p->pCache;

  assert( p->apSave[0]==0 && p->apSave[1]==0 );
  assert( p->flags & PGHDR_DIRTY );

  pcacheRemoveFromList(&pCache->pDirty, p);

  pcacheAddToList(&pCache->pClean, p);
  p->flags &= ~PGHDR_DIRTY;
  if( p->nRef==0 ){
    pcacheAddToLruList(p);
    pCache->nPinned--;
  }
  expensive_assert( pCache->nPinned==pcachePinnedCount(pCache) );
}

/*
** Make sure the page is marked as clean.  If it isn't clean already,
** make it so.
*/
void sqlite3PcacheMakeClean(PgHdr *p){
  if( (p->flags & PGHDR_DIRTY) ){
    pcacheEnterMutex();
    pcacheMakeClean(p);
    pcacheExitMutex();
  }
}

/*
** Make every page in the cache clean.
*/
void sqlite3PcacheCleanAll(PCache *pCache){
  PgHdr *p;
848
849
850
851
852
853
854

855
856
857
858
859
860
861
862
863
864

865
866

867

868
869
870
871
872
873
874
/*
** 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);

}

/*
** Remove all content from a page cache
*/
void pcacheClear(PCache *pCache){
  PgHdr *p, *pNext;







>




<


<


>
|
|
>

>







850
851
852
853
854
855
856
857
858
859
860
861

862
863

864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
/*
** Change the page number of page p to newPgno. If newPgno is 0, then the
** page object is added to the clean-list and the PGHDR_REUSE_UNLIKELY 
** flag set.
*/
void sqlite3PcacheMove(PgHdr *p, Pgno newPgno){
  assert( p->nRef>0 );
  pcacheEnterMutex();
  pcacheRemoveFromHash(p);
  p->pgno = newPgno;
  if( newPgno==0 ){
    p->flags |= PGHDR_REUSE_UNLIKELY;

    pcacheFree(p->apSave[0]);
    pcacheFree(p->apSave[1]);

    p->apSave[0] = 0;
    p->apSave[1] = 0;
    if( (p->flags & PGHDR_DIRTY) ){
      pcacheMakeClean(p);
    }
  }
  pcacheAddToHash(p);
  pcacheExitMutex();
}

/*
** Remove all content from a page cache
*/
void pcacheClear(PCache *pCache){
  PgHdr *p, *pNext;
Changes to test/thread003.test.
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#    May you share freely, never taking more than you give.
#
#***********************************************************************
#
#   This file contains tests that attempt to break the pcache module
#   by bombarding it with simultaneous requests from multiple threads.
#     
# $Id: thread003.test,v 1.1 2008/08/28 08:31:48 danielk1977 Exp $

set testdir [file dirname $argv0]

source $testdir/tester.tcl
source $testdir/thread_common.tcl
if {[info commands sqlthread] eq ""} {
  finish_test







|







8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#    May you share freely, never taking more than you give.
#
#***********************************************************************
#
#   This file contains tests that attempt to break the pcache module
#   by bombarding it with simultaneous requests from multiple threads.
#     
# $Id: thread003.test,v 1.2 2008/08/28 10:21:17 danielk1977 Exp $

set testdir [file dirname $argv0]

source $testdir/tester.tcl
source $testdir/thread_common.tcl
if {[info commands sqlthread] eq ""} {
  finish_test
102
103
104
105
106
107
108
109
110
111
112














































































113
114
115
    unset -nocomplain finished($zFile)
    thread_spawn finished($zFile) $thread_procs $SCRIPT
  }
  foreach zFile {test.db test2.db} {
    if {![info exists finished($zFile)]} {
      vwait finished($zFile)
    }
  }
  expr 0
} {0}















































































finish_test













>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>



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
    unset -nocomplain finished($zFile)
    thread_spawn finished($zFile) $thread_procs $SCRIPT
  }
  foreach zFile {test.db test2.db} {
    if {![info exists finished($zFile)]} {
      vwait finished($zFile)
    }
  }
  expr 0
} {0}

# This test is the same as the test above, except that each thread also
# writes to the database. This causes pages to be moved back and forth 
# between the caches internal dirty and clean lists, which is another
# opportunity for a thread-related bug to present itself.
#
set nSecond 30
puts "Starting thread003.3 (should run for ~$nSecond seconds)"
do_test thread003.3 {
  foreach zFile {test.db test2.db} {
    set SCRIPT [format {
      set iStart [clock seconds]
      set iEnd [expr {[clock seconds] + %d}]
      set ::DB [sqlthread open %s]
  
      # Set the cache size to 15 pages per cache. 30 available globally.
      execsql { PRAGMA cache_size = 15 }
  
      while {[clock seconds] < $iEnd} {
        set iQuery [expr {int(rand()*5000)}]
        execsql "SELECT * FROM t1 WHERE a = $iQuery"
        execsql "UPDATE t1 SET b = randomblob(200) 
                 WHERE a < $iQuery AND a > $iQuery + 20
        "
      }
  
      sqlite3_close $::DB
      expr 1
    } $nSecond $zFile]
  
    unset -nocomplain finished($zFile)
    thread_spawn finished($zFile) $thread_procs $SCRIPT
  }
  foreach zFile {test.db test2.db} {
    if {![info exists finished($zFile)]} {
      vwait finished($zFile)
    }
  }
  expr 0
} {0}

# In this test case, one thread is continually querying the database.
# The other thread does not have a database connection, but calls
# sqlite3_release_memory() over and over again.
#
set nSecond 30
puts "Starting thread003.3 (should run for ~$nSecond seconds)"
do_test thread003.4 {
  thread_spawn finished(1) $thread_procs [format {
    set iEnd [expr {[clock seconds] + %d}]
    set ::DB [sqlthread open test.db]

    # Set the cache size to 15 pages per cache. 30 available globally.
    execsql { PRAGMA cache_size = 15 }

    while {[clock seconds] < $iEnd} {
      set iQuery [expr {int(rand()*5000)}]
      execsql "SELECT * FROM t1 WHERE a = $iQuery"
    }

    sqlite3_close $::DB
    expr 1
  } $nSecond] 
  thread_spawn finished(2) [format {
    set iEnd [expr {[clock seconds] + %d}]

    while {[clock seconds] < $iEnd} {
      sqlite3_release_memory 1000
    }
  } $nSecond]
  
  foreach ii {1 2} {
    if {![info exists finished($ii)]} {
      vwait finished($ii)
    }
  }
  expr 0
} {0}

finish_test