@@ -19,66 +19,94 @@ import (
1919// sqlite library needs to be compiled with -DSQLITE_ENABLE_UNLOCK_NOTIFY
2020// https://www.sqlite.org/unlock_notify.html
2121
22- typedef struct UnlockNotification UnlockNotification;
22+ // A pointer to an instance of this structure is passed as the user-context
23+ // pointer when registering for an unlock-notify callback.
2324
25+ typedef struct UnlockNotification UnlockNotification;
2426struct UnlockNotification {
25- int fired; // True after unlock event has occurred
26- pthread_cond_t cond; // Condition variable to wait on
27- pthread_mutex_t mutex; // Mutex to protect structure
27+ int fired; // True after unlock event has occurred
28+ pthread_cond_t cond; // Condition variable to wait on
29+ pthread_mutex_t mutex; // Mutex to protect structure
2830};
2931
30- static void _unlock_notify_cb(void** apArg, int nArg) {
31- int i;
32- for(i = 0; i < nArg; i++) {
33- UnlockNotification *p = (UnlockNotification* )apArg[i];
34- pthread_mutex_lock(&p->mutex);
35- p->fired = 1;
36- pthread_cond_signal(&p->cond);
37- pthread_mutex_unlock(&p->mutex);
32+ // This function is an unlock-notify callback registered with SQLite.
33+ static void unlock_notify_cb(void **apArg, int nArg){
34+ for(int i = 0; i < nArg; i++){
35+ UnlockNotification *p = (UnlockNotification * )apArg[i];
36+ pthread_mutex_lock(&p->mutex);
37+ p->fired = 1;
38+ pthread_cond_signal(&p->cond);
39+ pthread_mutex_unlock(&p->mutex);
3840 }
3941}
4042
41- int _wait_for_unlock_notify(sqlite3* db) {
43+ // This function assumes that an SQLite API call (either sqlite3_prepare_v2()
44+ // or sqlite3_step()) has just returned SQLITE_LOCKED. The argument is the
45+ // associated database connection.
46+ //
47+ // This function calls sqlite3_unlock_notify() to register for an
48+ // unlock-notify callback, then blocks until that callback is delivered
49+ // and returns SQLITE_OK. The caller should then retry the failed operation.
50+ //
51+ // Or, if sqlite3_unlock_notify() indicates that to block would deadlock
52+ // the system, then this function returns SQLITE_LOCKED immediately. In
53+ // this case the caller should not retry the operation and should roll
54+ // back the current transaction (if any).
55+ static int wait_for_unlock_notify(sqlite3 *db) {
4256 int rc;
4357 UnlockNotification un;
4458
45- // Initialize the UnlockNotification structure
59+ // Initialize the UnlockNotification structure.
4660 un.fired = 0;
4761 pthread_mutex_init(&un.mutex, 0);
4862 pthread_cond_init(&un.cond, 0);
4963
50- // Register for an unlock-notify callback
64+ // Register for an unlock-notify callback.
65+ rc = sqlite3_unlock_notify(db, unlock_notify_cb, (void *)&un);
66+ assert( rc==SQLITE_LOCKED || rc==SQLITE_OK );
67+
5168 // The call to sqlite3_unlock_notify() always returns either SQLITE_LOCKED
52- // or SQLITE_OK
53- rc = sqlite3_unlock_notify(db, _unlock_notify_cb, (void* )&un);
54- assert(rc == SQLITE_LOCKED || rc == SQLITE_OK);
55- if(rc == SQLITE_OK) {
56- pthread_mutex_lock(&un.mutex);
57- if (!un.fired) {
69+ // or SQLITE_OK.
70+ //
71+ // If SQLITE_LOCKED was returned, then the system is deadlocked. In this
72+ // case this function needs to return SQLITE_LOCKED to the caller so
73+ // that the current transaction can be rolled back. Otherwise, block
74+ // until the unlock-notify callback is invoked, then return SQLITE_OK.
75+ if( rc==SQLITE_OK ){
76+ pthread_mutex_lock(&un.mutex);
77+ if( !un.fired ){
5878 pthread_cond_wait(&un.cond, &un.mutex);
5979 }
6080 pthread_mutex_unlock(&un.mutex);
6181 }
6282
63- // Destroy the mutex and condition variables
83+ // Destroy the mutex and condition variables.
6484 pthread_cond_destroy(&un.cond);
6585 pthread_mutex_destroy(&un.mutex);
86+
6687 return rc;
6788}
6889
90+
6991// This code is a wrapper around sqlite3_step
7092static int _sqlite3_blocking_step(sqlite3_stmt* stmt) {
71- while (1) {
72- int rc = sqlite3_step(stmt);
73- if ((rc & 0xFF) != SQLITE_LOCKED) {
74- return rc;
93+ int rc;
94+ sqlite3* db = sqlite3_db_handle(stmt);
95+ for (;;) {
96+ rc = sqlite3_step(stmt);
97+ if (rc != SQLITE_LOCKED) {
98+ break;
7599 }
76- rc = _wait_for_unlock_notify(sqlite3_db_handle(stmt));
100+ if (sqlite3_extended_errcode(db) != SQLITE_LOCKED_SHAREDCACHE) {
101+ break;
102+ }
103+ rc = wait_for_unlock_notify(db);
77104 if (rc != SQLITE_OK) {
78- return rc ;
105+ break ;
79106 }
80107 sqlite3_reset(stmt);
81108 }
109+ return rc;
82110}
83111
84112// This code is a wrapper around sqlite3_prepare_v2
@@ -90,16 +118,20 @@ static int _sqlite3_blocking_prepare_v2(
90118 const char** pz // OUT: End of parsed string
91119){
92120 int rc;
93- while (1 ) {
94- int rc = sqlite3_prepare_v2(db, sql, nSql, stmt, pz);
95- if ((rc & 0xFF) != SQLITE_LOCKED) {
96- return rc ;
121+ for (;; ) {
122+ rc = sqlite3_prepare_v2(db, sql, nSql, stmt, pz);
123+ if (rc != SQLITE_LOCKED) {
124+ break ;
97125 }
98- rc = _wait_for_unlock_notify(db);
126+ if (sqlite3_extended_errcode(db) != SQLITE_LOCKED_SHAREDCACHE) {
127+ break;
128+ }
129+ rc = wait_for_unlock_notify(db);
99130 if (rc != SQLITE_OK) {
100- return rc ;
131+ break ;
101132 }
102133 }
134+ return rc;
103135}
104136*/
105137import "C"
0 commit comments