|
|
単方向リスト: 並行処理編 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
|