Skip to content

Commit aba1e22

Browse files
committed
Add threadsafety documentation
Fixes #391
1 parent 9b8dfb4 commit aba1e22

File tree

2 files changed

+80
-0
lines changed

2 files changed

+80
-0
lines changed

README.md

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,78 @@ end
7676
# => ["Jane", "[email protected]", "A", "http://blog.janedoe.com"]
7777
```
7878

79+
## Thread Safety
80+
81+
When `SQLite3.threadsafe?` returns `true`, then SQLite3 has been compiled to
82+
support running in a multithreaded environment. However, this doesn't mean
83+
that all classes in the SQLite3 gem can be considered "thread safe".
84+
85+
When `SQLite3.threadsafe?` returns `true`, it is safe to share only
86+
`SQLite3::Database` instances among threads without providing your own locking
87+
mechanism. For example, the following code is fine because only the database
88+
instance is shared among threads:
89+
90+
```ruby
91+
require 'sqlite3'
92+
93+
db = SQLite3::Database.new ":memory:"
94+
95+
latch = Queue.new
96+
97+
ts = 10.times.map {
98+
Thread.new {
99+
latch.pop
100+
db.execute "SELECT '#{Thread.current.inspect}'"
101+
}
102+
}
103+
10.times { latch << nil }
104+
105+
p ts.map(&:value)
106+
```
107+
108+
Other instances can be shared among threads, but they require that you provide
109+
your own locking for thread safety. For example, `SQLite3::Statement` objects
110+
(prepared statements) are mutable, so applications must take care to add
111+
appropriate locks to avoid data race conditions when sharing these objects
112+
among threads.
113+
114+
Lets rewrite the above example but use a prepared statement and safely share
115+
the prepared statement among threads:
116+
117+
```ruby
118+
db = SQLite3::Database.new ":memory:"
119+
120+
# Prepare a statement
121+
stmt = db.prepare "SELECT :inspect"
122+
stmt_lock = Mutex.new
123+
124+
latch = Queue.new
125+
126+
ts = 10.times.map {
127+
Thread.new {
128+
latch.pop
129+
130+
# Add a lock when using the prepared statement.
131+
# Binding values, and walking over results will mutate the statement, so
132+
# in order to prevent other threads from "seeing" this thread's data, we
133+
# must lock when using the statement object
134+
stmt_lock.synchronize do
135+
stmt.execute(Thread.current.inspect).to_a
136+
end
137+
}
138+
}
139+
140+
10.times { latch << nil }
141+
142+
p ts.map(&:value)
143+
144+
stmt.close
145+
```
146+
147+
It is generally recommended that if applications want to share a database among
148+
threads, they _only_ share the database instance object. Other objects are
149+
fine to share, but may require manual locking for thread safety.
150+
79151
## Support
80152

81153
### Installation or database extensions

lib/sqlite3/database.rb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,14 @@ module SQLite3
3030
# ArrayFields module from Ara Howard. If you require the ArrayFields
3131
# module before performing a query, and if you have not enabled results as
3232
# hashes, then the results will all be indexible by field name.
33+
#
34+
# Thread safety:
35+
#
36+
# When `SQLite3.threadsafe?` returns true, it is safe to share instances of
37+
# the database class among threads without adding specific locking. Other
38+
# object instances may require applications to provide their own locks if
39+
# they are to be shared among threads. Please see the README.md for more
40+
# information.
3341
class Database
3442
attr_reader :collations
3543

0 commit comments

Comments
 (0)