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