SQLite4
Check-in [8d149a52d3]
Not logged in

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

Overview
Comment:Fix a bug in intra-process connection locking. Turn on multi-process mode by default.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | multi-process
Files: files | file ages | folders
SHA1: 8d149a52d3635f135bcad7377f129a87a2264669
User & Date: dan 2012-09-05 10:32:03
Context
2012-09-05
11:23
Merge in multi-process branch. check-in: ecae27d73a user: dan tags: trunk
10:32
Fix a bug in intra-process connection locking. Turn on multi-process mode by default. Leaf check-in: 8d149a52d3 user: dan tags: multi-process
2012-09-04
20:17
Defer closing file descriptors until all fcntl() locks have been dropped. check-in: 3d0cf4bb36 user: dan tags: multi-process
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/lsm_main.c.

80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
  pDb->nLogSz = LSM_DEFAULT_LOG_SIZE;
  pDb->nDfltPgsz = LSM_PAGE_SIZE;
  pDb->nDfltBlksz = LSM_BLOCK_SIZE;
  pDb->nMerge = LSM_DEFAULT_NMERGE;
  pDb->nMaxFreelist = LSM_MAX_FREELIST_ENTRIES;
  pDb->bUseLog = 1;
  pDb->iReader = -1;
  pDb->bMultiProc = 0;
  return LSM_OK;
}

lsm_env *lsm_get_env(lsm_db *pDb){
  assert( pDb->pEnv );
  return pDb->pEnv;
}







|







80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
  pDb->nLogSz = LSM_DEFAULT_LOG_SIZE;
  pDb->nDfltPgsz = LSM_PAGE_SIZE;
  pDb->nDfltBlksz = LSM_BLOCK_SIZE;
  pDb->nMerge = LSM_DEFAULT_NMERGE;
  pDb->nMaxFreelist = LSM_MAX_FREELIST_ENTRIES;
  pDb->bUseLog = 1;
  pDb->iReader = -1;
  pDb->bMultiProc = 1;
  return LSM_OK;
}

lsm_env *lsm_get_env(lsm_db *pDb){
  assert( pDb->pEnv );
  return pDb->pEnv;
}

Changes to src/lsm_shared.c.

988
989
990
991
992
993
994
995

996

997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
   || (eOp==LSM_LOCK_SHARED && (db->mLock & (me|ms))!=ms)
   || (eOp==LSM_LOCK_EXCL   && (db->mLock & me)==0)
  ){
    int nExcl = 0;                /* Number of connections holding EXCLUSIVE */
    int nShared = 0;              /* Number of connections holding SHARED */
    lsmMutexEnter(db->pEnv, p->pClientMutex);

    /* Figure out the locks currently held by this process on iLock. */

    for(pIter=p->pConn; pIter; pIter=pIter->pNext){

      if( pIter!=db ){
        if( pIter->mLock & me ){
          nExcl++;
        }else if( pIter->mLock & ms ){
          nShared++;
        }
      }
    }
    assert( nExcl==0 || nExcl==1 );
    assert( nExcl==0 || nShared==0 );
    assert( (db->mLock & me)==0 || (db->mLock & ms)!=0 );

    switch( eOp ){
      case LSM_LOCK_UNLOCK:
        if( nShared<=1 ){
          lsmEnvLock(db->pEnv, p->pFile, iLock, LSM_LOCK_UNLOCK);
        }
        db->mLock &= ~(me|ms);
        break;

      case LSM_LOCK_SHARED:
        if( nExcl && (db->mLock & me)==0 ){
          rc = LSM_BUSY;
        }else{
          if( nShared==0 ){
            rc = lsmEnvLock(db->pEnv, p->pFile, iLock, LSM_LOCK_SHARED);
          }
          db->mLock |= ms;
          db->mLock &= ~me;
        }
        break;

      default:
        assert( eOp==LSM_LOCK_EXCL );
        if( nExcl || nShared>1 || (nShared==1 && (db->mLock & ms)==0) ){
          rc = LSM_BUSY;
        }else{
          rc = lsmEnvLock(db->pEnv, p->pFile, iLock, LSM_LOCK_EXCL);
          db->mLock |= (me|ms);
        }
        break;
    }







|
>

>










|



|






|












|







988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
   || (eOp==LSM_LOCK_SHARED && (db->mLock & (me|ms))!=ms)
   || (eOp==LSM_LOCK_EXCL   && (db->mLock & me)==0)
  ){
    int nExcl = 0;                /* Number of connections holding EXCLUSIVE */
    int nShared = 0;              /* Number of connections holding SHARED */
    lsmMutexEnter(db->pEnv, p->pClientMutex);

    /* Figure out the locks currently held by this process on iLock, not
    ** including any held by connection db.  */
    for(pIter=p->pConn; pIter; pIter=pIter->pNext){
      assert( (pIter->mLock & me)==0 || (pIter->mLock & ms)!=0 );
      if( pIter!=db ){
        if( pIter->mLock & me ){
          nExcl++;
        }else if( pIter->mLock & ms ){
          nShared++;
        }
      }
    }
    assert( nExcl==0 || nExcl==1 );
    assert( nExcl==0 || nShared==0 );
    assert( nExcl==0 || (db->mLock & (me|ms))==0 );

    switch( eOp ){
      case LSM_LOCK_UNLOCK:
        if( nShared==0 ){
          lsmEnvLock(db->pEnv, p->pFile, iLock, LSM_LOCK_UNLOCK);
        }
        db->mLock &= ~(me|ms);
        break;

      case LSM_LOCK_SHARED:
        if( nExcl ){
          rc = LSM_BUSY;
        }else{
          if( nShared==0 ){
            rc = lsmEnvLock(db->pEnv, p->pFile, iLock, LSM_LOCK_SHARED);
          }
          db->mLock |= ms;
          db->mLock &= ~me;
        }
        break;

      default:
        assert( eOp==LSM_LOCK_EXCL );
        if( nExcl || nShared ){
          rc = LSM_BUSY;
        }else{
          rc = lsmEnvLock(db->pEnv, p->pFile, iLock, LSM_LOCK_EXCL);
          db->mLock |= (me|ms);
        }
        break;
    }

Changes to test/attach.test.

20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35

ifcapable !attach {
  finish_test
  return
}

for {set i 2} {$i<=15} {incr i} {
  forcedelete test$i.db
  forcedelete test$i.db-journal
}

do_test attach-1.1 {
  execsql {
    CREATE TABLE t1(a,b);
    INSERT INTO t1 VALUES(1,2);
    INSERT INTO t1 VALUES(3,4);







|
<







20
21
22
23
24
25
26
27

28
29
30
31
32
33
34

ifcapable !attach {
  finish_test
  return
}

for {set i 2} {$i<=15} {incr i} {
  db_delete test$i.db

}

do_test attach-1.1 {
  execsql {
    CREATE TABLE t1(a,b);
    INSERT INTO t1 VALUES(1,2);
    INSERT INTO t1 VALUES(3,4);

Changes to test/manydb.test.

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
#
# $Id: manydb.test,v 1.4 2008/11/21 00:10:35 aswift Exp $

set testdir [file dirname $argv0]
source $testdir/tester.tcl

set N 300
# if we're using proxy locks, we use 5 filedescriptors for a db
# that is open and in the middle of writing changes, normally
# sqlite uses 3 (proxy locking adds the conch and the local lock)
set using_proxy 0
foreach {name value} [array get env SQLITE4_FORCE_PROXY_LOCKING] {
  set using_proxy value
}
set num_fd_per_openwrite_db 3
if {$using_proxy>0} {
  set num_fd_per_openwrite_db 5
} 

# First test how many file descriptors are available for use. To open a
# database for writing SQLite requires 3 file descriptors (the database, the
# journal and the directory).
set filehandles {}
catch {
  for {set i 0} {$i<($N * 3)} {incr i} {
    lappend filehandles [open testfile.1 w]
  }
}
foreach fd $filehandles {
  close $fd
}
catch {







<
<
<
<
<
<
<
|
<
<
<






|







15
16
17
18
19
20
21







22



23
24
25
26
27
28
29
30
31
32
33
34
35
36
#
# $Id: manydb.test,v 1.4 2008/11/21 00:10:35 aswift Exp $

set testdir [file dirname $argv0]
source $testdir/tester.tcl

set N 300







set num_fd_per_openwrite_db 4




# First test how many file descriptors are available for use. To open a
# database for writing SQLite requires 3 file descriptors (the database, the
# journal and the directory).
set filehandles {}
catch {
  for {set i 0} {$i<($N * $num_fd_per_openwrite_db)} {incr i} {
    lappend filehandles [open testfile.1 w]
  }
}
foreach fd $filehandles {
  close $fd
}
catch {

Changes to test/tester.tcl.

17
18
19
20
21
22
23

24
25
26
27
28
29
30
...
355
356
357
358
359
360
361










362
363
364
365
366
367
368
369
370
371
372
373
374
375
....
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
....
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
# The commands provided by the code in this file to help with creating 
# test cases are as follows:
#
# Commands to manipulate the db and the file-system at a high level:
#
#      copy_file              FROM TO
#      delete_file            FILENAME

#      drop_all_tables        ?DB?
#      forcecopy              FROM TO
#      forcedelete            FILENAME
#
# Test the capability of the SQLite version built into the interpreter to
# determine if a specific test can be run:
#
................................................................................
  # If the --binarylog option was specified, create the logging VFS. This
  # call installs the new VFS as the default for all SQLite connections.
  #
  if {$cmdlinearg(binarylog)} {
    vfslog new binarylog {} vfslog.bin
  }
}











# Create a test database
#
proc reset_db {} {
  catch {db close}
  forcedelete test.db
  forcedelete test.db-log
  sqlite4 db ./test.db
  set ::DB [sqlite4_connection_pointer db]
  if {[info exists ::SETUP_SQL]} {
    db eval $::SETUP_SQL
  }
}
reset_db
................................................................................

    # Delete the files test.db and test2.db, then execute the TCL and 
    # SQL (in that order) to prepare for the test case.
    do_test $testname.$n.1 {
      set ::sqlite_io_error_pending 0
      catch {db close}
      catch {db2 close}
      catch {forcedelete test.db}
      catch {forcedelete test.db-journal}
      catch {forcedelete test2.db}
      catch {forcedelete test2.db-journal}
      set ::DB [sqlite4 db test.db; sqlite4_connection_pointer db]
      sqlite4_extended_result_codes $::DB $::ioerropts(-erc)
      if {[info exists ::ioerropts(-tclprep)]} {
        eval $::ioerropts(-tclprep)
      }
      if {[info exists ::ioerropts(-sqlprep)]} {
        execsql $::ioerropts(-sqlprep)
................................................................................
  db36231 close
  hexio_write test.db 28 $A
  hexio_write test.db 92 $B
  return ""
}

proc db_save {} {
  foreach f [glob -nocomplain sv_test.db*] { forcedelete $f }
  foreach f [glob -nocomplain test.db*] {
    set f2 "sv_$f"
    forcecopy $f $f2
  }
}
proc db_save_and_close {} {
  db_save
  catch { db close }
  return ""
}
proc db_restore {} {
  foreach f [glob -nocomplain test.db*] { forcedelete $f }
  foreach f2 [glob -nocomplain sv_test.db*] {
    set f [string range $f2 3 end]
    forcecopy $f2 $f
  }
}
proc db_restore_and_reopen {{dbfile test.db}} {
  catch { db close }
  db_restore
  sqlite4 db $dbfile
}
proc db_delete_and_reopen {{file test.db}} {
  catch { db close }
  foreach f [glob -nocomplain test.db*] { forcedelete $f }
  sqlite4 db $file
}

# Do an SQL statement.  Append the search count to the end of the result.
#
proc count {sql} {
  kvwrap reset







>







 







>
>
>
>
>
>
>
>
>
>





|
<







 







|
<
|
<







 







|











|












|







17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
...
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378

379
380
381
382
383
384
385
....
1042
1043
1044
1045
1046
1047
1048
1049

1050

1051
1052
1053
1054
1055
1056
1057
....
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
# The commands provided by the code in this file to help with creating 
# test cases are as follows:
#
# Commands to manipulate the db and the file-system at a high level:
#
#      copy_file              FROM TO
#      delete_file            FILENAME
#      db_delete              DBNAME
#      drop_all_tables        ?DB?
#      forcecopy              FROM TO
#      forcedelete            FILENAME
#
# Test the capability of the SQLite version built into the interpreter to
# determine if a specific test can be run:
#
................................................................................
  # If the --binarylog option was specified, create the logging VFS. This
  # call installs the new VFS as the default for all SQLite connections.
  #
  if {$cmdlinearg(binarylog)} {
    vfslog new binarylog {} vfslog.bin
  }
}

# Delete all files associated with LSM database $file. That is:
#
#     ${file}
#     ${file}-log
#     ${file}-shm
#
proc db_delete {file} {
  forcedelete $file $file-shm $file-log
}

# Create a test database
#
proc reset_db {} {
  catch {db close}
  db_delete test.db

  sqlite4 db ./test.db
  set ::DB [sqlite4_connection_pointer db]
  if {[info exists ::SETUP_SQL]} {
    db eval $::SETUP_SQL
  }
}
reset_db
................................................................................

    # Delete the files test.db and test2.db, then execute the TCL and 
    # SQL (in that order) to prepare for the test case.
    do_test $testname.$n.1 {
      set ::sqlite_io_error_pending 0
      catch {db close}
      catch {db2 close}
      catch {db_delete test.db}

      catch {db_delete test2.db}

      set ::DB [sqlite4 db test.db; sqlite4_connection_pointer db]
      sqlite4_extended_result_codes $::DB $::ioerropts(-erc)
      if {[info exists ::ioerropts(-tclprep)]} {
        eval $::ioerropts(-tclprep)
      }
      if {[info exists ::ioerropts(-sqlprep)]} {
        execsql $::ioerropts(-sqlprep)
................................................................................
  db36231 close
  hexio_write test.db 28 $A
  hexio_write test.db 92 $B
  return ""
}

proc db_save {} {
  db_delete sv_test.db
  foreach f [glob -nocomplain test.db*] {
    set f2 "sv_$f"
    forcecopy $f $f2
  }
}
proc db_save_and_close {} {
  db_save
  catch { db close }
  return ""
}
proc db_restore {} {
  db_delete test.db
  foreach f2 [glob -nocomplain sv_test.db*] {
    set f [string range $f2 3 end]
    forcecopy $f2 $f
  }
}
proc db_restore_and_reopen {{dbfile test.db}} {
  catch { db close }
  db_restore
  sqlite4 db $dbfile
}
proc db_delete_and_reopen {{file test.db}} {
  catch { db close }
  db_delete $file
  sqlite4 db $file
}

# Do an SQL statement.  Append the search count to the end of the result.
#
proc count {sql} {
  kvwrap reset