Skip to main content

error_handling/
error_handling.rs

1// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: Copyright (c) 2025-2026 John Ray <996351336@qq.com>
3
4//! Error handling example
5//!
6//! Demonstrates error handling mechanisms of the partitioned garbage collection system, including:
7//! - Out of memory and partition full errors
8//! - Safe release validation
9//! - Partition management errors
10//! - Invalid reference handling
11
12use gc_lite::{GcError, GcHeap, GcRef, GcResult, GcTrace, GcTraceCtx, gc_type_register};
13
14fn main() -> GcResult<()> {
15    println!("=== Error handling example of partitioned garbage collection system ===");
16
17    // Demonstrate out of memory errors
18    println!("\n=== Out of memory error handling ===");
19    demonstrate_out_of_memory()?;
20
21    // Demonstrate partition management errors
22    println!("\n=== Partition management errors ===");
23    demonstrate_partition_management_errors()?;
24
25    // Demonstrate GC threshold API errors
26    println!("\n=== GC threshold API errors ===");
27    demonstrate_gc_threshold_errors()?;
28
29    println!("\nAll error handling demonstrations completed!");
30    Ok(())
31}
32
33/// Demonstrate out of memory error handling
34fn demonstrate_out_of_memory() -> GcResult<()> {
35    println!("1. Create limited memory partition...");
36
37    let mut context = GcHeap::new(&GC_TYPE_REGISTRY);
38    context.set_memory_limit(2048); // 2KB global limit
39    let partition_id = context.create_partition(64 * 1024, 16 * 1024);
40
41    // Allocate first large object (1KB + header)
42    println!("2. Allocate first large object...");
43    let gc1: GcRef<LargeData> =
44        match unsafe { context.alloc_raw(partition_id, LargeData { data: [0; 1024] }) } {
45            Ok(gc_ref) => {
46                println!("  ✓ Successfully allocated first object (1KB)");
47                gc_ref
48            }
49            Err((error, _)) => {
50                println!("  ✗ First object allocation failed: {:?}", error);
51                return Ok(());
52            }
53        };
54
55    // Allocate second large object (1KB + header) - should exceed 2KB limit
56    println!("3. Try to allocate second large object...");
57    match unsafe { context.alloc_raw(partition_id, LargeData { data: [0; 1024] }) } {
58        Err((GcError::PartitionFull, _)) => {
59            println!("  ✓ Correctly detected partition full error");
60        }
61        Ok(_) => {
62            println!("  ✗ Expected partition full error, but allocation succeeded");
63            return Ok(());
64        }
65        Err((other_error, _)) => {
66            println!(
67                "  ✗ Expected partition full error, but got: {:?}",
68                other_error
69            );
70            return Ok(());
71        }
72    }
73
74    // Clean up - through garbage collection instead of manual release
75    println!("  ✓ Automatic cleanup through GC");
76    context.garbage_collect(partition_id, GcHeap::DUMMY_DISPOSE_CALLBACK);
77
78    Ok(())
79}
80
81/// Demonstrate partition management errors
82fn demonstrate_partition_management_errors() -> GcResult<()> {
83    println!("1. Test non-existent partition operations...");
84
85    let mut context = GcHeap::new(&GC_TYPE_REGISTRY);
86    let invalid_partition = gc_lite::GcPartitionId(9999); // Non-existent partition
87
88    // Test allocating objects in non-existent partition
89    match unsafe {
90        context.alloc_raw(
91            invalid_partition,
92            TestData {
93                value: 42,
94                name: "test".to_string(),
95            },
96        )
97    } {
98        Err((GcError::PartitionNotFound, _)) => {
99            println!("  ✓ Allocating objects in non-existent partition returns correct error");
100        }
101        Ok(_) => {
102            println!("  ✗ Expected partition not found error, but allocation succeeded");
103        }
104        Err((other_error, _)) => {
105            println!(
106                "  ✗ Expected partition not found error, but got: {:?}",
107                other_error
108            );
109        }
110    }
111
112    // Test getting non-existent partition information
113    let partition_info = context.partition(invalid_partition);
114    assert!(
115        partition_info.is_none(),
116        "Non-existent partition should return None"
117    );
118    println!("  ✓ Getting non-existent partition info returns None");
119
120    // Test removing non-existent partition (remove_partition doesn't return error, just silently fails)
121    context.remove_partition(invalid_partition, GcHeap::DUMMY_DISPOSE_CALLBACK);
122    println!("  ✓ Removing non-existent partition silently fails");
123
124    println!("\n2. Test non-empty partition deletion...");
125    let partition_id = context.create_partition(64 * 1024, 16 * 1024);
126
127    // Allocate objects in partition
128    let obj = unsafe {
129        context.alloc_raw(
130            partition_id,
131            TestData {
132                value: 1,
133                name: "obj".to_string(),
134            },
135        )
136    }
137    .unwrap();
138    let _ = obj;
139
140    // Try to delete non-empty partition (remove_partition will force cleanup)
141    context.remove_partition(partition_id, GcHeap::DUMMY_DISPOSE_CALLBACK);
142    println!("  ✓ Successfully deleted non-empty partition (root objects were force cleaned)");
143
144    Ok(())
145}
146
147/// Demonstrate GC threshold API errors
148fn demonstrate_gc_threshold_errors() -> GcResult<()> {
149    println!("1. Test GC threshold API...");
150
151    let mut context = GcHeap::new(&GC_TYPE_REGISTRY);
152    context.set_memory_limit(1024);
153    let _partition_id = context.create_partition(64 * 1024, 16 * 1024);
154
155    // Test default values
156    println!("2. Test default threshold...");
157    assert_eq!(context.gc_threshold(), 0);
158    println!("  ✓ Default threshold is 0, automatic GC disabled");
159
160    // Test setting threshold
161    println!("3. Test setting threshold...");
162    context.set_gc_threshold(512);
163    assert_eq!(context.gc_threshold(), 512);
164    println!("  ✓ Successfully set threshold to 512, automatic GC enabled");
165
166    // Test setting threshold exceeding memory limit
167    println!("4. Test setting threshold exceeding memory limit...");
168    context.set_gc_threshold(2048);
169    // Since threshold exceeds memory limit, will be capped at 0.8x of limit (1024 * 8 / 10 = 819)
170    assert_eq!(context.gc_threshold(), 819);
171    println!(
172        "  ✓ Setting threshold exceeding memory limit automatically adjusted to 0.8x of memory limit"
173    );
174
175    // Test disabling automatic GC
176    println!("5. Test disabling automatic GC...");
177    context.set_gc_threshold(0);
178    assert_eq!(context.gc_threshold(), 0);
179    println!("  ✓ Successfully disabled automatic GC, threshold set to 0");
180
181    Ok(())
182}
183
184// Supporting type definitions
185
186/// Large memory data structure
187#[derive(Debug)]
188struct LargeData {
189    data: [u8; 1024], // 1KB data
190}
191
192impl GcTrace for LargeData {
193    fn trace(&self, _: &mut GcTraceCtx) {}
194}
195
196/// Test data structure
197#[derive(Debug, PartialEq)]
198struct TestData {
199    value: i32,
200    name: String,
201}
202
203impl GcTrace for TestData {
204    fn trace(&self, _: &mut GcTraceCtx) {}
205}
206
207/// Node structure for reference detection testing
208struct Node {
209    value: i32,
210    next: Option<GcRef<Node>>,
211}
212
213impl GcTrace for Node {
214    fn trace(&self, tr: &mut GcTraceCtx) {
215        if let Some(next) = self.next {
216            tr.add(next);
217        }
218    }
219}
220
221gc_type_register! {
222    LargeData;
223    TestData;
224    Node;
225}