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