From b61cb23845f5784ca6232b3f9afed40f1f91f065 Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Thu, 14 Jan 2021 10:20:42 -0600 Subject: [PATCH] Reduce specialization in GLib methods (#552) A lot of these methods run on non-inferrable argument types. Consequently it seems to make sense to reduce the amount of specialization. Note there's one case with *increased* specialization, to make it more reliably inferrable and thus precompilable. This also adds a few additional precompiles. --- src/GLib/GLib.jl | 2 +- src/GLib/gtype.jl | 19 ++++++++++++------- src/GLib/gvalues.jl | 16 +++++++++------- src/GLib/signals.jl | 16 ++++++++-------- src/cairo.jl | 6 +++--- src/events.jl | 8 ++++---- src/precompile.jl | 4 ++++ 7 files changed, 41 insertions(+), 30 deletions(-) diff --git a/src/GLib/GLib.jl b/src/GLib/GLib.jl index 79556cd2..8fbc5ab8 100644 --- a/src/GLib/GLib.jl +++ b/src/GLib/GLib.jl @@ -33,7 +33,7 @@ export GConnectFlags export @sigatom, cfunction_ -cfunction_(f, r, a::Tuple) = cfunction_(f, r, Tuple{a...}) +cfunction_(@nospecialize(f), r, a::Tuple) = cfunction_(f, r, Tuple{a...}) @generated function cfunction_(f, R::Type{rt}, A::Type{at}) where {rt, at<:Tuple} quote diff --git a/src/GLib/gtype.jl b/src/GLib/gtype.jl index 53f08348..3540913a 100644 --- a/src/GLib/gtype.jl +++ b/src/GLib/gtype.jl @@ -285,10 +285,12 @@ convert(::Type{GBoxed}, unbox::Ptr{T}) where {T <: GBoxed} = GBoxedUnkown(unbox) convert(::Type{T}, unbox::Ptr{GBoxed}) where {T <: GBoxed} = convert(T, convert(Ptr{T}, unbox)) convert(::Type{T}, unbox::Ptr{T}) where {T <: GBoxed} = T(unbox) +cconvert(::Type{Ptr{GObject}}, @nospecialize(x::GObject)) = x + # All GObjects are expected to have a 'handle' field # of type Ptr{GObject} corresponding to the GLib object # or to override this method (e.g. GtkNullContainer, AbstractString) -unsafe_convert(::Type{Ptr{GObject}}, w::GObject) = w.handle +unsafe_convert(::Type{Ptr{GObject}}, w::GObject) = getfield(w, :handle) # this method should be used by gtk methods returning widgets of unknown type # and/or that might have been wrapped by julia before, @@ -366,6 +368,7 @@ function gc_unref(@nospecialize(x)) nothing end _gc_unref(@nospecialize(x), ::Ptr{Nothing}) = gc_unref(x) +gc_ref_closure(@nospecialize(cb::Function)) = (invoke(gc_ref, Tuple{Any}, cb), @cfunction(_gc_unref, Nothing, (Any, Ptr{Nothing}))) gc_ref_closure(x::T) where {T} = (gc_ref(x), @cfunction(_gc_unref, Nothing, (Any, Ptr{Nothing}))) # generally, you shouldn't be calling gc_ref(::Ptr{GObject}) @@ -377,7 +380,9 @@ const gc_preserve_glib_lock = Ref(false) # to satisfy this lock, must never decr const topfinalizer = Ref(true) # keep recursion to a minimum by only iterating from the top const await_finalize = Any[] -function finalize_gc_unref(@nospecialize(x)) +Base.isequal(x::GObject, w::WeakRef) = x === w.value # cuts the number of MethodInstances from O(N^2) to O(N) + +function finalize_gc_unref(@nospecialize(x::GObject)) # this records that the are no user references left to the object from Julia # and notifies GLib that it can free the object (if no reference exist from C) # it is intended to be called by GC, not in user code function @@ -385,7 +390,7 @@ function finalize_gc_unref(@nospecialize(x)) topfinalizer[] = false gc_preserve_glib_lock[] = true delete!(gc_preserve_glib, x) - if x.handle != C_NULL + if getfield(x, :handle) != C_NULL gc_preserve_glib[x] = true # convert to a strong-reference gc_preserve_glib_lock[] = false gc_unref(unsafe_convert(Ptr{GObject}, x)) # may clear the strong reference @@ -397,7 +402,7 @@ function finalize_gc_unref(@nospecialize(x)) nothing end -function delref(@nospecialize(x)) +function delref(@nospecialize(x::GObject)) # internal helper function exiting[] && return # unnecessary to cleanup if we are about to die anyways if gc_preserve_glib_lock[] || g_yielded[] @@ -407,7 +412,7 @@ function delref(@nospecialize(x)) finalize_gc_unref(x) nothing end -function addref(@nospecialize(x)) +function addref(@nospecialize(x::GObject)) # internal helper function ccall((:g_object_ref_sink, libgobject), Ptr{GObject}, (Ptr{GObject},), x) finalizer(delref, x) @@ -424,10 +429,10 @@ function gobject_ref(x::T) where T <: GObject ccall((:g_object_set_qdata_full, libgobject), Nothing, (Ptr{GObject}, UInt32, Any, Ptr{Nothing}), x, jlref_quark::UInt32, x, deref) # add a circular reference to the Julia object in the GObject - addref(x) + addref(Ref{GObject}(x)[]) elseif strong # oops, we previously deleted the link, but now it's back - addref(x) + addref(Ref{GObject}(x)[]) else # already gc-protected, nothing to do end diff --git a/src/GLib/gvalues.jl b/src/GLib/gvalues.jl index 08bb72f9..10333e64 100644 --- a/src/GLib/gvalues.jl +++ b/src/GLib/gvalues.jl @@ -159,21 +159,23 @@ function getindex(gv::GV, ::Type{Any}) end #end -get_gtk_property(w::GObject, name::AbstractString, ::Type{T}) where T = get_gtk_property(w, Symbol(name), T) -function get_gtk_property(w::GObject, name::Symbol, ::Type{T}) where T +get_gtk_property(w::GObject, name::AbstractString, ::Type{T}) where T = get_gtk_property(w, String(name)::String, T) +get_gtk_property(w::GObject, name::Symbol, ::Type{T}) where T = get_gtk_property(w, String(name), T) +function get_gtk_property(w::GObject, name::String, ::Type{T}) where T v = gvalue(T) ccall((:g_object_get_property, libgobject), Nothing, - (Ptr{GObject}, Ptr{UInt8}, Ptr{GValue}), w, GLib.bytestring(name), v) + (Ptr{GObject}, Ptr{UInt8}, Ptr{GValue}), w, name, v) val = v[T] ccall((:g_value_unset, libgobject), Nothing, (Ptr{GValue},), v) return val end set_gtk_property!(w::GObject, name, ::Type{T}, value) where T = set_gtk_property!(w, name, convert(T, value)) -set_gtk_property!(w::GObject, name::AbstractString, value) = set_gtk_property!(w::GObject, Symbol(name), value) -function set_gtk_property!(w::GObject, name::Symbol, value) +set_gtk_property!(w::GObject, name::AbstractString, value) = set_gtk_property!(w::GObject, String(name)::String, value) +set_gtk_property!(w::GObject, name::Symbol, value) = set_gtk_property!(w::GObject, String(name), value) +function set_gtk_property!(w::GObject, name::String, value) ccall((:g_object_set_property, libgobject), Nothing, - (Ptr{GObject}, Ptr{UInt8}, Ptr{GValue}), w, GLib.bytestring(name), gvalue(value)) + (Ptr{GObject}, Ptr{UInt8}, Ptr{GValue}), w, name, gvalue(value)) w end @@ -186,7 +188,7 @@ struct FieldRef{T} isdefined(obj, field) && return getfield(obj, field) new{T}(obj, field) end - + FieldRef(obj::T, field::Symbol) where T = new{T}(obj, field) end diff --git a/src/GLib/signals.jl b/src/GLib/signals.jl index f5d1789c..afbc39cc 100644 --- a/src/GLib/signals.jl +++ b/src/GLib/signals.jl @@ -1,12 +1,12 @@ # id = VERSION >= v"0.4-"get, :event, Nothing, (ArgsT...)) do ptr, evt_args..., closure # stuff # end -function signal_connect(cb::Function, w::GObject, sig::AbstractStringLike, +function signal_connect(@nospecialize(cb::Function), w::GObject, sig::AbstractStringLike, ::Type{RT}, param_types::Tuple, after::Bool = false, user_data::CT = w) where {CT, RT} signal_connect_generic(cb, w, sig, RT, param_types, after, user_data) end -function signal_connect_generic(cb::Function, w::GObject, sig::AbstractStringLike, +function signal_connect_generic(@nospecialize(cb::Function), w::GObject, sig::AbstractStringLike, ::Type{RT}, param_types::Tuple, after::Bool = false, user_data::CT = w) where {CT, RT} #TODO: assert that length(param_types) is correct callback = cfunction_(cb, RT, tuple(Ptr{GObject}, param_types..., Ref{CT})) ref, deref = gc_ref_closure(user_data) @@ -23,10 +23,10 @@ end # id = signal_connect(widget, :event) do obj, evt_args... # stuff # end -function signal_connect(cb::Function, w::GObject, sig::AbstractStringLike, after::Bool = false) +function signal_connect(@nospecialize(cb::Function), w::GObject, sig::AbstractStringLike, after::Bool = false) _signal_connect(cb, w, sig, after, false, nothing, nothing) end -function _signal_connect(cb::Function, w::GObject, sig::AbstractStringLike, after::Bool, gtk_call_conv::Bool, param_types, user_data) +function _signal_connect(@nospecialize(cb::Function), w::GObject, sig::AbstractStringLike, after::Bool, gtk_call_conv::Bool, param_types, user_data) @assert sizeof_gclosure > 0 closuref = ccall((:g_closure_new_object, libgobject), Ptr{Nothing}, (Cuint, Ptr{GObject}), sizeof_gclosure::Int + GLib.WORD_SIZE * 2, w) closure_env = convert(Ptr{Ptr{Nothing}}, closuref + sizeof_gclosure) @@ -39,7 +39,7 @@ function _signal_connect(cb::Function, w::GObject, sig::AbstractStringLike, afte else unsafe_store!(convert(Ptr{Int}, closure_env), 0, 2) end - ref_cb, deref_cb = gc_ref_closure(cb) + ref_cb, deref_cb = invoke(gc_ref_closure, Tuple{Function}, cb) unsafe_store!(closure_env, ref_cb, 1) ccall((:g_closure_add_invalidate_notifier, libgobject), Nothing, (Ptr{Nothing}, Ptr{Nothing}, Ptr{Nothing}), closuref, ref_cb, deref_cb) @@ -102,7 +102,7 @@ function GClosureMarshal(closuref::Ptr{Nothing}, return_value::Ptr{GValue}, n_pa return nothing end -function blame(cb) +function blame(@nospecialize(cb)) warn("Executing ", cb, ":") end @@ -132,7 +132,7 @@ the given `id`. signal_handler_is_connected(w::GObject, handler_id::Culong) = ccall((:g_signal_handler_is_connected, libgobject), Cint, (Ptr{GObject}, Culong), w, handler_id) == 1 -function signal_emit(w::GObject, sig::AbstractStringLike, RT::Type, args...) +function signal_emit(w::GObject, sig::AbstractStringLike, ::Type{RT}, args...) where RT i = isa(sig, AbstractString) ? something(findfirst("::", sig), 0:-1) : (0:-1) if !isempty(i) detail = @quark_str sig[last(i) + 1:end] @@ -202,7 +202,7 @@ macro sigatom(f) end end -function g_siginterruptible(f::Base.Callable, cb) # calls f (which may throw), but this function never throws +function g_siginterruptible(f::Base.Callable, @nospecialize(cb)) # calls f (which may throw), but this function never throws global g_sigatom_flag, g_stack prev = g_sigatom_flag[] @assert xor(prev, (current_task() !== g_stack)) diff --git a/src/cairo.jl b/src/cairo.jl index 45cdf79b..23c85d2d 100644 --- a/src/cairo.jl +++ b/src/cairo.jl @@ -17,10 +17,10 @@ mutable struct GtkCanvas <: GtkDrawingArea # NOT an @GType ids = Vector{Culong}(undef, 0) widget = new(da, false, false, MouseHandler(ids), nothing, nothing) widget.mouse.widget = widget - signal_connect(notify_realize, widget, "realize", Nothing, ()) - signal_connect(notify_unrealize, widget, "unrealize", Nothing, ()) + signal_connect(Base.inferencebarrier(notify_realize), widget, "realize", Nothing, ()) + signal_connect(Base.inferencebarrier(notify_unrealize), widget, "unrealize", Nothing, ()) on_signal_resize(notify_resize, widget) - signal_connect(canvas_on_draw_event, widget, "draw", Cint, (Ptr{Nothing},)) + signal_connect(Base.inferencebarrier(canvas_on_draw_event), widget, "draw", Cint, (Ptr{Nothing},)) push!(ids, on_signal_button_press(mousedown_cb, widget, false, widget.mouse)) push!(ids, on_signal_button_release(mouseup_cb, widget, false, widget.mouse)) push!(ids, on_signal_motion(mousemove_cb, widget, 0, 0, false, widget.mouse)) diff --git a/src/events.jl b/src/events.jl index 74eb8319..da9bd0e0 100644 --- a/src/events.jl +++ b/src/events.jl @@ -16,19 +16,19 @@ add_events(widget::GtkWidget, mask::Integer) = ccall((:gtk_widget_add_events, li #end -function on_signal_resize(resize_cb::Function, widget::GtkWidget, vargs...) +function on_signal_resize(@nospecialize(resize_cb::Function), widget::GtkWidget, vargs...) signal_connect(resize_cb, widget, "size-allocate", Nothing, (Ptr{GdkRectangle},), vargs...) end -function on_signal_destroy(destroy_cb::Function, widget::GObject, vargs...) +function on_signal_destroy(@nospecialize(destroy_cb::Function), widget::GObject, vargs...) signal_connect(destroy_cb, widget, "destroy", Nothing, (), vargs...) end -function on_signal_button_press(press_cb::Function, widget::GtkWidget, vargs...) +function on_signal_button_press(@nospecialize(press_cb::Function), widget::GtkWidget, vargs...) add_events(widget, GdkEventMask.BUTTON_PRESS) signal_connect(press_cb, widget, "button-press-event", Cint, (Ptr{GdkEventButton},), vargs...) end -function on_signal_button_release(release_cb::Function, widget::GtkWidget, vargs...) +function on_signal_button_release(@nospecialize(release_cb::Function), widget::GtkWidget, vargs...) add_events(widget, GdkEventMask.BUTTON_RELEASE) signal_connect(release_cb, widget, "button-release-event", Cint, (Ptr{GdkEventButton},), vargs...) end diff --git a/src/precompile.jl b/src/precompile.jl index b7263290..8acaa6f3 100644 --- a/src/precompile.jl +++ b/src/precompile.jl @@ -10,6 +10,7 @@ function _precompile_() precompile(Tuple{Type{GtkBox},Symbol}) precompile(Tuple{Type{GtkButtonLeaf},Ptr{GObject}}) precompile(Tuple{Type{GtkToolButton},String}) + precompile(Tuple{Type{GtkToolbar}}) precompile(Tuple{Type{GtkCanvas},Int,Int}) precompile(Tuple{typeof(signal_emit),GtkCanvas,String,Type,GdkEventScroll}) precompile(Tuple{typeof(signal_emit),GtkCanvas,String,Type,GdkEventMotion}) @@ -27,6 +28,9 @@ function _precompile_() precompile(Tuple{typeof(open_dialog),String,GtkWidget,Tuple{String}}) precompile(Tuple{typeof(open_dialog),String,GtkWidget,Tuple{String,String}}) precompile(Tuple{typeof(save_dialog),String,GtkWidget,Tuple{String}}) + precompile(Tuple{Type{GtkFileChooserDialogLeaf},String,GtkContainer,Int32,Tuple{Tuple{String, Int32}, Tuple{String, Int32}}}) + precompile(Tuple{Type{GtkFileFilterLeaf},String}) + precompile(Tuple{typeof(makefilters!),GtkFileChooser,Tuple{String, String}}) precompile(Tuple{typeof(draw),GtkCanvas,Bool}) for T in Any[ GtkWindowLeaf,