pipeline: give Mutex and ReMutex more Pythonic semantics

This allows using mutices in with-blocks and wraps up the functionality of acquire() and try_acquire() into a single acquire(blocking=True).

Furthermore, the GIL is no longer released in cases of no contention.
This commit is contained in:
rdb 2019-05-12 19:53:27 +02:00
parent 2e9bd0f241
commit c4a01ac564
12 changed files with 235 additions and 29 deletions

View File

@ -201,17 +201,6 @@ class Lock(core.Mutex):
def __init__(self, name = "PythonLock"): def __init__(self, name = "PythonLock"):
core.Mutex.__init__(self, name) core.Mutex.__init__(self, name)
def acquire(self, blocking = True):
if blocking:
core.Mutex.acquire(self)
return True
else:
return core.Mutex.tryAcquire(self)
__enter__ = acquire
def __exit__(self, t, v, tb):
self.release()
class RLock(core.ReMutex): class RLock(core.ReMutex):
""" This class provides a wrapper around Panda's ReMutex object. """ This class provides a wrapper around Panda's ReMutex object.
@ -221,18 +210,6 @@ class RLock(core.ReMutex):
def __init__(self, name = "PythonRLock"): def __init__(self, name = "PythonRLock"):
core.ReMutex.__init__(self, name) core.ReMutex.__init__(self, name)
def acquire(self, blocking = True):
if blocking:
core.ReMutex.acquire(self)
return True
else:
return core.ReMutex.tryAcquire(self)
__enter__ = acquire
def __exit__(self, t, v, tb):
self.release()
class Condition(core.ConditionVarFull): class Condition(core.ConditionVarFull):
""" This class provides a wrapper around Panda's ConditionVarFull """ This class provides a wrapper around Panda's ConditionVarFull

View File

@ -70,6 +70,8 @@ acquire(Thread *current_thread) const {
/** /**
* Returns immediately, with a true value indicating the mutex has been * Returns immediately, with a true value indicating the mutex has been
* acquired, and false indicating it has not. * acquired, and false indicating it has not.
*
* @deprecated Python users should use acquire(False), C++ users try_lock()
*/ */
INLINE bool MutexDebug:: INLINE bool MutexDebug::
try_acquire(Thread *current_thread) const { try_acquire(Thread *current_thread) const {

View File

@ -60,6 +60,8 @@ acquire() const {
/** /**
* Returns immediately, with a true value indicating the mutex has been * Returns immediately, with a true value indicating the mutex has been
* acquired, and false indicating it has not. * acquired, and false indicating it has not.
*
* @deprecated Python users should use acquire(False), C++ users try_lock()
*/ */
INLINE bool MutexDirect:: INLINE bool MutexDirect::
try_acquire() const { try_acquire() const {

View File

@ -49,6 +49,10 @@ PUBLISHED:
void operator = (const Mutex &copy) = delete; void operator = (const Mutex &copy) = delete;
EXTENSION(bool acquire(bool blocking=true) const);
EXTENSION(bool __enter__());
EXTENSION(void __exit__(PyObject *, PyObject *, PyObject *));
public: public:
// This is a global mutex set aside for the purpose of protecting Notify // This is a global mutex set aside for the purpose of protecting Notify
// messages from being interleaved between threads. // messages from being interleaved between threads.

View File

@ -0,0 +1,53 @@
/**
* PANDA 3D SOFTWARE
* Copyright (c) Carnegie Mellon University. All rights reserved.
*
* All use of this software is subject to the terms of the revised BSD
* license. You should have received a copy of this license along
* with this source code in a file named "LICENSE."
*
* @file pmutex_ext.h
* @author rdb
* @date 2019-05-12
*/
/**
* Acquires the mutex.
*/
INLINE bool Extension<Mutex>::
acquire(bool blocking) const {
if (_this->try_lock()) {
return true;
}
if (!blocking) {
return false;
}
// Release the GIL while we are waiting for the lock.
#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
PyThreadState *_save;
Py_UNBLOCK_THREADS
_this->lock();
Py_BLOCK_THREADS
#else
_this->lock();
#endif
return true;
}
/**
* Acquires the mutex.
*/
INLINE bool Extension<Mutex>::
__enter__() {
return acquire(true);
}
/**
* Releases the mutex.
*/
INLINE void Extension<Mutex>::
__exit__(PyObject *, PyObject *, PyObject *) {
_this->unlock();
}

View File

@ -0,0 +1,41 @@
/**
* PANDA 3D SOFTWARE
* Copyright (c) Carnegie Mellon University. All rights reserved.
*
* All use of this software is subject to the terms of the revised BSD
* license. You should have received a copy of this license along
* with this source code in a file named "LICENSE."
*
* @file pmutex_ext.h
* @author rdb
* @date 2019-05-12
*/
#ifndef PMUTEX_EXT_H
#define PMUTEX_EXT_H
#include "dtoolbase.h"
#ifdef HAVE_PYTHON
#include "extension.h"
#include "pmutex.h"
#include "py_panda.h"
/**
* This class defines the extension methods for Mutex, which are called
* instead of any C++ methods with the same prototype.
*/
template<>
class Extension<Mutex> : public ExtensionBase<Mutex> {
public:
INLINE bool acquire(bool blocking) const;
INLINE bool __enter__();
INLINE void __exit__(PyObject *, PyObject *, PyObject *);
};
#include "pmutex_ext.I"
#endif // HAVE_PYTHON
#endif // PMUTEX_EXT_H

View File

@ -42,6 +42,10 @@ PUBLISHED:
~ReMutex() = default; ~ReMutex() = default;
void operator = (const ReMutex &copy) = delete; void operator = (const ReMutex &copy) = delete;
EXTENSION(bool acquire(bool blocking=true) const);
EXTENSION(bool __enter__());
EXTENSION(void __exit__(PyObject *, PyObject *, PyObject *));
}; };
#include "reMutex.I" #include "reMutex.I"

View File

@ -105,6 +105,8 @@ acquire(Thread *current_thread) const {
/** /**
* Returns immediately, with a true value indicating the mutex has been * Returns immediately, with a true value indicating the mutex has been
* acquired, and false indicating it has not. * acquired, and false indicating it has not.
*
* @deprecated Python users should use acquire(False), C++ users try_lock()
*/ */
INLINE bool ReMutexDirect:: INLINE bool ReMutexDirect::
try_acquire() const { try_acquire() const {
@ -119,6 +121,8 @@ try_acquire() const {
/** /**
* Returns immediately, with a true value indicating the mutex has been * Returns immediately, with a true value indicating the mutex has been
* acquired, and false indicating it has not. * acquired, and false indicating it has not.
*
* @deprecated Python users should use acquire(False), C++ users try_lock()
*/ */
INLINE bool ReMutexDirect:: INLINE bool ReMutexDirect::
try_acquire(Thread *current_thread) const { try_acquire(Thread *current_thread) const {

View File

@ -0,0 +1,53 @@
/**
* PANDA 3D SOFTWARE
* Copyright (c) Carnegie Mellon University. All rights reserved.
*
* All use of this software is subject to the terms of the revised BSD
* license. You should have received a copy of this license along
* with this source code in a file named "LICENSE."
*
* @file pmutex_ext.h
* @author rdb
* @date 2019-05-12
*/
/**
* Acquires the mutex.
*/
INLINE bool Extension<ReMutex>::
acquire(bool blocking) const {
if (_this->try_lock()) {
return true;
}
if (!blocking) {
return false;
}
// Release the GIL while we are waiting for the lock.
#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
PyThreadState *_save;
Py_UNBLOCK_THREADS
_this->lock();
Py_BLOCK_THREADS
#else
_this->lock();
#endif
return true;
}
/**
* Acquires the mutex.
*/
INLINE bool Extension<ReMutex>::
__enter__() {
return acquire(true);
}
/**
* Releases the mutex.
*/
INLINE void Extension<ReMutex>::
__exit__(PyObject *, PyObject *, PyObject *) {
_this->unlock();
}

View File

@ -0,0 +1,41 @@
/**
* PANDA 3D SOFTWARE
* Copyright (c) Carnegie Mellon University. All rights reserved.
*
* All use of this software is subject to the terms of the revised BSD
* license. You should have received a copy of this license along
* with this source code in a file named "LICENSE."
*
* @file remutex_ext.h
* @author rdb
* @date 2019-05-12
*/
#ifndef REMUTEX_EXT_H
#define REMUTEX_EXT_H
#include "dtoolbase.h"
#ifdef HAVE_PYTHON
#include "extension.h"
#include "reMutex.h"
#include "py_panda.h"
/**
* This class defines the extension methods for ReMutex, which are called
* instead of any C++ methods with the same prototype.
*/
template<>
class Extension<ReMutex> : public ExtensionBase<ReMutex> {
public:
INLINE bool acquire(bool blocking) const;
INLINE bool __enter__();
INLINE void __exit__(PyObject *, PyObject *, PyObject *);
};
#include "reMutex_ext.I"
#endif // HAVE_PYTHON
#endif // REMUTEX_EXT_H

View File

@ -26,13 +26,12 @@ def test_cvar_notify_locked():
m = Mutex() m = Mutex()
cv = ConditionVarFull(m) cv = ConditionVarFull(m)
m.acquire() with m:
cv.notify() cv.notify()
m.release()
with m:
cv.notify_all()
m.acquire()
cv.notify_all()
m.release()
del cv del cv

View File

@ -2,6 +2,7 @@ from panda3d.core import Mutex, ReMutex
from panda3d import core from panda3d import core
from random import random from random import random
import pytest import pytest
import sys
def test_mutex_acquire_release(): def test_mutex_acquire_release():
@ -34,6 +35,19 @@ def test_mutex_try_acquire():
m.release() m.release()
def test_mutex_with():
m = Mutex()
rc = sys.getrefcount(m)
with m:
assert m.debug_is_locked()
with m:
assert m.debug_is_locked()
assert rc == sys.getrefcount(m)
@pytest.mark.skipif(not core.Thread.is_threading_supported(), @pytest.mark.skipif(not core.Thread.is_threading_supported(),
reason="Threading support disabled") reason="Threading support disabled")
def test_mutex_contention(): def test_mutex_contention():
@ -124,3 +138,15 @@ def test_remutex_try_acquire():
m.release() m.release()
m.release() m.release()
def test_remutex_with():
m = ReMutex()
rc = sys.getrefcount(m)
with m:
assert m.debug_is_locked()
with m:
assert m.debug_is_locked()
assert m.debug_is_locked()
assert rc == sys.getrefcount(m)