@@ -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,14 +1099,13 @@ 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 
@@ -1159,59 +1158,84 @@ end
11591158function  _insert_extension_triggers (parent:: PkgId , extensions:: Dict{String, <:Any} , weakdeps:: Dict{String, <:Any} )
11601159    for  (ext:: String , triggers:: Union{String, Vector{String}} ) in  extensions
11611160        triggers isa  String &&  (triggers =  [triggers])
1162-         triggers_id =  PkgId[]
11631161        id =  PkgId (uuid5 (parent. uuid, ext), ext)
1162+         gid =  ExtensionId (id, parent, 1  +  length (triggers))
1163+         trigger1 =  get! (Vector{ExtensionId}, EXT_DORMITORY, parent)
1164+         push! (trigger1, gid)
11641165        for  trigger in  triggers
11651166            #  TODO : Better error message if this lookup fails?
11661167            uuid_trigger =  UUID (weakdeps[trigger]:: String )
1167-             push! (triggers_id, PkgId (uuid_trigger, trigger))
1168+             trigger_id =  PkgId (uuid_trigger, trigger)
1169+             if  ! haskey (Base. loaded_modules, trigger_id) ||  haskey (package_locks, trigger_id)
1170+                 trigger1 =  get! (Vector{ExtensionId}, EXT_DORMITORY, trigger_id)
1171+                 push! (trigger1, gid)
1172+             else 
1173+                 gid. ntriggers -=  1 
1174+             end 
11681175        end 
1169-         gid =  ExtensionId (id, parent, triggers_id, false , false )
1170-         push! (EXT_DORMITORY, gid)
11711176    end 
11721177end 
11731178
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" 
1207-                 end 
1208-                 extid. succeeded =  true 
1209-             end 
1210-         end 
1179+ function  run_extension_callbacks (extid:: ExtensionId )
1180+     assert_havelock (require_lock)
1181+     succeeded =  try 
1182+         _require_prelocked (extid. id)
1183+         @debug  " Extension $(extid. id. name)  of $(extid. parentid. name)  loaded" 
1184+         true 
12111185    catch 
12121186        #  Try to continue loading if loading an extension errors
12131187        errs =  current_exceptions ()
12141188        @error  " Error during loading of extension"   exception= errs
1189+         false 
1190+     end 
1191+     return  succeeded
1192+ end 
1193+ 
1194+ function  run_extension_callbacks (pkgid:: PkgId )
1195+     assert_havelock (require_lock)
1196+     #  take ownership of extids that depend on this pkgid
1197+     extids =  pop! (EXT_DORMITORY, pkgid, nothing )
1198+     extids ===  nothing  &&  return 
1199+     for  extid in  extids
1200+         if  extid. ntriggers >  0 
1201+             #  It is possible that pkgid was loaded in an environment
1202+             #  below the one of the parent. This will cause a load failure when the
1203+             #  pkg ext tries to load the triggers. Therefore, check this first
1204+             #  before loading the pkg ext.
1205+             pkgenv =  Base. identify_package_env (extid. id, pkgid. name)
1206+             ext_not_allowed_load =  false 
1207+             if  pkgenv ===  nothing 
1208+                 ext_not_allowed_load =  true 
1209+             else 
1210+                 pkg, env =  pkgenv
1211+                 path =  Base. locate_package (pkg, env)
1212+                 if  path ===  nothing 
1213+                     ext_not_allowed_load =  true 
1214+                 end 
1215+             end 
1216+             if  ext_not_allowed_load
1217+                 @debug  " Extension $(extid. id. name)  of $(extid. parentid. name)  will not be loaded \
1218+                         since $(pkgid. name)  loaded in environment lower in load path"  
1219+                 #  indicate extid is expected to fail
1220+                 extid. ntriggers *=  - 1 
1221+             else 
1222+                 #  indicate pkgid is loaded
1223+                 extid. ntriggers -=  1 
1224+             end 
1225+         end 
1226+         if  extid. ntriggers <  0 
1227+             #  indicate pkgid is loaded
1228+             extid. ntriggers +=  1 
1229+             succeeded =  false 
1230+         else 
1231+             succeeded =  true 
1232+         end 
1233+         if  extid. ntriggers ==  0 
1234+             #  actually load extid, now that all dependencies are met,
1235+             #  and record the result
1236+             succeeded =  succeeded &&  run_extension_callbacks (extid)
1237+             succeeded ||  push! (EXT_DORMITORY_FAILED, extid)
1238+         end 
12151239    end 
12161240    nothing 
12171241end 
@@ -1224,7 +1248,19 @@ This is used in cases where the automatic loading of an extension failed
12241248due to some problem with the extension. Instead of restarting the Julia session, 
12251249the extension can be fixed, and this function run. 
12261250""" 
1227- retry_load_extensions () =  run_extension_callbacks (; force= true )
1251+ function  retry_load_extensions ()
1252+     @lock  require_lock begin 
1253+     #  this copy is desired since run_extension_callbacks will release this lock
1254+     #  so this can still mutate the list to drop successful ones
1255+     failed =  copy (EXT_DORMITORY_FAILED)
1256+     empty! (EXT_DORMITORY_FAILED)
1257+     filter! (failed) do  extid
1258+         return  ! run_extension_callbacks (extid)
1259+     end 
1260+     prepend! (EXT_DORMITORY_FAILED, failed)
1261+     end 
1262+     nothing 
1263+ end 
12281264
12291265""" 
12301266    get_extension(parent::Module, extension::Symbol) 
0 commit comments