Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
M
mariadb
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
mariadb
Commits
a79868ae
Commit
a79868ae
authored
Oct 19, 2006
by
unknown
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
comments
parent
aea73116
Changes
1
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
159 additions
and
34 deletions
+159
-34
mysys/lf_alloc-pin.c
mysys/lf_alloc-pin.c
+159
-34
No files found.
mysys/lf_alloc-pin.c
View file @
a79868ae
...
@@ -17,8 +17,63 @@
...
@@ -17,8 +17,63 @@
/*
/*
wait-free concurrent allocator based on pinning addresses
wait-free concurrent allocator based on pinning addresses
TODO test with more than 256 threads
It works as follows: every thread (strictly speaking - every CPU, but it's
TODO test w/o alloca
too difficult to do) has a small array of pointers. They're called "pins".
Before using an object its address must be stored in this array (pinned).
When an object is no longer necessary its address must be removed from
this array (unpinned). When a thread wants to free() an object it
scans all pins of all threads to see if somebody has this object pinned.
If yes - the object is not freed (but stored in a purgatory).
To reduce the cost of a single free() pins are not scanned on every free()
but only added to (thread-local) purgatory. On every LF_PURGATORY_SIZE
free() purgatory is scanned and all unpinned objects are freed.
Pins are used to solve ABA problem. To use pins one must obey
a pinning protocol:
1. Let's assume that PTR is a shared pointer to an object. Shared means
that any thread may modify it anytime to point to a different object and
free the old object. Later the freed object may be potentially allocated
by another thread. If we're unlucky that another thread may set PTR to
point to this object again. This is ABA problem.
2. Create a local pointer LOCAL_PTR.
3. Pin the PTR in a loop:
do
{
LOCAL_PTR= PTR;
pin(PTR, PIN_NUMBER);
} while (LOCAL_PTR != PTR)
4. It is guaranteed that after the loop is ended, LOCAL_PTR
points to an object (or NULL, if PTR may be NULL), that
will never be freed. It is not guaranteed though
that LOCAL_PTR == PTR
5. When done working with the object, remove the pin:
unpin(PIN_NUMBER)
6. When copying pins (as in the list:
while ()
{
pin(CUR, 0);
do
{
NEXT=CUR->next;
pin(NEXT, 1);
} while (NEXT != CUR->next);
...
...
pin(CUR, 1);
CUR=NEXT;
}
which keeps CUR address constantly pinned), note than pins may be copied
only upwards (!!!), that is pin N to pin M > N.
7. Don't keep the object pinned longer than necessary - the number of pins
you have is limited (and small), keeping an object pinned prevents its
reuse and cause unnecessary mallocs.
Implementation details:
Pins are given away from a "pinbox". Pinbox is stack-based allocator.
It used dynarray for storing pins, new elements are allocated by dynarray
as necessary, old are pushed in the stack for reuse. ABA is solved by
versioning a pointer - because we use an array, a pointer to pins is 32 bit,
upper 32 bits are used for a version.
*/
*/
#include <my_global.h>
#include <my_global.h>
...
@@ -29,6 +84,10 @@
...
@@ -29,6 +84,10 @@
static
void
_lf_pinbox_real_free
(
LF_PINS
*
pins
);
static
void
_lf_pinbox_real_free
(
LF_PINS
*
pins
);
/*
Initialize a pinbox. Must be usually called from lf_alloc_init.
See the latter for details.
*/
void
lf_pinbox_init
(
LF_PINBOX
*
pinbox
,
uint
free_ptr_offset
,
void
lf_pinbox_init
(
LF_PINBOX
*
pinbox
,
uint
free_ptr_offset
,
lf_pinbox_free_func
*
free_func
,
void
*
free_func_arg
)
lf_pinbox_free_func
*
free_func
,
void
*
free_func_arg
)
{
{
...
@@ -47,6 +106,14 @@ void lf_pinbox_destroy(LF_PINBOX *pinbox)
...
@@ -47,6 +106,14 @@ void lf_pinbox_destroy(LF_PINBOX *pinbox)
lf_dynarray_destroy
(
&
pinbox
->
pinstack
);
lf_dynarray_destroy
(
&
pinbox
->
pinstack
);
}
}
/*
Get pins from a pinbox. Usually called via lf_alloc_get_pins() or
lf_hash_get_pins().
DESCRIPTION
get a new LF_PINS structure from a stack of unused pins,
or allocate a new one out of dynarray.
*/
LF_PINS
*
_lf_pinbox_get_pins
(
LF_PINBOX
*
pinbox
)
LF_PINS
*
_lf_pinbox_get_pins
(
LF_PINBOX
*
pinbox
)
{
{
uint32
pins
,
next
,
top_ver
;
uint32
pins
,
next
,
top_ver
;
...
@@ -71,6 +138,14 @@ LF_PINS *_lf_pinbox_get_pins(LF_PINBOX *pinbox)
...
@@ -71,6 +138,14 @@ LF_PINS *_lf_pinbox_get_pins(LF_PINBOX *pinbox)
return
el
;
return
el
;
}
}
/*
Put pins back to a pinbox. Usually called via lf_alloc_put_pins() or
lf_hash_put_pins().
DESCRIPTION
empty the purgatory (XXX deadlock warning below!),
push LF_PINS structure to a stack
*/
void
_lf_pinbox_put_pins
(
LF_PINS
*
pins
)
void
_lf_pinbox_put_pins
(
LF_PINS
*
pins
)
{
{
LF_PINBOX
*
pinbox
=
pins
->
pinbox
;
LF_PINBOX
*
pinbox
=
pins
->
pinbox
;
...
@@ -80,11 +155,11 @@ void _lf_pinbox_put_pins(LF_PINS *pins)
...
@@ -80,11 +155,11 @@ void _lf_pinbox_put_pins(LF_PINS *pins)
{
{
int
i
;
int
i
;
for
(
i
=
0
;
i
<
LF_PINBOX_PINS
;
i
++
)
for
(
i
=
0
;
i
<
LF_PINBOX_PINS
;
i
++
)
assert
(
pins
->
pin
[
i
]
==
0
);
DBUG_ASSERT
(
pins
->
pin
[
i
]
==
0
);
}
}
#endif
#endif
/*
/*
Note -
this will deadlock if other threads will wait for
XXX
this will deadlock if other threads will wait for
the caller to do something after _lf_pinbox_put_pins(),
the caller to do something after _lf_pinbox_put_pins(),
and they would have pinned addresses that the caller wants to free.
and they would have pinned addresses that the caller wants to free.
Thus: only free pins when all work is done and nobody can wait for you!!!
Thus: only free pins when all work is done and nobody can wait for you!!!
...
@@ -130,6 +205,13 @@ static int ptr_cmp(void **a, void **b)
...
@@ -130,6 +205,13 @@ static int ptr_cmp(void **a, void **b)
(PINS)->purgatory_count++; \
(PINS)->purgatory_count++; \
} while (0)
} while (0)
/*
Free an object allocated via pinbox allocator
DESCRIPTION
add an object to purgatory. if necessary, call _lf_pinbox_real_free()
to actually free something.
*/
void
_lf_pinbox_free
(
LF_PINS
*
pins
,
void
*
addr
)
void
_lf_pinbox_free
(
LF_PINS
*
pins
,
void
*
addr
)
{
{
if
(
pins
->
purgatory_count
%
LF_PURGATORY_SIZE
)
if
(
pins
->
purgatory_count
%
LF_PURGATORY_SIZE
)
...
@@ -142,6 +224,10 @@ struct st_harvester {
...
@@ -142,6 +224,10 @@ struct st_harvester {
int
npins
;
int
npins
;
};
};
/*
callback for _lf_dynarray_iterate:
scan all pins or all threads and accumulate all pins
*/
static
int
harvest_pins
(
LF_PINS
*
el
,
struct
st_harvester
*
hv
)
static
int
harvest_pins
(
LF_PINS
*
el
,
struct
st_harvester
*
hv
)
{
{
int
i
;
int
i
;
...
@@ -159,6 +245,10 @@ static int harvest_pins(LF_PINS *el, struct st_harvester *hv)
...
@@ -159,6 +245,10 @@ static int harvest_pins(LF_PINS *el, struct st_harvester *hv)
return
0
;
return
0
;
}
}
/*
callback for _lf_dynarray_iterate:
scan all pins or all threads and see if addr is present there
*/
static
int
match_pins
(
LF_PINS
*
el
,
void
*
addr
)
static
int
match_pins
(
LF_PINS
*
el
,
void
*
addr
)
{
{
int
i
;
int
i
;
...
@@ -170,6 +260,9 @@ static int match_pins(LF_PINS *el, void *addr)
...
@@ -170,6 +260,9 @@ static int match_pins(LF_PINS *el, void *addr)
return
0
;
return
0
;
}
}
/*
Scan the purgatory as free everything that can be freed
*/
static
void
_lf_pinbox_real_free
(
LF_PINS
*
pins
)
static
void
_lf_pinbox_real_free
(
LF_PINS
*
pins
)
{
{
int
npins
;
int
npins
;
...
@@ -187,10 +280,12 @@ static void _lf_pinbox_real_free(LF_PINS *pins)
...
@@ -187,10 +280,12 @@ static void _lf_pinbox_real_free(LF_PINS *pins)
addr
=
(
void
**
)
alloca
(
sizeof
(
void
*
)
*
LF_PINBOX_PINS
*
npins
);
addr
=
(
void
**
)
alloca
(
sizeof
(
void
*
)
*
LF_PINBOX_PINS
*
npins
);
hv
.
granary
=
addr
;
hv
.
granary
=
addr
;
hv
.
npins
=
npins
;
hv
.
npins
=
npins
;
/* scan the dynarray and accumulate all pinned addresses */
_lf_dynarray_iterate
(
&
pinbox
->
pinstack
,
_lf_dynarray_iterate
(
&
pinbox
->
pinstack
,
(
lf_dynarray_func
)
harvest_pins
,
&
hv
);
(
lf_dynarray_func
)
harvest_pins
,
&
hv
);
npins
=
hv
.
granary
-
addr
;
npins
=
hv
.
granary
-
addr
;
/* and sort them */
if
(
npins
)
if
(
npins
)
qsort
(
addr
,
npins
,
sizeof
(
void
*
),
(
qsort_cmp
)
ptr_cmp
);
qsort
(
addr
,
npins
,
sizeof
(
void
*
),
(
qsort_cmp
)
ptr_cmp
);
}
}
...
@@ -207,7 +302,7 @@ static void _lf_pinbox_real_free(LF_PINS *pins)
...
@@ -207,7 +302,7 @@ static void _lf_pinbox_real_free(LF_PINS *pins)
list
=
*
(
void
**
)((
char
*
)
cur
+
pinbox
->
free_ptr_offset
);
list
=
*
(
void
**
)((
char
*
)
cur
+
pinbox
->
free_ptr_offset
);
if
(
npins
)
if
(
npins
)
{
{
if
(
addr
)
if
(
addr
)
/* use binary search */
{
{
void
**
a
,
**
b
,
**
c
;
void
**
a
,
**
b
,
**
c
;
for
(
a
=
addr
,
b
=
addr
+
npins
-
1
,
c
=
a
+
(
b
-
a
)
/
2
;
b
-
a
>
1
;
c
=
a
+
(
b
-
a
)
/
2
)
for
(
a
=
addr
,
b
=
addr
+
npins
-
1
,
c
=
a
+
(
b
-
a
)
/
2
;
b
-
a
>
1
;
c
=
a
+
(
b
-
a
)
/
2
)
...
@@ -220,7 +315,7 @@ static void _lf_pinbox_real_free(LF_PINS *pins)
...
@@ -220,7 +315,7 @@ static void _lf_pinbox_real_free(LF_PINS *pins)
if
(
cur
==
*
a
||
cur
==
*
b
)
if
(
cur
==
*
a
||
cur
==
*
b
)
goto
found
;
goto
found
;
}
}
else
else
/* no alloca - no cookie. linear search here */
{
{
if
(
_lf_dynarray_iterate
(
&
pinbox
->
pinstack
,
if
(
_lf_dynarray_iterate
(
&
pinbox
->
pinstack
,
(
lf_dynarray_func
)
match_pins
,
cur
))
(
lf_dynarray_func
)
match_pins
,
cur
))
...
@@ -236,6 +331,10 @@ found:
...
@@ -236,6 +331,10 @@ found:
}
}
}
}
/*
callback for _lf_pinbox_real_free to free an unpinned object -
add it back to the allocator stack
*/
static
void
alloc_free
(
void
*
node
,
LF_ALLOCATOR
*
allocator
)
static
void
alloc_free
(
void
*
node
,
LF_ALLOCATOR
*
allocator
)
{
{
void
*
tmp
;
void
*
tmp
;
...
@@ -247,7 +346,55 @@ static void alloc_free(void *node, LF_ALLOCATOR *allocator)
...
@@ -247,7 +346,55 @@ static void alloc_free(void *node, LF_ALLOCATOR *allocator)
LF_BACKOFF
);
LF_BACKOFF
);
}
}
/* lock-free memory allocator for fixed-size objects */
LF_REQUIRE_PINS
(
1
);
LF_REQUIRE_PINS
(
1
);
/*
initialize lock-free allocatod.
SYNOPSYS
allocator -
size a size of an object to allocate
free_ptr_offset an offset inside the object to a sizeof(void *)
memory that is guaranteed to be unused after
the object is put in the purgatory. Unused by ANY
thread, not only the purgatory owner.
*/
void
lf_alloc_init
(
LF_ALLOCATOR
*
allocator
,
uint
size
,
uint
free_ptr_offset
)
{
lf_pinbox_init
(
&
allocator
->
pinbox
,
free_ptr_offset
,
(
lf_pinbox_free_func
*
)
alloc_free
,
allocator
);
allocator
->
top
=
0
;
allocator
->
mallocs
=
0
;
allocator
->
element_size
=
size
;
DBUG_ASSERT
(
size
>=
(
int
)
sizeof
(
void
*
));
DBUG_ASSERT
(
free_ptr_offset
<
size
);
}
/*
destroy the allocator, free everything that's in it
*/
void
lf_alloc_destroy
(
LF_ALLOCATOR
*
allocator
)
{
void
*
el
=
allocator
->
top
;
while
(
el
)
{
void
*
tmp
=
*
(
void
**
)
el
;
my_free
(
el
,
MYF
(
0
));
el
=
tmp
;
}
lf_pinbox_destroy
(
&
allocator
->
pinbox
);
allocator
->
top
=
0
;
}
/*
Allocate and return an new object.
DESCRIPTION
Pop an unused object from the stack or malloc it is the stack is empty.
pin[0] is used, it's removed on return.
*/
void
*
_lf_alloc_new
(
LF_PINS
*
pins
)
void
*
_lf_alloc_new
(
LF_PINS
*
pins
)
{
{
LF_ALLOCATOR
*
allocator
=
(
LF_ALLOCATOR
*
)(
pins
->
pinbox
->
free_func_arg
);
LF_ALLOCATOR
*
allocator
=
(
LF_ALLOCATOR
*
)(
pins
->
pinbox
->
free_func_arg
);
...
@@ -262,47 +409,25 @@ void *_lf_alloc_new(LF_PINS *pins)
...
@@ -262,47 +409,25 @@ void *_lf_alloc_new(LF_PINS *pins)
if
(
!
node
)
if
(
!
node
)
{
{
if
(
!
(
node
=
my_malloc
(
allocator
->
element_size
,
MYF
(
MY_WME
|
MY_ZEROFILL
))))
if
(
!
(
node
=
my_malloc
(
allocator
->
element_size
,
MYF
(
MY_WME
|
MY_ZEROFILL
))))
goto
ret
;
break
;
#ifdef MY_LF_EXTRA_DEBUG
#ifdef MY_LF_EXTRA_DEBUG
my_atomic_add32
(
&
allocator
->
mallocs
,
1
);
my_atomic_add32
(
&
allocator
->
mallocs
,
1
);
#endif
#endif
goto
ret
;
break
;
}
}
if
(
my_atomic_casptr
((
void
**
)
&
allocator
->
top
,
if
(
my_atomic_casptr
((
void
**
)
&
allocator
->
top
,
(
void
*
)
&
node
,
*
(
void
**
)
node
))
(
void
*
)
&
node
,
*
(
void
**
)
node
))
goto
ret
;
break
;
}
}
ret:
_lf_unpin
(
pins
,
0
);
_lf_unpin
(
pins
,
0
);
return
node
;
return
node
;
}
}
void
lf_alloc_init
(
LF_ALLOCATOR
*
allocator
,
uint
size
,
uint
free_ptr_offset
)
{
lf_pinbox_init
(
&
allocator
->
pinbox
,
free_ptr_offset
,
(
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_destroy
(
LF_ALLOCATOR
*
allocator
)
{
void
*
el
=
allocator
->
top
;
while
(
el
)
{
void
*
tmp
=
*
(
void
**
)
el
;
my_free
(
el
,
MYF
(
0
));
el
=
tmp
;
}
lf_pinbox_destroy
(
&
allocator
->
pinbox
);
allocator
->
top
=
0
;
}
/*
/*
count the number of objects in a pool.
NOTE
NOTE
t
his is NOT thread-safe !!!
T
his is NOT thread-safe !!!
*/
*/
uint
lf_alloc_in_pool
(
LF_ALLOCATOR
*
allocator
)
uint
lf_alloc_in_pool
(
LF_ALLOCATOR
*
allocator
)
{
{
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment