diff options
author | unknown <serg@janus.mylan> | 2006-08-10 19:19:47 +0200 |
---|---|---|
committer | unknown <serg@janus.mylan> | 2006-08-10 19:19:47 +0200 |
commit | cd876fb11883f68f93027a70b5f3f99ad9234f27 (patch) | |
tree | 606c0ae70958f725f45d6d8f2bfbbf437a3873df /mysys/lf_alloc-pin.c | |
parent | fe84903b15772782aa3bfbaa5fee60d480eaa4f2 (diff) | |
download | mariadb-git-cd876fb11883f68f93027a70b5f3f99ad9234f27.tar.gz |
amd64 atomic ops
lock-free alloc (WL#3229), lock-free hash (WL#3230)
bit functions made inline
include/Makefile.am:
lf.h added
mysys/Makefile.am:
lf_hash.c lf_dynarray.c lf_alloc-pin.c
include/atomic/nolock.h:
amd64 atomic ops
include/atomic/rwlock.h:
s/rw_lock/mutex/g
include/atomic/x86-gcc.h:
amd64 atomic ops
try PAUSE
include/my_global.h:
STATIC_INLINE
mysys/mf_keycache.c:
make bit functions inline
mysys/my_atomic.c:
STATIC_INLINE
mysys/my_bitmap.c:
make bit functions inline
sql/ha_myisam.cc:
make bit functions inline
sql/item_func.cc:
make bit functions inline
include/my_atomic.h:
STATIC_INLINE
mysys/my_bit.c:
make bit functions inline
sql/sql_select.cc:
make bit functions inline
storage/myisam/mi_create.c:
make bit functions inline
storage/myisam/mi_test2.c:
make bit functions inline
storage/myisam/myisamchk.c:
make bit functions inline
mysys/my_init.c:
thread_size moved to mysys
sql/mysql_priv.h:
thread_size moved to mysys
sql/set_var.cc:
thread_size moved to mysys
include/my_sys.h:
thread_size moved to mysys
sql/mysqld.cc:
thread_size moved to mysys
sql/sql_parse.cc:
thread_size moved to mysys
sql/sql_test.cc:
thread_size moved to mysys
include/lf.h:
dylf_dynarray refactored to remove 65536 elements limit
mysys/lf_alloc-pin.c:
dylf_dynarray refactored to remove 65536 elements limit
mysys/lf_dynarray.c:
dylf_dynarray refactored to remove 65536 elements limit
mysys/lf_hash.c:
dylf_dynarray refactored to remove 65536 elements limit
unittest/mysys/my_atomic-t.c:
fix to commit (remove debug code)
Diffstat (limited to 'mysys/lf_alloc-pin.c')
-rw-r--r-- | mysys/lf_alloc-pin.c | 319 |
1 files changed, 319 insertions, 0 deletions
diff --git a/mysys/lf_alloc-pin.c b/mysys/lf_alloc-pin.c new file mode 100644 index 00000000000..a9ea1802c03 --- /dev/null +++ b/mysys/lf_alloc-pin.c @@ -0,0 +1,319 @@ +/* Copyright (C) 2000 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/* + concurrent allocator based on pinning addresses + + strictly speaking it's not lock-free, as it can be blocked + if a thread's purgatory is full and all addresses from there + are pinned. + + But until the above happens, it's wait-free. + + It can be made strictly wait-free by increasing purgatory size. + If it's larger than pins_in_stack*LF_PINBOX_PINS, then apocalyptical + condition above will never happen. But than the memory requirements + will be O(pins_in_stack^2). + + Note, that for large purgatory sizes it makes sense to remove + purgatory array, and link objects in a list using embedded pointer. + + TODO test with more than 256 threads + TODO test w/o alloca +*/ + +#include <my_global.h> +#include <my_sys.h> +#include <lf.h> + +#define LF_PINBOX_MAX_PINS 65536 + +static void _lf_pinbox_real_free(LF_PINS *pins); + +void lf_pinbox_init(LF_PINBOX *pinbox, lf_pinbox_free_func *free_func, + void *free_func_arg) +{ + DBUG_ASSERT(sizeof(LF_PINS) == 128); + lf_dynarray_init(&pinbox->pinstack, sizeof(LF_PINS)); + pinbox->pinstack_top_ver=0; + pinbox->pins_in_stack=0; + pinbox->free_func=free_func; + pinbox->free_func_arg=free_func_arg; +} + +void lf_pinbox_end(LF_PINBOX *pinbox) +{ + lf_dynarray_end(&pinbox->pinstack); +} + +LF_PINS *_lf_pinbox_get_pins(LF_PINBOX *pinbox) +{ + uint32 pins, next, top_ver; + LF_PINS *el; + + top_ver=pinbox->pinstack_top_ver; + do + { + if (!(pins=top_ver % LF_PINBOX_MAX_PINS)) + { + pins=my_atomic_add32(&pinbox->pins_in_stack, 1)+1; + el=(LF_PINS *)_lf_dynarray_lvalue(&pinbox->pinstack, pins); + break; + } + el=(LF_PINS *)_lf_dynarray_value(&pinbox->pinstack, pins); + next=el->link; + } while (!my_atomic_cas32(&pinbox->pinstack_top_ver, &top_ver, + top_ver-pins+next+LF_PINBOX_MAX_PINS)); + el->link=pins; + el->purgatory_count=0; + el->pinbox=pinbox; + return el; +} + +void _lf_pinbox_put_pins(LF_PINS *pins) +{ + LF_PINBOX *pinbox=pins->pinbox; + uint32 top_ver, nr; + nr=pins->link; +#ifdef MY_LF_EXTRA_DEBUG + { + int i; + for (i=0; i < LF_PINBOX_PINS; i++) + assert(pins->pin[i] == 0); + } +#endif + while (pins->purgatory_count) + { + _lf_pinbox_real_free(pins); + if (pins->purgatory_count && my_getncpus() == 1) + { + my_atomic_rwlock_wrunlock(&pins->pinbox->pinstack.lock); + pthread_yield(); + my_atomic_rwlock_wrlock(&pins->pinbox->pinstack.lock); + } + } + top_ver=pinbox->pinstack_top_ver; + if (nr == pinbox->pins_in_stack) + { + int32 tmp=nr; + if (my_atomic_cas32(&pinbox->pins_in_stack, &tmp, tmp-1)) + goto ret; + } + + do + { + pins->link=top_ver % LF_PINBOX_MAX_PINS; + } while (!my_atomic_cas32(&pinbox->pinstack_top_ver, &top_ver, + top_ver-pins->link+nr+LF_PINBOX_MAX_PINS)); +ret: + return; +} + +static int ptr_cmp(void **a, void **b) +{ + return *a < *b ? -1 : *a == *b ? 0 : 1; +} + +void _lf_pinbox_free(LF_PINS *pins, void *addr) +{ + while (pins->purgatory_count == LF_PURGATORY_SIZE) + { + _lf_pinbox_real_free(pins); + if (pins->purgatory_count == LF_PURGATORY_SIZE && my_getncpus() == 1) + { + my_atomic_rwlock_wrunlock(&pins->pinbox->pinstack.lock); + pthread_yield(); + my_atomic_rwlock_wrlock(&pins->pinbox->pinstack.lock); + } + } + pins->purgatory[pins->purgatory_count++]=addr; +} + +struct st_harvester { + void **granary; + int npins; +}; + +static int harvest_pins(LF_PINS *el, struct st_harvester *hv) +{ + int i; + LF_PINS *el_end= el+min(hv->npins, LF_DYNARRAY_LEVEL_LENGTH); + for (; el < el_end; el++) + { + for (i= 0; i < LF_PINBOX_PINS; i++) + { + void *p= el->pin[i]; + if (p) + *hv->granary++= p; + } + } + hv->npins-= LF_DYNARRAY_LEVEL_LENGTH; + return 0; +} + +static int match_pins(LF_PINS *el, void *addr) +{ + int i; + LF_PINS *el_end= el+LF_DYNARRAY_LEVEL_LENGTH; + for (; el < el_end; el++) + for (i= 0; i < LF_PINBOX_PINS; i++) + if (el->pin[i] == addr) + return 1; + return 0; +} + +static void _lf_pinbox_real_free(LF_PINS *pins) +{ + int npins; + void **addr=0; + void **start, **cur, **end=pins->purgatory+pins->purgatory_count; + LF_PINBOX *pinbox=pins->pinbox; + + npins=pinbox->pins_in_stack+1; + +#ifdef HAVE_ALLOCA + /* create a sorted list of pinned addresses, to speed up searches */ + if (sizeof(void *)*LF_PINBOX_PINS*npins < my_thread_stack_size) + { + struct st_harvester hv; + addr= (void **) alloca(sizeof(void *)*LF_PINBOX_PINS*npins); + hv.granary=addr; + hv.npins=npins; + _lf_dynarray_iterate(&pinbox->pinstack, + (lf_dynarray_func)harvest_pins, &hv); + + npins=hv.granary-addr; + if (npins) + qsort(addr, npins, sizeof(void *), (qsort_cmp)ptr_cmp); + } +#endif + + start= cur= pins->purgatory; + end= start+pins->purgatory_count; + for (; cur < end; cur++) + { + if (npins) + { + if (addr) + { + void **a,**b,**c; + for (a=addr, b=addr+npins-1, c=a+(b-a)/2; b-a>1; c=a+(b-a)/2) + if (*cur == *c) + a=b=c; + else if (*cur > *c) + a=c; + else + b=c; + if (*cur == *a || *cur == *b) + goto found; + } + else + { + if (_lf_dynarray_iterate(&pinbox->pinstack, + (lf_dynarray_func)match_pins, *cur)) + goto found; + } + } + /* not pinned - freeing */ + pinbox->free_func(*cur, pinbox->free_func_arg); + continue; +found: + /* pinned - keeping */ + *start++=*cur; + } + pins->purgatory_count=start-pins->purgatory; +#ifdef MY_LF_EXTRA_DEBUG + while (start < pins->purgatory + LF_PURGATORY_SIZE) + *start++=0; +#endif +} + +static void alloc_free(void *node, LF_ALLOCATOR *allocator) +{ + void *tmp; + tmp=allocator->top; + do + { + (*(void **)node)=tmp; + } while (!my_atomic_casptr((void **)&allocator->top, (void **)&tmp, node) && + LF_BACKOFF); +} + +LF_REQUIRE_PINS(1); +void *_lf_alloc_new(LF_PINS *pins) +{ + LF_ALLOCATOR *allocator=(LF_ALLOCATOR *)(pins->pinbox->free_func_arg); + void *node; + for (;;) + { + do + { + node=allocator->top; + _lf_pin(pins, 0, node); + } while (node !=allocator->top && LF_BACKOFF); + if (!node) + { + if (!(node=my_malloc(allocator->element_size, MYF(MY_WME|MY_ZEROFILL)))) + goto ret; +#ifdef MY_LF_EXTRA_DEBUG + my_atomic_add32(&allocator->mallocs, 1); +#endif + goto ret; + } + if (my_atomic_casptr((void **)&allocator->top, + (void *)&node, *(void **)node)) + goto ret; + } +ret: + _lf_unpin(pins, 0); + return node; +} + +void lf_alloc_init(LF_ALLOCATOR *allocator, uint size) +{ + lf_pinbox_init(&allocator->pinbox, + (lf_pinbox_free_func *)alloc_free, allocator); + allocator->top=0; + allocator->mallocs=0; + allocator->element_size=size; + DBUG_ASSERT(size >= (int)sizeof(void *)); +} + +void lf_alloc_end(LF_ALLOCATOR *allocator) +{ + void *el=allocator->top; + while (el) + { + void *tmp=*(void **)el; + my_free(el, MYF(0)); + el=tmp; + } + lf_pinbox_end(&allocator->pinbox); + allocator->top=0; +} + +/* + NOTE + this is NOT thread-safe !!! +*/ +uint lf_alloc_in_pool(LF_ALLOCATOR *allocator) +{ + uint i; + void *node; + for (node=allocator->top, i=0; node; node=*(void **)node, i++) /* no op */; + return i; +} + |