picotm  0.10.0
The Transactional Memory Module

The Transactional Memory module provides transactional semantics when accessing main memory. You can load and store variables in main memory, or adopt memory regions into a transactions. More...


file  picotm-tm.h
 Public interfaces of picotm's Transactional Memory module.

Detailed Description

The Transactional Memory module provides load and store operations for main memory. In order to avoid conflicting access to shared memory locations, picotm has to know which transaction uses which location. The Transactional Memory module maintains these information.

Call load_tx() to load a memory location into your transaction.

int x;
int x_tx;
load_tx(&x, &x_tx, sizeof(x));
// The value of 'x' is now stored in 'x_tx'.

This copies the value of 'x' into your transaction's variable 'x_tx' and puts the memory location of 'x' under control of the transaction manager. You're free to change the value of 'x_tx' at will, since it's transaction local. If you change it, the original, non-transactional value in 'x' remains unchanged.

Similarly, you can store a copy of a transactional variable in a memory location. This is done with store_tx().

int x;
int x_tx;
store_tx(&x, &x_tx, sizeof(x));
// The value of 'x_tx' will be committed into 'x'.

As with all transactional modifications, the store will not be executed immediately, but only become permanent after the transaction successfully committed.

You don't have to deal with all these addresses yourself. The TM module comes with helper functions for various basic C types are provided. With load_int_tx() and store_int_tx() we can rewrite the example transactions as shown below.

int x;
int x_tx = load_int_tx(&x);
// The value of 'x' is now stored in 'x_tx'.
store_int_tx(&x, x_tx);
// The value of 'x_tx' will be committed into 'x'.

Besides load_int_tx() and store_int_tx(), the Transactional Memory module provides similar functions for the basic C types. Each is defined via the macros PICOTM_TM_LOAD_TX() and PICOTM_TM_STORE_TX(). You can use these macros to define load and store functions for your application's data types. Both macros expand to inline C functions, so you don't loose performance compared to load_tx() and store_tx().

// An application-specific type
typedef unsigned int my_int_type;
// Define load_my_int_type_tx()
PICOTM_TM_LOAD_TX(my_int_type, my_int_type);
// Define store_my_int_type_tx()
PICOTM_TM_STORE_TX(my_int_type, my_int_type);

Since address and pointer handling can be tricky, there are also helpers for loading and storing pointers. Note that these functions load and store the address stored in a pointer variable, but not the value stored at that address.

int* x_ptr;
int* x_ptr_tx = load_ptr_tx(&x_ptr);
// The value of 'x_ptr' is now stored in 'x_ptr_tx'.
store_ptr_tx(&x_ptr, &x_ptr_tx);
// The value of 'x_ptr_tx' will be committed into 'x_ptr'.

Loads and stores always copy values into or out of a transaction. There are cases where you don't want a copy, but the exact memory location of a value. This is called privatization. For example, the function memcpy() loads and stores an indefinite amount of data between memory buffers. The actual buffer size is often not known in advance. It would be wasteful to first load the data into a transaction-local buffer and then further store it in another buffer.

The functions privatize_tx() and privatize_c_tx() offer privatization of memory locations.

int x;
// The memory location of 'x' is now available within the transaction.
// Changes to 'x' will be committed into the memory location of 'x'.

This privatizes 'x' for transactional access from within the transaction. The number of bytes is given in the second argument. The flags argument is a bitmask of PICOTM_TM_PRIVATIZE_LOAD and PICOTM_TM_PRIVATIZE_STORE. These flags control how picotm handles the memory location. If you only privatized a memory location for loading or storing, you should never invoke the other operation. Setting no flags at all will discard the memory location. This signals to other transactions that the memory is invalid and to be freed.

There can be cases where you don't know in advance how long the privatized buffer is going to be. For example, when you privatize a C string, the length is not always given, but given by the location of the terminating \0 character. To privatize a memory region up to and including a specific character, use privatize_c_tx().

char* str = "foo";
// The string in 'str' is now available within the transaction.
// Changes to 'str' will be committed into the string of 'str'.

Using privatize_c_tx() with strings is the most common use case, but arbitrary characters are possible.

Finally, there is loadstore_tx(). Even through the name suggests load-and-store, the function is a mixture of load, store and privatization. It privatizes an input buffer for loading and an output buffer for storing, and copies the input buffer's content into the output buffer.

int ibuf[20];
int obuf[20];
loadstore_tx(ibuf, obuf, sizeof(obuf));
// The data in 'ibuf' will be committed into the memory of 'obuf'.

A call to loadstore_tx() is like a call to memcpy() that privatizes its input buffers. It's an optimization for platform without transactional C library.