Skip to content

Commit 1deedc4

Browse files
committed
feat(hot-reload): targeted lifecycle broadcast and ReloadRequestKind dispatch
Wire ReloadRequestKind through HandleHotReloadNotify callback so the runtime can dispatch FullRuntime requests directly to ReloadModules() without attempting targeted reload first. Add BroadcastLifecycleEventForModule() to emit MOD_RELOAD for only the single affected module instead of the full snapshot. Add BML_TEST-gated accessors (TestHandleHotReloadNotify, TestBroadcastLifecycleEventForModule, TestGetLifecycleBroadcastTargets) for unit-testing private methods.
1 parent 046e26f commit 1deedc4

2 files changed

Lines changed: 85 additions & 8 deletions

File tree

src/Core/ModuleRuntime.cpp

Lines changed: 72 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -361,6 +361,28 @@ namespace BML::Core {
361361
m_DiagCallback = std::move(callback);
362362
}
363363

364+
#if defined(BML_TEST)
365+
void ModuleRuntime::TestHandleHotReloadNotify(const std::string &mod_id, ReloadResult result,
366+
unsigned int version, ReloadFailure failure,
367+
ReloadRequestKind kind) {
368+
HandleHotReloadNotify(mod_id, result, version, failure, kind);
369+
}
370+
371+
void ModuleRuntime::TestBroadcastLifecycleEventForModule(const char *topic,
372+
const std::string &mod_id) const {
373+
BroadcastLifecycleEventForModule(topic, mod_id);
374+
}
375+
376+
std::vector<std::string> ModuleRuntime::TestGetLifecycleBroadcastTargets(
377+
const std::string &mod_id) const {
378+
std::vector<std::string> result;
379+
for (const auto &module : CollectLifecycleModulesForBroadcast(mod_id)) {
380+
result.push_back(module.id);
381+
}
382+
return result;
383+
}
384+
#endif
385+
364386
bool ModuleRuntime::ReloadModulesInternal(ModuleBootstrapDiagnostics &out_diag) {
365387
if (m_DiscoveredModsDir.empty()) {
366388
out_diag.load_error.message = "Hot reload requested before discovery";
@@ -470,6 +492,35 @@ namespace BML::Core {
470492
}
471493
}
472494

495+
void ModuleRuntime::BroadcastLifecycleEventForModule(const char *topic,
496+
const std::string &mod_id) const {
497+
if (!m_Kernel || mod_id.empty()) {
498+
return;
499+
}
500+
501+
auto modules = CollectLifecycleModulesForBroadcast(mod_id);
502+
if (!modules.empty()) {
503+
BroadcastLifecycleEvent(topic, modules);
504+
}
505+
}
506+
507+
std::vector<LoadedModuleSnapshot> ModuleRuntime::CollectLifecycleModulesForBroadcast(
508+
const std::string &mod_id) const {
509+
std::vector<LoadedModuleSnapshot> result;
510+
if (!m_Kernel || mod_id.empty()) {
511+
return result;
512+
}
513+
514+
auto snapshot = m_Kernel->context->GetLoadedModuleSnapshot();
515+
for (const auto &module : snapshot) {
516+
if (module.id == mod_id && module.manifest) {
517+
result.push_back(module);
518+
break;
519+
}
520+
}
521+
return result;
522+
}
523+
473524
void ModuleRuntime::UpdateHotReloadRegistration() {
474525
if (!m_HotReloadEnabled || !m_HotReloadCoordinator)
475526
return;
@@ -543,8 +594,9 @@ namespace BML::Core {
543594

544595
m_HotReloadCoordinator->SetNotifyCallback(
545596
[this](const std::string &mod_id, ReloadResult result,
546-
unsigned int version, ReloadFailure failure) {
547-
HandleHotReloadNotify(mod_id, result, version, failure);
597+
unsigned int version, ReloadFailure failure,
598+
ReloadRequestKind kind) {
599+
HandleHotReloadNotify(mod_id, result, version, failure, kind);
548600
});
549601

550602
m_HotReloadCoordinator->Start();
@@ -580,12 +632,12 @@ namespace BML::Core {
580632
}
581633

582634
// The ReloadableModuleSlot has already performed the DLL swap
583-
// (UnloadCurrent LoadVersion) including:
635+
// (UnloadCurrent -> LoadVersion) including:
584636
// - PrepareModuleForDetach gate (checks dependencies)
585637
// - DETACH entrypoint call
586638
// - CleanupModuleKernelState
587639
// - FreeLibrary old DLL
588-
// - CopyDllToTemp LoadLibrary new DLL
640+
// - CopyDllToTemp -> LoadLibrary new DLL
589641
// - ATTACH entrypoint call
590642
//
591643
// We just need to synchronize Context's loaded-module record
@@ -605,15 +657,28 @@ namespace BML::Core {
605657
return false;
606658
}
607659

608-
BroadcastLifecycleEvent(BML_TOPIC_SYSTEM_MOD_RELOAD, context.GetLoadedModuleSnapshot());
660+
BroadcastLifecycleEventForModule(BML_TOPIC_SYSTEM_MOD_RELOAD, mod_id);
609661
CoreLog(BML_LOG_INFO, kModuleRuntimeLogCategory,
610662
"Targeted hot reload of '%s' succeeded (version %u)",
611663
mod_id.c_str(), m_HotReloadCoordinator->GetModuleVersion(mod_id));
612664
return true;
613665
}
614666

615667
void ModuleRuntime::HandleHotReloadNotify(const std::string &mod_id, ReloadResult result,
616-
unsigned int version, ReloadFailure failure) {
668+
unsigned int version, ReloadFailure failure,
669+
ReloadRequestKind kind) {
670+
if (kind == ReloadRequestKind::FullRuntime) {
671+
ModuleBootstrapDiagnostics diag;
672+
if (ReloadModules(diag)) {
673+
CoreLog(BML_LOG_INFO, kModuleRuntimeLogCategory,
674+
"Full hot reload succeeded for '%s'", mod_id.c_str());
675+
} else {
676+
CoreLog(BML_LOG_ERROR, kModuleRuntimeLogCategory,
677+
"Full hot reload failed for '%s'", mod_id.c_str());
678+
}
679+
return;
680+
}
681+
617682
if (result == ReloadResult::Success) {
618683
CoreLog(BML_LOG_INFO, kModuleRuntimeLogCategory,
619684
"Hot reload notification: mod '%s' version %u, result=%d",
@@ -622,7 +687,7 @@ namespace BML::Core {
622687
// Try targeted single-module reload first (only updates Context state)
623688
ModuleBootstrapDiagnostics diag;
624689
if (ReloadSingleModule(mod_id, diag)) {
625-
// Targeted reload succeeded other modules were not disturbed
690+
// Targeted reload succeeded - other modules were not disturbed
626691
} else {
627692
// Fall back to full reload (e.g. module not found, Context sync failed)
628693
CoreLog(BML_LOG_WARN, kModuleRuntimeLogCategory,

src/Core/ModuleRuntime.h

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,16 +51,28 @@ namespace BML::Core {
5151

5252
void SetDiagnosticsCallback(std::function<void(const ModuleBootstrapDiagnostics &)> callback);
5353

54+
#if defined(BML_TEST)
55+
void TestHandleHotReloadNotify(const std::string &mod_id, ReloadResult result,
56+
unsigned int version, ReloadFailure failure,
57+
ReloadRequestKind kind);
58+
void TestBroadcastLifecycleEventForModule(const char *topic, const std::string &mod_id) const;
59+
std::vector<std::string> TestGetLifecycleBroadcastTargets(const std::string &mod_id) const;
60+
#endif
61+
5462
private:
5563
void FilterDisabledModules(std::vector<ResolvedNode> &order) const;
5664
void RecordLoadOrder(const std::vector<ResolvedNode> &order, ModuleBootstrapDiagnostics &diag) const;
5765
bool ReloadModulesInternal(ModuleBootstrapDiagnostics &out_diag);
5866
void BroadcastLifecycleEvent(const char *topic, const std::vector<LoadedModuleSnapshot> &modules) const;
67+
void BroadcastLifecycleEventForModule(const char *topic, const std::string &mod_id) const;
68+
std::vector<LoadedModuleSnapshot> CollectLifecycleModulesForBroadcast(
69+
const std::string &mod_id) const;
5970
void UpdateHotReloadRegistration();
6071
void EnsureHotReloadCoordinator();
6172
void StopHotReloadCoordinator();
6273
void HandleHotReloadNotify(const std::string &mod_id, ReloadResult result,
63-
unsigned int version, ReloadFailure failure);
74+
unsigned int version, ReloadFailure failure,
75+
ReloadRequestKind kind);
6476
bool ReloadSingleModule(const std::string &mod_id, ModuleBootstrapDiagnostics &out_diag);
6577
bool ShouldEnableHotReload() const;
6678
std::wstring GetHotReloadTempDirectory() const;

0 commit comments

Comments
 (0)