Skip to content

Commit 9c36dfb

Browse files
author
Martijn The
committed
Add finalize_cb to jerry_context_data_manager_t
This patch adds a new finalize_cb callback to jerry_context_data_manager_t. The callback is run as the very last thing in jerry_cleanup, after the VM has been torn down entirely. There was already the deinit_cb, which is run while the VM is still in the process of being torn down. The reason the deinit_cb is not always sufficient is that there may still be objects alive (because they still being referenced) that have native pointers associated with the context manager that is being deinit'ed. As a result, the free_cb's for those objects can get called *after* the associated context manager's deinit_cb is run. This makes cleanup of manager state that is depended on by the live objects impossible to do in the deinit_cb. That type of cleanup can only be done when all values have been torn down completely. JerryScript-DCO-1.0-Signed-off-by: Martijn The [email protected]
1 parent 78bd11e commit 9c36dfb

File tree

4 files changed

+109
-14
lines changed

4 files changed

+109
-14
lines changed

docs/02.API-REFERENCE.md

Lines changed: 41 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -128,17 +128,53 @@ typedef uint32_t jerry_value_t;
128128

129129
Structure that defines how a context data item will be initialized and deinitialized. JerryScript zeroes out the memory
130130
for the item by default, and if the `init_cb` field is not NULL, it will be called with the pointer to the memory as
131-
an additional custom initializer. The `deinit_cb` (if non-`NULL`) is called during a call to `jerry_cleanup()` to run
132-
any custom deinitialization.
131+
an additional custom initializer. The `deinit_cb` (if non-`NULL`) is called during a call to `jerry_cleanup ()` to run
132+
any custom deinitialization *before* the VM has been fully cleaned up. The `finalize_cb` (if non-`NULL`) is also called
133+
during a call to `jerry_cleanup ()` to run any custom deinitialization *after* the VM has been fully cleaned up.
133134

134135
**Prototype**
135136

136137
```c
137138
typedef struct
138139
{
139-
void (*init_cb) (void *); /**< callback responsible for initializing a context item, or NULL */
140-
void (*deinit_cb) (void *); /**< callback responsible for deinitializing a context item, or NULL */
141-
size_t bytes_needed; /**< number of bytes to allocate for this manager */
140+
/**
141+
* Callback responsible for initializing a context item, or NULL to zero out the memory. This is called lazily, the
142+
* first time jerry_get_context_data () is called with this manager.
143+
*
144+
* @param [in] data The buffer that JerryScript allocated for the manager. The buffer is zeroed out. The size is
145+
* determined by the bytes_needed field. The buffer is kept alive until jerry_cleanup () is called.
146+
*/
147+
void (*init_cb) (void *data);
148+
149+
/**
150+
* Callback responsible for deinitializing a context item, or NULL. This is called as part of jerry_cleanup (),
151+
* right *before* the VM has been cleaned up. This is a good place to release strong references to jerry_value_t's
152+
* that the manager may be holding.
153+
* Note: because the VM has not been fully cleaned up yet, jerry_object_native_info_t free_cb's can still get called
154+
* *after* all deinit_cb's have been run. See finalize_cb for a callback that is guaranteed to run *after* all
155+
* free_cb's have been run.
156+
*
157+
* @param [in] data The buffer that JerryScript allocated for the manager.
158+
*/
159+
void (*deinit_cb) (void *data);
160+
161+
/**
162+
* Callback responsible for finalizing a context item, or NULL. This is called as part of jerry_cleanup (),
163+
* right *after* the VM has been cleaned up and destroyed and jerry_... APIs cannot be called any more. At this point,
164+
* all values in the VM have been cleaned up. This is a good place to clean up native state that can only be cleaned
165+
* up at the very end when there are no more VM values around that may need to access that state.
166+
*
167+
* @param [in] data The buffer that JerryScript allocated for the manager. After returning from this callback,
168+
* the data pointer may no longer be used.
169+
*/
170+
void (*finalize_cb) (void *data);
171+
172+
/**
173+
* Number of bytes to allocate for this manager. This is the size of the buffer that JerryScript will allocate on
174+
* behalf of the manager. The pointer to this buffer is passed into init_cb, deinit_cb and finalize_cb. It is also
175+
* returned from the jerry_get_context_data () API.
176+
*/
177+
size_t bytes_needed;
142178
} jerry_context_data_manager_t;
143179
```
144180

jerry-core/api/jerry.c

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -210,24 +210,35 @@ jerry_cleanup (void)
210210
}
211211
#endif /* JERRY_DEBUGGER */
212212

213-
for (jerry_context_data_header_t *this_p = JERRY_CONTEXT (context_data_p), *next_p = NULL;
213+
for (jerry_context_data_header_t *this_p = JERRY_CONTEXT (context_data_p);
214214
this_p != NULL;
215-
this_p = next_p)
215+
this_p = this_p->next_p)
216216
{
217-
next_p = this_p->next_p;
218217
if (this_p->manager_p->deinit_cb)
219218
{
220219
this_p->manager_p->deinit_cb (JERRY_CONTEXT_DATA_HEADER_USER_DATA (this_p));
221220
}
222-
jmem_heap_free_block (this_p, sizeof (jerry_context_data_header_t) + this_p->manager_p->bytes_needed);
223221
}
224222

225223
#ifndef CONFIG_DISABLE_ES2015_PROMISE_BUILTIN
226224
ecma_free_all_enqueued_jobs ();
227225
#endif /* CONFIG_DISABLE_ES2015_PROMISE_BUILTIN */
228226
ecma_finalize ();
229-
jmem_finalize ();
230227
jerry_make_api_unavailable ();
228+
229+
for (jerry_context_data_header_t *this_p = JERRY_CONTEXT (context_data_p), *next_p = NULL;
230+
this_p != NULL;
231+
this_p = next_p)
232+
{
233+
next_p = this_p->next_p;
234+
if (this_p->manager_p->finalize_cb)
235+
{
236+
this_p->manager_p->finalize_cb (JERRY_CONTEXT_DATA_HEADER_USER_DATA (this_p));
237+
}
238+
jmem_heap_free_block (this_p, sizeof (jerry_context_data_header_t) + this_p->manager_p->bytes_needed);
239+
}
240+
241+
jmem_finalize ();
231242
} /* jerry_cleanup */
232243

233244
/**

jerry-core/include/jerryscript-core.h

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -233,9 +233,44 @@ typedef bool (*jerry_objects_foreach_by_native_info_t) (const jerry_value_t obje
233233
*/
234234
typedef struct
235235
{
236-
void (*init_cb) (void *); /**< callback responsible for initializing a context item, or NULL to zero out the memory */
237-
void (*deinit_cb) (void *); /**< callback responsible for deinitializing a context item, or NULL */
238-
size_t bytes_needed; /**< number of bytes to allocate for this manager */
236+
/**
237+
* Callback responsible for initializing a context item, or NULL to zero out the memory. This is called lazily, the
238+
* first time jerry_get_context_data () is called with this manager.
239+
*
240+
* @param [in] data The buffer that JerryScript allocated for the manager. The buffer is zeroed out. The size is
241+
* determined by the bytes_needed field. The buffer is kept alive until jerry_cleanup () is called.
242+
*/
243+
void (*init_cb) (void *data);
244+
245+
/**
246+
* Callback responsible for deinitializing a context item, or NULL. This is called as part of jerry_cleanup (),
247+
* right *before* the VM has been cleaned up. This is a good place to release strong references to jerry_value_t's
248+
* that the manager may be holding.
249+
* Note: because the VM has not been fully cleaned up yet, jerry_object_native_info_t free_cb's can still get called
250+
* *after* all deinit_cb's have been run. See finalize_cb for a callback that is guaranteed to run *after* all
251+
* free_cb's have been run.
252+
*
253+
* @param [in] data The buffer that JerryScript allocated for the manager.
254+
*/
255+
void (*deinit_cb) (void *data);
256+
257+
/**
258+
* Callback responsible for finalizing a context item, or NULL. This is called as part of jerry_cleanup (),
259+
* right *after* the VM has been cleaned up and destroyed and jerry_... APIs cannot be called any more. At this point,
260+
* all values in the VM have been cleaned up. This is a good place to clean up native state that can only be cleaned
261+
* up at the very end when there are no more VM values around that may need to access that state.
262+
*
263+
* @param [in] data The buffer that JerryScript allocated for the manager. After returning from this callback,
264+
* the data pointer may no longer be used.
265+
*/
266+
void (*finalize_cb) (void *data);
267+
268+
/**
269+
* Number of bytes to allocate for this manager. This is the size of the buffer that JerryScript will allocate on
270+
* behalf of the manager. The pointer to this buffer is passed into init_cb, deinit_cb and finalize_cb. It is also
271+
* returned from the jerry_get_context_data () API.
272+
*/
273+
size_t bytes_needed;
239274
} jerry_context_data_manager_t;
240275

241276
/**

tests/unit-core/test-context-data.c

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,10 @@
1919

2020
static bool test_context_data1_new_called = false;
2121
static bool test_context_data2_new_called = false;
22+
static bool test_context_data3_new_called = false;
2223
static bool test_context_data1_free_called = false;
2324
static bool test_context_data2_free_called = false;
24-
static bool test_context_data3_new_called = false;
25+
static bool test_context_data1_finalize_called = false;
2526

2627
/* Context item 1 */
2728
const char *string1 = "item1";
@@ -38,12 +39,23 @@ test_context_data1_free (void *user_data_p)
3839
{
3940
test_context_data1_free_called = true;
4041
TEST_ASSERT ((*(const char **) user_data_p) == string1);
42+
TEST_ASSERT (!test_context_data1_finalize_called);
4143
} /* test_context_data1_free */
4244

45+
static void
46+
test_context_data1_finalize (void *user_data_p)
47+
{
48+
TEST_ASSERT (test_context_data1_free_called);
49+
TEST_ASSERT (!test_context_data1_finalize_called);
50+
TEST_ASSERT ((*(const char **) user_data_p) == string1);
51+
test_context_data1_finalize_called = true;
52+
} /* test_context_data1_finalize */
53+
4354
static const jerry_context_data_manager_t manager1 =
4455
{
4556
.init_cb = test_context_data1_new,
4657
.deinit_cb = test_context_data1_free,
58+
.finalize_cb = test_context_data1_finalize,
4759
.bytes_needed = sizeof (const char *)
4860
};
4961

@@ -86,6 +98,7 @@ static const jerry_context_data_manager_t manager3 =
8698
.init_cb = test_context_data3_new,
8799
/* NULL is allowed: */
88100
.deinit_cb = NULL,
101+
.finalize_cb = NULL,
89102
.bytes_needed = 0,
90103
};
91104

0 commit comments

Comments
 (0)