Protect the rtree/extent interactions with a mutex pool.

Instead of embedding a lock bit in rtree leaf elements, we associate extents
with a small set of mutexes.  This gets us two things:

- We can use the system mutexes.  This (hypothetically) protects us from
  priority inversion, and lets us stop doing a backoff/sleep loop, instead
  opting for precise wakeups from the mutex.
- Cuts down on the number of mutex acquisitions we have to do (from 4 in the
  worst case to two).

We end up simplifying most of the rtree code (which no longer has to deal with
locking or concurrency at all), at the cost of additional complexity in the
extent code: since the mutex protecting the rtree leaf elements is determined by
reading the extent out of those elements, the initial read is racy, so that we
may acquire an out of date mutex.  We re-check the extent in the leaf after
acquiring the mutex to protect us from this race.
This commit is contained in:
David Goldblatt
2017-05-15 14:23:51 -07:00
committed by David Goldblatt
parent 26c792e61a
commit 3f685e8824
18 changed files with 341 additions and 537 deletions

View File

@@ -304,121 +304,6 @@ rtree_leaf_elm_lookup_hard(tsdn_t *tsdn, rtree_t *rtree, rtree_ctx_t *rtree_ctx,
not_reached();
}
static int
rtree_leaf_elm_witness_comp(const witness_t *a, void *oa, const witness_t *b,
void *ob) {
uintptr_t ka = (uintptr_t)oa;
uintptr_t kb = (uintptr_t)ob;
assert(ka != 0);
assert(kb != 0);
return (ka > kb) - (ka < kb);
}
static witness_t *
rtree_leaf_elm_witness_alloc(tsd_t *tsd, uintptr_t key,
const rtree_leaf_elm_t *elm) {
witness_t *witness;
size_t i;
rtree_leaf_elm_witness_tsd_t *witnesses =
tsd_rtree_leaf_elm_witnessesp_get(tsd);
/* Iterate over entire array to detect double allocation attempts. */
witness = NULL;
for (i = 0; i < RTREE_ELM_ACQUIRE_MAX; i++) {
rtree_leaf_elm_witness_t *rew = &witnesses->witnesses[i];
assert(rew->elm != elm);
if (rew->elm == NULL && witness == NULL) {
rew->elm = elm;
witness = &rew->witness;
witness_init(witness, "rtree_leaf_elm",
WITNESS_RANK_RTREE_ELM, rtree_leaf_elm_witness_comp,
(void *)key);
}
}
assert(witness != NULL);
return witness;
}
static witness_t *
rtree_leaf_elm_witness_find(tsd_t *tsd, const rtree_leaf_elm_t *elm) {
size_t i;
rtree_leaf_elm_witness_tsd_t *witnesses =
tsd_rtree_leaf_elm_witnessesp_get(tsd);
for (i = 0; i < RTREE_ELM_ACQUIRE_MAX; i++) {
rtree_leaf_elm_witness_t *rew = &witnesses->witnesses[i];
if (rew->elm == elm) {
return &rew->witness;
}
}
not_reached();
}
static void
rtree_leaf_elm_witness_dalloc(tsd_t *tsd, witness_t *witness,
const rtree_leaf_elm_t *elm) {
size_t i;
rtree_leaf_elm_witness_tsd_t *witnesses =
tsd_rtree_leaf_elm_witnessesp_get(tsd);
for (i = 0; i < RTREE_ELM_ACQUIRE_MAX; i++) {
rtree_leaf_elm_witness_t *rew = &witnesses->witnesses[i];
if (rew->elm == elm) {
rew->elm = NULL;
witness_init(&rew->witness, "rtree_leaf_elm",
WITNESS_RANK_RTREE_ELM, rtree_leaf_elm_witness_comp,
NULL);
return;
}
}
not_reached();
}
void
rtree_leaf_elm_witness_acquire(tsdn_t *tsdn, const rtree_t *rtree,
uintptr_t key, const rtree_leaf_elm_t *elm) {
witness_t *witness;
if (tsdn_null(tsdn)) {
return;
}
witness = rtree_leaf_elm_witness_alloc(tsdn_tsd(tsdn), key, elm);
witness_lock(tsdn, witness);
}
void
rtree_leaf_elm_witness_access(tsdn_t *tsdn, const rtree_t *rtree,
const rtree_leaf_elm_t *elm) {
witness_t *witness;
if (tsdn_null(tsdn)) {
return;
}
witness = rtree_leaf_elm_witness_find(tsdn_tsd(tsdn), elm);
witness_assert_owner(tsdn, witness);
}
void
rtree_leaf_elm_witness_release(tsdn_t *tsdn, const rtree_t *rtree,
const rtree_leaf_elm_t *elm) {
witness_t *witness;
if (tsdn_null(tsdn)) {
return;
}
witness = rtree_leaf_elm_witness_find(tsdn_tsd(tsdn), elm);
witness_unlock(tsdn, witness);
rtree_leaf_elm_witness_dalloc(tsdn_tsd(tsdn), witness, elm);
}
void
rtree_ctx_data_init(rtree_ctx_t *ctx) {
for (unsigned i = 0; i < RTREE_CTX_NCACHE; i++) {