Skip to content

Commit 915ab0b

Browse files
authored
Merge pull request #832 from rosenrodt/pr-set-default-command-queue
Allow manually setting default command queue via `default_queue()`
2 parents 5a29a4d + 2583e31 commit 915ab0b

File tree

9 files changed

+365
-15
lines changed

9 files changed

+365
-15
lines changed

doc/reference.qbk

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,8 @@ Header: `<boost/compute/exception.hpp>`
149149
* [classref boost::compute::no_device_found no_device_found]
150150
* [classref boost::compute::opencl_error opencl_error]
151151
* [classref boost::compute::unsupported_extension_error unsupported_extension_error]
152+
* [classref boost::compute::program_build_failure program_build_failure]
153+
* [classref boost::compute::set_default_queue_error set_default_queue_error]
152154

153155
[h3 Iterators]
154156

example/CMakeLists.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,11 @@ set(EXAMPLES
3737
# boost library link dependencies
3838
set(EXAMPLE_BOOST_COMPONENTS program_options)
3939

40+
if(${BOOST_COMPUTE_USE_CPP11})
41+
# allow examples to use C++11 features
42+
add_definitions(-DBOOST_COMPUTE_USE_CPP11)
43+
endif()
44+
4045
if (${BOOST_COMPUTE_USE_OFFLINE_CACHE})
4146
set(EXAMPLE_BOOST_COMPONENTS ${EXAMPLE_BOOST_COMPONENTS} system filesystem)
4247
endif()

include/boost/compute/context.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include <boost/compute/config.hpp>
1919
#include <boost/compute/device.hpp>
2020
#include <boost/compute/exception/opencl_error.hpp>
21+
#include <boost/compute/exception/set_default_queue_error.hpp>
2122
#include <boost/compute/detail/assert_cl_success.hpp>
2223

2324
namespace boost {
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
//---------------------------------------------------------------------------//
2+
// Copyright (c) 2019 Anthony Chang <ac.chang@outlook.com>
3+
//
4+
// Distributed under the Boost Software License, Version 1.0
5+
// See accompanying file LICENSE_1_0.txt or copy at
6+
// http://www.boost.org/LICENSE_1_0.txt
7+
//
8+
// See http://boostorg.github.com/compute for more information.
9+
//---------------------------------------------------------------------------//
10+
11+
#ifndef BOOST_COMPUTE_EXCEPTION_SET_DEFAULT_QUEUE_ERROR_HPP
12+
#define BOOST_COMPUTE_EXCEPTION_SET_DEFAULT_QUEUE_ERROR_HPP
13+
14+
#include <exception>
15+
16+
namespace boost {
17+
namespace compute {
18+
19+
/// \class set_default_queue_error
20+
/// \brief Exception thrown when failure to set default command queue
21+
///
22+
/// This exception is thrown when Boost.Compute fails to set up user-provided
23+
/// default command queue for the system.
24+
class set_default_queue_error : public std::exception
25+
{
26+
public:
27+
/// Creates a new set_default_queue_error exception object.
28+
set_default_queue_error() throw()
29+
{
30+
}
31+
32+
/// Destroys the set_default_queue_error object.
33+
~set_default_queue_error() throw()
34+
{
35+
}
36+
37+
/// Returns a string with a description of the error.
38+
const char* what() const throw()
39+
{
40+
return "User command queue mismatches default device and/or context";
41+
}
42+
};
43+
44+
} // end compute namespace
45+
} // end boost namespace
46+
47+
#endif // BOOST_COMPUTE_EXCEPTION_SET_DEFAULT_QUEUE_ERROR_HPP

include/boost/compute/system.hpp

Lines changed: 169 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,26 @@
1717

1818
#include <boost/throw_exception.hpp>
1919

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+
2032
#include <boost/compute/cl.hpp>
2133
#include <boost/compute/device.hpp>
2234
#include <boost/compute/context.hpp>
2335
#include <boost/compute/platform.hpp>
2436
#include <boost/compute/command_queue.hpp>
2537
#include <boost/compute/detail/getenv.hpp>
2638
#include <boost/compute/exception/no_device_found.hpp>
39+
#include <boost/compute/exception/context_error.hpp>
2740

2841
namespace boost {
2942
namespace compute {
@@ -78,9 +91,7 @@ class system
7891
/// \endcode
7992
static device default_device()
8093
{
81-
static device default_device = find_default_device();
82-
83-
return default_device;
94+
return init_default_device();
8495
}
8596

8697
/// Returns the device with \p name.
@@ -149,17 +160,27 @@ class system
149160
/// same context object being returned.
150161
static context default_context()
151162
{
152-
static context default_context(default_device());
153-
154-
return default_context;
163+
return init_default_context();
155164
}
156165

157166
/// 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())
159182
{
160-
static command_queue queue(default_context(), default_device());
161-
162-
return queue;
183+
return init_default_queue(user_queue);
163184
}
164185

165186
/// Blocks until all outstanding computations on the default
@@ -280,6 +301,144 @@ class system
280301
{
281302
return str.find(pattern) != std::string::npos;
282303
}
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+
}
283442
};
284443

285444
} // end compute namespace

test/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,8 @@ add_compute_test("core.program" test_program.cpp)
8383
add_compute_test("core.system" test_system.cpp)
8484
add_compute_test("core.type_traits" test_type_traits.cpp)
8585
add_compute_test("core.user_event" test_user_event.cpp)
86+
add_compute_test("core.attach_user_queue_error" test_attach_user_queue_error.cpp)
87+
add_compute_test("core.attach_user_queue_thread_safety" test_attach_user_queue_thread_safety.cpp)
8688

8789
add_compute_test("utility.extents" test_extents.cpp)
8890
add_compute_test("utility.invoke" test_invoke.cpp)
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
//---------------------------------------------------------------------------//
2+
// Copyright (c) 2019 Anthony Chang <ac.chang@outlook.com>
3+
//
4+
// Distributed under the Boost Software License, Version 1.0
5+
// See accompanying file LICENSE_1_0.txt or copy at
6+
// http://www.boost.org/LICENSE_1_0.txt
7+
//
8+
// See http://boostorg.github.com/compute for more information.
9+
//---------------------------------------------------------------------------//
10+
11+
#define BOOST_TEST_MODULE TestAttachUserQueueError
12+
#include <boost/test/unit_test.hpp>
13+
14+
#include <boost/compute/context.hpp>
15+
#include <boost/compute/device.hpp>
16+
#include <boost/compute/system.hpp>
17+
18+
namespace compute = boost::compute;
19+
20+
// For correct usage of setting up global default queue, see test_system.cpp
21+
BOOST_AUTO_TEST_CASE(user_context_device_mismatch)
22+
{
23+
//! [queue_mismatch]
24+
compute::device user_device = compute::system::devices().front();
25+
compute::context user_context(user_device);
26+
compute::command_queue user_queue(user_context, user_device);
27+
28+
// Don't call default_device() or default_context() before calling
29+
// default_queue() if you wish to attach your command queue
30+
compute::system::default_context();
31+
32+
BOOST_CHECK_THROW(
33+
compute::system::default_queue(user_queue),
34+
compute::set_default_queue_error);
35+
//! [queue_mismatch]
36+
}

0 commit comments

Comments
 (0)