Coder Social home page Coder Social logo

autolazy's Introduction

AutoLazy

Post compile tool using Fody to implement the double-checked locking pattern.

The nuget package NuGet Status

https://nuget.org/packages/AutoLazy.Fody/

PM> Install-Package AutoLazy.Fody

Works on

  • static or instance members
  • parameterless methods
  • properties
  • methods with a single parameter (new)

Contributing

Please read the guidelines before submitting any changes. Thank you!

Example

Turns this

public class MyClass
{
	// This would work as a method, e.g. GetSettings(), as well.
	[Lazy]
	public static Settings Settings
	{
		get
		{
			using (var fs = File.Open("settings.xml", FileMode.Open))
			{
				var serializer = new XmlSerializer(typeof(Settings));
				return (Settings)serializer.Deserialize(fs);
			}
		}
	}

	[Lazy]
	public static Settings GetSettingsFile(string fileName)
	{
		using (var fs = File.Open(fileName, FileMode.Open))
		{
			var serializer = new XmlSerializer(typeof(Settings));
			return (Settings)serializer.Deserialize(fs);
		}
	}
}

Into something equivalent to this

public class MyClass
{
	// begin - fields added by the post-compile step
	private static readonly object _syncRoot = new object();
	private static volatile Settings _settings;
	// end
	
	[Lazy]
	public static Settings Settings
	{
		get
		{
			// thread-safe double-checked locking pattern generated here
			var result = _settings;
			if (result == null)
			{
				lock(_syncRoot)
				{
					result = _settings;
					if (result == null)
					{
						using (var fs = File.Open("settings.xml", FileMode.Open))
						{
							var serializer = new XmlSerializer(typeof(Settings));
							result = (Settings)serializer.Deserialize(fs);
							_settings = result;
						}
					}
				}
			}
			return result;
		}
	}

	// begin - fields added by post-compile step
	private static readonly object _getSettingsFileSyncRoot = new object();
	private static volatile Dictionary<string, Settings> _getSettingsFileCache
		= new Dictionary<string, Settings>();
	// end

	[Lazy]
	public static Settings GetSettingsFile(string fileName)
	{
		Settings result;
		if (!_getSettingsFileCache.TryGetValue(fileName, out result)) // volatile read
		{
			lock (_getSettingsFileSyncRoot)
			{
				if (!_getSettingsFileCache.TryGetValue(fileName, out result))
				{
					using (var fs = File.Open(fileName, FileMode.Open))
					{
						var serializer = new XmlSerializer(typeof(Settings));
						result = (Settings)serializer.Deserialize(fs);
					}
					// note - we can't mutate the dictionary after
					// exposing it to reads (possibly from other threads)
					// therefore, we have to copy the dictionary into a new one
					// then expose it by setting _getSettingsFileCache
					var newCache = new Dictionary<string, Settings>(_getSettingsFileCache.Count + 1);
					newCache.Add(fileName, result);
					foreach (var pair in _getSettingsFileCache)
					{
						newCache.Add(pair.Key, pair.Value);
					}
					_getSettingsFileCache = newCache; // volatile write
				}
			}
		}
		return result;
	}

}

autolazy's People

Contributors

bcuff avatar juntalis avatar wenceywang avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

autolazy's Issues

Update reference

1>MSBUILD : error : Fody: The weaver assembly 'AutoLazy.Fody, Version=2.2.1.0, Culture=neutral, PublicKeyToken=null' references an out of date version of Mono.Cecil.dll. Expected strong name token of '1C-A0-91-87-7D-12-CA-03' but got '50-CE-BF-1C-CE-B9-D0-5E'. The weaver needs to update to at least version 3.0 of FodyHelpers.

Write to log info about which method AutoLazy is instrumenting to

Maybe consider something like this in LazyWeaver.cs:

    public bool Instrument()
    {
        **Context.Weaver.LogInfo($"Instrumenting [Lazy] to {Method.DeclaringType.FullName}.{Method.Name}.");**

this will also help in debugging in order to figure out if AutoLazy didn't instrument to some property we were expecting.

Null handling.

This is a really cool project.

If the result of the body of the property is null, then the body of the property is called each time.

This could be fixed using a slightly different implementation:

// begin - fields added by the post-compile step
private static Lazy<Settings> _settings = new Lazy<Settings>(() => __Settings);
public static Settings Settings { get { return _settings.Value; } }
// end

// Property is made private and name changed somehow
private static Settings __Settings
{
	get
	{
		using (var fs = File.Open("settings.xml", FileMode.Open))
		{
			var serializer = new XmlSerializer(typeof(Settings));
			return (Settings)serializer.Deserialize(fs);
		}
	}
}

Why decide against ConcurrentDictionary?

Hey,
is there any reason behind not using a ConcurrentDictionary and instead locking the method for all calls? It would be quite an improvement if you wouldn't lock the whole method for different parameters.

It would be better if you would keep a second dictionary that contains lock objects and only lock on a global lock for checking this dictionary (and lock on individual locks for unique parameters).

Update Fody to 2.0

Fody Version 2 is out and you will need to do an update for your adding to be usable with it

Specifically:

  • Update to the newest Fody.Cecil nuget
  • Change the min dependency ranged for Fody to be at least 2.0

AutoLazy does not apply to nested classes?

I have a class nested inside another class, and had a get-property with the Lazy attribute. While testing I saw AutoLazy behavior was not applied to it.

example test program:

class Program {
static void Main(string[] args) {
var outer = new OuterClass();
Debug.WriteLine(outer.GetProperty);
Debug.WriteLine(outer.GetProperty);

        var inner = new OuterClass.InnerClass();
        Debug.WriteLine(inner.GetProperty);
        Debug.WriteLine(inner.GetProperty);
    }
}

class OuterClass {
    [Lazy] public int GetProperty {
        get {
            Debug.WriteLine($"Running {nameof(OuterClass)}.{nameof(OuterClass.GetProperty)}");
            return 1;
        }
    }

    public class InnerClass {
        [Lazy] public int GetProperty {
            get {
                Debug.WriteLine($"Running {nameof(InnerClass)}.{nameof(InnerClass.GetProperty)}");
                return 2;
            }
        }
    }
}

output:
Running OuterClass.GetProperty
1
1
Running InnerClass.GetProperty
2
Running InnerClass.GetProperty
2

expected output:
Running OuterClass.GetProperty
1
1
Running InnerClass.GetProperty
2
2

Proposed Doc (and Implementation?) Tweaks

I stumbled across this while looking at what people have done with Fody and was intrigued. I haven't looked at anything but README.md, so it's possible that the weaver itself is fine. I'm not planning to spend too much more time here, so I'm sticking to just README.md and there's a nonzero chance I'm just going to disappear like a ghost after logging this.

  1. I'm pretty sure all your instances of volatile are unnecessary and likely harm the common case (to a small degree). Monitor.Enter and Monitor.Exit are both full-fence, so you should already have the same guarantee at double-check time that volatile enforces (which is where it really matters), without having to pay whatever the volatile read cost is on every initial check after it's populated.
  2. The getter for Settings doesn't actually set the _settings field.
  3. Dictionary<TKey, TValue> throws when the key is null, so you should probably be weaving in some kind of special handling for that. Suggestion: separate TValue-typed field that says the value to use for a null key.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.