summaryrefslogtreecommitdiff
path: root/src/clpp11.hpp
diff options
context:
space:
mode:
authorIvan Shapovalov <intelfx@intelfx.name>2016-10-22 05:14:19 +0300
committerIvan Shapovalov <intelfx@intelfx.name>2016-10-22 08:45:25 +0300
commitb98af44fcf89b9946e1de438b1f5527e6bf28905 (patch)
treefbd5ec2ab1e418830b88e5de42279845911ea0da /src/clpp11.hpp
parent5d03d48f7aaf38d3b28bad612638d2d9db8ebee0 (diff)
treewide: use C++ exceptions properly
Since the codebase is designed around proper C++ idioms such as RAII, it makes sense to only use C++ exceptions internally instead of mixing exceptions and error codes. The exceptions are now caught at top level to preserve compatibility with the existing error code-based API. Note that we deliberately do not catch C++ runtime errors (such as `std::bad_alloc`) nor logic errors (aka failed assertions) because no actual handling can ever happen for such errors. However, in the C interface we do catch _all_ exceptions (...) and convert them into a wild-card error code.
Diffstat (limited to 'src/clpp11.hpp')
-rw-r--r--src/clpp11.hpp111
1 files changed, 67 insertions, 44 deletions
diff --git a/src/clpp11.hpp b/src/clpp11.hpp
index 700a98f6..d306bb87 100644
--- a/src/clpp11.hpp
+++ b/src/clpp11.hpp
@@ -41,7 +41,6 @@
#include <string> // std::string
#include <vector> // std::vector
#include <memory> // std::shared_ptr
-#include <stdexcept> // std::runtime_error
#include <numeric> // std::accumulate
#include <cstring> // std::strlen
@@ -52,28 +51,41 @@
#include <CL/opencl.h>
#endif
+// Exception classes
+#include "cxpp11_common.hpp"
+
namespace clblast {
// =================================================================================================
-// Error occurred in the C++11 OpenCL header (this file)
-inline void Error(const std::string &message) {
- throw std::runtime_error("Internal OpenCL error: "+message);
-}
+// Represents a runtime error returned by an OpenCL API function
+class CLError : public ErrorCode<DeviceError, cl_int> {
+ public:
+ explicit CLError(cl_int status, const std::string &where):
+ ErrorCode(status,
+ where,
+ "OpenCL error: " + where + ": " + std::to_string(static_cast<int>(status))) {
+ }
-// Error occurred in OpenCL
-inline void CheckError(const cl_int status) {
- if (status != CL_SUCCESS) {
- throw std::runtime_error("Internal OpenCL error: "+std::to_string(status));
+ static void Check(const cl_int status, const std::string &where) {
+ if (status != CL_SUCCESS) {
+ throw CLError(status, where);
+ }
}
-}
-// Error occured in OpenCL (no-exception version for destructors)
-inline void CheckErrorDtor(const cl_int status) {
- if (status != CL_SUCCESS) {
- auto message = "Internal OpenCL Error: "+std::to_string(status) + " (ignoring)";
- fprintf(stderr, "%s\n", message.c_str());
+ static void CheckDtor(const cl_int status, const std::string &where) {
+ if (status != CL_SUCCESS) {
+ fprintf(stderr, "CLBlast: %s (ignoring)\n", CLError(status, where).what());
+ }
}
-}
+};
+
+// =================================================================================================
+
+// Error occurred in OpenCL
+#define CheckError(call) CLError::Check(call, CLError::TrimCallString(#call))
+
+// Error occured in OpenCL (no-exception version for destructors)
+#define CheckErrorDtor(call) CLError::CheckDtor(call, CLError::TrimCallString(#call))
// =================================================================================================
@@ -140,10 +152,14 @@ class Platform {
explicit Platform(const size_t platform_id) {
auto num_platforms = cl_uint{0};
CheckError(clGetPlatformIDs(0, nullptr, &num_platforms));
- if (num_platforms == 0) { Error("no platforms found"); }
+ if (num_platforms == 0) {
+ throw RuntimeError("Platform: no platforms found");
+ }
+ if (platform_id >= num_platforms) {
+ throw RuntimeError("Platform: invalid platform ID "+std::to_string(platform_id));
+ }
auto platforms = std::vector<cl_platform_id>(num_platforms);
CheckError(clGetPlatformIDs(num_platforms, platforms.data(), nullptr));
- if (platform_id >= num_platforms) { Error("invalid platform ID "+std::to_string(platform_id)); }
platform_ = platforms[platform_id];
}
@@ -183,11 +199,16 @@ class Device {
// Initialize the device. Note that this constructor can throw exceptions!
explicit Device(const Platform &platform, const size_t device_id) {
auto num_devices = platform.NumDevices();
- if (num_devices == 0) { Error("no devices found"); }
+ if (num_devices == 0) {
+ throw RuntimeError("Device: no devices found");
+ }
+ if (device_id >= num_devices) {
+ throw RuntimeError("Device: invalid device ID "+std::to_string(device_id));
+ }
+
auto devices = std::vector<cl_device_id>(num_devices);
CheckError(clGetDeviceIDs(platform(), CL_DEVICE_TYPE_ALL, static_cast<cl_uint>(num_devices),
devices.data(), nullptr));
- if (device_id >= num_devices) { Error("invalid device ID "+std::to_string(device_id)); }
device_ = devices[device_id];
}
@@ -315,7 +336,7 @@ class Context {
auto status = CL_SUCCESS;
const cl_device_id dev = device();
*context_ = clCreateContext(nullptr, 1, &dev, nullptr, nullptr, &status);
- CheckError(status);
+ CLError::Check(status, "clCreateContext");
}
// Accessor to the private data-member
@@ -346,7 +367,7 @@ class Program {
source_ptr_(&source_[0]) {
auto status = CL_SUCCESS;
*program_ = clCreateProgramWithSource(context(), 1, &source_ptr_, &length_, &status);
- CheckError(status);
+ CLError::Check(status, "clCreateProgramWithSource");
}
// Binary-based constructor with memory management
@@ -361,25 +382,15 @@ class Program {
*program_ = clCreateProgramWithBinary(context(), 1, &dev, &length_,
reinterpret_cast<const unsigned char**>(&source_ptr_),
&status1, &status2);
- CheckError(status1);
- CheckError(status2);
+ CLError::Check(status1, "clCreateProgramWithBinary (binary status)");
+ CLError::Check(status2, "clCreateProgramWithBinary");
}
// Compiles the device program and returns whether or not there where any warnings/errors
- BuildStatus Build(const Device &device, std::vector<std::string> &options) {
+ void Build(const Device &device, std::vector<std::string> &options) {
auto options_string = std::accumulate(options.begin(), options.end(), std::string{" "});
const cl_device_id dev = device();
- auto status = clBuildProgram(*program_, 1, &dev, options_string.c_str(), nullptr, nullptr);
- if (status == CL_BUILD_PROGRAM_FAILURE) {
- return BuildStatus::kError;
- }
- else if (status == CL_INVALID_BINARY) {
- return BuildStatus::kInvalid;
- }
- else {
- CheckError(status);
- return BuildStatus::kSuccess;
- }
+ CheckError(clBuildProgram(*program_, 1, &dev, options_string.c_str(), nullptr, nullptr));
}
// Retrieves the warning/error message from the compiler (if any)
@@ -436,15 +447,17 @@ class Queue {
{
cl_queue_properties properties[] = {CL_QUEUE_PROPERTIES, CL_QUEUE_PROFILING_ENABLE, 0};
*queue_ = clCreateCommandQueueWithProperties(context(), device(), properties, &status);
+ CLError::Check(status, "clCreateCommandQueueWithProperties");
}
else
{
*queue_ = clCreateCommandQueue(context(), device(), CL_QUEUE_PROFILING_ENABLE, &status);
+ CLError::Check(status, "clCreateCommandQueue");
}
#else
*queue_ = clCreateCommandQueue(context(), device(), CL_QUEUE_PROFILING_ENABLE, &status);
+ CLError::Check(status, "clCreateCommandQueue");
#endif
- CheckError(status);
}
// Synchronizes the queue
@@ -536,7 +549,7 @@ class Buffer {
if (access_ == BufferAccess::kWriteOnly) { flags = CL_MEM_WRITE_ONLY; }
auto status = CL_SUCCESS;
*buffer_ = clCreateBuffer(context(), flags, size*sizeof(T), nullptr, &status);
- CheckError(status);
+ CLError::Check(status, "clCreateBuffer");
}
// As above, but now with read/write access as a default
@@ -557,18 +570,24 @@ class Buffer {
// Copies from device to host: reading the device buffer a-synchronously
void ReadAsync(const Queue &queue, const size_t size, T* host, const size_t offset = 0) const {
- if (access_ == BufferAccess::kWriteOnly) { Error("reading from a write-only buffer"); }
+ if (access_ == BufferAccess::kWriteOnly) {
+ throw LogicError("Buffer: reading from a write-only buffer");
+ }
CheckError(clEnqueueReadBuffer(queue(), *buffer_, CL_FALSE, offset*sizeof(T), size*sizeof(T),
host, 0, nullptr, nullptr));
}
void ReadAsync(const Queue &queue, const size_t size, std::vector<T> &host,
const size_t offset = 0) const {
- if (host.size() < size) { Error("target host buffer is too small"); }
+ if (host.size() < size) {
+ throw LogicError("Buffer: target host buffer is too small");
+ }
ReadAsync(queue, size, host.data(), offset);
}
void ReadAsync(const Queue &queue, const size_t size, BufferHost<T> &host,
const size_t offset = 0) const {
- if (host.size() < size) { Error("target host buffer is too small"); }
+ if (host.size() < size) {
+ throw LogicError("Buffer: target host buffer is too small");
+ }
ReadAsync(queue, size, host.data(), offset);
}
@@ -588,8 +607,12 @@ class Buffer {
// Copies from host to device: writing the device buffer a-synchronously
void WriteAsync(const Queue &queue, const size_t size, const T* host, const size_t offset = 0) {
- if (access_ == BufferAccess::kReadOnly) { Error("writing to a read-only buffer"); }
- if (GetSize() < (offset+size)*sizeof(T)) { Error("target device buffer is too small"); }
+ if (access_ == BufferAccess::kReadOnly) {
+ throw LogicError("Buffer: writing to a read-only buffer");
+ }
+ if (GetSize() < (offset+size)*sizeof(T)) {
+ throw LogicError("Buffer: target device buffer is too small");
+ }
CheckError(clEnqueueWriteBuffer(queue(), *buffer_, CL_FALSE, offset*sizeof(T), size*sizeof(T),
host, 0, nullptr, nullptr));
}
@@ -658,7 +681,7 @@ class Kernel {
kernel_(new cl_kernel, [](cl_kernel* k) { CheckErrorDtor(clReleaseKernel(*k)); delete k; }) {
auto status = CL_SUCCESS;
*kernel_ = clCreateKernel(program(), name.c_str(), &status);
- CheckError(status);
+ CLError::Check(status, "clCreateKernel");
}
// Sets a kernel argument at the indicated position