int l_remove(node_t *head, key_t key) { node_t *pred, *item, *sitem; while (TRUE) { if (!l_find(&pred, &item, head, key)) { trace("remove item failed %d\n", key); return FALSE; } sitem = STRIP_MARK(item); node_t *inext = sitem->next; /* 先标记再删除 */ if (!CAS(&sitem->next, inext, MARK(inext))) { trace("cas item %p mark failed\n", sitem->next); continue; } sitem->val = NULL_VALUE; int tag = GET_TAG(pred->next) + 1; if (CAS(&pred->next, item, TAG(STRIP_MARK(sitem->next), tag))) { trace("remove item %p success\n", item); haz_defer_free(sitem); return TRUE; } trace("cas item remove item %p failed\n", item); } return FALSE; }
value_t l_lookup(node_t *head, key_t key) { node_t *pred, *item; if (l_find(&pred, &item, head, key)) { return STRIP_MARK(item)->val; } return NULL_VALUE; }
int l_find(node_t **pred_ptr, node_t **item_ptr, node_t *head, key_t key) { node_t *pred = head; node_t *item = head->next; /* pred和next会被使用,所以进行标记 */ hazard_t *hp1 = haz_get(0); hazard_t *hp2 = haz_get(1); while (item) { node_t *sitem = STRIP_MARK(item); haz_set_ptr(hp1, STRIP_MARK(pred)); haz_set_ptr(hp2, STRIP_MARK(item)); /* 如果已被标记,那么紧接着item可能被移除链表甚至释放,所以需要重头查找 */ if (HAS_MARK(sitem->next)) { trace("item->next %p %p marked, retry\n", item, sitem->next); return l_find(pred_ptr, item_ptr, head, key); } int d = KEY_CMP(sitem->key, key); if (d >= 0) { trace("item %p match key %d, pred %p\n", item, key, pred); *pred_ptr = pred; *item_ptr = item; return d == 0 ? TRUE : FALSE; } pred = item; item = sitem->next; } trace("not found key %d\n", key); *pred_ptr = pred; *item_ptr = NULL; return FALSE; }
/* DESCRIPTION deletes a node as identified by hashnr/keey/keylen from the list that starts from 'head' RETURN 0 - ok 1 - not found NOTE it uses pins[0..2], on return all pins are removed. */ static int l_delete(LF_SLIST * volatile *head, CHARSET_INFO *cs, uint32 hashnr, const uchar *key, uint keylen, LF_PINS *pins) { CURSOR cursor; int res; for (;;) { if (!l_find(head, cs, hashnr, key, keylen, &cursor, pins)) { res= 1; /* not found */ break; } else { /* mark the node deleted */ if (my_atomic_casptr((void **) (char*) &(cursor.curr->link), (void **) (char*) &cursor.next, (void *)(((intptr)cursor.next) | 1))) { /* and remove it from the list */ if (my_atomic_casptr((void **)cursor.prev, (void **)(char*)&cursor.curr, cursor.next)) _lf_alloc_free(pins, cursor.curr); else { /* somebody already "helped" us and removed the node ? Let's check if we need to help that someone too! (to ensure the number of "set DELETED flag" actions is equal to the number of "remove from the list" actions) */ l_find(head, cs, hashnr, key, keylen, &cursor, pins); } res= 0; break; } } } _lf_unpin(pins, 0); _lf_unpin(pins, 1); _lf_unpin(pins, 2); return res; }
/* Test the find function. */ void find_test(linked_list list, int item, int result) { link l = l_find(list, item); if (l == NULL && result != 0) { printf("Find test failed: expected to find %d, got NULL.\n", item); } else if (l != NULL && result == 0) { printf("Find test failed: expected NULL when finding %d.\n", item); } else if (l != NULL && l->item != item) { printf("Find test failed: expected %d, found %d.\n", item, l->item); } }
/* DESCRIPTION searches for a node as identified by hashnr/keey/keylen in the list that starts from 'head' RETURN 0 - not found node - found NOTE it uses pins[0..2], on return the pin[2] keeps the node found all other pins are removed. */ static LF_SLIST *l_search(LF_SLIST * volatile *head, CHARSET_INFO *cs, uint32 hashnr, const uchar *key, uint keylen, LF_PINS *pins) { CURSOR cursor; int res= l_find(head, cs, hashnr, key, keylen, &cursor, pins); if (res) _lf_pin(pins, 2, cursor.curr); else _lf_unpin(pins, 2); _lf_unpin(pins, 1); _lf_unpin(pins, 0); return res ? cursor.curr : 0; }
static int add_section_label(struct label *l, const char *name, const char *attribute, uint64_t value, struct label **length) { char label[255]; int errcode; errcode = create_section_label_name(label, sizeof(label), name, attribute); if (errcode < 0) return errcode; errcode = l_append(l, label, value); if (errcode < 0) return errcode; if (length) *length = l_find(l, label); return 0; }
/** Iterate over all elements in hash and call function with the element @note If one of 'action' invocations returns 1 the iteration aborts. 'action' might see some elements twice! @retval 0 ok @retval 1 error (action returned 1) */ int lf_hash_iterate(LF_HASH *hash, LF_PINS *pins, my_hash_walk_action action, void *argument) { CURSOR cursor; uint bucket= 0; int res; LF_SLIST * volatile *el; el= lf_dynarray_lvalue(&hash->array, bucket); if (unlikely(!el)) return 0; /* if there's no bucket==0, the hash is empty */ if (*el == NULL && unlikely(initialize_bucket(hash, el, bucket, pins))) return 0; /* if there's no bucket==0, the hash is empty */ res= l_find(el, 0, 0, (uchar*)argument, 0, &cursor, pins, action); lf_unpin(pins, 2); lf_unpin(pins, 1); lf_unpin(pins, 0); return res; }
/* DESCRIPTION insert a 'node' in the list that starts from 'head' in the correct position (as found by l_find) RETURN 0 - inserted not 0 - a pointer to a duplicate (not pinned and thus unusable) NOTE it uses pins[0..2], on return all pins are removed. if there're nodes with the same key value, a new node is added before them. */ static LF_SLIST *l_insert(LF_SLIST * volatile *head, CHARSET_INFO *cs, LF_SLIST *node, LF_PINS *pins, uint flags) { CURSOR cursor; int res; for (;;) { if (l_find(head, cs, node->hashnr, node->key, node->keylen, &cursor, pins) && (flags & LF_HASH_UNIQUE)) { res= 0; /* duplicate found */ break; } else { node->link= (intptr)cursor.curr; DBUG_ASSERT(node->link != (intptr)node); /* no circular references */ DBUG_ASSERT(cursor.prev != &node->link); /* no circular references */ if (my_atomic_casptr((void **) cursor.prev, (void **)(char*) &cursor.curr, node)) { res= 1; /* inserted ok */ break; } } } _lf_unpin(pins, 0); _lf_unpin(pins, 1); _lf_unpin(pins, 2); /* Note that cursor.curr is not pinned here and the pointer is unreliable, the object may dissapear anytime. But if it points to a dummy node, the pointer is safe, because dummy nodes are never freed - initialize_bucket() uses this fact. */ return res ? 0 : cursor.curr; }
value_t l_insert(node_t *head, key_t key, value_t val) { node_t *pred, *item, *new_item; while (TRUE) { if (l_find(&pred, &item, head, key)) { /* update its value */ /* 更新值时,使用item->val互斥,NULL_VALUE表示被删除 */ node_t *sitem = STRIP_MARK(item); value_t old_val = sitem->val; while (old_val != NULL_VALUE) { value_t ret = CAS_V(&sitem->val, old_val, val); if (ret == old_val) { trace("update item %p value success\n", item); return ret; } trace("update item lost race %p\n", item); old_val = ret; } trace("item %p removed, retry\n", item); continue; /* the item has been removed, we retry */ } new_item = (node_t*) malloc(sizeof(node_t)); new_item->key = key; new_item->val = val; new_item->next = item; /* a). pred是否有效问题:无效只会发生在使用pred->next时,而l_remove正移除该pred 就会在CAS(&item->next)中竞争,如果remove中成功,那么insert CAS就失败, 然后重试;反之,remove失败重试,所以保证了pred有效 b). item本身增加tag一定程度上降低ABA问题几率 */ if (CAS(&pred->next, item, new_item)) { trace("insert item %p(next) %p success\n", item, new_item); return val; } trace("cas item %p failed, retry\n", item); free(new_item); } return NULL_VALUE; }
void big_test() { linked_list list = l_create(); l_add_front(list, 3); length_test(list, 1); front_test(list, 3); back_test(list, 3); //list = 3 l_add_back(list, 4); length_test(list, 2); front_test(list, 3); back_test(list, 4); //list = 3, 4 l_add_front(list, 2); length_test(list, 3); front_test(list, 2); back_test(list, 4); //list = 2, 3, 4 int array1[] = {2, 3, 4}; print_test(list, array1, 3); l_add_front(list, 1); l_add_back(list, 5); length_test(list, 5); front_test(list, 1); back_test(list, 5); //list = 1, 2, 3, 4, 5 find_test(list, 3, 1); link l = l_find(list, 3); l_remove(list, l); find_test(list, 3, 0); length_test(list, 4); front_test(list, 1); back_test(list, 5); //list = 1, 2, 4, 5 int array2[] = {1, 2, 4, 5}; print_test(list, array2, 4); l_remove_back(list); length_test(list, 3); back_test(list, 4); //list = 1, 2, 4 l_remove_front(list); length_test(list, 2); front_test(list, 2); //list = 2, 4 l_destroy(list); }
int l_exist(node_t *head, key_t key) { node_t *pred, *item; return l_find(&pred, &item, head, key); }