1use serde_json::Value;
21use std::collections::HashMap;
22use std::sync::{
23 Arc, Mutex, RwLock,
24 atomic::{AtomicBool, AtomicU64, Ordering},
25};
26use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
27
28#[inline(always)]
30fn likely(b: bool) -> bool {
31 if !b {
33 #[cold]
34 fn cold_path() {}
35 cold_path();
36 }
37 b
38}
39
40#[allow(dead_code)]
42#[inline(always)]
43fn unlikely(b: bool) -> bool {
44 if b {
46 #[cold]
47 fn cold_path() {}
48 cold_path();
49 }
50 b
51}
52
53#[derive(Debug, Clone)]
56pub struct PerlLspCancellationToken {
57 pub(crate) cancelled: Arc<AtomicBool>,
59 pub(crate) request_id: Value,
61 pub(crate) provider: String,
63 pub(crate) created_at: Instant,
65 pub(crate) timestamp: u64,
67}
68
69impl PerlLspCancellationToken {
70 pub fn new(request_id: Value, provider: String) -> Self {
72 let timestamp =
73 SystemTime::now().duration_since(UNIX_EPOCH).unwrap_or(Duration::ZERO).as_millis()
74 as u64;
75
76 Self {
77 cancelled: Arc::new(AtomicBool::new(false)),
78 request_id,
79 provider,
80 created_at: Instant::now(),
81 timestamp,
82 }
83 }
84
85 #[inline]
88 pub fn is_cancelled(&self) -> bool {
89 self.cancelled.load(Ordering::Relaxed)
90 }
91
92 #[inline]
96 pub fn is_cancelled_relaxed(&self) -> bool {
97 !likely(!self.cancelled.load(Ordering::Relaxed))
99 }
100
101 #[inline]
104 pub fn is_cancelled_hot_path(&self) -> bool {
105 self.cancelled.load(Ordering::Relaxed)
107 }
108
109 pub fn cancel(&self) {
111 self.cancelled.store(true, Ordering::Release);
112 }
113
114 pub fn request_id(&self) -> &Value {
116 &self.request_id
117 }
118
119 pub fn provider(&self) -> &str {
121 &self.provider
122 }
123
124 pub fn elapsed(&self) -> Duration {
126 self.created_at.elapsed()
127 }
128
129 pub fn timestamp(&self) -> u64 {
131 self.timestamp
132 }
133}
134
135pub struct ProviderCleanupContext {
137 pub provider_type: String,
139 pub request_params: Option<Value>,
141 pub cleanup_callback: Option<Box<dyn Fn() + Send + Sync>>,
143 pub cancelled_at: Instant,
145}
146
147impl ProviderCleanupContext {
148 pub fn new(provider_type: String, request_params: Option<Value>) -> Self {
150 Self { provider_type, request_params, cleanup_callback: None, cancelled_at: Instant::now() }
151 }
152
153 pub fn with_cleanup<F>(mut self, callback: F) -> Self
155 where
156 F: Fn() + Send + Sync + 'static,
157 {
158 self.cleanup_callback = Some(Box::new(callback));
159 self
160 }
161
162 pub fn execute_cleanup(&self) {
164 if let Some(callback) = &self.cleanup_callback {
165 callback();
166 }
167 }
168}
169
170pub struct CancellationRegistry {
172 tokens: Arc<RwLock<HashMap<String, PerlLspCancellationToken>>>,
174 cleanup_contexts: Arc<Mutex<HashMap<String, ProviderCleanupContext>>>,
176 metrics: Arc<CancellationMetrics>,
178 token_cache: Arc<RwLock<HashMap<String, PerlLspCancellationToken>>>,
180 max_cache_size: usize,
182}
183
184impl Default for CancellationRegistry {
185 fn default() -> Self {
186 Self::new()
187 }
188}
189
190impl CancellationRegistry {
191 pub fn new() -> Self {
193 Self {
194 tokens: Arc::new(RwLock::new(HashMap::new())),
195 cleanup_contexts: Arc::new(Mutex::new(HashMap::new())),
196 metrics: Arc::new(CancellationMetrics::new()),
197 token_cache: Arc::new(RwLock::new(HashMap::new())),
198 max_cache_size: 100, }
200 }
201
202 pub fn register_token(&self, token: PerlLspCancellationToken) -> Result<(), CancellationError> {
204 let key = format!("{:?}", token.request_id);
205
206 if let Ok(mut tokens) = self.tokens.write() {
207 tokens.insert(key.clone(), token);
208 self.metrics.increment_registered();
209 Ok(())
210 } else {
211 Err(CancellationError::LockError("Failed to acquire write lock".into()))
212 }
213 }
214
215 pub fn register_cleanup(
217 &self,
218 request_id: &Value,
219 context: ProviderCleanupContext,
220 ) -> Result<(), CancellationError> {
221 let key = format!("{:?}", request_id);
222
223 if let Ok(mut contexts) = self.cleanup_contexts.lock() {
224 contexts.insert(key, context);
225 Ok(())
226 } else {
227 Err(CancellationError::LockError("Failed to acquire cleanup lock".into()))
228 }
229 }
230
231 pub fn cancel_request(
233 &self,
234 request_id: &Value,
235 ) -> Result<Option<ProviderCleanupContext>, CancellationError> {
236 let key = format!("{:?}", request_id);
237
238 if let Ok(tokens) = self.tokens.read() {
240 if let Some(token) = tokens.get(&key) {
241 token.cancel();
242 self.metrics.increment_cancelled();
243 }
244 }
245
246 if let Ok(mut contexts) = self.cleanup_contexts.lock() {
248 if let Some(context) = contexts.remove(&key) {
249 context.execute_cleanup();
250 Ok(Some(context))
251 } else {
252 Ok(None)
253 }
254 } else {
255 Err(CancellationError::LockError("Failed to acquire cleanup lock".into()))
256 }
257 }
258
259 pub fn get_token(&self, request_id: &Value) -> Option<PerlLspCancellationToken> {
261 let key = format!("{:?}", request_id);
262
263 if let Ok(cache) = self.token_cache.read() {
265 if let Some(token) = cache.get(&key) {
266 return Some(token.clone());
267 }
268 }
269
270 if let Ok(tokens) = self.tokens.read() {
272 if let Some(token) = tokens.get(&key) {
273 let token_clone = token.clone();
274
275 if let Ok(mut cache) = self.token_cache.try_write() {
277 if cache.len() >= self.max_cache_size {
278 cache.clear(); }
280 cache.insert(key, token_clone.clone());
281 }
282
283 Some(token_clone)
284 } else {
285 None
286 }
287 } else {
288 None
289 }
290 }
291
292 #[inline]
294 pub fn is_cancelled(&self, request_id: &Value) -> bool {
295 let key = format!("{:?}", request_id);
296
297 if let Ok(cache) = self.token_cache.try_read() {
299 if let Some(token) = cache.get(&key) {
300 return token.is_cancelled_relaxed();
301 }
302 }
303
304 if let Ok(tokens) = self.tokens.try_read() {
306 if let Some(token) = tokens.get(&key) { token.is_cancelled_relaxed() } else { false }
307 } else {
308 false
309 }
310 }
311
312 pub fn remove_request(&self, request_id: &Value) {
314 let key = format!("{:?}", request_id);
315
316 if let Ok(mut tokens) = self.tokens.write() {
317 tokens.remove(&key);
318 }
319
320 if let Ok(mut contexts) = self.cleanup_contexts.lock() {
321 contexts.remove(&key);
322 }
323
324 self.metrics.increment_completed();
325 }
326
327 pub fn metrics(&self) -> &CancellationMetrics {
329 &self.metrics
330 }
331
332 pub fn active_count(&self) -> usize {
334 if let Ok(tokens) = self.tokens.read() { tokens.len() } else { 0 }
335 }
336}
337
338pub struct CancellationMetrics {
340 registered: AtomicU64,
342 cancelled: AtomicU64,
344 completed: AtomicU64,
346 created_at: Instant,
348}
349
350impl Default for CancellationMetrics {
351 fn default() -> Self {
352 Self::new()
353 }
354}
355
356impl CancellationMetrics {
357 pub fn new() -> Self {
359 Self {
360 registered: AtomicU64::new(0),
361 cancelled: AtomicU64::new(0),
362 completed: AtomicU64::new(0),
363 created_at: Instant::now(),
364 }
365 }
366
367 pub fn increment_registered(&self) {
369 self.registered.fetch_add(1, Ordering::Relaxed);
370 }
371
372 pub fn increment_cancelled(&self) {
374 self.cancelled.fetch_add(1, Ordering::Relaxed);
375 }
376
377 pub fn increment_completed(&self) {
379 self.completed.fetch_add(1, Ordering::Relaxed);
380 }
381
382 pub fn registered_count(&self) -> u64 {
384 self.registered.load(Ordering::Relaxed)
385 }
386
387 pub fn cancelled_count(&self) -> u64 {
389 self.cancelled.load(Ordering::Relaxed)
390 }
391
392 pub fn completed_count(&self) -> u64 {
394 self.completed.load(Ordering::Relaxed)
395 }
396
397 pub fn uptime(&self) -> Duration {
399 self.created_at.elapsed()
400 }
401
402 pub fn memory_overhead_bytes(&self) -> usize {
404 std::mem::size_of::<Self>() + 1024 }
407}
408
409#[derive(Debug)]
411pub enum CancellationError {
412 LockError(String),
414 InvalidRequest(String),
416 ProviderNotFound(String),
418 Timeout(Duration),
420}
421
422impl std::fmt::Display for CancellationError {
423 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
424 match self {
425 CancellationError::LockError(msg) => write!(f, "Lock error: {}", msg),
426 CancellationError::InvalidRequest(msg) => write!(f, "Invalid request: {}", msg),
427 CancellationError::ProviderNotFound(msg) => write!(f, "Provider not found: {}", msg),
428 CancellationError::Timeout(duration) => write!(f, "Operation timeout: {:?}", duration),
429 }
430 }
431}
432
433impl std::error::Error for CancellationError {}
434
435pub trait CancellableProvider {
437 fn check_cancellation(&self, token: &PerlLspCancellationToken)
439 -> Result<(), CancellationError>;
440
441 fn cleanup_on_cancel(&self, context: &ProviderCleanupContext);
443
444 fn provider_name(&self) -> &'static str;
446}
447
448#[macro_export]
450macro_rules! check_cancellation {
451 ($token:expr) => {
452 if $token.is_cancelled() {
453 return Err($crate::CancellationError::InvalidRequest("Request was cancelled".into()));
454 }
455 };
456}
457
458pub struct RequestCleanupGuard {
470 request_id: Option<Value>,
471}
472
473impl RequestCleanupGuard {
474 pub fn new(request_id: Option<Value>) -> Self {
478 Self { request_id }
479 }
480
481 pub fn from_ref(request_id: Option<&Value>) -> Self {
486 Self { request_id: request_id.cloned() }
487 }
488}
489
490impl Drop for RequestCleanupGuard {
491 fn drop(&mut self) {
492 if let Some(ref req_id) = self.request_id {
493 GLOBAL_CANCELLATION_REGISTRY.remove_request(req_id);
494 }
495 }
496}
497
498use std::sync::LazyLock;
499
500pub static GLOBAL_CANCELLATION_REGISTRY: LazyLock<CancellationRegistry> =
502 LazyLock::new(CancellationRegistry::new);
503
504#[cfg(test)]
505mod tests {
506 use super::*;
507 use serde_json::json;
508
509 #[test]
510 fn test_cancellation_token_creation() {
511 let token = PerlLspCancellationToken::new(json!(42), "hover".to_string());
512 assert!(!token.is_cancelled());
513 assert_eq!(token.provider(), "hover");
514 assert_eq!(token.request_id(), &json!(42));
515 }
516
517 #[test]
518 fn test_atomic_cancellation_operations() {
519 let token = PerlLspCancellationToken::new(json!(123), "completion".to_string());
520
521 assert!(!token.is_cancelled());
523
524 token.cancel();
526 assert!(token.is_cancelled());
527 }
528
529 #[test]
530 fn test_cancellation_registry_operations() -> Result<(), Box<dyn std::error::Error>> {
531 let registry = CancellationRegistry::new();
532 let token = PerlLspCancellationToken::new(json!(456), "references".to_string());
533
534 registry.register_token(token.clone())?;
536 assert_eq!(registry.active_count(), 1);
537
538 assert!(!registry.is_cancelled(&json!(456)));
540
541 registry.cancel_request(&json!(456))?;
543 assert!(registry.is_cancelled(&json!(456)));
544
545 registry.remove_request(&json!(456));
547 assert_eq!(registry.active_count(), 0);
548 Ok(())
549 }
550
551 #[test]
552 fn test_provider_cleanup_context() {
553 let mut context =
554 ProviderCleanupContext::new("test_provider".to_string(), Some(json!({"test": "data"})));
555
556 let cleanup_executed = Arc::new(AtomicBool::new(false));
557 let cleanup_flag = cleanup_executed.clone();
558
559 context = context.with_cleanup(move || {
560 cleanup_flag.store(true, Ordering::Relaxed);
561 });
562
563 context.execute_cleanup();
565 assert!(cleanup_executed.load(Ordering::Relaxed));
566 }
567
568 #[test]
569 fn test_performance_metrics() {
570 let metrics = CancellationMetrics::new();
571
572 assert_eq!(metrics.registered_count(), 0);
573 assert_eq!(metrics.cancelled_count(), 0);
574 assert_eq!(metrics.completed_count(), 0);
575
576 metrics.increment_registered();
577 metrics.increment_cancelled();
578 metrics.increment_completed();
579
580 assert_eq!(metrics.registered_count(), 1);
581 assert_eq!(metrics.cancelled_count(), 1);
582 assert_eq!(metrics.completed_count(), 1);
583
584 assert!(metrics.memory_overhead_bytes() < 1024 * 1024); }
587
588 static TEST_LOCK: std::sync::Mutex<()> = std::sync::Mutex::new(());
590
591 #[test]
592 fn test_request_cleanup_guard_auto_cleanup() -> Result<(), Box<dyn std::error::Error>> {
593 let _lock = TEST_LOCK.lock().map_err(|e| format!("lock error: {}", e))?;
595
596 let req_id = json!(9999);
597
598 GLOBAL_CANCELLATION_REGISTRY.remove_request(&req_id);
600 let count_before = GLOBAL_CANCELLATION_REGISTRY.active_count();
601
602 {
603 let token = PerlLspCancellationToken::new(req_id.clone(), "test".to_string());
605 GLOBAL_CANCELLATION_REGISTRY.register_token(token)?;
606
607 assert_eq!(
608 GLOBAL_CANCELLATION_REGISTRY.active_count(),
609 count_before + 1,
610 "Token should be registered"
611 );
612
613 let _guard = RequestCleanupGuard::new(Some(req_id.clone()));
615 }
617
618 assert_eq!(
620 GLOBAL_CANCELLATION_REGISTRY.active_count(),
621 count_before,
622 "Token should be removed by guard drop"
623 );
624 Ok(())
625 }
626
627 #[test]
628 fn test_request_cleanup_guard_none_is_noop() {
629 let _guard = RequestCleanupGuard::new(None);
631 }
633
634 #[test]
635 fn test_request_cleanup_guard_from_ref() -> Result<(), Box<dyn std::error::Error>> {
636 let req_id = json!(9998);
638 let guard = RequestCleanupGuard::from_ref(Some(&req_id));
639
640 assert!(guard.request_id.is_some());
642 assert_eq!(guard.request_id.as_ref().ok_or("expected request_id")?, &req_id);
643
644 drop(guard);
646 Ok(())
647 }
648}