|
|
|
単方向リスト: 並行処理編 Part 4ソースはGitHubに移行しました。 Non-Blocking Singly-linked List 2(Non-Blocking単方向リスト)Non-Blocking Slingly-Linked Listについて、 T.L.Harris版の改良である "Lock-Free Linked Lists and Skip Lists" Mikhail Fomitchev, Eric Ruppert を実装した。 ソースはGitHubに移行しました。 データ構造データ構造list_tとnode_tを定義する。 list_tはListの本体、node_tはListに連結するnodeのデータ構造である。
nodeとlistのデータ構造を示す。
NonBlockingList.h:
#define MARKED 0x0001
#define UNMARKED 0x0000
#define MARKED_MASK 0x0001
#define FLAGGED 0x0002
#define UNFLAGGED 0x0000
#define FLAGGED_MASK 0x0002
typedef struct _next_ref {
int mark;
void *node_ptr;
}__attribute__((packed)) next_ref;
typedef struct _node_t
{
lkey_t key; /* key */
val_t val; /* 値 */
struct _node_t *backlink;
next_ref succ; /* 次のnodeへのリファレンス */
}__attribute__((packed)) node_t;
typedef struct _list_t
{
node_t *head;
node_t *tail;
} list_t;
list_tには、リストの先頭headと末尾tailが確保される。 基本関数listの生成
/*
* list_t *init_list(void)
*
* listを生成する。
*
* 成功すればlistへのポインタを返す。失敗ならNULLを返す。
*
*/
list_t *init_list(void)
{
list_t *list;
if ((list = (list_t *) calloc(1, sizeof(list_t))) == NULL) {
elog("calloc error");
return NULL;
}
if ((list->head = (node_t *) calloc(1, sizeof(node_t))) == NULL) {
elog("calloc error");
goto end;
}
if ((list->tail = (node_t *) calloc(1, sizeof(node_t))) == NULL) {
elog("calloc error");
goto end;
}
list->head->key = INT_MIN;
list->head->succ.mark = UNMARKED;
list->head->succ.node_ptr = (void *)list->tail;
list->tail->key = INT_MAX;
list->tail->succ.mark = UNMARKED;
list->tail->succ.node_ptr = NULL;
return list;
end:
free (list->head);
free (list);
return NULL;
}
ノードの検索
inline bool_t
cas64(next_ref * addr, const next_ref oldp, const next_ref newp)
{
char result;
__asm__ __volatile__("lock; cmpxchg8b %0; setz %1":"=m"(*addr),
"=q"(result)
:"m"(*addr), "a"(oldp.mark), "d"(oldp.node_ptr),
"b"(newp.mark), "c"(newp.node_ptr)
:"memory");
return (((int)result == 0) ? false:true);
}
#define is_marked_ref(ref) ((ref.mark & MARKED_MASK) == MARKED ? true:false)
#define is_unmarked_ref(ref) ((ref.mark & MARKED_MASK) == UNMARKED ? true:false)
#define is_flagged_ref(ref) ((ref.mark & FLAGGED_MASK) == FLAGGED ? true:false)
#define is_unflagged_ref(ref) ((ref.mark & FLAGGED_MASK) == UNFLAGGED ? true:false)
static next_ref make_ref(node_t * node_ptr, const int marked, const int flagged)
{
next_ref ref;
assert(!(marked == MARKED && flagged == FLAGGED));
ref.mark = (marked | flagged);
ref.node_ptr = (node_t *) node_ptr;
return ref;
}
#define get_unmarked_ref(node_ptr) make_ref (node_ptr, UNMARKED, UNFLAGGED)
#define get_marked_ref(node_ptr) make_ref (node_ptr, MARKED, UNFLAGGED)
static void
helpMarked (node_t *prev_node, node_t *del_node)
{
node_t *next_node;
assert (del_node->succ.node_ptr != NULL);
next_node = del_node->succ.node_ptr;
cas64(&prev_node->succ,
make_ref(del_node, UNMARKED, FLAGGED),
make_ref(next_node, UNMARKED, UNFLAGGED));
}
static bool_t searchFrom2 (const lkey_t key, node_t *curr_node, node_t **curr, node_t **next)
{
node_t *next_node = curr_node->succ.node_ptr;
while (next_node->key < key) {
while (is_marked_ref(next_node->succ)
&& (is_unmarked_ref(curr_node->succ) || (curr_node->succ.node_ptr != next_node))
) {
if (curr_node->succ.node_ptr == next_node)
helpMarked(curr_node, next_node);
next_node = curr_node->succ.node_ptr;
}
if (next_node->key < key) {
curr_node = next_node;
next_node = curr_node->succ.node_ptr;
}
}
*curr = curr_node;
*next = next_node;
return true;
}
static bool_t
tryFlag (node_t *prev_node, node_t *target_node, node_t **result_node)
{
bool_t ret = true;
node_t *del_node;
next_ref result;
*result_node = NULL;
while (1) {
if (prev_node->succ.node_ptr == target_node
&& (is_unmarked_ref(prev_node->succ) && is_flagged_ref(prev_node->succ))) {
*result_node = prev_node;
return false;
}
if (cas64(&prev_node->succ,
make_ref(target_node, UNMARKED, UNFLAGGED),
make_ref(target_node, UNMARKED, FLAGGED)) == true) {
*result_node = prev_node;
return true;
}
result = prev_node->succ;
if ((result.node_ptr == target_node) && (is_unmarked_ref(result)) && (is_flagged_ref(result))) {
*result_node = prev_node;
return false;
}
while (is_marked_ref(prev_node->succ)) {
prev_node = prev_node->backlink;
}
}
searchFrom2(target_node->key, prev_node, &prev_node, &del_node);
if (del_node != target_node) {
ret = false;
*result_node = NULL;
}
return ret;
}
static void
tryMark(node_t *del_node)
{
node_t *next_node;
next_ref result;
do {
next_node = del_node->succ.node_ptr;
cas64(&del_node->succ, make_ref(next_node, UNMARKED, UNFLAGGED),
make_ref(next_node, MARKED, UNFLAGGED));
result = del_node->succ;
if (is_unmarked_ref(result) && is_flagged_ref(result)) {
helpFlagged(del_node, result.node_ptr);
}
} while (is_unmarked_ref(del_node->succ));
}
static void
helpFlagged (node_t *prev_node, node_t *del_node)
{
del_node->backlink = prev_node;
if (is_unmarked_ref(del_node->succ)) {
tryMark(del_node);
}
helpMarked(prev_node, del_node);
}
ノードの追加
/*
* bool_t add(list_t * list, const lkey_t key, const val_t val)
*
* listにnode(key,val)を追加する。
* 追加位置はkeyの昇順に従う。すでに同じkeyを持つnodeがある場合は失敗。
*
* 成功すればtrue、失敗すればfalseを返す。
*/
bool_t add(list_t * list, const lkey_t key, const val_t val)
{
node_t *newNode;
node_t *prev_node, *next_node;
next_ref prev_succ, result;
if (searchFrom2(key, list->head, &prev_node, &next_node) != true)
return false;
if (prev_node->key == key)
return false;
if ((newNode = create_node(key, val)) == NULL)
return false;
while (1) {
prev_succ = prev_node->succ;
if ((prev_succ.mark & FLAGGED_MASK) == FLAGGED) {
helpFlagged(prev_node, prev_succ.node_ptr);
}
else {
newNode->succ = make_ref(next_node, UNMARKED, UNFLAGGED);
if (cas64(&prev_node->succ, make_ref(next_node, UNMARKED, UNFLAGGED),
make_ref(newNode, UNMARKED, UNFLAGGED)) == true) {
return true; // return newNode;
}
else {
result = prev_node->succ;
if (is_unmarked_ref(result) && is_flagged_ref(result)) {
helpFlagged(prev_node, result.node_ptr);
}
while (is_marked_ref(prev_node->succ)) {
prev_node = prev_node->backlink;
}
}
}
searchFrom2(key, list->head, &prev_node, &next_node);
if (prev_node->key == key) {
free (newNode);
return false;
}
}
}
関数add()内部で呼ぶnode生成関数create_node()を示す。
/*
* node_t *create_node(const lkey_t key, const val_t val)
*
* (key, val)を持つnodeを生成する。
*
* 成功すればnodeへのポインタを返す。失敗すればNULLを返す。
*/
static node_t *create_node(const lkey_t key, const val_t val)
{
node_t *node;
if ((node = (node_t *) calloc(1, sizeof(node_t))) == NULL) {
elog("calloc error");
return NULL;
}
node->next = make_ref(NULL, UNMARKED);
node->key = key;
node->val = val;
return node;
}
ノードの削除ノードの削除も基本戦略は追加と同じである。
/*
* bool_t delete(list_t * list, const lkey_t key, val_t *val)
*
* listからkeyを持つnodeを削除し、そのnodeのvalを*valに書き込む。
* keyが存在しない場合は失敗。
*
* 成功すればtrue、失敗すればfalseを返す。
*/
bool_t delete(list_t * list, const lkey_t key, val_t *val)
{
node_t *prev_node, *del_node;
node_t *result_node;
bool_t result;
searchFrom2(key, list->head, &prev_node, &del_node);
if (del_node->key != key)
return false;
result = tryFlag(prev_node, del_node, &result_node);
if (result_node != NULL) {
helpFlagged(result_node, del_node);
}
if (result == false)
return false;
*val = del_node->val;
free_node(del_node);
return true;
}
ノードの検索
/*
* bool_t find(list_t * list, const lkey_t key, val_t *val)
*
* listからkeyを持つnodeを検索し、そのnodeのvalを*valに書き込む。
* keyが存在しない場合は失敗。
*
* 成功すればtrue、失敗すればfalseを返す。
*/
bool_t delete(list_t * list, const lkey_t key, val_t *val)
{
node_t *prev_node, *del_node;
node_t *result_node;
bool_t result;
searchFrom2(key, list->head, &prev_node, &del_node);
if (del_node->key != key)
return false;
result = tryFlag(prev_node, del_node, &result_node);
if (result_node != NULL) {
helpFlagged(result_node, del_node);
}
if (result == false)
return false;
*val = del_node->val;
free_node(del_node);
return true;
}
/*
* bool_t find(list_t * list, const lkey_t key, val_t *val)
*
* listからkeyを持つnodeを検索し、そのnodeのvalを*valに書き込む。
* keyが存在しない場合は失敗。
*
* 成功すればtrue、失敗すればfalseを返す。
*/
bool find(list_t * l, const lkey_t key)
{
node_t *curr;
curr = search(l, key);
if ((curr == l->tail) || (curr->key != key))
return false;
return true;
}
実行ソースはGitHubに移行しました。
Last-modified: 2014-7-6
|