tlds
Transactional Operations for Linked Data Structures
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros
library.hpp
Go to the documentation of this file.
1 /**
2  * Copyright (C) 2011
3  * University of Rochester Department of Computer Science
4  * and
5  * Lehigh University Department of Computer Science and Engineering
6  *
7  * License: Modified BSD
8  * Please see the file LICENSE.RSTM for licensing information
9  */
10 
11 /**
12  * This file presents a simple library API for using RSTM without compiler
13  * support. The API consists of the following:
14  *
15  * TM_ALLOC : Allocate memory inside a transaction
16  * TM_FREE : Deallocate memory inside a transaction
17  * TM_SYS_INIT : Initialize the STM library
18  * TM_SYS_SHUTDOWN : Shut down the STM library
19  * TM_THREAD_INIT : Initialize a thread before using TM
20  * TM_THREAD_SHUTDOWN : Shut down a thread
21  * TM_SET_POLICY(P) : Change the STM algorithm on the fly
22  * TM_BECOME_IRREVOC() : Become irrevocable or abort
23  * TM_READ(var) : Read from shared memory from a txn
24  * TM_WRITE(var, val) : Write to shared memory from a txn
25  * TM_BEGIN(type) : Start a transaction... use 'atomic' as type
26  * TM_END : End a transaction
27  *
28  * Custom Features:
29  *
30  * stm::restart() : Self-abort and immediately retry a txn
31  * TM_BEGIN_FAST_INITIALIZATION : For fast initialization
32  * TM_END_FAST_INITIALIZATION : For fast initialization
33  * TM_GET_ALGNAME() : Get the current algorithm name
34  *
35  * Compiler Compatibility::Transaction Descriptor Management:
36  *
37  * TM_GET_THREAD() : for getting the thread's descriptor, if needed
38  * TM_ARG_ALONE : for passing descriptors to transactional functions
39  * TM_ARG : (same)
40  * TM_PARAM : (same)
41  * TM_PARAM_ALONE : (same)
42  *
43  * Compiler Compatibility::Annotations (unused in library):
44  *
45  * TM_WAIVER : mark a block that does not get TM instrumentation
46  * TM_CALLABLE : mark a function as being callable by TM
47  */
48 
49 #ifndef API_LIBRARY_HPP__
50 #define API_LIBRARY_HPP__
51 
52 #include <setjmp.h>
53 #include <stm/config.h>
54 #include <common/platform.hpp>
55 #include <stm/txthread.hpp>
56 
57 namespace stm
58 {
59  /**
60  * Code to start a transaction. We assume the caller already performed a
61  * setjmp, and is passing a valid setjmp buffer to this function.
62  *
63  * The code to begin a transaction *could* all live on the far side of a
64  * function pointer. By putting some of the code into this inlined
65  * function, we can:
66  *
67  * (a) avoid overhead under subsumption nesting and
68  * (b) avoid code duplication or MACRO nastiness
69  */
70  TM_INLINE
71  inline void begin(TxThread* tx, scope_t* s, uint32_t /*abort_flags*/)
72  {
73  if (++tx->nesting_depth > 1)
74  return;
75 
76  // we must ensure that the write of the transaction's scope occurs
77  // *before* the read of the begin function pointer. On modern x86, a
78  // CAS is faster than using WBR or xchg to achieve the ordering. On
79  // SPARC, WBR is best.
80 #ifdef STM_CPU_SPARC
81  tx->scope = s; WBR;
82 #else
83  // NB: this CAS fails on a transaction restart... is that too expensive?
84  casptr((volatile uintptr_t*)&tx->scope, (uintptr_t)0, (uintptr_t)s);
85 #endif
86 
87  // some adaptivity mechanisms need to know nontransactional and
88  // transactional time. This code suffices, because it gets the time
89  // between transactions. If we need the time for a single transaction,
90  // we can run ProfileTM
91  if (tx->end_txn_time)
92  tx->total_nontxn_time += (tick() - tx->end_txn_time);
93 
94  // now call the per-algorithm begin function
96  }
97 
98  /**
99  * Code to commit a transaction. As in begin(), we are using forced
100  * inlining to save a little bit of overhead for subsumption nesting, and to
101  * prevent code duplication.
102  */
103  TM_INLINE
104  inline void commit(TxThread* tx)
105  {
106  // don't commit anything if we're nested... just exit this scope
107  if (--tx->nesting_depth)
108  return;
109 
110  // dispatch to the appropriate end function
111 #ifdef STM_PROTECT_STACK
112  void* top_of_stack;
113  tx->tmcommit(tx, &top_of_stack);
114 #else
115  tx->tmcommit(tx);
116 #endif
117 
118  // zero scope (to indicate "not in tx")
119  CFENCE;
120  tx->scope = NULL;
121 
122  // record start of nontransactional time
123  tx->end_txn_time = tick();
124  }
125 
126  /**
127  * The STM system provides a message that exits the program (preferable to
128  * 'assert(false)'). We use this in the API too, so it needs to be visible
129  * here
130  */
131  void NORETURN UNRECOVERABLE(const char*);
132 
133  /**
134  * This portion of the API addresses allocation. We provide tx-safe malloc
135  * and free calls, which also work from nontransactional contexts.
136  */
137 
138  /**
139  * get a chunk of memory that will be automatically reclaimed if the caller
140  * is a transaction that ultimately aborts
141  */
142  inline void* tx_alloc(size_t size) { return Self->allocator.txAlloc(size); }
143 
144  /**
145  * Free some memory. If the caller is a transaction that ultimately aborts,
146  * the free will not happen. If the caller is a transaction that commits,
147  * the free will happen at commit time.
148  */
149  inline void tx_free(void* p) { Self->allocator.txFree(p); }
150 
151  /**
152  * Master class for all objects that are used in transactions, to ensure
153  * that those objects have tx-safe allocation
154  *
155  * WARNING: DEPRECATED
156  *
157  * We no longer use the Object class. In G++ it is unsafe to call
158  * destructors from within a transaction (they trash the vtable
159  * pointer in an unrecoverable way!), and some compilers don't handle
160  * new() within a transaction correctly. Ugly though it is, for now,
161  * you should just use malloc and free to create objects.
162  */
163  struct Object
164  {
165  void* operator new(size_t size) { return tx_alloc(size); }
166 
167  // it is never safe to call a destructor inside a tx with g++, because
168  // the vtable will be overwritten; if the tx aborts, the vtable will not
169  // be restored. We hope this never gets called...
170  void operator delete(void* ptr)
171  {
172  tx_free(ptr);
173  UNRECOVERABLE("Calling destructors is not supported.");
174  }
175  virtual ~Object() { }
176  };
177 
178  /**
179  * Here we declare the rest of the api to the STM library
180  */
181 
182  /**
183  * Initialize the library (call before doing any per-thread initialization)
184  *
185  * We rely on the default setjmp/longjmp abort handling when using the
186  * library API.
187  */
188  void sys_init(void (*abort_handler)(TxThread*) = NULL);
189 
190  /**
191  * Shut down the library. This just dumps some statistics.
192  */
193  void sys_shutdown();
194 
195  /*** Set up a thread's transactional context */
196  inline void thread_init() { TxThread::thread_init(); }
197 
198  /*** Shut down a thread's transactional context */
200 
201  /**
202  * Set the current STM algorithm/policy. This should be called at the
203  * beginning of each program phase
204  */
205  void set_policy(const char*);
206 
207  /*** Report the algorithm name that was used to initialize libstm */
208  const char* get_algname();
209 
210  /**
211  * Try to become irrevocable. Call this from within a transaction.
212  */
213  bool become_irrevoc(STM_WHEN_PROTECT_STACK(void** top_of_stack));
214 
215 #ifdef STM_PROTECT_STACK
216 # define TM_BECOME_IRREVOC() ({ \
217  void* top_of_stack; \
218  stm::become_irrevoc(&top_of_stack); })
219 #else
220 # define TM_BECOME_IRREVOC() stm::become_irrevoc()
221 #endif
222 
223  /**
224  * Abort the current transaction and restart immediately.
225  */
226  void restart();
227 }
228 
229 /*** pull in the per-memory-access instrumentation framework */
230 #include "library_inst.hpp"
231 
232 /**
233  * Now we can make simple macros for reading and writing shared memory, by
234  * using templates to dispatch to the right code:
235  */
236 namespace stm
237 {
238  template <typename T>
239  inline T stm_read(T* addr, TxThread* thread)
240  {
241  return DISPATCH<T, sizeof(T)>::read(addr, thread);
242  }
243 
244  template <typename T>
245  inline void stm_write(T* addr, T val, TxThread* thread)
246  {
247  DISPATCH<T, sizeof(T)>::write(addr, val, thread);
248  }
249 } // namespace stm
250 
251 /**
252  * Code should only use these calls, not the template stuff declared above
253  */
254 #define TM_READ(var) stm::stm_read(&var, tx)
255 #define TM_WRITE(var, val) stm::stm_write(&var, val, tx)
256 
257 /**
258  * This is the way to start a transaction
259  */
260 #define TM_BEGIN(TYPE) \
261  { \
262  stm::TxThread* tx = (stm::TxThread*)stm::Self; \
263  jmp_buf _jmpbuf; \
264  uint32_t abort_flags = setjmp(_jmpbuf); \
265  stm::begin(tx, &_jmpbuf, abort_flags); \
266  CFENCE; \
267  {
268 
269 /**
270  * This is the way to commit a transaction. Note that these macros weakly
271  * enforce lexical scoping
272  */
273 #define TM_END \
274  } \
275  stm::commit(tx); \
276  }
277 
278 /**
279  * Macro to get STM context. This currently produces a pointer to a TxThread
280  */
281 #define TM_GET_THREAD() stm::TxThread* tx = (stm::TxThread*)stm::Self
282 #define TM_ARG_ALONE stm::TxThread* tx
283 #define TM_ARG , TM_ARG_ALONE
284 #define TM_PARAM , tx
285 #define TM_PARAM_ALONE tx
286 
287 #define TM_WAIVER
288 #define TM_CALLABLE
289 
290 #define TM_SYS_INIT() stm::sys_init()
291 #define TM_THREAD_INIT stm::thread_init
292 #define TM_THREAD_SHUTDOWN() stm::thread_shutdown()
293 #define TM_SYS_SHUTDOWN stm::sys_shutdown
294 #define TM_ALLOC stm::tx_alloc
295 #define TM_FREE stm::tx_free
296 #define TM_SET_POLICY(P) stm::set_policy(P)
297 
298 #define TM_GET_ALGNAME() stm::get_algname()
299 
300 /**
301  * This is gross. ITM, like any good compiler, will make nontransactional
302  * versions of code so that we can cleanly do initialization from outside of
303  * a transaction. The library **can** do this, but only via some cumbersome
304  * template games that we really don't want to keep playing (see the previous
305  * release for examples).
306  *
307  * Since we don't want to have transactional configuration (it is slow, and
308  * it messes up some accounting of commits and transaction sizes), we use the
309  * following trick: if we aren't using a compiler for instrumentation, then
310  * BEGIN_FAST_INITIALIZATION will copy the current STM configuration (envar
311  * STM_CONFIG) to a local, then switch the mode to CGL, then call the
312  * instrumented functions using CGL instrumentation (e.g., the lightest
313  * possible, and correct without a 'commit'). Likewise, if we aren't using a
314  * compiler for instrumentation, then END_FAST_INITIALIZATION will restore
315  * the original configuration, so that the app will use the STM as expected.
316  */
317 #ifdef STM_API_ITM
318 # define TM_BEGIN_FAST_INITIALIZATION()
319 # define TM_END_FAST_INITIALIZATION()
320 #else
321 # define TM_BEGIN_FAST_INITIALIZATION() \
322  const char* __config_string__ = TM_GET_ALGNAME(); \
323  TM_SET_POLICY("CGL"); \
324  TM_GET_THREAD()
325 # define TM_END_FAST_INITIALIZATION() \
326  TM_SET_POLICY(__config_string__)
327 #endif
328 
329 #endif // API_LIBRARY_HPP__
void *volatile ptr
Definition: counted_ptr.hpp:57
Definition: library.hpp:163
Definition: stm_fraser.c:61
void tx_free(void *p)
Definition: library.hpp:149
static void thread_init()
Definition: txthread.cpp:177
Definition: my_thread.hpp:41
scope_t *volatile scope
Definition: txthread.hpp:57
TM_INLINE void begin(TxThread *tx, scope_t *s, uint32_t)
Definition: library.hpp:71
void sys_init(void(*abort_handler)(TxThread *)=NULL)
uint64_t end_txn_time
Definition: txthread.hpp:92
Definition: library_inst.hpp:46
TM_INLINE void commit(TxThread *tx)
Definition: library.hpp:104
void thread_shutdown()
Definition: library.hpp:199
void stm_write(T *addr, T val, TxThread *thread)
Definition: library.hpp:245
TM_FASTCALL void(* tmcommit)(STM_COMMIT_SIG(,))
Definition: txthread.hpp:118
void scope_t
Definition: metadata.hpp:39
T stm_read(T *addr, TxThread *thread)
Definition: library.hpp:239
#define TM_INLINE
Definition: platform.hpp:77
stm_tx * tx
Definition: stmskip.cc:245
virtual ~Object()
Definition: library.hpp:175
uint64_t total_nontxn_time
Definition: txthread.hpp:93
void sys_shutdown()
Definition: txthread.cpp:203
uint32_t nesting_depth
Definition: txthread.hpp:51
bool become_irrevoc(STM_WHEN_PROTECT_STACK(void **top_of_stack))
void thread_init()
Definition: library.hpp:196
Definition: txthread.hpp:47
struct @18 p
void NORETURN UNRECOVERABLE(const char *)
Definition: txthread.cpp:155
const char * get_algname()
Definition: txthread.cpp:386
void * tx_alloc(size_t size)
Definition: library.hpp:142
static TM_FASTCALL bool(*volatile tmbegin)(TxThread *)
Definition: txthread.hpp:115
#define STM_WHEN_PROTECT_STACK(S)
Definition: macros.hpp:71
void set_policy(const char *)
Definition: txthread.cpp:261
void restart()
Definition: txthread.cpp:189
static void thread_shutdown()
Definition: txthread.hpp:147
#define NORETURN
Definition: platform.hpp:47