Skip to main content

ri/c/
device.rs

1//! Copyright © 2025-2026 Wenze Wei. All Rights Reserved.
2//!
3//! This file is part of Ri.
4//! The Ri project belongs to the Dunimd Team.
5//!
6//! Licensed under the Apache License, Version 2.0 (the "License");
7//! You may not use this file except in compliance with the License.
8//! You may obtain a copy of the License at
9//!
10//!     http://www.apache.org/licenses/LICENSE-2.0
11//!
12//! Unless required by applicable law or agreed to in writing, software
13//! distributed under the License is distributed on an "AS IS" BASIS,
14//! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15//! See the License for the specific language governing permissions and
16//! limitations under the License.
17
18//! # Device Module C API
19//!
20//! This module provides C language bindings for Ri's device management subsystem. The device
21//! module delivers comprehensive device abstraction and control capabilities for managing various
22//! types of computational resources including CPU, GPU, memory, storage, network interfaces,
23//! sensors, and actuators. This C API enables C/C++ applications to leverage Ri's device
24//! orchestration features for resource management, scheduling, and hardware abstraction.
25//!
26//! ## Module Architecture
27//!
28//! The device management module comprises four primary components that together provide complete
29//! device lifecycle management:
30//!
31//! - **RiDevice**: Fundamental device abstraction representing any computational resource.
32//!   Each device instance encapsulates identity, type, capabilities, and state information.
33//!   Devices can be queried for properties, monitored for status, and controlled through
34//!   standardized interfaces regardless of underlying hardware implementation.
35//!
36//! - **RiDeviceController**: Device control interface providing operational methods for
37//!   device manipulation. The controller handles device initialization, configuration,
38//!   activation, deactivation, and error recovery. Controllers implement device-specific
39//!   logic while presenting a uniform control interface to the rest of the system.
40//!
41//! - **RiDeviceScheduler**: Resource scheduling component for coordinating device usage
42//!   across multiple requestors. The scheduler implements allocation policies, fair queuing,
43//!   and priority-based scheduling to optimize device utilization while preventing resource
44//!   contention. Supports both synchronous and asynchronous scheduling modes.
45//!
46//! - **RiDeviceType**: Enumeration defining supported device categories. Each device type
47//!   indicates the general class of hardware or resource being represented. The type system
48//!   enables type-safe device operations and automatic dispatch to appropriate handlers.
49//!
50//! ## Device Types
51//!
52//! The device module supports the following device categories:
53//!
54//! - **CPU**: Central processing unit resources. CPU devices provide processing capability
55//!   for computational tasks. Scheduling considerations include core count, clock frequency,
56//!   cache hierarchy, and instruction set capabilities.
57//!
58//! - **GPU**: Graphics processing unit resources. GPU devices are specialized for
59//!   parallel computation, machine learning inference, and graphics rendering. Support
60//!   includes CUDA, OpenCL, and Vulkan compute capabilities.
61//!
62//! - **Memory**: Random access memory resources. Memory devices represent available RAM
63//!   that can be allocated for data processing. Considerations include capacity, latency,
64//!   bandwidth, and memory hierarchy (cache, main memory, swap).
65//!
66//! - **Storage**: Persistent storage resources. Storage devices provide durable data
67//!   retention including SSDs, HDDs, and network storage. Performance characteristics
68//!   include IOPS, throughput, latency, and durability ratings.
69//!
70//! - **Network**: Network interface resources. Network devices enable communication
71//!   with external systems. Properties include bandwidth, latency, protocol support,
72//!   and connection state.
73//!
74//! - **Sensor**: Data acquisition devices. Sensors collect environmental or system
75//!   data including temperature, pressure, location, and system metrics. Support
76//!   includes polling and event-driven data collection.
77//!
78//! - **Actuator**: Action execution devices. Actuators perform physical or logical
79//!   actions based on commands. Examples include motor controllers, relay switches,
80//!   and service invocation endpoints.
81//!
82//! - **Custom**: User-defined device types. Custom devices allow application-specific
83//!   resource types beyond the standard categories. Custom types can implement any
84//!   device-like behavior required by the application.
85//!
86//! ## Device Lifecycle
87//!
88//! Devices transition through well-defined lifecycle states:
89//!
90//! 1. **DISCOVERED**: Device detected but not yet configured or available for use
91//! 2. **CONFIGURED**: Device has been initialized with required settings
92//! 3. **AVAILABLE**: Device ready for allocation and operational use
93//! 4. **ALLOCATED**: Device assigned to a specific consumer or task
94//! 5. **BUSY**: Device actively executing operations
95//! 6. **ERROR**: Device encountered an error condition
96//! 7. **UNAVAILABLE**: Device temporarily or permanently unavailable
97//! 8. **RELEASED**: Device resources freed after allocation
98//!
99//! ## Scheduling Policies
100//!
101//! The device scheduler implements multiple allocation strategies:
102//!
103//! - **FIFO (First In, First Out)**: Requests processed in arrival order. Simple
104//!   and predictable, suitable for uniform priority workloads.
105//!
106//! - **Priority-Based**: Requests assigned priorities affecting scheduling order.
107//!   Higher priority requests jump ahead of lower priority ones. Supports multiple
108//!   priority levels with configurable behavior at each level.
109//!
110//! - **Fair-Sharing**: Resources distributed equitably across requestors. Prevents
111//!   any single consumer from monopolizing device capacity. Implements weighted fair
112//!   queuing for proportional allocation.
113//!
114//! - **Deadline-Driven**: Requests scheduled to meet deadline requirements.
115//!   Suitable for real-time workloads with timing constraints. Requires deadline
116//!   specification at request time.
117//!
118//! - **Load-Balancing**: Requests distributed across multiple identical devices.
119//!   Optimizes resource utilization and maximizes throughput for parallelizable work.
120//!
121//! ## Device Capabilities
122//!
123//! Each device advertises its capabilities through a standardized interface:
124//!
125//! - **Properties**: Static characteristics including manufacturer, model, serial
126//!   number, firmware version, and unique identifiers.
127//!
128//! - **Metrics**: Dynamic measurements including utilization, temperature, error
129//!   rates, and operational statistics. Metrics are sampled periodically or on demand.
130//!
131//! - **Capabilities**: Supported operations and modes including read/write access,
132//!   concurrent operation support, and specialized features.
133//!
134//! - **Constraints**: Operational limits including maximum throughput, memory
135//!   capacity, power limits, and environmental requirements.
136//!
137//! ## Memory Management
138//!
139//! All C API objects use opaque pointers with manual memory management:
140//!
141//! - Constructor functions allocate new instances on the heap
142//! - Destructor functions must be called to release memory
143//! - Device instances must be properly released after allocation
144//! - Null pointer checks are required before all operations
145//!
146//! ## Thread Safety
147//!
148//! The underlying implementations are thread-safe:
149//!
150//! - Device controllers handle concurrent access with internal synchronization
151//! - Scheduler operations are thread-safe for multi-threaded request submission
152//! - Device state queries can be performed concurrently
153//! - Device control operations may require exclusive access
154//!
155//! ## Performance Characteristics
156//!
157//! Device operations have the following performance profiles:
158//!
159//! - Device discovery: O(n) where n is number of potential devices
160//! - Device allocation: O(1) average case, O(log n) for complex policies
161//! - Metric collection: O(1) for cached metrics, O(n) for hardware sampling
162//! - Scheduling decisions: O(1) for FIFO, O(log p) for priority (p = priority levels)
163//!
164//! ## Usage Example
165//!
166//! ```c
167//! // Create a CPU device
168//! RiDevice* cpu = ri_device_new("worker-node-1", DEVICE_TYPE_CPU);
169//!
170//! // Create device controller
171//! RiDeviceController* controller = ri_device_controller_new(cpu);
172//!
173//! // Configure device
174//! ri_device_controller_configure(controller, "max_frequency", "3000000000");
175//!
176//! // Initialize device for use
177//! int result = ri_device_controller_initialize(controller);
178//!
179//! if (result == 0) {
180//!     // Device ready, create scheduler
181//!     RiDeviceScheduler* scheduler = ri_device_scheduler_new();
182//!
183//!     // Register device with scheduler
184//!     ri_device_scheduler_register(scheduler, cpu);
185//!
186//!     // Allocate device for task
187//!     RiDevice* allocated = ri_device_scheduler_allocate(scheduler,
188//!         DEVICE_TYPE_CPU, PRIORITY_NORMAL);
189//!
190//!     // Use device...
191//!
192//!     // Release when done
193//!     ri_device_scheduler_release(scheduler, allocated);
194//!     ri_device_scheduler_free(scheduler);
195//! }
196//!
197//! // Cleanup
198//! ri_device_controller_free(controller);
199//! ri_device_free(cpu);
200//! ```
201//!
202//! ## Dependencies
203//!
204//! This module depends on the following Ri components:
205//!
206//! - `crate::device`: Rust device module implementation
207//! - `crate::prelude`: Common types and traits
208//!
209//! ## Feature Flags
210//!
211//! The device module is always enabled as it provides fundamental infrastructure
212//! for resource management in Ri applications.
213
214use crate::device::{RiDevice, RiDeviceController, RiDeviceScheduler, RiDeviceType};
215use std::ffi::c_char;
216use std::sync::Arc;
217
218c_wrapper!(CRiDevice, RiDevice);
219
220c_wrapper!(CRiDeviceController, RiDeviceController);
221
222c_wrapper!(CRiDeviceScheduler, RiDeviceScheduler);
223
224/// Device type enumeration values.
225///
226/// These integer constants identify the category of device being created or managed.
227/// The values map to the RiDeviceType Rust enumeration.
228///
229/// # Type Mapping
230///
231/// The following mapping applies between C constants and device types:
232///
233/// - 0: CPU - Central processing unit
234/// - 1: GPU - Graphics processing unit
235/// - 2: Memory - RAM resources
236/// - 3: Storage - Persistent storage devices
237/// - 4: Network - Network interfaces
238/// - 5: Sensor - Data acquisition devices
239/// - 6: Actuator - Action execution devices
240/// - 7+: Custom - Application-specific types
241///
242/// # Usage
243///
244/// When creating devices or filtering by type, pass the appropriate constant:
245///
246/// ```c
247/// RiDevice* cpu = ri_device_new("compute-0", 0);  // CPU device
248/// RiDevice* gpu = ri_device_new("render-0", 1);  // GPU device
249/// ```
250///
251/// # Extensibility
252///
253/// Applications can define custom device types beyond the standard categories
254/// by using values greater than or equal to 7. Custom types should be
255/// documented and handled appropriately by application code.
256
257/// Creates a new RiDevice instance with specified name and device type.
258///
259/// Allocates a new device object with the given identification and classification.
260/// The device is created in DISCOVERED state and requires configuration and
261/// initialization before use.
262///
263/// # Parameters
264///
265/// - `name`: Pointer to null-terminated C string containing the device name.
266///   Must not be NULL. The name should be unique within the device namespace.
267///   Names follow naming conventions: lowercase with hyphens for standard devices.
268/// - `device_type`: Integer constant indicating the device category.
269///   Use predefined constants (0-6) for standard types or custom values for
270///   application-specific devices.
271///
272/// # Returns
273///
274/// Pointer to newly allocated RiDevice on success, or NULL if:
275/// - `name` parameter is NULL
276/// - Memory allocation fails
277/// - Name contains invalid UTF-8 sequences
278///
279/// # Initial State
280///
281/// A newly created device:
282///
283/// - Has DISCOVERED lifecycle state
284/// - Has no assigned controller (controller must be created separately)
285/// - Has no configured settings (defaults applied)
286/// - Is not registered with any scheduler
287///
288/// # Example
289///
290/// ```c
291/// // Create a GPU device
292/// RiDevice* gpu = ri_device_new("training-gpu-0", DEVICE_TYPE_GPU);
293/// if (gpu == NULL) {
294///     fprintf(stderr, "Failed to create device\n");
295///     return ERROR_DEVICE_CREATION;
296/// }
297///
298/// // Configure and initialize...
299///
300/// // Cleanup when done
301/// ri_device_free(gpu);
302/// ```
303///
304/// # Naming Conventions
305///
306/// Device names should follow these guidelines:
307///
308/// - Descriptive: Indicate device purpose or location
309/// - Unique: No two devices share the same name
310/// - Consistent: Follow naming pattern for device type
311/// - Persistent: Names remain stable across restarts
312#[no_mangle]
313pub extern "C" fn ri_device_new(name: *const c_char, device_type: i32) -> *mut CRiDevice {
314    if name.is_null() {
315        return std::ptr::null_mut();
316    }
317    unsafe {
318        let name_str = match std::ffi::CStr::from_ptr(name).to_str() {
319            Ok(s) => s,
320            Err(_) => return std::ptr::null_mut(),
321        };
322        let dtype = match device_type {
323            0 => RiDeviceType::CPU,
324            1 => RiDeviceType::GPU,
325            2 => RiDeviceType::Memory,
326            3 => RiDeviceType::Storage,
327            4 => RiDeviceType::Network,
328            5 => RiDeviceType::Sensor,
329            6 => RiDeviceType::Actuator,
330            _ => RiDeviceType::Custom,
331        };
332        let device = RiDevice::new(name_str.to_string(), dtype);
333        Box::into_raw(Box::new(CRiDevice::new(device)))
334    }
335}
336
337c_destructor!(ri_device_free, CRiDevice);
338
339// RiDevice getters
340c_string_getter!(
341    ri_device_get_name,
342    CRiDevice,
343    |inner: &RiDevice| inner.name().to_string()
344);
345
346#[no_mangle]
347pub extern "C" fn ri_device_get_id(device: *mut CRiDevice) -> *mut std::ffi::c_char {
348    if device.is_null() {
349        return std::ptr::null_mut();
350    }
351    unsafe {
352        match std::ffi::CString::new((*device).inner.id().to_string()) {
353            Ok(c_str) => c_str.into_raw(),
354            Err(_) => std::ptr::null_mut(),
355        }
356    }
357}
358
359#[no_mangle]
360pub extern "C" fn ri_device_get_type(device: *mut CRiDevice) -> std::ffi::c_int {
361    if device.is_null() {
362        return -1;
363    }
364    unsafe {
365        match (*device).inner.device_type() {
366            RiDeviceType::CPU => 0,
367            RiDeviceType::GPU => 1,
368            RiDeviceType::Memory => 2,
369            RiDeviceType::Storage => 3,
370            RiDeviceType::Network => 4,
371            RiDeviceType::Sensor => 5,
372            RiDeviceType::Actuator => 6,
373            RiDeviceType::Custom => 7,
374        }
375    }
376}
377
378#[no_mangle]
379pub extern "C" fn ri_device_get_status(device: *mut CRiDevice) -> std::ffi::c_int {
380    if device.is_null() {
381        return -1;
382    }
383    unsafe {
384        match (*device).inner.status() {
385            crate::device::RiDeviceStatus::Unknown => 0,
386            crate::device::RiDeviceStatus::Available => 1,
387            crate::device::RiDeviceStatus::Busy => 2,
388            crate::device::RiDeviceStatus::Error => 3,
389            crate::device::RiDeviceStatus::Offline => 4,
390            crate::device::RiDeviceStatus::Maintenance => 5,
391            crate::device::RiDeviceStatus::Degraded => 6,
392            crate::device::RiDeviceStatus::Allocated => 7,
393        }
394    }
395}
396
397// RiDeviceController C bindings
398#[no_mangle]
399pub extern "C" fn ri_device_controller_new() -> *mut CRiDeviceController {
400    Box::into_raw(Box::new(CRiDeviceController::new(RiDeviceController::new())))
401}
402c_destructor!(ri_device_controller_free, CRiDeviceController);
403
404#[no_mangle]
405pub extern "C" fn ri_device_controller_add_device(
406    controller: *mut CRiDeviceController,
407    device: *mut CRiDevice,
408    location: *const std::ffi::c_char,
409) -> std::ffi::c_int {
410    if controller.is_null() || device.is_null() || location.is_null() {
411        return -1;
412    }
413    let rt = match tokio::runtime::Runtime::new() {
414        Ok(rt) => rt,
415        Err(_) => return -2,
416    };
417    unsafe {
418        let device = (*device).inner.clone();
419        let location_str = match std::ffi::CStr::from_ptr(location).to_str() {
420            Ok(s) => s.to_string(),
421            Err(_) => return -3,
422        };
423        rt.block_on(async {
424            (*controller).inner.add_device(device, location_str).await
425        }).map(|_| 0).unwrap_or(-4)
426    }
427}
428
429#[no_mangle]
430pub extern "C" fn ri_device_controller_remove_device(
431    controller: *mut CRiDeviceController,
432    device_id: *const std::ffi::c_char,
433) -> std::ffi::c_int {
434    if controller.is_null() || device_id.is_null() {
435        return -1;
436    }
437    let rt = match tokio::runtime::Runtime::new() {
438        Ok(rt) => rt,
439        Err(_) => return -2,
440    };
441    unsafe {
442        let device_id_str = match std::ffi::CStr::from_ptr(device_id).to_str() {
443            Ok(s) => s,
444            Err(_) => return -3,
445        };
446        rt.block_on(async {
447            let _ = (*controller).inner.remove_device(device_id_str).await;
448        });
449    }
450    0
451}
452
453#[no_mangle]
454pub extern "C" fn ri_device_controller_get_device(
455    controller: *mut CRiDeviceController,
456    device_id: *const std::ffi::c_char,
457) -> *mut CRiDevice {
458    if controller.is_null() || device_id.is_null() {
459        return std::ptr::null_mut();
460    }
461    let rt = match tokio::runtime::Runtime::new() {
462        Ok(rt) => rt,
463        Err(_) => return std::ptr::null_mut(),
464    };
465    unsafe {
466        let device_id_str = match std::ffi::CStr::from_ptr(device_id).to_str() {
467            Ok(s) => s,
468            Err(_) => return std::ptr::null_mut(),
469        };
470        match rt.block_on(async { (*controller).inner.get_device(device_id_str).await }) {
471            Some(device) => Box::into_raw(Box::new(CRiDevice::new(device))),
472            None => std::ptr::null_mut(),
473        }
474    }
475}
476
477#[no_mangle]
478pub extern "C" fn ri_device_controller_get_device_count(controller: *mut CRiDeviceController) -> usize {
479    if controller.is_null() {
480        return 0;
481    }
482    let rt = match tokio::runtime::Runtime::new() {
483        Ok(rt) => rt,
484        Err(_) => return 0,
485    };
486    unsafe {
487        rt.block_on(async { (*controller).inner.get_all_devices().len() })
488    }
489}
490
491#[no_mangle]
492pub extern "C" fn ri_device_controller_discover(
493    controller: *mut CRiDeviceController,
494    out_devices: *mut *mut CRiDevice,
495    out_count: *mut usize,
496) -> std::ffi::c_int {
497    if controller.is_null() || out_devices.is_null() || out_count.is_null() {
498        return -1;
499    }
500    let rt = match tokio::runtime::Runtime::new() {
501        Ok(rt) => rt,
502        Err(_) => return -2,
503    };
504    unsafe {
505        match rt.block_on(async { (*controller).inner.discover_devices().await }) {
506            Ok(result) => {
507                let count = result.discovered_devices.len();
508                *out_count = count;
509                if count == 0 {
510                    *out_devices = std::ptr::null_mut();
511                    return 0;
512                }
513                let devices: Vec<CRiDevice> = result.discovered_devices.into_iter().map(CRiDevice::new).collect();
514                let ptr = Box::into_raw(Box::new(devices));
515                *out_devices = ptr as *mut CRiDevice;
516                0
517            }
518            Err(_) => -3,
519        }
520    }
521}
522
523// RiDeviceScheduler C bindings
524#[no_mangle]
525pub extern "C" fn ri_device_scheduler_new() -> *mut CRiDeviceScheduler {
526    let pool_manager = Arc::new(tokio::sync::RwLock::new(crate::device::RiResourcePoolManager::new()));
527    Box::into_raw(Box::new(CRiDeviceScheduler::new(RiDeviceScheduler::new(pool_manager))))
528}
529c_destructor!(ri_device_scheduler_free, CRiDeviceScheduler);
530
531#[no_mangle]
532pub extern "C" fn ri_device_scheduler_allocate(
533    scheduler: *mut CRiDeviceScheduler,
534    device_type: std::ffi::c_int,
535    priority: u32,
536    timeout_secs: u64,
537) -> *mut CRiDevice {
538    if scheduler.is_null() {
539        return std::ptr::null_mut();
540    }
541    let rt = match tokio::runtime::Runtime::new() {
542        Ok(rt) => rt,
543        Err(_) => return std::ptr::null_mut(),
544    };
545    unsafe {
546        let dtype = match device_type {
547            0 => RiDeviceType::CPU,
548            1 => RiDeviceType::GPU,
549            2 => RiDeviceType::Memory,
550            3 => RiDeviceType::Storage,
551            4 => RiDeviceType::Network,
552            5 => RiDeviceType::Sensor,
553            6 => RiDeviceType::Actuator,
554            7 => RiDeviceType::Custom,
555            _ => RiDeviceType::Custom,
556        };
557        let request = crate::device::scheduler::RiAllocationRequest {
558            device_type: dtype,
559            capabilities: crate::device::RiDeviceCapabilities::default(),
560            priority,
561            timeout_secs,
562            sla_class: None,
563            resource_weights: None,
564            affinity: None,
565            anti_affinity: None,
566        };
567        match rt.block_on(async { (*scheduler).inner.select_device(&request).await }) {
568            Some(device) => Box::into_raw(Box::new(CRiDevice::new((*device).clone()))),
569            None => std::ptr::null_mut(),
570        }
571    }
572}
573
574#[no_mangle]
575pub extern "C" fn ri_device_scheduler_release(
576    scheduler: *mut CRiDeviceScheduler,
577    device_id: *const std::ffi::c_char,
578) -> std::ffi::c_int {
579    if scheduler.is_null() || device_id.is_null() {
580        return -1;
581    }
582    let rt = match tokio::runtime::Runtime::new() {
583        Ok(rt) => rt,
584        Err(_) => return -2,
585    };
586    unsafe {
587        let device_id_str = match std::ffi::CStr::from_ptr(device_id).to_str() {
588            Ok(s) => s,
589            Err(_) => return -3,
590        };
591        match rt.block_on(async { (*scheduler).inner.release_device(device_id_str).await }) {
592            Ok(_) => 0,
593            Err(_) => -4,
594        }
595    }
596}
597
598// RiResourcePool C bindings
599c_wrapper!(CRiResourcePool, crate::device::RiResourcePool);
600
601#[no_mangle]
602pub extern "C" fn ri_resource_pool_new(name: *const std::ffi::c_char, capacity: usize) -> *mut CRiResourcePool {
603    if name.is_null() {
604        return std::ptr::null_mut();
605    }
606    unsafe {
607        let name_str = match std::ffi::CStr::from_ptr(name).to_str() {
608            Ok(s) => s.to_string(),
609            Err(_) => return std::ptr::null_mut(),
610        };
611        let config = crate::device::RiResourcePoolConfig {
612            name: name_str,
613            device_type: RiDeviceType::Custom,
614            max_concurrent_allocations: capacity,
615            allocation_timeout_secs: 30,
616            health_check_interval_secs: 60,
617        };
618        Box::into_raw(Box::new(CRiResourcePool::new(crate::device::RiResourcePool::new(config))))
619    }
620}
621c_destructor!(ri_resource_pool_free, CRiResourcePool);
622
623#[no_mangle]
624pub extern "C" fn ri_resource_pool_get_capacity(pool: *mut CRiResourcePool) -> usize {
625    if pool.is_null() {
626        return 0;
627    }
628    unsafe { (*pool).inner.get_status().total_capacity }
629}
630
631#[no_mangle]
632pub extern "C" fn ri_resource_pool_get_available(pool: *mut CRiResourcePool) -> usize {
633    if pool.is_null() {
634        return 0;
635    }
636    unsafe { (*pool).inner.get_status().available_capacity }
637}
638
639#[no_mangle]
640pub extern "C" fn ri_resource_pool_get_utilization(pool: *mut CRiResourcePool) -> f64 {
641    if pool.is_null() {
642        return 0.0;
643    }
644    unsafe { (*pool).inner.get_status().utilization_rate }
645}