Program Listing for File FLAMEGPUDeviceException_device.cuh

Return to documentation for file (include/flamegpu/exception/FLAMEGPUDeviceException_device.cuh)

#ifndef INCLUDE_FLAMEGPU_EXCEPTION_FLAMEGPUDEVICEEXCEPTION_DEVICE_CUH_
#define INCLUDE_FLAMEGPU_EXCEPTION_FLAMEGPUDEVICEEXCEPTION_DEVICE_CUH_

#include <cuda_runtime.h>
#include <device_launch_parameters.h>  // Required for blockIdx, when not built using nvcc / nvrtc. In which case this header file is invalid anyway. It it still required to improve MSVC intellisense though?

#include <cstring>

#include "flamegpu/runtime/detail/SharedBlock.h"

namespace flamegpu {
namespace exception {

#if !defined(FLAMEGPU_SEATBELTS) || FLAMEGPU_SEATBELTS
#ifdef __CUDACC__
#define DTHROW flamegpu::exception::DeviceException::create(__FILE__, __LINE__).setMessage
#else
// Just trying to make host compiler happy when it sees device code by mistake
#define DTHROW(...)
#endif

struct DeviceExceptionBuffer {
    static const unsigned int MAX_ARGS = 20;
    static const unsigned int ARG_BUFF_LEN = 4096;
    static const unsigned int FORMAT_BUFF_LEN = 4096;
    static const unsigned int FILE_BUFF_LEN = 1024;
    static const unsigned int OUT_STRING_LEN = FORMAT_BUFF_LEN * 2;
    unsigned int error_count;
    char file_path[FILE_BUFF_LEN];
    unsigned int line_no;
    unsigned int block_id[3];
    unsigned int thread_id[3];
    char format_string[FORMAT_BUFF_LEN];
    unsigned int format_args_sizes[MAX_ARGS];
    char format_args[ARG_BUFF_LEN];
    unsigned int arg_count;
    unsigned int arg_offset;
};
#ifdef __CUDACC__
class DeviceException {
 public:
    __device__ static DeviceException create(const char *file, const unsigned int line) {
        return {file, line};
    }
    template<typename... Args>
    __device__ void setMessage(const char *format, Args... args) {
        using detail::sm;
        // Only the thread which first reported error gets to output
        if (hasError) {
            // Only output once
            if (sm()->device_exception->format_string[0])
                return;
            // Copy the format string
            unsigned int eos = 0;
            for (eos = 0; eos < DeviceExceptionBuffer::FORMAT_BUFF_LEN; ++eos)
                if (format[eos] == '\0')
                    break;
            memcpy(sm()->device_exception->format_string, format, eos * sizeof(char));
            // Process args
            subformat_recurse(sm()->device_exception, args...);
        }
    }

 private:
    template<typename T>
    __device__ inline void subformat(DeviceExceptionBuffer *buff, T t);
    template<typename T, typename... Args>
    __device__ void subformat_recurse(DeviceExceptionBuffer *buff, const T t, Args... args) {
        // Call subformat with T
        subformat(buff, t);
        // Recurse with the rest of the list
        subformat_recurse(buff, args...);
    }
    __device__ void subformat_recurse(DeviceExceptionBuffer *buff) { }
    __device__ unsigned int strlen(const char *c) {
        unsigned int eos = 0;
        for (eos = 0; eos < DeviceExceptionBuffer::FORMAT_BUFF_LEN; ++eos)
            if (*(c + eos) == '\0' || eos >= DeviceExceptionBuffer::FORMAT_BUFF_LEN)
                break;
        return eos + 1;  // Include the terminating character
    }
    __device__ DeviceException(const char *file, const unsigned int line)
        : hasError(!getErrorCount()) {
        using detail::sm;
        if (hasError) {
            // Copy file location
            const size_t file_len = strlen(file);
            memcpy(sm()->device_exception->file_path, file, file_len);
            // Copy line no
            sm()->device_exception->line_no = line;
            // Copy block/thread indices
            const uint3 bid3 = blockIdx;
            memcpy(sm()->device_exception->block_id, &bid3, sizeof(unsigned int) * 3);
            const uint3 tid3 = threadIdx;
            memcpy(sm()->device_exception->thread_id, &tid3, sizeof(unsigned int) * 3);
        }
    }
    __device__ inline unsigned int getErrorCount();
    const bool hasError;
};
template<typename T>
__device__ inline void DeviceException::subformat(DeviceExceptionBuffer *buff, T t) {
    if (buff->arg_count < DeviceExceptionBuffer::MAX_ARGS) {
        if (buff->arg_offset + sizeof(T) <= DeviceExceptionBuffer::ARG_BUFF_LEN) {
            // Copy arg size
            buff->format_args_sizes[buff->arg_count] = sizeof(T);
            // Copy arg value
            memcpy(buff->format_args + buff->arg_offset, &t, sizeof(T));
            // Update offsets
            ++buff->arg_count;
            buff->arg_offset += sizeof(T);
        }
    }
}
template<>
__device__ inline void DeviceException::subformat(DeviceExceptionBuffer *buff, const char *t) {
    if (buff->arg_count < DeviceExceptionBuffer::MAX_ARGS) {
        const unsigned int string_length = strlen(t);
        if (buff->arg_offset + string_length <= DeviceExceptionBuffer::ARG_BUFF_LEN) {
            // Copy arg size
            buff->format_args_sizes[buff->arg_count] = string_length;
            // Copy arg value
            memcpy(buff->format_args + buff->arg_offset, t, string_length);
            // Update offsets
            ++buff->arg_count;
            buff->arg_offset += string_length;
        }
    }
}
__device__ unsigned int DeviceException::getErrorCount() {
    using detail::sm;
    // Are we the first exception
    return atomicInc(&sm()->device_exception->error_count, UINT_MAX);
}
#endif
#else
#define DTHROW(nop)
#endif  // FLAMEGPU_SEATBELTS=OFF

}  // namespace exception
}  // namespace flamegpu

#endif  // INCLUDE_FLAMEGPU_EXCEPTION_FLAMEGPUDEVICEEXCEPTION_DEVICE_CUH_