Skip to content
Toggle navigation
P
Projects
G
Groups
S
Snippets
Help
las3_pub
/
predictable_parallel_patterns
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
4cf3848f
authored
5 years ago
by
FritzFlorian
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Sketch out idea for lock free trading deque.
parent
1b576824
Pipeline
#1342
failed with stages
in 36 seconds
Changes
2
Pipelines
1
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
201 additions
and
1 deletions
+201
-1
lib/pls/CMakeLists.txt
+1
-1
lib/pls/include/pls/internal/data_structures/bounded_trading_deque.h
+200
-0
No files found.
lib/pls/CMakeLists.txt
View file @
4cf3848f
...
@@ -58,7 +58,7 @@ add_library(pls STATIC
...
@@ -58,7 +58,7 @@ add_library(pls STATIC
include/pls/internal/scheduling/task.h src/internal/scheduling/task.cpp
include/pls/internal/scheduling/task.h src/internal/scheduling/task.cpp
include/pls/internal/scheduling/cont_manager.h
include/pls/internal/scheduling/cont_manager.h
include/pls/internal/scheduling/cont.h
include/pls/internal/scheduling/cont.h
include/pls/internal/data_structures/bounded_ws_deque.h include/pls/internal/data_structures/optional.h include/pls/internal/scheduling/memory_block.h include/pls/internal/scheduling/thread_state_static.h src/internal/base/error_handling.cpp
)
include/pls/internal/data_structures/bounded_ws_deque.h include/pls/internal/data_structures/optional.h include/pls/internal/scheduling/memory_block.h include/pls/internal/scheduling/thread_state_static.h src/internal/base/error_handling.cpp
include/pls/internal/data_structures/bounded_trading_deque.h
)
# Add everything in `./include` to be in the include path of this project
# Add everything in `./include` to be in the include path of this project
target_include_directories
(
pls
target_include_directories
(
pls
...
...
This diff is collapsed.
Click to expand it.
lib/pls/include/pls/internal/data_structures/bounded_trading_deque.h
0 → 100644
View file @
4cf3848f
#ifndef PLS_INTERNAL_DATA_STRUCTURES_BOUNDED_TRADING_DEQUE_H_
#define PLS_INTERNAL_DATA_STRUCTURES_BOUNDED_TRADING_DEQUE_H_
#include <atomic>
#include <pls/internal/base/system_details.h>
#include "pls/internal/base/error_handling.h"
#include "pls/internal/base/system_details.h"
#include "pls/internal/data_structures/optional.h"
#include "pls/internal/data_structures/stamped_integer.h"
namespace
pls
{
namespace
internal
{
namespace
data_structures
{
template
<
typename
TradedType
>
class
traded_field
{
// TODO: Replace unsigned long with a portable sized integer
// (some systems might have different pointer sizes to long sizes).
void
fill_with_tag
(
unsigned
long
tag
)
{
pointer_
=
(
void
*
)
((
tag
<<
1lu
)
|
0x1lu
);
}
unsigned
long
get_tag
()
{
PLS_ASSERT
(
is_filled_with_tag
(),
"Must only read out the tag when the traded field contains one."
);
return
((
unsigned
long
)
(
pointer_
))
>>
1lu
;
}
bool
is_filled_with_tag
()
{
return
((
unsigned
long
)
(
pointer_
))
&
0x1lu
;
}
void
fill_with_object
(
TradedType
*
object
)
{
PLS_ASSERT
((
object
&
0x1lu
)
==
0
,
"Must only store aligned objects in this data structure (last bits are needed for tag bit)"
);
pointer_
=
object
;
}
TradedType
*
get_object
()
{
PLS_ASSERT
(
is_filled_with_object
(),
"Must only read out the object when the traded field contains one."
);
return
pointer_
;
}
bool
is_filled_with_object
()
{
return
!
is_filled_with_tag
()
&&
pointer_
!=
nullptr
;
}
private
:
void
*
pointer_
{
nullptr
};
};
template
<
typename
EntryType
,
typename
TradedType
>
class
alignas
(
base
::
system_details
::
CACHE_LINE_SIZE
)
trading_deque_entry
{
/*
* Fill the slot with its initial values, making it ready for being stolen.
* Performs no synchronization/memory ordering constraints.
*
* Method is called to init a field on pushBot.
*/
void
fill_slots
(
EntryType
*
entry_item
,
unsigned
long
tag
)
{
entry_slot_
.
store
(
entry_item
,
std
::
memory_order_relaxed
);
// Relaxed is fine for this, as adding elements is synced over the bot pointer
auto
old
=
trade_slot_
.
load
(
std
::
memory_order_relaxed
);
old
.
fill_with_tag
(
tag
);
trade_slot_
.
store
(
std
::
memory_order_relaxed
);
}
/**
* Tries to atomically read out the object traded in by thieves.
* Either returns the traded in field (the slot was stolen) or no result (the slot is still owned locally).
*
* Method is used to pop a field on popBot.
*/
optional
<
TradedType
*>
acquire_traded_type
()
{
traded_field
<
TradedType
>
empty_field
;
traded_field
<
TradedType
>
old_field_value
=
trade_slot_
.
exchange
(
empty_field
,
std
::
memory_order_acq_rel
);
if
(
old_field_value
.
is_filled_with_tag
())
{
return
optional
<
TradedType
*>
();
}
else
{
return
optional
<
TradedType
*>
(
old_field_value
.
get_object
());
}
}
EntryType
*
get_object
()
{
return
entry_slot_
;
}
optional
<
EntryType
*>
trade_object
(
TradedType
*
offered_object
,
unsigned
long
expected_tag
)
{
// Read our potential result
EntryType
*
result
=
entry_slot_
.
load
(
std
::
memory_order_relaxed
);
// Try to get it by CAS with the expected field entry, giving up our offered_object for it
traded_field
<
TradedType
>
expected_field
;
expected_field
.
fill_with_tag
(
expected_tag
);
traded_field
<
TradedType
>
offered_field
;
offered_field
.
fill_with_object
(
offered_object
);
if
(
trade_slot_
.
compare_exchange_strong
(
expected_field
,
offered_field
,
std
::
memory_order_acq_rel
))
{
return
optional
<
EntryType
*>
(
result
);
}
else
{
return
optional
<
EntryType
*>
(
nullptr
);
}
}
private
:
std
::
atomic
<
EntryType
*>
entry_slot_
{
nullptr
};
std
::
atomic
<
traded_field
<
TradedType
>>
trade_slot_
{};
};
/**
* A work stealing deque (single produces/consumer at the end, multiple consumers at the start).
* A pointer to an OfferedType object can only be acquired by stealing consumers (from the start),
* when they also offer a pointer to a TradeType object.
*
* The exchange of 'goods' (OfferedType and TradedType) happens atomically at a linearization point.
* This means that the owning thread always gets a TradedType for each and every OfferedType that was
* successfully stolen.
*
* The owner of the deque must pop ALL elements, even the stolen ones (to get the traded goods instead).
*
* @tparam EntryType The type of objects stored in the deque
* @tparam TradedType The type of objects traded in for acquiring a deque element.
*/
template
<
typename
EntryType
,
typename
TradedType
>
class
bounded_trading_deque
{
using
deque_entry
=
trading_deque_entry
<
EntryType
,
TradedType
>
;
public
:
bounded_trading_deque
(
deque_entry
*
entries
,
size_t
num_entries
)
:
entries_
{
entries
},
num_entries_
{
num_entries
}
{};
void
push_bot
(
EntryType
*
offered_object
)
{
auto
expected_stamp
=
bot_internal_
.
stamp
;
auto
*
current_entry
=
entries_
[
bot_internal_
.
value
];
current_entry
->
fill_slots
(
offered_object
,
expected_stamp
);
bot_internal_
.
stamp
++
;
bot_internal_
.
value
++
;
bot_
.
store
(
bot_internal_
.
value
,
std
::
memory_order_release
);
}
struct
pop_result
{
explicit
pop_result
(
optional
<
EntryType
*>
entry
,
optional
<
TradedType
*>
traded
)
:
entry_
{
entry
},
traded_
{
traded
}
{};
pop_result
()
:
entry_
{},
traded_
{}
{};
optional
<
EntryType
*>
entry_
;
optional
<
TradedType
*>
traded_
;
};
pop_result
pop_bot
()
{
if
(
bot_internal_
.
value
==
0
)
{
return
pop_result
{};
// Empty, nothing to return...
}
// Go one step back
bot_internal_
.
value
--
;
auto
*
current_entry
=
entries_
[
bot_internal_
.
value
];
optional
<
TradedType
*>
traded_object
=
current_entry
->
acquire_traded_type
();
optional
<
EntryType
*>
queue_entry
;
if
(
traded_object
)
{
// We do not return an entry, but the traded object
queue_entry
=
{};
}
else
{
// We still got it locally, grab the object
queue_entry
=
{
current_entry
->
get_object
()};
// Keep the tag up to date (we must re-use it, as the head is just increasing by steps of one from the beginning)
bot_internal_
.
stamp
--
;
}
bot_
.
store
(
bot_internal_
.
value
,
std
::
memory_order_relaxed
);
return
pop_result
{
queue_entry
,
traded_object
};
}
optional
<
EntryType
*>
pop_top
(
TradedType
*
trade_offer
)
{
auto
local_top
=
top_
.
load
();
optional
<
EntryType
*>
entry
=
entries_
[
local_top
.
value
].
trade_object
(
trade_offer
,
local_top
.
stamp
);
top_
.
compare_exchange_strong
(
local_top
,
{
local_top
.
stamp
+
1
,
local_top
.
value
+
1
});
return
entry
;
}
private
:
deque_entry
*
entries_
;
size_t
num_entries_
;
std
::
atomic
<
stamped_integer
>
top_
{{
0
,
0
}};
std
::
atomic
<
size_t
>
bot_
{
0
};
stamped_integer
bot_internal_
{
0
,
0
};
};
}
}
}
#endif //PLS_INTERNAL_DATA_STRUCTURES_BOUNDED_TRADING_DEQUE_H_
This diff is collapsed.
Click to expand it.
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