-
Notifications
You must be signed in to change notification settings - Fork 0
/
mem-record-rtmseq.c
152 lines (135 loc) · 4.85 KB
/
mem-record-rtmseq.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
#include "mem-record.h"
#include "tsx/rtm.h"
#include "tsx/assert.h"
#include <stdio.h>
// Provide atomic version & memory access by RTM (seqlock as fallback handler).
uint32_t mem_read(tid_t tid, uint32_t *addr) {
version_t version;
uint32_t val;
objid_t objid = calc_objid(addr);
struct objinfo *info = &g_objinfo[objid];
int ret = 0;
if ((ret = _xbegin()) == _XBEGIN_STARTED) {
version = info->version;
// XXX TSX note: this transaction may execute and commit while writer is
// inside fallback handler and has increased version by one. So the
// writer's update to version and memory does not appear atomic to the
// reader. The assertion may fire, but it will abort checking lock in
// that case.
/*tsx_assert((version & 1) == 0);*/
val = *addr;
// Check if lock is hold to avoid the problem mentioned above.
// Do this at the end of TX to lower abort rate.
//
// Suppose are using spinlock, if we check lock at start of the
// transaction, the following sequence will abort:
//
// A B
// lock
// ...
// ...
// xbegin
// check lock
// unlock
//
// The problem here is that unlock is a write which touches a single
// memory address. If we move the checking code to the end, and lock
// check happens after unlock, the transaction has chance to commit.
/*
*if (info->write_lock) {
* _xabort(1);
*}
*/
// XXX Actually we can check for odd version to ensure read tx does not
// execute in parrallel with write (this is how seqlock works). If we
// get even version, we are either before the 1st version inc or after
// the 2 version inc. Though we may execute while the write lock is
// taken, if the tx commits, the read tx happens atomically before or
// after the write. This can avoid some unnecessary aborts caused by
// lock taken by write.
if (version & 1) {
_xabort(1);
}
_xend();
} else {
#ifdef RTM_STAT
fprintf(stderr, "T%d R%ld aborted %x, %d\n", g_tid, memop, ret, _XABORT_CODE(ret));
g_rtm_abort_cnt++;
#endif
do {
version = info->version;
while (unlikely(version & 1)) {
cpu_relax();
version = info->version;
}
barrier();
val = *addr;
barrier();
} while (version != info->version);
}
struct last_objinfo *lastobj = &g_last[objid];
if (lastobj->version != version) {
log_order(objid, version, lastobj);
// Update version so that following write after read don't need logs.
lastobj->version = version;
}
#ifdef DEBUG_ACCESS
log_access('R', objid, version, val);
#endif
lastobj->memop = memop;
memop++;
return val;
}
void mem_write(tid_t tid, uint32_t *addr, uint32_t val) {
version_t version;
objid_t objid = calc_objid(addr);
struct objinfo *info = &g_objinfo[objid];
int ret = 0;
if ((ret = _xbegin()) == _XBEGIN_STARTED) {
version = info->version;
// XXX TSX note: same as read transaction, this may execute and commit
// while other writer is in fallback handler.
/*tsx_assert((version & 1) == 0);*/
barrier();
*addr = val;
__sync_synchronize();
info->version += 2;
// XXX For write, we must check for lock being taken since we need to
// ensure exclusive write.
// The problem happens if the write tx commits, while another write
// fallback just finished version read and before the 1st version inc.
// See the next comment.
if (info->write_lock) {
_xabort(2);
}
_xend();
} else {
#ifdef RTM_STAT
fprintf(stderr, "T%d W%ld aborted %x, %d\n", g_tid, memop, ret, _XABORT_CODE(ret));
g_rtm_abort_cnt++;
#endif
spin_lock(&info->write_lock);
version = info->version;
barrier();
// XXX If write tx does not check for lock or just check for odd
// version, may have problem here.
// Odd version means that there's writer trying to update value.
info->version++;
barrier();
*addr = val;
// This barrier disallows read to happen before the write.
// The explicit barrier here may also make the compiler unnecessary here.
__sync_synchronize();
info->version++;
spin_unlock(&info->write_lock);
}
struct last_objinfo *lastobj = &g_last[objid];
if (lastobj->version != version) {
log_order(objid, version, lastobj);
}
#ifdef DEBUG_ACCESS
log_access('W', objid, version, val);
#endif
lastobj->memop = -1;
memop++;
}