core_foundation/
filedescriptor.rs1pub use core_foundation_sys::filedescriptor::*;
11
12use core_foundation_sys::base::{kCFAllocatorDefault, CFOptionFlags};
13use core_foundation_sys::base::{Boolean, CFIndex};
14
15use crate::base::TCFType;
16use crate::runloop::CFRunLoopSource;
17
18use std::mem::MaybeUninit;
19use std::os::unix::io::{AsRawFd, RawFd};
20use std::ptr;
21
22declare_TCFType! {
23 CFFileDescriptor, CFFileDescriptorRef
24}
25impl_TCFType!(
26 CFFileDescriptor,
27 CFFileDescriptorRef,
28 CFFileDescriptorGetTypeID
29);
30
31impl CFFileDescriptor {
32 pub fn new(
33 fd: RawFd,
34 closeOnInvalidate: bool,
35 callout: CFFileDescriptorCallBack,
36 context: Option<&CFFileDescriptorContext>,
37 ) -> Option<CFFileDescriptor> {
38 let context = context.map_or(ptr::null(), |c| c as *const _);
39 unsafe {
40 let fd_ref = CFFileDescriptorCreate(
41 kCFAllocatorDefault,
42 fd,
43 closeOnInvalidate as Boolean,
44 callout,
45 context,
46 );
47 if fd_ref.is_null() {
48 None
49 } else {
50 Some(TCFType::wrap_under_create_rule(fd_ref))
51 }
52 }
53 }
54
55 pub fn context(&self) -> CFFileDescriptorContext {
56 unsafe {
57 let mut context = MaybeUninit::<CFFileDescriptorContext>::uninit();
58 CFFileDescriptorGetContext(self.0, context.as_mut_ptr());
59 context.assume_init()
60 }
61 }
62
63 pub fn enable_callbacks(&self, callback_types: CFOptionFlags) {
64 unsafe { CFFileDescriptorEnableCallBacks(self.0, callback_types) }
65 }
66
67 pub fn disable_callbacks(&self, callback_types: CFOptionFlags) {
68 unsafe { CFFileDescriptorDisableCallBacks(self.0, callback_types) }
69 }
70
71 pub fn valid(&self) -> bool {
72 unsafe { CFFileDescriptorIsValid(self.0) != 0 }
73 }
74
75 pub fn invalidate(&self) {
76 unsafe { CFFileDescriptorInvalidate(self.0) }
77 }
78
79 pub fn to_run_loop_source(&self, order: CFIndex) -> Option<CFRunLoopSource> {
80 unsafe {
81 let source_ref =
82 CFFileDescriptorCreateRunLoopSource(kCFAllocatorDefault, self.0, order);
83 if source_ref.is_null() {
84 None
85 } else {
86 Some(TCFType::wrap_under_create_rule(source_ref))
87 }
88 }
89 }
90}
91
92impl AsRawFd for CFFileDescriptor {
93 fn as_raw_fd(&self) -> RawFd {
94 unsafe { CFFileDescriptorGetNativeDescriptor(self.0) }
95 }
96}
97
98#[cfg(test)]
99mod test {
100 use super::*;
101 use crate::runloop::CFRunLoop;
102 use core_foundation_sys::base::CFOptionFlags;
103 use core_foundation_sys::runloop::kCFRunLoopDefaultMode;
104 use libc::O_RDWR;
105 use std::ffi::CString;
106 use std::os::raw::c_void;
107
108 #[test]
109 fn test_unconsumed() {
110 let path = CString::new("/dev/null").unwrap();
111 let raw_fd = unsafe { libc::open(path.as_ptr(), O_RDWR, 0) };
112 let cf_fd = CFFileDescriptor::new(raw_fd, false, never_callback, None);
113 assert!(cf_fd.is_some());
114 let cf_fd = cf_fd.unwrap();
115
116 assert!(cf_fd.valid());
117 cf_fd.invalidate();
118 assert!(!cf_fd.valid());
119
120 assert_eq!(unsafe { libc::close(raw_fd) }, 0);
122 }
123
124 extern "C" fn never_callback(
125 _f: CFFileDescriptorRef,
126 _callback_types: CFOptionFlags,
127 _info_ptr: *mut c_void,
128 ) {
129 unreachable!();
130 }
131
132 struct TestInfo {
133 value: CFOptionFlags,
134 }
135
136 #[test]
137 fn test_callback() {
138 let mut info = TestInfo { value: 0 };
139 let context = CFFileDescriptorContext {
140 version: 0,
141 info: &mut info as *mut _ as *mut c_void,
142 retain: None,
143 release: None,
144 copyDescription: None,
145 };
146
147 let path = CString::new("/dev/null").unwrap();
148 let raw_fd = unsafe { libc::open(path.as_ptr(), O_RDWR, 0) };
149 let cf_fd = CFFileDescriptor::new(raw_fd, true, callback, Some(&context));
150 assert!(cf_fd.is_some());
151 let cf_fd = cf_fd.unwrap();
152
153 assert!(cf_fd.valid());
154
155 let run_loop = CFRunLoop::get_current();
156 let source = CFRunLoopSource::from_file_descriptor(&cf_fd, 0);
157 assert!(source.is_some());
158 unsafe {
159 run_loop.add_source(&source.unwrap(), kCFRunLoopDefaultMode);
160 }
161
162 info.value = 0;
163 cf_fd.enable_callbacks(kCFFileDescriptorReadCallBack);
164 CFRunLoop::run_current();
165 assert_eq!(info.value, kCFFileDescriptorReadCallBack);
166
167 info.value = 0;
168 cf_fd.enable_callbacks(kCFFileDescriptorWriteCallBack);
169 CFRunLoop::run_current();
170 assert_eq!(info.value, kCFFileDescriptorWriteCallBack);
171
172 info.value = 0;
173 cf_fd.disable_callbacks(kCFFileDescriptorReadCallBack | kCFFileDescriptorWriteCallBack);
174
175 cf_fd.invalidate();
176 assert!(!cf_fd.valid());
177 }
178
179 extern "C" fn callback(
180 _f: CFFileDescriptorRef,
181 callback_types: CFOptionFlags,
182 info_ptr: *mut c_void,
183 ) {
184 assert!(!info_ptr.is_null());
185
186 let info: *mut TestInfo = info_ptr as *mut TestInfo;
187
188 unsafe { (*info).value = callback_types };
189
190 CFRunLoop::get_current().stop();
191 }
192}