hydrate_loader/
loader.rs

1use crate::storage::{ArtifactLoadOp, ArtifactStorage, HandleOp, IndirectIdentifier};
2use crate::ArtifactTypeId;
3use crossbeam_channel::{Receiver, Sender};
4use hydrate_base::handle::{
5    ArtifactRef, LoadState, LoadStateProvider, LoaderInfoProvider, ResolvedLoadHandle,
6};
7use hydrate_base::hashing::{HashMap, HashSet};
8use hydrate_base::ArtifactId;
9use hydrate_base::{ArtifactManifestData, LoadHandle, StringHash};
10use std::fmt::Formatter;
11use std::hash::Hash;
12use std::sync::atomic::Ordering;
13use std::sync::{Arc, Mutex};
14
15//
16// Interface for IO
17//
18// Data about artifacts is stored in three places:
19// - Manifest: full list of all artifacts, always in-memory
20// - Metadata: stored usually at the head of the artifact data itself. Requires an IO operation to read
21//   but is lightweight in terms of bytes
22// - Data: The actual payload of the artifact
23//
24// The primary use of metadata is to get a list of other artifacts that must be loaded in order to
25// a particular artifact. We will fire off requests for those other artifacts and wait for them to
26// complete before requesting payload data
27//
28
29// Data about an artifact that is not in the manifest
30#[derive(Debug)]
31pub struct ArtifactMetadata {
32    pub dependencies: Vec<ArtifactId>,
33    pub artifact_type_id: ArtifactTypeId,
34    pub hash: u64,
35    // size?
36}
37
38// The actual payload data of an artifact
39pub struct ArtifactData {
40    pub data: Vec<u8>,
41}
42
43impl std::fmt::Debug for ArtifactData {
44    fn fmt(
45        &self,
46        f: &mut std::fmt::Formatter<'_>,
47    ) -> std::fmt::Result {
48        f.debug_struct("ArtifactData")
49            .field("data_length", &self.data.len())
50            .finish()
51    }
52}
53
54// When IO completes a request for artifact metadata, it will send us a loader event containing this
55#[derive(Debug)]
56pub struct RequestMetadataResult {
57    pub artifact_id: ArtifactId,
58    pub load_handle: LoadHandle,
59    //pub hash: u64,
60    pub result: std::io::Result<ArtifactMetadata>,
61}
62
63// When IO completes a request for artifact payload data, it will send us a loader event containing this
64#[derive(Debug)]
65pub struct RequestDataResult {
66    pub artifact_id: ArtifactId,
67    pub load_handle: LoadHandle,
68    //pub hash: u64,
69    //pub subresource: Option<u32>,
70    pub result: std::io::Result<ArtifactData>,
71}
72
73// A hash of a particular data build. This encompasses everything that was in a single manifest.
74// If it changes, we need to check for artifacts that have changed, load them, and update indirect
75// handles to point at them. The LoaderIO will provide a new build hash to indicate this has occurred.
76#[derive(Copy, Clone, PartialEq, Eq)]
77pub struct ManifestBuildHash(pub u64);
78
79impl std::fmt::Debug for ManifestBuildHash {
80    fn fmt(
81        &self,
82        f: &mut Formatter<'_>,
83    ) -> std::fmt::Result {
84        write!(f, "ManifestBuildHash({:0>16x})", self.0)
85    }
86}
87
88// Represents a data source from which we can load content
89pub trait LoaderIO: Sync + Send {
90    fn update(&mut self);
91
92    // Returns the latest known build hash that we are currently able to read from
93    fn current_build_hash(&self) -> ManifestBuildHash;
94
95    // Build hash that we are prepared to switch to
96    fn pending_build_hash(&self) -> Option<ManifestBuildHash>;
97
98    // Switches to using the new manifest for future requests
99    fn activate_pending_build_hash(
100        &mut self,
101        new_build_hash: ManifestBuildHash,
102    );
103
104    // Provide manifest data for a particular artifact by ID
105    fn manifest_entry(
106        &self,
107        artifact_id: ArtifactId,
108    ) -> Option<&ArtifactManifestData>;
109
110    // Provide manifest data for an artifact, determined by indirect identifier (for example a
111    // symbol name)
112    fn resolve_indirect(
113        &self,
114        indirect_identifier: &IndirectIdentifier,
115    ) -> Option<&ArtifactManifestData>;
116
117    // Load the metadata for an artifact.
118    // This results in a RequestMetadataResult being sent to the loader
119    fn request_metadata(
120        &self,
121        build_hash: ManifestBuildHash,
122        load_handle: LoadHandle,
123        artifact_id: ArtifactId,
124    );
125
126    // Load the payload for an artifact.
127    // This results in a RequestDataResult being sent to the loader
128    fn request_data(
129        &self,
130        build_hash: ManifestBuildHash,
131        load_handle: LoadHandle,
132        artifact_id: ArtifactId,
133        hash: u64,
134    );
135}
136
137//
138// Loader events which drive state changes for loaded artifacts
139//
140#[derive(Debug)]
141pub enum LoaderEvent {
142    // Sent when artifact ref count goes from 0 to 1
143    TryLoad(LoadHandle),
144    // Sent when artifact ref count goes from 1 to 0
145    TryUnload(LoadHandle),
146    // Sent by LoaderIO when metadata request succeeds or fails
147    MetadataRequestComplete(RequestMetadataResult),
148    // Sent when all dependencies that were blocking a load have completed loading
149    DependenciesLoaded(LoadHandle),
150    // Sent by LoaderIO when data request succeeds or fails
151    DataRequestComplete(RequestDataResult),
152    // Sent by engine code to indicate success or failure at loading an artifact
153    LoadResult(HandleOp),
154    // Sent by LoaderIO when there are new versions available of the given artifacts.
155    //ArtifactsUpdated(ManifestBuildHash),
156}
157
158// Information about indirect load handles that have been requested
159#[derive(Debug)]
160struct IndirectLoad {
161    // Identifies what this indirect load refers to. This could be a symbol, and artifact, etc.
162    id: IndirectIdentifier,
163    // The artifact that the identifier currently maps to. This could change if we reload data.
164    //TODO: Update this on reload
165    resolved_id_and_hash: Option<ArtifactIdAndHash>,
166    // The reference count of external handles (i.e. explicitly requested references, not references
167    // due to other artifacts depending on this artifact) matching this indirect identifier
168    external_ref_count_indirect: u32,
169}
170
171// Information about direct load handles that are currently loaded or were loaded at some point in
172// the past. A load handle points to a particular version of an artifact, uniquely identified by
173// an artifact ID and the hash.
174struct LoadHandleInfo {
175    artifact_id: ArtifactId,
176    artifact_type_id: ArtifactTypeId,
177
178    // Used to uniquely identify a version of this artifact.
179    hash: u64,
180    // State this particular artifact is in
181    load_state: LoadState,
182
183    // This will be set to true if we reload and this artifact is no longer the latest version of
184    // the artifact. Already loaded objects may stay loaded, but we would cancel any further attempts
185    // to load this object. (Additionally the currently available manifest data won't be compatible
186    // with this artifact, so we would not be able to continue loading it)
187    //replaced_by_newer_version: bool,
188
189    // The reference count of external handles (i.e. explicitly requested references, not references
190    // due to other artifacts depending on this artifact) for this artifact. Indirect handles will
191    // count here and it may be that this artifact is referenced by multiple unique indirect handles.
192    external_ref_count_direct: u32,
193
194    // Number of references to this artifact, including explicitly via indirect handles or implicitly
195    // due to other requested artifacts requiring this artifact to be loaded first. This reference
196    // count is the primary determinent of when it's safe to free an artifact from in-memory storage
197    internal_ref_count: u32,
198
199    // Number of artifacts that need to finish loading before this artifact can request data and load
200    blocking_dependency_count: u32,
201
202    // load handles for any artifacts that are waiting on this artifact to load in order to continue
203    blocked_loads: Vec<LoadHandle>,
204
205    // load handles for any artifacts that need to be released when this is unloaded. This artifact
206    // implicitly requires these artifacts to load fully before this artifact can finish loading.
207    dependencies: Vec<LoadHandle>,
208
209    // for debugging/convenience, not actually required
210    symbol: Option<StringHash>,
211    // for debugging/convenience, not actually required
212    debug_name: Option<Arc<String>>,
213}
214
215//TODO: This may need to track the changed artifacts to wait for them to load before updating
216// indirect handles and removing ref counts from the direct handles they used to be associated with?
217struct ReloadAction {
218    // old direct handles, there is no new corresponding handle
219    //load_handles_to_unload: Vec<LoadHandle>,
220    // new direct handles, we don't need the old handle here because ref count changes will
221    // eventually cause the old handle to be dropped
222    load_handles_to_reload: Vec<LoadHandle>,
223}
224
225#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
226struct ArtifactIdAndHash {
227    id: ArtifactId,
228    hash: u64,
229}
230
231struct LoaderInner {
232    next_handle_index: u64,
233
234    // All direct load handles that are currently loaded or were loaded in the past
235    // This should only contain direct handles
236    load_handle_infos: HashMap<LoadHandle, LoadHandleInfo>,
237    // The direct handle for a given artifact ID
238    // This should only contain direct handles
239    //TODO: This will get updated on reload
240    artifact_id_to_handle: HashMap<ArtifactIdAndHash, LoadHandle>,
241
242    // The data source we will load content from
243    loader_io: Box<dyn LoaderIO>,
244
245    // The event queue that drives artifact load states changing. Events are produced by API calls
246    // from the game, LoaderIO results, significant changes in reference counts, etc.
247    events_tx: Sender<LoaderEvent>,
248    events_rx: Receiver<LoaderEvent>,
249
250    // All indirect load handles that exist or previously existed
251    indirect_states: HashMap<LoadHandle, IndirectLoad>,
252    // All indirect identifiers that exist or previously existed, along with the indirect and direct
253    // load handles associated with them
254    //TODO: The direct handles will be updated on a reload
255    indirect_to_load: HashMap<IndirectIdentifier, Arc<ResolvedLoadHandle>>,
256
257    // Update-specific state, mainly to do with reload detection/handling
258    current_build_hash: ManifestBuildHash,
259    current_reload_action: Option<ReloadAction>,
260}
261
262impl LoaderInner {
263    pub fn log_load_state_recursive(
264        &self,
265        load_handle: LoadHandle,
266        indent: usize,
267    ) {
268        let load_handle_info = self.load_handle_infos.get(&load_handle).unwrap();
269        log::debug!(
270            "{:indent$}{:?} {:?} {:?}",
271            "",
272            load_handle_info.artifact_id,
273            load_handle_info.debug_name,
274            load_handle_info.load_state,
275            indent = indent
276        );
277        for dependency in &load_handle_info.dependencies {
278            self.log_load_state_recursive(*dependency, indent + 2);
279        }
280    }
281
282    // Process all events, possibly changing load status of artifacts
283    // Also commit reload of artifact data if needed
284    #[profiling::function]
285    fn update(
286        &mut self,
287        artifact_storage: &mut dyn ArtifactStorage,
288    ) {
289        self.loader_io.update();
290
291        if let Some(current_reload_action) = &self.current_reload_action {
292            //
293            // Check that the load handles we are waiting on to complete a reload are in a committed
294            // state. These are all the resolved/direct load handles that were pointed at by at least
295            // on indirect handle when we started the reload.
296            //
297            let mut reload_complete = true;
298            for &load_handle in &current_reload_action.load_handles_to_reload {
299                let load_handle_info = self.load_handle_infos.get(&load_handle).unwrap();
300                if load_handle_info.load_state != LoadState::Loaded {
301                    //log::debug!("Reloading waiting for {:?} {:?} {:?} to load, it's in state {:?}", load_handle, load_handle_info.artifact_id, load_handle_info.debug_name, load_handle_info.load_state);
302                    //self.log_load_state_recursive(load_handle, 2);
303                    reload_complete = false;
304                    break;
305                }
306            }
307
308            if reload_complete {
309                //
310                // New versions of artifacts for indirect handles have loaded, switch over to using them now
311                //
312                log::info!("All artifacts we need to reload are ready, updating indirect handles to point at new data");
313
314                // Update any indirect handles to point at new data
315                for (_, indirect_load) in &mut self.indirect_states {
316                    // Resolve the indirect handle under the new manifest
317                    let new_manifest_entry = self.loader_io.resolve_indirect(&indirect_load.id);
318                    let new_id_and_hash = new_manifest_entry.map(|x| ArtifactIdAndHash {
319                        id: x.artifact_id,
320                        hash: x.combined_build_hash,
321                    });
322                    let old_id_and_hash = indirect_load.resolved_id_and_hash;
323
324                    // If the resolved UUID changes, we need to point the indirect load at the new
325                    // version of the artifact (or None) and update ref counts accordingly
326                    let artifact_changed = old_id_and_hash != new_id_and_hash;
327                    if artifact_changed {
328                        // Get the new direct load handle (and add a ref count to it if it is valid)
329                        let new_load_handle_direct = if let Some(new_id_and_hash) = new_id_and_hash
330                        {
331                            let new_load_handle_direct =
332                                *self.artifact_id_to_handle.get(&new_id_and_hash).unwrap();
333                            let new_load_handle_info = self
334                                .load_handle_infos
335                                .get_mut(&new_load_handle_direct)
336                                .unwrap();
337
338                            // Add indirect references to the new load handle. Each indirect ref
339                            // count represents an external ref count and an internal ref count on the
340                            // direct load handle
341                            //TODO: Cleaner way to do this using self.add_engine_ref_by_handle_direct?
342                            new_load_handle_info.external_ref_count_direct +=
343                                indirect_load.external_ref_count_indirect;
344                            for _ in 0..indirect_load.external_ref_count_indirect {
345                                Self::add_internal_ref(
346                                    &self.events_tx,
347                                    new_load_handle_direct,
348                                    new_load_handle_info,
349                                );
350                            }
351                            new_load_handle_direct
352                        } else {
353                            // The artifact doesn't exist in the new manifest
354                            LoadHandle(0)
355                        };
356
357                        // Point the indirect load to the new version
358                        indirect_load.resolved_id_and_hash = new_id_and_hash;
359                        let old_load_handle_direct = self
360                            .indirect_to_load
361                            .get(&indirect_load.id)
362                            .unwrap()
363                            .direct_load_handle
364                            .swap(new_load_handle_direct.0, Ordering::Relaxed);
365                        log::info!(
366                            "Update indirect handle {:?} => {:?} -> {:?}",
367                            indirect_load.id,
368                            LoadHandle(old_load_handle_direct),
369                            new_load_handle_direct
370                        );
371
372                        // Drop ref count to old version, if it existed
373                        if let Some(old_id_and_hash) = &old_id_and_hash {
374                            let old_load_handle_direct =
375                                *self.artifact_id_to_handle.get(&old_id_and_hash).unwrap();
376                            let old_load_handle_info = self
377                                .load_handle_infos
378                                .get_mut(&old_load_handle_direct)
379                                .unwrap();
380
381                            // Remove the indirect references to the old load handle. Each indirect ref
382                            // count represents an external ref count and an internal ref count on the
383                            // direct load handle
384                            //TODO: Cleaner way to do this using self.remove_engine_ref_direct?
385                            old_load_handle_info.external_ref_count_direct -=
386                                indirect_load.external_ref_count_indirect;
387                            for _ in 0..indirect_load.external_ref_count_indirect {
388                                Self::remove_internal_ref(
389                                    &self.events_tx,
390                                    old_load_handle_direct,
391                                    old_load_handle_info,
392                                );
393                            }
394                        }
395                    }
396                }
397
398                //remove temporary ref count added when we started the reload
399                for &load_handle in &current_reload_action.load_handles_to_reload {
400                    let load_handle_info = self.load_handle_infos.get_mut(&load_handle).unwrap();
401                    log::info!("Remove temporary load handle ref for {:?}", load_handle);
402                    Self::remove_internal_ref(&self.events_tx, load_handle, load_handle_info);
403                }
404
405                // indicate that the reload is complete
406                log::info!(
407                    "Finished artifact reload, now on manifest build hash {:?}",
408                    self.current_build_hash
409                );
410                self.current_reload_action = None;
411            }
412        } else if let Some(pending_build_hash) = self.loader_io.pending_build_hash() {
413            //
414            // See if there is a new build hash available. If there is, we may need to reload/unload
415            // some artifacts.
416            //
417
418            // Switch to the new manifest
419            let old_build_hash = self.current_build_hash;
420            self.loader_io
421                .activate_pending_build_hash(pending_build_hash);
422            self.current_build_hash = self.loader_io.current_build_hash();
423
424            log::info!(
425                "Begin artifact reload {:?} -> {:?}",
426                old_build_hash,
427                self.current_build_hash
428            );
429
430            // Iterate indirect handles and see if what they are pointing at has changed
431            let mut artifacts_to_reload = HashSet::default();
432            for (_, indirect_load) in &self.indirect_states {
433                // Resolve the indirect handle under the new manifest
434                let new_manifest_entry = self.loader_io.resolve_indirect(&indirect_load.id);
435                let new_id_and_hash = new_manifest_entry.map(|x| ArtifactIdAndHash {
436                    id: x.artifact_id,
437                    hash: x.combined_build_hash,
438                });
439                let old_id_and_hash = indirect_load.resolved_id_and_hash;
440
441                // If it has changed (and exists), add it to the list of artifacts that need to load
442                // before we update
443                let artifact_changed = old_id_and_hash != new_id_and_hash;
444                if artifact_changed {
445                    log::info!("indirect load {:?} is in the new manifest but has changed, hash {:?} -> {:?}", indirect_load.id, old_id_and_hash, new_id_and_hash);
446                    // Either add the artifact to the reload or unload list
447                    if let Some(new_manifest_entry) = &new_manifest_entry {
448                        artifacts_to_reload.insert(ArtifactIdAndHash {
449                            id: new_manifest_entry.artifact_id,
450                            hash: new_manifest_entry.combined_build_hash,
451                        });
452                    }
453                }
454            }
455
456            // Add temporary ref counts to new version of anything that has changed (causing it to load)
457            let mut load_handles_to_reload = vec![];
458            for new_handle in artifacts_to_reload {
459                let new_load_handle = self.get_or_insert_direct(new_handle);
460                let new_load_handle_info =
461                    self.load_handle_infos.get_mut(&new_load_handle).unwrap();
462
463                // This reference is temporary and will be removed when we finish the reload
464                log::info!("Add temporary load handle ref for {:?}", new_load_handle);
465                Self::add_internal_ref(&self.events_tx, new_load_handle, new_load_handle_info);
466                load_handles_to_reload.push(new_load_handle);
467            }
468
469            self.current_reload_action = Some(ReloadAction {
470                load_handles_to_reload,
471            });
472        }
473
474        while let Ok(loader_event) = self.events_rx.try_recv() {
475            log::debug!("handle event {:?}", loader_event);
476            match loader_event {
477                LoaderEvent::TryLoad(load_handle) => {
478                    self.handle_try_load(self.current_build_hash, load_handle)
479                }
480                LoaderEvent::TryUnload(load_handle) => {
481                    self.handle_try_unload(load_handle, artifact_storage)
482                }
483                LoaderEvent::MetadataRequestComplete(result) => {
484                    self.handle_request_metadata_result(self.current_build_hash, result)
485                }
486                LoaderEvent::DependenciesLoaded(load_handle) => {
487                    self.handle_dependencies_loaded(self.current_build_hash, load_handle)
488                }
489                LoaderEvent::DataRequestComplete(result) => {
490                    self.handle_request_data_result(result, artifact_storage)
491                }
492                LoaderEvent::LoadResult(load_result) => {
493                    self.handle_load_result(load_result, artifact_storage)
494                }
495            }
496        }
497    }
498
499    fn handle_try_load(
500        &mut self,
501        build_hash: ManifestBuildHash,
502        load_handle: LoadHandle,
503    ) {
504        // Should always exist, we don't delete load handles
505        let load_state_info = self.load_handle_infos.get_mut(&load_handle).unwrap();
506
507        log::debug!(
508            "handle_try_load {:?} {:?} {:?} {:0>16x}",
509            load_handle,
510            load_state_info.debug_name,
511            load_state_info.artifact_id,
512            load_state_info.hash
513        );
514
515        // We expect any try_load requests to be for the latest version. If this ends up not being a
516        // valid assertion, perhaps we should just load the most recent version.
517        let artifact_id = load_state_info.artifact_id;
518        if load_state_info.load_state == LoadState::Unloaded {
519            // We have not started to load this artifact, so we can potentially start it now
520            if load_state_info.internal_ref_count > 0 {
521                // The engine is still referencing it, so we should start loading it now
522                self.loader_io
523                    .request_metadata(build_hash, load_handle, artifact_id);
524                load_state_info.load_state = LoadState::WaitingForMetadata;
525            } else {
526                // it's not referenced anymore, don't bother loading it. If it becomes
527                // referenced again later, we will get another TryLoad event
528            }
529        } else {
530            // If we are in any other state, then we are loading or already loaded.
531            // We don't need to do anything in this case.
532        }
533    }
534
535    fn handle_try_unload(
536        &mut self,
537        load_handle: LoadHandle,
538        artifact_storage: &mut dyn ArtifactStorage,
539    ) {
540        // Should always exist, we don't delete load handles
541        let load_state_info = self.load_handle_infos.get_mut(&load_handle).unwrap();
542
543        log::debug!(
544            "handle_try_unload {:?} {:?} {:?} {:0>16x}",
545            load_handle,
546            load_state_info.debug_name,
547            load_state_info.artifact_id,
548            load_state_info.hash
549        );
550
551        let mut dependencies = vec![];
552
553        if load_state_info.load_state != LoadState::Unloaded {
554            // We are somewhere in the state machine to load the artifact, we can stop loading it now
555            // if it's no longer referenced
556            if load_state_info.internal_ref_count > 0 {
557                // It's referenced, don't unload it
558            } else {
559                // It's not referenced, so go ahead and unloaded it...
560
561                // If it's been loaded, tell artifact storage to drop it
562                if load_state_info.load_state == LoadState::Loading
563                    || load_state_info.load_state == LoadState::Loaded
564                {
565                    artifact_storage.free_artifact(load_state_info.artifact_type_id, load_handle);
566                }
567
568                std::mem::swap(&mut dependencies, &mut load_state_info.dependencies);
569
570                load_state_info.load_state = LoadState::Unloaded;
571            }
572        } else {
573            // We are already unloaded and don't need to do anything
574        }
575
576        // Remove dependency refs, we do this after we finish mutating the load info so that we don't
577        // take multiple locks, which risks deadlock
578        for depenency_load_handle in dependencies {
579            let depenency_load_handle_info = self
580                .load_handle_infos
581                .get_mut(&depenency_load_handle)
582                .unwrap();
583            Self::remove_internal_ref(
584                &self.events_tx,
585                depenency_load_handle,
586                depenency_load_handle_info,
587            );
588        }
589    }
590
591    fn handle_request_metadata_result(
592        &mut self,
593        build_hash: ManifestBuildHash,
594        result: RequestMetadataResult,
595    ) {
596        if let Some(load_state_info) = self.load_handle_infos.get(&result.load_handle) {
597            log::debug!(
598                "handle_request_metadata_result {:?} {:?} {:?} {:0>16x}",
599                result.load_handle,
600                load_state_info.debug_name,
601                load_state_info.artifact_id,
602                load_state_info.hash
603            );
604            let load_state = load_state_info.load_state;
605            // Bail if the artifact is unloaded
606            if load_state == LoadState::Unloaded {
607                return;
608            }
609
610            assert_eq!(load_state, LoadState::WaitingForMetadata);
611        } else {
612            // We don't recognize the load_handle.. we currently never delete them so this shouldn't happen
613            unreachable!();
614        }
615
616        // add references for other artifacts, either wait for dependents metadata or start loading
617        let metadata = result.result.unwrap();
618
619        let mut blocking_dependency_count = 0;
620
621        let mut dependency_load_handles = vec![];
622        for dependency in &metadata.dependencies {
623            let dependency_manifest_entry = self.loader_io.manifest_entry(*dependency).unwrap();
624
625            let dependency_load_handle = self.get_or_insert_direct(ArtifactIdAndHash {
626                id: *dependency,
627                hash: dependency_manifest_entry.combined_build_hash,
628            });
629            let dependency_load_handle_info = self
630                .load_handle_infos
631                .get_mut(&dependency_load_handle)
632                .unwrap();
633
634            dependency_load_handles.push(dependency_load_handle);
635
636            Self::add_internal_ref(
637                &self.events_tx,
638                dependency_load_handle,
639                dependency_load_handle_info,
640            );
641
642            let load_state = dependency_load_handle_info.load_state;
643            if load_state != LoadState::Loaded {
644                blocking_dependency_count += 1;
645
646                dependency_load_handle_info
647                    .blocked_loads
648                    .push(result.load_handle);
649            }
650        }
651
652        if let Some(load_state_info) = self.load_handle_infos.get_mut(&result.load_handle) {
653            let artifact_id = load_state_info.artifact_id;
654            load_state_info.artifact_type_id = metadata.artifact_type_id;
655            load_state_info.hash = metadata.hash;
656            load_state_info.dependencies = dependency_load_handles;
657
658            if blocking_dependency_count == 0 {
659                log::debug!("load handle {:?} has no dependencies", result.load_handle);
660                self.loader_io.request_data(
661                    build_hash,
662                    result.load_handle,
663                    artifact_id,
664                    metadata.hash,
665                    //None,
666                );
667                assert_eq!(load_state_info.blocking_dependency_count, 0);
668                load_state_info.load_state = LoadState::WaitingForData;
669            } else {
670                log::debug!(
671                    "load handle {:?} has {} dependencies",
672                    result.load_handle,
673                    blocking_dependency_count
674                );
675                load_state_info.blocking_dependency_count = blocking_dependency_count;
676                load_state_info.load_state = LoadState::WaitingForDependencies;
677                // Processing for this artifact will continue with dependencies load and our
678                // blocking_dependency_count hits 0. (It will be decremented as dependencies are
679                // loaded in)
680            }
681        } else {
682            // We don't recognize the load_handle.. we currently never delete them so this shouldn't happen
683            unreachable!();
684        }
685    }
686
687    fn handle_dependencies_loaded(
688        &mut self,
689        build_hash: ManifestBuildHash,
690        load_handle: LoadHandle,
691    ) {
692        //are we still in the correct state?
693        let load_state_info = self.load_handle_infos.get_mut(&load_handle).unwrap();
694        log::debug!(
695            "handle_dependencies_loaded {:?} {:?} {:?} {:0>16x}",
696            load_handle,
697            load_state_info.debug_name,
698            load_state_info.artifact_id,
699            load_state_info.hash,
700        );
701        if load_state_info.load_state == LoadState::Unloaded {
702            return;
703        }
704
705        assert_eq!(
706            load_state_info.load_state,
707            LoadState::WaitingForDependencies
708        );
709
710        self.loader_io.request_data(
711            build_hash,
712            load_handle,
713            load_state_info.artifact_id,
714            load_state_info.hash,
715            //None,
716        );
717        load_state_info.load_state = LoadState::WaitingForData;
718    }
719
720    fn handle_request_data_result(
721        &mut self,
722        result: RequestDataResult,
723        artifact_storage: &mut dyn ArtifactStorage,
724    ) {
725        // if self.artifact_id_to_handle.get(&result.artifact_id).unwrap() != result.artifact_id {
726        //     assert!(version.load_state == LoadState::
727        //     // This
728        //     // let load_state_info = self.load_handle_infos.get_mut(&result.load_handle).unwrap();
729        //     // let version = &mut load_state_info.version;
730        //     // version.load_state = LoadState::Unloaded
731        // }
732
733        // Should always exist, we don't delete load handles
734        let (load_op, load_state_info, data) = {
735            let load_state_info = self.load_handle_infos.get(&result.load_handle).unwrap();
736            log::debug!(
737                "handle_request_data_result {:?} {:?} {:?} {:0>16x}",
738                result.load_handle,
739                load_state_info.debug_name,
740                load_state_info.artifact_id,
741                load_state_info.hash
742            );
743            // Bail if the artifact is unloaded
744            if load_state_info.load_state == LoadState::Unloaded {
745                return;
746            }
747
748            assert_eq!(load_state_info.load_state, LoadState::WaitingForData);
749
750            // start loading
751            let data = result.result.unwrap();
752
753            let load_op = ArtifactLoadOp::new(self.events_tx.clone(), result.load_handle);
754
755            (load_op, load_state_info, data)
756        };
757
758        let info_provider = LoadHandleInfoProviderImpl {
759            artifact_id_to_handle: &self.artifact_id_to_handle,
760            load_handle_infos: &self.load_handle_infos,
761            loader_io: &*self.loader_io,
762        };
763
764        // We dropped the load_state_info lock before calling this because the serde deserializer may query for artifact
765        // references, which can cause deadlocks if we are still holding a lock
766        artifact_storage
767            .load_artifact(
768                &info_provider,
769                &load_state_info.artifact_type_id,
770                load_state_info.artifact_id,
771                data.data,
772                result.load_handle,
773                load_op,
774            )
775            .unwrap();
776
777        // Should always exist, we don't delete load handles
778        let load_state_info = self.load_handle_infos.get_mut(&result.load_handle).unwrap();
779        load_state_info.load_state = LoadState::Loading;
780    }
781
782    fn handle_load_result(
783        &mut self,
784        load_result: HandleOp,
785        artifact_storage: &mut dyn ArtifactStorage,
786    ) {
787        //while let Ok(handle_op) = self.handle_op_rx.try_recv() {
788        // Handle the operation
789        match load_result {
790            HandleOp::Error(load_handle, error) => {
791                let load_handle_info = self.load_handle_infos.get(&load_handle).unwrap();
792                log::debug!(
793                    "handle_load_result error {:?} {:?} {:?} {:0>16x}",
794                    load_handle,
795                    load_handle_info.debug_name,
796                    load_handle_info.artifact_id,
797                    load_handle_info.hash
798                );
799                //TODO: How to handle errors?
800                log::error!("load error {}", error);
801                panic!("load error {}", error);
802            }
803            HandleOp::Complete(load_handle) => {
804                // Advance state... maybe we can commit now, otherwise we have to wait until other
805                // dependencies are ready
806
807                // Flag any loads that were waiting on this load to proceed
808                let mut blocked_loads = Vec::default();
809                let artifact_type_id = {
810                    let load_handle_info = self.load_handle_infos.get_mut(&load_handle).unwrap();
811                    log::debug!(
812                        "handle_load_result complete {:?} {:?} {:?} {:0>16x}",
813                        load_handle,
814                        load_handle_info.debug_name,
815                        load_handle_info.artifact_id,
816                        load_handle_info.hash
817                    );
818                    std::mem::swap(&mut blocked_loads, &mut load_handle_info.blocked_loads);
819                    load_handle_info.load_state = LoadState::Loaded;
820                    load_handle_info.artifact_type_id
821                };
822
823                for blocked_load_handle in blocked_loads {
824                    log::trace!("blocked load {:?}", blocked_load_handle);
825                    let blocked_load = self
826                        .load_handle_infos
827                        .get_mut(&blocked_load_handle)
828                        .unwrap();
829                    blocked_load.blocking_dependency_count -= 1;
830                    if blocked_load.blocking_dependency_count == 0 {
831                        // Kick off the blocked load
832                        self.events_tx
833                            .send(LoaderEvent::DependenciesLoaded(blocked_load_handle))
834                            .unwrap();
835                    }
836                }
837
838                artifact_storage.commit_artifact(artifact_type_id, load_handle);
839            }
840            HandleOp::Drop(load_handle) => {
841                log::debug!("handle_load_result drop {:?}", load_handle);
842                log::error!(
843                    "load op dropped without calling complete/error, handle {:?}",
844                    load_handle,
845                );
846                panic!(
847                    "load op dropped without calling complete/error, handle {:?}",
848                    load_handle
849                )
850            }
851        }
852    }
853
854    // This returns a ResolvedLoadHandle which is either already pointing at a direct load or will need
855    // to be populated with a direct load
856    fn get_or_insert_indirect(
857        &mut self,
858        indirect_id: &IndirectIdentifier,
859    ) -> Arc<ResolvedLoadHandle> {
860        let next_handle_index = &mut self.next_handle_index;
861        let indirect_states = &mut self.indirect_states;
862        let loader_io = &mut self.loader_io;
863        self.indirect_to_load
864            .entry(indirect_id.clone())
865            .or_insert_with(|| {
866                let indirect_load_handle = LoadHandle::new(*next_handle_index, true);
867                *next_handle_index += 1;
868
869                let resolved = loader_io.resolve_indirect(indirect_id);
870                if resolved.is_none() {
871                    panic!("Couldn't find artifact {:?}", indirect_id);
872                }
873
874                let manifest_entry = resolved.unwrap();
875                log::debug!(
876                    "Allocate indirect load handle {:?} for indirect id {:?} -> {:?}",
877                    indirect_load_handle,
878                    &indirect_id,
879                    manifest_entry.artifact_id
880                );
881
882                let resolved_load_handle =
883                    ResolvedLoadHandle::new(indirect_load_handle, LoadHandle(0));
884
885                indirect_states.insert(
886                    indirect_load_handle,
887                    IndirectLoad {
888                        id: indirect_id.clone(),
889                        resolved_id_and_hash: Some(ArtifactIdAndHash {
890                            id: manifest_entry.artifact_id,
891                            hash: manifest_entry.combined_build_hash,
892                        }),
893                        external_ref_count_indirect: 0,
894                    },
895                );
896                resolved_load_handle
897            })
898            .clone()
899    }
900
901    fn get_or_insert_direct(
902        &mut self,
903        artifact_id_and_hash: ArtifactIdAndHash,
904    ) -> LoadHandle {
905        let next_handle_index = &mut self.next_handle_index;
906        let load_handle_infos = &mut self.load_handle_infos;
907        let loader_io = &mut self.loader_io;
908        *self
909            .artifact_id_to_handle
910            .entry(artifact_id_and_hash)
911            .or_insert_with(|| {
912                let direct_load_handle = LoadHandle::new(*next_handle_index, false);
913                *next_handle_index += 1;
914                let manifest_entry = loader_io.manifest_entry(artifact_id_and_hash.id).unwrap();
915                assert_eq!(
916                    manifest_entry.combined_build_hash,
917                    artifact_id_and_hash.hash
918                );
919
920                log::debug!(
921                    "Allocate load handle {:?} for artifact id {:?}",
922                    direct_load_handle,
923                    artifact_id_and_hash,
924                );
925
926                load_handle_infos.insert(
927                    direct_load_handle,
928                    LoadHandleInfo {
929                        artifact_id: artifact_id_and_hash.id,
930                        external_ref_count_direct: 0,
931                        load_state: LoadState::Unloaded,
932                        artifact_type_id: ArtifactTypeId::default(),
933                        hash: artifact_id_and_hash.hash,
934                        //replaced_by_newer_version: false,
935                        internal_ref_count: 0,
936                        blocking_dependency_count: 0,
937                        blocked_loads: vec![],
938                        dependencies: vec![],
939                        symbol: manifest_entry.symbol_hash.clone(),
940                        debug_name: manifest_entry.debug_name.clone(),
941                    },
942                );
943
944                direct_load_handle
945            })
946    }
947
948    fn add_engine_ref_indirect(
949        &mut self,
950        id: IndirectIdentifier,
951    ) -> Arc<ResolvedLoadHandle> {
952        let indirect_load_handle = self.get_or_insert_indirect(&id);
953
954        // It's possible this has already been resolved, but we still need to add a ref count.
955        let direct_load_handle = self.add_engine_ref_by_handle_indirect(indirect_load_handle.id);
956
957        // We expect that the direct handle in the ResolvedLoadHandle is either unset (0) or
958        // is consistent with the direct handle returned by add_engine_ref_by_handle_indirect().
959        // If it's unset, we need to set it.
960        let direct_load_test = indirect_load_handle
961            .direct_load_handle
962            .swap(direct_load_handle.0, Ordering::Relaxed);
963        assert!(direct_load_test == 0 || direct_load_test == direct_load_handle.0);
964
965        indirect_load_handle
966    }
967
968    // Returns the direct load handle
969    fn add_engine_ref_by_handle_indirect(
970        &mut self,
971        indirect_load_handle: LoadHandle,
972    ) -> LoadHandle {
973        assert!(indirect_load_handle.is_indirect());
974        let state = self.indirect_states.get_mut(&indirect_load_handle).unwrap();
975        state.external_ref_count_indirect += 1;
976
977        let resolved_id_and_hash = state.resolved_id_and_hash;
978        if let Some(resolved_id_and_hash) = resolved_id_and_hash {
979            let direct_load_handle = self.get_or_insert_direct(resolved_id_and_hash);
980            self.add_engine_ref_by_handle_direct(direct_load_handle);
981            direct_load_handle
982        } else {
983            LoadHandle(0)
984        }
985    }
986
987    // Returns the direct load handle
988    fn add_engine_ref_by_handle_direct(
989        &mut self,
990        direct_load_handle: LoadHandle,
991    ) -> LoadHandle {
992        assert!(!direct_load_handle.is_indirect());
993        let load_handle_info = self.load_handle_infos.get_mut(&direct_load_handle).unwrap();
994        load_handle_info.external_ref_count_direct += 1;
995
996        Self::add_internal_ref(&self.events_tx, direct_load_handle, load_handle_info);
997
998        direct_load_handle
999    }
1000
1001    fn remove_engine_ref_indirect(
1002        &mut self,
1003        indirect_load_handle: LoadHandle,
1004    ) {
1005        assert!(indirect_load_handle.is_indirect());
1006        let state = self.indirect_states.get_mut(&indirect_load_handle).unwrap();
1007        state.external_ref_count_indirect -= 1;
1008        if let Some(resolved_id_and_hash) = &state.resolved_id_and_hash {
1009            let direct_load_handle = *self
1010                .artifact_id_to_handle
1011                .get(resolved_id_and_hash)
1012                .unwrap();
1013            self.remove_engine_ref_direct(direct_load_handle);
1014        }
1015    }
1016
1017    fn remove_engine_ref_direct(
1018        &mut self,
1019        direct_load_handle: LoadHandle,
1020    ) {
1021        assert!(!direct_load_handle.is_indirect());
1022        let load_handle_info = self.load_handle_infos.get_mut(&direct_load_handle).unwrap();
1023        load_handle_info.external_ref_count_direct -= 1;
1024
1025        // Engine always adjusts the latest version count
1026        Self::remove_internal_ref(&self.events_tx, direct_load_handle, load_handle_info);
1027    }
1028
1029    // Internal references are only to direct load handles
1030    fn add_internal_ref(
1031        events_tx: &Sender<LoaderEvent>,
1032        direct_load_handle: LoadHandle,
1033        load_handle_info: &mut LoadHandleInfo,
1034    ) {
1035        assert!(!direct_load_handle.is_indirect());
1036        load_handle_info.internal_ref_count += 1;
1037
1038        // If this is the first reference to the artifact, put it in the queue to be loaded
1039        if load_handle_info.internal_ref_count == 1 {
1040            events_tx
1041                .send(LoaderEvent::TryLoad(direct_load_handle))
1042                .unwrap();
1043        }
1044    }
1045
1046    // Internal references are only to direct load handles
1047    fn remove_internal_ref(
1048        events_tx: &Sender<LoaderEvent>,
1049        direct_load_handle: LoadHandle,
1050        load_handle_info: &mut LoadHandleInfo,
1051    ) {
1052        assert!(!direct_load_handle.is_indirect());
1053        load_handle_info.internal_ref_count -= 1;
1054        // If this was the last reference to the artifact, put it in queue to be dropped
1055        if load_handle_info.internal_ref_count == 0 {
1056            events_tx
1057                .send(LoaderEvent::TryUnload(direct_load_handle))
1058                .unwrap();
1059        }
1060    }
1061
1062    pub fn get_load_info(
1063        &self,
1064        handle: LoadHandle,
1065    ) -> Option<LoadInfo> {
1066        let handle = if handle.is_indirect() {
1067            let indirect_id = self.indirect_states.get(&handle).unwrap().id.clone();
1068            self.indirect_to_load
1069                .get(&indirect_id)
1070                .unwrap()
1071                .direct_load_handle()
1072        } else {
1073            handle
1074        };
1075
1076        let load_info = self.load_handle_infos.get(&handle)?;
1077        Some(LoadInfo {
1078            artifact_id: load_info.artifact_id,
1079            refs: load_info.external_ref_count_direct,
1080            symbol: load_info.symbol.clone(),
1081            debug_name: load_info.debug_name.clone(),
1082            //path: load_info.versions.last().unwrap().
1083        })
1084    }
1085}
1086
1087/// Information about an artifact load operation.
1088///
1089/// **Note:** The information is true at the time the `LoadInfo` is retrieved. The actual number of
1090/// references may change.
1091#[derive(Debug)]
1092pub struct LoadInfo {
1093    /// UUID of the artifact.
1094    pub artifact_id: ArtifactId,
1095    /// Number of references to the artifact.
1096    pub refs: u32,
1097    pub symbol: Option<StringHash>,
1098    pub debug_name: Option<Arc<String>>,
1099    // Path to artifact's source file. Not guaranteed to always be available.
1100    //pub path: Option<String>,
1101    // Name of artifact's source file. Not guaranteed to always be available.
1102    //pub file_name: Option<String>,
1103    // Asset name. Not guaranteed to always be available.
1104    //pub asset_name: Option<String>,
1105}
1106
1107//
1108// The Loader acts as a semi-public interface for LoaderInner.
1109//
1110#[derive(Clone)]
1111pub struct Loader {
1112    inner: Arc<Mutex<LoaderInner>>,
1113}
1114
1115impl Loader {
1116    pub(crate) fn new(
1117        loader_io: Box<dyn LoaderIO>,
1118        events_tx: Sender<LoaderEvent>,
1119        events_rx: Receiver<LoaderEvent>,
1120    ) -> Self {
1121        let build_hash = loader_io.current_build_hash();
1122
1123        let inner = LoaderInner {
1124            // start at 1 because 0 means null
1125            next_handle_index: 1,
1126            artifact_id_to_handle: Default::default(),
1127            load_handle_infos: Default::default(),
1128            loader_io,
1129            events_tx,
1130            events_rx,
1131            indirect_states: Default::default(),
1132            indirect_to_load: Default::default(),
1133            current_build_hash: build_hash,
1134            current_reload_action: None,
1135        };
1136
1137        Loader {
1138            inner: Arc::new(Mutex::new(inner)),
1139        }
1140    }
1141
1142    pub(crate) fn update(
1143        &self,
1144        artifact_storage: &mut dyn ArtifactStorage,
1145    ) {
1146        self.inner.lock().unwrap().update(artifact_storage);
1147    }
1148
1149    pub(crate) fn add_engine_ref_indirect(
1150        &self,
1151        id: IndirectIdentifier,
1152    ) -> Arc<ResolvedLoadHandle> {
1153        self.inner.lock().unwrap().add_engine_ref_indirect(id)
1154    }
1155
1156    pub(crate) fn add_engine_ref_by_handle(
1157        &self,
1158        load_handle: LoadHandle,
1159    ) -> LoadHandle {
1160        if load_handle.is_indirect() {
1161            self.inner
1162                .lock()
1163                .unwrap()
1164                .add_engine_ref_by_handle_indirect(load_handle)
1165        } else {
1166            self.inner
1167                .lock()
1168                .unwrap()
1169                .add_engine_ref_by_handle_direct(load_handle)
1170        }
1171    }
1172
1173    // from remove_refs
1174    pub(crate) fn remove_engine_ref(
1175        &self,
1176        load_handle: LoadHandle,
1177    ) {
1178        if load_handle.is_indirect() {
1179            self.inner
1180                .lock()
1181                .unwrap()
1182                .remove_engine_ref_indirect(load_handle);
1183        } else {
1184            self.inner
1185                .lock()
1186                .unwrap()
1187                .remove_engine_ref_direct(load_handle);
1188        }
1189    }
1190
1191    /// Returns handles to all active artifact loads.
1192    pub fn get_active_loads(&self) -> Vec<LoadHandle> {
1193        let mut loading_handles = Vec::default();
1194        let inner = self.inner.lock().unwrap();
1195        for k in inner.load_handle_infos.keys() {
1196            loading_handles.push(k.clone());
1197        }
1198
1199        loading_handles
1200    }
1201
1202    pub fn get_load_info(
1203        &self,
1204        handle: LoadHandle,
1205    ) -> Option<LoadInfo> {
1206        self.inner.lock().unwrap().get_load_info(handle)
1207    }
1208
1209    pub fn log_load_state_recursive(
1210        &self,
1211        load_handle: LoadHandle,
1212    ) {
1213        self.inner
1214            .lock()
1215            .unwrap()
1216            .log_load_state_recursive(load_handle, 0);
1217    }
1218}
1219
1220//
1221// This impl allows a handle in hydrate_base to implement load_state and artifact_id on handle itself,
1222// proxying the call to this loader
1223//
1224impl LoadStateProvider for Loader {
1225    fn load_state(
1226        &self,
1227        load_handle: &Arc<ResolvedLoadHandle>,
1228    ) -> LoadState {
1229        self.inner
1230            .lock()
1231            .unwrap()
1232            .load_handle_infos
1233            .get(&load_handle.direct_load_handle())
1234            .unwrap()
1235            .load_state
1236    }
1237
1238    fn artifact_id(
1239        &self,
1240        load_handle: &Arc<ResolvedLoadHandle>,
1241    ) -> ArtifactId {
1242        self.inner
1243            .lock()
1244            .unwrap()
1245            .load_handle_infos
1246            .get(&load_handle.direct_load_handle())
1247            .unwrap()
1248            .artifact_id
1249    }
1250}
1251
1252//
1253// This is used by SerdeContext to handle serializing/deserializing artifact references. We always
1254// provide direct handles when an artifact references another artifact. We ensure that if an artifact
1255// is loading that references another artifact, that artifact is already loaded. So generally we
1256// should not fail to find the requested artifact id
1257//
1258#[derive(Copy, Clone)]
1259struct LoadHandleInfoProviderImpl<'a> {
1260    artifact_id_to_handle: &'a HashMap<ArtifactIdAndHash, LoadHandle>,
1261    load_handle_infos: &'a HashMap<LoadHandle, LoadHandleInfo>,
1262    loader_io: &'a dyn LoaderIO,
1263}
1264
1265impl<'a> LoaderInfoProvider for LoadHandleInfoProviderImpl<'a> {
1266    // Used when deserializing to convert an artifact id into the load handle of the already-loaded
1267    // artifact
1268    fn resolved_load_handle(
1269        &self,
1270        id: &ArtifactRef,
1271    ) -> Option<Arc<ResolvedLoadHandle>> {
1272        let artifact_id = ArtifactId::from_uuid(id.0.as_uuid());
1273        let build_hash = self
1274            .loader_io
1275            .manifest_entry(artifact_id)
1276            .unwrap()
1277            .combined_build_hash;
1278
1279        let load_handle = self
1280            .artifact_id_to_handle
1281            .get(&ArtifactIdAndHash {
1282                id: artifact_id,
1283                hash: build_hash,
1284            })
1285            .map(|l| *l)?;
1286        Some(ResolvedLoadHandle::new(load_handle, load_handle))
1287    }
1288
1289    // Used when serializing to convert a load handle to an artifact id
1290    fn artifact_id(
1291        &self,
1292        load: LoadHandle,
1293    ) -> Option<ArtifactId> {
1294        self.load_handle_infos.get(&load).map(|l| l.artifact_id)
1295    }
1296}