|
17 | 17 |
|
18 | 18 | #include <boost/throw_exception.hpp> |
19 | 19 |
|
| 20 | +#if defined(BOOST_COMPUTE_THREAD_SAFE) |
| 21 | +# if defined(BOOST_COMPUTE_USE_CPP11) |
| 22 | +# include <mutex> |
| 23 | +# include <thread> |
| 24 | +# include <atomic> |
| 25 | +# else |
| 26 | +# include <boost/thread/mutex.hpp> |
| 27 | +# include <boost/thread/lock_guard.hpp> |
| 28 | +# include <boost/atomic.hpp> |
| 29 | +# endif |
| 30 | +#endif |
| 31 | + |
20 | 32 | #include <boost/compute/cl.hpp> |
21 | 33 | #include <boost/compute/device.hpp> |
22 | 34 | #include <boost/compute/context.hpp> |
23 | 35 | #include <boost/compute/platform.hpp> |
24 | 36 | #include <boost/compute/command_queue.hpp> |
25 | 37 | #include <boost/compute/detail/getenv.hpp> |
26 | 38 | #include <boost/compute/exception/no_device_found.hpp> |
| 39 | +#include <boost/compute/exception/context_error.hpp> |
27 | 40 |
|
28 | 41 | namespace boost { |
29 | 42 | namespace compute { |
@@ -78,9 +91,7 @@ class system |
78 | 91 | /// \endcode |
79 | 92 | static device default_device() |
80 | 93 | { |
81 | | - static device default_device = find_default_device(); |
82 | | - |
83 | | - return default_device; |
| 94 | + return init_default_device(); |
84 | 95 | } |
85 | 96 |
|
86 | 97 | /// Returns the device with \p name. |
@@ -149,17 +160,27 @@ class system |
149 | 160 | /// same context object being returned. |
150 | 161 | static context default_context() |
151 | 162 | { |
152 | | - static context default_context(default_device()); |
153 | | - |
154 | | - return default_context; |
| 163 | + return init_default_context(); |
155 | 164 | } |
156 | 165 |
|
157 | 166 | /// Returns the default command queue for the system. |
158 | | - static command_queue& default_queue() |
| 167 | + /// |
| 168 | + /// If user-provided command queue is given, the system-wide default context |
| 169 | + /// and default device will be set up appropriately so that the default queue |
| 170 | + /// matches the default context and device. |
| 171 | + /// |
| 172 | + /// If the OpenCL context and device associated with user-provided command queue |
| 173 | + /// does not match the default context and device that have already been set, |
| 174 | + /// a set_default_queue_error exception is thrown. For example: |
| 175 | + /// |
| 176 | + /// \snippet test/test_attach_user_queue_error.cpp queue_mismatch |
| 177 | + /// |
| 178 | + /// The default queue is created once on the first time this function is |
| 179 | + /// called. Calling this function multiple times will always result in the |
| 180 | + /// same command queue object being returned. |
| 181 | + static command_queue& default_queue(const command_queue &user_queue = command_queue()) |
159 | 182 | { |
160 | | - static command_queue queue(default_context(), default_device()); |
161 | | - |
162 | | - return queue; |
| 183 | + return init_default_queue(user_queue); |
163 | 184 | } |
164 | 185 |
|
165 | 186 | /// Blocks until all outstanding computations on the default |
@@ -280,6 +301,144 @@ class system |
280 | 301 | { |
281 | 302 | return str.find(pattern) != std::string::npos; |
282 | 303 | } |
| 304 | + |
| 305 | + /// \internal_ |
| 306 | + static device init_default_device(const device &user_device = device()) |
| 307 | + { |
| 308 | + static device default_device; |
| 309 | + |
| 310 | +#ifdef BOOST_COMPUTE_THREAD_SAFE |
| 311 | + #ifdef BOOST_COMPUTE_USE_CPP11 |
| 312 | + using namespace std; |
| 313 | + #else |
| 314 | + using namespace boost; |
| 315 | + #endif |
| 316 | + static atomic<bool> is_init; |
| 317 | + static mutex init_mutex; |
| 318 | + |
| 319 | + bool is_init_value = is_init.load(memory_order_consume); |
| 320 | + if (!is_init_value) |
| 321 | + { |
| 322 | + lock_guard<mutex> lock(init_mutex); |
| 323 | + is_init_value = is_init.load(memory_order_consume); |
| 324 | + if (!is_init_value) |
| 325 | + { |
| 326 | + default_device = user_device.get() ? |
| 327 | + user_device : find_default_device(); |
| 328 | + |
| 329 | + is_init.store(true, memory_order_release); |
| 330 | + } |
| 331 | + } |
| 332 | +#else // BOOST_COMPUTE_THREAD_SAFE |
| 333 | + if (!default_device.get()) |
| 334 | + { |
| 335 | + default_device = user_device.get() ? |
| 336 | + user_device : find_default_device(); |
| 337 | + } |
| 338 | +#endif // BOOST_COMPUTE_THREAD_SAFE |
| 339 | + return default_device; |
| 340 | + } |
| 341 | + |
| 342 | + /// \internal_ |
| 343 | + static context init_default_context(const context &user_context = context()) |
| 344 | + { |
| 345 | + static context default_context; |
| 346 | + |
| 347 | +#ifdef BOOST_COMPUTE_THREAD_SAFE |
| 348 | + #ifdef BOOST_COMPUTE_USE_CPP11 |
| 349 | + using namespace std; |
| 350 | + #else |
| 351 | + using namespace boost; |
| 352 | + #endif |
| 353 | + static atomic<bool> is_init; |
| 354 | + static mutex init_mutex; |
| 355 | + |
| 356 | + bool is_init_value = is_init.load(memory_order_consume); |
| 357 | + if (!is_init_value) |
| 358 | + { |
| 359 | + lock_guard<mutex> lock(init_mutex); |
| 360 | + is_init_value = is_init.load(memory_order_consume); |
| 361 | + if (!is_init_value) |
| 362 | + { |
| 363 | + default_context = user_context.get() ? |
| 364 | + user_context : context(default_device()); |
| 365 | + |
| 366 | + is_init.store(true, memory_order_release); |
| 367 | + } |
| 368 | + } |
| 369 | +#else // BOOST_COMPUTE_THREAD_SAFE |
| 370 | + if (!default_context.get()) |
| 371 | + { |
| 372 | + default_context = user_context.get() ? |
| 373 | + user_context : context(default_device()); |
| 374 | + } |
| 375 | +#endif // BOOST_COMPUTE_THREAD_SAFE |
| 376 | + return default_context; |
| 377 | + } |
| 378 | + |
| 379 | + /// \internal_ |
| 380 | + static void init_default_device_and_context(const command_queue &user_queue) |
| 381 | + { |
| 382 | + device user_device = user_queue.get_device(); |
| 383 | + context user_context = user_queue.get_context(); |
| 384 | + |
| 385 | + if ( (user_device != init_default_device(user_device)) || |
| 386 | + (user_context != init_default_context(user_context)) ) |
| 387 | + { |
| 388 | + // Try invoking default_queue() before anything else |
| 389 | + BOOST_THROW_EXCEPTION(set_default_queue_error()); |
| 390 | + } |
| 391 | + } |
| 392 | + |
| 393 | + /// \internal_ |
| 394 | + static command_queue& init_default_queue(const command_queue &user_queue = command_queue()) |
| 395 | + { |
| 396 | + static command_queue default_queue; |
| 397 | + |
| 398 | +#ifdef BOOST_COMPUTE_THREAD_SAFE |
| 399 | + #ifdef BOOST_COMPUTE_USE_CPP11 |
| 400 | + using namespace std; |
| 401 | + #else |
| 402 | + using namespace boost; |
| 403 | + #endif |
| 404 | + static atomic<bool> is_init; |
| 405 | + static mutex init_mutex; |
| 406 | + |
| 407 | + bool is_init_value = is_init.load(memory_order_consume); |
| 408 | + if (!is_init_value) |
| 409 | + { |
| 410 | + lock_guard<mutex> lock(init_mutex); |
| 411 | + is_init_value = is_init.load(memory_order_consume); |
| 412 | + if (!is_init_value) |
| 413 | + { |
| 414 | + if (user_queue.get()) |
| 415 | + init_default_device_and_context(user_queue); |
| 416 | + |
| 417 | + default_queue = user_queue.get() ? |
| 418 | + user_queue : |
| 419 | + command_queue(default_context(), default_device()); |
| 420 | + |
| 421 | + is_init.store(true, memory_order_release); |
| 422 | + } |
| 423 | + } |
| 424 | +#else // BOOST_COMPUTE_THREAD_SAFE |
| 425 | + if (!default_queue.get()) |
| 426 | + { |
| 427 | + if (user_queue.get()) |
| 428 | + init_default_device_and_context(user_queue); |
| 429 | + |
| 430 | + default_queue = user_queue.get() ? |
| 431 | + user_queue : |
| 432 | + command_queue(default_context(), default_device()); |
| 433 | + } |
| 434 | +#endif // BOOST_COMPUTE_THREAD_SAFE |
| 435 | + else |
| 436 | + { |
| 437 | + BOOST_ASSERT_MSG(user_queue.get() == 0, |
| 438 | + "Default command queue has already been set."); |
| 439 | + } |
| 440 | + return default_queue; |
| 441 | + } |
283 | 442 | }; |
284 | 443 |
|
285 | 444 | } // end compute namespace |
|
0 commit comments