Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

lazy LibGit2 initialization #28113

Merged
merged 1 commit into from
Jul 17, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion base/precompile.jl
Original file line number Diff line number Diff line change
Expand Up @@ -465,7 +465,8 @@ precompile(Tuple{typeof(Core.Compiler.length), Tuple{typeof(Base.:(*)), Int64}})
precompile(Tuple{typeof(Core.Compiler.length), Tuple{typeof(Base.open_flags)}})
precompile(Tuple{typeof(Core.Compiler.vect), Type{typeof(typeassert)}})
precompile(Tuple{typeof(Distributed.terminate_all_workers)})
precompile(Tuple{typeof(LibGit2.__init__)})
precompile(Tuple{typeof(LibGit2.ensure_initialized)})
precompile(Tuple{typeof(LibGit2.initialize)})
precompile(Tuple{typeof(Logging.__init__)})
precompile(Tuple{typeof(Logging.default_metafmt), Base.CoreLogging.LogLevel, Module, Symbol, Symbol, String, Int64})
precompile(Tuple{typeof(Logging.default_metafmt), Base.CoreLogging.LogLevel, Nothing, Symbol, Symbol, String, Int64})
Expand Down
41 changes: 29 additions & 12 deletions stdlib/LibGit2/src/LibGit2.jl
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ export with, GitRepo, GitConfig
const GITHUB_REGEX =
r"^(?:git@|git://|https://(?:[\w\.\+\-]+@)?)github.com[:/](([^/].+)/(.+?))(?:\.git)?$"i

const REFCOUNT = Threads.Atomic{UInt}()
const REFCOUNT = Threads.Atomic{Int}(0)

function ensure_initialized end

include("utils.jl")
include("consts.jl")
Expand Down Expand Up @@ -963,22 +965,29 @@ function transact(f::Function, repo::GitRepo)
end
end

function set_ssl_cert_locations(cert_loc)
cert_file = isfile(cert_loc) ? cert_loc : Cstring(C_NULL)
cert_dir = isdir(cert_loc) ? cert_loc : Cstring(C_NULL)
cert_file == C_NULL && cert_dir == C_NULL && return
@check ccall((:git_libgit2_opts, :libgit2), Cint,
(Cint, Cstring, Cstring),
Cint(Consts.SET_SSL_CERT_LOCATIONS), cert_file, cert_dir)
## lazy libgit2 initialization

function ensure_initialized()
x = Threads.atomic_cas!(REFCOUNT, 0, 1)
if x < 0
negative_refcount_error(x)::Union{}
end
if x == 0
initialize()
end
return nothing
end

@noinline function negative_refcount_error(x::Int)
error("Negative LibGit2 REFCOUNT $x\nThis shouldn't happen, please file a bug report!")
end

function __init__()
@noinline function initialize()
@check ccall((:git_libgit2_init, :libgit2), Cint, ())
REFCOUNT[] = 1

atexit() do
if Threads.atomic_sub!(REFCOUNT, UInt(1)) == 1
# refcount zero, no objects to be finalized
# refcount zero, no objects to be finalized
if Threads.atomic_sub!(REFCOUNT, 1) >= 1
ccall((:git_libgit2_shutdown, :libgit2), Cint, ())
end
end
Expand All @@ -998,5 +1007,13 @@ function __init__()
end
end

function set_ssl_cert_locations(cert_loc)
cert_file = isfile(cert_loc) ? cert_loc : Cstring(C_NULL)
cert_dir = isdir(cert_loc) ? cert_loc : Cstring(C_NULL)
cert_file == C_NULL && cert_dir == C_NULL && return
@check ccall((:git_libgit2_opts, :libgit2), Cint,
(Cint, Cstring, Cstring),
Cint(Consts.SET_SSL_CERT_LOCATIONS), cert_file, cert_dir)
end

end # module
3 changes: 3 additions & 0 deletions stdlib/LibGit2/src/blame.jl
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ the file when, and how. `options` controls how to separate the contents of the f
which commits to probe - see [`BlameOptions`](@ref) for more information.
"""
function GitBlame(repo::GitRepo, path::AbstractString; options::BlameOptions=BlameOptions())
ensure_initialized()
blame_ptr_ptr = Ref{Ptr{Cvoid}}(C_NULL)
@check ccall((:git_blame_file, :libgit2), Cint,
(Ptr{Ptr{Cvoid}}, Ptr{Cvoid}, Cstring, Ptr{BlameOptions}),
Expand All @@ -25,13 +26,15 @@ a function added to a source file or an inner loop that was optimized out of
that function later.
"""
function counthunks(blame::GitBlame)
ensure_initialized()
return ccall((:git_blame_get_hunk_count, :libgit2), Int32, (Ptr{Cvoid},), blame.ptr)
end

function Base.getindex(blame::GitBlame, i::Integer)
if !(1 <= i <= counthunks(blame))
throw(BoundsError(blame, (i,)))
end
ensure_initialized()
GC.@preserve blame begin
hunk_ptr = ccall((:git_blame_get_hunk_byindex, :libgit2),
Ptr{BlameHunk},
Expand Down
4 changes: 4 additions & 0 deletions stdlib/LibGit2/src/blob.jl
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# This file is a part of Julia. License is MIT: https://julialang.org/license

function Base.length(blob::GitBlob)
ensure_initialized()
return ccall((:git_blob_rawsize, :libgit2), Int64, (Ptr{Cvoid},), blob.ptr)
end

Expand All @@ -18,6 +19,7 @@ See also [`content`](@ref), which *will* throw an error if the content of the `b
is binary and not valid Unicode.
"""
function rawcontent(blob::GitBlob)
ensure_initialized()
ptr = ccall((:git_blob_rawcontent, :libgit2), Ptr{UInt8}, (Ptr{Cvoid},), blob.ptr)
copy(unsafe_wrap(Array, ptr, (length(blob),), own = false))
end
Expand All @@ -44,6 +46,7 @@ looking for a reasonable ratio of printable to non-printable characters among
the first 8000 bytes.
"""
function isbinary(blob::GitBlob)
ensure_initialized()
bin_flag = ccall((:git_blob_is_binary, :libgit2), Cint, (Ptr{Cvoid},), blob.ptr)
return bin_flag == 1
end
Expand All @@ -62,6 +65,7 @@ id = LibGit2.addblob!(repo, blob_file)
```
"""
function addblob!(repo::GitRepo, path::AbstractString)
ensure_initialized()
id_ref = Ref{GitHash}()
@check ccall((:git_blob_create_fromdisk, :libgit2), Cint,
(Ptr{GitHash}, Ptr{Cvoid}, Cstring),
Expand Down
7 changes: 7 additions & 0 deletions stdlib/LibGit2/src/callbacks.jl
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ Function sets `+refs/*:refs/*` refspecs and `mirror` flag for remote reference.
"""
function mirror_callback(remote::Ptr{Ptr{Cvoid}}, repo_ptr::Ptr{Cvoid},
name::Cstring, url::Cstring, payload::Ptr{Cvoid})
ensure_initialized()
# Create the remote with a mirroring url
fetch_spec = "+refs/*:refs/*"
err = ccall((:git_remote_create_with_fetchspec, :libgit2), Cint,
Expand Down Expand Up @@ -40,6 +41,7 @@ function is_passphrase_required(private_key::AbstractString)
end

function user_abort()
ensure_initialized()
# Note: Potentially it could be better to just throw a Julia error.
ccall((:giterr_set_str, :libgit2), Cvoid,
(Cint, Cstring), Cint(Error.Callback),
Expand All @@ -48,20 +50,23 @@ function user_abort()
end

function prompt_limit()
ensure_initialized()
ccall((:giterr_set_str, :libgit2), Cvoid,
(Cint, Cstring), Cint(Error.Callback),
"Aborting, maximum number of prompts reached.")
return Cint(Error.EAUTH)
end

function exhausted_abort()
ensure_initialized()
ccall((:giterr_set_str, :libgit2), Cvoid,
(Cint, Cstring), Cint(Error.Callback),
"All authentication methods have failed.")
return Cint(Error.EAUTH)
end

function authenticate_ssh(libgit2credptr::Ptr{Ptr{Cvoid}}, p::CredentialPayload, username_ptr)
ensure_initialized()
cred = p.credential::SSHCredential
revised = false

Expand Down Expand Up @@ -173,6 +178,7 @@ function authenticate_ssh(libgit2credptr::Ptr{Ptr{Cvoid}}, p::CredentialPayload,
end

function authenticate_userpass(libgit2credptr::Ptr{Ptr{Cvoid}}, p::CredentialPayload)
ensure_initialized()
cred = p.credential::UserPasswordCredential
revised = false

Expand Down Expand Up @@ -326,6 +332,7 @@ function credentials_callback(libgit2credptr::Ptr{Ptr{Cvoid}}, url_ptr::Cstring,
# with the requested authentication method.
if err == 0
if p.explicit !== nothing
ensure_initialized()
ccall((:giterr_set_str, :libgit2), Cvoid, (Cint, Cstring), Cint(Error.Callback),
"The explicitly provided credential is incompatible with the requested " *
"authentication methods.")
Expand Down
4 changes: 4 additions & 0 deletions stdlib/LibGit2/src/commit.jl
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ leading newlines removed). If `raw` is `true`, the message is not stripped
of any such newlines.
"""
function message(c::GitCommit, raw::Bool=false)
ensure_initialized()
GC.@preserve c begin
local msg_ptr::Cstring
msg_ptr = raw ? ccall((:git_commit_message_raw, :libgit2), Cstring, (Ptr{Cvoid},), c.ptr) :
Expand All @@ -28,6 +29,7 @@ Return the `Signature` of the author of the commit `c`. The author is
the person who made changes to the relevant file(s). See also [`committer`](@ref).
"""
function author(c::GitCommit)
ensure_initialized()
GC.@preserve c begin
ptr = ccall((:git_commit_author, :libgit2), Ptr{SignatureStruct}, (Ptr{Cvoid},), c.ptr)
@assert ptr != C_NULL
Expand All @@ -45,6 +47,7 @@ need not be the same as the `author`, for example, if the `author` emailed a pat
a `committer` who committed it.
"""
function committer(c::GitCommit)
ensure_initialized()
GC.@preserve c begin
ptr = ccall((:git_commit_committer, :libgit2), Ptr{SignatureStruct}, (Ptr{Cvoid},), c.ptr)
sig = Signature(ptr)
Expand All @@ -65,6 +68,7 @@ function commit(repo::GitRepo,
committer::GitSignature,
tree::GitTree,
parents::GitCommit...)
ensure_initialized()
commit_id_ptr = Ref(GitHash())
nparents = length(parents)
parentptrs = Ptr{Cvoid}[c.ptr for c in parents]
Expand Down
17 changes: 17 additions & 0 deletions stdlib/LibGit2/src/config.jl
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ function GitConfig(path::AbstractString,
level::Consts.GIT_CONFIG = Consts.CONFIG_LEVEL_APP,
repo::Union{GitRepo, Nothing}=nothing,
force::Bool=false)
ensure_initialized()
# create new config object
cfg_ptr_ptr = Ref{Ptr{Cvoid}}(C_NULL)
@check ccall((:git_config_new, :libgit2), Cint, (Ptr{Ptr{Cvoid}},), cfg_ptr_ptr)
Expand All @@ -31,6 +32,7 @@ have a specific configuration file set, the default git configuration will be
used.
"""
function GitConfig(repo::GitRepo)
ensure_initialized()
cfg_ptr_ptr = Ref{Ptr{Cvoid}}(C_NULL)
@check ccall((:git_repository_config, :libgit2), Cint,
(Ptr{Ptr{Cvoid}}, Ptr{Cvoid}), cfg_ptr_ptr, repo.ptr)
Expand All @@ -45,6 +47,7 @@ files into a prioritized configuration. This can be used to access default confi
options outside a specific git repository.
"""
function GitConfig(level::Consts.GIT_CONFIG = Consts.CONFIG_LEVEL_DEFAULT)
ensure_initialized()
cfg_ptr_ptr = Ref{Ptr{Cvoid}}(C_NULL)
@check ccall((:git_config_open_default, :libgit2), Cint,
(Ptr{Ptr{Cvoid}},), cfg_ptr_ptr)
Expand Down Expand Up @@ -85,6 +88,7 @@ function addfile(cfg::GitConfig, path::AbstractString,
level::Consts.GIT_CONFIG = Consts.CONFIG_LEVEL_APP,
repo::Union{GitRepo, Nothing} = nothing,
force::Bool=false)
ensure_initialized()
@static if LibGit2.VERSION >= v"0.27.0"
@check ccall((:git_config_add_file_ondisk, :libgit2), Cint,
(Ptr{Ptr{Cvoid}}, Cstring, Cint, Ptr{Cvoid}, Cint),
Expand All @@ -98,6 +102,7 @@ function addfile(cfg::GitConfig, path::AbstractString,
end

function get(::Type{<:AbstractString}, c::GitConfig, name::AbstractString)
ensure_initialized()
buf_ref = Ref(Buffer())
@check ccall((:git_config_get_string_buf, :libgit2), Cint,
(Ptr{Buffer}, Ptr{Cvoid}, Cstring), buf_ref, c.ptr, name)
Expand All @@ -108,20 +113,23 @@ function get(::Type{<:AbstractString}, c::GitConfig, name::AbstractString)
end

function get(::Type{Bool}, c::GitConfig, name::AbstractString)
ensure_initialized()
val_ptr = Ref(Cint(0))
@check ccall((:git_config_get_bool, :libgit2), Cint,
(Ptr{Cint}, Ptr{Cvoid}, Cstring), val_ptr, c.ptr, name)
return Bool(val_ptr[])
end

function get(::Type{Int32}, c::GitConfig, name::AbstractString)
ensure_initialized()
val_ptr = Ref(Cint(0))
@check ccall((:git_config_get_int32, :libgit2), Cint,
(Ptr{Cint}, Ptr{Cvoid}, Cstring), val_ptr, c.ptr, name)
return val_ptr[]
end

function get(::Type{Int64}, c::GitConfig, name::AbstractString)
ensure_initialized()
val_ptr = Ref(Cintmax_t(0))
@check ccall((:git_config_get_int64, :libgit2), Cint,
(Ptr{Cintmax_t}, Ptr{Cvoid}, Cstring), val_ptr, c.ptr, name)
Expand Down Expand Up @@ -155,34 +163,40 @@ function getconfig(name::AbstractString, default)
end

function set!(c::GitConfig, name::AbstractString, value::AbstractString)
ensure_initialized()
@check ccall((:git_config_set_string, :libgit2), Cint,
(Ptr{Cvoid}, Cstring, Cstring), c.ptr, name, value)
end

function set!(c::GitConfig, name::AbstractString, value::Bool)
ensure_initialized()
bval = Int32(value)
@check ccall((:git_config_set_bool, :libgit2), Cint,
(Ptr{Cvoid}, Cstring, Cint), c.ptr, name, bval)
end

function set!(c::GitConfig, name::AbstractString, value::Int32)
ensure_initialized()
@check ccall((:git_config_set_int32, :libgit2), Cint,
(Ptr{Cvoid}, Cstring, Cint), c.ptr, name, value)
end

function set!(c::GitConfig, name::AbstractString, value::Int64)
ensure_initialized()
@check ccall((:git_config_set_int64, :libgit2), Cint,
(Ptr{Cvoid}, Cstring, Cintmax_t), c.ptr, name, value)
end

function GitConfigIter(cfg::GitConfig)
ensure_initialized()
ci_ptr = Ref{Ptr{Cvoid}}(C_NULL)
@check ccall((:git_config_iterator_new, :libgit2), Cint,
(Ptr{Ptr{Cvoid}}, Ptr{Cvoid}), ci_ptr, cfg.ptr)
return GitConfigIter(ci_ptr[])
end

function GitConfigIter(cfg::GitConfig, name::AbstractString)
ensure_initialized()
ci_ptr = Ref{Ptr{Cvoid}}(C_NULL)
@check ccall((:git_config_multivar_iterator_new, :libgit2), Cint,
(Ptr{Ptr{Cvoid}}, Ptr{Cvoid}, Cstring, Cstring),
Expand All @@ -191,6 +205,7 @@ function GitConfigIter(cfg::GitConfig, name::AbstractString)
end

function GitConfigIter(cfg::GitConfig, name::AbstractString, value::Regex)
ensure_initialized()
ci_ptr = Ref{Ptr{Cvoid}}(C_NULL)
@check ccall((:git_config_multivar_iterator_new, :libgit2), Cint,
(Ptr{Ptr{Cvoid}}, Ptr{Cvoid}, Cstring, Cstring),
Expand All @@ -199,6 +214,7 @@ function GitConfigIter(cfg::GitConfig, name::AbstractString, value::Regex)
end

function GitConfigIter(cfg::GitConfig, name::Regex)
ensure_initialized()
ci_ptr = Ref{Ptr{Cvoid}}(C_NULL)
@check ccall((:git_config_iterator_glob_new, :libgit2), Cint,
(Ptr{Ptr{Cvoid}}, Ptr{Cvoid}, Cstring),
Expand All @@ -207,6 +223,7 @@ function GitConfigIter(cfg::GitConfig, name::Regex)
end

function Base.iterate(ci::GitConfigIter, state=nothing)
ensure_initialized()
entry_ptr_ptr = Ref{Ptr{ConfigEntry}}(C_NULL)
err = ccall((:git_config_next, :libgit2), Cint,
(Ptr{Ptr{ConfigEntry}}, Ptr{Cvoid}), entry_ptr_ptr, ci.ptr)
Expand Down
2 changes: 1 addition & 1 deletion stdlib/LibGit2/src/consts.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

module Consts

import ..LibGit2: version
import ..LibGit2: version, ensure_initialized

const HEAD_FILE = "HEAD"
const FETCH_HEAD = "FETCH_HEAD"
Expand Down
Loading