// -----------------------------------------------------------------------
//
//
// Licensed to the Apache Software Foundation (ASF) under one or more
// contributor license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright ownership.
// The ASF licenses this file to You under the Apache License, Version 2.0
// (the "License"); you may not use this file except in compliance with
// the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//
// -----------------------------------------------------------------------
namespace Lucene.Net.Support.Threading
{
using System;
///
/// Provides thread-local storage of data.
///
/// The type of data to be stored.
public class ThreadLocal : IDisposable
{
private LocalDataStoreSlot slot;
private Exception cachedException;
private bool disposed = false;
///
/// Initializes a new instance of the class.
///
public ThreadLocal()
{
this.Factory = () => {
return default(T);
};
this.slot = ThreadData.AllocateDataSlot();
}
///
/// Initializes a new instance of the class.
///
/// The value factory.
public ThreadLocal(Func valueFactory)
{
this.Factory = valueFactory;
this.slot = ThreadData.AllocateDataSlot();
}
///
/// Gets or sets the value.
///
///
///
/// If the value will attempt to auto initialize using the supplied
/// valueFactory or default constructor.
/// If an exception is thrown, it is cached and rethrown each time this
/// property is attempted to be accessed.
///
///
/// The value.
///
/// Thrown when is already disposed.
///
///
/// The initialization function attempted to reference Value recursively.
///
public T Value
{
get
{
if (this.disposed)
throw new ObjectDisposedException("This instance is already disposed");
if (this.cachedException != null)
throw this.cachedException;
return this.FetchValue();
}
set
{
if (this.disposed)
throw new ObjectDisposedException("This instance is already disposed");
if (this.cachedException != null)
throw this.cachedException;
var state = this.GetState();
state.Initialized = true;
state.FetchValue = () => value;
}
}
///
/// Gets a value indicating whether the value has been created.
///
///
/// true if the value is created; otherwise, false.
///
///
/// Thrown when is already disposed.
///
public bool IsValueCreated
{
get
{
if (this.disposed)
throw new ObjectDisposedException("This instance is already disposed");
if (this.cachedException != null)
throw this.cachedException;
return this.IsThreadLocalInitialized;
}
}
private Func Factory { get; set; }
private bool IsThreadLocalInitialized
{
get
{
var state = this.GetState();
return state.Initialized;
}
}
///
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
///
public void Dispose()
{
this.Dispose(true);
GC.SuppressFinalize(this);
}
///
/// Releases unmanaged and - optionally - managed resources
///
/// true to release both managed and unmanaged resources; false to release only unmanaged resources.
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
this.Factory = null;
ThreadData.FreeLocalSlotData(this.slot.SlotId, this.slot.IsThreadLocal);
this.slot = null;
this.disposed = true;
}
}
private T FetchValue()
{
var state = this.GetState();
if (state.Initializing)
throw new InvalidOperationException("The initialization function attempted to reference Value recursively");
return state.FetchValue();
}
private ValueState GetState()
{
var state = ThreadData.GetData(this.slot) as ValueState;
if (state == null)
{
state = this.CreateState();
ThreadData.SetData(this.slot, state);
}
return state;
}
private ValueState CreateState()
{
var factory = this.Factory;
var state = new ValueState();
state.FetchValue = () => {
state.Initializing = true;
try
{
T value = factory();
state.Initializing = false;
state.Initialized = true;
state.FetchValue = () => { return value; };
return value;
}
catch (Exception ex)
{
this.cachedException = ex;
throw;
}
};
return state;
}
private class ValueState
{
public bool Initializing { get; set; }
public bool Initialized { get; set; }
public Func FetchValue { get; set; }
}
}
}