use crate::storage::{ArtifactLoadOp, ArtifactStorage, HandleOp, IndirectIdentifier};
use crate::ArtifactTypeId;
use crossbeam_channel::{Receiver, Sender};
use hydrate_base::handle::{
ArtifactRef, LoadState, LoadStateProvider, LoaderInfoProvider, ResolvedLoadHandle,
};
use hydrate_base::hashing::{HashMap, HashSet};
use hydrate_base::ArtifactId;
use hydrate_base::{ArtifactManifestData, LoadHandle, StringHash};
use std::fmt::Formatter;
use std::hash::Hash;
use std::sync::atomic::Ordering;
use std::sync::{Arc, Mutex};
#[derive(Debug)]
pub struct ArtifactMetadata {
pub dependencies: Vec<ArtifactId>,
pub artifact_type_id: ArtifactTypeId,
pub hash: u64,
}
pub struct ArtifactData {
pub data: Vec<u8>,
}
impl std::fmt::Debug for ArtifactData {
fn fmt(
&self,
f: &mut std::fmt::Formatter<'_>,
) -> std::fmt::Result {
f.debug_struct("ArtifactData")
.field("data_length", &self.data.len())
.finish()
}
}
#[derive(Debug)]
pub struct RequestMetadataResult {
pub artifact_id: ArtifactId,
pub load_handle: LoadHandle,
pub result: std::io::Result<ArtifactMetadata>,
}
#[derive(Debug)]
pub struct RequestDataResult {
pub artifact_id: ArtifactId,
pub load_handle: LoadHandle,
pub result: std::io::Result<ArtifactData>,
}
#[derive(Copy, Clone, PartialEq, Eq)]
pub struct ManifestBuildHash(pub u64);
impl std::fmt::Debug for ManifestBuildHash {
fn fmt(
&self,
f: &mut Formatter<'_>,
) -> std::fmt::Result {
write!(f, "ManifestBuildHash({:0>16x})", self.0)
}
}
pub trait LoaderIO: Sync + Send {
fn update(&mut self);
fn current_build_hash(&self) -> ManifestBuildHash;
fn pending_build_hash(&self) -> Option<ManifestBuildHash>;
fn activate_pending_build_hash(
&mut self,
new_build_hash: ManifestBuildHash,
);
fn manifest_entry(
&self,
artifact_id: ArtifactId,
) -> Option<&ArtifactManifestData>;
fn resolve_indirect(
&self,
indirect_identifier: &IndirectIdentifier,
) -> Option<&ArtifactManifestData>;
fn request_metadata(
&self,
build_hash: ManifestBuildHash,
load_handle: LoadHandle,
artifact_id: ArtifactId,
);
fn request_data(
&self,
build_hash: ManifestBuildHash,
load_handle: LoadHandle,
artifact_id: ArtifactId,
hash: u64,
);
}
#[derive(Debug)]
pub enum LoaderEvent {
TryLoad(LoadHandle),
TryUnload(LoadHandle),
MetadataRequestComplete(RequestMetadataResult),
DependenciesLoaded(LoadHandle),
DataRequestComplete(RequestDataResult),
LoadResult(HandleOp),
}
#[derive(Debug)]
struct IndirectLoad {
id: IndirectIdentifier,
resolved_id_and_hash: Option<ArtifactIdAndHash>,
external_ref_count_indirect: u32,
}
struct LoadHandleInfo {
artifact_id: ArtifactId,
artifact_type_id: ArtifactTypeId,
hash: u64,
load_state: LoadState,
external_ref_count_direct: u32,
internal_ref_count: u32,
blocking_dependency_count: u32,
blocked_loads: Vec<LoadHandle>,
dependencies: Vec<LoadHandle>,
symbol: Option<StringHash>,
debug_name: Option<Arc<String>>,
}
struct ReloadAction {
load_handles_to_reload: Vec<LoadHandle>,
}
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
struct ArtifactIdAndHash {
id: ArtifactId,
hash: u64,
}
struct LoaderInner {
next_handle_index: u64,
load_handle_infos: HashMap<LoadHandle, LoadHandleInfo>,
artifact_id_to_handle: HashMap<ArtifactIdAndHash, LoadHandle>,
loader_io: Box<dyn LoaderIO>,
events_tx: Sender<LoaderEvent>,
events_rx: Receiver<LoaderEvent>,
indirect_states: HashMap<LoadHandle, IndirectLoad>,
indirect_to_load: HashMap<IndirectIdentifier, Arc<ResolvedLoadHandle>>,
current_build_hash: ManifestBuildHash,
current_reload_action: Option<ReloadAction>,
}
impl LoaderInner {
pub fn log_load_state_recursive(
&self,
load_handle: LoadHandle,
indent: usize,
) {
let load_handle_info = self.load_handle_infos.get(&load_handle).unwrap();
log::debug!(
"{:indent$}{:?} {:?} {:?}",
"",
load_handle_info.artifact_id,
load_handle_info.debug_name,
load_handle_info.load_state,
indent = indent
);
for dependency in &load_handle_info.dependencies {
self.log_load_state_recursive(*dependency, indent + 2);
}
}
#[profiling::function]
fn update(
&mut self,
artifact_storage: &mut dyn ArtifactStorage,
) {
self.loader_io.update();
if let Some(current_reload_action) = &self.current_reload_action {
let mut reload_complete = true;
for &load_handle in ¤t_reload_action.load_handles_to_reload {
let load_handle_info = self.load_handle_infos.get(&load_handle).unwrap();
if load_handle_info.load_state != LoadState::Loaded {
reload_complete = false;
break;
}
}
if reload_complete {
log::info!("All artifacts we need to reload are ready, updating indirect handles to point at new data");
for (_, indirect_load) in &mut self.indirect_states {
let new_manifest_entry = self.loader_io.resolve_indirect(&indirect_load.id);
let new_id_and_hash = new_manifest_entry.map(|x| ArtifactIdAndHash {
id: x.artifact_id,
hash: x.combined_build_hash,
});
let old_id_and_hash = indirect_load.resolved_id_and_hash;
let artifact_changed = old_id_and_hash != new_id_and_hash;
if artifact_changed {
let new_load_handle_direct = if let Some(new_id_and_hash) = new_id_and_hash
{
let new_load_handle_direct =
*self.artifact_id_to_handle.get(&new_id_and_hash).unwrap();
let new_load_handle_info = self
.load_handle_infos
.get_mut(&new_load_handle_direct)
.unwrap();
new_load_handle_info.external_ref_count_direct +=
indirect_load.external_ref_count_indirect;
for _ in 0..indirect_load.external_ref_count_indirect {
Self::add_internal_ref(
&self.events_tx,
new_load_handle_direct,
new_load_handle_info,
);
}
new_load_handle_direct
} else {
LoadHandle(0)
};
indirect_load.resolved_id_and_hash = new_id_and_hash;
let old_load_handle_direct = self
.indirect_to_load
.get(&indirect_load.id)
.unwrap()
.direct_load_handle
.swap(new_load_handle_direct.0, Ordering::Relaxed);
log::info!(
"Update indirect handle {:?} => {:?} -> {:?}",
indirect_load.id,
LoadHandle(old_load_handle_direct),
new_load_handle_direct
);
if let Some(old_id_and_hash) = &old_id_and_hash {
let old_load_handle_direct =
*self.artifact_id_to_handle.get(&old_id_and_hash).unwrap();
let old_load_handle_info = self
.load_handle_infos
.get_mut(&old_load_handle_direct)
.unwrap();
old_load_handle_info.external_ref_count_direct -=
indirect_load.external_ref_count_indirect;
for _ in 0..indirect_load.external_ref_count_indirect {
Self::remove_internal_ref(
&self.events_tx,
old_load_handle_direct,
old_load_handle_info,
);
}
}
}
}
for &load_handle in ¤t_reload_action.load_handles_to_reload {
let load_handle_info = self.load_handle_infos.get_mut(&load_handle).unwrap();
log::info!("Remove temporary load handle ref for {:?}", load_handle);
Self::remove_internal_ref(&self.events_tx, load_handle, load_handle_info);
}
log::info!(
"Finished artifact reload, now on manifest build hash {:?}",
self.current_build_hash
);
self.current_reload_action = None;
}
} else if let Some(pending_build_hash) = self.loader_io.pending_build_hash() {
let old_build_hash = self.current_build_hash;
self.loader_io
.activate_pending_build_hash(pending_build_hash);
self.current_build_hash = self.loader_io.current_build_hash();
log::info!(
"Begin artifact reload {:?} -> {:?}",
old_build_hash,
self.current_build_hash
);
let mut artifacts_to_reload = HashSet::default();
for (_, indirect_load) in &self.indirect_states {
let new_manifest_entry = self.loader_io.resolve_indirect(&indirect_load.id);
let new_id_and_hash = new_manifest_entry.map(|x| ArtifactIdAndHash {
id: x.artifact_id,
hash: x.combined_build_hash,
});
let old_id_and_hash = indirect_load.resolved_id_and_hash;
let artifact_changed = old_id_and_hash != new_id_and_hash;
if artifact_changed {
log::info!("indirect load {:?} is in the new manifest but has changed, hash {:?} -> {:?}", indirect_load.id, old_id_and_hash, new_id_and_hash);
if let Some(new_manifest_entry) = &new_manifest_entry {
artifacts_to_reload.insert(ArtifactIdAndHash {
id: new_manifest_entry.artifact_id,
hash: new_manifest_entry.combined_build_hash,
});
}
}
}
let mut load_handles_to_reload = vec![];
for new_handle in artifacts_to_reload {
let new_load_handle = self.get_or_insert_direct(new_handle);
let new_load_handle_info =
self.load_handle_infos.get_mut(&new_load_handle).unwrap();
log::info!("Add temporary load handle ref for {:?}", new_load_handle);
Self::add_internal_ref(&self.events_tx, new_load_handle, new_load_handle_info);
load_handles_to_reload.push(new_load_handle);
}
self.current_reload_action = Some(ReloadAction {
load_handles_to_reload,
});
}
while let Ok(loader_event) = self.events_rx.try_recv() {
log::debug!("handle event {:?}", loader_event);
match loader_event {
LoaderEvent::TryLoad(load_handle) => {
self.handle_try_load(self.current_build_hash, load_handle)
}
LoaderEvent::TryUnload(load_handle) => {
self.handle_try_unload(load_handle, artifact_storage)
}
LoaderEvent::MetadataRequestComplete(result) => {
self.handle_request_metadata_result(self.current_build_hash, result)
}
LoaderEvent::DependenciesLoaded(load_handle) => {
self.handle_dependencies_loaded(self.current_build_hash, load_handle)
}
LoaderEvent::DataRequestComplete(result) => {
self.handle_request_data_result(result, artifact_storage)
}
LoaderEvent::LoadResult(load_result) => {
self.handle_load_result(load_result, artifact_storage)
}
}
}
}
fn handle_try_load(
&mut self,
build_hash: ManifestBuildHash,
load_handle: LoadHandle,
) {
let load_state_info = self.load_handle_infos.get_mut(&load_handle).unwrap();
log::debug!(
"handle_try_load {:?} {:?} {:?} {:0>16x}",
load_handle,
load_state_info.debug_name,
load_state_info.artifact_id,
load_state_info.hash
);
let artifact_id = load_state_info.artifact_id;
if load_state_info.load_state == LoadState::Unloaded {
if load_state_info.internal_ref_count > 0 {
self.loader_io
.request_metadata(build_hash, load_handle, artifact_id);
load_state_info.load_state = LoadState::WaitingForMetadata;
} else {
}
} else {
}
}
fn handle_try_unload(
&mut self,
load_handle: LoadHandle,
artifact_storage: &mut dyn ArtifactStorage,
) {
let load_state_info = self.load_handle_infos.get_mut(&load_handle).unwrap();
log::debug!(
"handle_try_unload {:?} {:?} {:?} {:0>16x}",
load_handle,
load_state_info.debug_name,
load_state_info.artifact_id,
load_state_info.hash
);
let mut dependencies = vec![];
if load_state_info.load_state != LoadState::Unloaded {
if load_state_info.internal_ref_count > 0 {
} else {
if load_state_info.load_state == LoadState::Loading
|| load_state_info.load_state == LoadState::Loaded
{
artifact_storage.free_artifact(load_state_info.artifact_type_id, load_handle);
}
std::mem::swap(&mut dependencies, &mut load_state_info.dependencies);
load_state_info.load_state = LoadState::Unloaded;
}
} else {
}
for depenency_load_handle in dependencies {
let depenency_load_handle_info = self
.load_handle_infos
.get_mut(&depenency_load_handle)
.unwrap();
Self::remove_internal_ref(
&self.events_tx,
depenency_load_handle,
depenency_load_handle_info,
);
}
}
fn handle_request_metadata_result(
&mut self,
build_hash: ManifestBuildHash,
result: RequestMetadataResult,
) {
if let Some(load_state_info) = self.load_handle_infos.get(&result.load_handle) {
log::debug!(
"handle_request_metadata_result {:?} {:?} {:?} {:0>16x}",
result.load_handle,
load_state_info.debug_name,
load_state_info.artifact_id,
load_state_info.hash
);
let load_state = load_state_info.load_state;
if load_state == LoadState::Unloaded {
return;
}
assert_eq!(load_state, LoadState::WaitingForMetadata);
} else {
unreachable!();
}
let metadata = result.result.unwrap();
let mut blocking_dependency_count = 0;
let mut dependency_load_handles = vec![];
for dependency in &metadata.dependencies {
let dependency_manifest_entry = self.loader_io.manifest_entry(*dependency).unwrap();
let dependency_load_handle = self.get_or_insert_direct(ArtifactIdAndHash {
id: *dependency,
hash: dependency_manifest_entry.combined_build_hash,
});
let dependency_load_handle_info = self
.load_handle_infos
.get_mut(&dependency_load_handle)
.unwrap();
dependency_load_handles.push(dependency_load_handle);
Self::add_internal_ref(
&self.events_tx,
dependency_load_handle,
dependency_load_handle_info,
);
let load_state = dependency_load_handle_info.load_state;
if load_state != LoadState::Loaded {
blocking_dependency_count += 1;
dependency_load_handle_info
.blocked_loads
.push(result.load_handle);
}
}
if let Some(load_state_info) = self.load_handle_infos.get_mut(&result.load_handle) {
let artifact_id = load_state_info.artifact_id;
load_state_info.artifact_type_id = metadata.artifact_type_id;
load_state_info.hash = metadata.hash;
load_state_info.dependencies = dependency_load_handles;
if blocking_dependency_count == 0 {
log::debug!("load handle {:?} has no dependencies", result.load_handle);
self.loader_io.request_data(
build_hash,
result.load_handle,
artifact_id,
metadata.hash,
);
assert_eq!(load_state_info.blocking_dependency_count, 0);
load_state_info.load_state = LoadState::WaitingForData;
} else {
log::debug!(
"load handle {:?} has {} dependencies",
result.load_handle,
blocking_dependency_count
);
load_state_info.blocking_dependency_count = blocking_dependency_count;
load_state_info.load_state = LoadState::WaitingForDependencies;
}
} else {
unreachable!();
}
}
fn handle_dependencies_loaded(
&mut self,
build_hash: ManifestBuildHash,
load_handle: LoadHandle,
) {
let load_state_info = self.load_handle_infos.get_mut(&load_handle).unwrap();
log::debug!(
"handle_dependencies_loaded {:?} {:?} {:?} {:0>16x}",
load_handle,
load_state_info.debug_name,
load_state_info.artifact_id,
load_state_info.hash,
);
if load_state_info.load_state == LoadState::Unloaded {
return;
}
assert_eq!(
load_state_info.load_state,
LoadState::WaitingForDependencies
);
self.loader_io.request_data(
build_hash,
load_handle,
load_state_info.artifact_id,
load_state_info.hash,
);
load_state_info.load_state = LoadState::WaitingForData;
}
fn handle_request_data_result(
&mut self,
result: RequestDataResult,
artifact_storage: &mut dyn ArtifactStorage,
) {
let (load_op, load_state_info, data) = {
let load_state_info = self.load_handle_infos.get(&result.load_handle).unwrap();
log::debug!(
"handle_request_data_result {:?} {:?} {:?} {:0>16x}",
result.load_handle,
load_state_info.debug_name,
load_state_info.artifact_id,
load_state_info.hash
);
if load_state_info.load_state == LoadState::Unloaded {
return;
}
assert_eq!(load_state_info.load_state, LoadState::WaitingForData);
let data = result.result.unwrap();
let load_op = ArtifactLoadOp::new(self.events_tx.clone(), result.load_handle);
(load_op, load_state_info, data)
};
let info_provider = LoadHandleInfoProviderImpl {
artifact_id_to_handle: &self.artifact_id_to_handle,
load_handle_infos: &self.load_handle_infos,
loader_io: &*self.loader_io,
};
artifact_storage
.load_artifact(
&info_provider,
&load_state_info.artifact_type_id,
load_state_info.artifact_id,
data.data,
result.load_handle,
load_op,
)
.unwrap();
let load_state_info = self.load_handle_infos.get_mut(&result.load_handle).unwrap();
load_state_info.load_state = LoadState::Loading;
}
fn handle_load_result(
&mut self,
load_result: HandleOp,
artifact_storage: &mut dyn ArtifactStorage,
) {
match load_result {
HandleOp::Error(load_handle, error) => {
let load_handle_info = self.load_handle_infos.get(&load_handle).unwrap();
log::debug!(
"handle_load_result error {:?} {:?} {:?} {:0>16x}",
load_handle,
load_handle_info.debug_name,
load_handle_info.artifact_id,
load_handle_info.hash
);
log::error!("load error {}", error);
panic!("load error {}", error);
}
HandleOp::Complete(load_handle) => {
let mut blocked_loads = Vec::default();
let artifact_type_id = {
let load_handle_info = self.load_handle_infos.get_mut(&load_handle).unwrap();
log::debug!(
"handle_load_result complete {:?} {:?} {:?} {:0>16x}",
load_handle,
load_handle_info.debug_name,
load_handle_info.artifact_id,
load_handle_info.hash
);
std::mem::swap(&mut blocked_loads, &mut load_handle_info.blocked_loads);
load_handle_info.load_state = LoadState::Loaded;
load_handle_info.artifact_type_id
};
for blocked_load_handle in blocked_loads {
log::trace!("blocked load {:?}", blocked_load_handle);
let blocked_load = self
.load_handle_infos
.get_mut(&blocked_load_handle)
.unwrap();
blocked_load.blocking_dependency_count -= 1;
if blocked_load.blocking_dependency_count == 0 {
self.events_tx
.send(LoaderEvent::DependenciesLoaded(blocked_load_handle))
.unwrap();
}
}
artifact_storage.commit_artifact(artifact_type_id, load_handle);
}
HandleOp::Drop(load_handle) => {
log::debug!("handle_load_result drop {:?}", load_handle);
log::error!(
"load op dropped without calling complete/error, handle {:?}",
load_handle,
);
panic!(
"load op dropped without calling complete/error, handle {:?}",
load_handle
)
}
}
}
fn get_or_insert_indirect(
&mut self,
indirect_id: &IndirectIdentifier,
) -> Arc<ResolvedLoadHandle> {
let next_handle_index = &mut self.next_handle_index;
let indirect_states = &mut self.indirect_states;
let loader_io = &mut self.loader_io;
self.indirect_to_load
.entry(indirect_id.clone())
.or_insert_with(|| {
let indirect_load_handle = LoadHandle::new(*next_handle_index, true);
*next_handle_index += 1;
let resolved = loader_io.resolve_indirect(indirect_id);
if resolved.is_none() {
panic!("Couldn't find artifact {:?}", indirect_id);
}
let manifest_entry = resolved.unwrap();
log::debug!(
"Allocate indirect load handle {:?} for indirect id {:?} -> {:?}",
indirect_load_handle,
&indirect_id,
manifest_entry.artifact_id
);
let resolved_load_handle =
ResolvedLoadHandle::new(indirect_load_handle, LoadHandle(0));
indirect_states.insert(
indirect_load_handle,
IndirectLoad {
id: indirect_id.clone(),
resolved_id_and_hash: Some(ArtifactIdAndHash {
id: manifest_entry.artifact_id,
hash: manifest_entry.combined_build_hash,
}),
external_ref_count_indirect: 0,
},
);
resolved_load_handle
})
.clone()
}
fn get_or_insert_direct(
&mut self,
artifact_id_and_hash: ArtifactIdAndHash,
) -> LoadHandle {
let next_handle_index = &mut self.next_handle_index;
let load_handle_infos = &mut self.load_handle_infos;
let loader_io = &mut self.loader_io;
*self
.artifact_id_to_handle
.entry(artifact_id_and_hash)
.or_insert_with(|| {
let direct_load_handle = LoadHandle::new(*next_handle_index, false);
*next_handle_index += 1;
let manifest_entry = loader_io.manifest_entry(artifact_id_and_hash.id).unwrap();
assert_eq!(
manifest_entry.combined_build_hash,
artifact_id_and_hash.hash
);
log::debug!(
"Allocate load handle {:?} for artifact id {:?}",
direct_load_handle,
artifact_id_and_hash,
);
load_handle_infos.insert(
direct_load_handle,
LoadHandleInfo {
artifact_id: artifact_id_and_hash.id,
external_ref_count_direct: 0,
load_state: LoadState::Unloaded,
artifact_type_id: ArtifactTypeId::default(),
hash: artifact_id_and_hash.hash,
internal_ref_count: 0,
blocking_dependency_count: 0,
blocked_loads: vec![],
dependencies: vec![],
symbol: manifest_entry.symbol_hash.clone(),
debug_name: manifest_entry.debug_name.clone(),
},
);
direct_load_handle
})
}
fn add_engine_ref_indirect(
&mut self,
id: IndirectIdentifier,
) -> Arc<ResolvedLoadHandle> {
let indirect_load_handle = self.get_or_insert_indirect(&id);
let direct_load_handle = self.add_engine_ref_by_handle_indirect(indirect_load_handle.id);
let direct_load_test = indirect_load_handle
.direct_load_handle
.swap(direct_load_handle.0, Ordering::Relaxed);
assert!(direct_load_test == 0 || direct_load_test == direct_load_handle.0);
indirect_load_handle
}
fn add_engine_ref_by_handle_indirect(
&mut self,
indirect_load_handle: LoadHandle,
) -> LoadHandle {
assert!(indirect_load_handle.is_indirect());
let state = self.indirect_states.get_mut(&indirect_load_handle).unwrap();
state.external_ref_count_indirect += 1;
let resolved_id_and_hash = state.resolved_id_and_hash;
if let Some(resolved_id_and_hash) = resolved_id_and_hash {
let direct_load_handle = self.get_or_insert_direct(resolved_id_and_hash);
self.add_engine_ref_by_handle_direct(direct_load_handle);
direct_load_handle
} else {
LoadHandle(0)
}
}
fn add_engine_ref_by_handle_direct(
&mut self,
direct_load_handle: LoadHandle,
) -> LoadHandle {
assert!(!direct_load_handle.is_indirect());
let load_handle_info = self.load_handle_infos.get_mut(&direct_load_handle).unwrap();
load_handle_info.external_ref_count_direct += 1;
Self::add_internal_ref(&self.events_tx, direct_load_handle, load_handle_info);
direct_load_handle
}
fn remove_engine_ref_indirect(
&mut self,
indirect_load_handle: LoadHandle,
) {
assert!(indirect_load_handle.is_indirect());
let state = self.indirect_states.get_mut(&indirect_load_handle).unwrap();
state.external_ref_count_indirect -= 1;
if let Some(resolved_id_and_hash) = &state.resolved_id_and_hash {
let direct_load_handle = *self
.artifact_id_to_handle
.get(resolved_id_and_hash)
.unwrap();
self.remove_engine_ref_direct(direct_load_handle);
}
}
fn remove_engine_ref_direct(
&mut self,
direct_load_handle: LoadHandle,
) {
assert!(!direct_load_handle.is_indirect());
let load_handle_info = self.load_handle_infos.get_mut(&direct_load_handle).unwrap();
load_handle_info.external_ref_count_direct -= 1;
Self::remove_internal_ref(&self.events_tx, direct_load_handle, load_handle_info);
}
fn add_internal_ref(
events_tx: &Sender<LoaderEvent>,
direct_load_handle: LoadHandle,
load_handle_info: &mut LoadHandleInfo,
) {
assert!(!direct_load_handle.is_indirect());
load_handle_info.internal_ref_count += 1;
if load_handle_info.internal_ref_count == 1 {
events_tx
.send(LoaderEvent::TryLoad(direct_load_handle))
.unwrap();
}
}
fn remove_internal_ref(
events_tx: &Sender<LoaderEvent>,
direct_load_handle: LoadHandle,
load_handle_info: &mut LoadHandleInfo,
) {
assert!(!direct_load_handle.is_indirect());
load_handle_info.internal_ref_count -= 1;
if load_handle_info.internal_ref_count == 0 {
events_tx
.send(LoaderEvent::TryUnload(direct_load_handle))
.unwrap();
}
}
pub fn get_load_info(
&self,
handle: LoadHandle,
) -> Option<LoadInfo> {
let handle = if handle.is_indirect() {
let indirect_id = self.indirect_states.get(&handle).unwrap().id.clone();
self.indirect_to_load
.get(&indirect_id)
.unwrap()
.direct_load_handle()
} else {
handle
};
let load_info = self.load_handle_infos.get(&handle)?;
Some(LoadInfo {
artifact_id: load_info.artifact_id,
refs: load_info.external_ref_count_direct,
symbol: load_info.symbol.clone(),
debug_name: load_info.debug_name.clone(),
})
}
}
#[derive(Debug)]
pub struct LoadInfo {
pub artifact_id: ArtifactId,
pub refs: u32,
pub symbol: Option<StringHash>,
pub debug_name: Option<Arc<String>>,
}
#[derive(Clone)]
pub struct Loader {
inner: Arc<Mutex<LoaderInner>>,
}
impl Loader {
pub(crate) fn new(
loader_io: Box<dyn LoaderIO>,
events_tx: Sender<LoaderEvent>,
events_rx: Receiver<LoaderEvent>,
) -> Self {
let build_hash = loader_io.current_build_hash();
let inner = LoaderInner {
next_handle_index: 1,
artifact_id_to_handle: Default::default(),
load_handle_infos: Default::default(),
loader_io,
events_tx,
events_rx,
indirect_states: Default::default(),
indirect_to_load: Default::default(),
current_build_hash: build_hash,
current_reload_action: None,
};
Loader {
inner: Arc::new(Mutex::new(inner)),
}
}
pub(crate) fn update(
&self,
artifact_storage: &mut dyn ArtifactStorage,
) {
self.inner.lock().unwrap().update(artifact_storage);
}
pub(crate) fn add_engine_ref_indirect(
&self,
id: IndirectIdentifier,
) -> Arc<ResolvedLoadHandle> {
self.inner.lock().unwrap().add_engine_ref_indirect(id)
}
pub(crate) fn add_engine_ref_by_handle(
&self,
load_handle: LoadHandle,
) -> LoadHandle {
if load_handle.is_indirect() {
self.inner
.lock()
.unwrap()
.add_engine_ref_by_handle_indirect(load_handle)
} else {
self.inner
.lock()
.unwrap()
.add_engine_ref_by_handle_direct(load_handle)
}
}
pub(crate) fn remove_engine_ref(
&self,
load_handle: LoadHandle,
) {
if load_handle.is_indirect() {
self.inner
.lock()
.unwrap()
.remove_engine_ref_indirect(load_handle);
} else {
self.inner
.lock()
.unwrap()
.remove_engine_ref_direct(load_handle);
}
}
pub fn get_active_loads(&self) -> Vec<LoadHandle> {
let mut loading_handles = Vec::default();
let inner = self.inner.lock().unwrap();
for k in inner.load_handle_infos.keys() {
loading_handles.push(k.clone());
}
loading_handles
}
pub fn get_load_info(
&self,
handle: LoadHandle,
) -> Option<LoadInfo> {
self.inner.lock().unwrap().get_load_info(handle)
}
pub fn log_load_state_recursive(
&self,
load_handle: LoadHandle,
) {
self.inner
.lock()
.unwrap()
.log_load_state_recursive(load_handle, 0);
}
}
impl LoadStateProvider for Loader {
fn load_state(
&self,
load_handle: &Arc<ResolvedLoadHandle>,
) -> LoadState {
self.inner
.lock()
.unwrap()
.load_handle_infos
.get(&load_handle.direct_load_handle())
.unwrap()
.load_state
}
fn artifact_id(
&self,
load_handle: &Arc<ResolvedLoadHandle>,
) -> ArtifactId {
self.inner
.lock()
.unwrap()
.load_handle_infos
.get(&load_handle.direct_load_handle())
.unwrap()
.artifact_id
}
}
#[derive(Copy, Clone)]
struct LoadHandleInfoProviderImpl<'a> {
artifact_id_to_handle: &'a HashMap<ArtifactIdAndHash, LoadHandle>,
load_handle_infos: &'a HashMap<LoadHandle, LoadHandleInfo>,
loader_io: &'a dyn LoaderIO,
}
impl<'a> LoaderInfoProvider for LoadHandleInfoProviderImpl<'a> {
fn resolved_load_handle(
&self,
id: &ArtifactRef,
) -> Option<Arc<ResolvedLoadHandle>> {
let artifact_id = ArtifactId::from_uuid(id.0.as_uuid());
let build_hash = self
.loader_io
.manifest_entry(artifact_id)
.unwrap()
.combined_build_hash;
let load_handle = self
.artifact_id_to_handle
.get(&ArtifactIdAndHash {
id: artifact_id,
hash: build_hash,
})
.map(|l| *l)?;
Some(ResolvedLoadHandle::new(load_handle, load_handle))
}
fn artifact_id(
&self,
load: LoadHandle,
) -> Option<ArtifactId> {
self.load_handle_infos.get(&load).map(|l| l.artifact_id)
}
}