-
Notifications
You must be signed in to change notification settings - Fork 14.4k
[MLIR:Python] Fix race on PyOperations. #139721
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -83,7 +83,7 @@ class PyObjectRef { | |
} | ||
|
||
T *get() { return referrent; } | ||
T *operator->() { | ||
T *operator->() const { | ||
assert(referrent && object); | ||
return referrent; | ||
} | ||
|
@@ -229,7 +229,7 @@ class PyMlirContext { | |
static size_t getLiveCount(); | ||
|
||
/// Get a list of Python objects which are still in the live context map. | ||
std::vector<PyOperation *> getLiveOperationObjects(); | ||
std::vector<nanobind::object> getLiveOperationObjects(); | ||
|
||
/// Gets the count of live operations associated with this context. | ||
/// Used for testing. | ||
|
@@ -254,9 +254,10 @@ class PyMlirContext { | |
void clearOperationsInside(PyOperationBase &op); | ||
void clearOperationsInside(MlirOperation op); | ||
|
||
/// Clears the operaiton _and_ all operations inside using | ||
/// `clearOperation(MlirOperation)`. | ||
void clearOperationAndInside(PyOperationBase &op); | ||
/// Clears the operation _and_ all operations inside using | ||
/// `clearOperation(MlirOperation)`. Requires that liveOperations mutex is | ||
/// held. | ||
void clearOperationAndInsideLocked(PyOperationBase &op); | ||
|
||
/// Gets the count of live modules associated with this context. | ||
/// Used for testing. | ||
|
@@ -278,6 +279,9 @@ class PyMlirContext { | |
struct ErrorCapture; | ||
|
||
private: | ||
// Similar to clearOperation, but requires the liveOperations mutex to be held | ||
void clearOperationLocked(MlirOperation op); | ||
|
||
// Interns the mapping of live MlirContext::ptr to PyMlirContext instances, | ||
// preserving the relationship that an MlirContext maps to a single | ||
// PyMlirContext wrapper. This could be replaced in the future with an | ||
|
@@ -302,6 +306,9 @@ class PyMlirContext { | |
// attempt to access it will raise an error. | ||
using LiveOperationMap = | ||
llvm::DenseMap<void *, std::pair<nanobind::handle, PyOperation *>>; | ||
|
||
// liveOperationsMutex guards both liveOperations and the valid field of | ||
// PyOperation objects in free-threading mode. | ||
nanobind::ft_mutex liveOperationsMutex; | ||
|
||
// Guarded by liveOperationsMutex in free-threading mode. | ||
|
@@ -336,6 +343,7 @@ class BaseContextObject { | |
} | ||
|
||
/// Accesses the context reference. | ||
const PyMlirContextRef &getContext() const { return contextRef; } | ||
PyMlirContextRef &getContext() { return contextRef; } | ||
|
||
private: | ||
|
@@ -677,6 +685,10 @@ class PyOperation : public PyOperationBase, public BaseContextObject { | |
checkValid(); | ||
return operation; | ||
} | ||
MlirOperation getLocked() const { | ||
checkValidLocked(); | ||
return operation; | ||
} | ||
|
||
PyOperationRef getRef() { | ||
return PyOperationRef(this, nanobind::borrow<nanobind::object>(handle)); | ||
|
@@ -692,6 +704,7 @@ class PyOperation : public PyOperationBase, public BaseContextObject { | |
attached = false; | ||
} | ||
void checkValid() const; | ||
void checkValidLocked() const; | ||
|
||
/// Gets the owning block or raises an exception if the operation has no | ||
/// owning block. | ||
|
@@ -725,19 +738,27 @@ class PyOperation : public PyOperationBase, public BaseContextObject { | |
/// parent context's live operations map, and sets the valid bit false. | ||
void erase(); | ||
|
||
/// Invalidate the operation. | ||
void setInvalid() { valid = false; } | ||
|
||
/// Clones this operation. | ||
nanobind::object clone(const nanobind::object &ip); | ||
|
||
/// Invalidate the operation. | ||
void setInvalid() { | ||
nanobind::ft_lock_guard lock(getContext()->liveOperationsMutex); | ||
setInvalidLocked(); | ||
} | ||
/// Like setInvalid(), but requires the liveOperations mutex to be held. | ||
void setInvalidLocked() { valid = false; } | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. dumb question: is there a way to put a runtime assert/check here that the mutex is actually held? so that there's some way for people that don't read the doc strings (...like me...) to save themselves via compiling with asserts. e.g. i'm wondering if if that's too tedious/onerous a change than i propose we rename the method to something like There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That would be nice, wouldn't it. Sadly It would probably be possible for nanobind to clone that code, much as we're doing for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I should add: there's no particular reason we have to use a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
seems special enough to me. we can file this as a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. whoops should've read everything first - guess you already did rename things. cool |
||
|
||
PyOperation(PyMlirContextRef contextRef, MlirOperation operation); | ||
|
||
private: | ||
static PyOperationRef createInstance(PyMlirContextRef contextRef, | ||
MlirOperation operation, | ||
nanobind::object parentKeepAlive); | ||
|
||
// Like erase(), but requires the caller to hold the liveOperationsMutex. | ||
void eraseLocked(); | ||
|
||
MlirOperation operation; | ||
nanobind::handle handle; | ||
// Keeps the parent alive, regardless of whether it is an Operation or | ||
|
@@ -748,6 +769,9 @@ class PyOperation : public PyOperationBase, public BaseContextObject { | |
// ir_operation.py regarding testing corresponding lifetime guarantees. | ||
nanobind::object parentKeepAlive; | ||
bool attached = true; | ||
|
||
// Guarded by 'context->liveOperationsMutex'. Valid objects must be present | ||
// in context->liveOperations. | ||
bool valid = true; | ||
|
||
friend class PyOperationBase; | ||
|
Uh oh!
There was an error while loading. Please reload this page.