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}