24 #ifndef GRAPHLAB_LOCAL_CHANDY_MISRA_LOCKFREE_HPP
25 #define GRAPHLAB_LOCAL_CHANDY_MISRA_LOCKFREE_HPP
27 #include <graphlab/logger/assertions.hpp>
28 #include <graphlab/parallel/pthread_tools.hpp>
29 #include <graphlab/parallel/atomic.hpp>
30 #include <graphlab/macros_def.hpp>
33 template <
typename GraphType>
34 class chandy_misra_lockfree {
44 std::vector<unsigned char> forkset;
49 enum {OWNER_SOURCE = 0, OWNER_TARGET = 1};
50 static inline unsigned char request_bit(
bool owner) {
51 return owner ? REQUEST_1 : REQUEST_0;
56 atomic<vertex_id_type> forks_acquired;
60 if (num_edges == forks_acquired.value) {
66 std::vector<philosopher> philosopherset;
77 inline void request_for_fork(
size_t forkid,
bool nextowner) {
78 __sync_fetch_and_or(&forkset[forkid], request_bit(nextowner));
81 inline bool fork_owner(
size_t forkid) {
82 return forkset[forkid] & OWNER_BIT;
85 inline bool fork_dirty(
size_t forkid) {
86 return !!(forkset[forkid] & DIRTY_BIT);
89 inline void dirty_fork(
size_t forkid) {
90 __sync_fetch_and_or(&forkset[forkid], (
unsigned char)DIRTY_BIT);
97 inline bool advance_fork_state_on_lock(
size_t forkid,
101 unsigned char forkval = forkset[forkid];
102 unsigned char currentowner = forkval & OWNER_BIT;
104 unsigned char my_request_bit = request_bit(currentowner);
105 unsigned char other_request_bit = request_bit(!currentowner);
107 bool current_owner_is_eating =
108 (currentowner == OWNER_SOURCE && philosopherset[source].state == EATING) ||
109 (currentowner == OWNER_TARGET && philosopherset[target].state == EATING);
110 bool current_owner_is_hungry =
111 (currentowner == OWNER_SOURCE && philosopherset[source].state == HUNGRY) ||
112 (currentowner == OWNER_TARGET && philosopherset[target].state == HUNGRY);
116 if (current_owner_is_eating ==
false &&
117 (forkval & DIRTY_BIT) &&
118 (forkval & other_request_bit)) {
120 unsigned char newforkval = (!currentowner);
121 if (current_owner_is_hungry) {
122 newforkval |= my_request_bit;
136 inline bool advance_fork_state_on_unlock(
size_t forkid,
140 unsigned char currentowner = forkset[forkid] & OWNER_BIT;
142 unsigned char my_request_bit = request_bit(currentowner);
143 unsigned char other_request_bit = request_bit(!currentowner);
147 if ((forkset[forkid] & DIRTY_BIT) &&
148 (forkset[forkid] & other_request_bit)) {
151 forkset[forkid] = (forkset[forkid] & my_request_bit) | (!currentowner);
157 void compute_initial_fork_arrangement() {
159 philosopherset[i].num_edges = graph.num_in_edges(i) +
160 graph.num_out_edges(i);
161 philosopherset[i].state = THINKING;
162 foreach(
typename GraphType::edge_type edge, graph.in_edges(i)) {
163 if (edge.source() > edge.target()) {
164 forkset[graph.edge_id(edge)] = DIRTY_BIT | 1;
167 forkset[graph.edge_id(edge)] = DIRTY_BIT;
181 philosopherset[v2].lock.lock();
183 else if (!philosopherset[v2].lock.try_lock()) {
184 philosopherset[v1].lock.unlock();
185 philosopherset[v2].lock.lock();
186 philosopherset[v1].lock.lock();
192 inline chandy_misra_lockfree(GraphType &graph):graph(graph) {
193 forkset.resize(graph.num_edges(), 0);
194 philosopherset.resize(graph.num_vertices());
195 compute_initial_fork_arrangement();
205 philosopherset[p_id].forks_acquired.value = 0;
207 ASSERT_EQ((
int)philosopherset[p_id].state, (
int)THINKING);
208 philosopherset[p_id].state = HUNGRY;
214 foreach(
typename GraphType::edge_type edge, graph.in_edges(p_id)) {
217 size_t edgeid = graph.edge_id(edge);
219 if (fork_owner(edgeid) == OWNER_SOURCE) {
220 request_for_fork(edgeid, OWNER_TARGET);
222 philosopherset[p_id].forks_acquired.inc(
223 advance_fork_state_on_lock(edgeid, edge.source(), edge.target()));
226 philosopherset[p_id].forks_acquired.inc();
231 foreach(
typename GraphType::edge_type edge, graph.out_edges(p_id)) {
234 size_t edgeid = graph.edge_id(edge);
237 if (fork_owner(edgeid) == OWNER_TARGET) {
238 request_for_fork(edgeid, OWNER_SOURCE);
239 philosopherset[p_id].forks_acquired.inc(
240 advance_fork_state_on_lock(edgeid, edge.source(), edge.target()));
243 philosopherset[p_id].forks_acquired.inc();
249 if (philosopherset[p_id].atomic_eat()) {
257 inline std::vector<vertex_id_type> philosopher_stops_eating(
size_t p_id) {
258 std::vector<vertex_id_type> retval;
261 ASSERT_EQ((
int)philosopherset[p_id].state, (
int)EATING);
263 foreach(
typename GraphType::edge_type edge, graph.in_edges(p_id)) {
265 size_t edgeid = graph.edge_id(edge);
268 philosopherset[other].forks_acquired.inc(
269 advance_fork_state_on_unlock(edgeid, edge.source(), edge.target()));
270 if (philosopherset[other].atomic_eat()) {
272 retval.push_back(other);
277 foreach(
typename GraphType::edge_type edge, graph.out_edges(p_id)) {
279 size_t edgeid = graph.edge_id(edge);
282 philosopherset[other].forks_acquired.inc(
283 advance_fork_state_on_unlock(edgeid, edge.source(), edge.target()));
284 if (philosopherset[other].atomic_eat()) {
286 retval.push_back(other);
290 philosopherset[p_id].state = THINKING;
296 void no_locks_consistency_check() {
298 for (
size_t i = 0;i < forkset.size(); ++i) ASSERT_TRUE(fork_dirty(i));
300 for (
size_t i = 0;i < philosopherset.size(); ++i) ASSERT_TRUE(philosopherset[i].state == THINKING);
303 void complete_consistency_check() {
307 size_t numowned_clean = 0;
308 foreach(
typename GraphType::edge_type edge, graph.in_edges(v)) {
309 size_t edgeid = graph.edge_id(edge);
310 if (fork_owner(edgeid) == OWNER_TARGET) {
312 if (!fork_dirty(edgeid)) numowned_clean++;
315 foreach(
typename GraphType::edge_type edge, graph.out_edges(v)) {
316 size_t edgeid = graph.edge_id(edge);
317 if (fork_owner(edgeid) == OWNER_SOURCE) {
319 if (!fork_dirty(edgeid)) numowned_clean++;
323 if (philosopherset[v].state == THINKING) {
324 ASSERT_EQ(numowned_clean, 0);
326 else if (philosopherset[v].state == HUNGRY) {
327 ASSERT_EQ(philosopherset[v].forks_acquired.value, numowned);
329 else if (philosopherset[v].state == EATING) {
330 ASSERT_EQ(philosopherset[v].forks_acquired.value, philosopherset[v].num_edges);
331 ASSERT_EQ(philosopherset[v].forks_acquired.value, numowned);
339 #include <graphlab/macros_undef.hpp>