use crate::error::{LoglyError, Result};
use parking_lot::RwLock;
use std::sync::Arc;
pub struct GpuLogger {
#[cfg(feature = "gpu")]
ctx_stream: Option<Box<dyn std::any::Any + Send + Sync>>,
enabled: Arc<RwLock<bool>>,
#[allow(dead_code)]
buffer_size: usize,
}
impl GpuLogger {
pub fn new(buffer_size: usize) -> Result<Self> {
#[cfg(feature = "gpu")]
{
let ctx_stream = std::panic::catch_unwind(|| cudarc::driver::CudaContext::new(0))
.ok()
.and_then(|r| r.ok())
.map(|ctx| {
let stream = ctx.default_stream();
Box::new((ctx, stream)) as Box<dyn std::any::Any + Send + Sync>
});
let is_available = ctx_stream.is_some();
Ok(Self {
ctx_stream,
enabled: Arc::new(RwLock::new(is_available)),
buffer_size,
})
}
#[cfg(not(feature = "gpu"))]
{
Ok(Self {
enabled: Arc::new(RwLock::new(false)),
buffer_size,
})
}
}
pub fn is_available(&self) -> bool {
#[cfg(feature = "gpu")]
{
self.ctx_stream.is_some()
}
#[cfg(not(feature = "gpu"))]
{
false
}
}
pub fn is_enabled(&self) -> bool {
*self.enabled.read()
}
pub fn enable(&self) -> Result<()> {
#[cfg(feature = "gpu")]
{
if self.ctx_stream.is_none() {
return Err(LoglyError::GpuError(
"CUDA device not available".to_string(),
));
}
*self.enabled.write() = true;
Ok(())
}
#[cfg(not(feature = "gpu"))]
{
Err(LoglyError::GpuError(
"GPU feature not enabled. Compile with --features gpu".to_string(),
))
}
}
pub fn disable(&self) {
*self.enabled.write() = false;
}
#[cfg(feature = "gpu")]
pub fn write_to_gpu(&self, data: &[u8]) -> Result<()> {
if !self.is_enabled() {
return Ok(());
}
if let Some(ref ctx_stream_box) = self.ctx_stream {
type CtxStream = (
Arc<cudarc::driver::CudaContext>,
Arc<cudarc::driver::CudaStream>,
);
if let Some((_ctx, stream)) = ctx_stream_box.downcast_ref::<CtxStream>() {
match stream.memcpy_stod(data) {
Ok(_buffer) => Ok(()),
Err(e) => Err(LoglyError::GpuError(format!(
"Failed to copy to GPU: {:?}",
e
))),
}
} else {
Err(LoglyError::GpuError(
"Invalid CUDA context type".to_string(),
))
}
} else {
Err(LoglyError::GpuError(
"CUDA device not available".to_string(),
))
}
}
#[cfg(not(feature = "gpu"))]
pub fn write_to_gpu(&self, _data: &[u8]) -> Result<()> {
Err(LoglyError::GpuError("GPU feature not enabled".to_string()))
}
pub fn get_info(&self) -> String {
#[cfg(feature = "gpu")]
{
if self.ctx_stream.is_some() {
format!(
"GPU Logging: Enabled\nDevice: CUDA Device 0\nBuffer Size: {} bytes\nStatus: {}",
self.buffer_size,
if self.is_enabled() {
"Active"
} else {
"Inactive"
}
)
} else {
"GPU Logging: Not Available (CUDA device initialization failed)".to_string()
}
}
#[cfg(not(feature = "gpu"))]
{
"GPU Logging: Not Available (compile with --features gpu)".to_string()
}
}
}
impl Default for GpuLogger {
fn default() -> Self {
Self::new(1024 * 1024).unwrap_or_else(|_| {
#[cfg(feature = "gpu")]
{
Self {
ctx_stream: None,
enabled: Arc::new(RwLock::new(false)),
buffer_size: 1024 * 1024,
}
}
#[cfg(not(feature = "gpu"))]
{
Self {
enabled: Arc::new(RwLock::new(false)),
buffer_size: 1024 * 1024,
}
}
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_gpu_logger_creation() {
let gpu = GpuLogger::new(1024 * 1024);
assert!(gpu.is_ok());
}
#[test]
fn test_gpu_logger_default() {
let gpu = GpuLogger::default();
assert_eq!(gpu.buffer_size, 1024 * 1024);
}
#[test]
fn test_gpu_availability() {
if let Ok(gpu) = GpuLogger::new(1024) {
let _ = gpu.is_available();
}
}
#[test]
fn test_gpu_enable_disable() {
if let Ok(gpu) = GpuLogger::new(1024) {
gpu.disable();
assert!(!gpu.is_enabled());
}
}
#[test]
fn test_gpu_info() {
if let Ok(gpu) = GpuLogger::new(1024) {
let info = gpu.get_info();
assert!(!info.is_empty());
assert!(info.contains("GPU Logging"));
}
}
#[test]
fn test_gpu_write_when_disabled() {
if let Ok(gpu) = GpuLogger::new(1024) {
gpu.disable();
let data = b"test log data";
let result = gpu.write_to_gpu(data);
#[cfg(feature = "gpu")]
assert!(result.is_ok());
#[cfg(not(feature = "gpu"))]
assert!(result.is_err());
}
}
#[cfg(not(feature = "gpu"))]
#[test]
fn test_gpu_not_available_without_feature() {
let gpu = GpuLogger::new(1024).unwrap();
assert!(!gpu.is_available());
assert!(gpu.enable().is_err());
}
#[cfg(feature = "gpu")]
#[test]
fn test_gpu_write_to_gpu() {
if let Ok(gpu) = GpuLogger::new(1024) {
if gpu.is_available() {
let _ = gpu.enable();
let data = b"test log data";
let _ = gpu.write_to_gpu(data);
}
}
}
}