starnix_core/vfs/
eventfd.rs1use crate::task::{CurrentTask, EventHandler, WaitCanceler, WaitQueue, Waiter};
6use crate::vfs::buffers::{InputBuffer, InputBufferExt as _, OutputBuffer};
7use crate::vfs::{
8 Anon, FileHandle, FileObject, FileOps, fileops_impl_nonseekable, fileops_impl_noop_sync,
9};
10use starnix_sync::{FileOpsCore, LockEqualOrBefore, Locked, Mutex};
11use starnix_uapi::error;
12use starnix_uapi::errors::Errno;
13use starnix_uapi::open_flags::OpenFlags;
14use starnix_uapi::vfs::FdEvents;
15
16const DATA_SIZE: usize = 8;
17
18pub enum EventFdType {
19 Counter,
20 Semaphore,
21}
22
23pub struct EventFdFileObject {
30 inner: Mutex<u64>,
31 eventfd_type: EventFdType,
32 wait_queue: WaitQueue,
33}
34
35pub fn new_eventfd<L>(
36 locked: &mut Locked<L>,
37 current_task: &CurrentTask,
38 value: u32,
39 eventfd_type: EventFdType,
40 blocking: bool,
41) -> FileHandle
42where
43 L: LockEqualOrBefore<FileOpsCore>,
44{
45 let open_flags = if blocking { OpenFlags::RDWR } else { OpenFlags::RDWR | OpenFlags::NONBLOCK };
46 Anon::new_private_file(
47 locked,
48 current_task,
49 Box::new(EventFdFileObject {
50 inner: Mutex::new(value.into()),
51 eventfd_type,
52 wait_queue: WaitQueue::default(),
53 }),
54 open_flags,
55 "[eventfd]",
56 )
57}
58
59impl FileOps for EventFdFileObject {
60 fileops_impl_nonseekable!();
61 fileops_impl_noop_sync!();
62
63 fn write(
64 &self,
65 locked: &mut Locked<FileOpsCore>,
66 file: &FileObject,
67 current_task: &CurrentTask,
68 offset: usize,
69 data: &mut dyn InputBuffer,
70 ) -> Result<usize, Errno> {
71 debug_assert!(offset == 0);
72 file.blocking_op(locked, current_task, FdEvents::POLLOUT | FdEvents::POLLHUP, None, |_| {
73 let written_data = data.read_to_array::<DATA_SIZE>()?;
74 let add_value = u64::from_ne_bytes(written_data);
75 if add_value == u64::MAX {
76 return error!(EINVAL);
77 }
78
79 let should_notify = {
80 let mut inner = self.inner.lock();
81 let headroom = u64::MAX - *inner - 1;
82 if headroom < add_value {
83 return error!(EAGAIN);
84 }
85 *inner += add_value;
86 *inner > 0
87 };
88 if should_notify {
89 self.wait_queue.notify_fd_events(FdEvents::POLLIN);
90 }
91 Ok(DATA_SIZE)
92 })
93 }
94
95 fn read(
96 &self,
97 locked: &mut Locked<FileOpsCore>,
98 file: &FileObject,
99 current_task: &CurrentTask,
100 offset: usize,
101 data: &mut dyn OutputBuffer,
102 ) -> Result<usize, Errno> {
103 debug_assert!(offset == 0);
104 file.blocking_op(locked, current_task, FdEvents::POLLIN | FdEvents::POLLHUP, None, |_| {
105 if data.available() < DATA_SIZE {
106 return error!(EINVAL);
107 }
108
109 let return_value = {
110 let mut inner = self.inner.lock();
111 if *inner == 0 {
112 return error!(EAGAIN);
113 }
114
115 match self.eventfd_type {
116 EventFdType::Counter => {
117 let start_value = *inner;
118 *inner = 0;
119 start_value
120 }
121 EventFdType::Semaphore => {
122 *inner -= 1;
123 1
124 }
125 }
126 };
127 data.write_all(&return_value.to_ne_bytes())?;
128 self.wait_queue.notify_fd_events(FdEvents::POLLOUT);
129
130 Ok(DATA_SIZE)
131 })
132 }
133
134 fn wait_async(
135 &self,
136 _locked: &mut Locked<FileOpsCore>,
137 _file: &FileObject,
138 _current_task: &CurrentTask,
139 waiter: &Waiter,
140 events: FdEvents,
141 handler: EventHandler,
142 ) -> Option<WaitCanceler> {
143 Some(self.wait_queue.wait_async_fd_events(waiter, events, handler))
144 }
145
146 fn query_events(
147 &self,
148 _locked: &mut Locked<FileOpsCore>,
149 _file: &FileObject,
150 _current_task: &CurrentTask,
151 ) -> Result<FdEvents, Errno> {
152 let inner = self.inner.lock();
153 let mut events = FdEvents::empty();
155 if *inner > 0 {
156 events |= FdEvents::POLLIN;
157 }
158 if *inner < u64::MAX - 1 {
159 events |= FdEvents::POLLOUT;
160 }
161 Ok(events)
162 }
163}