|
Wildmeshing Toolkit
|
A per-vertex lock plus the id of the thread currently holding it. More...
#include <vertex_mutex.hpp>
Public Member Functions | |
| VertexMutex (const VertexMutex &) noexcept | |
| VertexMutex (VertexMutex &&) noexcept | |
| VertexMutex & | operator= (const VertexMutex &) noexcept |
| VertexMutex & | operator= (VertexMutex &&) noexcept |
| bool | trylock () |
| void | unlock () |
| int | get_owner () const |
| void | set_owner (int n) |
| void | reset_owner () |
Static Public Member Functions | |
| static constexpr int | no_owner () |
| Sentinel stored in the owner field when no thread holds the vertex. | |
Private Attributes | |
| spin_mutex | m_mutex |
| std::atomic< int > | m_owner {no_owner()} |
A per-vertex lock plus the id of the thread currently holding it.
One of these exists per vertex in TriMesh and TetMesh. Before running a topology-changing operation a thread claims every vertex in the operation's one- or two-ring; on any failure it releases what it has taken and the operation is retried later. spin_mutex provides the mutual exclusion; the owner field exists only so that the ring walks can tell "already mine" from "someone else's".
That distinction is load-bearing because spin_mutex is not recursive. A two-ring walk visits the same vertex many times (a vertex is in the one-ring of several of its neighbours), so without the owner check the walk would try to re-lock vertices it already holds, fail, and abort an operation that should have succeeded.
VertexMutex in both TriMesh.h and TetMesh.h. The two copies drifted – TriMesh's unlock() cleared the owner before releasing the mutex and TetMesh's did it after, which is a bug (see below) – so they are unified here.
|
inlinenoexcept |
Copy and move reset to unlocked-and-unowned rather than propagating state, mirroring spin_mutex. These exist only so a std::vector<VertexMutex> can be grown by resize_vertex_mutex(), which happens single-threaded during mesh (re)initialization when nothing is held. std::atomic is neither copyable nor movable, so without these the vector would not compile.
|
inline |
Read the owning thread id, or no_owner().
Deliberately callable without holding the lock – that is precisely how the ring walks use it – which is why the field has to be atomic rather than a plain int.
Relaxed ordering is sufficient, and the reasoning is worth keeping because the field looks like it wants an acquire/release pair and does not need one:
no_owner(), falls through to trylock(), and the spin_mutex's acquire/release pair is the real arbiter. A stale answer there costs one wasted attempt, never correctness.The mutual exclusion and the memory visibility of everything the operation touches come from spin_mutex, not from this field.