diff --git a/callback.c b/callback.c new file mode 100644 index 0000000..95b7ecb --- /dev/null +++ b/callback.c @@ -0,0 +1,5 @@ +void _unlock_notify_callback(void *arg, int argc) +{ + extern void unlock_notify_callback(void *, int); + unlock_notify_callback(arg, argc); +} diff --git a/callback.go b/callback.go index 29ece3d..e9d614c 100644 --- a/callback.go +++ b/callback.go @@ -20,6 +20,8 @@ package sqlite3 void _sqlite3_result_text(sqlite3_context* ctx, const char* s); void _sqlite3_result_blob(sqlite3_context* ctx, const void* b, int l); + +void _unlock_notify_callback(void *arg, int argc); */ import "C" @@ -362,3 +364,35 @@ func callbackSyntheticForTests(v reflect.Value, err error) callbackArgConverter return v, err } } + +type unlockNotification struct { + notify chan struct{} + lock sync.Mutex +} + +//export unlock_notify_callback +func unlock_notify_callback(pargv unsafe.Pointer, argc C.int) { + argv := *(*uintptr)(pargv) + v := (*[1 << 30]uintptr)(unsafe.Pointer(argv)) + for i := 0; i < int(argc); i++ { + un := lookupHandle(v[i]).(unlockNotification) + un.notify <- struct{}{} + } +} + +var notifyMutex sync.Mutex + +//export unlock_notify_wait +func unlock_notify_wait(db *C.sqlite3) C.int { + var un unlockNotification + + un.notify = make(chan struct{}) + defer close(un.notify) + + argv := [1]uintptr{newHandle(nil, un)} + if rv := C.sqlite3_unlock_notify(db, (*[0]byte)(C._unlock_notify_callback), unsafe.Pointer(&argv)); rv != C.SQLITE_OK { + return rv + } + <-un.notify + return C.SQLITE_OK +} diff --git a/sqlite3.go b/sqlite3.go index 1ff58c3..41a05ec 100644 --- a/sqlite3.go +++ b/sqlite3.go @@ -11,6 +11,7 @@ package sqlite3 #cgo CFLAGS: -DSQLITE_ENABLE_FTS3 -DSQLITE_ENABLE_FTS3_PARENTHESIS -DSQLITE_ENABLE_FTS4_UNICODE61 #cgo CFLAGS: -DSQLITE_TRACE_SIZE_LIMIT=15 #cgo CFLAGS: -DSQLITE_DISABLE_INTRINSIC +#cgo CFLAGS: -DSQLITE_ENABLE_UNLOCK_NOTIFY #cgo CFLAGS: -Wno-deprecated-declarations #ifndef USE_LIBSQLITE3 #include @@ -70,8 +71,25 @@ _sqlite3_exec(sqlite3* db, const char* pcmd, long long* rowid, long long* change static int _sqlite3_step(sqlite3_stmt* stmt, long long* rowid, long long* changes) { - int rv = sqlite3_step(stmt); + extern int unlock_notify_wait(sqlite3 *db); + int rv; sqlite3* db = sqlite3_db_handle(stmt); + + for (;;) { + rv = sqlite3_step(stmt); + if (rv!=SQLITE_LOCKED) { + break; + } + if (sqlite3_extended_errcode(db)!=SQLITE_LOCKED_SHAREDCACHE) { + break; + } + rv = unlock_notify_wait(db); + if (rv != SQLITE_OK) { + break; + } + sqlite3_reset(stmt); + } + *rowid = (long long) sqlite3_last_insert_rowid(db); *changes = (long long) sqlite3_changes(db); return rv; @@ -814,7 +832,19 @@ func (c *SQLiteConn) prepare(ctx context.Context, query string) (driver.Stmt, er defer C.free(unsafe.Pointer(pquery)) var s *C.sqlite3_stmt var tail *C.char - rv := C.sqlite3_prepare_v2(c.db, pquery, -1, &s, &tail) + var rv C.int + for { + rv = C.sqlite3_prepare_v2(c.db, pquery, -1, &s, &tail) + if rv == C.SQLITE_OK { + break + } + if rv == C.SQLITE_LOCKED { + rv = unlock_notify_wait(c.db) + if rv != C.SQLITE_OK { + break + } + } + } if rv != C.SQLITE_OK { return nil, c.lastError() }