-
Notifications
You must be signed in to change notification settings - Fork 11
/
Copy pathMCS3.c
134 lines (102 loc) · 4.37 KB
/
MCS3.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
// John M. Mellor-Crummey and Michael L. Scott, Algorithms for Scalable Synchronization on Shared-Memory Multiprocessors,
// ACM Transactions on Computer Systems, 9(1), February 1991, Fig. 5, p. 30
#include <stdbool.h>
#include <stdatomic.h>
typedef struct mcs_node {
struct mcs_node * volatile next;
VTYPE spin;
} MCS_node CALIGN;
typedef MCS_node * MCS_lock;
#define load( var, order ) atomic_load_explicit( var, order )
#define store( var, val, order ) atomic_store_explicit( var, val, order )
#define exchange( var, val, order ) atomic_exchange_explicit( var, val, order )
#define CAS( var, comp, val, order1, order2 ) atomic_compare_exchange_strong_explicit( var, comp, val, order1, order2 )
#define fetch_add( var, val ) atomic_fetch_add_explicit( var, val, memory_order_seq_cst )
static inline void mcs_lock( MCS_lock * lock, MCS_node * node ) {
store( &node->next, NULL, memory_order_relaxed );
#ifndef MCS_OPT1 // default option
store( &node->spin, true, memory_order_relaxed ); // mark as waiting
#endif // MCS_OPT1
MCS_node * prev = exchange( lock, node, memory_order_seq_cst );
if ( SLOWPATH( prev == NULL ) ) return; // no one on list ?
#ifdef MCS_OPT1
atomic_store_explicit( &node->spin, true, memory_order_relaxed ); // mark as waiting
#endif // MCS_OPT1
store( &prev->next, node, memory_order_release); // add to list of waiting threads
#ifndef MPAUSE
while ( load( &node->spin, memory_order_acquire ) == true ) Pause(); // busy wait on my spin variable
#else
MPause( node->spin, == true ); // busy wait on my spin variable
#endif // MPAUSE
} // mcs_lock
static inline void mcs_unlock( MCS_lock * lock, MCS_node * node ) {
#ifdef MCS_OPT2 // original, default option
if ( FASTPATH( load( &node->next, memory_order_relaxed ) == NULL ) ) { // no one waiting ?
MCS_node * temp = node; // copy because exchange overwrites expected
if ( SLOWPATH( CAS( lock, &temp, NULL, memory_order_release, memory_order_relaxed ) ) ) return;
#ifndef MPAUSE
while ( load( &node->next, memory_order_relaxed ) == NULL ) Pause(); // busy wait until my node is modified
#else
MPause( node->next, == NULL ); // busy wait until my node is modified
#endif // MPAUSE
} // if
// MCS_node * next = atomic_load_explicit( &node->next, memory_order_acquire );
store( &node->next->spin, false, memory_order_release );
#else // Scott book Figure 4.8
MCS_node * succ = load( &node->next, memory_order_relaxed );
if ( FASTPATH( succ == NULL ) ) { // no one waiting ?
// node is potentially at the tail of the MCS chain
MCS_node * temp = node; // copy because exchange overwrites expected
if ( SLOWPATH( CAS( lock, &temp, NULL, memory_order_release, memory_order_relaxed ) ) ) return;
#ifndef MPAUSE
while ( (succ = load( &node->next, memory_order_relaxed ) ) == NULL) Pause(); // busy wait until my node is modified
#else
MPauseS( succ =, node->next, == NULL ); // busy wait until my node is modified
#endif // MPAUSE
} // if
store( &succ->spin, false, memory_order_release );
#endif // MCS_OPT2
} // mcs_unlock
static TYPE PAD1 CALIGN __attribute__(( unused )); // protect further false sharing
static MCS_lock lock CALIGN;
static TYPE PAD2 CALIGN __attribute__(( unused )); // protect further false sharing
static void * Worker( void * arg ) {
TYPE id = (size_t)arg;
uint64_t entry;
#ifdef FAST
unsigned int cnt = 0, oid = id;
#endif // FAST
NCS_DECL;
MCS_node node;
for ( int r = 0; r < RUNS; r += 1 ) {
RTYPE randomThreadChecksum = 0;
for ( entry = 0; stop == 0; entry += 1 ) {
NCS;
mcs_lock( &lock, &node );
randomThreadChecksum += CS( id );
mcs_unlock( &lock, &node );
#ifdef FAST
id = startpoint( cnt ); // different starting point each experiment
cnt = cycleUp( cnt, NoStartPoints );
#endif // FAST
} // for
fetch_add( &sumOfThreadChecksums, randomThreadChecksum );
#ifdef FAST
id = oid;
#endif // FAST
entries[r][id] = entry;
fetch_add( &Arrived, 1 );
while ( stop != 0 ) Pause();
fetch_add( &Arrived, -1 );
} // for
return NULL;
} // Worker
void __attribute__((noinline)) ctor() {
lock = NULL;
} // ctor
void __attribute__((noinline)) dtor() {
} // dtor
// Local Variables: //
// tab-width: 4 //
// compile-command: "gcc -Wall -Wextra -std=gnu11 -O3 -DNDEBUG -fno-reorder-functions -DPIN -DAlgorithm=MCS3 Harness.c -lpthread -lm -D`hostname` -DCFMT -DCNT=0" //
// End: //