// ----------------------------------------------------------------------- // // // 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; } } } }