sqlite3_opt_unlock_notify.go 2.0 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697
  1. // Copyright (C) 2019 Yasuhiro Matsumoto <mattn.jp@gmail.com>.
  2. //
  3. // Use of this source code is governed by an MIT-style
  4. // license that can be found in the LICENSE file.
  5. //go:build cgo && sqlite_unlock_notify
  6. // +build cgo,sqlite_unlock_notify
  7. package sqlite3
  8. /*
  9. #cgo CFLAGS: -DSQLITE_ENABLE_UNLOCK_NOTIFY
  10. #include <stdlib.h>
  11. #ifndef USE_LIBSQLITE3
  12. #include "sqlite3-binding.h"
  13. #else
  14. #include <sqlite3.h>
  15. #endif
  16. extern void unlock_notify_callback(void *arg, int argc);
  17. */
  18. import "C"
  19. import (
  20. "fmt"
  21. "math"
  22. "sync"
  23. "unsafe"
  24. )
  25. type unlock_notify_table struct {
  26. sync.Mutex
  27. seqnum uint
  28. table map[uint]chan struct{}
  29. }
  30. var unt unlock_notify_table = unlock_notify_table{table: make(map[uint]chan struct{})}
  31. func (t *unlock_notify_table) add(c chan struct{}) uint {
  32. t.Lock()
  33. defer t.Unlock()
  34. h := t.seqnum
  35. t.table[h] = c
  36. t.seqnum++
  37. return h
  38. }
  39. func (t *unlock_notify_table) remove(h uint) {
  40. t.Lock()
  41. defer t.Unlock()
  42. delete(t.table, h)
  43. }
  44. func (t *unlock_notify_table) get(h uint) chan struct{} {
  45. t.Lock()
  46. defer t.Unlock()
  47. c, ok := t.table[h]
  48. if !ok {
  49. panic(fmt.Sprintf("Non-existent key for unlcok-notify channel: %d", h))
  50. }
  51. return c
  52. }
  53. //export unlock_notify_callback
  54. func unlock_notify_callback(argv unsafe.Pointer, argc C.int) {
  55. for i := 0; i < int(argc); i++ {
  56. parg := ((*(*[(math.MaxInt32 - 1) / unsafe.Sizeof((*C.uint)(nil))]*[1]uint)(argv))[i])
  57. arg := *parg
  58. h := arg[0]
  59. c := unt.get(h)
  60. c <- struct{}{}
  61. }
  62. }
  63. //export unlock_notify_wait
  64. func unlock_notify_wait(db *C.sqlite3) C.int {
  65. // It has to be a bufferred channel to not block in sqlite_unlock_notify
  66. // as sqlite_unlock_notify could invoke the callback before it returns.
  67. c := make(chan struct{}, 1)
  68. defer close(c)
  69. h := unt.add(c)
  70. defer unt.remove(h)
  71. pargv := C.malloc(C.sizeof_uint)
  72. defer C.free(pargv)
  73. argv := (*[1]uint)(pargv)
  74. argv[0] = h
  75. if rv := C.sqlite3_unlock_notify(db, (*[0]byte)(C.unlock_notify_callback), unsafe.Pointer(pargv)); rv != C.SQLITE_OK {
  76. return rv
  77. }
  78. <-c
  79. return C.SQLITE_OK
  80. }