Monitor doesn't seem to lock the object
.net, c#, mono, multithreading
Solution
Only the thread that took the lock can exit the lock. You can't `Enter` it in the constructor on the calling thread then `Exit` from the thread-pool when it completes - the thread-pool worker does not have the lock.
And conversely: presumably it is the same thread that created the future that is accessing the getter: that is allowed to `Enter` again: it is re-entrant. Also, you need to `Exit` the same number of times that you `Enter`, otherwise it isn't actually released.
Basically, I don't think `Monitor` is the right approach here.
Problem
I'm trying to implement a basic `Future` class (yeah, I know about `Task` but this is for educational purposes) and ran into strange behavior of `Monitor` class. The class is implemented so that it enters the lock in constructor, queues an action which exits the lock to a thread pool. Result getter checks an instance variable to see if the action is completed and if it isn't, enters lock and then returns the result. Problem is that in fact result getter doesn't wait for the queued action to finish and proceeds anyway leading to incorrect results. Here's the code. ``` // The class itself public class Future<T> { private readonly Func<T> _f; private volatile bool _complete = false; private T _result; private Exception _error = new Exception("WTF"); private volatile bool _success = false; private readonly ConcurrentStack<Action<T>> _callbacks = new ConcurrentStack<Action<T>>(); private readonly ConcurrentStack<Action<Exception>> _errbacks = new ConcurrentStack<Action<Exception>>(); private readonly object _lock = new object(); public Future(Func<T> f) { _f = f; Monitor.Enter(_lock); ThreadPool.QueueUserWorkItem(Run); } public void OnSuccess(Action<T> a) { _callbacks.Push(a); if (_complete && _success) a(_result); } public void OnError(Action<Exception> a) { _errbacks.Push(a); if (_complete && !_success) a(_error); } private void Run(object state) { try { _result = _f(); _success = true; _complete = true; foreach (var cb in _callbacks) { cb(_result); } } catch (Exception e) { _error = e; _complete = true; foreach (var cb in _errbacks) { cb(e); } } finally { Monitor.Exit(_lock); } } public T Result { get { if (!_complete) { Monitor.Enter(_lock); } if (_success) { return _result; } else { Console.WriteLine("Throwing error complete={0} success={1}", _complete, _success); throw _error; } } } // Failing test public void TestResultSuccess() { var f = new Future<int>(() => 1); var x = f.Result; Assert.AreEqual (1, x); } ``` I'm using Mono 3.2.3 on Mac OS X 10.9.