commit - ba140128df35d91f4de66435f4e72fa777fbb885
commit + 9da70207aa5c8a2e8dca78ce34a425b18e4711bc
blob - 146027757b6bbc2aefe3b08a03bfe6ff2145a4b0
blob + 77b39af248b692bb60cef3e9acefbf257765e7fd
--- perf/tuple.cc
+++ perf/tuple.cc
i = 0;
}
struct tuple *t = tuples[i++];
- benchmark::DoNotOptimize(bool(t->is_dirty));
+ // Previously tuple had is_dirty bit field, which was later
+ // replaced with tuple flags. So to avoid changing test
+ // semantics we check now if tuple has corresponding flag.
+ benchmark::DoNotOptimize(tuple_has_flag(t, TUPLE_IS_DIRTY));
benchmark::DoNotOptimize(uint16_t(t->format_id));
}
total_count += i;
blob - 499cdf43ffd579517f5c7dc75627fe5e5dcb3f76
blob + f1356a29d17a8885da7b9df65d82986ef730b530
--- src/box/memtx_tx.c
+++ src/box/memtx_tx.c
bool tuple_is_referenced_to_pk)
{
txm.must_do_gc_steps += TX_MANAGER_GC_STEPS_SIZE;
- assert(!tuple->is_dirty);
+ assert(!tuple_has_flag(tuple, TUPLE_IS_DIRTY));
uint32_t index_count = space->index_count;
assert(index_count < BOX_INDEX_MAX);
struct mempool *pool = &txm.memtx_tx_story_pool[index_count];
(const struct memtx_story **) &story;
struct memtx_story **empty = NULL;
mh_history_put(txm.history, put_story, &empty, 0);
- tuple->is_dirty = true;
+ tuple_set_flag(tuple, TUPLE_IS_DIRTY);
tuple_ref(tuple);
story->status = MEMTX_TX_STORY_USED;
struct memtx_tx_stats *stats = &txm.story_stats[story->status];
assert(pos != mh_end(txm.history));
mh_history_del(txm.history, pos, 0);
- story->tuple->is_dirty = false;
+ tuple_clear_flag(story->tuple, TUPLE_IS_DIRTY);
tuple_unref(story->tuple);
#ifndef NDEBUG
static struct memtx_story *
memtx_tx_story_get(struct tuple *tuple)
{
- assert(tuple->is_dirty);
+ assert(tuple_has_flag(tuple, TUPLE_IS_DIRTY));
mh_int_t pos = mh_history_find(txm.history, tuple, 0);
assert(pos != mh_end(txm.history));
enum dup_replace_mode mode,
struct memtx_tx_conflict **collected_conflicts)
{
- assert(replaced[0] == NULL || !replaced[0]->is_dirty);
+ assert(replaced[0] == NULL ||
+ !tuple_has_flag(replaced[0], TUPLE_IS_DIRTY));
struct space *space = stmt->space;
struct txn *txn = stmt->txn;
collected_conflicts);
continue;
}
- if (!replaced[i]->is_dirty) {
+ if (!tuple_has_flag(replaced[i], TUPLE_IS_DIRTY)) {
/* Check like there's no mvcc. */
if (memtx_tx_check_dup(new_tuple, replaced[0],
replaced[i], DUP_INSERT,
enum dup_replace_mode mode,
struct memtx_tx_conflict **collected_conflicts)
{
- assert(replaced[0] != NULL && replaced[0]->is_dirty);
+ assert(replaced[0] != NULL &&
+ tuple_has_flag(replaced[0], TUPLE_IS_DIRTY));
struct space *space = stmt->space;
struct txn *txn = stmt->txn;
collected_conflicts);
continue;
}
- if (!replaced[i]->is_dirty) {
+ if (!tuple_has_flag(replaced[i], TUPLE_IS_DIRTY)) {
/*
* Non-null clean tuple cannot be NULL or
* visible_replaced since visible_replaced is dirty.
struct memtx_tx_conflict **collected_conflicts)
{
struct tuple *replaced = directly_replaced[0];
- if (replaced == NULL || !replaced->is_dirty)
+ if (replaced == NULL || !tuple_has_flag(replaced, TUPLE_IS_DIRTY))
return check_dup_clean(stmt, new_tuple, directly_replaced,
old_tuple, mode,
collected_conflicts);
memtx_tx_cause_conflict(txn, fsc_item->txn) != 0)
return -1;
}
- if (successor != NULL && !successor->is_dirty)
+ if (successor != NULL && !tuple_has_flag(successor, TUPLE_IS_DIRTY))
return 0; /* no gap records */
struct rlist *list = &index->nearby_gaps;
if (successor != NULL) {
- assert(successor->is_dirty);
+ assert(tuple_has_flag(successor, TUPLE_IS_DIRTY));
struct memtx_story *succ_story = memtx_tx_story_get(successor);
assert(ind < succ_story->index_count);
list = &succ_story->link[ind].nearby_gaps;
goto fail;
memtx_tx_story_link_added_by(add_story, stmt);
- if (replaced != NULL && !replaced->is_dirty) {
+ if (replaced != NULL && !tuple_has_flag(replaced, TUPLE_IS_DIRTY)) {
/*
* Note that despite the tuple is not in pk,
* it is referenced to it, so we pass true as the last argument.
goto fail;
continue;
}
- assert(directly_replaced[i]->is_dirty);
+ assert(tuple_has_flag(directly_replaced[i], TUPLE_IS_DIRTY));
struct memtx_story *secondary_replaced =
memtx_tx_story_get(directly_replaced[i]);
memtx_tx_story_link_top_light(add_story, secondary_replaced, i);
}
if (old_tuple != NULL) {
- assert(old_tuple->is_dirty);
+ assert(tuple_has_flag(old_tuple, TUPLE_IS_DIRTY));
struct memtx_story *del_story = NULL;
if (old_tuple == replaced)
struct space *space = stmt->space;
struct memtx_story *del_story;
- if (old_tuple->is_dirty) {
+ if (tuple_has_flag(old_tuple, TUPLE_IS_DIRTY)) {
del_story = memtx_tx_story_get(old_tuple);
} else {
assert(stmt->txn != NULL);
assert(stmt != NULL);
assert(stmt->space != NULL);
assert(new_tuple != NULL || old_tuple != NULL);
- assert(new_tuple == NULL || !new_tuple->is_dirty);
+ assert(new_tuple == NULL || !tuple_has_flag(new_tuple, TUPLE_IS_DIRTY));
if (new_tuple != NULL)
return memtx_tx_history_add_insert_stmt(stmt, old_tuple,
struct tuple *tuple, struct index *index,
uint32_t mk_index, bool is_prepared_ok)
{
- assert(tuple->is_dirty);
+ assert(tuple_has_flag(tuple, TUPLE_IS_DIRTY));
struct memtx_story *story = memtx_tx_story_get(tuple);
bool own_change = false;
struct tuple *result = NULL;
if (space->def->opts.is_ephemeral)
return 0;
- if (tuple->is_dirty) {
+ if (tuple_has_flag(tuple, TUPLE_IS_DIRTY)) {
struct memtx_story *story = memtx_tx_story_get(tuple);
return memtx_tx_track_read_story(txn, space, story, UINT64_MAX);
} else {
if (successor != NULL) {
struct memtx_story *story;
- if (successor->is_dirty) {
+ if (tuple_has_flag(successor, TUPLE_IS_DIRTY)) {
story = memtx_tx_story_get(successor);
} else {
/*
blob - dc06fcc9972e865e597437f704c9efb5e4b1275d
blob + 1ca14ebc7b1f2929e9a94f2ed306c311075aa6e8
--- src/box/memtx_tx.h
+++ src/box/memtx_tx.h
{
if (!memtx_tx_manager_use_mvcc_engine)
return tuple;
- if (!tuple->is_dirty) {
+ if (!tuple_has_flag(tuple, TUPLE_IS_DIRTY)) {
memtx_tx_track_read(txn, space, tuple);
return tuple;
}
blob - 60e008a5b1133b4e33d73638dca50d86c804d28e
blob + 6c2e89ebc0c78aa6600bb32e7e61f7c5412f93db
--- src/box/tuple.c
+++ src/box/tuple.c
assert(format->vtab.tuple_delete == tuple_format_runtime_vtab.tuple_delete);
say_debug("%s(%p)", __func__, tuple);
assert(tuple->local_refs == 0);
- assert(!tuple->has_uploaded_refs);
+ assert(!tuple_has_flag(tuple, TUPLE_HAS_UPLOADED_REFS));
size_t total = tuple_size(tuple);
tuple_format_unref(format);
smfree(&runtime_alloc, tuple, total);
static uint32_t
tuple_ref_get_uploaded_refs(struct tuple *tuple)
{
- if (!tuple->has_uploaded_refs)
+ if (!tuple_has_flag(tuple, TUPLE_HAS_UPLOADED_REFS))
return 0;
mh_int_t pos = mh_tuple_uploaded_refs_find(tuple_uploaded_refs, tuple,
0);
static void
tuple_ref_drop_uploaded_refs(struct tuple *tuple)
{
- assert(tuple->has_uploaded_refs);
+ assert(tuple_has_flag(tuple, TUPLE_HAS_UPLOADED_REFS));
mh_int_t pos = mh_tuple_uploaded_refs_find(tuple_uploaded_refs, tuple,
0);
assert(pos != mh_end(tuple_uploaded_refs));
assert(tuple->local_refs == TUPLE_LOCAL_REF_MAX);
uint32_t refs = tuple_ref_get_uploaded_refs(tuple);
tuple_ref_set_uploaded_refs(tuple, refs + TUPLE_UPLOAD_REFS);
- tuple->has_uploaded_refs = true;
+ tuple_set_flag(tuple, TUPLE_HAS_UPLOADED_REFS);
tuple->local_refs -= TUPLE_UPLOAD_REFS;
}
tuple_acquire_refs(struct tuple *tuple)
{
assert(tuple->local_refs == 0);
- assert(tuple->has_uploaded_refs);
+ assert(tuple_has_flag(tuple, TUPLE_HAS_UPLOADED_REFS));
uint32_t refs = tuple_ref_get_uploaded_refs(tuple);
if (refs == TUPLE_UPLOAD_REFS) {
tuple_ref_drop_uploaded_refs(tuple);
- tuple->has_uploaded_refs = false;
+ tuple_clear_flag(tuple, TUPLE_HAS_UPLOADED_REFS);
} else {
tuple_ref_set_uploaded_refs(tuple, refs - TUPLE_UPLOAD_REFS);
}
blob - 1bedac77dad1f70ee3893cba9e8436f5cd113af0
blob + 4c5c99de0f64753068e8cbc940190ad3303ad406
--- src/box/tuple.h
+++ src/box/tuple.h
box_tuple_validate(box_tuple_t *tuple, box_tuple_format_t *format);
/** \endcond public */
+
+/**
+ * Tuple flags. Each value is a position of corresponding bit in
+ * tuple->flags member.
+ */
+enum tuple_flag {
+ /**
+ * Flag that shows that the tuple has more references in separate
+ * external storage, see @sa tuple_upload_refs and tuple_acquire_refs.
+ * For private use only.
+ */
+ TUPLE_HAS_UPLOADED_REFS = 0,
+ /**
+ * The tuple (if it's found in index for example) could be invisible
+ * for current transactions. The flag means that the tuple must
+ * be clarified by transaction engine.
+ */
+ TUPLE_IS_DIRTY = 1,
+ tuple_flag_MAX,
+};
/**
* An atom of Tarantool storage. Represents MsgPack Array.
* tuple_ref_init, tuple_ref, tuple_unref instead.
*/
uint8_t local_refs;
- /**
- * Flag that shows that the tuple has more references in separate
- * external storage, see @sa tuple_upload_refs and tuple_acquire_refs.
- * For private use only.
- */
- bool has_uploaded_refs : 1;
- /**
- * The tuple (if it's found in index for example) could be invisible
- * for current transactions. The flag means that the tuple must
- * be clarified by transaction engine.
- */
- bool is_dirty : 1;
+ /** Tuple flags (e.g. see TUPLE_HAS_UPLOADED_REFS). */
+ uint8_t flags;
/** Format identifier. */
uint16_t format_id;
/**
};
static_assert(sizeof(struct tuple) == 10, "Just to be sure");
+
+static_assert(DIV_ROUND_UP(tuple_flag_MAX, 8) <=
+ sizeof(((struct tuple *)0)->flags),
+ "enum tuple_flag doesn't fit into tuple->flags");
+/** Set flag of the tuple. */
+static inline void
+tuple_set_flag(struct tuple *tuple, enum tuple_flag flag)
+{
+ assert(flag < tuple_flag_MAX);
+ tuple->flags |= (1 << flag);
+}
+
+/** Test if tuple has a flag. */
+static inline bool
+tuple_has_flag(struct tuple *tuple, enum tuple_flag flag)
+{
+ assert(flag < tuple_flag_MAX);
+ return (tuple->flags & (1 << flag)) != 0;
+}
+
+/** Clears tuple flag. */
+static inline void
+tuple_clear_flag(struct tuple *tuple, enum tuple_flag flag)
+{
+ assert(flag < tuple_flag_MAX);
+ tuple->flags &= ~(1 << flag);
+}
+
enum {
/** Maximal value of tuple::local_refs. */
TUPLE_LOCAL_REF_MAX = UINT8_MAX,
{
assert(data_offset <= INT16_MAX);
tuple->local_refs = refs;
- tuple->has_uploaded_refs = false;
- tuple->is_dirty = false;
+ tuple->flags = 0;
tuple->format_id = format_id;
if (make_compact) {
assert(tuple_can_be_compact(data_offset, bsize));
tuple_ref_init(struct tuple *tuple, uint8_t refs)
{
tuple->local_refs = refs;
- tuple->has_uploaded_refs = false;
+ tuple_clear_flag(tuple, TUPLE_HAS_UPLOADED_REFS);
}
/**
{
say_debug("%s(%p)", __func__, tuple);
assert(tuple->local_refs == 0);
- assert(!tuple->has_uploaded_refs);
- assert(!tuple->is_dirty);
+ assert(tuple->flags == 0);
struct tuple_format *format = tuple_format(tuple);
format->vtab.tuple_delete(format, tuple);
}
{
assert(tuple->local_refs >= 1);
if (--tuple->local_refs == 0) {
- if (unlikely(tuple->has_uploaded_refs))
+ if (unlikely(tuple_has_flag(tuple, TUPLE_HAS_UPLOADED_REFS)))
tuple_acquire_refs(tuple);
else
tuple_delete(tuple);