You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
classRwLocker:
def__init__(self, filename):
self.procLock=threading.Lock()
self.con=sqlite3.connect(filename)
# Redundant unless there are "rogue" processes that open the db# and switch the the db to journal_mode=WALself.con.execute('PRAGMA journal_mode=DELETE;')
defwriteLock(self):
withself.procLock:
ifself.curisnotNone:
ifself.lock_mode!="write":
# raise error because direct promotion of read locks to write is deadlock-proneraiseRwLockErrorreturnself.lock_mode="write"withself.con.execute('BEGIN EXCLUSIVE TRANSACTION;') ascur:
self.cur=curcur.execute('CREATE TABLE IF NOT EXISTS lock (value INTEGER UNIQUE);')
cur.execute('INSERT OR IGNORE INTO lock (value) VALUES (42);')
defwriteUnlock(self):
withself.procLock:
cur=self.curcur.execute('END TRANSACTION;')
self.cur=Noneself.lock_mode=Nonecur.close()
defreadLock(self):
withself.procLock:
ifself.curisnotNone:
returnself.lock_mode="read"withself.con.execute('BEGIN TRANSACTION;') ascur:
self.cur=curcur.execute('CREATE TABLE IF NOT EXISTS lock (value INTEGER UNIQUE);')
cur.execute('INSERT OR IGNORE INTO lock (value) VALUES (42);')
ifcur.rowcount==0:
# I guess SQLite doesn't promote the lock to exclusive if the table and the row# already exist, so we assume it's the beginning of the read transaction.return# This readLock() accesses the lock for the very first time and created the table.# That table creation promoted the current transaction to EXCLUSIVE. We need to# need to exit it and start a new transaction that won't be promoted, but will stay SHARED.cur.execute('END TRANSACTION;')
cur.execute('BEGIN TRANSACTION;')
# BEGIN doesn't itself acquire a SHARED lock on the db, that is needed for# effective exclusion with writeLock(). A SELECT is needed.cur.execute('SELECT * from lock LIMIT 1;')
defreadUnlock(self):
self.writeUnlock() # Read unlock is the same as write unlock
Should be cross-platform at least across Unix and Windows.
Using the legacy journal mode rather than more modern WAL mode because, apparently, in WAL mode it's impossible to enforce that read transactions (started with BEGIN TRANSACTION) are blocked if a concurrent write transaction, even EXCLUSIVE, is in progress, unless the read transactions actually read any pages modified by the write transaction. But in the legacy journal mode, it seems, it's possible to do this read-write locking without table data modification at each exclusive lock.
Disadvantage: lock files may not be "application" files, definitely should be separate files (obviously, because the file is a database).
Activity
gaborbernat commentedon Feb 23, 2024
PR welcome 👍
leventov commentedon Feb 19, 2025
@gaborbernat @Yard1 @evan0greenup How about piggy-backing SQLite?
https://sqlite.org/lang_transaction.html#deferred_immediate_and_exclusive_transactions
Should be cross-platform at least across Unix and Windows.
Using the legacy journal mode rather than more modern WAL mode because, apparently, in WAL mode it's impossible to enforce that read transactions (started with
BEGIN TRANSACTION
) are blocked if a concurrent write transaction, evenEXCLUSIVE
, is in progress, unless the read transactions actually read any pages modified by the write transaction. But in the legacy journal mode, it seems, it's possible to do this read-write locking without table data modification at each exclusive lock.Disadvantage: lock files may not be "application" files, definitely should be separate files (obviously, because the file is a database).
WDYT?
gaborbernat commentedon Feb 19, 2025
I think that's a good idea 🤔 as long as we only use https://docs.python.org/3/library/sqlite3.html I'm happy to accept it.
Yard1 commentedon Feb 19, 2025
That's a pretty interesting idea! I think it would work yeah.