views:

156

answers:

2

The documentation of the enterprise library says :

Because of the way the Cache object operates, you are guaranteed that any backing store will be called in a single-threaded manner. This means you do not have to make your implementation thread safe.

And concerning the CacheManager :

Every method call made through the CacheManager object is thread safe.

But a simple test proves the contrary :

Here is my custom backing store (only the Add method is relevant)

public class MyStore : IBackingStore
{
    volatile bool isEntered = false;
    #region IBackingStore Members

    public void Add(CacheItem newCacheItem)
    {
     if(isEntered)
      throw new NotImplementedException();
     isEntered = true;

     Thread.Sleep(1000);

     isEntered = false;

    }

    public int Count
    {
     get
     {
      throw new NotImplementedException();
     }
    }

    public void Flush()
    {
     throw new NotImplementedException();
    }

    public System.Collections.Hashtable Load()
    {
     return new System.Collections.Hashtable();
    }

    public void Remove(string key)
    {
     throw new NotImplementedException();
    }

    public void UpdateLastAccessedTime(string key, DateTime timestamp)
    {
     throw new NotImplementedException();
    }

    #endregion

    #region IDisposable Members

    public void Dispose()
    {
     throw new NotImplementedException();
    }

    #endregion
}

And here is a test wich access to the same CacheManager through two differents threads :

DictionaryConfigurationSource configSource = new DictionaryConfigurationSource();
CacheManagerSettings cacheSettings = new CacheManagerSettings();
configSource.Add(CacheManagerSettings.SectionName, cacheSettings);
CacheStorageData storageConfig = new CacheStorageData("MyStorage", typeof(MyStore));
cacheSettings.BackingStores.Add(storageConfig);
CacheManagerData cacheManagerData = new CacheManagerData("CustomCache", 120, 100, 5, storageConfig.Name);
cacheSettings.CacheManagers.Add(cacheManagerData);
cacheSettings.DefaultCacheManager = cacheManagerData.Name;


CacheManagerFactory cacheFactory = new CacheManagerFactory(configSource);
ICacheManager cacheManager = cacheFactory.CreateDefault();
Thread thread = new Thread(() =>
{
    cacheManager.Add("item1", "odaiu");
});
thread.Start();
cacheManager.Add("item2", "dzaoiudoiza");

The Add method is executed two times in two differents thread (because it throws the "NotImplementedException" of the Add method).

Is something wrong with my code or the documentation of the enterprise library is wrong ?

A: 

Since the documentation is so explicit about this, I would trust the documentation.

Your proof is flawed in that you are creating an explicitmulti-threaded use case for the class. There is nothing inherent about the particular interface that makes it thread safe. So this will certainly fail.

The enterprise library is making a guarantee that they will manage the interface in a thread safe manner. Internally they will take care to manage the thread safety of the class.

Note: I don't have any specific knowledge about this library, but with documentation that explicit, I'd trust it.

JaredPar
A: 

I agree with JaredPar that the documentation states access is synchronized and thus treadsafe. However if you look at the source code for the implementation of the delivered backing stores you will see it is coded with the expectation to be run in a multithreaded environment. So perhaps this is a case of stale documentation.

The following excerpt is from the IsolatedStorageBackingStore in EntLib 5.0 thought the 4.1 implemenation is the same. The excerpt is just one method for clarity sake but all the access to the underlying IsolatedStorageFile is locked.

/// <summary>
/// Adds new item to persistence store
/// </summary>
/// <param name="storageKey">Unique key for storage item</param>
/// <param name="newItem">Item to be added to cache. May not be null.</param>
protected override void AddNewItem(int storageKey, CacheItem newItem)
{
    lock (store)
    {
        string storageLocation = GenerateItemLocation(storageKey);
        IsolatedStorageCacheItem cacheItem =
            new IsolatedStorageCacheItem(store, storageLocation, this.encryptionProvider);
        cacheItem.Store(newItem);
    }
}
Tedford