sqlite3_opt_unlock_notify.go 1.9 KB

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