#include <sys/types.h>
#include <sys/stat.h>
#include <pthread.h>
#include <unistd.h>
#include <stddef.h>
#include <misc/result.h>
#include <misc/pool.h>
#include <misc/malloc.h>
#include <misc/debug.h>
#include <misc/mutex.h>
#include <core/config.h>
#include <core/sentinel.h>
#include <core/tx.h>
#include <core/txdesc.h>
Go to the source code of this file.
Sentinels overview
Most transactional memory systems operate only at user level and do not isolate changes to kernel data made within a transaction. For example, a thread may view speculative data written to a file by a transaction that later aborts. Such transient effects do not occur with locks and may cause the program to behave incorrectly.
The xCall interface provides isolation for kernel objects with sentinels. A sentinel is a lightweight, revocable user-level lock for a kernel object. The purpose of a sentinel is to isolate the effects of system calls from other threads in the same process. Similar to a database lock, sentinels are centrally managed to detect deadlocks and can be revoked to recover from deadlocks.
The xCall APIs associate sentinels with distinct kernel objects, such as sockets and file descriptors. Sentinels are released only when the transaction commits or aborts to implement two-phase locking. However, sentinels do not protect programs with inherent race conditions, such as when a non-transactional thread reads data from a file written by a transaction.
Before invoking a system call, an xCall acquires the sentinel that isolates the underlying kernel object accessed by the call. Sentinels only lock the logical state of the kernel that is visible through system calls. Internally, kernel queues and buffers may have different contents as long as system calls do not observe a change.
The sentinel implementation must detect deadlock when two transactions acquire the same set of sentinels concurrently in different orders. On deadlock, one transaction aborts and releases its sentinels
Implementation
The sentinel manager subsystem allocates and maps sentinels to logical kernel objects (KOA) in user mode. We implement process-wide sentinels with POSIX mutex locks.
The manager maintains lists of the sentinels acquired by each transaction and releases them at commit or abort. The sentinel list is actually kept in the transaction's descriptor but managed by the sentinel manager. The sentinel manager is informed about commit/abort/retry events indirectly by registering commit/undo actions. This allows to incrementally pay the cost when the transaction acquires any sentinels.
To prevent deadlocks, the sentinel manager enforces a canonical global order over all sentinels based on the sentinel’s index in the global table of allocated sentinels. When a transaction cannot acquire a sentinel, the sentinel manager aborts the transaction and releases the acquired sentinels. The transaction reacquires all the sentinels it encountered in canonical order before restarting. The transaction holds on to the sentinels until commit, even if it does not require the same sentinels when reexecuted.
Attach/Detach operations:
Whenever creating and enlisting a sentinel, we logically attach to the sentinel by calling sentinel_attach
. Attaching to a sentinel increments the sentinel's reference counter by one. Whenever releasing a sentinel, we logically detach from the sentinel by calling sentinel_detach
. Detaching from a sentinel decrements the sentinel's reference counter by one. Attach and detach operations prevent someone from destroying the sentinel as long as there are transactions interested in the sentinel. Here are some races which are prevented through this scheme:
close
operation. Other transactions might have tried to acquire it but failed to do so and enlisted the sentinel to acquire it after a restart. If the sentinel is gone then the transaction may fail acquiring the sentinel after a restart. Definition in file sentinel.c.
void txc_sentinel_destroy | ( | txc_sentinel_t * | sentinel | ) |
Destroys a sentinel.
[in] | sentinel | The sentinel to destroy (deallocate). |
Definition at line 252 of file sentinel.c.
References txc_sentinel_s::synch_mutex, TXC_ASSERT, TXC_MUTEX_LOCK, and TXC_MUTEX_UNLOCK.
txc_result_t txc_sentinel_detach | ( | txc_sentinel_t * | sentinel | ) |
Detaches from a sentinel.
It logically detaches from the sentinel by decrementing the sentinel's refererence counter. If reference counter becomes zero then sentinel is destroyed.
[in] | sentinel | The sentinel to detach from. |
Definition at line 294 of file sentinel.c.
References txc_sentinel_s::synch_mutex, TXC_MUTEX_LOCK, and TXC_MUTEX_UNLOCK.
Referenced by txc_koa_destroy().
txc_result_t txc_sentinel_enlist | ( | txc_tx_t * | txd, | |
txc_sentinel_t * | sentinel, | |||
int | flags | |||
) |
Enlist the sentinel in the transaction's sentinel list.
It also attaches to the sentinel by bumping up the sentinel's refcount via a call to sentinel_attach().
[in] | txd | Transactional descriptor. |
[in] | sentinel | Sentinel to enlist. |
[in] | flags | TXC_SENTINEL_ACQUIREONRETRY |
Definition at line 730 of file sentinel.c.
References txc_tx_s::sentinel_list, txc_sentinel_s::synch_mutex, TXC_MUTEX_LOCK, TXC_MUTEX_UNLOCK, TXC_R_SUCCESS, and TXC_SENTINEL_ACQUIREONRETRY.
txc_result_t txc_sentinel_list_create | ( | txc_sentinelmgr_t * | sentinelmgr, | |
txc_sentinel_list_t ** | sentinel_list | |||
) |
Create a sentinel list.
[in] | sentinelmgr | Sentinel manager responsible for the sentinel list. |
[in,out] | sentinel_list | Pointer to the created sentinel list. |
Definition at line 618 of file sentinel.c.
References FREE, MALLOC, TXC_R_NOMEMORY, TXC_R_SUCCESS, and TXC_SENTINEL_LIST_SIZE.
Referenced by txc_txmgr_create().
txc_result_t txc_sentinel_list_destroy | ( | txc_sentinel_list_t ** | sentinel_list | ) |
Destroy a sentinel list.
Deallocate sentinel list entries and then deallocates the list itself
[in,out] | sentinel_list | Pointer to the sentinel list to deallocate. Pointer will point to NULL after return. |
Definition at line 669 of file sentinel.c.
References FREE, and TXC_R_SUCCESS.
Referenced by txc_txmgr_destroy().
txc_result_t txc_sentinel_list_init | ( | txc_sentinel_list_t * | sentinel_list | ) |
Initialize a sentinel list.
[in] | sentinel_list | Sentinel list to deallocate. |
Definition at line 651 of file sentinel.c.
References txc_sentinel_list_s::num_entries, and TXC_R_SUCCESS.
Referenced by txc_tx_create().
txc_result_t txc_sentinel_tryacquire | ( | txc_tx_t * | txd, | |
txc_sentinel_t * | sentinel, | |||
int | flags | |||
) |
Try to acquire a sentinel.
It will try to acquire the sentinel. If sentinel is acquired it will enlist it in the transaction's sentinel list as acquired, otherwise if the sentinel is not acquired (busy) it will return without block-waiting for the sentinel to become free (prevent deadlock).
[in] | txd | Transactional descriptor. |
[in] | sentinel | Sentinel to acquire. |
[in] | flags | TXC_SENTINEL_ACQUIRED, TXC_SENTINEL_ACQUIREONRETRY |
Definition at line 759 of file sentinel.c.
References txc_sentinel_s::id, txc_sentinel_s::owner, txc_sentinel_s::sentinel_mutex, txc_sentinel_s::synch_mutex, TXC_ASSERT, TXC_DEBUG_PRINT, TXC_INTERNALERROR, TXC_MUTEX_LOCK, TXC_MUTEX_TRYLOCK, TXC_MUTEX_UNLOCK, TXC_R_BUSYSENTINEL, TXC_R_SUCCESS, txc_runtime_settings, TXC_SENTINEL_ACQUIREONRETRY, txc_sentinel_is_enlisted(), txc_sentinel_owner(), and txc_tx_get_xactstate().
Referenced by x_close(), x_create(), x_dup(), x_fsync(), x_lseek(), x_open(), x_pipe(), x_read(), x_rename(), x_sendmsg(), x_socket(), x_unlink(), and x_write_pipe().
txc_result_t txc_sentinelmgr_create | ( | txc_sentinelmgr_t ** | sentinelmgrp | ) |
Creates a sentinel manager.
It preallocates a pool of sentinels to make sentinel allocation fast.
[out] | sentinelmgrp | Pointer to the created sentinel manager. |
Definition at line 158 of file sentinel.c.
References FREE, txc_sentinel_s::id, MALLOC, txc_sentinel_s::manager, txc_sentinel_s::owner, txc_sentinel_s::sentinel_mutex, txc_sentinel_s::synch_mutex, TXC_MUTEX_INIT, txc_pool_create(), TXC_POOL_OBJECT_ALLOCATED, txc_pool_object_first(), TXC_POOL_OBJECT_FREE, txc_pool_object_next(), txc_pool_object_of(), TXC_R_NOMEMORY, TXC_R_SUCCESS, TXC_SENTINEL_NOOWNER, and TXC_SENTINEL_NUM.
Referenced by _TXC_global_init().
void txc_sentinelmgr_destroy | ( | txc_sentinelmgr_t ** | sentinelmgrp | ) |
Destroys a sentinel manager.
It deallocates all sentinels that it manages.
[in,out] | sentinelmgrp | Poitner to the sentinel manager to be destroyed. |
Definition at line 206 of file sentinel.c.
References FREE, and txc_pool_destroy().