1#![no_std]
6
7use cbuf::Cbuf;
8use core::ffi::{c_char, c_void};
9use core::sync::atomic::{AtomicU32, Ordering};
10use pin_init::stack_pin_init;
11use zx_status::Status;
12use zx_types::ZX_TIME_INFINITE;
13
14const ZX_ERR_INTERNAL_INTR_KILLED: i32 = -502;
15
16#[unsafe(no_mangle)]
17pub extern "C" fn test_cbuf_constructor() -> bool {
18 stack_pin_init!(let cbuf = Cbuf::init());
19 if !cbuf.full() {
20 return false;
21 }
22
23 let mut buf = [0u8; 4];
24 unsafe {
26 if cbuf.initialize(buf.len(), buf.as_mut_ptr()).is_err() {
27 return false;
28 }
29 }
30 if cbuf.full() {
31 return false;
32 }
33
34 true
35}
36
37#[unsafe(no_mangle)]
38pub extern "C" fn test_cbuf_read_write() -> bool {
39 stack_pin_init!(let cbuf = Cbuf::init());
40
41 let mut buf = [0u8; 4];
42 unsafe {
44 if cbuf.initialize(buf.len(), buf.as_mut_ptr()).is_err() {
45 return false;
46 }
47 }
48
49 if cbuf.full() {
50 return false;
51 }
52
53 if cbuf.read_char(false) != Err(Status::SHOULD_WAIT) {
55 return false;
56 }
57
58 let data = b"ABC";
60 for &c in data {
61 if cbuf.write_char(c) != 1 {
62 return false;
63 }
64 }
65 if !cbuf.full() {
66 return false;
67 }
68
69 if cbuf.write_char(b'D') != 0 {
71 return false;
72 }
73
74 for (i, &expected) in data.iter().enumerate() {
76 match cbuf.read_char_with_context(true) {
77 Ok(res) => {
78 if res.transitioned_from_full != (i == 0) {
79 return false;
80 }
81 if res.c != expected {
82 return false;
83 }
84 }
85 Err(_) => return false,
86 }
87 }
88 if cbuf.full() {
89 return false;
90 }
91
92 true
93}
94
95extern "C" fn reader_thread_entry(arg: *mut c_void) -> i32 {
96 let cbuf = unsafe { &*(arg as *const Cbuf) };
98 loop {
99 match cbuf.read_char(true) {
100 Ok(_) => {}
101 Err(status) => return status.into_raw(),
102 }
103 }
104}
105
106#[unsafe(no_mangle)]
107pub extern "C" fn test_cbuf_read_write_race() -> bool {
108 stack_pin_init!(let cbuf = Cbuf::init());
109
110 let mut buf = [0u8; 4];
111 unsafe {
113 if cbuf.initialize(buf.len(), buf.as_mut_ptr()).is_err() {
114 return false;
115 }
116 }
117
118 let thread_name = b"cbuf_rust_race\0".as_ptr() as *const c_char;
119 let cbuf_ptr = &*cbuf as *const Cbuf as *mut c_void;
120
121 unsafe {
124 let thread = match kernel::thread::spawn(thread_name, reader_thread_entry, cbuf_ptr) {
125 Ok(t) => t,
126 Err(_) => return false,
127 };
128
129 for _ in 0..1000 {
130 while cbuf.write_char(b'A') == 0 {
131 kernel::thread::r#yield();
132 }
133 }
134
135 thread.kill();
136
137 let ret = match thread.join(ZX_TIME_INFINITE) {
138 Ok(r) => r,
139 Err(_) => return false,
140 };
141 if ret != ZX_ERR_INTERNAL_INTR_KILLED {
142 return false;
143 }
144 }
145
146 true
147}
148
149#[unsafe(no_mangle)]
150pub extern "C" fn test_cbuf_init_limits() -> bool {
151 stack_pin_init!(let cbuf = Cbuf::init());
152 let mut buf = [0u8; 4];
153
154 unsafe {
156 if cbuf.initialize(0, buf.as_mut_ptr()) != Err(Status::INVALID_ARGS) {
157 return false;
158 }
159 }
160
161 unsafe {
163 if cbuf.initialize(3, buf.as_mut_ptr()) != Err(Status::INVALID_ARGS) {
164 return false;
165 }
166 if cbuf.initialize(5, buf.as_mut_ptr()) != Err(Status::INVALID_ARGS) {
167 return false;
168 }
169 }
170
171 unsafe {
173 if cbuf.initialize(4, buf.as_mut_ptr()).is_err() {
174 return false;
175 }
176 }
177
178 true
179}
180
181#[unsafe(no_mangle)]
182pub extern "C" fn test_cbuf_uninitialized() -> bool {
183 stack_pin_init!(let cbuf = Cbuf::init());
184
185 if !cbuf.full() {
186 return false;
187 }
188
189 if cbuf.write_char(b'A') != 0 {
190 return false;
191 }
192
193 if cbuf.read_char(false) != Err(Status::SHOULD_WAIT) {
194 return false;
195 }
196
197 true
198}
199
200#[unsafe(no_mangle)]
201pub extern "C" fn test_cbuf_wrap_around() -> bool {
202 stack_pin_init!(let cbuf = Cbuf::init());
203 let mut buf = [0u8; 4];
204
205 unsafe {
206 if cbuf.initialize(buf.len(), buf.as_mut_ptr()).is_err() {
207 return false;
208 }
209 }
210
211 if cbuf.write_char(b'A') != 1 {
213 return false;
214 }
215 if cbuf.write_char(b'B') != 1 {
216 return false;
217 }
218 if cbuf.write_char(b'C') != 1 {
219 return false;
220 }
221
222 if !cbuf.full() {
223 return false;
224 }
225
226 if cbuf.read_char(false) != Ok(b'A') {
228 return false;
229 }
230 if cbuf.read_char(false) != Ok(b'B') {
231 return false;
232 }
233 if cbuf.read_char(false) != Ok(b'C') {
234 return false;
235 }
236
237 if cbuf.full() {
238 return false;
239 }
240
241 if cbuf.write_char(b'D') != 1 {
243 return false;
244 }
245 if cbuf.write_char(b'E') != 1 {
246 return false;
247 }
248
249 if cbuf.read_char(false) != Ok(b'D') {
251 return false;
252 }
253 if cbuf.read_char(false) != Ok(b'E') {
254 return false;
255 }
256
257 if cbuf.read_char(false) != Err(Status::SHOULD_WAIT) {
259 return false;
260 }
261
262 true
263}
264
265struct BlockingReadContext {
266 cbuf: *mut Cbuf,
267 state: *const AtomicU32, read_char: *mut u8,
269}
270
271unsafe impl Send for BlockingReadContext {}
273
274extern "C" fn blocking_reader_entry(arg: *mut c_void) -> i32 {
275 let ctx = unsafe { &*(arg as *const BlockingReadContext) };
276 let cbuf = unsafe { &*ctx.cbuf };
277 let state = unsafe { &*ctx.state };
278
279 state.store(1, Ordering::SeqCst);
280 let c = cbuf.read_char(true); match c {
283 Ok(val) => {
284 unsafe { *ctx.read_char = val };
285 state.store(2, Ordering::SeqCst);
286 0
287 }
288 Err(status) => {
289 state.store(3, Ordering::SeqCst); status.into_raw()
291 }
292 }
293}
294
295#[unsafe(no_mangle)]
296pub extern "C" fn test_cbuf_blocking_read() -> bool {
297 stack_pin_init!(let cbuf = Cbuf::init());
298
299 let mut buf = [0u8; 4];
300 unsafe {
302 if cbuf.initialize(buf.len(), buf.as_mut_ptr()).is_err() {
303 return false;
304 }
305 }
306
307 let state = AtomicU32::new(0);
308 let mut read_char = 0u8;
309
310 let mut ctx = BlockingReadContext {
311 cbuf: &*cbuf as *const Cbuf as *mut Cbuf,
312 state: &state,
313 read_char: &mut read_char,
314 };
315
316 let thread_name = b"cbuf_blocking_read\0".as_ptr() as *const c_char;
317 let ctx_ptr = &mut ctx as *mut BlockingReadContext as *mut c_void;
318
319 unsafe {
320 let thread = match kernel::thread::spawn(thread_name, blocking_reader_entry, ctx_ptr) {
321 Ok(t) => t,
322 Err(_) => return false,
323 };
324
325 while state.load(Ordering::SeqCst) < 1 {
327 kernel::thread::r#yield();
328 }
329
330 while !thread.is_blocked() {
332 kernel::thread::r#yield();
333 if state.load(Ordering::SeqCst) == 3 {
335 break;
336 }
337 }
338
339 if state.load(Ordering::SeqCst) == 3 {
340 thread.join(ZX_TIME_INFINITE).ok();
341 return false;
342 }
343
344 if !thread.is_blocked() || state.load(Ordering::SeqCst) != 1 {
346 thread.join(ZX_TIME_INFINITE).ok();
347 return false;
348 }
349
350 if cbuf.write_char(b'X') != 1 {
352 thread.join(ZX_TIME_INFINITE).ok();
353 return false;
354 }
355
356 let ret = match thread.join(ZX_TIME_INFINITE) {
358 Ok(r) => r,
359 Err(_) => return false,
360 };
361
362 if ret != 0 {
363 return false;
364 }
365
366 if state.load(Ordering::SeqCst) != 2 {
367 return false;
368 }
369
370 if read_char != b'X' {
371 return false;
372 }
373 }
374
375 true
376}