Fixes and verbose logging

This commit is contained in:
Marc Hernandez 2024-05-05 20:18:46 -07:00
parent 380974d673
commit 08cf4d3aca
2 changed files with 305 additions and 218 deletions

View File

@ -7,259 +7,309 @@ using System.Diagnostics;
using System.Reflection; using System.Reflection;
using System.Collections.Immutable; using System.Collections.Immutable;
using System.Threading; using System.Threading;
using System.IO;
using Microsoft.CodeAnalysis;
namespace res namespace res;
using ImmDefLoad = ImmutableQueue<(string name, Ref)>;
public interface Res_old
{ {
}
using ImmDefLoad = ImmutableQueue<(string name, Ref)>; [DebuggerDisplay("Path = {path}")]
public class Ref : lib.I_Serialize
{
static public bool s_verboseLogging = true;
public interface Res_old public string Filename =>path;
//For construction
public Ref()
{
path = "{set_from_ref_default_cons}";
if( s_verboseLogging ) log.info( $"Ref: {GetType().Name} {path}" );
}
public Ref( string filename )
{
path = filename;
if( s_verboseLogging ) log.info( $"Ref: {GetType().Name} {path}" );
}
virtual public void OnChange()
{
}
virtual internal void load()
{ {
} }
[Serializable] private string path = "{set_from_inline_cons}";
public class Ref : lib.I_Serialize }
[Serializable]
[DebuggerDisplay("Path = {path} / Res = {res}")]
public class Ref<T> : Ref where T : class
{
public T? res => m_res != null ? m_res : lookup();
public T? lookup()
{ {
public string Filename =>path; m_res = Mgr.load<T>( Filename );
if( s_verboseLogging ) log.info( $"Ref.lookup {GetType().Name} {GetType().GenericTypeArguments[0]} path {Filename}" );
//For construction return m_res;
public Ref()
{
path = "{UNSET_CONS}";
}
public Ref( string filename )
{
path = filename;
}
virtual public void OnSerialize()
{
}
virtual public void OnDeserialize( object enclosing )
{
}
virtual public void OnChange()
{
}
virtual internal void load()
{
}
private string path = "{UNSET_INLINE}";
} }
[Serializable] //For serialization
public class Ref<T> : Ref where T : class public Ref()
:
base( "{set_from_ref<>_default_cons}" )
{ {
public T? res => m_res != null ? m_res : ( m_res = Mgr.load<T>( Filename ) ); if( s_verboseLogging ) log.info( $"Ref {GetType().Name} {GetType().GenericTypeArguments[0]} path {Filename}" );
}
//For serialization public Ref( string filename )
public Ref() :
: base( filename )
base( "{unknown}" ) {
if( s_verboseLogging ) log.info( $"Ref {GetType().Name} {GetType().GenericTypeArguments[0]} path {Filename}" );
}
override internal void load()
{
m_res = Mgr.load<T>( Filename );
if( s_verboseLogging ) log.info( $"Ref.load {GetType().Name} {GetType().GenericTypeArguments[0]} path {Filename}" );
}
public object OnDeserialize( object enclosing )
{
return enclosing;
}
static public Ref<T> createAsset( T v, string path )
{
if( File.Exists( path ) )
{ {
log.warn( $"For {typeof(T).Name}, saving asset to {path}, but it already exists" );
var newPath = $"{path}_{DateTime.Now.ToShortDateString()}_{DateTime.Now.ToShortTimeString()}";
System.IO.File.Move(path, newPath );
log.warn( $"For {typeof(T).Name}, renamed to {newPath}" );
} }
public Ref( string filename ) var newRef = new Ref<T>( path );
:
base( filename )
{
}
/*
public Ref( string filename, T res ) : base( filename )
{
m_res = res;
}
*/
override internal void load()
{
m_res = Mgr.load<T>( Filename );
}
[NonSerialized]
private T m_res;
return newRef;
} }
[NonSerialized]
protected T m_res;
}
public class Resource public class RefMemory<T> : Ref<T> where T : class
{
//For serialization
public RefMemory( T res )
:
base( "{memory}" )
{ {
static public Mgr mgr; m_res = res;
} }
override internal void load()
/*
public class Loader<T>
{ {
static public T load( string filename )
{
Debug.Assert( false, "Specialize Loader for your type for file" );
return default(T);
}
}
*/
public delegate T Load<out T>( string filename );
class LoadHolder
{
internal virtual object load()
{
return null;
}
} }
}
class LoadHolder<T> : LoadHolder
public class Resource
{
static public Mgr mgr;
}
public delegate T Load<out T>( string filename );
class LoadHolder
{
internal virtual object load()
{ {
public LoadHolder( Load<T> fnLoad ) return null;
{ }
_fnLoad = fnLoad; }
}
public Load<T> _fnLoad;
internal override object load() class LoadHolder<T> : LoadHolder
{ {
return load(); public LoadHolder( Load<T> fnLoad )
} {
_fnLoad = fnLoad;
} }
//generic classes make a new static per generic type public Load<T> _fnLoad;
class ResCache<T> where T : class
internal override object load()
{ {
public static T s_default = default; return load();
public static ImmutableDictionary<string, WeakReference<T>> s_cache = ImmutableDictionary<string, WeakReference<T>>.Empty; }
}
public record class ResourceHolder<T>( WeakReference<T> weak, string Name, DateTime captured ) : imm.Recorded<ResourceHolder<T>>
where T : class
{
}
//generic classes make a new static per generic type
class ResCache<T> where T : class
{
public static T s_default = default;
public static ImmutableDictionary<string, ResourceHolder<T>> s_cache = ImmutableDictionary<string, ResourceHolder<T>>.Empty;
}
public class Mgr
{
static public void startup()
{
Resource.mgr = new Mgr();
} }
static public void register<T>( Load<T> loader )
public class Mgr
{ {
static public void startup() Debug.Assert( !Resource.mgr.m_loaders.ContainsKey( typeof( T ) ) );
var lh = new LoadHolder<T>( 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<T>()
{
registerSub( typeof(T) );
}
static public void registerSub( Type baseType )
{
log.info( $"Registering loader for {baseType.Name}" );
Type[] typeParams = new Type[1];
foreach( var mi in baseType.GetMethods() )
{ {
Resource.mgr = new Mgr(); if( mi.Name == "res_load" && mi.IsGenericMethod )
}
static public void register<T>( Load<T> loader )
{
Debug.Assert( !Resource.mgr.m_loaders.ContainsKey( typeof( T ) ) );
var lh = new LoadHolder<T>( 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<T>()
{
registerSub( typeof(T) );
}
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 ass in AppDomain.CurrentDomain.GetAssemblies() ) foreach( var t in ass.GetTypes() )
{ {
foreach( var t in ass.GetTypes() ) if( !baseType.IsAssignableFrom( t ) )
{ continue;
if( !baseType.IsAssignableFrom( t ) )
continue;
log.debug( $"Making a loader for {t.Name}" ); log.debug( $"Making a loader for {t.Name}" );
typeParams[0] = t; typeParams[0] = t;
var mi_ng = mi.MakeGenericMethod( typeParams ); var mi_ng = mi.MakeGenericMethod( typeParams );
var loadGenType = typeof( Load<> ); var loadGenType = typeof( Load<> );
var loadType = loadGenType.MakeGenericType( t ); var loadType = loadGenType.MakeGenericType( t );
var loader = Delegate.CreateDelegate( loadType, mi_ng ); var loader = Delegate.CreateDelegate( loadType, mi_ng );
var lhGenType = typeof( LoadHolder<> ); var lhGenType = typeof( LoadHolder<> );
var lhType = lhGenType.MakeGenericType( t ); var lhType = lhGenType.MakeGenericType( t );
var lh = Activator.CreateInstance( lhType, loader ) as LoadHolder; var lh = Activator.CreateInstance( lhType, loader ) as LoadHolder;
ImmutableInterlocked.TryAdd( ref Resource.mgr.m_loaders, t, lh ); ImmutableInterlocked.TryAdd( ref Resource.mgr.m_loaders, t, lh );
}
} }
return;
} }
return;
} }
} }
}
static public Ref<T> lookup<T>( string filename ) where T : class static public Ref<T> lookup<T>( string filename ) where T : class
{
return new Ref<T>( filename );
}
//*
static public Ref lookup( string filename, Type t )
{
return new Ref( filename );
}
//*/
// @@@ TODO Pass information through here
static public T? load<T>( string filename ) where T : class
{
if( ResCache<T>.s_cache.TryGetValue( filename, out var holder ) )
{ {
return new Ref<T>( filename );
}
//* if( holder.weak.TryGetTarget( out var v ) )
static public Ref lookup( string filename, Type t )
{
return new Ref( filename );
}
//*/
static public T? load<T>( string filename ) where T : class
{
if( ResCache<T>.s_cache.TryGetValue( filename, out var wr ) )
{ {
if( wr.TryGetTarget( out var v ) ) return v;
return v;
log.info( $"{filename} was in cache, but its been dropped, reloading." );
} }
log.warn( $"Block Loading {filename}." ); log.info( $"{filename} was in cache, but its been dropped, reloading." );
var newV = actualLoad<T>( filename );
return newV;
} }
static public T actualLoad<T>( string filename ) where T : class log.warn( $"Block Loading {filename}." );
var newV = actualLoad<T>( filename );
return newV;
}
static public T actualLoad<T>( string filename ) where T : class
{
lock(s_loading)
{ {
if( s_loading.TryGetValue( filename, out var evt ) ) if( s_loading.TryGetValue( filename, out var evt ) )
{ {
evt.WaitOne();
if( ResCache<T>.s_cache.TryGetValue( filename, out var wr ) ) //var waiting = evt.WaitOne();
if( ResCache<T>.s_cache.TryGetValue( filename, out var holder ) )
{ {
if( wr.TryGetTarget( out var v ) ) if( holder.weak.TryGetTarget( out var v ) )
{
log.trace( $"{typeof(T).Name} loading {filename}" );
return v; return v;
}
log.error( $"{filename} was in cache, but its been dropped, reloading." ); log.error( $"{filename} was in cache, but its been dropped, reloading." );
} }
} }
var evtNew = new AutoResetEvent( false ); //var evtNew = new AutoResetEvent( false );
if( ImmutableInterlocked.TryAdd( ref s_loading, filename, evtNew ) ) //if( ImmutableInterlocked.TryAdd( ref s_loading, filename, evtNew ) )
{ {
if( Resource.mgr.m_loaders.TryGetValue( typeof( T ), out var loaderGen ) ) if( Resource.mgr.m_loaders.TryGetValue( typeof( T ), out var loaderGen ) )
{ {
@ -269,14 +319,18 @@ namespace res
var weak = new WeakReference<T>( v ); var weak = new WeakReference<T>( v );
var alreadyAdded = !ImmutableInterlocked.TryAdd( ref ResCache<T>.s_cache, filename, weak ); var holder = new ResourceHolder<T>( weak, $"", DateTime.Now ).Record();
evtNew.Set(); log.info( $"To {typeof(T).Name} add {filename}" );
var alreadyAdded = !ImmutableInterlocked.TryAdd( ref ResCache<T>.s_cache, filename, holder );
//evtNew.Set();
//Done loading //Done loading
if( !ImmutableInterlocked.TryRemove( ref s_loading, filename, out var oldEvt ) ) //if( !ImmutableInterlocked.TryRemove( ref s_loading, filename, out var oldEvt ) )
{ {
log.error( $"Error removing loading event for {filename}" ); //log.error( $"Error removing loading event for {filename}" );
} }
if( alreadyAdded ) if( alreadyAdded )
@ -294,49 +348,46 @@ namespace res
} }
} }
return actualLoad<T>( filename );
} }
static object s_loadingLock = new object(); return actualLoad<T>( filename );
}
static ImmutableDictionary<string, AutoResetEvent> s_loading = ImmutableDictionary<string, AutoResetEvent>.Empty; static object s_loadingLock = new object();
static ImmDefLoad s_deferredLoad = ImmDefLoad.Empty;
static ImmutableDictionary<string, AutoResetEvent> s_loading = ImmutableDictionary<string, AutoResetEvent>.Empty;
static ImmDefLoad s_deferredLoad = ImmDefLoad.Empty;
Mgr() Mgr()
{
log.info( $"Creating Res.Mgr" );
var ts = new ThreadStart( deferredLoader );
m_deferredLoader = new Thread( ts );
m_deferredLoader.Start();
}
void deferredLoader()
{
while( true )
{ {
log.info( $"Creating Res.Mgr" ); Thread.Sleep( 1 );
var ts = new ThreadStart( deferredLoader ); if( ImmutableInterlocked.TryDequeue( ref s_deferredLoad, out var v ) )
m_deferredLoader = new Thread( ts );
m_deferredLoader.Start();
}
void deferredLoader()
{
while( true )
{ {
Thread.Sleep( 1 ); v.Item2.load();
if( ImmutableInterlocked.TryDequeue( ref s_deferredLoad, out var v ) )
{
v.Item2.load();
}
} }
} }
ImmutableDictionary<Type, LoadHolder> m_loaders = ImmutableDictionary<Type, LoadHolder>.Empty;
Thread m_deferredLoader;
} }
ImmutableDictionary<Type, LoadHolder> m_loaders = ImmutableDictionary<Type, LoadHolder>.Empty;
Thread m_deferredLoader;
} }

View File

@ -38,8 +38,8 @@ namespace lib
public interface I_Serialize public interface I_Serialize
{ {
void OnSerialize(); void OnSerialize() {}
void OnDeserialize( object enclosing ); object OnDeserialize( object enclosing ) => this;
} }
[Flags] [Flags]
@ -123,6 +123,8 @@ namespace lib
public class XmlFormatter2Cfg : Config public class XmlFormatter2Cfg : Config
{ {
public bool VerboseLogging = true;
public Datastructure datastructure = Datastructure.Tree; public Datastructure datastructure = Datastructure.Tree;
public int Version = 2; public int Version = 2;
@ -286,6 +288,8 @@ namespace lib
{ {
TypeCode typeCode = Type.GetTypeCode( type ); TypeCode typeCode = Type.GetTypeCode( type );
if( _cfg.VerboseLogging ) log.info( $"{type.Name}.{name} {existing} {mi?.Name}" );
if( typeCode != TypeCode.Object ) if( typeCode != TypeCode.Object )
{ {
return DeserializeConcrete( elem, mi, name, type ); return DeserializeConcrete( elem, mi, name, type );
@ -302,11 +306,10 @@ namespace lib
{ {
object obj = DeserializeObject( elem, mi, type, existing ); object obj = DeserializeObject( elem, mi, type, existing );
if( obj is I_Serialize ) if( obj is I_Serialize iser )
{ {
var iser = obj as I_Serialize; if( _cfg.VerboseLogging ) log.info( $"" );
obj = iser.OnDeserialize( null );
iser.OnDeserialize( null );
} }
return obj; return obj;
@ -320,7 +323,7 @@ namespace lib
} }
catch( Exception ex ) catch( Exception ex )
{ {
log.warn( $"Caught exception fn {mi.Name} type {type.Name} of {ex.Message}" ); log.warn( $"Caught exception fn {mi?.Name} type {type?.Name} of {ex.Message}" );
} }
return existing; return existing;
@ -343,6 +346,8 @@ namespace lib
private object DeserializeConcrete( XmlElement elem, MemberInfo mi, string name, Type type ) private object DeserializeConcrete( XmlElement elem, MemberInfo mi, string name, Type type )
{ {
if( _cfg.VerboseLogging ) log.info( $"" );
string val = ""; string val = "";
if( elem.HasAttribute( "v" ) ) if( elem.HasAttribute( "v" ) )
@ -425,6 +430,8 @@ namespace lib
private object HydrateObject( XmlElement elem, MemberInfo mi, Type finalType, object obj ) private object HydrateObject( XmlElement elem, MemberInfo mi, Type finalType, object obj )
{ {
if( _cfg.VerboseLogging ) log.info( $"" );
if( obj is IList ) if( obj is IList )
{ {
var list = obj as IList; var list = obj as IList;
@ -513,6 +520,8 @@ namespace lib
private object HydrateObjectOfNarrowType( XmlElement elem, MemberInfo mi, Type narrowType, object obj ) private object HydrateObjectOfNarrowType( XmlElement elem, MemberInfo mi, Type narrowType, object obj )
{ {
if( _cfg.VerboseLogging ) log.info( $"" );
var isImm = typeof(imm.Imm).IsAssignableFrom( narrowType ); var isImm = typeof(imm.Imm).IsAssignableFrom( narrowType );
XmlNodeList allChildren = elem.ChildNodes; XmlNodeList allChildren = elem.ChildNodes;
@ -671,6 +680,7 @@ namespace lib
private object GetObjectForDeser( XmlElement elem, Type type, out Type finalType, object obj ) private object GetObjectForDeser( XmlElement elem, Type type, out Type finalType, object obj )
{ {
finalType = type; finalType = type;
if( elem.HasAttribute( "_.t" ) ) if( elem.HasAttribute( "_.t" ) )
{ {
@ -685,6 +695,8 @@ namespace lib
int refInt = refString.Length > 0 ? Convert.ToInt32( refString ) : -1; int refInt = refString.Length > 0 ? Convert.ToInt32( refString ) : -1;
if( _cfg.VerboseLogging ) log.info( $"{finalType?.Name}({type?.Name}) refInt {refInt} exitingObj = {obj?.ToString()}" );
obj = createObject( elem, finalType, refInt, obj ); obj = createObject( elem, finalType, refInt, obj );
return obj; return obj;
@ -692,6 +704,8 @@ namespace lib
private object DeserializeList( XmlElement elem, MemberInfo mi, Type type, IList list ) private object DeserializeList( XmlElement elem, MemberInfo mi, Type type, IList list )
{ {
if( _cfg.VerboseLogging ) log.info( $"" );
XmlNodeList arrNodeList = elem.ChildNodes; XmlNodeList arrNodeList = elem.ChildNodes;
Type t = list.GetType(); Type t = list.GetType();
@ -726,6 +740,7 @@ namespace lib
typeElem = typeof( KeyValuePair<,> ).MakeGenericType( type.GenericTypeArguments ); typeElem = typeof( KeyValuePair<,> ).MakeGenericType( type.GenericTypeArguments );
} }
if( _cfg.VerboseLogging ) log.info( $"DserCol {type.GetType().Name} {typeElem.Name} into reflT {mi.ReflectedType.Name} declT {mi.DeclaringType.Name} {mi.Name}" );
string refString = elem.GetAttribute( "ref" ); string refString = elem.GetAttribute( "ref" );
int refInt = refString.Length > 0 ? Convert.ToInt32( refString ) : -1; int refInt = refString.Length > 0 ? Convert.ToInt32( refString ) : -1;
@ -752,7 +767,9 @@ namespace lib
finalType = typeElem; finalType = typeElem;
} }
arr.SetValue( Deserialize( arrElem, mi, finalType, null ), i ); var arrItem = Deserialize( arrElem, mi, finalType, null );
arr.SetValue( arrItem, i );
} }
} }
@ -768,6 +785,8 @@ namespace lib
var typeGen = Type.MakeGenericSignatureType( type ); var typeGen = Type.MakeGenericSignatureType( type );
if( _cfg.VerboseLogging ) log.info( $"TypeGen: {typeGen.Name}" );
if( type == typeof( ImmutableArray<> ).MakeGenericType( typeElem ) ) if( type == typeof( ImmutableArray<> ).MakeGenericType( typeElem ) )
{ {
var genMeth = GetType().GetMethod( "MakeImmutableArray", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic ); var genMeth = GetType().GetMethod( "MakeImmutableArray", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic );
@ -823,6 +842,8 @@ namespace lib
private object DeserializeArray( XmlElement elem, MemberInfo mi, Type type ) private object DeserializeArray( XmlElement elem, MemberInfo mi, Type type )
{ {
if( _cfg.VerboseLogging ) log.info( $"" );
Type typeElem = type.GetElementType(); Type typeElem = type.GetElementType();
string refString = elem.GetAttribute( "ref" ); string refString = elem.GetAttribute( "ref" );
@ -866,11 +887,12 @@ namespace lib
private object createObject( XmlElement elem, Type type, int refInt, object existingObj ) private object createObject( XmlElement elem, Type type, int refInt, object existingObj )
{ {
TypeCode tc = Type.GetTypeCode( type ); TypeCode tc = Type.GetTypeCode( type );
if( _cfg.datastructure == Datastructure.Graph && refInt > 0 && m_alreadySerialized.ContainsKey( refInt ) ) if( _cfg.datastructure == Datastructure.Graph && refInt > 0 && m_alreadySerialized.ContainsKey( refInt ) )
{ {
//lib.log.info( "Reusing object for {0}", refInt ); if( _cfg.VerboseLogging ) log.info( $"Reuse object" );
return m_alreadySerialized[refInt]; return m_alreadySerialized[refInt];
} }
@ -880,6 +902,7 @@ namespace lib
if( isProxy ) if( isProxy )
{ {
if( _cfg.VerboseLogging ) log.info( $"use Proxy" );
object obj = null; object obj = null;
var tryType = type; var tryType = type;
@ -921,28 +944,41 @@ namespace lib
var isSubclass = type.IsSubclassOf( existingObjType ) || existingObjType.IsSubclassOf( type ); var isSubclass = type.IsSubclassOf( existingObjType ) || existingObjType.IsSubclassOf( type );
if( isSubclass ) if( isSubclass )
{
if( _cfg.VerboseLogging ) log.info( $"Using existing obj {existingObj?.ToString()}" );
return existingObj; return existingObj;
}
// old // old
//if( type == existingObjType ) return existingObj; //if( type == existingObjType ) return existingObj;
} }
if( typeof(res.Ref).IsAssignableFrom( type ) )
{
log.info( $"Ref time!" );
}
// THIRD create a new object // THIRD create a new object
{ {
object obj = null; object obj = null;
try try
{ {
if( _cfg.VerboseLogging ) log.info( $"Activator.CreateInstance" );
//Trying the nice way to creat objects first. //Trying the nice way to creat objects first.
obj = Activator.CreateInstance( type ); obj = Activator.CreateInstance( type );
if( _cfg.VerboseLogging ) log.info( $"Got obj {obj?.ToString()}" );
} }
catch( Exception ) catch( Exception ex )
{ {
try try
{ {
if( _cfg.VerboseLogging ) log.info( $"GetUninitializedObject" );
obj = System.Runtime.Serialization.FormatterServices.GetUninitializedObject( type ); obj = System.Runtime.Serialization.FormatterServices.GetUninitializedObject( type );
if( _cfg.VerboseLogging ) log.info( $"Got obj {obj?.ToString()}" );
} }
catch( Exception exInner ) catch( Exception exInner )
{ {