diff --git a/db/deferredinvoker.h b/db/deferredinvoker.h index cad7d7d0673..e6ba4951889 100644 --- a/db/deferredinvoker.h +++ b/db/deferredinvoker.h @@ -41,7 +41,7 @@ namespace mongo { template< class V > class DeferredInvoker { public: - DeferredInvoker() { + DeferredInvoker() : _invokeMutex("deferredinvoker") { _which = 0; } @@ -55,9 +55,9 @@ namespace mongo { /** call to process pending invocations. - concurrency: only one thread at a time may call invoke(). however other threads can call defer() - simultaneously. - + concurrency: handled herein. multiple threads could call invoke(), but their efforts will be + serialized. the common case is that there is a single processor of invocations. + normally, you call this outside of any lock. but if you want to fully drain the queue, call from within a read lock. a good way to drain : { @@ -69,16 +69,19 @@ namespace mongo { } */ void invoke() { - int full = _which; - int empty = _which ^ 1; - - // flip { + // flip defer to the other queue readlock lk; - assert( _queues[empty].empty() ); // queue-ers only touch the active queue, not the other one - _which = empty; + mutex::scoped_lock lk2(_invokeMutex); + int cur = _which; + int other = _which ^ 1; + if( _queues[other].empty() ) + _which = other; + } + { + mutex::scoped_lock lk(_invokeMutex); + _drain( _queues[_which^1] ); } - _drain( _queues[full] ); } private: @@ -86,6 +89,9 @@ namespace mongo { typedef vector Queue; Queue _queues[2]; + // lock order when multiple locks: dbMutex, _invokeMutex + mongo::mutex _invokeMutex; + void _drain(Queue& queue) { unsigned oldCap = queue.capacity(); for( Queue::iterator i = queue.begin(); i != queue.end(); i++ ) {