Skip to content

Commit 57a9ffa

Browse files
committed
webassembly: Register PyProxy objects for JS-side finalisation.
And clear the corresponding `proxy_c_ref[c_ref]` entry when the finaliser runs. This then allows the C side to (eventually) garbage collect the corresponding Python object. Signed-off-by: Damien George <damien@micropython.org>
1 parent 5c7a414 commit 57a9ffa

File tree

3 files changed

+38
-1
lines changed

3 files changed

+38
-1
lines changed

ports/webassembly/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ EXPORTED_FUNCTIONS_EXTRA += ,\
5454
_mp_js_do_exec_async,\
5555
_mp_js_do_import,\
5656
_mp_js_register_js_module,\
57+
_proxy_c_free_obj,\
5758
_proxy_c_init,\
5859
_proxy_c_to_js_call,\
5960
_proxy_c_to_js_delete_attr,\

ports/webassembly/proxy_c.c

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@
3232
#include "py/runtime.h"
3333
#include "proxy_c.h"
3434

35+
// Number of static entries at the start of proxy_c_ref.
36+
#define PROXY_C_REF_NUM_STATIC (1)
37+
3538
// These constants should match the constants in proxy_js.js.
3639

3740
enum {
@@ -71,23 +74,50 @@ static const mp_obj_base_t mp_const_undefined_obj = {&mp_type_undefined};
7174

7275
MP_DEFINE_EXCEPTION(JsException, Exception)
7376

77+
// Index to start searching for the next available slot in proxy_c_ref.
78+
static size_t proxy_c_ref_next;
79+
7480
void proxy_c_init(void) {
7581
MP_STATE_PORT(proxy_c_ref) = mp_obj_new_list(0, NULL);
7682
mp_obj_list_append(MP_STATE_PORT(proxy_c_ref), MP_OBJ_NULL);
83+
proxy_c_ref_next = PROXY_C_REF_NUM_STATIC;
7784
}
7885

7986
MP_REGISTER_ROOT_POINTER(mp_obj_t proxy_c_ref);
8087

88+
// obj cannot be MP_OBJ_NULL.
8189
static inline size_t proxy_c_add_obj(mp_obj_t obj) {
82-
size_t id = ((mp_obj_list_t *)MP_OBJ_TO_PTR(MP_STATE_PORT(proxy_c_ref)))->len;
90+
// Search for the first free slot in proxy_c_ref.
91+
mp_obj_list_t *l = (mp_obj_list_t *)MP_OBJ_TO_PTR(MP_STATE_PORT(proxy_c_ref));
92+
while (proxy_c_ref_next < l->len) {
93+
if (l->items[proxy_c_ref_next] == MP_OBJ_NULL) {
94+
// Free slot found, reuse it.
95+
size_t id = proxy_c_ref_next;
96+
++proxy_c_ref_next;
97+
l->items[id] = obj;
98+
return id;
99+
}
100+
++proxy_c_ref_next;
101+
}
102+
103+
// No free slots, so grow proxy_c_ref by one (append at the end of the list).
104+
size_t id = l->len;
83105
mp_obj_list_append(MP_STATE_PORT(proxy_c_ref), obj);
106+
proxy_c_ref_next = l->len;
84107
return id;
85108
}
86109

87110
static inline mp_obj_t proxy_c_get_obj(uint32_t c_ref) {
88111
return ((mp_obj_list_t *)MP_OBJ_TO_PTR(MP_STATE_PORT(proxy_c_ref)))->items[c_ref];
89112
}
90113

114+
void proxy_c_free_obj(uint32_t c_ref) {
115+
if (c_ref >= PROXY_C_REF_NUM_STATIC) {
116+
((mp_obj_list_t *)MP_OBJ_TO_PTR(MP_STATE_PORT(proxy_c_ref)))->items[c_ref] = MP_OBJ_NULL;
117+
proxy_c_ref_next = MIN(proxy_c_ref_next, c_ref);
118+
}
119+
}
120+
91121
mp_obj_t proxy_convert_js_to_mp_obj_cside(uint32_t *value) {
92122
if (value[0] == PROXY_KIND_JS_UNDEFINED) {
93123
return mp_const_undefined;

ports/webassembly/proxy_js.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,11 @@ class PythonError extends Error {
6161
function proxy_js_init() {
6262
globalThis.proxy_js_ref = [globalThis, undefined];
6363
globalThis.proxy_js_ref_next = PROXY_JS_REF_NUM_STATIC;
64+
globalThis.pyProxyFinalizationRegistry = new FinalizationRegistry(
65+
(cRef) => {
66+
Module.ccall("proxy_c_free_obj", "null", ["number"], [cRef]);
67+
},
68+
);
6469
}
6570

6671
// js_obj cannot be undefined
@@ -251,6 +256,7 @@ function proxy_convert_mp_to_js_obj_jsside(value) {
251256
const target = new PyProxy(id);
252257
obj = new Proxy(target, py_proxy_handler);
253258
}
259+
globalThis.pyProxyFinalizationRegistry.register(obj, id);
254260
}
255261
return obj;
256262
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy