195 lines
5.8 KiB
Rust
195 lines
5.8 KiB
Rust
// Copyright 2013 The Servo Project Developers. See the COPYRIGHT
|
|
// file at the top-level directory of this distribution.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
|
// option. This file may not be copied, modified, or distributed
|
|
// except according to those terms.
|
|
|
|
pub use core_foundation_sys::filedescriptor::*;
|
|
|
|
use core_foundation_sys::base::{Boolean, CFIndex};
|
|
use core_foundation_sys::base::{kCFAllocatorDefault, CFOptionFlags};
|
|
|
|
use base::TCFType;
|
|
use runloop::CFRunLoopSource;
|
|
|
|
use std::mem::MaybeUninit;
|
|
use std::os::unix::io::{AsRawFd, RawFd};
|
|
use std::ptr;
|
|
|
|
declare_TCFType!{
|
|
CFFileDescriptor, CFFileDescriptorRef
|
|
}
|
|
impl_TCFType!(CFFileDescriptor, CFFileDescriptorRef, CFFileDescriptorGetTypeID);
|
|
|
|
impl CFFileDescriptor {
|
|
pub fn new(fd: RawFd,
|
|
closeOnInvalidate: bool,
|
|
callout: CFFileDescriptorCallBack,
|
|
context: Option<&CFFileDescriptorContext>) -> Option<CFFileDescriptor> {
|
|
let context = context.map_or(ptr::null(), |c| c as *const _);
|
|
unsafe {
|
|
let fd_ref = CFFileDescriptorCreate(kCFAllocatorDefault,
|
|
fd,
|
|
closeOnInvalidate as Boolean,
|
|
callout,
|
|
context);
|
|
if fd_ref.is_null() {
|
|
None
|
|
} else {
|
|
Some(TCFType::wrap_under_create_rule(fd_ref))
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn context(&self) -> CFFileDescriptorContext {
|
|
unsafe {
|
|
let mut context = MaybeUninit::<CFFileDescriptorContext>::uninit();
|
|
CFFileDescriptorGetContext(self.0, context.as_mut_ptr());
|
|
context.assume_init()
|
|
}
|
|
}
|
|
|
|
pub fn enable_callbacks(&self, callback_types: CFOptionFlags) {
|
|
unsafe {
|
|
CFFileDescriptorEnableCallBacks(self.0, callback_types)
|
|
}
|
|
}
|
|
|
|
pub fn disable_callbacks(&self, callback_types: CFOptionFlags) {
|
|
unsafe {
|
|
CFFileDescriptorDisableCallBacks(self.0, callback_types)
|
|
}
|
|
}
|
|
|
|
pub fn valid(&self) -> bool {
|
|
unsafe {
|
|
CFFileDescriptorIsValid(self.0) != 0
|
|
}
|
|
}
|
|
|
|
pub fn invalidate(&self) {
|
|
unsafe {
|
|
CFFileDescriptorInvalidate(self.0)
|
|
}
|
|
}
|
|
|
|
pub fn to_run_loop_source(&self, order: CFIndex) -> Option<CFRunLoopSource> {
|
|
unsafe {
|
|
let source_ref = CFFileDescriptorCreateRunLoopSource(
|
|
kCFAllocatorDefault,
|
|
self.0,
|
|
order
|
|
);
|
|
if source_ref.is_null() {
|
|
None
|
|
} else {
|
|
Some(TCFType::wrap_under_create_rule(source_ref))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
impl AsRawFd for CFFileDescriptor {
|
|
fn as_raw_fd(&self) -> RawFd {
|
|
unsafe {
|
|
CFFileDescriptorGetNativeDescriptor(self.0)
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
#[cfg(test)]
|
|
mod test {
|
|
extern crate libc;
|
|
|
|
use super::*;
|
|
use std::ffi::CString;
|
|
use std::os::raw::c_void;
|
|
use core_foundation_sys::base::{CFOptionFlags};
|
|
use core_foundation_sys::runloop::{kCFRunLoopDefaultMode};
|
|
use libc::O_RDWR;
|
|
use runloop::{CFRunLoop};
|
|
|
|
#[test]
|
|
fn test_unconsumed() {
|
|
let path = CString::new("/dev/null").unwrap();
|
|
let raw_fd = unsafe { libc::open(path.as_ptr(), O_RDWR, 0) };
|
|
let cf_fd = CFFileDescriptor::new(raw_fd, false, never_callback, None);
|
|
assert!(cf_fd.is_some());
|
|
let cf_fd = cf_fd.unwrap();
|
|
|
|
assert!(cf_fd.valid());
|
|
cf_fd.invalidate();
|
|
assert!(!cf_fd.valid());
|
|
|
|
// close() should succeed
|
|
assert_eq!(unsafe { libc::close(raw_fd) }, 0);
|
|
}
|
|
|
|
extern "C" fn never_callback(_f: CFFileDescriptorRef,
|
|
_callback_types: CFOptionFlags,
|
|
_info_ptr: *mut c_void) {
|
|
unreachable!();
|
|
}
|
|
|
|
struct TestInfo {
|
|
value: CFOptionFlags
|
|
}
|
|
|
|
#[test]
|
|
fn test_callback() {
|
|
let mut info = TestInfo { value: 0 };
|
|
let context = CFFileDescriptorContext {
|
|
version: 0,
|
|
info: &mut info as *mut _ as *mut c_void,
|
|
retain: None,
|
|
release: None,
|
|
copyDescription: None
|
|
};
|
|
|
|
let path = CString::new("/dev/null").unwrap();
|
|
let raw_fd = unsafe { libc::open(path.as_ptr(), O_RDWR, 0) };
|
|
let cf_fd = CFFileDescriptor::new(raw_fd, true, callback, Some(&context));
|
|
assert!(cf_fd.is_some());
|
|
let cf_fd = cf_fd.unwrap();
|
|
|
|
assert!(cf_fd.valid());
|
|
|
|
let run_loop = CFRunLoop::get_current();
|
|
let source = CFRunLoopSource::from_file_descriptor(&cf_fd, 0);
|
|
assert!(source.is_some());
|
|
unsafe {
|
|
run_loop.add_source(&source.unwrap(), kCFRunLoopDefaultMode);
|
|
}
|
|
|
|
info.value = 0;
|
|
cf_fd.enable_callbacks(kCFFileDescriptorReadCallBack);
|
|
CFRunLoop::run_current();
|
|
assert_eq!(info.value, kCFFileDescriptorReadCallBack);
|
|
|
|
info.value = 0;
|
|
cf_fd.enable_callbacks(kCFFileDescriptorWriteCallBack);
|
|
CFRunLoop::run_current();
|
|
assert_eq!(info.value, kCFFileDescriptorWriteCallBack);
|
|
|
|
info.value = 0;
|
|
cf_fd.disable_callbacks(kCFFileDescriptorReadCallBack | kCFFileDescriptorWriteCallBack);
|
|
|
|
cf_fd.invalidate();
|
|
assert!(!cf_fd.valid());
|
|
}
|
|
|
|
extern "C" fn callback(_f: CFFileDescriptorRef, callback_types: CFOptionFlags, info_ptr: *mut c_void) {
|
|
assert!(!info_ptr.is_null());
|
|
|
|
let info: *mut TestInfo = info_ptr as *mut TestInfo;
|
|
|
|
unsafe { (*info).value = callback_types };
|
|
|
|
CFRunLoop::get_current().stop();
|
|
}
|
|
}
|