1use core::ffi::{c_char, c_void};
15use core::panic::AssertUnwindSafe;
16
17use crate::support::{RawBytes, RawCString};
18
19fn guard<T>(fallback: T, f: impl FnOnce() -> T) -> T {
27 match std::panic::catch_unwind(AssertUnwindSafe(f)) {
28 Ok(v) => v,
29 Err(_) => {
30 eprintln!(
32 "whiteout: a panic in a host-implemented interface was contained \
33 at the FFI boundary; the operation reports failure"
34 );
35 fallback
36 }
37 }
38}
39
40const BUF_HEADER: usize = 16; fn leak_buffer(data: Vec<u8>, out_data: *mut *mut u8, out_size: *mut usize) {
50 unsafe {
52 *out_data = core::ptr::null_mut();
53 *out_size = 0;
54 }
55 if data.is_empty() {
56 return;
57 }
58 let len = data.len();
59 let Ok(layout) = std::alloc::Layout::from_size_align(BUF_HEADER + len, BUF_HEADER) else {
60 return;
61 };
62 let base = unsafe { std::alloc::alloc(layout) };
64 if base.is_null() {
65 return;
66 }
67 unsafe {
70 (base as *mut usize).write(len);
71 core::ptr::copy_nonoverlapping(data.as_ptr(), base.add(BUF_HEADER), len);
72 *out_data = base.add(BUF_HEADER);
73 *out_size = len;
74 }
75}
76
77unsafe extern "C" fn free_buffer(data: *mut u8) {
78 if data.is_null() {
79 return;
80 }
81 unsafe {
84 let base = data.sub(BUF_HEADER);
85 let len = (base as *const usize).read();
86 let layout = std::alloc::Layout::from_size_align_unchecked(BUF_HEADER + len, BUF_HEADER);
87 std::alloc::dealloc(base, layout);
88 }
89}
90
91unsafe fn str_of<'a>(p: *const c_char, len: usize) -> &'a str {
94 if p.is_null() || len == 0 {
95 return "";
96 }
97 let bytes = unsafe { core::slice::from_raw_parts(p as *const u8, len) };
99 core::str::from_utf8(bytes).unwrap_or("")
100}
101
102unsafe fn bytes_of<'a>(data: *const u8, size: usize) -> &'a [u8] {
105 if data.is_null() || size == 0 {
106 return &[];
107 }
108 unsafe { core::slice::from_raw_parts(data, size) }
110}
111
112pub trait FileSystem: Send + Sync {
119 fn read_file(&self, path: &str) -> Option<Vec<u8>>;
120
121 fn write_file(&self, _path: &str, _data: &[u8]) -> bool {
122 false
123 }
124
125 fn file_exists(&self, path: &str) -> bool {
126 self.read_file(path).is_some()
127 }
128}
129
130#[repr(C)]
131struct VfsFnTable {
132 read_file: unsafe extern "C" fn(*mut c_void, *const c_char, usize, *mut *mut u8, *mut usize),
133 free_buffer: unsafe extern "C" fn(*mut u8),
134 write_file: unsafe extern "C" fn(*mut c_void, *const c_char, usize, *const u8, usize) -> i32,
135 file_exists: unsafe extern "C" fn(*mut c_void, *const c_char, usize) -> i32,
136}
137
138unsafe fn vfs_of<'a>(userdata: *mut c_void) -> &'a dyn FileSystem {
141 unsafe { &**(userdata as *const Box<dyn FileSystem>) }
143}
144
145unsafe extern "C" fn vfs_read_file(
146 userdata: *mut c_void,
147 path: *const c_char,
148 path_len: usize,
149 out_data: *mut *mut u8,
150 out_size: *mut usize,
151) {
152 let data = guard(Vec::new(), || {
153 let fs = unsafe { vfs_of(userdata) };
155 let path = unsafe { str_of(path, path_len) };
156 fs.read_file(path).unwrap_or_default()
157 });
158 leak_buffer(data, out_data, out_size);
159}
160
161unsafe extern "C" fn vfs_write_file(
162 userdata: *mut c_void,
163 path: *const c_char,
164 path_len: usize,
165 data: *const u8,
166 size: usize,
167) -> i32 {
168 guard(0, || {
169 let fs = unsafe { vfs_of(userdata) };
171 let path = unsafe { str_of(path, path_len) };
172 let bytes = unsafe { bytes_of(data, size) };
173 i32::from(fs.write_file(path, bytes))
174 })
175}
176
177unsafe extern "C" fn vfs_file_exists(
178 userdata: *mut c_void,
179 path: *const c_char,
180 path_len: usize,
181) -> i32 {
182 guard(0, || {
183 let fs = unsafe { vfs_of(userdata) };
185 let path = unsafe { str_of(path, path_len) };
186 i32::from(fs.file_exists(path))
187 })
188}
189
190extern "C" {
191 fn whiteout_hostimpl_VirtualPathFileSystem_create(
192 userdata: *mut c_void,
193 fns: *const VfsFnTable,
194 ) -> *mut c_void;
195 fn whiteout_hostimpl_VirtualPathFileSystem_delete(handle: *mut c_void);
196
197 fn whiteout_hostimpl_test_VirtualPathFileSystem_readFile(
198 handle: *mut c_void,
199 path: *const c_char,
200 ) -> RawBytes;
201 fn whiteout_hostimpl_test_VirtualPathFileSystem_fileExists(
202 handle: *mut c_void,
203 path: *const c_char,
204 ) -> i32;
205}
206
207pub struct HostFileSystem {
212 handle: *mut c_void,
213 userdata: *mut Box<dyn FileSystem>,
214}
215
216impl HostFileSystem {
217 pub fn new<F: FileSystem + 'static>(fs: F) -> Self {
218 let boxed: Box<dyn FileSystem> = Box::new(fs);
219 let userdata = Box::into_raw(Box::new(boxed));
220 let table = VfsFnTable {
221 read_file: vfs_read_file,
222 free_buffer,
223 write_file: vfs_write_file,
224 file_exists: vfs_file_exists,
225 };
226 let handle = unsafe {
228 whiteout_hostimpl_VirtualPathFileSystem_create(userdata as *mut c_void, &table)
229 };
230 HostFileSystem { handle, userdata }
231 }
232
233 pub fn as_ptr(&self) -> *mut c_void {
236 self.handle
237 }
238
239 pub fn read_through_native(&self, path: &str) -> Option<Vec<u8>> {
241 let c = std::ffi::CString::new(path).ok()?;
242 let raw = unsafe {
244 whiteout_hostimpl_test_VirtualPathFileSystem_readFile(self.handle, c.as_ptr())
245 };
246 unsafe { crate::support::Bytes::from_raw(raw) }.map(|b| b.to_vec())
248 }
249
250 pub fn exists_through_native(&self, path: &str) -> bool {
251 let Ok(c) = std::ffi::CString::new(path) else {
252 return false;
253 };
254 unsafe {
256 whiteout_hostimpl_test_VirtualPathFileSystem_fileExists(self.handle, c.as_ptr()) != 0
257 }
258 }
259}
260
261impl Drop for HostFileSystem {
262 fn drop(&mut self) {
263 unsafe {
265 whiteout_hostimpl_VirtualPathFileSystem_delete(self.handle);
266 drop(Box::from_raw(self.userdata));
267 }
268 }
269}
270
271impl core::fmt::Debug for HostFileSystem {
272 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
273 f.debug_struct("HostFileSystem").finish_non_exhaustive()
274 }
275}
276
277unsafe impl Send for HostFileSystem {}
280unsafe impl Sync for HostFileSystem {}
281
282pub trait CascFileSystem: Send + Sync {
288 fn read_file(&self, file_id: u32) -> Option<Vec<u8>>;
289
290 fn reserve_file_id(&self, _path: &str) -> Option<u32> {
291 None
292 }
293
294 fn write_file(&self, _file_id: u32, _data: &[u8]) -> bool {
295 false
296 }
297
298 fn file_exists(&self, file_id: u32) -> bool {
299 self.read_file(file_id).is_some()
300 }
301}
302
303#[repr(C)]
304struct CascFsFnTable {
305 read_file: unsafe extern "C" fn(*mut c_void, u32, *mut *mut u8, *mut usize),
306 free_buffer: unsafe extern "C" fn(*mut u8),
307 reserve_file_id: unsafe extern "C" fn(*mut c_void, *const c_char, usize, *mut u32) -> i32,
308 write_file: unsafe extern "C" fn(*mut c_void, u32, *const u8, usize) -> i32,
309 file_exists: unsafe extern "C" fn(*mut c_void, u32) -> i32,
310}
311
312unsafe fn casc_of<'a>(userdata: *mut c_void) -> &'a dyn CascFileSystem {
315 unsafe { &**(userdata as *const Box<dyn CascFileSystem>) }
317}
318
319unsafe extern "C" fn casc_read_file(
320 userdata: *mut c_void,
321 file_id: u32,
322 out_data: *mut *mut u8,
323 out_size: *mut usize,
324) {
325 let data = guard(Vec::new(), || {
326 unsafe { casc_of(userdata) }
328 .read_file(file_id)
329 .unwrap_or_default()
330 });
331 leak_buffer(data, out_data, out_size);
332}
333
334unsafe extern "C" fn casc_reserve_file_id(
335 userdata: *mut c_void,
336 path: *const c_char,
337 path_len: usize,
338 out_id: *mut u32,
339) -> i32 {
340 guard(0, || {
341 let fs = unsafe { casc_of(userdata) };
343 let path = unsafe { str_of(path, path_len) };
344 match fs.reserve_file_id(path) {
345 Some(id) => {
346 unsafe { *out_id = id };
348 1
349 }
350 None => 0,
351 }
352 })
353}
354
355unsafe extern "C" fn casc_write_file(
356 userdata: *mut c_void,
357 file_id: u32,
358 data: *const u8,
359 size: usize,
360) -> i32 {
361 guard(0, || {
362 let fs = unsafe { casc_of(userdata) };
364 let bytes = unsafe { bytes_of(data, size) };
365 i32::from(fs.write_file(file_id, bytes))
366 })
367}
368
369unsafe extern "C" fn casc_file_exists(userdata: *mut c_void, file_id: u32) -> i32 {
370 guard(0, || {
371 i32::from(unsafe { casc_of(userdata) }.file_exists(file_id))
373 })
374}
375
376extern "C" {
377 fn whiteout_hostimpl_CascFileSystem_create(
378 userdata: *mut c_void,
379 fns: *const CascFsFnTable,
380 ) -> *mut c_void;
381 fn whiteout_hostimpl_CascFileSystem_delete(handle: *mut c_void);
382
383 fn whiteout_hostimpl_test_CascFileSystem_readFile(handle: *mut c_void, id: u32) -> RawBytes;
384 fn whiteout_hostimpl_test_CascFileSystem_fileExists(handle: *mut c_void, id: u32) -> i32;
385}
386
387pub struct HostCascFileSystem {
389 handle: *mut c_void,
390 userdata: *mut Box<dyn CascFileSystem>,
391}
392
393impl HostCascFileSystem {
394 pub fn new<F: CascFileSystem + 'static>(fs: F) -> Self {
395 let boxed: Box<dyn CascFileSystem> = Box::new(fs);
396 let userdata = Box::into_raw(Box::new(boxed));
397 let table = CascFsFnTable {
398 read_file: casc_read_file,
399 free_buffer,
400 reserve_file_id: casc_reserve_file_id,
401 write_file: casc_write_file,
402 file_exists: casc_file_exists,
403 };
404 let handle =
406 unsafe { whiteout_hostimpl_CascFileSystem_create(userdata as *mut c_void, &table) };
407 HostCascFileSystem { handle, userdata }
408 }
409
410 pub fn as_ptr(&self) -> *mut c_void {
411 self.handle
412 }
413
414 pub fn read_through_native(&self, file_id: u32) -> Option<Vec<u8>> {
415 let raw = unsafe { whiteout_hostimpl_test_CascFileSystem_readFile(self.handle, file_id) };
417 unsafe { crate::support::Bytes::from_raw(raw) }.map(|b| b.to_vec())
419 }
420
421 pub fn exists_through_native(&self, file_id: u32) -> bool {
422 unsafe { whiteout_hostimpl_test_CascFileSystem_fileExists(self.handle, file_id) != 0 }
424 }
425}
426
427impl Drop for HostCascFileSystem {
428 fn drop(&mut self) {
429 unsafe {
431 whiteout_hostimpl_CascFileSystem_delete(self.handle);
432 drop(Box::from_raw(self.userdata));
433 }
434 }
435}
436
437impl core::fmt::Debug for HostCascFileSystem {
438 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
439 f.debug_struct("HostCascFileSystem").finish_non_exhaustive()
440 }
441}
442
443unsafe impl Send for HostCascFileSystem {}
445unsafe impl Sync for HostCascFileSystem {}
446
447pub mod http_capability {
451 pub const NONE: u32 = 0;
453 pub const HTTP2_MULTIPLEXING: u32 = 0x1;
455}
456
457pub struct HttpResponder {
468 handle: *mut c_void,
469}
470
471impl HttpResponder {
472 pub fn respond(self, status: i32, body: &[u8]) {
474 let me = core::mem::ManuallyDrop::new(self);
475 unsafe {
478 whiteout_hostimpl_HttpResponseCallback_fire(
479 me.handle,
480 status,
481 body.as_ptr(),
482 body.len(),
483 core::ptr::null(),
484 );
485 }
486 }
487
488 pub fn fail(self, error: &str) {
490 let me = core::mem::ManuallyDrop::new(self);
491 let c = std::ffi::CString::new(error).unwrap_or_default();
492 unsafe {
494 whiteout_hostimpl_HttpResponseCallback_fire(
495 me.handle,
496 0,
497 core::ptr::null(),
498 0,
499 c.as_ptr(),
500 );
501 }
502 }
503}
504
505impl Drop for HttpResponder {
506 fn drop(&mut self) {
507 unsafe { whiteout_hostimpl_HttpResponseCallback_cancel(self.handle) };
512 }
513}
514
515impl core::fmt::Debug for HttpResponder {
516 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
517 f.debug_struct("HttpResponder").finish_non_exhaustive()
518 }
519}
520
521unsafe impl Send for HttpResponder {}
524
525pub trait HttpHandler: Send + Sync {
531 fn capabilities(&self) -> u32 {
532 http_capability::NONE
533 }
534
535 fn get(&self, url: &str, responder: HttpResponder);
536
537 fn get_range(&self, url: &str, start: u64, end: u64, responder: HttpResponder);
539}
540
541#[repr(C)]
542struct HttpFnTable {
543 capabilities: unsafe extern "C" fn(*mut c_void) -> u32,
544 get_async: unsafe extern "C" fn(*mut c_void, *const c_char, usize, *mut c_void),
545 get_range_async: unsafe extern "C" fn(*mut c_void, *const c_char, usize, u64, u64, *mut c_void),
546}
547
548unsafe fn http_of<'a>(userdata: *mut c_void) -> &'a dyn HttpHandler {
551 unsafe { &**(userdata as *const Box<dyn HttpHandler>) }
553}
554
555unsafe extern "C" fn http_capabilities(userdata: *mut c_void) -> u32 {
556 guard(http_capability::NONE, || {
557 unsafe { http_of(userdata) }.capabilities()
559 })
560}
561
562unsafe extern "C" fn http_get_async(
563 userdata: *mut c_void,
564 url: *const c_char,
565 url_len: usize,
566 callback: *mut c_void,
567) {
568 let responder = HttpResponder { handle: callback };
569 guard((), move || {
573 let h = unsafe { http_of(userdata) };
575 let url = unsafe { str_of(url, url_len) };
576 h.get(url, responder);
577 });
578}
579
580unsafe extern "C" fn http_get_range_async(
581 userdata: *mut c_void,
582 url: *const c_char,
583 url_len: usize,
584 start: u64,
585 end: u64,
586 callback: *mut c_void,
587) {
588 let responder = HttpResponder { handle: callback };
589 guard((), move || {
590 let h = unsafe { http_of(userdata) };
592 let url = unsafe { str_of(url, url_len) };
593 h.get_range(url, start, end, responder);
594 });
595}
596
597extern "C" {
598 fn whiteout_hostimpl_HttpHandler_create(
599 userdata: *mut c_void,
600 fns: *const HttpFnTable,
601 ) -> *mut c_void;
602 fn whiteout_hostimpl_HttpHandler_delete(handle: *mut c_void);
603 fn whiteout_hostimpl_HttpResponseCallback_fire(
604 callback: *mut c_void,
605 status: i32,
606 body: *const u8,
607 body_len: usize,
608 error: *const c_char,
609 );
610 fn whiteout_hostimpl_HttpResponseCallback_cancel(callback: *mut c_void);
611
612 fn whiteout_hostimpl_test_HttpHandler_capabilities(handle: *mut c_void) -> u32;
613 fn whiteout_hostimpl_test_HttpHandler_getAsync(
614 handle: *mut c_void,
615 url: *const c_char,
616 out_status: *mut i32,
617 out_body: *mut RawBytes,
618 out_error: *mut RawCString,
619 );
620 fn whiteout_hostimpl_test_HttpHandler_getRangeAsync(
621 handle: *mut c_void,
622 url: *const c_char,
623 start: u64,
624 end: u64,
625 out_status: *mut i32,
626 out_body: *mut RawBytes,
627 out_error: *mut RawCString,
628 );
629}
630
631#[derive(Debug, Clone, PartialEq, Eq)]
633pub struct HttpOutcome {
634 pub status: i32,
635 pub body: Vec<u8>,
636 pub error: String,
637}
638
639fn empty_bytes() -> RawBytes {
640 RawBytes {
641 data: core::ptr::null(),
642 size: 0,
643 owner: core::ptr::null_mut(),
644 }
645}
646
647fn empty_cstring() -> RawCString {
648 RawCString {
649 chars: core::ptr::null(),
650 length: 0,
651 owner: core::ptr::null_mut(),
652 }
653}
654
655unsafe fn collect_outcome(status: i32, body: RawBytes, error: RawCString) -> HttpOutcome {
659 let body = unsafe { crate::support::Bytes::from_raw(body) }
661 .map(|b| b.to_vec())
662 .unwrap_or_default();
663 let error = unsafe { crate::support::take_string_opt(error) }.unwrap_or_default();
665 HttpOutcome {
666 status,
667 body,
668 error,
669 }
670}
671
672pub struct HostHttpHandler {
674 handle: *mut c_void,
675 userdata: *mut Box<dyn HttpHandler>,
676}
677
678impl HostHttpHandler {
679 pub fn new<H: HttpHandler + 'static>(handler: H) -> Self {
680 let boxed: Box<dyn HttpHandler> = Box::new(handler);
681 let userdata = Box::into_raw(Box::new(boxed));
682 let table = HttpFnTable {
683 capabilities: http_capabilities,
684 get_async: http_get_async,
685 get_range_async: http_get_range_async,
686 };
687 let handle =
689 unsafe { whiteout_hostimpl_HttpHandler_create(userdata as *mut c_void, &table) };
690 HostHttpHandler { handle, userdata }
691 }
692
693 pub fn as_ptr(&self) -> *mut c_void {
695 self.handle
696 }
697
698 pub fn capabilities_through_native(&self) -> u32 {
699 unsafe { whiteout_hostimpl_test_HttpHandler_capabilities(self.handle) }
701 }
702
703 pub fn get_through_native(&self, url: &str) -> HttpOutcome {
715 let c = std::ffi::CString::new(url).unwrap_or_default();
716 let mut status = 0i32;
717 let mut body = empty_bytes();
718 let mut error = empty_cstring();
719 unsafe {
721 whiteout_hostimpl_test_HttpHandler_getAsync(
722 self.handle,
723 c.as_ptr(),
724 &mut status,
725 &mut body,
726 &mut error,
727 );
728 collect_outcome(status, body, error)
729 }
730 }
731
732 pub fn get_range_through_native(&self, url: &str, start: u64, end: u64) -> HttpOutcome {
733 let c = std::ffi::CString::new(url).unwrap_or_default();
734 let mut status = 0i32;
735 let mut body = empty_bytes();
736 let mut error = empty_cstring();
737 unsafe {
739 whiteout_hostimpl_test_HttpHandler_getRangeAsync(
740 self.handle,
741 c.as_ptr(),
742 start,
743 end,
744 &mut status,
745 &mut body,
746 &mut error,
747 );
748 collect_outcome(status, body, error)
749 }
750 }
751}
752
753impl Drop for HostHttpHandler {
754 fn drop(&mut self) {
755 unsafe {
757 whiteout_hostimpl_HttpHandler_delete(self.handle);
758 drop(Box::from_raw(self.userdata));
759 }
760 }
761}
762
763impl core::fmt::Debug for HostHttpHandler {
764 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
765 f.debug_struct("HostHttpHandler").finish_non_exhaustive()
766 }
767}
768
769unsafe impl Send for HostHttpHandler {}
771unsafe impl Sync for HostHttpHandler {}
772
773pub struct WorkerTask {
785 fn_handle: *mut c_void,
786 wait: Option<(*mut c_void, u64)>,
787 signal: Option<(*mut c_void, u64)>,
788}
789
790impl WorkerTask {
791 pub fn run(self) {
794 let me = core::mem::ManuallyDrop::new(self);
795 unsafe {
798 if let Some((sem, value)) = me.wait {
799 whiteout_hostimpl_TimelineSemaphore_await(sem, value);
800 }
801 whiteout_hostimpl_WorkerTaskFn_fire(me.fn_handle);
802 if let Some((sem, value)) = me.signal {
803 whiteout_hostimpl_TimelineSemaphore_signal(sem, value);
804 }
805 }
806 }
807}
808
809impl Drop for WorkerTask {
810 fn drop(&mut self) {
811 unsafe { whiteout_hostimpl_WorkerTaskFn_cancel(self.fn_handle) };
814 }
815}
816
817impl core::fmt::Debug for WorkerTask {
818 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
819 f.debug_struct("WorkerTask").finish_non_exhaustive()
820 }
821}
822
823unsafe impl Send for WorkerTask {}
826
827pub trait WorkerPool: Send + Sync {
829 fn submit(&self, task: WorkerTask);
831
832 fn wait_idle(&self);
834
835 fn thread_count(&self) -> usize;
836}
837
838#[repr(C)]
839struct WorkerTaskFlat {
840 fn_handle: *mut c_void,
841 wait_semaphore: *mut c_void,
842 wait_value: u64,
843 signal_semaphore: *mut c_void,
844 signal_value: u64,
845}
846
847#[repr(C)]
848struct WorkerPoolFnTable {
849 submit: unsafe extern "C" fn(*mut c_void, *const WorkerTaskFlat),
850 wait_idle: unsafe extern "C" fn(*mut c_void),
851 thread_count: unsafe extern "C" fn(*mut c_void) -> usize,
852}
853
854unsafe fn pool_of<'a>(userdata: *mut c_void) -> &'a dyn WorkerPool {
857 unsafe { &**(userdata as *const Box<dyn WorkerPool>) }
859}
860
861unsafe extern "C" fn pool_submit(userdata: *mut c_void, flat: *const WorkerTaskFlat) {
862 if flat.is_null() {
863 return;
864 }
865 let flat = unsafe { &*flat };
867 let task = WorkerTask {
868 fn_handle: flat.fn_handle,
869 wait: (!flat.wait_semaphore.is_null()).then_some((flat.wait_semaphore, flat.wait_value)),
870 signal: (!flat.signal_semaphore.is_null())
871 .then_some((flat.signal_semaphore, flat.signal_value)),
872 };
873 guard((), move || {
874 unsafe { pool_of(userdata) }.submit(task);
876 });
877}
878
879unsafe extern "C" fn pool_wait_idle(userdata: *mut c_void) {
880 guard((), || {
881 unsafe { pool_of(userdata) }.wait_idle();
883 });
884}
885
886unsafe extern "C" fn pool_thread_count(userdata: *mut c_void) -> usize {
887 guard(1, || {
888 unsafe { pool_of(userdata) }.thread_count()
890 })
891}
892
893extern "C" {
894 fn whiteout_hostimpl_WorkerPool_create(
895 userdata: *mut c_void,
896 fns: *const WorkerPoolFnTable,
897 ) -> *mut c_void;
898 fn whiteout_hostimpl_WorkerPool_delete(handle: *mut c_void);
899 fn whiteout_hostimpl_WorkerTaskFn_fire(fn_handle: *mut c_void);
900 fn whiteout_hostimpl_WorkerTaskFn_cancel(fn_handle: *mut c_void);
901 fn whiteout_hostimpl_TimelineSemaphore_await(sem: *mut c_void, value: u64);
902 fn whiteout_hostimpl_TimelineSemaphore_signal(sem: *mut c_void, value: u64);
903
904 fn whiteout_hostimpl_test_WorkerPool_threadCount(handle: *mut c_void) -> usize;
905 fn whiteout_hostimpl_test_WorkerPool_waitIdle(handle: *mut c_void);
906 fn whiteout_hostimpl_test_WorkerPool_submitIncrementSentinel(
907 handle: *mut c_void,
908 out_sentinel: *mut i32,
909 );
910}
911
912pub struct HostWorkerPool {
914 handle: *mut c_void,
915 userdata: *mut Box<dyn WorkerPool>,
916}
917
918impl HostWorkerPool {
919 pub fn new<P: WorkerPool + 'static>(pool: P) -> Self {
920 let boxed: Box<dyn WorkerPool> = Box::new(pool);
921 let userdata = Box::into_raw(Box::new(boxed));
922 let table = WorkerPoolFnTable {
923 submit: pool_submit,
924 wait_idle: pool_wait_idle,
925 thread_count: pool_thread_count,
926 };
927 let handle =
929 unsafe { whiteout_hostimpl_WorkerPool_create(userdata as *mut c_void, &table) };
930 HostWorkerPool { handle, userdata }
931 }
932
933 pub fn as_ptr(&self) -> *mut c_void {
935 self.handle
936 }
937
938 pub fn thread_count_through_native(&self) -> usize {
939 unsafe { whiteout_hostimpl_test_WorkerPool_threadCount(self.handle) }
941 }
942
943 pub fn wait_idle_through_native(&self) {
944 unsafe { whiteout_hostimpl_test_WorkerPool_waitIdle(self.handle) }
946 }
947
948 pub fn submit_sentinel_through_native(&self, sentinel: &mut i32) {
954 unsafe {
957 whiteout_hostimpl_test_WorkerPool_submitIncrementSentinel(self.handle, sentinel);
958 }
959 }
960}
961
962impl Drop for HostWorkerPool {
963 fn drop(&mut self) {
964 unsafe {
966 whiteout_hostimpl_WorkerPool_delete(self.handle);
967 drop(Box::from_raw(self.userdata));
968 }
969 }
970}
971
972impl core::fmt::Debug for HostWorkerPool {
973 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
974 f.debug_struct("HostWorkerPool").finish_non_exhaustive()
975 }
976}
977
978unsafe impl Send for HostWorkerPool {}
980unsafe impl Sync for HostWorkerPool {}