Skip to content

Commit 6d089e8

Browse files
committed
Fix memory leak around update hooks
1 parent e568c2d commit 6d089e8

5 files changed

Lines changed: 35 additions & 34 deletions

File tree

crates/core/src/pre_close_vtab.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use powersync_sqlite_nostd as sqlite;
88
use sqlite::{Connection, ResultCode};
99

1010
use crate::state::DatabaseState;
11+
use crate::update_hooks::uninstall_update_hooks;
1112
use crate::vtab_util::*;
1213

1314
/// A virtual table hack to implement a "pre-close hook" for databases.
@@ -20,6 +21,7 @@ use crate::vtab_util::*;
2021
struct VirtualTable {
2122
base: sqlite::vtab,
2223
state: Rc<DatabaseState>,
24+
db: *mut sqlite::sqlite3,
2325
}
2426

2527
extern "C" fn connect(
@@ -42,6 +44,7 @@ extern "C" fn connect(
4244
zErrMsg: core::ptr::null_mut(),
4345
},
4446
state: DatabaseState::clone_from(aux),
47+
db,
4548
}));
4649
*vtab = tab.cast::<sqlite::vtab>();
4750
let _ = sqlite::vtab_config(db, 0);
@@ -57,6 +60,7 @@ extern "C" fn disconnect(vtab: *mut sqlite::vtab) -> c_int {
5760
// So we can use this as a "pre-close" hook and ensure we clear prepared statements the core
5861
// extension might hold.
5962
vtab.state.release_resources();
63+
uninstall_update_hooks(vtab.db, &vtab.state);
6064

6165
ResultCode::OK as c_int
6266
}

crates/core/src/state.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ use crate::{
2525
#[derive(Default)]
2626
pub struct DatabaseState {
2727
pub is_in_sync_local: Cell<bool>,
28+
/// Whether the core extension has installed update, commit and rollback hooks.
29+
pub core_extension_has_update_hooks: Cell<bool>,
2830
schema: RefCell<Option<Schema>>,
2931
pending_updates: RefCell<BTreeSet<String>>,
3032
commited_updates: RefCell<BTreeSet<String>>,

crates/core/src/update_hooks.rs

Lines changed: 11 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
use core::{
2-
cell::Cell,
32
ffi::{CStr, c_char, c_int, c_void},
43
ptr::null_mut,
54
};
@@ -21,11 +20,7 @@ use crate::{constants::SUBTYPE_JSON, error::PowerSyncError, state::DatabaseState
2120
/// The update hooks don't have to be uninstalled manually, that happens when the connection is
2221
/// closed and the function is unregistered.
2322
pub fn register(db: *mut sqlite::sqlite3, state: Rc<DatabaseState>) -> Result<(), ResultCode> {
24-
let state = Box::new(HookState {
25-
has_registered_hooks: Cell::new(false),
26-
db,
27-
state,
28-
});
23+
let state = Box::new(HookState { db, state });
2924

3025
db.create_function_v2(
3126
"powersync_update_hooks",
@@ -41,31 +36,13 @@ pub fn register(db: *mut sqlite::sqlite3, state: Rc<DatabaseState>) -> Result<()
4136
}
4237

4338
struct HookState {
44-
has_registered_hooks: Cell<bool>,
4539
db: *mut sqlite::sqlite3,
4640
state: Rc<DatabaseState>,
4741
}
4842

4943
extern "C" fn destroy_function(ctx: *mut c_void) {
5044
let state = unsafe { Box::from_raw(ctx as *mut HookState) };
51-
52-
if state.has_registered_hooks.get() {
53-
check_previous(
54-
"update",
55-
&state.state,
56-
state.db.update_hook(None, null_mut()),
57-
);
58-
check_previous(
59-
"commit",
60-
&state.state,
61-
state.db.commit_hook(None, null_mut()),
62-
);
63-
check_previous(
64-
"rollback",
65-
&state.state,
66-
state.db.rollback_hook(None, null_mut()),
67-
);
68-
}
45+
uninstall_update_hooks(state.db, &state.state);
6946
}
7047

7148
extern "C" fn powersync_update_hooks(
@@ -107,7 +84,7 @@ extern "C" fn powersync_update_hooks(
10784
Rc::into_raw(db_state.clone()) as *mut c_void,
10885
),
10986
);
110-
state.has_registered_hooks.set(true);
87+
db_state.core_extension_has_update_hooks.set(true);
11188
}
11289
"get" => {
11390
let state = unsafe { user_data.as_ref().unwrap_unchecked() };
@@ -155,6 +132,14 @@ unsafe extern "C" fn rollback_hook_impl(ctx: *mut c_void) {
155132
state.track_rollback();
156133
}
157134

135+
pub fn uninstall_update_hooks(db: *mut sqlite::sqlite3, state: &Rc<DatabaseState>) {
136+
if state.core_extension_has_update_hooks.take() {
137+
check_previous("update", state, db.update_hook(None, null_mut()));
138+
check_previous("commit", state, db.commit_hook(None, null_mut()));
139+
check_previous("rollback", state, db.rollback_hook(None, null_mut()));
140+
}
141+
}
142+
158143
fn check_previous(desc: &'static str, expected: &Rc<DatabaseState>, previous: *const c_void) {
159144
let expected = Rc::as_ptr(expected);
160145

dart/pubspec.lock

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -237,10 +237,10 @@ packages:
237237
dependency: transitive
238238
description:
239239
name: native_toolchain_c
240-
sha256: "89e83885ba09da5fdf2cdacc8002a712ca238c28b7f717910b34bcd27b0d03ac"
240+
sha256: "8aaead321425bd3f03bd5894aa27c8ea6993eab95531da7e59f5d39c6e5708ec"
241241
url: "https://pub.dev"
242242
source: hosted
243-
version: "0.17.4"
243+
version: "0.18.0"
244244
node_preamble:
245245
dependency: transitive
246246
description:
@@ -364,11 +364,12 @@ packages:
364364
sqlite3:
365365
dependency: "direct main"
366366
description:
367-
name: sqlite3
368-
sha256: c6cfe9b1cc159c9eb8ba174b533a60b5126f9db8c6e34efb127d2bc04bc45034
369-
url: "https://pub.dev"
370-
source: hosted
371-
version: "3.1.4"
367+
path: sqlite3
368+
ref: main
369+
resolved-ref: "97268a5b32cf6313dd670e6fa57ae88d38a6564b"
370+
url: "https://github.com/simolus3/sqlite3.dart.git"
371+
source: git
372+
version: "3.3.2"
372373
sqlite3_test:
373374
dependency: "direct dev"
374375
description:
@@ -514,4 +515,4 @@ packages:
514515
source: hosted
515516
version: "3.1.3"
516517
sdks:
517-
dart: ">=3.9.999 <4.0.0"
518+
dart: ">=3.10.0 <4.0.0"

dart/pubspec.yaml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,15 @@ dev_dependencies:
1919
meta: ^1.16.0
2020
path: ^1.9.1
2121

22+
dependency_overrides:
23+
# TODO: Remove after https://github.com/simolus3/sqlite3.dart/commit/97268a5b32cf6313dd670e6fa57ae88d38a6564b
24+
# gets released.
25+
sqlite3:
26+
git:
27+
url: https://github.com/simolus3/sqlite3.dart.git
28+
path: sqlite3
29+
ref: main
30+
2231
# See https://pub.dev/documentation/sqlite3/latest/topics/hook-topic.html
2332
hooks:
2433
user_defines:

0 commit comments

Comments
 (0)