@@ -1076,9 +1076,9 @@ function register_restored_modules(sv::SimpleVector, pkg::PkgId, path::String)
10761076end
10771077
10781078function run_package_callbacks (modkey:: PkgId )
1079+ run_extension_callbacks (modkey)
10791080 assert_havelock (require_lock)
10801081 unlock (require_lock)
1081- run_extension_callbacks ()
10821082 try
10831083 for callback in package_callbacks
10841084 invokelatest (callback, modkey)
@@ -1099,23 +1099,23 @@ end
10991099# #############
11001100
11011101mutable struct ExtensionId
1102- const id:: PkgId # Could be symbol?
1103- const parentid:: PkgId
1104- const triggers:: Vector{PkgId} # What packages have to be loaded for the extension to get loaded
1105- triggered:: Bool
1106- succeeded:: Bool
1102+ const id:: PkgId
1103+ const parentid:: PkgId # just need the name, for printing
1104+ ntriggers:: Int # how many more packages must be defined until this is loaded
11071105end
11081106
1109- const EXT_DORMITORY = ExtensionId[]
1107+ const EXT_DORMITORY = Dict {PkgId,Vector{ExtensionId}} ()
1108+ const EXT_DORMITORY_FAILED = ExtensionId[]
11101109
11111110function insert_extension_triggers (pkg:: PkgId )
11121111 pkg. uuid === nothing && return
1112+ extensions_added = Set {PkgId} ()
11131113 for env in load_path ()
1114- insert_extension_triggers ( env, pkg)
1114+ insert_extension_triggers! (extensions_added, env, pkg)
11151115 end
11161116end
11171117
1118- function insert_extension_triggers ( env:: String , pkg:: PkgId ):: Union{Nothing,Missing}
1118+ function insert_extension_triggers! (extensions_added :: Set{PkgId} , env:: String , pkg:: PkgId ):: Union{Nothing,Missing}
11191119 project_file = env_project_file (env)
11201120 if project_file isa String
11211121 manifest_file = project_file_manifest_path (project_file)
@@ -1133,7 +1133,7 @@ function insert_extension_triggers(env::String, pkg::PkgId)::Union{Nothing,Missi
11331133 extensions === nothing && return
11341134 weakdeps === nothing && return
11351135 if weakdeps isa Dict{String, Any}
1136- return _insert_extension_triggers ( pkg, extensions, weakdeps)
1136+ return _insert_extension_triggers! (extensions_added, pkg, extensions, weakdeps)
11371137 end
11381138
11391139 d_weakdeps = Dict {String, String} ()
@@ -1148,70 +1148,97 @@ function insert_extension_triggers(env::String, pkg::PkgId)::Union{Nothing,Missi
11481148 d_weakdeps[dep_name] = uuid
11491149 end
11501150 @assert length (d_weakdeps) == length (weakdeps)
1151- return _insert_extension_triggers ( pkg, extensions, d_weakdeps)
1151+ return _insert_extension_triggers! (extensions_added, pkg, extensions, d_weakdeps)
11521152 end
11531153 end
11541154 end
11551155 end
11561156 return nothing
11571157end
11581158
1159- function _insert_extension_triggers ( parent:: PkgId , extensions:: Dict{String, <:Any} , weakdeps:: Dict{String, <:Any} )
1159+ function _insert_extension_triggers! (extensions_added :: Set{PkgId} , parent:: PkgId , extensions:: Dict{String, <:Any} , weakdeps:: Dict{String, <:Any} )
11601160 for (ext:: String , triggers:: Union{String, Vector{String}} ) in extensions
11611161 triggers isa String && (triggers = [triggers])
1162- triggers_id = PkgId[]
11631162 id = PkgId (uuid5 (parent. uuid, ext), ext)
1163+ # Only add triggers for an extension from one env.
1164+ id in extensions_added && continue
1165+ push! (extensions_added, id)
1166+ gid = ExtensionId (id, parent, 1 + length (triggers))
1167+ trigger1 = get! (Vector{ExtensionId}, EXT_DORMITORY, parent)
1168+ push! (trigger1, gid)
11641169 for trigger in triggers
11651170 # TODO : Better error message if this lookup fails?
11661171 uuid_trigger = UUID (weakdeps[trigger]:: String )
1167- push! (triggers_id, PkgId (uuid_trigger, trigger))
1172+ trigger_id = PkgId (uuid_trigger, trigger)
1173+ if ! haskey (Base. loaded_modules, trigger_id) || haskey (package_locks, trigger_id)
1174+ trigger1 = get! (Vector{ExtensionId}, EXT_DORMITORY, trigger_id)
1175+ push! (trigger1, gid)
1176+ else
1177+ gid. ntriggers -= 1
1178+ end
11681179 end
1169- gid = ExtensionId (id, parent, triggers_id, false , false )
1170- push! (EXT_DORMITORY, gid)
11711180 end
11721181end
11731182
1174- function run_extension_callbacks (; force:: Bool = false )
1175- try
1176- # TODO , if `EXT_DORMITORY` becomes very long, do something smarter
1177- for extid in EXT_DORMITORY
1178- extid. succeeded && continue
1179- ! force && extid. triggered && continue
1180- if all (x -> haskey (Base. loaded_modules, x) && ! haskey (package_locks, x), extid. triggers)
1181- ext_not_allowed_load = nothing
1182- extid. triggered = true
1183- # It is possible that some of the triggers were loaded in an environment
1184- # below the one of the parent. This will cause a load failure when the
1185- # pkg ext tries to load the triggers. Therefore, check this first
1186- # before loading the pkg ext.
1187- for trigger in extid. triggers
1188- pkgenv = Base. identify_package_env (extid. id, trigger. name)
1189- if pkgenv === nothing
1190- ext_not_allowed_load = trigger
1191- break
1192- else
1193- pkg, env = pkgenv
1194- path = Base. locate_package (pkg, env)
1195- if path === nothing
1196- ext_not_allowed_load = trigger
1197- break
1198- end
1199- end
1200- end
1201- if ext_not_allowed_load != = nothing
1202- @debug " Extension $(extid. id. name) of $(extid. parentid. name) not loaded due to \
1203- $(ext_not_allowed_load. name) loaded in environment lower in load path"
1204- else
1205- require (extid. id)
1206- @debug " Extension $(extid. id. name) of $(extid. parentid. name) loaded"
1183+ function run_extension_callbacks (extid:: ExtensionId )
1184+ assert_havelock (require_lock)
1185+ succeeded = try
1186+ _require_prelocked (extid. id)
1187+ @debug " Extension $(extid. id. name) of $(extid. parentid. name) loaded"
1188+ true
1189+ catch
1190+ # Try to continue loading if loading an extension errors
1191+ @error " Error during loading of extension $(extid. id. name) of $(extid. parentid. name) "
1192+ false
1193+ end
1194+ return succeeded
1195+ end
1196+
1197+ function run_extension_callbacks (pkgid:: PkgId )
1198+ assert_havelock (require_lock)
1199+ # take ownership of extids that depend on this pkgid
1200+ extids = pop! (EXT_DORMITORY, pkgid, nothing )
1201+ extids === nothing && return
1202+ for extid in extids
1203+ if extid. ntriggers > 0
1204+ # It is possible that pkgid was loaded in an environment
1205+ # below the one of the parent. This will cause a load failure when the
1206+ # pkg ext tries to load the triggers. Therefore, check this first
1207+ # before loading the pkg ext.
1208+ pkgenv = Base. identify_package_env (extid. id, pkgid. name)
1209+ ext_not_allowed_load = false
1210+ if pkgenv === nothing
1211+ ext_not_allowed_load = true
1212+ else
1213+ pkg, env = pkgenv
1214+ path = Base. locate_package (pkg, env)
1215+ if path === nothing
1216+ ext_not_allowed_load = true
12071217 end
1208- extid. succeeded = true
1218+ end
1219+ if ext_not_allowed_load
1220+ @debug " Extension $(extid. id. name) of $(extid. parentid. name) will not be loaded \
1221+ since $(pkgid. name) loaded in environment lower in load path"
1222+ # indicate extid is expected to fail
1223+ extid. ntriggers *= - 1
1224+ else
1225+ # indicate pkgid is loaded
1226+ extid. ntriggers -= 1
12091227 end
12101228 end
1211- catch
1212- # Try to continue loading if loading an extension errors
1213- errs = current_exceptions ()
1214- @error " Error during loading of extension" exception= errs
1229+ if extid. ntriggers < 0
1230+ # indicate pkgid is loaded
1231+ extid. ntriggers += 1
1232+ succeeded = false
1233+ else
1234+ succeeded = true
1235+ end
1236+ if extid. ntriggers == 0
1237+ # actually load extid, now that all dependencies are met,
1238+ # and record the result
1239+ succeeded = succeeded && run_extension_callbacks (extid)
1240+ succeeded || push! (EXT_DORMITORY_FAILED, extid)
1241+ end
12151242 end
12161243 nothing
12171244end
@@ -1224,7 +1251,19 @@ This is used in cases where the automatic loading of an extension failed
12241251due to some problem with the extension. Instead of restarting the Julia session,
12251252the extension can be fixed, and this function run.
12261253"""
1227- retry_load_extensions () = run_extension_callbacks (; force= true )
1254+ function retry_load_extensions ()
1255+ @lock require_lock begin
1256+ # this copy is desired since run_extension_callbacks will release this lock
1257+ # so this can still mutate the list to drop successful ones
1258+ failed = copy (EXT_DORMITORY_FAILED)
1259+ empty! (EXT_DORMITORY_FAILED)
1260+ filter! (failed) do extid
1261+ return ! run_extension_callbacks (extid)
1262+ end
1263+ prepend! (EXT_DORMITORY_FAILED, failed)
1264+ end
1265+ nothing
1266+ end
12281267
12291268"""
12301269 get_extension(parent::Module, extension::Symbol)
0 commit comments