using System; using System.Collections.Generic; using System.Linq; using System.Text; //using System.Threading.Tasks; using System.Diagnostics; using System.Reflection; using System.Collections.Immutable; using System.Threading; namespace res { using ImmDefLoad = ImmutableQueue<(string name, Ref)>; public interface Res_old { } [Serializable] public class Ref : lib.I_Serialize { public string Filename =>_filename; //For construction public Ref() { _filename = "{UNSET_CONS}"; } public Ref( string filename ) { _filename = filename; } virtual public void OnSerialize() { } virtual public void OnDeserialize( object enclosing ) { } virtual public void OnChange() { } virtual internal void load() { } protected string _filename = "{UNSET_INLINE}"; } [Serializable] public class Ref : Ref where T : class { public T? res => m_res != null ? m_res : ( m_res = Mgr.load( Filename ) ); //For serialization public Ref() : base( "{unknown}" ) { } public Ref( string filename ) : base( filename ) { } /* public Ref( string filename, T res ) : base( filename ) { m_res = res; } */ override internal void load() { m_res = Mgr.load( Filename ); } [NonSerialized] private T m_res; } public class Resource { static public Mgr mgr; } /* public class Loader { static public T load( string filename ) { Debug.Assert( false, "Specialize Loader for your type for file" ); return default(T); } } */ public delegate T Load( string filename ); class LoadHolder { internal virtual object load() { return null; } } class LoadHolder : LoadHolder { public LoadHolder( Load fnLoad ) { m_fnLoad = fnLoad; } public Load m_fnLoad; internal override object load() { return load(); } } //generic classes make a new static per generic type class ResCache where T : class { public static T s_default = default; public static ImmutableDictionary> s_cache = ImmutableDictionary>.Empty; } public class Mgr { static public void startup() { Resource.mgr = new Mgr(); } static public void register( Load loader ) { Debug.Assert( !Resource.mgr.m_loaders.ContainsKey( typeof( T ) ) ); var lh = new LoadHolder( loader ); ImmutableInterlocked.TryAdd( ref Resource.mgr.m_loaders, typeof( T ), lh ); } //Register all subclasses of a particular type //???? Should we just always do this? static public void registerSub( Type baseType ) { log.info( $"Registering loader for {baseType.Name}" ); Type[] typeParams = new Type[1]; foreach( var mi in baseType.GetMethods() ) { if( mi.Name == "res_load" && mi.IsGenericMethod ) { foreach( var ass in AppDomain.CurrentDomain.GetAssemblies() ) { foreach( var t in ass.GetTypes() ) { if( !baseType.IsAssignableFrom( t ) ) continue; log.debug( $"Making a lodaer for {t.Name}" ); typeParams[0] = t; var mi_ng = mi.MakeGenericMethod( typeParams ); var loadGenType = typeof( Load<> ); var loadType = loadGenType.MakeGenericType( t ); var loader = Delegate.CreateDelegate( loadType, mi_ng ); var lhGenType = typeof( LoadHolder<> ); var lhType = lhGenType.MakeGenericType( t ); var lh = Activator.CreateInstance( lhType, loader ) as LoadHolder; ImmutableInterlocked.TryAdd( ref Resource.mgr.m_loaders, t, lh ); } } return; } } } static public Ref lookup( string filename ) where T : class { /* LoadHolder loader_gen; Resource.mgr.m_loaders.TryGetValue( typeof( T ), out loader_gen ); var loaderHolder = loader_gen as LoadHolder; if( loaderHolder != null ) { var rf_raw = loaderHolder.dlgtLoad( filename ); Ref rf = rf_raw as Ref; return rf; } */ return new Ref( filename ); } //* static public Ref lookup( string filename, Type t ) { /* LoadHolder loader_gen; Resource.mgr.m_loaders.TryGetValue( t, out loader_gen ); var lhGenType = typeof(LoadHolder<>); if( loaderHolder != null ) { var rf_raw = loaderHolder.load( filename ); return rf_raw; } */ return new Ref( filename ); } //*/ static public T? load( string filename ) where T : class { if( ResCache.s_cache.TryGetValue( filename, out var wr ) ) { if( wr.TryGetTarget( out var v ) ) return v; log.info( $"{filename} was in cache, but its been dropped, reloading." ); } log.warn( $"Block Loading {filename}." ); var newV = actualLoad( filename ); return newV; } static public T actualLoad( string filename ) where T : class { if( s_loading.TryGetValue( filename, out var evt ) ) { evt.WaitOne(); if( ResCache.s_cache.TryGetValue( filename, out var wr ) ) { if( wr.TryGetTarget( out var v ) ) return v; log.error( $"{filename} was in cache, but its been dropped, reloading." ); } } var evtNew = new AutoResetEvent( false ); if( ImmutableInterlocked.TryAdd( ref s_loading, filename, evtNew ) ) { if( Resource.mgr.m_loaders.TryGetValue( typeof( T ), out var loaderGen ) ) { var loader = loaderGen as LoadHolder; var v = loader.m_fnLoad( filename ); var weak = new WeakReference( v ); var alreadyAdded = !ImmutableInterlocked.TryAdd( ref ResCache.s_cache, filename, weak ); evtNew.Set(); //Done loading if( !ImmutableInterlocked.TryRemove( ref s_loading, filename, out var oldEvt ) ) { log.error( $"Error removing loading event for {filename}" ); } if( alreadyAdded ) { log.error( $"Key {filename} already existed, though it shouldnt." ); } return v; } else { log.error( $"Loader could not be found for type {typeof( T )}" ); return ResCache.s_default; } } return actualLoad( filename ); } static object s_loadingLock = new object(); static ImmutableDictionary s_loading = ImmutableDictionary.Empty; static ImmDefLoad s_deferredLoad = ImmDefLoad.Empty; Mgr() { log.info( $"Creating Res.Mgr" ); var ts = new ThreadStart( deferredLoader ); m_deferredLoader = new Thread( ts ); m_deferredLoader.Start(); } void deferredLoader() { while( true ) { Thread.Sleep( 1 ); if( ImmutableInterlocked.TryDequeue( ref s_deferredLoad, out var v ) ) { v.Item2.load(); } } } ImmutableDictionary m_loaders = ImmutableDictionary.Empty; Thread m_deferredLoader; } }