Skip to content

Commit 8c3a0ce

Browse files
authored
Merge pull request #175 from LLNL/bugfix/burmark1/get_a_race
Fix race conditions in resources get_a_* functions
2 parents 526be3b + ae3faf5 commit 8c3a0ce

File tree

4 files changed

+190
-122
lines changed

4 files changed

+190
-122
lines changed

include/camp/resource/cuda.hpp

Lines changed: 16 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include "camp/resource/platform.hpp"
1818

1919
#include <cuda_runtime.h>
20+
#include <array>
2021
#include <mutex>
2122

2223
namespace camp
@@ -81,28 +82,25 @@ namespace resources
8182
{
8283
static cudaStream_t get_a_stream(int num)
8384
{
84-
static cudaStream_t streams[16] = {};
85-
static int previous = 0;
86-
87-
static std::once_flag m_onceFlag;
88-
static std::mutex m_mtx;
89-
90-
std::call_once(m_onceFlag, [] {
91-
if (streams[0] == nullptr) {
92-
for (auto &s : streams) {
93-
campCudaErrchkDiscardReturn(cudaStreamCreate(&s));
94-
}
95-
}
96-
});
85+
static constexpr int num_streams = 16;
86+
static std::array<cudaStream_t, num_streams> s_streams = [] {
87+
std::array<cudaStream_t, num_streams> streams;
88+
for (auto &s : streams) {
89+
campCudaErrchkDiscardReturn(cudaStreamCreate(&s));
90+
}
91+
return streams;
92+
}();
93+
94+
static std::mutex s_mtx;
95+
static int s_previous = num_streams-1;
9796

9897
if (num < 0) {
99-
m_mtx.lock();
100-
previous = (previous + 1) % 16;
101-
m_mtx.unlock();
102-
return streams[previous];
98+
std::lock_guard<std::mutex> lock(s_mtx);
99+
s_previous = (s_previous + 1) % num_streams;
100+
return s_streams[s_previous];
103101
}
104102

105-
return streams[num % 16];
103+
return s_streams[num % num_streams];
106104
}
107105

108106
// Private from-stream constructor

include/camp/resource/hip.hpp

Lines changed: 16 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include "camp/resource/platform.hpp"
1717

1818
#include <hip/hip_runtime.h>
19+
#include <array>
1920
#include <mutex>
2021

2122
namespace camp
@@ -78,28 +79,25 @@ namespace resources
7879
{
7980
static hipStream_t get_a_stream(int num)
8081
{
81-
static hipStream_t streams[16] = {};
82-
static int previous = 0;
83-
84-
static std::once_flag m_onceFlag;
85-
static std::mutex m_mtx;
86-
87-
std::call_once(m_onceFlag, [] {
88-
if (streams[0] == nullptr) {
89-
for (auto &s : streams) {
90-
campHipErrchkDiscardReturn(hipStreamCreate(&s));
91-
}
92-
}
93-
});
82+
static constexpr int num_streams = 16;
83+
static std::array<hipStream_t, num_streams> s_streams = [] {
84+
std::array<hipStream_t, num_streams> streams;
85+
for (auto &s : streams) {
86+
campHipErrchkDiscardReturn(hipStreamCreate(&s));
87+
}
88+
return streams;
89+
}();
90+
91+
static std::mutex s_mtx;
92+
static int s_previous = num_streams-1;
9493

9594
if (num < 0) {
96-
m_mtx.lock();
97-
previous = (previous + 1) % 16;
98-
m_mtx.unlock();
99-
return streams[previous];
95+
std::lock_guard<std::mutex> lock(s_mtx);
96+
s_previous = (s_previous + 1) % num_streams;
97+
return s_streams[s_previous];
10098
}
10199

102-
return streams[num % 16];
100+
return s_streams[num % num_streams];
103101
}
104102

105103
// Private from-stream constructor

include/camp/resource/omp_target.hpp

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -63,19 +63,19 @@ namespace resources
6363
{
6464
static char *get_addr(int num)
6565
{
66-
static char addrs[16] = {};
67-
static int previous = 0;
66+
static constexpr int num_addrs = 16;
67+
static char s_addrs[num_addrs] = {};
6868

69-
static std::mutex m_mtx;
69+
static std::mutex s_mtx;
70+
static int s_previous = num_addrs-1;
7071

7172
if (num < 0) {
72-
m_mtx.lock();
73-
previous = (previous + 1) % 16;
74-
m_mtx.unlock();
75-
return &addrs[previous];
73+
std::lock_guard<std::mutex> lock(s_mtx);
74+
s_previous = (s_previous + 1) % num_addrs;
75+
return &s_addrs[s_previous];
7676
}
7777

78-
return &addrs[num % 16];
78+
return &s_addrs[num % num_addrs];
7979
}
8080

8181
// Private from-address constructor

include/camp/resource/sycl.hpp

Lines changed: 150 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -44,94 +44,162 @@ namespace resources
4444

4545
class Sycl
4646
{
47-
static sycl::queue *get_a_queue(sycl::context &syclContext,
48-
int num,
49-
bool useContext)
50-
{
51-
constexpr auto gpuSelector = sycl::gpu_selector_v;
52-
static sycl::property_list propertyList =
53-
sycl::property_list(sycl::property::queue::in_order());
54-
static sycl::context privateContext;
55-
static sycl::context *contextInUse = NULL;
56-
static std::map<sycl::context *, std::array<sycl::queue, 16>> queueMap;
57-
58-
59-
static std::mutex m_mtx;
60-
m_mtx.lock();
61-
62-
// User passed a context, use it
63-
if (useContext) {
64-
contextInUse = &syclContext;
65-
66-
if (queueMap.find(contextInUse) == queueMap.end()) {
67-
queueMap[contextInUse] = {
68-
sycl::queue(*contextInUse, gpuSelector, propertyList),
69-
sycl::queue(*contextInUse, gpuSelector, propertyList),
70-
sycl::queue(*contextInUse, gpuSelector, propertyList),
71-
sycl::queue(*contextInUse, gpuSelector, propertyList),
72-
sycl::queue(*contextInUse, gpuSelector, propertyList),
73-
sycl::queue(*contextInUse, gpuSelector, propertyList),
74-
sycl::queue(*contextInUse, gpuSelector, propertyList),
75-
sycl::queue(*contextInUse, gpuSelector, propertyList),
76-
sycl::queue(*contextInUse, gpuSelector, propertyList),
77-
sycl::queue(*contextInUse, gpuSelector, propertyList),
78-
sycl::queue(*contextInUse, gpuSelector, propertyList),
79-
sycl::queue(*contextInUse, gpuSelector, propertyList),
80-
sycl::queue(*contextInUse, gpuSelector, propertyList),
81-
sycl::queue(*contextInUse, gpuSelector, propertyList),
82-
sycl::queue(*contextInUse, gpuSelector, propertyList),
83-
sycl::queue(*contextInUse, gpuSelector, propertyList)};
84-
}
85-
} else { // User did not pass context, use last used or private one
86-
if (contextInUse == NULL) {
87-
contextInUse = &privateContext;
88-
queueMap[contextInUse] = {
89-
sycl::queue(*contextInUse, gpuSelector, propertyList),
90-
sycl::queue(*contextInUse, gpuSelector, propertyList),
91-
sycl::queue(*contextInUse, gpuSelector, propertyList),
92-
sycl::queue(*contextInUse, gpuSelector, propertyList),
93-
sycl::queue(*contextInUse, gpuSelector, propertyList),
94-
sycl::queue(*contextInUse, gpuSelector, propertyList),
95-
sycl::queue(*contextInUse, gpuSelector, propertyList),
96-
sycl::queue(*contextInUse, gpuSelector, propertyList),
97-
sycl::queue(*contextInUse, gpuSelector, propertyList),
98-
sycl::queue(*contextInUse, gpuSelector, propertyList),
99-
sycl::queue(*contextInUse, gpuSelector, propertyList),
100-
sycl::queue(*contextInUse, gpuSelector, propertyList),
101-
sycl::queue(*contextInUse, gpuSelector, propertyList),
102-
sycl::queue(*contextInUse, gpuSelector, propertyList),
103-
sycl::queue(*contextInUse, gpuSelector, propertyList),
104-
sycl::queue(*contextInUse, gpuSelector, propertyList)};
105-
}
47+
/*
48+
* \brief Get the camp managed sycl context.
49+
*
50+
* Note that the first call sets up the context with the given argument.
51+
*
52+
* \return Reference to the camp managed sycl context.
53+
*/
54+
static sycl::context& get_private_context(const sycl::context* syclContext)
55+
{
56+
static sycl::context s_context(syclContext ? *syclContext : sycl::context());
57+
return s_context;
58+
}
59+
60+
/*
61+
* \brief Get the per thread camp managed sycl context.
62+
*
63+
* Note that the first call sets up the context with the given argument.
64+
*
65+
* \return Reference to the per thread camp managed sycl context.
66+
*/
67+
static sycl::context& get_thread_private_context(sycl::context const& syclContext)
68+
{
69+
thread_local sycl::context t_context(syclContext);
70+
return t_context;
71+
}
72+
73+
/*
74+
* \brief Get the per thread camp managed sycl context.
75+
*
76+
* Note that the first call sets up the context with the given argument.
77+
*
78+
* \return Reference to the per thread camp managed sycl context.
79+
*/
80+
static sycl::context const& get_thread_default_context(sycl::context const& syclContext)
81+
{
82+
get_private_context(&syclContext);
83+
return get_thread_private_context(syclContext);
84+
}
85+
86+
public:
87+
/*
88+
* \brief Get the camp managed sycl context.
89+
*
90+
* \return Const reference to the camp managed sycl context.
91+
*/
92+
static sycl::context const& get_default_context()
93+
{
94+
return get_private_context(nullptr);
95+
}
96+
97+
/*
98+
* \brief Get the per thread camp managed sycl context.
99+
*
100+
* \return Const reference to the per thread camp managed sycl context.
101+
*/
102+
static sycl::context const& get_thread_default_context()
103+
{
104+
return get_thread_private_context(get_private_context(nullptr));
105+
}
106+
107+
/*
108+
* \brief Set the camp managed sycl context.
109+
*/
110+
static void set_default_context(sycl::context const& syclContext)
111+
{
112+
get_private_context(&syclContext) = syclContext;
113+
}
114+
115+
/*
116+
* \brief Set the per thread camp managed sycl context.
117+
*/
118+
static void set_thread_default_context(sycl::context const& syclContext)
119+
{
120+
get_private_context(&syclContext);
121+
get_thread_private_context(syclContext) = syclContext;
122+
}
123+
124+
private:
125+
static sycl::queue *get_a_queue(const sycl::context* syclContext,
126+
int num)
127+
{
128+
static constexpr int num_queues = 16;
129+
130+
static std::mutex s_mtx;
131+
132+
// note that this type must not invalidate iterators when modified
133+
using value_second_type = std::pair<int, std::array<sycl::queue, num_queues>>;
134+
using queueMap_type = std::map<const sycl::context*, value_second_type>;
135+
static queueMap_type queueMap;
136+
static const typename queueMap_type::iterator queueMap_end = queueMap.end();
137+
thread_local typename queueMap_type::iterator cachedContextIter = queueMap_end;
138+
139+
if (syclContext) {
140+
// implement sticky contexts
141+
set_thread_default_context(*syclContext);
142+
}
143+
syclContext = &get_thread_default_context();
144+
145+
if (syclContext != cachedContextIter->first) {
146+
cachedContextIter = queueMap_end;
106147
}
107-
m_mtx.unlock();
108148

109-
static int previous = 0;
149+
if (cachedContextIter == queueMap_end || num < 0) {
150+
std::lock_guard<std::mutex> lock(s_mtx);
151+
152+
if (cachedContextIter == queueMap_end) {
153+
cachedContextIter = queueMap.find(syclContext);
154+
if (cachedContextIter == queueMap_end) {
155+
static constexpr auto gpuSelector = sycl::gpu_selector_v;
156+
static const sycl::property_list propertyList =
157+
sycl::property_list(sycl::property::queue::in_order());
158+
159+
cachedContextIter = queueMap.emplace(syclContext,
160+
value_second_type(num_queues-1, {
161+
sycl::queue(*syclContext, gpuSelector, propertyList),
162+
sycl::queue(*syclContext, gpuSelector, propertyList),
163+
sycl::queue(*syclContext, gpuSelector, propertyList),
164+
sycl::queue(*syclContext, gpuSelector, propertyList),
165+
sycl::queue(*syclContext, gpuSelector, propertyList),
166+
sycl::queue(*syclContext, gpuSelector, propertyList),
167+
sycl::queue(*syclContext, gpuSelector, propertyList),
168+
sycl::queue(*syclContext, gpuSelector, propertyList),
169+
sycl::queue(*syclContext, gpuSelector, propertyList),
170+
sycl::queue(*syclContext, gpuSelector, propertyList),
171+
sycl::queue(*syclContext, gpuSelector, propertyList),
172+
sycl::queue(*syclContext, gpuSelector, propertyList),
173+
sycl::queue(*syclContext, gpuSelector, propertyList),
174+
sycl::queue(*syclContext, gpuSelector, propertyList),
175+
sycl::queue(*syclContext, gpuSelector, propertyList),
176+
sycl::queue(*syclContext, gpuSelector, propertyList)})
177+
).first;
178+
}
179+
}
110180

111-
static std::once_flag m_onceFlag;
112-
CAMP_ALLOW_UNUSED_LOCAL(m_onceFlag);
113-
if (num < 0) {
114-
m_mtx.lock();
115-
previous = (previous + 1) % 16;
116-
m_mtx.unlock();
117-
return &queueMap[contextInUse][previous];
181+
if (num < 0) {
182+
int& previous = cachedContextIter->second.first;
183+
previous = (previous + 1) % num_queues;
184+
return &cachedContextIter->second.second[previous];
185+
}
118186
}
119187

120-
return &queueMap[contextInUse][num % 16];
188+
return &cachedContextIter->second.second[num % num_queues];
121189
}
122190

123191
// Private from-queue constructor
124192
Sycl(sycl::queue& q) : qu(&q) {}
125193

126194
public:
127-
Sycl(int group = -1)
195+
Sycl(int group = -1, sycl::context const& syclContext = get_thread_default_context())
196+
: qu(get_a_queue(&syclContext, group))
128197
{
129-
sycl::context temp;
130-
qu = get_a_queue(temp, group, false);
131198
}
132199

133-
Sycl(sycl::context &syclContext, int group = -1)
134-
: qu(get_a_queue(syclContext, group, true))
200+
[[deprecated]]
201+
Sycl(sycl::context const& syclContext, int group = -1)
202+
: qu(get_a_queue(&syclContext, group))
135203
{
136204
}
137205

@@ -141,13 +209,16 @@ namespace resources
141209
return Sycl(q);
142210
}
143211

144-
// Methods
145-
Platform get_platform() const { return Platform::sycl; }
212+
// get default resource
146213
static Sycl get_default()
147214
{
148-
static Sycl h;
149-
return h;
215+
return Sycl(0, get_default_context());
150216
}
217+
218+
// Methods
219+
Platform get_platform() const { return Platform::sycl; }
220+
221+
// Event
151222
SyclEvent get_event() { return SyclEvent(get_queue()); }
152223
Event get_event_erased() { return Event{SyclEvent(get_queue())}; }
153224
void wait() { qu->wait(); }
@@ -207,6 +278,7 @@ namespace resources
207278
}
208279
}
209280

281+
// implementation specific
210282
sycl::queue *get_queue() { return qu; }
211283
sycl::queue const *get_queue() const { return qu; }
212284

0 commit comments

Comments
 (0)