/ Check-in [d2f69e5e]
Login

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

Overview
Comment:Defer the exclusive db lock until the pager cache is flushed to disk. 41 tests now fail. (CVS 1528)
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1:d2f69e5ef2f261a00bb8427a4e2a1638ecfd17a9
User & Date: danielk1977 2004-06-04 10:38:30
Context
2004-06-05
00:01
Critical bugs fixed in btree.c. Incompatible file format change. Unrelated comment fix in select.c (CVS 1530) check-in: cb1ffabf user: drh tags: trunk
2004-06-04
10:38
Defer the exclusive db lock until the pager cache is flushed to disk. 41 tests now fail. (CVS 1528) check-in: d2f69e5e user: danielk1977 tags: trunk
06:22
Move the 'busy-callback' logic to the pager layer. (CVS 1527) check-in: ff70b6d2 user: danielk1977 tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/os.h.

80
81
82
83
84
85
86

87
88
89
90
91
92
93
# define TEMP_FILE_PREFIX "sqlite_"
#endif

/*
** The following values may be passed as the second argument to
** sqlite3OsLock().
*/

#define SHARED_LOCK     1
#define RESERVED_LOCK   2
#define PENDING_LOCK    3
#define EXCLUSIVE_LOCK  4

int sqlite3OsDelete(const char*);
int sqlite3OsFileExists(const char*);







>







80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
# define TEMP_FILE_PREFIX "sqlite_"
#endif

/*
** The following values may be passed as the second argument to
** sqlite3OsLock().
*/
#define NO_LOCK         0
#define SHARED_LOCK     1
#define RESERVED_LOCK   2
#define PENDING_LOCK    3
#define EXCLUSIVE_LOCK  4

int sqlite3OsDelete(const char*);
int sqlite3OsFileExists(const char*);

Changes to src/os_unix.c.

637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
  if( fstat(id->fd, &buf)!=0 ){
    return SQLITE_IOERR;
  }
  *pSize = buf.st_size;
  return SQLITE_OK;
}


/*
** Change the status of the lock on the file "id" to be a readlock.
** If the file was write locked, then this reduces the lock to a read.
** If the file was read locked, then this acquires a new read lock.
**
** Return SQLITE_OK on success and SQLITE_BUSY on failure.  If this
** library was compiled with large file support (LFS) but LFS is not
** available on the host, then an SQLITE_NOLFS is returned.
*/
int sqlite3OsReadLock(OsFile *id){
  return sqlite3OsLock(id, SHARED_LOCK);
}

/*
** Change the lock status to be an exclusive or write lock.  Return
** SQLITE_OK on success and SQLITE_BUSY on a failure.  If this
** library was compiled with large file support (LFS) but LFS is not
** available on the host, then an SQLITE_NOLFS is returned.
*/
int sqlite3OsWriteLock(OsFile *id){
  return sqlite3OsLock(id, EXCLUSIVE_LOCK);
}

/*
** This routine checks if there is a RESERVED lock held on the specified
** file by this or any other process. If such a lock is held, return
** non-zero, otherwise zero.
*/
int sqlite3OsCheckWriteLock(OsFile *id){
  int r = 0;







<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<







637
638
639
640
641
642
643
























644
645
646
647
648
649
650
  if( fstat(id->fd, &buf)!=0 ){
    return SQLITE_IOERR;
  }
  *pSize = buf.st_size;
  return SQLITE_OK;
}

























/*
** This routine checks if there is a RESERVED lock held on the specified
** file by this or any other process. If such a lock is held, return
** non-zero, otherwise zero.
*/
int sqlite3OsCheckWriteLock(OsFile *id){
  int r = 0;

Changes to src/pager.c.

14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
...
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
...
654
655
656
657
658
659
660

661
662

663
664
665
666
667






668

669





670
671
672
673
674
675
676
....
1528
1529
1530
1531
1532
1533
1534

1535
1536
1537






























1538
1539
1540
1541
1542
1543
1544
....
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
....
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
....
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
....
2511
2512
2513
2514
2515
2516
2517
2518
2519
2520
2521
2522
2523
2524
2525
** 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.111 2004/06/04 06:22:01 danielk1977 Exp $
*/
#include "os.h"         /* Must be first to enable large file support */
#include "sqliteInt.h"
#include "pager.h"
#include <assert.h>
#include <string.h>

................................................................................
      pPg->inJournal = 0;
      pPg->dirty = 0;
      pPg->needSync = 0;
    }
  }else{
    assert( pPager->dirtyFile==0 || pPager->useJournal==0 );
  }
  rc = sqlite3OsReadLock(&pPager->fd);
  if( rc==SQLITE_OK ){
    pPager->state = SQLITE_READLOCK;
  }else{
    /* This can only happen if a process does a BEGIN, then forks and the
    ** child process does the COMMIT.  Because of the semantics of unix
    ** file locking, the unlock will fail.
    */
................................................................................
      if( sqlite3OsFileExists(zJournal) ){
        /* One of the journals pointed to by the master journal exists.
        ** Open it and check if it points at the master journal. If
        ** so, return without deleting the master journal file.
        */
        OsFile journal;
        int nMaster;


        rc = sqlite3OsOpenReadOnly(zJournal, &journal);

        if( rc!=SQLITE_OK ){
          sqlite3OsClose(&journal);
          sqliteFree(zJournal);
          goto delmaster_out;
        }






        sqlite3OsClose(&journal);







        /* Seek to the point in the journal where the master journal name
        ** is stored. Read the master journal name into memory obtained
        ** from malloc.
        */
        rc = sqlite3OsSeek(&journal, sizeof(aJournalMagic3)+2*sizeof(u32));
        if( rc!=SQLITE_OK ) goto delmaster_out;
        rc = read32bits(3, &journal, (u32 *)&nMaster);
................................................................................
** Given a list of pages (connected by the PgHdr.pDirty pointer) write
** every one of those pages out to the database file and mark them all
** as clean.
*/
static int pager_write_pagelist(PgHdr *pList){
  Pager *pPager;
  int rc;


  if( pList==0 ) return SQLITE_OK;
  pPager = pList->pPager;






























  while( pList ){
    assert( pList->dirty );
    sqlite3OsSeek(&pPager->fd, (pList->pgno-1)*(off_t)SQLITE_PAGE_SIZE);
    CODEC(pPager, PGHDR_TO_DATA(pList), pList->pgno, 6);
    TRACE2("STORE %d\n", pList->pgno);
    rc = sqlite3OsWrite(&pPager->fd, PGHDR_TO_DATA(pList), SQLITE_PAGE_SIZE);
    CODEC(pPager, PGHDR_TO_DATA(pList), pList->pgno, 0);
................................................................................
  }

  /* If this is the first page accessed, then get a SHARED lock
  ** on the database file.
  */
  if( pPager->nRef==0 && !pPager->memDb ){
    int busy = 1;
    while( busy ){
      rc = sqlite3OsReadLock(&pPager->fd);
      if( rc==SQLITE_BUSY && 
          pPager->pBusyHandler && 
          pPager->pBusyHandler->xFunc && 
          pPager->pBusyHandler->xFunc(pPager->pBusyHandler->pArg, "", busy++)
      ){
        rc = SQLITE_OK;
      }else{
        busy = 0;
      }
      if( rc!=SQLITE_OK ){
        return rc;
      }
    }
    pPager->state = SQLITE_READLOCK;

    /* If a journal file exists, and there is no RESERVED lock on the
    ** database file, then it either needs to be played back or deleted.
    */
    if( pPager->useJournal && 
................................................................................
  int rc;
  assert( pPager->state==SQLITE_WRITELOCK );
  assert( pPager->journalOpen==0 );
  assert( pPager->useJournal );
  sqlite3pager_pagecount(pPager);
  pPager->aInJournal = sqliteMalloc( pPager->dbSize/8 + 1 );
  if( pPager->aInJournal==0 ){
    sqlite3OsReadLock(&pPager->fd);
    pPager->state = SQLITE_READLOCK;
    return SQLITE_NOMEM;
  }
  rc = sqlite3OsOpenExclusive(pPager->zJournal, &pPager->jfd,pPager->tempFile);
  if( rc!=SQLITE_OK ){
    sqliteFree(pPager->aInJournal);
    pPager->aInJournal = 0;
    sqlite3OsReadLock(&pPager->fd);
    pPager->state = SQLITE_READLOCK;
    return SQLITE_CANTOPEN;
  }
  sqlite3OsOpenDirectory(pPager->zDirectory, &pPager->jfd);
  pPager->journalOpen = 1;
  pPager->journalStarted = 0;
  pPager->needSync = 0;
................................................................................
  if( pPager->state==SQLITE_READLOCK ){
    assert( pPager->aInJournal==0 );
    if( pPager->memDb ){
      pPager->state = SQLITE_WRITELOCK;
      pPager->origDbSize = pPager->dbSize;
    }else{
      int busy = 1;
      while( busy ){
        rc = sqlite3OsWriteLock(&pPager->fd);
        if( rc==SQLITE_BUSY && 
            pPager->pBusyHandler && 
            pPager->pBusyHandler->xFunc && 
            pPager->pBusyHandler->xFunc(pPager->pBusyHandler->pArg, "", busy++)
        ){
          rc = SQLITE_OK;
        }else{
          busy = 0;
        }
        if( rc!=SQLITE_OK ){
          return rc;
        }
      }
      pPager->nMaster = nMaster;
      pPager->state = SQLITE_WRITELOCK;
      pPager->dirtyFile = 0;
      TRACE1("TRANSACTION\n");
      if( pPager->useJournal && !pPager->tempFile ){
        rc = pager_open_journal(pPager);
................................................................................
  if( !pPager->journalOpen ){
    pPager->stmtAutoopen = 1;
    return SQLITE_OK;
  }
  assert( pPager->journalOpen );
  pPager->aInStmt = sqliteMalloc( pPager->dbSize/8 + 1 );
  if( pPager->aInStmt==0 ){
    sqlite3OsReadLock(&pPager->fd);
    return SQLITE_NOMEM;
  }
#ifndef NDEBUG
  rc = sqlite3OsFileSize(&pPager->jfd, &pPager->stmtJSize);
  if( rc ) goto stmt_begin_failed;
  assert( pPager->stmtJSize == 
    pPager->nRec*JOURNAL_PG_SZ(journal_format) + 







|







 







|







 







>


>


<


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







 







>



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







 







|
|
|
|
|
|
|
<
<
<
<
|
|
<







 







|







|







 







|
|
|
|
|
|
|
|
|
|
|
|
|
|







 







|







14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
...
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
...
654
655
656
657
658
659
660
661
662
663
664
665
666

667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
....
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
....
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660




1661
1662

1663
1664
1665
1666
1667
1668
1669
....
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
....
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
....
2550
2551
2552
2553
2554
2555
2556
2557
2558
2559
2560
2561
2562
2563
2564
** 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.112 2004/06/04 10:38:31 danielk1977 Exp $
*/
#include "os.h"         /* Must be first to enable large file support */
#include "sqliteInt.h"
#include "pager.h"
#include <assert.h>
#include <string.h>

................................................................................
      pPg->inJournal = 0;
      pPg->dirty = 0;
      pPg->needSync = 0;
    }
  }else{
    assert( pPager->dirtyFile==0 || pPager->useJournal==0 );
  }
  rc = sqlite3OsLock(&pPager->fd, SHARED_LOCK);
  if( rc==SQLITE_OK ){
    pPager->state = SQLITE_READLOCK;
  }else{
    /* This can only happen if a process does a BEGIN, then forks and the
    ** child process does the COMMIT.  Because of the semantics of unix
    ** file locking, the unlock will fail.
    */
................................................................................
      if( sqlite3OsFileExists(zJournal) ){
        /* One of the journals pointed to by the master journal exists.
        ** Open it and check if it points at the master journal. If
        ** so, return without deleting the master journal file.
        */
        OsFile journal;
        int nMaster;
        off_t jsz;

        rc = sqlite3OsOpenReadOnly(zJournal, &journal);
        sqliteFree(zJournal);
        if( rc!=SQLITE_OK ){
          sqlite3OsClose(&journal);

          goto delmaster_out;
        }

	/* Check if the file is big enough to be a format 3 journal file
        ** with the required master journal name. If not, ignore it.
        */
        rc = sqlite3OsFileSize(&journal, &jsz);
        if( rc!=SQLITE_OK ){
          sqlite3OsClose(&journal);
          goto delmaster_out;
        }
        if( jsz<(sizeof(aJournalMagic3)+4*sizeof(u32)+strlen(zMaster)+1) ){
          sqlite3OsClose(&journal);
          continue;
        }
        
        /* Seek to the point in the journal where the master journal name
        ** is stored. Read the master journal name into memory obtained
        ** from malloc.
        */
        rc = sqlite3OsSeek(&journal, sizeof(aJournalMagic3)+2*sizeof(u32));
        if( rc!=SQLITE_OK ) goto delmaster_out;
        rc = read32bits(3, &journal, (u32 *)&nMaster);
................................................................................
** Given a list of pages (connected by the PgHdr.pDirty pointer) write
** every one of those pages out to the database file and mark them all
** as clean.
*/
static int pager_write_pagelist(PgHdr *pList){
  Pager *pPager;
  int rc;
  int busy = 1;

  if( pList==0 ) return SQLITE_OK;
  pPager = pList->pPager;

  /* At this point there may be either a RESERVED or EXCLUSIVE lock on the
  ** database file. If there is already an EXCLUSIVE lock, the following
  ** calls to sqlite3OsLock() are no-ops.
  **
  ** The upgrade from a RESERVED to PENDING lock cannot return SQLITE_BUSY,
  ** unless someone is not following the locking protocol. 
  **
  ** The upgrade from PENDING to EXCLUSIVE can return SQLITE_BUSY. It's
  ** not totally clear that the busy-callback should be invoked here
  ** though. (?)
  */
  rc = sqlite3OsLock(&pPager->fd, PENDING_LOCK);
  if( rc==SQLITE_BUSY ){
    return SQLITE_PROTOCOL;
  }
  if( rc!=SQLITE_OK ){
    return rc;
  }
  do {
    rc = sqlite3OsLock(&pPager->fd, EXCLUSIVE_LOCK);
  }while( rc==SQLITE_BUSY && 
      pPager->pBusyHandler && 
      pPager->pBusyHandler->xFunc && 
      pPager->pBusyHandler->xFunc(pPager->pBusyHandler->pArg, "", busy++)
  );
  if( rc!=SQLITE_OK ){
    return rc;
  }

  while( pList ){
    assert( pList->dirty );
    sqlite3OsSeek(&pPager->fd, (pList->pgno-1)*(off_t)SQLITE_PAGE_SIZE);
    CODEC(pPager, PGHDR_TO_DATA(pList), pList->pgno, 6);
    TRACE2("STORE %d\n", pList->pgno);
    rc = sqlite3OsWrite(&pPager->fd, PGHDR_TO_DATA(pList), SQLITE_PAGE_SIZE);
    CODEC(pPager, PGHDR_TO_DATA(pList), pList->pgno, 0);
................................................................................
  }

  /* If this is the first page accessed, then get a SHARED lock
  ** on the database file.
  */
  if( pPager->nRef==0 && !pPager->memDb ){
    int busy = 1;
    do {
      rc = sqlite3OsLock(&pPager->fd, SHARED_LOCK);
    }while( rc==SQLITE_BUSY && 
        pPager->pBusyHandler && 
        pPager->pBusyHandler->xFunc && 
        pPager->pBusyHandler->xFunc(pPager->pBusyHandler->pArg, "", busy++)
    );




    if( rc!=SQLITE_OK ){
      return rc;

    }
    pPager->state = SQLITE_READLOCK;

    /* If a journal file exists, and there is no RESERVED lock on the
    ** database file, then it either needs to be played back or deleted.
    */
    if( pPager->useJournal && 
................................................................................
  int rc;
  assert( pPager->state==SQLITE_WRITELOCK );
  assert( pPager->journalOpen==0 );
  assert( pPager->useJournal );
  sqlite3pager_pagecount(pPager);
  pPager->aInJournal = sqliteMalloc( pPager->dbSize/8 + 1 );
  if( pPager->aInJournal==0 ){
    sqlite3OsLock(&pPager->fd, SHARED_LOCK);
    pPager->state = SQLITE_READLOCK;
    return SQLITE_NOMEM;
  }
  rc = sqlite3OsOpenExclusive(pPager->zJournal, &pPager->jfd,pPager->tempFile);
  if( rc!=SQLITE_OK ){
    sqliteFree(pPager->aInJournal);
    pPager->aInJournal = 0;
    sqlite3OsLock(&pPager->fd, SHARED_LOCK);
    pPager->state = SQLITE_READLOCK;
    return SQLITE_CANTOPEN;
  }
  sqlite3OsOpenDirectory(pPager->zDirectory, &pPager->jfd);
  pPager->journalOpen = 1;
  pPager->journalStarted = 0;
  pPager->needSync = 0;
................................................................................
  if( pPager->state==SQLITE_READLOCK ){
    assert( pPager->aInJournal==0 );
    if( pPager->memDb ){
      pPager->state = SQLITE_WRITELOCK;
      pPager->origDbSize = pPager->dbSize;
    }else{
      int busy = 1;
      do {
	/* If the library grabs an EXCLUSIVE lock here, as in the commented
        ** out line, then it exhibits the old locking behaviour - a writer
        ** excludes all readers, not just other writers.
        */
        /* rc = sqlite3OsLock(&pPager->fd, EXCLUSIVE_LOCK); */
        rc = sqlite3OsLock(&pPager->fd, RESERVED_LOCK);
      }while( rc==SQLITE_BUSY && 
          pPager->pBusyHandler && 
          pPager->pBusyHandler->xFunc && 
          pPager->pBusyHandler->xFunc(pPager->pBusyHandler->pArg, "", busy++)
      );
      if( rc!=SQLITE_OK ){
        return rc;
      }
      pPager->nMaster = nMaster;
      pPager->state = SQLITE_WRITELOCK;
      pPager->dirtyFile = 0;
      TRACE1("TRANSACTION\n");
      if( pPager->useJournal && !pPager->tempFile ){
        rc = pager_open_journal(pPager);
................................................................................
  if( !pPager->journalOpen ){
    pPager->stmtAutoopen = 1;
    return SQLITE_OK;
  }
  assert( pPager->journalOpen );
  pPager->aInStmt = sqliteMalloc( pPager->dbSize/8 + 1 );
  if( pPager->aInStmt==0 ){
    sqlite3OsLock(&pPager->fd, SHARED_LOCK);
    return SQLITE_NOMEM;
  }
#ifndef NDEBUG
  rc = sqlite3OsFileSize(&pPager->jfd, &pPager->stmtJSize);
  if( rc ) goto stmt_begin_failed;
  assert( pPager->stmtJSize == 
    pPager->nRec*JOURNAL_PG_SZ(journal_format) +