Skip to content

Commit

Permalink
lazy LibGit2 initialization
Browse files Browse the repository at this point in the history
  • Loading branch information
StefanKarpinski committed Jul 16, 2018
1 parent a5a4e99 commit 723dbf5
Show file tree
Hide file tree
Showing 23 changed files with 219 additions and 19 deletions.
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

0 comments on commit 723dbf5

Please sign in to comment.