Skip to content
This repository has been archived by the owner on Jul 22, 2024. It is now read-only.

Commit

Permalink
Reduce specialization in GLib methods (#552)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
timholy authored Jan 14, 2021
1 parent 8742856 commit b61cb23
Show file tree
Hide file tree
Showing 7 changed files with 41 additions and 30 deletions.
2 changes: 1 addition & 1 deletion src/GLib/GLib.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
19 changes: 12 additions & 7 deletions src/GLib/gtype.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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})
Expand All @@ -377,15 +380,17 @@ 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
istop = topfinalizer[]
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
Expand All @@ -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[]
Expand All @@ -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)
Expand All @@ -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
Expand Down
16 changes: 9 additions & 7 deletions src/GLib/gvalues.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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

Expand Down
16 changes: 8 additions & 8 deletions src/GLib/signals.jl
Original file line number Diff line number Diff line change
@@ -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)
Expand All @@ -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)
Expand All @@ -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)
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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]
Expand Down Expand Up @@ -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))
Expand Down
6 changes: 3 additions & 3 deletions src/cairo.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
8 changes: 4 additions & 4 deletions src/events.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions src/precompile.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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})
Expand All @@ -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,
Expand Down

0 comments on commit b61cb23

Please sign in to comment.