/* DESCRIPTION inserts a new element to a hash. it will have a _copy_ of data, not a pointer to it. RETURN 0 - inserted 1 - didn't (unique key conflict) -1 - out of memory NOTE see l_insert() for pin usage notes */ int lf_hash_insert(LF_HASH *hash, LF_PINS *pins, const void *data) { int csize, bucket, hashnr; LF_SLIST *node, * volatile *el; node= (LF_SLIST *)lf_alloc_new(pins); if (unlikely(!node)) return -1; hash->initializer(hash, node + 1, data); node->key= hash_key(hash, (uchar *)(node+1), &node->keylen); hashnr= hash->hash_function(hash->charset, node->key, node->keylen) & INT_MAX32; bucket= hashnr % hash->size; el= lf_dynarray_lvalue(&hash->array, bucket); if (unlikely(!el)) return -1; if (*el == NULL && unlikely(initialize_bucket(hash, el, bucket, pins))) return -1; node->hashnr= my_reverse_bits(hashnr) | 1; /* normal node */ if (l_insert(el, hash->charset, node, pins, hash->flags)) { lf_alloc_free(pins, node); return 1; } csize= hash->size; if ((my_atomic_add32(&hash->count, 1)+1.0) / csize > MAX_LOAD) my_atomic_cas32(&hash->size, &csize, csize*2); return 0; }
pthread_handler_t test_lf_alloc(void *arg) { int m= (*(int *)arg)/2; int32 x,y= 0; LF_PINS *pins; if (with_my_thread_init) my_thread_init(); pins= lf_alloc_get_pins(&lf_allocator); for (x= ((int)(intptr)(&m)); m ; m--) { TLA *node1, *node2; x= (x*m+0x87654321) & INT_MAX32; node1= (TLA *)lf_alloc_new(pins); node1->data= x; y+= node1->data; node1->data= 0; node2= (TLA *)lf_alloc_new(pins); node2->data= x; y-= node2->data; node2->data= 0; lf_alloc_free(pins, node1); lf_alloc_free(pins, node2); } lf_alloc_put_pins(pins); pthread_mutex_lock(&mutex); bad+= y; if (--N == 0) { diag("%d mallocs, %d pins in stack", lf_allocator.mallocs, lf_allocator.pinbox.pins_in_array); #ifdef MY_LF_EXTRA_DEBUG bad|= lf_allocator.mallocs - lf_alloc_pool_count(&lf_allocator); #endif } if (!--running_threads) pthread_cond_signal(&cond); pthread_mutex_unlock(&mutex); if (with_my_thread_init) my_thread_end(); return 0; }
/* 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, 0)) { 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, 0); } res= 0; break; } } } lf_unpin(pins, 0); lf_unpin(pins, 1); lf_unpin(pins, 2); return res; }
/** walk the list, searching for an element or invoking a callback Search for hashnr/key/keylen in the list starting from 'head' and position the cursor. The list is ORDER BY hashnr, key @param head start walking the list from this node @param cs charset for comparing keys, NULL if callback is used @param hashnr hash number to search for @param key key to search for OR data for the callback @param keylen length of the key to compare, 0 if callback is used @param cursor for returning the found element @param pins see lf_alloc-pin.c @param callback callback action, invoked for every element @note cursor is positioned in either case pins[0..2] are used, they are NOT removed on return callback might see some elements twice (because of retries) @return if find: 0 - not found 1 - found if callback: 0 - ok 1 - error (callbck returned 1) */ static int l_find(LF_SLIST * volatile *head, CHARSET_INFO *cs, uint32 hashnr, const uchar *key, uint keylen, CURSOR *cursor, LF_PINS *pins, my_hash_walk_action callback) { uint32 cur_hashnr; const uchar *cur_key; uint cur_keylen; intptr link; DBUG_ASSERT(!cs || !callback); /* should not be set both */ DBUG_ASSERT(!keylen || !callback); /* should not be set both */ retry: cursor->prev= (intptr *)head; do { /* PTR() isn't necessary below, head is a dummy node */ cursor->curr= (LF_SLIST *)(*cursor->prev); lf_pin(pins, 1, cursor->curr); } while (*cursor->prev != (intptr)cursor->curr && LF_BACKOFF); for (;;) { if (unlikely(!cursor->curr)) return 0; /* end of the list */ cur_hashnr= cursor->curr->hashnr; cur_keylen= cursor->curr->keylen; cur_key= cursor->curr->key; do { link= cursor->curr->link; cursor->next= PTR(link); lf_pin(pins, 0, cursor->next); } while (link != cursor->curr->link && LF_BACKOFF); if (!DELETED(link)) { if (unlikely(callback)) { if (cur_hashnr & 1 && callback(cursor->curr + 1, (void*)key)) return 1; } else if (cur_hashnr >= hashnr) { int r= 1; if (cur_hashnr > hashnr || (r= my_strnncoll(cs, cur_key, cur_keylen, key, keylen)) >= 0) return !r; } cursor->prev= &(cursor->curr->link); if (!(cur_hashnr & 1)) /* dummy node */ head= (LF_SLIST **)cursor->prev; lf_pin(pins, 2, cursor->curr); } else { /* we found a deleted node - be nice, help the other thread and remove this deleted node */ if (my_atomic_casptr((void **) cursor->prev, (void **) &cursor->curr, cursor->next) && LF_BACKOFF) lf_alloc_free(pins, cursor->curr); else goto retry; } cursor->curr= cursor->next; lf_pin(pins, 1, cursor->curr); } }