summaryrefslogtreecommitdiffhomepage
path: root/mullvad-ios/src/api_client/retry_strategy.rs
blob: 06b9aa94db49a749b3ff0bc4d441fb11040a5c61 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
use std::time::Duration;

use talpid_future::retry::{ConstantInterval, ExponentialBackoff, Jittered};

#[repr(C)]
pub struct SwiftRetryStrategy(*mut RetryStrategy);

impl SwiftRetryStrategy {
    /// # Safety
    /// The pointer must be a pointing to a valid instance of a `Box<RetryStrategy>`.
    pub unsafe fn into_rust(self) -> RetryStrategy {
        unsafe { *Box::from_raw(self.0) }
    }
}

pub struct RetryStrategy {
    delays: RetryDelay,
    max_retries: usize,
}

impl RetryStrategy {
    pub fn delays(self) -> impl Iterator<Item = Duration> + Send {
        let Self {
            delays,
            max_retries,
        } = self;

        let delays: Box<dyn Iterator<Item = Duration> + Send> = match delays {
            RetryDelay::Never => Box::new(std::iter::empty()),
            RetryDelay::Constant(constant_delays) => Box::new(constant_delays.take(max_retries)),
            RetryDelay::Exponential(exponential_delays) => {
                Box::new(exponential_delays.take(max_retries))
            }
        };

        Jittered::jitter(delays)
    }
}

#[repr(C)]
pub enum RetryDelay {
    Never,
    Constant(ConstantInterval),
    Exponential(ExponentialBackoff),
}

/// Creates a retry strategy that never retries after failure.
/// The result needs to be consumed.
#[unsafe(no_mangle)]
pub extern "C" fn mullvad_api_retry_strategy_never() -> SwiftRetryStrategy {
    let retry_strategy = RetryStrategy {
        delays: RetryDelay::Never,
        max_retries: 0,
    };

    let ptr = Box::into_raw(Box::new(retry_strategy));
    SwiftRetryStrategy(ptr)
}

/// Creates a retry strategy that retries `max_retries` times with a constant delay of `delay_sec`.
/// The result needs to be consumed.
#[unsafe(no_mangle)]
pub extern "C" fn mullvad_api_retry_strategy_constant(
    max_retries: usize,
    delay_sec: u64,
) -> SwiftRetryStrategy {
    let interval = Duration::from_secs(delay_sec);
    let retry_strategy = RetryStrategy {
        delays: RetryDelay::Constant(ConstantInterval::new(interval, Some(max_retries))),
        max_retries: 0,
    };
    let ptr = Box::into_raw(Box::new(retry_strategy));

    SwiftRetryStrategy(ptr)
}

/// Creates a retry strategy that retries `max_retries` times with a exponantially increating delay.
/// The delay will never exceed `max_delay_sec`
/// The result needs to be consumed.
#[unsafe(no_mangle)]
pub extern "C" fn mullvad_api_retry_strategy_exponential(
    max_retries: usize,
    initial_sec: u64,
    factor: u32,
    max_delay_sec: u64,
) -> SwiftRetryStrategy {
    let initial_delay = Duration::from_secs(initial_sec);

    let backoff = ExponentialBackoff::new(initial_delay, factor)
        .max_delay(Some(Duration::from_secs(max_delay_sec)));

    let retry_strategy = RetryStrategy {
        delays: RetryDelay::Exponential(backoff),
        max_retries,
    };

    let ptr = Box::into_raw(Box::new(retry_strategy));
    SwiftRetryStrategy(ptr)
}