Skip to content
Toggle navigation
P
Projects
G
Groups
S
Snippets
Help
FORMUS3IC_LAS3
/
embb
This project
Loading...
Sign in
Toggle navigation
Go to a project
Project
Repository
Issues
0
Merge Requests
0
Pipelines
Wiki
Members
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Commit
053ca488
authored
Oct 21, 2015
by
Christian Kern
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Worked on review comments for ticket #523
parent
a023d6e4
Show whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
491 additions
and
501 deletions
+491
-501
base_c/src/internal/thread_index.c
+12
-6
containers_cpp/include/embb/containers/internal/hazard_pointer-inl.h
+163
-182
containers_cpp/include/embb/containers/internal/hazard_pointer.h
+123
-114
containers_cpp/test/hazard_pointer_test.cc
+147
-150
containers_cpp/test/hazard_pointer_test.h
+46
-49
No files found.
base_c/src/internal/thread_index.c
View file @
053ca488
...
...
@@ -128,13 +128,19 @@ void embb_internal_thread_index_set_max(unsigned int max) {
*
embb_max_number_thread_indices
()
=
max
;
}
/**
* \pre the calling thread is the only active thread
*
* \post the thread indices count and calling thread index is reset
*/
void
embb_internal_thread_index_reset
()
{
// This function is only called in tests, usually when all other threads
// except the main thread have terminated. However, the main thread still has
// potentially still stored its old index value in its thread local storage,
// which might be assigned additionally to another thread (as the counter is
// reset), which may lead to hard to detect bugs. Therefore, reset the thread
// local thread id here.
/** This function is only called in tests, usually when all other threads
* except the main thread have terminated. However, the main thread still has
* potentially stored its old index value in its thread local storage,
* which might be assigned additionally to another thread (as the counter is
* reset), which may lead to hard to detect bugs. Therefore, reset the thread
* local thread id here.
*/
embb_internal_thread_index_var
=
UINT_MAX
;
embb_counter_init
(
embb_thread_index_counter
());
...
...
containers_cpp/include/embb/containers/internal/hazard_pointer-inl.h
View file @
053ca488
...
...
@@ -30,7 +30,6 @@
namespace
embb
{
namespace
containers
{
namespace
internal
{
// Visual Studio is complaining, that the return in the last line of this
// function is not reachable. This is true, as long as exceptions are enabled.
// Otherwise, the exception becomes an assertion and with disabling assertions,
...
...
@@ -39,32 +38,31 @@ namespace internal {
#pragma warning(push)
#pragma warning(disable:4702)
#endif
template
<
typename
GuardType
>
unsigned
int
HazardPointer
<
GuardType
>::
GetCurrentThreadIndex
()
{
template
<
typename
GuardType
>
unsigned
int
HazardPointer
<
GuardType
>::
GetObjectLocalThreadIndex
()
{
// first, get the EMBB native thread id.
unsigned
int
embbThreadI
ndex
;
unsigned
int
embb_thread_i
ndex
;
int
return_val
=
embb_internal_thread_index
(
&
embbThreadI
ndex
);
int
return_val
=
embb_internal_thread_index
(
&
embb_thread_i
ndex
);
if
(
return_val
!=
EMBB_SUCCESS
)
{
EMBB_THROW
(
embb
::
base
::
ErrorException
,
"Could not get thread id"
);
}
// iterate over the mappings array
for
(
unsigned
int
i
=
0
;
i
!=
accessorCount
;
++
i
)
{
for
(
unsigned
int
i
=
0
;
i
!=
max_accessors_count_
;
++
i
)
{
// end of mappings? then we need to write our id
if
(
threadIdMapping
[
i
]
==
-
1
)
{
if
(
thread_id_mapping_
[
i
]
==
-
1
)
{
// try to CAS the initial value with out thread id
int
expected
=
-
1
;
if
(
threadIdMapping
[
i
].
CompareAndSwap
(
expected
,
static_cast
<
int
>
(
embbThreadI
ndex
)))
{
if
(
thread_id_mapping_
[
i
].
CompareAndSwap
(
expected
,
static_cast
<
int
>
(
embb_thread_i
ndex
)))
{
//successful, return our mapping
return
i
;
}
}
if
(
threadIdMapping
[
i
]
==
static_cast
<
int
>
(
embbThreadI
ndex
))
{
if
(
thread_id_mapping_
[
i
]
==
static_cast
<
int
>
(
embb_thread_i
ndex
))
{
// found our mapping!
return
i
;
}
...
...
@@ -75,176 +73,157 @@ unsigned int HazardPointer< GuardType >::GetCurrentThreadIndex() {
EMBB_THROW
(
embb
::
base
::
ErrorException
,
"Too many accessors"
);
return
0
;
}
}
#ifdef EMBB_PLATFORM_COMPILER_MSVC
#pragma warning(pop)
#endif
template
<
typename
GuardType
>
void
HazardPointer
<
GuardType
>::
RemoveGuard
(
int
guardP
osition
){
const
unsigned
int
myThreadId
=
GetCurrent
ThreadIndex
();
template
<
typename
GuardType
>
void
HazardPointer
<
GuardType
>::
RemoveGuard
(
int
guard_p
osition
){
const
unsigned
int
my_thread_id
=
GetObjectLocal
ThreadIndex
();
// check invariants...
assert
(
guardPosition
<
guardsPerThread
&&
myThreadId
<
accessorCount
);
assert
(
guard_position
<
max_guards_per_thread_
);
assert
(
my_thread_id
<
max_accessors_count_
);
// set guard
guards
[
guardPosition
*
accessorCount
+
myThreadId
]
=
undefinedGuard
;
}
guards_
[
guard_position
*
max_accessors_count_
+
my_thread_id
]
=
undefined_guard_
;
}
template
<
typename
GuardType
>
HazardPointer
<
GuardType
>::
HazardPointer
(
template
<
typename
GuardType
>
HazardPointer
<
GuardType
>::
HazardPointer
(
embb
::
base
::
Function
<
void
,
GuardType
>
freeGuardCallback
,
GuardType
undefinedGuard
,
int
guardsPerThread
,
int
accessors
)
:
accessorCount
(
accessors
==
-
1
?
embb
::
base
::
Thread
::
GetThreadsMaxCount
()
:
accessors
),
undefinedGuard
(
undefinedGuard
),
guardsPerThread
(
guardsPerThread
),
freeGuardCallback
(
freeGuardCallback
)
{
threadIdMapping
=
static_cast
<
embb
::
base
::
Atomic
<
int
>*>
(
GuardType
undefined_guard
,
int
guardsPerThread
,
int
accessors
)
:
max_accessors_count_
(
accessors
<
0
?
embb
::
base
::
Thread
::
GetThreadsMaxCount
()
:
accessors
),
undefined_guard_
(
undefined_guard
),
max_guards_per_thread_
(
guardsPerThread
),
release_object_callback_
(
freeGuardCallback
),
thread_id_mapping_
(
static_cast
<
embb
::
base
::
Atomic
<
int
>*>
(
embb
::
base
::
Allocation
::
Allocate
(
sizeof
(
embb
::
base
::
Atomic
<
int
>
)
*
accessorCount
));
for
(
unsigned
int
i
=
0
;
i
!=
accessorCount
;
++
i
)
{
//in-place new for each cell
new
(
&
threadIdMapping
[
i
])
embb
::
base
::
Atomic
<
int
>
;
}
guards
=
static_cast
<
embb
::
base
::
Atomic
<
GuardType
>*>
*
max_accessors_count_
))),
guards_
(
static_cast
<
embb
::
base
::
Atomic
<
GuardType
>*>
(
embb
::
base
::
Allocation
::
Allocate
(
sizeof
(
embb
::
base
::
Atomic
<
GuardType
>
)
*
max_guards_per_thread_
*
max_accessors_count_
))),
thread_local_retired_lists_temp_
(
static_cast
<
GuardType
*>
(
embb
::
base
::
Allocation
::
Allocate
(
sizeof
(
embb
::
base
::
Atomic
<
GuardType
>
)
*
guardsPerThread
*
accessorCount
));
sizeof
(
GuardType
)
*
max_guards_per_thread_
*
max_accessors_count_
*
max_accessors_count_
))),
thread_local_retired_lists_
(
static_cast
<
GuardType
*>
(
embb
::
base
::
Allocation
::
Allocate
(
sizeof
(
GuardType
)
*
max_guards_per_thread_
*
max_accessors_count_
*
max_accessors_count_
)))
{
const
unsigned
int
count_guards
=
max_guards_per_thread_
*
max_accessors_count_
;
const
unsigned
int
count_ret_elements
=
count_guards
*
max_accessors_count_
;
for
(
unsigned
int
i
=
0
;
i
!=
guardsPerThread
*
accessorCount
;
++
i
)
{
for
(
unsigned
int
i
=
0
;
i
!=
max_accessors_count_
;
++
i
)
{
//in-place new for each cell
new
(
&
guards
[
i
])
embb
::
base
::
Atomic
<
GuardType
>
;
new
(
&
thread_id_mapping_
[
i
])
embb
::
base
::
Atomic
<
int
>
(
-
1
)
;
}
threadLocalRetiredListsTemp
=
static_cast
<
GuardType
*>
(
embb
::
base
::
Allocation
::
Allocate
(
sizeof
(
GuardType
)
*
guardsPerThread
*
accessorCount
*
accessorCount
));
for
(
unsigned
int
i
=
0
;
i
!=
guardsPerThread
*
accessorCount
*
accessorCount
;
++
i
)
{
for
(
unsigned
int
i
=
0
;
i
!=
count_guards
;
++
i
)
{
//in-place new for each cell
new
(
&
threadLocalRetiredListsTemp
[
i
])
GuardType
;
new
(
&
guards_
[
i
])
embb
::
base
::
Atomic
<
GuardType
>
(
undefined_guard
)
;
}
threadLocalRetiredLists
=
static_cast
<
GuardType
*>
(
embb
::
base
::
Allocation
::
Allocate
(
sizeof
(
GuardType
)
*
guardsPerThread
*
accessorCount
*
accessorCount
));
for
(
unsigned
int
i
=
0
;
i
!=
guardsPerThread
*
accessorCount
*
accessorCount
;
++
i
)
{
for
(
unsigned
int
i
=
0
;
i
!=
count_ret_elements
;
++
i
)
{
//in-place new for each cell
new
(
&
threadLocalRetiredLists
[
i
])
GuardType
;
new
(
&
thread_local_retired_lists_temp_
[
i
])
GuardType
(
undefined_guard
)
;
}
// init guards and retired lists to the undefined guard
for
(
unsigned
int
i
=
0
;
i
!=
static_cast
<
unsigned
int
>
(
guardsPerThread
);
++
i
)
{
for
(
unsigned
int
i2
=
0
;
i2
!=
accessorCount
;
++
i2
)
{
guards
[
i
*
accessorCount
+
i2
]
=
undefinedGuard
;
for
(
unsigned
int
i
=
0
;
i
!=
count_ret_elements
;
++
i
)
{
//in-place new for each cell
new
(
&
thread_local_retired_lists_
[
i
])
GuardType
(
undefined_guard
);
}
}
for
(
unsigned
int
j
=
0
;
j
!=
accessorCount
;
++
j
)
{
for
(
unsigned
int
i
=
0
;
i
!=
guardsPerThread
*
accessorCount
;
++
i
)
{
threadLocalRetiredListsTemp
[
j
*
(
accessorCount
*
guardsPerThread
)
+
i
]
=
undefinedGuard
;
threadLocalRetiredLists
[
j
*
(
accessorCount
*
guardsPerThread
)
+
i
]
=
undefinedGuard
;
}
}
template
<
typename
GuardType
>
HazardPointer
<
GuardType
>::~
HazardPointer
()
{
const
unsigned
int
count_guards
=
max_guards_per_thread_
*
max_accessors_count_
;
for
(
unsigned
int
i
=
0
;
i
!=
accessorCount
;
++
i
)
{
//in-place new for each cell
threadIdMapping
[
i
]
=
-
1
;
}
}
template
<
typename
GuardType
>
HazardPointer
<
GuardType
>::~
HazardPointer
()
{
const
unsigned
int
count_ret_elements
=
count_guards
*
max_accessors_count_
;
// Release references from all retired lists. Note that for this to work, the
// data structure using hazard pointer has still to be active... So first, the
// hazard pointer class shall be destructed, then the memory management class
// (e.g. some pool). Otherwise, the hazard pointer class would try to return
// memory to an already destructed memory manager.
for
(
unsigned
int
j
=
0
;
j
!=
accessorCount
;
++
j
)
{
for
(
unsigned
int
i
=
0
;
i
!=
accessorCount
*
guardsPerThread
;
++
i
)
{
// Release references from all retired lists. Note that for this to work,
// the data structure using hazard pointer has still to be active... So
// first, the hazard pointer class shall be destructed, then the memory
// management class (e.g. some pool). Otherwise, the hazard pointer class
// would try to return memory to an already destructed memory manager.
for
(
unsigned
int
i
=
0
;
i
!=
count_ret_elements
;
++
i
)
{
GuardType
pointerToFree
=
threadLocalRetiredLists
[
j
*
accessorCount
*
guardsPerThread
+
i
];
if
(
pointerToFree
==
undefinedGuard
)
{
thread_local_retired_lists_
[
i
];
if
(
pointerToFree
==
undefined_guard_
)
{
break
;
}
freeGuardCallback
(
pointerToFree
);
}
release_object_callback_
(
pointerToFree
);
}
for
(
unsigned
int
i
=
0
;
i
!=
accessorCount
;
++
i
)
{
threadIdMapping
[
i
].
~
Atomic
();
for
(
unsigned
int
i
=
0
;
i
!=
max_accessors_count_
;
++
i
)
{
thread_id_mapping_
[
i
].
~
Atomic
();
}
embb
::
base
::
Allocation
::
Free
(
threadIdMapping
);
embb
::
base
::
Allocation
::
Free
(
thread_id_mapping_
);
for
(
unsigned
int
i
=
0
;
i
!=
guardsPerThread
*
accessorCount
;
++
i
)
{
guards
[
i
].
~
Atomic
();
for
(
unsigned
int
i
=
0
;
i
!=
count_guards
;
++
i
)
{
guards_
[
i
].
~
Atomic
();
}
embb
::
base
::
Allocation
::
Free
(
guards
);
embb
::
base
::
Allocation
::
Free
(
guards_
);
for
(
unsigned
int
i
=
0
;
i
!=
guardsPerThread
*
accessorCount
*
accessorCount
;
++
i
)
{
threadLocalRetiredListsTemp
[
i
].
~
GuardType
();
for
(
unsigned
int
i
=
0
;
i
!=
count_ret_elements
;
++
i
)
{
thread_local_retired_lists_temp_
[
i
].
~
GuardType
();
}
embb
::
base
::
Allocation
::
Free
(
threadLocalRetiredListsTemp
);
embb
::
base
::
Allocation
::
Free
(
thread_local_retired_lists_temp_
);
for
(
unsigned
int
i
=
0
;
i
!=
guardsPerThread
*
accessorCount
*
accessorCount
;
++
i
)
{
threadLocalRetiredLists
[
i
].
~
GuardType
();
for
(
unsigned
int
i
=
0
;
i
!=
count_ret_elements
;
++
i
)
{
thread_local_retired_lists_
[
i
].
~
GuardType
();
}
embb
::
base
::
Allocation
::
Free
(
threadLocalRetiredLists
);
}
embb
::
base
::
Allocation
::
Free
(
thread_local_retired_lists_
);
}
template
<
typename
GuardType
>
void
HazardPointer
<
GuardType
>::
Guard
(
int
guardPosition
,
template
<
typename
GuardType
>
void
HazardPointer
<
GuardType
>::
Guard
(
int
guardPosition
,
GuardType
guardedElement
)
{
const
unsigned
int
myThreadId
=
GetCurrent
ThreadIndex
();
const
unsigned
int
my_thread_id
=
GetObjectLocal
ThreadIndex
();
// check invariants...
assert
(
guardPosition
<
guardsPerThread
&&
myThreadId
<
accessorCount
);
assert
(
guardPosition
<
max_guards_per_thread_
);
assert
(
my_thread_id
<
max_accessors_count_
);
// set guard
guards
[
guardPosition
*
accessorCount
+
myThreadId
]
=
guardedElement
;
}
template
<
typename
GuardType
>
size_t
HazardPointer
<
GuardType
>::
ComputeMaximumRetiredObjectCount
(
size_t
guardsPerThread
,
int
accessors
)
{
guards_
[
guardPosition
*
max_accessors_count_
+
my_thread_id
]
=
guardedElement
;
}
template
<
typename
GuardType
>
size_t
HazardPointer
<
GuardType
>::
ComputeMaximumRetiredObjectCount
(
size_t
guardsPerThread
,
int
accessors
)
{
unsigned
int
accessorCount
=
(
accessors
==
-
1
?
embb
::
base
::
Thread
::
GetThreadsMaxCount
()
:
accessors
);
return
static_cast
<
size_t
>
(
guardsPerThread
*
accessorCount
*
accessorCount
);
}
}
template
<
typename
GuardType
>
void
HazardPointer
<
GuardType
>::
CopyRetiredList
(
GuardType
*
sourceList
,
/**
* Remark: it might be faster to just swap pointers for temp retired list and
* retired list. However, with the current implementation (one array for all
* retired and retired temp lists, respectively) this is not possible. This is
* not changed until this copying accounts for a performance problem. The
* copying is not the bottleneck currently.
*/
template
<
typename
GuardType
>
void
HazardPointer
<
GuardType
>::
CopyRetiredList
(
GuardType
*
sourceList
,
GuardType
*
targetList
,
unsigned
int
retiredListSize
,
GuardType
undefinedGuard
)
{
bool
done
=
false
;
...
...
@@ -274,137 +253,139 @@ void HazardPointer< GuardType >::CopyRetiredList(GuardType* sourceList,
}
}
}
}
template
<
typename
GuardType
>
void
HazardPointer
<
GuardType
>::
UpdateRetiredList
(
GuardType
*
retiredList
,
GuardType
*
updatedRetiredList
,
unsigned
int
retiredListSize
,
GuardType
guardedElement
,
GuardType
consideredHazard
,
GuardType
undefinedGuard
)
{
}
template
<
typename
GuardType
>
void
HazardPointer
<
GuardType
>::
UpdateRetiredList
(
GuardType
*
retired_list
,
GuardType
*
updated_retired_list
,
unsigned
int
retired_list_size
,
GuardType
guarded_element
,
GuardType
considered_hazard
,
GuardType
undefined_guard
)
{
// no hazard set here
if
(
consideredHazard
==
undefinedG
uard
)
if
(
considered_hazard
==
undefined_g
uard
)
return
;
// if this hazard is currently in the union of
// threadLocalRetiredLists and pointerToRetire, but not yet in
// threadLocalRetiredListsTemp, add it to that list
bool
containedInU
nion
=
false
;
bool
contained_in_u
nion
=
false
;
// first iterate over our retired list
for
(
unsigned
int
ii
=
0
;
ii
!=
retiredListSize
;
++
i
i
)
{
for
(
unsigned
int
i
=
0
;
i
!=
retired_list_size
;
++
i
)
{
// when reaching 0, we can stop iterating (end of the "list")
if
(
retiredList
[
i
i
]
==
0
)
if
(
retired_list
[
i
]
==
0
)
break
;
// the hazard is contained in the retired list... it shall go
// into the temp list, if not already there
if
(
retiredList
[
ii
]
==
consideredH
azard
)
{
containedInU
nion
=
true
;
if
(
retired_list
[
i
]
==
considered_h
azard
)
{
contained_in_u
nion
=
true
;
break
;
}
}
// the union also contains pointerToRetire
if
(
!
containedInU
nion
)
{
containedInUnion
=
(
consideredHazard
==
guardedE
lement
);
if
(
!
contained_in_u
nion
)
{
contained_in_union
=
(
considered_hazard
==
guarded_e
lement
);
}
// add the pointer to temp. retired list, if not already there
if
(
containedInUnion
)
{
for
(
unsigned
int
iii
=
0
;
iii
!=
retiredListSize
;
++
iii
)
{
if
(
contained_in_union
)
{
for
(
unsigned
int
ii
=
0
;
ii
!=
retired_list_size
;
++
ii
)
{
// is it already there?
if
(
updatedRetiredList
[
iii
]
==
consideredH
azard
)
if
(
updated_retired_list
[
ii
]
==
considered_h
azard
)
break
;
// end of the list
if
(
updatedRetiredList
[
iii
]
==
undefinedGuard
)
{
if
(
updated_retired_list
[
ii
]
==
undefined_guard
)
{
// add hazard
updatedRetiredList
[
iii
]
=
consideredH
azard
;
updated_retired_list
[
ii
]
=
considered_h
azard
;
// we are done here...
break
;
}
}
}
}
template
<
typename
GuardType
>
void
HazardPointer
<
GuardType
>::
EnqueueForDeletion
(
GuardType
toRetire
)
{
}
unsigned
int
myThreadId
=
GetCurrentThreadIndex
();
template
<
typename
GuardType
>
void
HazardPointer
<
GuardType
>::
EnqueueForDeletion
(
GuardType
toRetire
)
{
unsigned
int
my_thread_id
=
GetObjectLocalThreadIndex
();
// check for invariant
assert
(
myThreadId
<
accessorCount
);
assert
(
my_thread_id
<
max_accessors_count_
);
const
unsigned
int
retired_list_size
=
max_accessors_count_
*
max_guards_per_thread_
;
unsigned
int
retiredListSize
=
accessorCount
*
guardsPerThread
;
const
unsigned
int
count_guards
=
max_accessors_count_
*
max_guards_per_thread_
;
GuardType
*
retiredL
ist
=
&
threadLocalRetiredLists
[
myThreadId
*
retiredListS
ize
];
GuardType
*
retired_l
ist
=
&
thread_local_retired_lists_
[
my_thread_id
*
retired_list_s
ize
];
GuardType
*
retiredListT
emp
=
&
threadLocalRetiredListsTemp
[
myThreadId
*
retiredListS
ize
];
GuardType
*
retired_list_t
emp
=
&
thread_local_retired_lists_temp_
[
my_thread_id
*
retired_list_s
ize
];
// wipe my temp. retired list...
for
(
unsigned
int
i
=
0
;
i
<
retiredListS
ize
;
++
i
)
{
for
(
unsigned
int
i
=
0
;
i
<
retired_list_s
ize
;
++
i
)
{
// the list is filled always from left to right, so occurring the first
// undefinedGuard, the remaining ones are also undefinedGuard...
if
(
retiredListTemp
[
i
]
==
undefinedGuard
)
if
(
retired_list_temp
[
i
]
==
undefined_guard_
)
break
;
retiredListTemp
[
i
]
=
undefinedGuard
;
retired_list_temp
[
i
]
=
undefined_guard_
;
}
// we test each hazard if it is in the union of retiredList and
// guardedElement. If it is, it goes into the new retired list...
for
(
unsigned
int
i
=
0
;
i
!=
accessorCount
*
guardsPerThread
;
++
i
)
{
for
(
unsigned
int
i
=
0
;
i
!=
count_guards
;
++
i
)
{
// consider each current active guard
GuardType
consideredHazard
=
guards
[
i
].
Load
();
UpdateRetiredList
(
retiredList
,
retiredListTemp
,
retiredListS
ize
,
toRetire
,
consideredHazard
,
undefinedGuard
);
GuardType
considered_hazard
=
guards_
[
i
].
Load
();
UpdateRetiredList
(
retired_list
,
retired_list_temp
,
retired_list_s
ize
,
toRetire
,
considered_hazard
,
undefined_guard_
);
}
// now we created a a new retired list... the elements that are "removed" from
// the old retired list can be safely deleted now...
for
(
int
ii
=
-
1
;
ii
!=
static_cast
<
int
>
(
retiredListSize
);
++
ii
)
{
int
retired_list_size_signed
=
static_cast
<
int
>
(
retired_list_size
);
assert
(
retired_list_size_signed
>=
0
);
// now we created a a new retired list... the elements that are "removed"
// from the old retired list can be safely deleted now...
for
(
int
i
=
-
1
;
i
!=
retired_list_size_signed
;
++
i
)
{
// we iterate over the current retired list... -1 is used as dummy element
// in the iteration, to also iterate over the pointerToRetire, which is
// logically also part of the current retired list...
// end of the list, stop iterating
if
(
ii
>=
0
&&
retiredList
[
ii
]
==
undefinedGuard
)
if
(
i
>=
0
&&
retired_list
[
i
]
==
undefined_guard_
)
break
;
GuardType
toCheckIfInNewList
=
undefinedGuard
;
GuardType
to_check_if_in_new_list
=
undefined_guard_
;
toCheckIfInNewList
=
(
ii
==
-
1
?
toRetire
:
retiredList
[
i
i
]);
to_check_if_in_new_list
=
(
i
==
-
1
?
toRetire
:
retired_list
[
i
]);
// still in the new retired list?
bool
stillInL
ist
=
false
;
for
(
unsigned
int
iii
=
0
;
iii
!=
retiredListSize
;
++
i
ii
)
{
bool
still_in_l
ist
=
false
;
for
(
unsigned
int
ii
=
0
;
ii
!=
retired_list_size
;
++
ii
)
{
// end of list
if
(
retiredListTemp
[
iii
]
==
undefinedGuard
)
if
(
retired_list_temp
[
ii
]
==
undefined_guard_
)
break
;
if
(
toCheckIfInNewList
==
retiredListTemp
[
i
ii
])
{
// still in list, cannot delete
!
stillInL
ist
=
true
;
if
(
to_check_if_in_new_list
==
retired_list_temp
[
ii
])
{
// still in list, cannot delete element
!
still_in_l
ist
=
true
;
break
;
}
}
if
(
!
stillInL
ist
)
{
this
->
freeGuardCallback
(
toCheckIfInNewL
ist
);
if
(
!
still_in_l
ist
)
{
this
->
release_object_callback_
(
to_check_if_in_new_l
ist
);
}
}
// copy the updated retired list (temp) to the retired list...
CopyRetiredList
(
retiredListTemp
,
retiredList
,
retiredListSize
,
undefinedGuard
);
}
CopyRetiredList
(
retired_list_temp
,
retired_list
,
retired_list_size
,
undefined_guard_
);
}
}
// namespace internal
}
// namespace containers
}
// namespace embb
...
...
containers_cpp/include/embb/containers/internal/hazard_pointer.h
View file @
053ca488
...
...
@@ -53,7 +53,6 @@ class HazardPointerTest2;
namespace
embb
{
namespace
containers
{
namespace
internal
{
/**
* This class contains a hazard pointer implementation following publication:
*
...
...
@@ -61,7 +60,7 @@ namespace internal {
* objects." IEEE Transactions on Parallel and Distributed Systems, 15.6 (2004)
* : 491-504.
*
* Hazard pointer are a wait-free memory reclamation scheme for lock-free
* Hazard pointer
s
are a wait-free memory reclamation scheme for lock-free
* algorithms. Loosely speaking, they act as garbage collector. The release of
* objects contained within the memory, managed by the hazard pointer class, is
* intercepted and possibly delayed to avoid concurrency bugs.
...
...
@@ -107,111 +106,13 @@ namespace internal {
* when objects shall be freed. In this implementation, we free whenever it is
* possibly to do so, as we want to keep the memory footprint as low as
* possible. We also don't see a performance drop in the current algorithms that
* are using hazard pointer, when not using a threshold.
* are using hazard pointer
s
, when not using a threshold.
*
* \tparam GuardType the type of the guards. Usually the pointer type of some
* object to protect.
*/
template
<
typename
GuardType
>
class
HazardPointer
{
private
:
/**
* HazardPointerTest2 is a white-box test, needing access to private members
* of this class. So declaring it as friend.
*/
friend
class
embb
::
containers
::
test
::
HazardPointerTest2
;
/**
* The hazard pointer guards, represented as array. Each thread has a fixed
* set of slots (guardsPerThread) within this array.
*/
embb
::
base
::
Atomic
<
GuardType
>*
guards
;
/**
* \see threadLocalRetiredLists documentation
*/
GuardType
*
threadLocalRetiredListsTemp
;
/**
* A lists of lists, represented as single array. Each thread maintains a
* list of retired pointers, that are objects that are logically released
* but not released because some thread placed a guard on it.
*/
GuardType
*
threadLocalRetiredLists
;
/**
* This number determines the amount of maximal accessors (threads) that
* will access this hazard pointer instance. Note that a thread once
* accessing this object will be permanently count as accessor, even if not
* participating anymore. If too many threads access this object, an
* assertion is thrown.
*/
unsigned
int
accessorCount
;
/**
* The guard value denoting "not guarded"
*/
GuardType
undefinedGuard
;
/**
* The count of guards that can be set per thread.
*/
int
guardsPerThread
;
/**
* The functor that is called to release an object. This is called by this
* class, when it is safe to do so, i.e., no thread accesses this object
* anymore.
*/
embb
::
base
::
Function
<
void
,
GuardType
>
freeGuardCallback
;
/**
* Mapping from EMBB thread id to internal thread ids Internal thread ids
* are in range [0;accesor_count-1]. The position of a EMBB thread id in
* that array determines the respective internal thread id.
*/
embb
::
base
::
Atomic
<
int
>*
threadIdMapping
;
/**
* Each thread is assigned a thread index (starting with 0). Get the index of
* the current thread. Note that this is not the global index, but an internal
* one. The user is free to define less accessors than the amount of default
* threads. This is useful, as the number of accessors accounts quadratic for
* the memory consumption, so the user should have the possibility to avoid
* memory wastage, when only having a small, fixed size, number of accessors.
*
* @return current thread index
*/
unsigned
int
GetCurrentThreadIndex
();
/**
* Copy retired list \c sourceList to retired list \c targetList
*/
static
void
CopyRetiredList
(
GuardType
*
sourceList
,
/**<[IN] the source retired list*/
GuardType
*
targetList
,
/**<[IN] the target retired list*/
unsigned
int
singleRetiredListSize
,
/**<[IN] the size of a thread local retired list*/
GuardType
undefinedGuard
/**<[IN] the undefined guard (usually the NULL pointer)*/
);
static
void
UpdateRetiredList
(
GuardType
*
retiredList
,
/**<[IN] the old retired list*/
GuardType
*
updatedRetiredList
,
/**<[IN] the updated retired list*/
unsigned
int
retiredListSize
,
/**<[IN] the size of a thread local retired list*/
GuardType
toRetire
,
/**<[IN] the element to retire*/
GuardType
consideredHazard
,
/**<[IN] the currently considered hazard*/
GuardType
undefinedGuard
/**<[IN] the undefined guard (usually the NULL pointer)*/
);
public
:
/**
...
...
@@ -221,7 +122,7 @@ class HazardPointer {
* guarantee at each point in time. More specific, on top of the guaranteed
* count of objects, he has to provide the additional count of objects that
* can be (worst-case) contained in the retired lists and therefore are not
* released yet. The size of all retired lists is guardsPerThread *
* released yet. The size
sum
of all retired lists is guardsPerThread *
* accessorCount * accessorCount, which is computed using this function. So
* the result of function denotes to the user, how many objects he has to
* allocate additionally to the guaranteed count.
...
...
@@ -245,9 +146,9 @@ class HazardPointer {
*
* \memory We dynamically allocate the following:
*
* (sizeof(Atomic<int>) * accessor
Count
) + (sizeof(Atomic<GuardType>) *
* guards_per_thread * accessor
Count
) + (2*sizeof(GuardType) *
* guards_per_thread * accessor
Count
^2)
* (sizeof(Atomic<int>) * accessor
s
) + (sizeof(Atomic<GuardType>) *
* guards_per_thread * accessor
s
) + (2*sizeof(GuardType) *
* guards_per_thread * accessor
s
^2)
*
* The last addend is the dominant one, as accessorCount accounts
* quadratically for it.
...
...
@@ -277,29 +178,137 @@ class HazardPointer {
~
HazardPointer
();
/**
* Guards \c to
Guard. If the guardedE
lement is passed to \c EnqueueForDeletion
* Guards \c to
_guard. If the guarded_e
lement is passed to \c EnqueueForDeletion
* it is prevented from release from now on. The user must have a check, that
* EnqueueForDeletion has not been called on to
G
uard, before the guarding took
* EnqueueForDeletion has not been called on to
_g
uard, before the guarding took
* effect.
*
* \waitfree
*/
void
Guard
(
int
guardPosition
,
GuardType
toGuard
);
void
Guard
(
int
guard_position
,
/**<[IN] position to place guard*/
GuardType
to_guard
/**<[IN] element to guard*/
);
/**
* Enqueue
a pointer for deletion. If not guarded, it is deleted immediately.
*
If it is guarded, it is added to a thread local retired list, and deleted
*
in a subsequent call to \c EnqueueForDeletion, when no guard is placed on
* it anymore.
* Enqueue
guarded element for deletion. If not guarded, it is deleted
*
immediately. If it is guarded, it is added to a thread local retired list,
*
and deleted in a subsequent call to \c EnqueueForDeletion, when no guard is
*
placed on
it anymore.
*/
void
EnqueueForDeletion
(
GuardType
guardedElement
);
void
EnqueueForDeletion
(
GuardType
guarded_element
/**<[IN] element to logically delete*/
);
/**
* Explicitly remove guard from thread local slot.
*
* \waitfree
*/
void
RemoveGuard
(
int
guardPosition
);
void
RemoveGuard
(
int
guard_position
);
private
:
/**
* HazardPointerTest2 is a white-box test, needing access to private members
* of this class. So declaring it as friend.
*/
friend
class
embb
::
containers
::
test
::
HazardPointerTest2
;
/**
* This number determines the amount of maximal accessors (threads) that
* will access this hazard pointer instance. Note that a thread once
* accessing this object will be permanently count as accessor, even if not
* participating anymore. If too many threads access this object, an
* exception is thrown.
*/
unsigned
int
max_accessors_count_
;
/**
* The guard value denoting "not guarded"
*/
GuardType
undefined_guard_
;
/**
* The maximal count of guards that can be set per thread.
*/
int
max_guards_per_thread_
;
/**
* The functor that is called to release an object. This is called by this
* class, when it is safe to do so, i.e., no thread accesses this object
* anymore.
*/
embb
::
base
::
Function
<
void
,
GuardType
>
release_object_callback_
;
/**
* Mapping from EMBB thread id to hazard pointer thread ids. Hazard pointer
* thread ids are in range [0;accesor_count-1]. The position of a EMBB thread
* id in that array determines the respective hazard pointer thread id.
*/
embb
::
base
::
Atomic
<
int
>*
thread_id_mapping_
;
/**
* The hazard pointer guards, represented as array. Each thread has a fixed
* set of slots (guardsPerThread) within this array.
*/
embb
::
base
::
Atomic
<
GuardType
>*
guards_
;
/**
* \see threadLocalRetiredLists documentation
*/
GuardType
*
thread_local_retired_lists_temp_
;
/**
* A list of lists, represented as single array. Each thread maintains a list
* of retired pointers, that are objects that are logically released but not
* released because some thread placed a guard on it.
*/
GuardType
*
thread_local_retired_lists_
;
/**
* Each thread is assigned a thread index (starting with 0). Get the index of
* the current thread. Note that this is not the global index, but an hazard
* pointer class internal one. The user is free to define less accessors than
* the amount of default threads. This is useful, as the number of accessors
* accounts quadratic for the memory consumption, so the user should have the
* possibility to avoid memory wastage when only having a small, fixed size,
* number of accessors.
*
* @return current (hazard pointer object local) thread index
*/
unsigned
int
GetObjectLocalThreadIndex
();
/**
* Copy retired list \c sourceList to retired list \c targetList
*/
static
void
CopyRetiredList
(
GuardType
*
source_list
,
/**<[IN] the source retired list*/
GuardType
*
target_list
,
/**<[IN] the target retired list*/
unsigned
int
single_retired_list_size
,
/**<[IN] the size of a thread local retired list*/
GuardType
undefined_guard
/**<[IN] the undefined guard (usually the NULL pointer)*/
);
static
void
UpdateRetiredList
(
GuardType
*
retired_list
,
/**<[IN] the old retired list*/
GuardType
*
updated_retired_list
,
/**<[IN] the updated retired list*/
unsigned
int
retired_list_size
,
/**<[IN] the size of a thread local retired list*/
GuardType
to_retire
,
/**<[IN] the element to retire*/
GuardType
considered_hazard
,
/**<[IN] the currently considered hazard*/
GuardType
undefined_guard
/**<[IN] the undefined guard (usually the NULL pointer)*/
);
};
}
// namespace internal
}
// namespace containers
...
...
containers_cpp/test/hazard_pointer_test.cc
View file @
053ca488
...
...
@@ -31,23 +31,22 @@
namespace
embb
{
namespace
containers
{
namespace
test
{
IntObjectTestPool
::
IntObjectTestPool
(
unsigned
int
poolSize
)
:
poolSize
(
poolSize
)
IntObjectTestPool
::
IntObjectTestPool
(
unsigned
int
pool_size
)
:
poolSize
(
pool_size
)
{
simplePoolObjects
=
static_cast
<
int
*>
(
embb
::
base
::
Allocation
::
Allocate
(
sizeof
(
int
)
*
pool
S
ize
));
embb
::
base
::
Allocation
::
Allocate
(
sizeof
(
int
)
*
pool
_s
ize
));
simplePool
=
static_cast
<
embb
::
base
::
Atomic
<
int
>*>
(
embb
::
base
::
Allocation
::
Allocate
(
sizeof
(
embb
::
base
::
Atomic
<
int
>
)
*
pool
S
ize
));
pool
_s
ize
));
for
(
unsigned
int
i
=
0
;
i
!=
pool
S
ize
;
++
i
)
{
for
(
unsigned
int
i
=
0
;
i
!=
pool
_s
ize
;
++
i
)
{
//in-place new for each array cell
new
(
&
simplePool
[
i
])
embb
::
base
::
Atomic
<
int
>
;
}
for
(
unsigned
int
i
=
0
;
i
!=
pool
S
ize
;
++
i
)
{
for
(
unsigned
int
i
=
0
;
i
!=
pool
_s
ize
;
++
i
)
{
simplePool
[
i
]
=
FREE_MARKER
;
simplePoolObjects
[
i
]
=
0
;
}
...
...
@@ -75,8 +74,8 @@ int* IntObjectTestPool::Allocate() {
return
0
;
}
void
IntObjectTestPool
::
Release
(
int
*
object
P
ointer
)
{
int
cell
=
object
P
ointer
-
simplePoolObjects
;
void
IntObjectTestPool
::
Release
(
int
*
object
_p
ointer
)
{
int
cell
=
object
_p
ointer
-
simplePoolObjects
;
simplePool
[
cell
].
Store
(
FREE_MARKER
);
}
...
...
@@ -85,17 +84,17 @@ HazardPointerTest::HazardPointerTest() :
#pragma warning(push)
#pragma warning(disable:4355)
#endif
delete
PointerCallback
(
*
this
,
&
HazardPointerTest
::
DeletePointerCallback
),
delete
_pointer_callback_
(
*
this
,
&
HazardPointerTest
::
DeletePointerCallback
),
#ifdef EMBB_PLATFORM_COMPILER_MSVC
#pragma warning(pop)
#endif
object
Pool
(
NULL
),
stack
(
NULL
),
hazard
Pointer
(
NULL
),
n
Threads
(
static_cast
<
int
>
object
_pool_
(
NULL
),
stack
_
(
NULL
),
hazard
_pointer_
(
NULL
),
n
_threads_
(
static_cast
<
int
>
(
partest
::
TestSuite
::
GetDefaultNumThreads
()))
{
n
ElementsPerThread
=
100
;
n
Elements
=
nThreads
*
nElementsPerThread
;
n
_elements_per_thread_
=
100
;
n
_elements_
=
n_threads_
*
n_elements_per_thread_
;
embb
::
base
::
Function
<
void
,
embb
::
base
::
Atomic
<
int
>*
>
deletePointerCallback
(
*
this
,
...
...
@@ -111,45 +110,45 @@ deletePointerCallback(*this, &HazardPointerTest::DeletePointerCallback),
Pre
(
&
HazardPointerTest
::
HazardPointerTest1Pre
,
this
).
Add
(
&
HazardPointerTest
::
HazardPointerTest1ThreadMethod
,
this
,
static_cast
<
size_t
>
(
n
Threads
)).
this
,
static_cast
<
size_t
>
(
n
_threads_
)).
Post
(
&
HazardPointerTest
::
HazardPointerTest1Post
,
this
);
}
void
HazardPointerTest
::
HazardPointerTest1Pre
()
{
embb_internal_thread_index_reset
();
object
Pool
=
object
_pool_
=
embb
::
base
::
Allocation
::
New
<
embb
::
containers
::
ObjectPool
<
embb
::
base
::
Atomic
<
int
>
>
>
(
static_cast
<
size_t
>
(
n
Elements
));
(
static_cast
<
size_t
>
(
n
_elements_
));
stack
=
embb
::
base
::
Allocation
::
stack
_
=
embb
::
base
::
Allocation
::
New
<
embb
::
containers
::
LockFreeStack
<
embb
::
base
::
Atomic
<
int
>*
>
>
(
static_cast
<
size_t
>
(
n
Elements
));
(
static_cast
<
size_t
>
(
n
_elements_
));
hazard
Pointer
=
embb
::
base
::
Allocation
::
hazard
_pointer_
=
embb
::
base
::
Allocation
::
New
<
embb
::
containers
::
internal
::
HazardPointer
<
embb
::
base
::
Atomic
<
int
>*
>
>
(
delete
PointerCallback
,
(
delete
_pointer_callback_
,
static_cast
<
embb
::
base
::
Atomic
<
int
>*>
(
NULL
),
1
);
}
void
HazardPointerTest
::
HazardPointerTest1Post
()
{
embb
::
base
::
Allocation
::
Delete
(
hazard
Pointer
);
embb
::
base
::
Allocation
::
Delete
(
object
Pool
);
embb
::
base
::
Allocation
::
Delete
(
stack
);
embb
::
base
::
Allocation
::
Delete
(
hazard
_pointer_
);
embb
::
base
::
Allocation
::
Delete
(
object
_pool_
);
embb
::
base
::
Allocation
::
Delete
(
stack
_
);
}
void
HazardPointerTest
::
HazardPointerTest1ThreadMethod
()
{
unsigned
int
thread_index
;
embb_internal_thread_index
(
&
thread_index
);
for
(
int
i
=
0
;
i
!=
n
ElementsPerThread
;
++
i
)
{
embb
::
base
::
Atomic
<
int
>*
allocated_object
=
object
Pool
->
Allocate
(
0
);
for
(
int
i
=
0
;
i
!=
n
_elements_per_thread_
;
++
i
)
{
embb
::
base
::
Atomic
<
int
>*
allocated_object
=
object
_pool_
->
Allocate
(
0
);
hazard
Pointer
->
Guard
(
0
,
allocated_object
);
hazard
_pointer_
->
Guard
(
0
,
allocated_object
);
bool
success
=
stack
->
TryPush
(
allocated_object
);
bool
success
=
stack
_
->
TryPush
(
allocated_object
);
PT_ASSERT
(
success
==
true
);
...
...
@@ -161,7 +160,7 @@ void HazardPointerTest::HazardPointerTest1ThreadMethod() {
bool
success_pop
;
while
(
(
success_pop
=
stack
->
TryPop
(
allocated_object_from_different_thread
))
(
success_pop
=
stack
_
->
TryPop
(
allocated_object_from_different_thread
))
==
true
&&
allocated_object_from_different_thread
==
allocated_object
)
{
...
...
@@ -171,99 +170,100 @@ void HazardPointerTest::HazardPointerTest1ThreadMethod() {
same
=
true
;
break
;
}
bool
success
=
stack
->
TryPush
(
allocated_object_from_different_thread
);
bool
success
=
stack
_
->
TryPush
(
allocated_object_from_different_thread
);
PT_ASSERT
(
success
==
true
);
}
PT_ASSERT
(
success_pop
==
true
);
allocated_object
->
Store
(
1
);
hazard
Pointer
->
EnqueueForDeletion
(
allocated_object
);
hazard
_pointer_
->
EnqueueForDeletion
(
allocated_object
);
if
(
!
same
)
{
hazard
Pointer
->
Guard
(
0
,
allocated_object_from_different_thread
);
hazard
_pointer_
->
Guard
(
0
,
allocated_object_from_different_thread
);
// if this holds, we were successful in guarding... otherwise we
// were to late, because the pointer has already been added
// to the retired list.
if
(
*
allocated_object_from_different_thread
==
0
)
{
// the pointer must not be deleted here!
vector
Mutex
.
Lock
();
vector
_mutex_
.
Lock
();
for
(
std
::
vector
<
embb
::
base
::
Atomic
<
int
>*
>::
iterator
it
=
deleted
Vector
.
begin
();
it
!=
deleted
Vector
.
end
();
it
=
deleted
_vector_
.
begin
();
it
!=
deleted
_vector_
.
end
();
++
it
)
{
PT_ASSERT
(
*
it
!=
allocated_object_from_different_thread
);
}
vector
Mutex
.
Unlock
();
vector
_mutex_
.
Unlock
();
}
hazard
Pointer
->
Guard
(
0
,
NULL
);
hazard
_pointer_
->
Guard
(
0
,
NULL
);
}
}
}
void
HazardPointerTest
::
DeletePointerCallback
(
embb
::
base
::
Atomic
<
int
>*
to_delete
)
{
vector
Mutex
.
Lock
();
deleted
Vector
.
push_back
(
to_delete
);
vector
Mutex
.
Unlock
();
vector
_mutex_
.
Lock
();
deleted
_vector_
.
push_back
(
to_delete
);
vector
_mutex_
.
Unlock
();
}
void
HazardPointerTest2
::
DeletePointerCallback
(
int
*
to
D
elete
)
{
test
Pool
->
Release
(
toD
elete
);
void
HazardPointerTest2
::
DeletePointerCallback
(
int
*
to
_d
elete
)
{
test
_pool_
->
Release
(
to_d
elete
);
}
bool
HazardPointerTest2
::
SetRelativeGuards
()
{
unsigned
int
thread
I
ndex
;
embb_internal_thread_index
(
&
thread
I
ndex
);
unsigned
int
thread
_i
ndex
;
embb_internal_thread_index
(
&
thread
_i
ndex
);
unsigned
int
my_begin
=
guards
PerThreadCount
*
threadI
ndex
;
int
guard
N
umber
=
0
;
unsigned
int
my_begin
=
guards
_per_phread_count_
*
thread_i
ndex
;
int
guard
_n
umber
=
0
;
unsigned
int
alreadyGuarded
=
0
;
for
(
unsigned
int
i
=
my_begin
;
i
!=
my_begin
+
guardsPerThreadCount
;
++
i
){
if
(
sharedGuarded
[
i
]
!=
0
)
{
for
(
unsigned
int
i
=
my_begin
;
i
!=
my_begin
+
guards_per_phread_count_
;
++
i
){
if
(
shared_guarded_
[
i
]
!=
0
)
{
alreadyGuarded
++
;
guard
N
umber
++
;
guard
_n
umber
++
;
continue
;
}
int
*
to
Guard
=
sharedAllocated
[
i
];
if
(
to
G
uard
)
{
hazard
Pointer
->
Guard
(
guardNumber
,
toG
uard
);
int
*
to
_guard
=
shared_allocated_
[
i
];
if
(
to
_g
uard
)
{
hazard
_pointer_
->
Guard
(
guard_number
,
to_g
uard
);
// changed in the meantime?
if
(
to
Guard
==
sharedAllocated
[
i
].
Load
())
{
if
(
to
_guard
==
shared_allocated_
[
i
].
Load
())
{
// guard was successful. Communicate to other threads.
shared
Guarded
[
i
]
=
toG
uard
;
shared
_guarded_
[
i
]
=
to_g
uard
;
}
else
{
// reset the guard, couldn't guard...
hazard
Pointer
->
RemoveGuard
(
guardN
umber
);
hazard
_pointer_
->
RemoveGuard
(
guard_n
umber
);
}
}
guard
N
umber
++
;
guard
_n
umber
++
;
}
return
(
alreadyGuarded
==
guards
PerThreadCount
);
return
(
alreadyGuarded
==
guards
_per_phread_count_
);
}
void
HazardPointerTest2
::
HazardPointerTest2Master
()
{
// while the hazard pointer guard array is not full
int
**
allocatedLocal
=
static_cast
<
int
**>
(
embb
::
base
::
Allocation
::
Allocate
(
sizeof
(
int
*
)
*
guaranteed
CapacityPool
));
embb
::
base
::
Allocation
::
Allocate
(
sizeof
(
int
*
)
*
guaranteed
_capacity_pool_
));
bool
full
=
false
;
while
(
!
full
)
{
full
=
true
;
for
(
unsigned
int
i
=
0
;
i
!=
guaranteed
CapacityPool
;
++
i
)
{
if
(
shared
Guarded
[
i
]
==
0
)
{
for
(
unsigned
int
i
=
0
;
i
!=
guaranteed
_capacity_pool_
;
++
i
)
{
if
(
shared
_guarded_
[
i
]
==
0
)
{
full
=
false
;
break
;
}
}
// not all guards set
for
(
unsigned
int
i
=
0
;
i
!=
guaranteed
CapacityPool
;
++
i
)
{
allocatedLocal
[
i
]
=
test
Pool
->
Allocate
();
shared
Allocated
[
i
].
Store
(
allocatedLocal
[
i
]);
for
(
unsigned
int
i
=
0
;
i
!=
guaranteed
_capacity_pool_
;
++
i
)
{
allocatedLocal
[
i
]
=
test
_pool_
->
Allocate
();
shared
_allocated_
[
i
].
Store
(
allocatedLocal
[
i
]);
}
// set my hazards. We do not have to check, this must be successful
...
...
@@ -271,9 +271,9 @@ void HazardPointerTest2::HazardPointerTest2Master() {
SetRelativeGuards
();
// free
for
(
unsigned
int
i
=
0
;
i
!=
guaranteed
CapacityPool
;
++
i
)
{
shared
Allocated
[
i
].
Store
(
0
);
hazard
Pointer
->
EnqueueForDeletion
(
allocatedLocal
[
i
]);
for
(
unsigned
int
i
=
0
;
i
!=
guaranteed
_capacity_pool_
;
++
i
)
{
shared
_allocated_
[
i
].
Store
(
0
);
hazard
_pointer_
->
EnqueueForDeletion
(
allocatedLocal
[
i
]);
}
}
...
...
@@ -289,54 +289,54 @@ void HazardPointerTest2::HazardPointerTest2Slave() {
void
HazardPointerTest2
::
HazardPointerTest2Pre
()
{
embb_internal_thread_index_reset
();
current
Master
=
0
;
sync1
=
0
;
sync2
=
0
;
current
_master_
=
0
;
sync1
_
=
0
;
sync2
_
=
0
;
// first the test pool has to be created
testPool
=
embb
::
base
::
Allocation
::
New
<
IntObjectTestPool
>
(
poolSizeUsingHazardPointer
);
test_pool_
=
embb
::
base
::
Allocation
::
New
<
IntObjectTestPool
>
(
pool_size_using_hazard_pointer_
);
// after the pool has been created, we create the hp class
hazard
Pointer
=
embb
::
base
::
Allocation
::
New
<
hazard
_pointer_
=
embb
::
base
::
Allocation
::
New
<
embb
::
containers
::
internal
::
HazardPointer
<
int
*>
>
(
delete
PointerCallback
,
static_cast
<
int
*>
(
NULL
),
static_cast
<
int
>
(
guards
PerThreadCount
),
nT
hreads
);
(
delete
_pointer_callback_
,
static_cast
<
int
*>
(
NULL
),
static_cast
<
int
>
(
guards
_per_phread_count_
),
n_t
hreads
);
shared
Guarded
=
static_cast
<
embb
::
base
::
Atomic
<
int
*>*>
(
shared
_guarded_
=
static_cast
<
embb
::
base
::
Atomic
<
int
*>*>
(
embb
::
base
::
Allocation
::
Allocate
(
sizeof
(
embb
::
base
::
Atomic
<
int
*>
)
*
guaranteed
CapacityPool
)
guaranteed
_capacity_pool_
)
);
for
(
unsigned
int
i
=
0
;
i
!=
guaranteed
CapacityPool
;
++
i
)
{
guaranteed
_capacity_pool_
;
++
i
)
{
//in-place new for each array cell
new
(
&
shared
Guarded
[
i
])
embb
::
base
::
Atomic
<
int
*
>
;
new
(
&
shared
_guarded_
[
i
])
embb
::
base
::
Atomic
<
int
*
>
;
}
shared
Allocated
=
static_cast
<
embb
::
base
::
Atomic
<
int
*>*>
(
shared
_allocated_
=
static_cast
<
embb
::
base
::
Atomic
<
int
*>*>
(
embb
::
base
::
Allocation
::
Allocate
(
sizeof
(
embb
::
base
::
Atomic
<
int
*>
)
*
guaranteed
CapacityPool
)
guaranteed
_capacity_pool_
)
);
for
(
unsigned
int
i
=
0
;
i
!=
guaranteed
CapacityPool
;
++
i
)
{
guaranteed
_capacity_pool_
;
++
i
)
{
//in-place new for each array cell
new
(
&
shared
Allocated
[
i
])
embb
::
base
::
Atomic
<
int
*
>
;
new
(
&
shared
_allocated_
[
i
])
embb
::
base
::
Atomic
<
int
*
>
;
}
for
(
unsigned
int
i
=
0
;
i
!=
guaranteed
CapacityPool
;
++
i
)
{
shared
Guarded
[
i
]
=
0
;
shared
Allocated
[
i
]
=
0
;
for
(
unsigned
int
i
=
0
;
i
!=
guaranteed
_capacity_pool_
;
++
i
)
{
shared
_guarded_
[
i
]
=
0
;
shared
_allocated_
[
i
]
=
0
;
}
}
void
HazardPointerTest2
::
HazardPointerTest2Post
()
{
for
(
unsigned
int
i
=
0
;
i
!=
static_cast
<
unsigned
int
>
(
nThreads
);
++
i
)
{
for
(
unsigned
int
i2
=
0
;
i2
!=
static_cast
<
unsigned
int
>
(
nThreads
)
*
guardsPerThreadCount
;
++
i2
)
{
if
(
hazardPointer
->
threadLocalRetiredLists
[
i2
+
i
*
nThreads
*
guardsPerThreadCount
]
==
NULL
)
{
for
(
unsigned
int
i
=
0
;
i
!=
static_cast
<
unsigned
int
>
(
n_threads
);
++
i
)
{
for
(
unsigned
int
i2
=
0
;
i2
!=
static_cast
<
unsigned
int
>
(
n_threads
)
*
guards_per_phread_count_
;
++
i2
)
{
if
(
hazard_pointer_
->
thread_local_retired_lists_
[
i2
+
i
*
n_threads
*
guards_per_phread_count_
]
==
NULL
)
{
// all retired lists must be completely filled
PT_ASSERT
(
false
);
}
...
...
@@ -344,21 +344,21 @@ void HazardPointerTest2::HazardPointerTest2Post() {
}
unsigned
int
checks
=
0
;
for
(
unsigned
int
i
=
0
;
i
!=
static_cast
<
unsigned
int
>
(
n
T
hreads
);
++
i
)
{
for
(
unsigned
int
i2
=
0
;
i2
!=
static_cast
<
unsigned
int
>
(
n
T
hreads
)
*
guards
PerThreadCount
;
++
i2
)
{
for
(
unsigned
int
j
=
0
;
j
!=
static_cast
<
unsigned
int
>
(
n
T
hreads
);
++
j
)
{
for
(
unsigned
int
j2
=
0
;
j2
!=
static_cast
<
unsigned
int
>
(
n
T
hreads
)
*
guards
PerThreadCount
;
++
j2
)
{
for
(
unsigned
int
i
=
0
;
i
!=
static_cast
<
unsigned
int
>
(
n
_t
hreads
);
++
i
)
{
for
(
unsigned
int
i2
=
0
;
i2
!=
static_cast
<
unsigned
int
>
(
n
_t
hreads
)
*
guards
_per_phread_count_
;
++
i2
)
{
for
(
unsigned
int
j
=
0
;
j
!=
static_cast
<
unsigned
int
>
(
n
_t
hreads
);
++
j
)
{
for
(
unsigned
int
j2
=
0
;
j2
!=
static_cast
<
unsigned
int
>
(
n
_t
hreads
)
*
guards
_per_phread_count_
;
++
j2
)
{
if
(
i2
==
j2
&&
i
==
j
)
continue
;
// all retired elements have to be disjoint
PT_ASSERT
(
hazard
Pointer
->
threadLocalRetiredLists
[
i2
+
i
*
n
Threads
*
guardsPerThreadCount
]
!=
hazard
Pointer
->
threadLocalRetiredLists
[
j2
+
j
*
n
Threads
*
guardsPerThreadCount
]
hazard
_pointer_
->
thread_local_retired_lists_
[
i2
+
i
*
n
_threads
*
guards_per_phread_count_
]
!=
hazard
_pointer_
->
thread_local_retired_lists_
[
j2
+
j
*
n
_threads
*
guards_per_phread_count_
]
);
checks
++
;
...
...
@@ -370,16 +370,16 @@ void HazardPointerTest2::HazardPointerTest2Post() {
// sanity check on the count of expected comparisons.
PT_ASSERT
(
checks
==
n
Threads
*
nThreads
*
guardsPerThreadCount
*
(
n
Threads
*
nThreads
*
guardsPerThreadCount
-
1
)
n
_threads
*
n_threads
*
guards_per_phread_count_
*
(
n
_threads
*
n_threads
*
guards_per_phread_count_
-
1
)
);
std
::
vector
<
int
*
>
additionallyAllocated
;
// we should be able to still allocate the guaranteed capacity of
// elements from the pool.
for
(
unsigned
int
i
=
0
;
i
!=
guaranteed
CapacityPool
;
++
i
)
{
int
*
allocated
=
test
Pool
->
Allocate
();
for
(
unsigned
int
i
=
0
;
i
!=
guaranteed
_capacity_pool_
;
++
i
)
{
int
*
allocated
=
test
_pool_
->
Allocate
();
// allocated is not allowed to be zero
PT_ASSERT
(
allocated
!=
NULL
);
...
...
@@ -390,11 +390,11 @@ void HazardPointerTest2::HazardPointerTest2Post() {
}
// the pool should now be empty
PT_ASSERT
(
test
Pool
->
Allocate
()
==
NULL
);
PT_ASSERT
(
test
_pool_
->
Allocate
()
==
NULL
);
// release allocated elements...
for
(
unsigned
int
i
=
0
;
i
!=
additionallyAllocated
.
size
();
++
i
)
{
test
Pool
->
Release
(
additionallyAllocated
[
i
]);
test
_pool_
->
Release
(
additionallyAllocated
[
i
]);
}
// the additionallyAllocated elements shall be disjoint
...
...
@@ -409,39 +409,39 @@ void HazardPointerTest2::HazardPointerTest2Post() {
// no allocated element should be in any retired list...
for
(
unsigned
int
a
=
0
;
a
!=
additionallyAllocated
.
size
();
++
a
)
{
for
(
unsigned
int
i
=
0
;
i
!=
static_cast
<
unsigned
int
>
(
n
T
hreads
);
++
i
)
{
for
(
unsigned
int
i2
=
0
;
i2
!=
static_cast
<
unsigned
int
>
(
n
T
hreads
)
*
guards
PerThreadCount
;
++
i2
)
{
for
(
unsigned
int
i
=
0
;
i
!=
static_cast
<
unsigned
int
>
(
n
_t
hreads
);
++
i
)
{
for
(
unsigned
int
i2
=
0
;
i2
!=
static_cast
<
unsigned
int
>
(
n
_t
hreads
)
*
guards
_per_phread_count_
;
++
i2
)
{
PT_ASSERT
(
hazard
Pointer
->
threadLocalRetiredLists
[
i2
+
i
*
n
Threads
*
guardsPerThreadCount
]
!=
hazard
_pointer_
->
thread_local_retired_lists_
[
i2
+
i
*
n
_threads
*
guards_per_phread_count_
]
!=
additionallyAllocated
[
a
]
);
}
}
}
for
(
unsigned
int
i
=
0
;
i
!=
guaranteed
CapacityPool
;
++
i
)
{
for
(
unsigned
int
i
=
0
;
i
!=
guaranteed
_capacity_pool_
;
++
i
)
{
//in-place new for each array cell
shared
Guarded
[
i
].
~
Atomic
();
shared
_guarded_
[
i
].
~
Atomic
();
}
embb
::
base
::
Allocation
::
Free
(
shared
Guarded
);
embb
::
base
::
Allocation
::
Free
(
shared
_guarded_
);
for
(
unsigned
int
i
=
0
;
i
!=
guaranteed
CapacityPool
;
++
i
)
{
for
(
unsigned
int
i
=
0
;
i
!=
guaranteed
_capacity_pool_
;
++
i
)
{
//in-place new for each array cell
shared
Allocated
[
i
].
~
Atomic
();
shared
_allocated_
[
i
].
~
Atomic
();
}
embb
::
base
::
Allocation
::
Free
(
shared
Allocated
);
embb
::
base
::
Allocation
::
Delete
(
hazard
Pointer
);
embb
::
base
::
Allocation
::
Free
(
shared
_allocated_
);
embb
::
base
::
Allocation
::
Delete
(
hazard
_pointer_
);
// after deleting the hazard pointer object, all retired pointers have
// to be returned to the pool!
std
::
vector
<
int
*>
elementsInPool
;
int
*
nextElement
;
while
((
nextElement
=
test
Pool
->
Allocate
())
!=
NULL
)
{
while
((
nextElement
=
test
_pool_
->
Allocate
())
!=
NULL
)
{
for
(
unsigned
int
i
=
0
;
i
!=
elementsInPool
.
size
();
++
i
)
{
// all elements need to be disjoint
PT_ASSERT
(
elementsInPool
[
i
]
!=
nextElement
);
...
...
@@ -451,85 +451,83 @@ void HazardPointerTest2::HazardPointerTest2Post() {
// all elements should have been returned by the hp object, so we should be
// able to acquire all elements.
PT_ASSERT
(
elementsInPool
.
size
()
==
pool
SizeUsingHazardPointer
);
PT_ASSERT
(
elementsInPool
.
size
()
==
pool
_size_using_hazard_pointer_
);
embb
::
base
::
Allocation
::
Delete
(
test
Pool
);
embb
::
base
::
Allocation
::
Delete
(
test
_pool_
);
}
void
HazardPointerTest2
::
HazardPointerTest2ThreadMethod
()
{
for
(;;)
{
unsigned
int
thread
I
ndex
;
embb_internal_thread_index
(
&
thread
I
ndex
);
unsigned
int
thread
_i
ndex
;
embb_internal_thread_index
(
&
thread
_i
ndex
);
if
(
thread
Index
==
currentMaster
)
{
if
(
thread
_index
==
current_master_
)
{
HazardPointerTest2Master
();
}
else
{
HazardPointerTest2Slave
();
}
sync1
.
FetchAndAdd
(
1
);
sync1
_
.
FetchAndAdd
(
1
);
// wait until cleanup thread signals to be finished
while
(
sync1
!=
0
)
{
int
expected
=
n
T
hreads
;
int
desired
=
finishMarker
;
while
(
sync1
_
!=
0
)
{
int
expected
=
n
_t
hreads
;
int
desired
=
FINISH_MARKER
;
// select thread, responsible for cleanup
if
(
sync1
.
CompareAndSwap
(
expected
,
desired
))
{
if
(
sync1_
.
CompareAndSwap
(
expected
,
desired
))
{
//wipe arrays!
for
(
unsigned
int
i
=
0
;
i
!=
guaranteed
CapacityPool
;
++
i
)
{
shared
Guarded
[
i
]
=
0
;
shared
Allocated
[
i
]
=
0
;
for
(
unsigned
int
i
=
0
;
i
!=
guaranteed
_capacity_pool_
;
++
i
)
{
shared
_guarded_
[
i
]
=
0
;
shared
_allocated_
[
i
]
=
0
;
}
// increase master
current
Master
.
FetchAndAdd
(
1
);
sync2
=
0
;
sync1
.
Store
(
0
);
current
_master_
.
FetchAndAdd
(
1
);
sync2
_
=
0
;
sync1
_
.
Store
(
0
);
}
}
// wait for all threads to reach this position
sync2
.
FetchAndAdd
(
1
);
while
(
sync2
!=
static_cast
<
unsigned
int
>
(
nT
hreads
))
{}
sync2
_
.
FetchAndAdd
(
1
);
while
(
sync2
_
!=
static_cast
<
unsigned
int
>
(
n_t
hreads
))
{}
// if each thread was master once, terminate.
if
(
current
Master
==
static_cast
<
unsigned
int
>
(
nT
hreads
))
{
if
(
current
_master_
==
static_cast
<
unsigned
int
>
(
n_t
hreads
))
{
return
;
}
}
}
HazardPointerTest2
::
HazardPointerTest2
()
:
n
T
hreads
(
static_cast
<
int
>
n
_t
hreads
(
static_cast
<
int
>
(
partest
::
TestSuite
::
GetDefaultNumThreads
())),
#ifdef EMBB_PLATFORM_COMPILER_MSVC
#pragma warning(push)
#pragma warning(disable:4355)
#endif
delete
PointerCallback
(
delete
_pointer_callback_
(
*
this
,
&
HazardPointerTest2
::
DeletePointerCallback
)
#ifdef EMBB_PLATFORM_COMPILER_MSVC
#pragma warning(pop)
#endif
{
guards
PerThreadCount
=
5
;
guaranteed
CapacityPool
=
guardsPerThreadCount
*
nT
hreads
;
pool
SizeUsingHazardPointer
=
guaranteedCapacityPool
+
guards
PerThreadCount
*
nThreads
*
nT
hreads
;
guards
_per_phread_count_
=
5
;
guaranteed
_capacity_pool_
=
guards_per_phread_count_
*
n_t
hreads
;
pool
_size_using_hazard_pointer_
=
guaranteed_capacity_pool_
+
guards
_per_phread_count_
*
n_threads
*
n_t
hreads
;
embb
::
base
::
Thread
::
GetThreadsMaxCount
();
CreateUnit
(
"HazardPointerTestSimulateMemoryWorstCase"
).
Pre
(
&
HazardPointerTest2
::
HazardPointerTest2Pre
,
this
).
Add
(
&
HazardPointerTest2
::
HazardPointerTest2ThreadMethod
,
this
,
static_cast
<
size_t
>
(
n
T
hreads
)).
this
,
static_cast
<
size_t
>
(
n
_t
hreads
)).
Post
(
&
HazardPointerTest2
::
HazardPointerTest2Post
,
this
);
}
}
// namespace test
}
// namespace containers
}
//
namespace
embb
\ No newline at end of file
containers_cpp/test/hazard_pointer_test.h
View file @
053ca488
...
...
@@ -36,7 +36,6 @@
namespace
embb
{
namespace
containers
{
namespace
test
{
/**
* @brief a very simple wait-free object pool implementation to have tests
* being independent of the EMBB object pool implementation.
...
...
@@ -51,7 +50,7 @@ class IntObjectTestPool {
static
const
int
FREE_MARKER
=
0
;
unsigned
int
poolSize
;
IntObjectTestPool
(
unsigned
int
pool
S
ize
);
IntObjectTestPool
(
unsigned
int
pool
_s
ize
);
~
IntObjectTestPool
();
...
...
@@ -67,27 +66,10 @@ class IntObjectTestPool {
*
* @param objectPointer the object to be freed
*/
void
Release
(
int
*
object
P
ointer
);
void
Release
(
int
*
object
_p
ointer
);
};
class
HazardPointerTest
:
public
partest
::
TestCase
{
private
:
embb
::
base
::
Function
<
void
,
embb
::
base
::
Atomic
<
int
>*>
deletePointerCallback
;
//used to allocate random stuff, we will just use the pointers, not the
//contents
embb
::
containers
::
ObjectPool
<
embb
::
base
::
Atomic
<
int
>
>*
objectPool
;
//used to move pointer between threads
embb
::
containers
::
LockFreeStack
<
embb
::
base
::
Atomic
<
int
>*
>*
stack
;
embb
::
base
::
Mutex
vectorMutex
;
embb
::
containers
::
internal
::
HazardPointer
<
embb
::
base
::
Atomic
<
int
>*>*
hazardPointer
;
std
::
vector
<
embb
::
base
::
Atomic
<
int
>*
>
deletedVector
;
int
nThreads
;
int
nElementsPerThread
;
int
nElements
;
public
:
/**
* Adds test methods.
...
...
@@ -96,56 +78,71 @@ class HazardPointerTest : public partest::TestCase {
void
HazardPointerTest1Pre
();
void
HazardPointerTest1Post
();
void
HazardPointerTest1ThreadMethod
();
void
DeletePointerCallback
(
embb
::
base
::
Atomic
<
int
>*
toDelete
);
void
DeletePointerCallback
(
embb
::
base
::
Atomic
<
int
>*
to_delete
);
private
:
embb
::
base
::
Function
<
void
,
embb
::
base
::
Atomic
<
int
>*>
delete_pointer_callback_
;
//used to allocate random stuff, we will just use the pointers, not the
//contents
embb
::
containers
::
ObjectPool
<
embb
::
base
::
Atomic
<
int
>
>*
object_pool_
;
//used to move pointer between threads
embb
::
containers
::
LockFreeStack
<
embb
::
base
::
Atomic
<
int
>*
>*
stack_
;
embb
::
base
::
Mutex
vector_mutex_
;
embb
::
containers
::
internal
::
HazardPointer
<
embb
::
base
::
Atomic
<
int
>*>*
hazard_pointer_
;
std
::
vector
<
embb
::
base
::
Atomic
<
int
>*
>
deleted_vector_
;
int
n_threads_
;
int
n_elements_per_thread_
;
int
n_elements_
;
};
class
HazardPointerTest2
:
public
partest
::
TestCase
{
public
:
void
DeletePointerCallback
(
int
*
to_delete
);
bool
SetRelativeGuards
();
void
HazardPointerTest2Master
();
void
HazardPointerTest2Slave
();
void
HazardPointerTest2Pre
();
void
HazardPointerTest2Post
();
void
HazardPointerTest2ThreadMethod
();
HazardPointerTest2
();
private
:
// number of threads, participating in that test
int
n
T
hreads
;
int
n
_t
hreads
;
embb
::
base
::
Function
<
void
,
int
*>
delete
PointerCallback
;
embb
::
base
::
Function
<
void
,
int
*>
delete
_pointer_callback_
;
// the thread id of the master
embb
::
base
::
Atomic
<
unsigned
int
>
current
Master
;
embb
::
base
::
Atomic
<
unsigned
int
>
current
_master_
;
// variables, to synchronize threads. At each point in time, one master,
// the master changes each round until each thread was assigned master once.
embb
::
base
::
Atomic
<
int
>
sync1
;
embb
::
base
::
Atomic
<
unsigned
int
>
sync2
;
embb
::
base
::
Atomic
<
int
>
sync1
_
;
embb
::
base
::
Atomic
<
unsigned
int
>
sync2
_
;
unsigned
int
guards
PerThreadCount
;
unsigned
int
guaranteed
CapacityPool
;
unsigned
int
pool
SizeUsingHazardPointer
;
unsigned
int
guards
_per_phread_count_
;
unsigned
int
guaranteed
_capacity_pool_
;
unsigned
int
pool
_size_using_hazard_pointer_
;
// The threads write here, if they guarded an object successfully. Used to
// determine when all allocated objects were guarded successfully.
embb
::
base
::
Atomic
<
int
*>*
shared
Guarded
;
embb
::
base
::
Atomic
<
int
*>*
shared
_guarded_
;
// This array is used by the master, to communicate and share what he has
// allocated with the slaves.
embb
::
base
::
Atomic
<
int
*>*
shared
Allocated
;
embb
::
base
::
Atomic
<
int
*>*
shared
_allocated_
;
// Reference to the object pool
IntObjectTestPool
*
testPool
;
embb
::
containers
::
internal
::
HazardPointer
<
int
*>*
hazardPointer
;
static
const
int
finishMarker
=
-
1
;
IntObjectTestPool
*
test_pool_
;
public
:
void
DeletePointerCallback
(
int
*
toDelete
);
bool
SetRelativeGuards
();
void
HazardPointerTest2Master
();
void
HazardPointerTest2Slave
();
void
HazardPointerTest2Pre
();
void
HazardPointerTest2Post
();
void
HazardPointerTest2ThreadMethod
();
HazardPointerTest2
();
embb
::
containers
::
internal
::
HazardPointer
<
int
*>*
hazard_pointer_
;
static
const
int
FINISH_MARKER
=
-
1
;
};
}
// namespace test
}
// namespace containers
}
// namespace embb
...
...
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