139 lines
6.2 KiB
Groff
139 lines
6.2 KiB
Groff
.Dd December 18, 2016
|
|
.Dt SQLITE3_UNLOCK_NOTIFY 3
|
|
.Os
|
|
.Sh NAME
|
|
.Nm sqlite3_unlock_notify
|
|
.Nd Unlock Notification
|
|
.Sh SYNOPSIS
|
|
.Ft int SQLITE_STDCALL
|
|
.Fo sqlite3_unlock_notify
|
|
.Fa "sqlite3 *pBlocked"
|
|
.Fa "void (*xNotify)(void **apArg, int nArg)"
|
|
.Fa "void *pNotifyArg "
|
|
.Fc
|
|
.Sh DESCRIPTION
|
|
When running in shared-cache mode, a database operation may fail with
|
|
an SQLITE_LOCKED error if the required locks on the shared-cache
|
|
or individual tables within the shared-cache cannot be obtained.
|
|
See SQLite Shared-Cache Mode for a description
|
|
of shared-cache locking.
|
|
This API may be used to register a callback that SQLite will invoke
|
|
when the connection currently holding the required lock relinquishes
|
|
it.
|
|
This API is only available if the library was compiled with the SQLITE_ENABLE_UNLOCK_NOTIFY
|
|
C-preprocessor symbol defined.
|
|
.Pp
|
|
Shared-cache locks are released when a database connection concludes
|
|
its current transaction, either by committing it or rolling it back.
|
|
.Pp
|
|
When a connection (known as the blocked connection) fails to obtain
|
|
a shared-cache lock and SQLITE_LOCKED is returned to the caller, the
|
|
identity of the database connection (the blocking connection) that
|
|
has locked the required resource is stored internally.
|
|
After an application receives an SQLITE_LOCKED error, it may call the
|
|
sqlite3_unlock_notify() method with the blocked connection handle as
|
|
the first argument to register for a callback that will be invoked
|
|
when the blocking connections current transaction is concluded.
|
|
The callback is invoked from within the sqlite3_step or
|
|
sqlite3_close call that concludes the blocking connections
|
|
transaction.
|
|
.Pp
|
|
If sqlite3_unlock_notify() is called in a multi-threaded application,
|
|
there is a chance that the blocking connection will have already concluded
|
|
its transaction by the time sqlite3_unlock_notify() is invoked.
|
|
If this happens, then the specified callback is invoked immediately,
|
|
from within the call to sqlite3_unlock_notify().
|
|
.Pp
|
|
If the blocked connection is attempting to obtain a write-lock on a
|
|
shared-cache table, and more than one other connection currently holds
|
|
a read-lock on the same table, then SQLite arbitrarily selects one
|
|
of the other connections to use as the blocking connection.
|
|
.Pp
|
|
There may be at most one unlock-notify callback registered by a blocked
|
|
connection.
|
|
If sqlite3_unlock_notify() is called when the blocked connection already
|
|
has a registered unlock-notify callback, then the new callback replaces
|
|
the old.
|
|
If sqlite3_unlock_notify() is called with a NULL pointer as its second
|
|
argument, then any existing unlock-notify callback is canceled.
|
|
The blocked connections unlock-notify callback may also be canceled
|
|
by closing the blocked connection using sqlite3_close().
|
|
.Pp
|
|
The unlock-notify callback is not reentrant.
|
|
If an application invokes any sqlite3_xxx API functions from within
|
|
an unlock-notify callback, a crash or deadlock may be the result.
|
|
.Pp
|
|
Unless deadlock is detected (see below), sqlite3_unlock_notify() always
|
|
returns SQLITE_OK.
|
|
.Pp
|
|
\fBCallback Invocation Details\fP
|
|
.Pp
|
|
When an unlock-notify callback is registered, the application provides
|
|
a single void* pointer that is passed to the callback when it is invoked.
|
|
However, the signature of the callback function allows SQLite to pass
|
|
it an array of void* context pointers.
|
|
The first argument passed to an unlock-notify callback is a pointer
|
|
to an array of void* pointers, and the second is the number of entries
|
|
in the array.
|
|
.Pp
|
|
When a blocking connections transaction is concluded, there may be
|
|
more than one blocked connection that has registered for an unlock-notify
|
|
callback.
|
|
If two or more such blocked connections have specified the same callback
|
|
function, then instead of invoking the callback function multiple times,
|
|
it is invoked once with the set of void* context pointers specified
|
|
by the blocked connections bundled together into an array.
|
|
This gives the application an opportunity to prioritize any actions
|
|
related to the set of unblocked database connections.
|
|
.Pp
|
|
\fBDeadlock Detection\fP
|
|
.Pp
|
|
Assuming that after registering for an unlock-notify callback a database
|
|
waits for the callback to be issued before taking any further action
|
|
(a reasonable assumption), then using this API may cause the application
|
|
to deadlock.
|
|
For example, if connection X is waiting for connection Y's transaction
|
|
to be concluded, and similarly connection Y is waiting on connection
|
|
X's transaction, then neither connection will proceed and the system
|
|
may remain deadlocked indefinitely.
|
|
.Pp
|
|
To avoid this scenario, the sqlite3_unlock_notify() performs deadlock
|
|
detection.
|
|
If a given call to sqlite3_unlock_notify() would put the system in
|
|
a deadlocked state, then SQLITE_LOCKED is returned and no unlock-notify
|
|
callback is registered.
|
|
The system is said to be in a deadlocked state if connection A has
|
|
registered for an unlock-notify callback on the conclusion of connection
|
|
B's transaction, and connection B has itself registered for an unlock-notify
|
|
callback when connection A's transaction is concluded.
|
|
Indirect deadlock is also detected, so the system is also considered
|
|
to be deadlocked if connection B has registered for an unlock-notify
|
|
callback on the conclusion of connection C's transaction, where connection
|
|
C is waiting on connection A.
|
|
Any number of levels of indirection are allowed.
|
|
.Pp
|
|
\fBThe "DROP TABLE" Exception\fP
|
|
.Pp
|
|
When a call to sqlite3_step() returns SQLITE_LOCKED,
|
|
it is almost always appropriate to call sqlite3_unlock_notify().
|
|
There is however, one exception.
|
|
When executing a "DROP TABLE" or "DROP INDEX" statement, SQLite checks
|
|
if there are any currently executing SELECT statements that belong
|
|
to the same connection.
|
|
If there are, SQLITE_LOCKED is returned.
|
|
In this case there is no "blocking connection", so invoking sqlite3_unlock_notify()
|
|
results in the unlock-notify callback being invoked immediately.
|
|
If the application then re-attempts the "DROP TABLE" or "DROP INDEX"
|
|
query, an infinite loop might be the result.
|
|
.Pp
|
|
One way around this problem is to check the extended error code returned
|
|
by an sqlite3_step() call.
|
|
If there is a blocking connection, then the extended error code is
|
|
set to SQLITE_LOCKED_SHAREDCACHE.
|
|
Otherwise, in the special "DROP TABLE/INDEX" case, the extended error
|
|
code is just SQLITE_LOCKED.
|
|
.Sh SEE ALSO
|
|
.Xr sqlite3_close 3 ,
|
|
.Xr sqlite3_step 3 ,
|
|
.Xr SQLITE_OK 3
|