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
100
101
102
103
use std::sync::Arc;
use mvutils::id_eq;
use mvutils::utils::next_id;
use crate::block::Signal;

/// A sync object used to add control flow to tasks. It is an internal sync object, meaning it can
/// only be read from within a task. If a task depends on another one to finish, you should use a
/// Semaphore to force the task to wait for the dependency to finish.
///
/// # Note
/// If a semaphore is bound to multiple tasks, it will signal as soon as the first task finishes.
/// Counter semaphores are planned to be added in the future.
pub struct Semaphore {
    id: u64,
    signaled: bool
}

impl Semaphore {
    pub(crate) fn new() -> Semaphore {
        Semaphore {
            id: next_id("MVSync"),
            signaled: false
        }
    }

    pub(crate) fn signal(&self) {
        unsafe {
            (self as *const Semaphore).cast_mut().as_mut().unwrap().signaled = true;
        }
    }

    pub(crate) fn ready(&self) -> bool {
        self.signaled
    }
}

/// A sync object used to wait for tasks. It is an external sync object, meaning it can
/// only be read from outside tasks. If you need to await a task whose result was passed into
/// another task, or you need to share a task "handle" without sharing the result, you should
/// use a Fence.
///
/// # Note
/// If a fence is bound to multiple tasks, it will open as soon as the first task finishes.
/// Counter fences are planned to be added in the future.
pub struct Fence {
    id: u64,
    signalled: Option<Arc<Signal>>
}

impl Fence {
    pub(crate) fn new() -> Self {
        Fence {
            id: next_id("MVSync"),
            signalled: None
        }
    }

    pub(crate) fn bind(&self, signal: Arc<Signal>) {
        unsafe {
            (self as *const Fence).cast_mut().as_mut().unwrap().signalled.replace(signal);
        }
    }

    /// Check if the fence is open.
    ///
    /// If this returns `true`, the task that signalled the fence has already finished.
    ///
    /// # Note
    /// If a fence is bound to multiple tasks, it will open as soon as the first task finishes.
    /// Counter fences are planned to be added in the future.
    pub fn ready(&self) -> bool {
        self.signalled.as_ref().expect("Checking unbound fence!").ready()
    }

    /// Block the current thread until the fence is signaled, indicating that the task
    /// this fence is bound to has finished.
    ///
    /// # Note
    /// If a fence is bound to multiple tasks, it will open as soon as the first task finishes.
    /// Counter fences are planned to be added in the future.
    pub fn wait(&self) {
        self.signalled.clone().expect("Checking unbound fence!").wait()
    }

    /// Block the current thread until the fence is signaled, indicating that the task
    /// this fence is bound to has finished.
    ///
    /// # Note
    /// If a fence is bound to multiple tasks, it will open as soon as the first task finishes.
    /// Counter fences are planned to be added in the future.
    pub async fn wait_async(&self) {
        self.signalled.clone().expect("Checking unbound fence!").wait_async().await
    }
}

id_eq!(Semaphore, Fence);

pub enum SemaphoreUsage {
    /// Wait for the semaphore to be signaled.
    Wait,
    /// Signal the semaphore upon completion.
    Signal
}