A modern C++17 header-only library for CAN (Controller Area Network) and CAN FD communication on Linux systems.
Provides command-line utilities cansend
and candump
.
- Header-only: No separate compilation required
- Type-safe CAN IDs: Separate types for Standard (11-bit) and Extended (29-bit) IDs with compile-time validation
- Dual Protocol Support: Works with both classic CAN and CAN FD frames
- Modern C++: Uses C++17 features for better performance and safety
- Thread Safety: Multiple threading policies for concurrent access patterns
- Zero-Contention Design: Per-thread socket instances for maximum performance
- Lock-Free Operations: High-frequency producer-consumer scenarios
- Builder Pattern: Fluent API for convenient frame construction
- Convenience Methods: Simple
sendFrame()
andsendFDFrame()
methods for quick messaging - High-Performance Batch Operations: Efficient
writeBatch()
andreadBatch()
for bulk operations - Memory Pool: Pre-allocated frame pools for zero-allocation performance
- Hardware Timestamps: Support for hardware timestamping when available
- Flexible Filtering: Easy-to-use filtering API for both single and multiple filters
- Non-blocking I/O: Support for both blocking and non-blocking socket operations
- Comprehensive Testing: Full test suite with virtual CAN interface support
- Enhanced Tools: Modern replacements for canutils (cansend, candump) with advanced features
This library includes enhanced versions of popular CAN utilities with advanced features:
- Backward Compatible: Works with standard canutils syntax
- Advanced Features: Batch sending, templates, timeout control, threading models
- Performance: Rate limiting, continuous sending, statistics
- Example:
cansend can0 123#DEADBEEF --repeat 10 --interval 100
- Advanced Filtering: Regex patterns, data ranges, rate limiting, change detection
- Multiple Formats: JSON, CSV, binary, pretty-print output
- Real-time Analysis: Live statistics, bandwidth monitoring
- Example:
candump can0 --filter 100-200 --only-changes --format json
See tools/README.md for detailed documentation and examples.
- C++17 compatible compiler (GCC 7+, Clang 5+)
- Linux with SocketCAN support
- CMake 3.16+ (for building tests)
- Conan 2.0+ (for dependency management)
This is a header-only library. Simply include cansocket.hpp
in your project:
#include "cansocket.hpp"
# Load the vcan kernel module
sudo modprobe vcan
# Create a virtual CAN interface
sudo ip link add dev vcan0 type vcan
sudo ip link set up vcan0
CAN 2.0A uses standard frames with 11-bit identifiers (0x000 to 0x7FF).
#include "cansocket.hpp"
#include <iostream>
int main() {
try {
// Create a CAN socket for classic CAN
CAN::Socket socket("vcan0", CAN::ProtocolMode::ClassicCAN);
// Create a standard ID (11-bit)
auto id = CAN::StandardID(0x123);
// Prepare data
std::array<uint8_t, 8> data = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08};
// Create and send a CAN 2.0A frame
CAN::Frame frame(id, 8, data);
socket.write(frame);
std::cout << "Sent CAN 2.0A frame with ID: 0x" << std::hex << id.justSFF() << std::endl;
// Read frames
auto result = socket.read();
if (auto* frame_opt = std::get_if<std::optional<std::unique_ptr<CAN::Frame>>>(&result)) {
if (frame_opt->has_value()) {
auto& received_frame = **frame_opt;
std::cout << "Received frame with ID: 0x" << std::hex << received_frame.getCANID() << std::endl;
}
}
} catch (const std::exception& e) {
std::cerr << "Error: " << e.what() << std::endl;
return 1;
}
return 0;
}
CAN 2.0B uses extended frames with 29-bit identifiers (0x00000000 to 0x1FFFFFFF).
#include "cansocket.hpp"
#include <iostream>
int main() {
try {
// Create a CAN socket for classic CAN
CAN::Socket socket("vcan0", CAN::ProtocolMode::ClassicCAN);
// Create an extended ID (29-bit)
auto id = CAN::ExtendedID(0x1ABCDEF);
// Prepare data
std::array<uint8_t, 8> data = {0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x11, 0x22};
// Create and send a CAN 2.0B frame
CAN::Frame frame(id, 8, data);
socket.write(frame);
std::cout << "Sent CAN 2.0B frame with ID: 0x" << std::hex << id.justEFF() << std::endl;
// Set a filter for extended frames
socket.setFilter(id, CAN::EFF_MASK | CAN::EFF_FLAG);
// Read frames with timeout
auto frames = socket.read(std::chrono::milliseconds(1000));
if (auto* can_frames = std::get_if<std::vector<CAN::Frame>>(&frames)) {
for (const auto& frame : *can_frames) {
std::cout << "Received extended frame with ID: 0x" << std::hex << frame.getCANID() << std::endl;
}
}
} catch (const std::exception& e) {
std::cerr << "Error: " << e.what() << std::endl;
return 1;
}
return 0;
}
CAN FD supports larger payloads (up to 64 bytes) and higher bit rates.
#include "cansocket.hpp"
#include <iostream>
#include <vector>
int main() {
try {
// Create a CAN FD socket
CAN::Socket socket("vcan0", CAN::ProtocolMode::CanFD);
// Create an extended ID for CAN FD
auto id = CAN::ExtendedID(0x18DAF110);
// Prepare larger data payload (up to 64 bytes)
std::vector<uint8_t> data;
for (int i = 0; i < 32; i++) {
data.push_back(static_cast<uint8_t>(i));
}
// Create a CAN FD frame with Bit Rate Switch (BRS) enabled
CAN::FD::Frame frame(id, data, CAN::FD::FDF_FLAG | CAN::FD::BRS_FLAG);
// Verify BRS flag is set
std::cout << "BRS enabled: " << (frame.hasBRS() ? "Yes" : "No") << std::endl;
// Send the frame
socket.write(frame);
std::cout << "Sent CAN FD frame with ID: 0x" << std::hex << id.justEFF()
<< ", length: " << static_cast<int>(frame.len) << " bytes" << std::endl;
// Read CAN FD frames
auto result = socket.read();
if (auto* frame_opt = std::get_if<std::optional<std::unique_ptr<CAN::FD::Frame>>>(&result)) {
if (frame_opt->has_value()) {
auto& received_frame = **frame_opt;
std::cout << "Received CAN FD frame with ID: 0x" << std::hex << received_frame.getCANID()
<< ", length: " << static_cast<int>(received_frame.len) << " bytes" << std::endl;
std::cout << "BRS: " << (received_frame.hasBRS() ? "Yes" : "No")
<< ", ESI: " << (received_frame.hasESI() ? "Yes" : "No") << std::endl;
}
}
} catch (const std::exception& e) {
std::cerr << "Error: " << e.what() << std::endl;
return 1;
}
return 0;
}
The library provides a fluent builder API for convenient frame construction:
#include "cansocket.hpp"
int main() {
try {
CAN::Socket socket("vcan0");
// Build a classic CAN frame using the builder pattern
auto frame = CAN::FrameBuilder()
.setID(CAN::StandardID(0x123))
.setDLC(8)
.setData({0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08})
.build();
socket.write(frame);
// Build a CAN FD frame with automatic length detection
auto fd_frame = CAN::FD::FrameBuilder()
.setID(CAN::ExtendedID(0x18DAF110))
.setData({0xAA, 0xBB, 0xCC, 0xDD}) // Length automatically set to 4
.enableBRS() // Enable Bit Rate Switch
.buildAuto(); // Automatically calculate length
socket.write(fd_frame);
} catch (const std::exception& e) {
std::cerr << "Error: " << e.what() << std::endl;
return 1;
}
return 0;
}
Simple one-line methods for quick messaging:
#include "cansocket.hpp"
int main() {
try {
CAN::Socket socket("vcan0");
// Send a classic CAN frame with one line
socket.sendFrame(0x123, {0x01, 0x02, 0x03, 0x04});
// Send to an extended ID
socket.sendFrame(CAN::ExtendedID(0x18DAF110), {0xAA, 0xBB});
// Send a CAN FD frame (requires CAN FD socket)
CAN::Socket fd_socket("vcan0", CAN::ProtocolMode::CanFD);
fd_socket.sendFDFrame(0x456, {0x01, 0x02, 0x03, 0x04, 0x05, 0x06});
// Send with BRS enabled
fd_socket.sendFDFrame(CAN::ExtendedID(0x18DAF110),
{0xAA, 0xBB, 0xCC, 0xDD},
CAN::FD::FDF_FLAG | CAN::FD::BRS_FLAG);
} catch (const std::exception& e) {
std::cerr << "Error: " << e.what() << std::endl;
return 1;
}
return 0;
}
For high-throughput applications, use batch operations to reduce system call overhead:
#include "cansocket.hpp"
#include <vector>
int main() {
try {
CAN::Socket socket("vcan0");
// Prepare multiple frames for batch sending
std::vector<CAN::Frame> frames;
std::array<uint8_t, 8> data = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08};
for (int i = 0; i < 100; ++i) {
frames.emplace_back(0x100 + i, 8, data);
}
// Send all frames in one batch operation
ssize_t bytes_sent = socket.writeBatch(frames);
std::cout << "Sent " << bytes_sent << " bytes in batch" << std::endl;
// Batch read with custom context
std::vector<CAN::Frame> received_frames;
CAN::BatchContext ctx;
ctx.batch_size = 50; // Read up to 50 frames at once
ctx.timeout = std::chrono::milliseconds(100);
size_t frames_received = socket.readBatch(received_frames, ctx);
std::cout << "Received " << frames_received << " frames" << std::endl;
// Process received frames
for (const auto& frame : received_frames) {
std::cout << "Frame ID: 0x" << std::hex << frame.getCANID()
<< ", DLC: " << static_cast<int>(frame.dlc) << std::endl;
}
} catch (const std::exception& e) {
std::cerr << "Error: " << e.what() << std::endl;
return 1;
}
return 0;
}
The library includes a memory pool for high-performance scenarios:
#include "cansocket.hpp"
int main() {
try {
CAN::Socket socket("vcan0");
// Get the global pool manager
auto& pool_manager = CAN::Socket::getPoolManager();
// Check pool statistics
auto [free_frames, free_fd_frames] = pool_manager.getPoolStats();
std::cout << "Available CAN frames: " << free_frames << std::endl;
std::cout << "Available CAN FD frames: " << free_fd_frames << std::endl;
// Allocate frame from pool (zero allocation)
CAN::Frame* frame = pool_manager.allocateFrame();
if (frame) {
// Use the frame...
frame->id = 0x123;
frame->dlc = 8;
// ... set data ...
// Return to pool when done
pool_manager.deallocateFrame(frame);
}
// The library automatically uses the pool for internal operations
// when possible, providing zero-allocation performance
} catch (const std::exception& e) {
std::cerr << "Error: " << e.what() << std::endl;
return 1;
}
return 0;
}
CANSocket is designed for high-performance applications with several optimization features:
- Memory Pools: Pre-allocated frame pools eliminate runtime allocations
- Static Thread-Local Storage: Batch operations use static buffers for zero-copy performance
- Pool Statistics: Monitor pool utilization for optimal sizing
- High Throughput:
writeBatch()
andreadBatch()
minimize system call overhead - Configurable Batching: Customize batch sizes and timeouts for your use case
- Automatic Fallback: Graceful degradation to single-frame operations when needed
- Builder Pattern: Construct frames with minimal overhead
- Convenience Methods: One-line
sendFrame()
calls for simple cases - Type Safety: Compile-time validation prevents runtime errors
CANSocket provides multiple threading models to handle different concurrent access patterns efficiently. The original Socket
class remains unchanged for backward compatibility, while new ThreadSafeSocket<Policy>
templates provide thread-safe wrappers.
For single-threaded applications or when external synchronization is handled by the user:
#include "cansocket.hpp"
// Zero overhead - no thread safety mechanisms
CAN::ThreadSafeSocket<CAN::NoLocking> socket("can0");
// Direct delegation to original Socket class
socket.sendFrame(0x123, {0x01, 0x02, 0x03, 0x04});
For applications with multiple readers and fewer writers (monitoring, logging):
#include "cansocket.hpp"
#include <thread>
#include <vector>
// Multiple readers can access simultaneously
CAN::ThreadSafeSocket<CAN::SharedMutexPolicy> socket("can0");
// Start multiple reader threads
std::vector<std::thread> readers;
for (int i = 0; i < 4; ++i) {
readers.emplace_back([&socket]() {
while (true) {
auto result = socket.read();
// Process frames...
}
});
}
// Single writer thread
std::thread writer([&socket]() {
for (int i = 0; i < 100; ++i) {
socket.sendFrame(0x200 + i, {0x11, 0x22, 0x33, 0x44});
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
});
// Join threads...
For high-frequency parallel operations where each thread gets its own socket:
#include "cansocket.hpp"
#include <thread>
#include <vector>
// Each thread gets its own socket to the same interface
CAN::ThreadSafeSocket<CAN::PerThreadPolicy> socket("can0");
// Multiple threads can write concurrently with zero contention
std::vector<std::thread> workers;
for (int thread_id = 0; thread_id < 8; ++thread_id) {
workers.emplace_back([&socket, thread_id]() {
for (int i = 0; i < 1000; ++i) {
uint32_t frame_id = 0x300 + (thread_id * 100) + i;
socket.sendFrame(frame_id, {
static_cast<uint8_t>(thread_id),
static_cast<uint8_t>(i),
0xAA, 0xBB
});
}
});
}
// Each thread operates on its own socket - no locking needed!
for (auto& worker : workers) {
worker.join();
}
std::cout << "Active thread sockets: " << socket.getThreadCount() << std::endl;
socket.closeAll(); // Clean up all thread sockets
For high-frequency scenarios with multiple producers and a dedicated I/O thread:
#include "cansocket.hpp"
#include <thread>
#include <atomic>
// Uses lock-free queues with dedicated I/O thread
CAN::ThreadSafeSocket<CAN::LockFreePolicy> socket("can0");
std::atomic<bool> stop{false};
// Multiple producer threads
std::vector<std::thread> producers;
for (int producer_id = 0; producer_id < 4; ++producer_id) {
producers.emplace_back([&socket, &stop, producer_id]() {
while (!stop.load()) {
CAN::Frame frame(0x400 + producer_id, 4, {
static_cast<uint8_t>(producer_id),
0x01, 0x02, 0x03
});
// Non-blocking push to lock-free queue
if (!socket.write(frame)) {
// Queue full - handle backpressure
std::this_thread::sleep_for(std::chrono::microseconds(1));
}
}
});
}
// Consumer thread
std::thread consumer([&socket, &stop]() {
while (!stop.load()) {
// Non-blocking read from lock-free buffer
auto frame = socket.read();
if (frame.has_value()) {
std::cout << "Consumed frame ID: 0x" << std::hex
<< frame->getCANID() << std::dec << std::endl;
}
}
});
// Run for 1 second
std::this_thread::sleep_for(std::chrono::seconds(1));
stop.store(true);
// Clean up
for (auto& producer : producers) {
producer.join();
}
consumer.join();
Create thread-safe sockets using convenient factory functions:
#include "cansocket.hpp"
// Create specific socket types
auto no_lock_socket = CAN::createNoLockingSocket("can0");
auto shared_mutex_socket = CAN::createSharedMutexSocket("can0");
auto per_thread_socket = CAN::createPerThreadSocket("can0");
auto lock_free_socket = CAN::createLockFreeSocket("can0");
// Use them with their specific APIs
no_lock_socket->sendFrame(0x501, {0x01});
shared_mutex_socket->sendFrame(0x502, {0x02});
per_thread_socket->sendFrame(0x503, {0x03});
lock_free_socket->write(CAN::Frame(0x504, 1, {0x04, 0, 0, 0, 0, 0, 0, 0}));
Threading Policy | Contention | Latency | Throughput | Memory Usage | Use Case |
---|---|---|---|---|---|
NoLocking | None | Baseline | Baseline | Low | Single-threaded, external sync |
SharedMutex | Low-Medium | Low | Medium-High | Low | Read-heavy monitoring |
PerThread | None | Very Low | Very High | Medium | High-frequency parallel ops |
LockFree | None | Very Low | Very High | Medium | Producer-consumer patterns |
CANSocket includes high-performance lock-free data structures for advanced scenarios:
#include "cansocket.hpp"
// Create a lock-free ring buffer
CAN::SPSCRingBuffer<CAN::Frame, 1024> buffer;
// Producer thread
std::thread producer([&buffer]() {
CAN::Frame frame(0x123, 8, {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08});
while (buffer.push(frame)) {
// Frame pushed successfully
}
// Buffer full
});
// Consumer thread
std::thread consumer([&buffer]() {
CAN::Frame frame;
while (buffer.pop(frame)) {
// Process frame
}
// Buffer empty
});
#include "cansocket.hpp"
// Create a lock-free MPSC queue
CAN::MPSCQueue<CAN::Frame, 2048> queue;
// Multiple producer threads
std::vector<std::thread> producers;
for (int i = 0; i < 4; ++i) {
producers.emplace_back([&queue, i]() {
CAN::Frame frame(0x100 + i, 4, {static_cast<uint8_t>(i), 0x01, 0x02, 0x03});
queue.push(frame);
});
}
// Single consumer thread
std::thread consumer([&queue]() {
CAN::Frame frame;
while (queue.pop(frame)) {
// Process frame from any producer
}
});
- Single-threaded applications: Use
NoLocking
for zero overhead - Monitoring/logging systems: Use
SharedMutex
for multiple readers - High-frequency trading: Use
PerThread
for zero contention - Data acquisition: Use
LockFree
for producer-consumer patterns - Real-time systems: Use
PerThread
orLockFree
for deterministic performance
See examples/thread_safety_demo.cpp
for a comprehensive demonstration of all threading policies with performance comparisons.
// Set multiple filters for different ID ranges
std::vector<std::pair<CAN::StandardID, uint32_t>> filters = {
{CAN::StandardID(0x100), 0x700}, // Accept IDs 0x100-0x1FF
{CAN::StandardID(0x200), 0x7F0}, // Accept IDs 0x200-0x20F
};
socket.setFilters(filters);
// Enable non-blocking mode
socket.setNonBlocking();
// Read without blocking
auto result = socket.read();
if (auto* frame_opt = std::get_if<std::optional<std::unique_ptr<CAN::Frame>>>(&result)) {
if (frame_opt->has_value()) {
// Frame received
auto& frame = **frame_opt;
// Process frame...
} else {
// No frame available
std::cout << "No frame available" << std::endl;
}
}
// Read with hardware timestamp
auto result = socket.readMsg();
if (auto* frame_opt = std::get_if<std::optional<std::unique_ptr<CAN::Frame>>>(&result)) {
if (frame_opt->has_value()) {
auto& frame = **frame_opt;
if (frame.hw_timestamp) {
std::cout << "Hardware timestamp: " << frame.hw_timestamp->tv_sec
<< "." << frame.hw_timestamp->tv_nsec << std::endl;
}
}
}
# Install system dependencies
sudo apt-get install build-essential clang libc++-dev libc++abi-dev ninja-build python3-pip
# Install Conan
pip3 install conan
conan profile detect --force
# Clone the repository
git clone <repository-url>
cd cansocket
# Run the build script (includes tests)
./build.sh
The build script will:
- Install dependencies using Conan
- Configure and build using CMake/Ninja
- Run tests with virtual CAN interface support
# Create Conan profile
cat > .conan-profile-debug << EOF
[settings]
os=Linux
arch=x86_64
compiler=clang
compiler.version=14
compiler.libcxx=libc++
compiler.cppstd=17
build_type=Debug
EOF
# Install dependencies
conan install . --build=missing --profile=.conan-profile-debug --output-folder=build
# Configure and build
cmake -B build -S . -G Ninja -DCMAKE_BUILD_TYPE=Debug
cmake --build build
Important: Tests require virtual CAN interfaces to be set up. Use the provided script:
# Set up virtual CAN interfaces for testing
sudo ./setup_test_env.sh
This script will:
- Load the vcan kernel module
- Create virtual CAN interfaces (vcan0, vcan1)
- Configure them for testing
After setting up the test environment:
# Run all tests
ctest --test-dir build -V
# Run specific tests
./build/tests/cansocket_tests "[can]" # CAN-specific tests
./build/tests/cansocket_tests "[canfd]" # CAN FD-specific tests
CAN::Socket
- Main socket class supporting both CAN and CAN FDCAN::ThreadSafeSocket<Policy>
- Thread-safe socket wrapper with configurable policiesCAN::StandardID
- 11-bit CAN identifier (CAN 2.0A)CAN::ExtendedID
- 29-bit CAN identifier (CAN 2.0B)CAN::Frame
- Classic CAN frame (up to 8 bytes)CAN::FD::Frame
- CAN FD frame (up to 64 bytes)
CAN::NoLocking
- Zero overhead, no thread safetyCAN::SharedMutexPolicy
- Multiple readers, single writer using shared_mutexCAN::PerThreadPolicy
- Each thread gets its own socket instanceCAN::LockFreePolicy
- Lock-free queues with dedicated I/O thread
CAN::SPSCRingBuffer<T, Size>
- Single producer, single consumer ring bufferCAN::MPSCQueue<T, Size>
- Multiple producer, single consumer queue
CAN::FrameBuilder
- Fluent API for building classic CAN framesCAN::FD::FrameBuilder
- Fluent API for building CAN FD frames
CAN::FramePool<T>
- Template class for memory poolsCAN::PoolManager
- Manages frame allocation poolsCAN::BatchContext
- Configuration for batch operations
write(frame)
- Send a single framewriteBatch(frames)
- Send multiple frames efficientlyread()
- Read a single framereadBatch(frames, context)
- Read multiple frames efficientlysendFrame(id, data)
- Convenience method for quick sendingsendFDFrame(id, data, flags)
- Convenience method for CAN FD
createNoLockingSocket(args...)
- Create zero-overhead thread-safe socketcreateSharedMutexSocket(args...)
- Create shared-mutex based socketcreatePerThreadSocket(args...)
- Create per-thread socketcreateLockFreeSocket(args...)
- Create lock-free socketgetThreadCount()
- Get number of active thread sockets (PerThread policy)closeAll()
- Close all thread sockets (PerThread policy)
setID(id)
- Set frame identifiersetDLC(dlc)
/setLength(len)
- Set data lengthsetData(data)
- Set frame dataenableBRS()
/enableESI()
- Enable CAN FD flagsbuild()
- Create frame with specified parametersbuildAuto()
- Create frame with automatic length detection
getPoolManager()
- Get global pool managerallocateFrame()
/allocateFDFrame()
- Get frame from pooldeallocateFrame()
/deallocateFDFrame()
- Return frame to poolgetPoolStats()
- Get pool utilization statistics
CAN::ProtocolMode::ClassicCAN
- Standard CAN 2.0A/2.0B modeCAN::ProtocolMode::CanFD
- CAN FD mode with extended capabilities
CAN::SocketException
- Socket-related errorsCAN::FrameException
- Frame validation errorsCAN::FilterException
- Filter configuration errorsCAN::IDException
- CAN ID validation errors (exceeding bit limits)
This project is licensed under the Apache 2.0 License - see the LICENSE-2.0.txt file for details.
- Fork the repository
- Create a feature branch
- Make your changes
- Run the tests
- Submit a pull request
If you encounter issues with virtual CAN interfaces:
# Check if vcan module is loaded
lsmod | grep vcan
# Load the module if not present
sudo modprobe vcan
# Check interface status
ip link show vcan0
Some operations may require root privileges:
# Run with sudo if needed
sudo ./build.sh
# Or set up permissions for your user
sudo usermod -a -G dialout $USER
For maximum performance:
# Increase socket buffer sizes (if needed)
echo 'net.core.rmem_max = 16777216' | sudo tee -a /etc/sysctl.conf
echo 'net.core.wmem_max = 16777216' | sudo tee -a /etc/sysctl.conf
sudo sysctl -p
# For high-frequency applications, consider:
# - Using batch operations for bulk transfers
# - Pre-sizing memory pools based on your traffic patterns
# - Setting appropriate BatchContext parameters
# - Using non-blocking I/O with proper error handling
# - Choosing the right threading policy for your use case
// Example: Optimize for high-frequency trading application
CAN::BatchContext high_perf_ctx;
high_perf_ctx.batch_size = 1000; // Large batches
high_perf_ctx.timeout = std::chrono::microseconds(10); // Low latency
high_perf_ctx.use_zero_copy = true; // Enable zero-copy optimization
socket.readBatch(frames, high_perf_ctx);
For multi-threaded applications:
// High-frequency parallel operations - use PerThread policy
CAN::ThreadSafeSocket<CAN::PerThreadPolicy> socket("can0");
// Each thread gets its own socket - zero contention
// Producer-consumer pattern - use LockFree policy
CAN::ThreadSafeSocket<CAN::LockFreePolicy> socket("can0");
// Lock-free queues with dedicated I/O thread
// Monitor application performance
auto per_thread_socket = CAN::createPerThreadSocket("can0");
std::cout << "Active threads: " << per_thread_socket->getThreadCount() << std::endl;
examples/thread_safety_demo.cpp
- Comprehensive threading demonstrationexamples/basic_sender.cpp
- Simple CAN frame sendingexamples/basic_receiver.cpp
- Simple CAN frame receivingexamples/canfd_example.cpp
- CAN FD functionalityexamples/performance_test.cpp
- Performance testing and benchmarks