views:

68

answers:

1

Suppose I have a custom collection class that provides some internal thread synchronization. For instance, a simplified Add method might look like this:

    public void Add(T item)
    {
        _lock.EnterWriteLock();
        try
        {
            _items.Add(item);
        }
        finally
        {
            _lock.ExitWriteLock();
        }
    }

The latest Code Contracts complains that CodeContracts: ensures unproven: this.Count >= Contract.OldValue(this.Count). The problem is that this really can't be proven. I can ensure that, internally, within the lock, Count will be greater than its previous value. I cannot ensure this, however, at the exit of the method. After the lock is exited and before the method completes, another thread could issue two Removes (likely of different elements), invalidating the contract.

The fundamental issue here is that the collection contracts can only be considered valid from within a particular locking context and only if the locking is used consistently throughout the application for all access to the collection. My collection must be used from multiple threads (with non-conflicting Add and Remove being a valid use case), but I would still like to implement ICollection<T>. Should I simply pretend that I can meet this ensures requirement with an Assume, even though I know I can't? It strikes me that none of the BCL collections can actually ensure this either.


EDIT:

Based on some further investigation, it sounds like the biggest issue is that the contract rewriter can introduce incorrect assertions, leading to runtime failures. Based on this, I think my only option is to restrict my interface implementation to IEnumerable<T>, as the contract on ICollection<T> implies that the implementing class cannot provide internal thread synchronization (access must always be synchronized externally.) This is acceptable for my particular case (all clients that wish to mutate the collection know about the class type directly), but I'm definitely interested to hear if there are other solutions to this.

+2  A: 

As you imply, no implementer can meet this contract. Indeed generally in the face of multi-threading unless the contract can be applied like:

 Take Lock
 gather Old Stuff

 work

 check Contract, which may compare Old Stuff
 Release Lock

I don't see how any contract could be honoured. From what I can see here this is an area that's not fully baked yet.

I think that using an Assume is the best you can do, in effect you are saying "by calling Add I am doing what the contract expects".

djna
Another thought: if the contract rewriter introduces assertions for the Ensures condition, this means the assertions can fail under multi-threaded use...
Dan Bryant
That seems to be acknowledged in the reference I give. To me this contract stuff looks like a nice idea that's not ready for prime time.
djna
@djna, thinking about this more, I think this may be reasonable. The contract assertions are basically indicating that a class implementing the interface is _not_ thread-safe and, in an odd way, you're actually violating the contract by attempting to provide internal thread-safety at a collection level. I notice that the new concurrent collections in .NET 4 don't implement `ICollection<T>`.
Dan Bryant
Pretty much all interface contracts assume single-thread access at the moment. It's quite hard to think about how the contracts would work in a multi-threaded scenario. I think essentially what you need here is to be able to specify *when* the post-condition should be checked - as this post says :)
Porges