SQLite

Check-in [7dddbeb586]
Login

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

Overview
Comment:A better fix for ticket #530 - one that is likely to work on unix implementations in addition to linux. Also more tests for multi-thread locking added. (CVS 1138)
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 7dddbeb586504de30c64a1e61614da447f18c8ba
User & Date: drh 2003-12-19 08:40:23.000
Context
2003-12-19
12:31
Makefile updates for the new "test4.c" module. Also disable large file tests for archic TCL versions. (CVS 1139) (check-in: 55e2488912 user: drh tags: trunk)
08:40
A better fix for ticket #530 - one that is likely to work on unix implementations in addition to linux. Also more tests for multi-thread locking added. (CVS 1138) (check-in: 7dddbeb586 user: drh tags: trunk)
02:52
Add new thread-testing code and fix locking under Linux threads. Ticket #530. (CVS 1137) (check-in: b36a4bb610 user: drh tags: trunk)
Changes
Unified Diff Ignore Whitespace Patch
Changes to src/os.c.
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
** structure.  The fcntl() system call is only invoked to set a 
** POSIX lock if the internal lock structure transitions between
** a locked and an unlocked state.
*/

/*
** An instance of the following structure serves as the key used
** to locate a particular lockInfo structure given its inode. 



*/
struct inodeKey {
  dev_t dev;   /* Device number */
  ino_t ino;   /* Inode number */
#ifdef SQLITE_UNIX_THREADS
  pthread_t thread_id;   /* Which thread are we */
#endif
};

/*
** An instance of the following structure is allocated for each inode.
** A single inode can have multiple file descriptors, so each OsFile
** structure contains a pointer to an instance of this object and this
** object keeps a count of the number of OsFiles pointing to it.
*/
struct lockInfo {
  struct inodeKey key;  /* The lookup key */
  int cnt;              /* 0: unlocked.  -1: write lock.  1...: read lock. */
  int nRef;             /* Number of pointers to this structure */
};

/* 
** This hash table maps inodes (in the form of inodeKey structures) into
** pointers to lockInfo structures.
*/
static Hash lockHash = { SQLITE_HASH_BINARY, 0, 0, 0, 0, 0 };

/*
** Given a file descriptor, locate a lockInfo structure that describes
** that file descriptor.  Create a new one if necessary.  NULL might
** be returned if malloc() fails.
*/
static struct lockInfo *findLockInfo(int fd){
  int rc;
  struct inodeKey key;
  struct stat statbuf;
  struct lockInfo *pInfo;
  rc = fstat(fd, &statbuf);
  if( rc!=0 ) return 0;
  memset(&key, 0, sizeof(key));
  key.dev = statbuf.st_dev;
  key.ino = statbuf.st_ino;
#ifdef SQLITE_UNIX_THREADS
  key.thread_id = pthread_self();
#endif
  pInfo = (struct lockInfo*)sqliteHashFind(&lockHash, &key, sizeof(key));
  if( pInfo==0 ){
    struct lockInfo *pOld;
    pInfo = sqliteMalloc( sizeof(*pInfo) );
    if( pInfo==0 ) return 0;
    pInfo->key = key;
    pInfo->nRef = 1;







|
>
>
>

|


<
|
<









|





|











|







<
|
<







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
** structure.  The fcntl() system call is only invoked to set a 
** POSIX lock if the internal lock structure transitions between
** a locked and an unlocked state.
*/

/*
** An instance of the following structure serves as the key used
** to locate a particular lockInfo structure given its inode.  Note
** that we have to include the process ID as part of the key.  On some
** threading implementations (ex: linux), each thread has a separate
** process ID.
*/
struct lockKey {
  dev_t dev;   /* Device number */
  ino_t ino;   /* Inode number */

  pid_t pid;   /* Process ID */

};

/*
** An instance of the following structure is allocated for each inode.
** A single inode can have multiple file descriptors, so each OsFile
** structure contains a pointer to an instance of this object and this
** object keeps a count of the number of OsFiles pointing to it.
*/
struct lockInfo {
  struct lockKey key;  /* The lookup key */
  int cnt;              /* 0: unlocked.  -1: write lock.  1...: read lock. */
  int nRef;             /* Number of pointers to this structure */
};

/* 
** This hash table maps inodes (in the form of lockKey structures) into
** pointers to lockInfo structures.
*/
static Hash lockHash = { SQLITE_HASH_BINARY, 0, 0, 0, 0, 0 };

/*
** Given a file descriptor, locate a lockInfo structure that describes
** that file descriptor.  Create a new one if necessary.  NULL might
** be returned if malloc() fails.
*/
static struct lockInfo *findLockInfo(int fd){
  int rc;
  struct lockKey key;
  struct stat statbuf;
  struct lockInfo *pInfo;
  rc = fstat(fd, &statbuf);
  if( rc!=0 ) return 0;
  memset(&key, 0, sizeof(key));
  key.dev = statbuf.st_dev;
  key.ino = statbuf.st_ino;

  key.pid = getpid();

  pInfo = (struct lockInfo*)sqliteHashFind(&lockHash, &key, sizeof(key));
  if( pInfo==0 ){
    struct lockInfo *pOld;
    pInfo = sqliteMalloc( sizeof(*pInfo) );
    if( pInfo==0 ) return 0;
    pInfo->key = key;
    pInfo->nRef = 1;
Changes to test/thread1.test.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 2003 December 18
#
# 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 regression tests for SQLite library.  The
# focus of this script is multithreading behavior
#
# $Id: thread1.test,v 1.1 2003/12/19 02:52:09 drh Exp $


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

# Skip this whole file if the thread testing code is not enabled
#













|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 2003 December 18
#
# 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 regression tests for SQLite library.  The
# focus of this script is multithreading behavior
#
# $Id: thread1.test,v 1.2 2003/12/19 08:40:24 drh Exp $


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

# Skip this whole file if the thread testing code is not enabled
#
34
35
36
37
38
39
40
41








42
43
44
45
46
47
48
    INSERT INTO t1 SELECT a+2, b||b FROM t1;
    INSERT INTO t1 SELECT a+4, b||b FROM t1;
    SELECT count(*), max(length(b)) FROM t1;
  }
} {8 64}

# Interleave two threads on read access.  Then make sure a third
# thread can write the database.








#
do_test thread1-1.2 {
  thread_create A test.db
  thread_create B test.db
  thread_create C test.db
  thread_compile A {SELECT a FROM t1}
  thread_step A







|
>
>
>
>
>
>
>
>







34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
    INSERT INTO t1 SELECT a+2, b||b FROM t1;
    INSERT INTO t1 SELECT a+4, b||b FROM t1;
    SELECT count(*), max(length(b)) FROM t1;
  }
} {8 64}

# Interleave two threads on read access.  Then make sure a third
# thread can write the database.  In other words:
#
#    read-lock A
#    read-lock B
#    unlock A
#    unlock B
#    write-lock C
#
# At one point, the write-lock of C would fail on Linux. 
#
do_test thread1-1.2 {
  thread_create A test.db
  thread_create B test.db
  thread_create C test.db
  thread_compile A {SELECT a FROM t1}
  thread_step A
78
79
80
81
82
83
84




85
86










87
















































88
  thread_step C
  thread_result C
} SQLITE_DONE
do_test thread1-1.11 {
  thread_finalize C
  thread_result C
} SQLITE_OK
















thread_halt *   
















































finish_test







>
>
>
>


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

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
  thread_step C
  thread_result C
} SQLITE_DONE
do_test thread1-1.11 {
  thread_finalize C
  thread_result C
} SQLITE_OK
do_test thread1-1.12 {
  catchsql {SELECT name FROM sqlite_master}
  execsql {SELECT name FROM sqlite_master}
} {t1 t2}


# Under this scenario:
#
#    read-lock A
#    read-lock B
#    unlock A
#    write-lock C
#
# Make sure the write-lock fails with SQLITE_BUSY 
#
do_test thread1-2.1 {
  thread_halt *
  thread_create A test.db
  thread_compile A {SELECT a FROM t1}
  thread_step A
  thread_result A
} SQLITE_ROW
do_test thread1-2.2 {
  thread_create B test.db
  thread_compile B {SELECT b FROM t1}
  thread_step B
  thread_result B
} SQLITE_ROW
do_test thread1-2.3 {
  thread_create C test.db
  thread_compile C {INSERT INTO t2 VALUES(98,99)}
  thread_step C
  thread_result C
} SQLITE_BUSY
do_test thread1-2.4 {
  execsql {SELECT * FROM t2}
} {}
do_test thread1-2.5 {
  thread_finalize A
  thread_result A
} SQLITE_OK
do_test thread1-2.6 {
  thread_step C
  thread_result C
} SQLITE_BUSY
do_test thread1-2.7 {
  execsql {SELECT * FROM t2}
} {}
do_test thread1-2.8 {
  thread_finalize B
  thread_result B
} SQLITE_OK
do_test thread1-2.9 {
  thread_step C
  thread_result C
} SQLITE_DONE
do_test thread1-2.10 {
  execsql {SELECT * FROM t2}
} {98 99}
do_test thread1-2.11 {
  thread_finalize C
  thread_result C
} SQLITE_OK

thread_halt *   
finish_test