Skip to main content

opcua_core/
handle.rs

1// OPCUA for Rust
2// SPDX-License-Identifier: MPL-2.0
3// Copyright (C) 2017-2024 Adam Lock
4
5//! Utility for producing sequential message handles.
6
7use std::sync::atomic::{AtomicU32, Ordering};
8
9use serde::Serialize;
10
11/// A simple handle factory for incrementing sequences of numbers.
12#[derive(Debug, Clone, Serialize)]
13pub struct Handle {
14    next: u32,
15    first: u32,
16}
17
18impl Handle {
19    /// Creates a new handle factory, that starts with the supplied number
20    pub fn new(first: u32) -> Handle {
21        Handle { next: first, first }
22    }
23
24    /// Returns the next handle to be issued, internally incrementing each time so the handle
25    /// is always different until it wraps back to the start.
26    #[allow(clippy::should_implement_trait)]
27    pub fn next(&mut self) -> u32 {
28        let next = self.next;
29        // Increment next
30        if self.next == u32::MAX {
31            self.next = self.first;
32        } else {
33            self.next += 1;
34        }
35        next
36    }
37
38    /// Peek the next value of the handle, without incrementing.
39    pub fn peek_next(&self) -> u32 {
40        self.next
41    }
42
43    /// Set the next handle value manually.
44    pub fn set_next(&mut self, next: u32) {
45        self.next = next;
46    }
47
48    /// Resets the handle to its initial state
49    pub fn reset(&mut self) {
50        self.set_next(self.first);
51    }
52}
53
54/// Variant of the handle factory using atomics
55#[derive(Debug)]
56pub struct AtomicHandle {
57    next: AtomicU32,
58    first: u32,
59}
60
61impl AtomicHandle {
62    /// Create a new atomic handle. `first` is the starting point and lowest value
63    /// this will produce.
64    pub fn new(first: u32) -> Self {
65        Self {
66            next: AtomicU32::new(first),
67            first,
68        }
69    }
70
71    /// Get the next handle.
72    pub fn next(&self) -> u32 {
73        let mut val = self.next.fetch_add(1, Ordering::Relaxed);
74
75        while val < self.first {
76            // On overflow, try to reset the next value to first + 1
77            match self.next.compare_exchange(
78                val + 1,
79                self.first + 1,
80                Ordering::Relaxed,
81                Ordering::Relaxed,
82            ) {
83                // If it succeeds, just use first directly.
84                Ok(_) => val = self.first,
85                Err(v) => {
86                    if v >= self.first {
87                        val = self.next.fetch_add(1, Ordering::Relaxed);
88                    } else {
89                        val = v;
90                    }
91                }
92            }
93        }
94        val
95    }
96
97    /// Set the next handle.
98    pub fn set_next(&self, next: u32) {
99        debug_assert!(next >= self.first);
100        self.next.store(next, Ordering::Relaxed);
101    }
102
103    /// Resets the handle to its initial state
104    pub fn reset(&self) {
105        self.set_next(self.first);
106    }
107}
108
109#[test]
110fn handle_increment() {
111    // Expect sequential handles
112    let mut h = Handle::new(0);
113    assert_eq!(h.next(), 0);
114    assert_eq!(h.next(), 1);
115    assert_eq!(h.next(), 2);
116    let mut h = Handle::new(100);
117    assert_eq!(h.next(), 100);
118    assert_eq!(h.next(), 101);
119}
120
121#[test]
122fn handle_wrap() {
123    // Simulate wrapping around
124    let mut h = Handle::new(u32::MAX - 2);
125    assert_eq!(h.next(), u32::MAX - 2);
126    assert_eq!(h.next(), u32::MAX - 1);
127    assert_eq!(h.next(), u32::MAX);
128    assert_eq!(h.next(), u32::MAX - 2);
129}
130
131#[test]
132fn atomic_handle_increment() {
133    // Expect sequential handles
134    let h = AtomicHandle::new(0);
135    assert_eq!(h.next(), 0);
136    assert_eq!(h.next(), 1);
137    assert_eq!(h.next(), 2);
138    let h = AtomicHandle::new(100);
139    assert_eq!(h.next(), 100);
140    assert_eq!(h.next(), 101);
141}
142
143#[test]
144fn atomic_handle_wrap() {
145    // Simulate wrapping around
146    let h = AtomicHandle::new(u32::MAX - 2);
147    assert_eq!(h.next(), u32::MAX - 2);
148    assert_eq!(h.next(), u32::MAX - 1);
149    assert_eq!(h.next(), u32::MAX);
150    assert_eq!(h.next(), u32::MAX - 2);
151}