Skip to content

Commit 8512dd2

Browse files
KenoaviateskIan Atol
authored
Make finalizer a built-in (#45423)
* Make finalizer a built-in Split out from #45272. This is prepratory work towards adding optimization passes that recognize this builtin. This PR adds `Core.finalizer` with essentially the same interface as `Base.finalizer`, but without the error checking or raw-C-pointer feature. In future commits, the Core.finalizer interface will likely expand slightly, but Base.finalizer will remain unchanged and is the supported interface for this functionality. * Update base/docs/basedocs.jl Co-authored-by: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> * Apply suggestions from code review Co-authored-by: Ian Atol <ian.atol@juliacomputing.com> Co-authored-by: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> Co-authored-by: Ian Atol <ian.atol@juliacomputing.com>
1 parent 86f5501 commit 8512dd2

File tree

9 files changed

+55
-15
lines changed

9 files changed

+55
-15
lines changed

base/compiler/tfuncs.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -559,6 +559,7 @@ add_tfunc(atomic_pointerswap, 3, 3, (a, v, order) -> (@nospecialize; pointer_elt
559559
add_tfunc(atomic_pointermodify, 4, 4, atomic_pointermodify_tfunc, 5)
560560
add_tfunc(atomic_pointerreplace, 5, 5, atomic_pointerreplace_tfunc, 5)
561561
add_tfunc(donotdelete, 0, INT_INF, (@nospecialize args...)->Nothing, 0)
562+
add_tfunc(Core.finalizer, 2, 2, (@nospecialize args...)->Nothing, 5)
562563

563564
# more accurate typeof_tfunc for vararg tuples abstract only in length
564565
function typeof_concrete_vararg(t::DataType)

base/docs/basedocs.jl

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3068,4 +3068,25 @@ end
30683068
"""
30693069
Base.donotdelete
30703070

3071+
"""
3072+
Core.finalizer(f, o)
3073+
3074+
This builtin is an implementation detail of [`Base.finalizer`](@ref) and end-users
3075+
should use the latter instead.
3076+
3077+
# Differences from `Base.finalizer`
3078+
3079+
The interface of `Core.finalizer` is essentially the same as `Base.finalizer`,
3080+
but there are a number of small differences. They are documented here for
3081+
completeness only and (unlike `Base.finalizer`) have no stability guarantees.
3082+
3083+
The current differences are:
3084+
- `Core.finalizer` does not check for mutability of `o`. Attempting to register
3085+
a finalizer for an immutable object is undefined behavior.
3086+
- The value `f` must be a Julia object. `Core.finalizer` does not support a
3087+
raw C function pointer.
3088+
- `Core.finalizer` returns `nothing` rather than `o`.
3089+
"""
3090+
Core.finalizer
3091+
30713092
end

base/gcutils.jl

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,27 @@
44
==(w::WeakRef, v) = isequal(w.value, v)
55
==(w, v::WeakRef) = isequal(w, v.value)
66

7+
# Used by `Base.finalizer` to validate mutability of an object being finalized.
8+
function _check_mutable(@nospecialize(o)) @noinline
9+
if !ismutable(o)
10+
error("objects of type ", typeof(o), " cannot be finalized")
11+
end
12+
end
13+
714
"""
815
finalizer(f, x)
916
1017
Register a function `f(x)` to be called when there are no program-accessible references to
11-
`x`, and return `x`. The type of `x` must be a `mutable struct`, otherwise the behavior of
12-
this function is unpredictable.
18+
`x`, and return `x`. The type of `x` must be a `mutable struct`, otherwise the function
19+
will throw.
1320
1421
`f` must not cause a task switch, which excludes most I/O operations such as `println`.
1522
Using the `@async` macro (to defer context switching to outside of the finalizer) or
1623
`ccall` to directly invoke IO functions in C may be helpful for debugging purposes.
1724
25+
Note that there is no guaranteed world age for the execution of `f`. It may be
26+
called in the world age in which the finalizer was registered or any later world age.
27+
1828
# Examples
1929
```julia
2030
finalizer(my_mutable_struct) do x
@@ -42,18 +52,13 @@ end
4252
```
4353
"""
4454
function finalizer(@nospecialize(f), @nospecialize(o))
45-
if !ismutable(o)
46-
error("objects of type ", typeof(o), " cannot be finalized")
47-
end
48-
ccall(:jl_gc_add_finalizer_th, Cvoid, (Ptr{Cvoid}, Any, Any),
49-
Core.getptls(), o, f)
55+
_check_mutable(o)
56+
Core.finalizer(f, o)
5057
return o
5158
end
5259

5360
function finalizer(f::Ptr{Cvoid}, o::T) where T @inline
54-
if !ismutable(o)
55-
error("objects of type ", typeof(o), " cannot be finalized")
56-
end
61+
_check_mutable(o)
5762
ccall(:jl_gc_add_ptr_finalizer, Cvoid, (Ptr{Cvoid}, Any, Ptr{Cvoid}),
5863
Core.getptls(), o, f)
5964
return o

src/builtin_proto.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ DECLARE_BUILTIN(_typevar);
5757
DECLARE_BUILTIN(donotdelete);
5858
DECLARE_BUILTIN(getglobal);
5959
DECLARE_BUILTIN(setglobal);
60+
DECLARE_BUILTIN(finalizer);
6061

6162
JL_CALLABLE(jl_f_invoke_kwsorter);
6263
#ifdef DEFINE_BUILTIN_GLOBALS
@@ -73,6 +74,7 @@ JL_CALLABLE(jl_f_get_binding_type);
7374
JL_CALLABLE(jl_f_set_binding_type);
7475
JL_CALLABLE(jl_f_donotdelete);
7576
JL_CALLABLE(jl_f_setglobal);
77+
JL_CALLABLE(jl_f_finalizer);
7678

7779
#ifdef __cplusplus
7880
}

src/builtins.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1600,6 +1600,14 @@ JL_CALLABLE(jl_f_donotdelete)
16001600
return jl_nothing;
16011601
}
16021602

1603+
JL_CALLABLE(jl_f_finalizer)
1604+
{
1605+
JL_NARGS(finalizer, 2, 2);
1606+
jl_task_t *ct = jl_current_task;
1607+
jl_gc_add_finalizer_(ct->ptls, args[1], args[0]);
1608+
return jl_nothing;
1609+
}
1610+
16031611
static int equiv_field_types(jl_value_t *old, jl_value_t *ft)
16041612
{
16051613
size_t nf = jl_svec_len(ft);
@@ -1970,6 +1978,7 @@ void jl_init_primitives(void) JL_GC_DISABLED
19701978
jl_builtin__typebody = add_builtin_func("_typebody!", jl_f__typebody);
19711979
add_builtin_func("_equiv_typedef", jl_f__equiv_typedef);
19721980
jl_builtin_donotdelete = add_builtin_func("donotdelete", jl_f_donotdelete);
1981+
add_builtin_func("finalizer", jl_f_finalizer);
19731982

19741983
// builtin types
19751984
add_builtin("Any", (jl_value_t*)jl_any_type);

src/codegen.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1126,7 +1126,8 @@ static const auto &builtin_func_map() {
11261126
{ jl_f_arrayset_addr, new JuliaFunction{XSTR(jl_f_arrayset), get_func_sig, get_func_attrs} },
11271127
{ jl_f_arraysize_addr, new JuliaFunction{XSTR(jl_f_arraysize), get_func_sig, get_func_attrs} },
11281128
{ jl_f_apply_type_addr, new JuliaFunction{XSTR(jl_f_apply_type), get_func_sig, get_func_attrs} },
1129-
{ jl_f_donotdelete_addr, new JuliaFunction{XSTR(jl_f_donotdelete), get_donotdelete_sig, get_donotdelete_func_attrs} }
1129+
{ jl_f_donotdelete_addr, new JuliaFunction{XSTR(jl_f_donotdelete), get_donotdelete_sig, get_donotdelete_func_attrs} },
1130+
{ jl_f_finalizer_addr, new JuliaFunction{XSTR(jl_f_finalizer), get_func_sig, get_func_attrs} }
11301131
};
11311132
return builtins;
11321133
}

src/gc.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -488,7 +488,7 @@ void jl_gc_run_all_finalizers(jl_task_t *ct)
488488
run_finalizers(ct);
489489
}
490490

491-
static void gc_add_finalizer_(jl_ptls_t ptls, void *v, void *f) JL_NOTSAFEPOINT
491+
void jl_gc_add_finalizer_(jl_ptls_t ptls, void *v, void *f) JL_NOTSAFEPOINT
492492
{
493493
assert(jl_atomic_load_relaxed(&ptls->gc_state) == 0);
494494
arraylist_t *a = &ptls->finalizers;
@@ -518,7 +518,7 @@ static void gc_add_finalizer_(jl_ptls_t ptls, void *v, void *f) JL_NOTSAFEPOINT
518518

519519
JL_DLLEXPORT void jl_gc_add_ptr_finalizer(jl_ptls_t ptls, jl_value_t *v, void *f) JL_NOTSAFEPOINT
520520
{
521-
gc_add_finalizer_(ptls, (void*)(((uintptr_t)v) | 1), f);
521+
jl_gc_add_finalizer_(ptls, (void*)(((uintptr_t)v) | 1), f);
522522
}
523523

524524
JL_DLLEXPORT void jl_gc_add_finalizer_th(jl_ptls_t ptls, jl_value_t *v, jl_function_t *f) JL_NOTSAFEPOINT
@@ -527,7 +527,7 @@ JL_DLLEXPORT void jl_gc_add_finalizer_th(jl_ptls_t ptls, jl_value_t *v, jl_funct
527527
jl_gc_add_ptr_finalizer(ptls, v, jl_unbox_voidpointer(f));
528528
}
529529
else {
530-
gc_add_finalizer_(ptls, v, f);
530+
jl_gc_add_finalizer_(ptls, v, f);
531531
}
532532
}
533533

src/julia_internal.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -466,6 +466,7 @@ void jl_gc_track_malloced_array(jl_ptls_t ptls, jl_array_t *a) JL_NOTSAFEPOINT;
466466
void jl_gc_count_allocd(size_t sz) JL_NOTSAFEPOINT;
467467
void jl_gc_run_all_finalizers(jl_task_t *ct);
468468
void jl_release_task_stack(jl_ptls_t ptls, jl_task_t *task);
469+
void jl_gc_add_finalizer_(jl_ptls_t ptls, void *v, void *f) JL_NOTSAFEPOINT;
469470

470471
// Set GC memory trigger in bytes for greedy memory collecting
471472
void jl_gc_set_max_memory(uint64_t max_mem);

src/staticdata.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -314,7 +314,7 @@ static const jl_fptr_args_t id_to_fptrs[] = {
314314
&jl_f_ifelse, &jl_f__structtype, &jl_f__abstracttype, &jl_f__primitivetype,
315315
&jl_f__typebody, &jl_f__setsuper, &jl_f__equiv_typedef, &jl_f_get_binding_type,
316316
&jl_f_set_binding_type, &jl_f_opaque_closure_call, &jl_f_donotdelete,
317-
&jl_f_getglobal, &jl_f_setglobal,
317+
&jl_f_getglobal, &jl_f_setglobal, &jl_f_finalizer,
318318
NULL };
319319

320320
typedef struct {

0 commit comments

Comments
 (0)