@@ -15,28 +15,34 @@ import (
15
15
#include <sqlite3.h>
16
16
#include <pthread.h>
17
17
#include <assert.h>
18
+ #include <stdio.h>
18
19
19
20
// sqlite library needs to be compiled with -DSQLITE_ENABLE_UNLOCK_NOTIFY
20
21
// https://www.sqlite.org/unlock_notify.html
21
22
22
23
// A pointer to an instance of this structure is passed as the user-context
23
24
// pointer when registering for an unlock-notify callback.
24
-
25
- typedef struct UnlockNotification UnlockNotification;
26
- struct UnlockNotification {
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
30
- };
25
+ typedef struct {
26
+ int fired; // True after unlock event has occurred
27
+ pthread_cond_t cond; // Condition variable to wait on
28
+ pthread_mutex_t mutex; // Mutex to protect structure
29
+ } UnlockNotification;
30
+
31
+ // This function allocates a new UnlockNotification structure and returns a pointer to it.
32
+ static UnlockNotification* alloc_unlock_notification(void) {
33
+ return (UnlockNotification* )malloc(sizeof(UnlockNotification));
34
+ }
31
35
32
36
// 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);
37
+ static void unlock_notify_cb(void** apArg, int nArg){
38
+ for(int i = 0; i < nArg; i++) {
39
+ UnlockNotification* un = (UnlockNotification* )apArg[i];
40
+ printf(" unlocking un=%p\n", un);
41
+ fflush(stdout);
42
+ pthread_mutex_lock(&un->mutex);
43
+ un->fired = 1;
44
+ pthread_cond_signal(&un->cond);
45
+ pthread_mutex_unlock(&un->mutex);
40
46
}
41
47
}
42
48
@@ -52,77 +58,97 @@ static void unlock_notify_cb(void **apArg, int nArg){
52
58
// the system, then this function returns SQLITE_LOCKED immediately. In
53
59
// this case the caller should not retry the operation and should roll
54
60
// back the current transaction (if any).
55
- static int wait_for_unlock_notify(sqlite3 *db) {
56
- int rc;
61
+ static int wait_for_unlock_notify(sqlite3* db){
57
62
UnlockNotification un;
58
-
59
- // Initialize the UnlockNotification structure.
60
63
un.fired = 0;
61
64
pthread_mutex_init(&un.mutex, 0);
62
65
pthread_cond_init(&un.cond, 0);
63
66
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
+ printf("wait_for_unlock_notify %p un=%p\n",db,&un);
68
+ fflush(stdout);
67
69
70
+ // Register for an unlock-notify callback.
68
71
// The call to sqlite3_unlock_notify() always returns either SQLITE_LOCKED
69
72
// or SQLITE_OK.
70
73
//
71
74
// If SQLITE_LOCKED was returned, then the system is deadlocked. In this
72
75
// case this function needs to return SQLITE_LOCKED to the caller so
73
76
// that the current transaction can be rolled back. Otherwise, block
74
77
// until the unlock-notify callback is invoked, then return SQLITE_OK.
75
- if( rc==SQLITE_OK ){
78
+ int rc = sqlite3_unlock_notify(db, unlock_notify_cb, (void* )&un);
79
+ assert(rc==SQLITE_LOCKED || rc==SQLITE_OK);
80
+ if(rc == SQLITE_OK) {
76
81
pthread_mutex_lock(&un.mutex);
77
- if( !un.fired ) {
82
+ if ( !un.fired) {
78
83
pthread_cond_wait(&un.cond, &un.mutex);
79
84
}
80
85
pthread_mutex_unlock(&un.mutex);
81
86
}
82
87
88
+ printf("unlocked %p un=%p rc=%d\n",db,&un,rc);
89
+ fflush(stdout);
90
+
83
91
// Destroy the mutex and condition variables.
84
92
pthread_cond_destroy(&un.cond);
85
93
pthread_mutex_destroy(&un.mutex);
86
94
87
95
return rc;
88
96
}
89
97
90
-
91
98
// This code is a wrapper around sqlite3_step
92
- static int _sqlite3_blocking_step(sqlite3_stmt* stmt) {
99
+ static int _sqlite3_blocking_step(sqlite3_stmt* stmt, UnlockNotification* un ) {
93
100
int rc;
94
101
sqlite3* db = sqlite3_db_handle(stmt);
95
102
for (;;) {
103
+ printf("make step db=%p\n",db);
104
+ fflush(stdout);
96
105
rc = sqlite3_step(stmt);
97
- if (rc != SQLITE_LOCKED) {
98
- break;
99
- }
100
106
if (sqlite3_extended_errcode(db) != SQLITE_LOCKED_SHAREDCACHE) {
101
107
break;
102
108
}
109
+ printf("calling wait_for_unlock_notify db=%p\n",db);
110
+ fflush(stdout);
103
111
rc = wait_for_unlock_notify(db);
104
112
if (rc != SQLITE_OK) {
105
113
break;
106
114
}
115
+ printf("resetting db=%p\n",db);
116
+ fflush(stdout);
107
117
sqlite3_reset(stmt);
108
118
}
109
119
return rc;
110
120
}
111
121
112
122
// This code is a wrapper around sqlite3_prepare_v2
113
123
static int _sqlite3_blocking_prepare_v2(
114
- sqlite3* db, // Database handle
115
- const char* sql, // UTF-8 encoded SQL statement
116
- int nSql, // Length of zSql in bytes
117
- sqlite3_stmt** stmt, // OUT: A pointer to the prepared statement
118
- const char** pz // OUT: End of parsed string
124
+ sqlite3* db, // Database handle
125
+ UnlockNotification* un, // IN: Unlock notification object
126
+ const char* sql, // IN: UTF-8 encoded SQL statement
127
+ int nSql, // IN: Length of zSql in bytes
128
+ sqlite3_stmt** stmt, // OUT: A pointer to the prepared statement
129
+ const char** pz // OUT: End of parsed string
119
130
){
120
131
int rc;
121
132
for (;;) {
122
133
rc = sqlite3_prepare_v2(db, sql, nSql, stmt, pz);
123
- if (rc != SQLITE_LOCKED) {
134
+ if (sqlite3_extended_errcode(db) != SQLITE_LOCKED_SHAREDCACHE) {
135
+ break;
136
+ }
137
+ rc = wait_for_unlock_notify(db);
138
+ if (rc != SQLITE_OK) {
124
139
break;
125
140
}
141
+ }
142
+ return rc;
143
+ }
144
+
145
+
146
+ // This code is a wrapper around sqlite3_reset
147
+ static int _sqlite3_blocking_reset(sqlite3_stmt* stmt, UnlockNotification* un) {
148
+ int rc;
149
+ sqlite3* db = sqlite3_db_handle(stmt);
150
+ for (;;) {
151
+ rc = sqlite3_reset(stmt);
126
152
if (sqlite3_extended_errcode(db) != SQLITE_LOCKED_SHAREDCACHE) {
127
153
break;
128
154
}
@@ -206,7 +232,9 @@ func (c *Conn) Prepare(query string) (*Statement, string, error) {
206
232
}
207
233
208
234
// Prepare statement
209
- if err := SQError (C ._sqlite3_blocking_prepare_v2 ((* C .sqlite3 )(c ), cQuery , - 1 , & s , & cExtra )); err != SQLITE_OK {
235
+ un := C .alloc_unlock_notification ()
236
+ defer C .free (unsafe .Pointer (un ))
237
+ if err := SQError (C ._sqlite3_blocking_prepare_v2 ((* C .sqlite3 )(c ), un , cQuery , - 1 , & s , & cExtra )); err != SQLITE_OK {
210
238
return nil , "" , err .With (C .GoString (C .sqlite3_errmsg ((* C .sqlite3 )(c ))))
211
239
}
212
240
@@ -246,9 +274,11 @@ func (s *Statement) Conn() *Conn {
246
274
247
275
// Reset statement
248
276
func (s * Statement ) Reset () error {
249
- err := SQError (C .sqlite3_reset ((* C .sqlite3_stmt )(s )))
277
+ un := C .alloc_unlock_notification ()
278
+ defer C .free (unsafe .Pointer (un ))
279
+ err := SQError (C ._sqlite3_blocking_reset ((* C .sqlite3_stmt )(s ), un ))
250
280
if (err & 0xFF ) == SQLITE_LOCKED {
251
- fmt .Println ("TODO: Locked" )
281
+ fmt .Println ("TODO: Locked Reset" , int ( err ) )
252
282
}
253
283
if err != SQLITE_OK {
254
284
return err .With (C .GoString (C .sqlite3_errmsg ((* C .sqlite3 )(s .Conn ()))))
@@ -285,9 +315,11 @@ func (s *Statement) Finalize() error {
285
315
286
316
// Step statement
287
317
func (s * Statement ) Step () error {
288
- err := SQError (C ._sqlite3_blocking_step ((* C .sqlite3_stmt )(s )))
318
+ un := C .alloc_unlock_notification ()
319
+ defer C .free (unsafe .Pointer (un ))
320
+ err := SQError (C ._sqlite3_blocking_step ((* C .sqlite3_stmt )(s ), un ))
289
321
if (err & 0xFF ) == SQLITE_LOCKED {
290
- fmt .Println ("TODO Locked ( Step)" )
322
+ fmt .Println ("TODO Locked Step" , int ( err ) )
291
323
}
292
324
return err
293
325
}
0 commit comments