Skip to content

Commit

Permalink
Support running NSRunLoop up until date
Browse files Browse the repository at this point in the history
Change-Id: Ief7a73aed752e105510b3a137905eb515aca4a5e
  • Loading branch information
ciciplusplus committed Jan 28, 2025
1 parent d7e3c9a commit 3f2bdf3
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 28 deletions.
17 changes: 8 additions & 9 deletions src/frameworks/core_foundation/cf_run_loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use crate::dyld::{export_c_func, ConstantExports, FunctionExports, HostConstant}
use crate::frameworks::core_foundation::time::CFTimeInterval;
use crate::frameworks::foundation::ns_run_loop::run_run_loop_single_iteration;
use crate::frameworks::foundation::ns_string;
use crate::objc::{msg, msg_class};
use crate::objc::{id, msg, msg_class};
use crate::Environment;

pub type CFRunLoopRef = super::CFTypeRef;
Expand All @@ -39,14 +39,13 @@ fn CFRunLoopRunInMode(
msg![env; mode isEqualToString:default_mode]
|| msg![env; mode isEqualToString:common_modes]
);
assert!(seconds <= 0.001);
// TODO: we're currently supporting only the main run loop
log_dbg!(
"TODO: properly implement CFRunLoopRunInMode [current thread {}], running a single iteration of the main run loop",
env.current_thread
);
let main_run_loop = CFRunLoopGetMain(env);
run_run_loop_single_iteration(env, main_run_loop);
let current_run_loop = CFRunLoopGetCurrent(env);
if seconds == 0.0 {
run_run_loop_single_iteration(env, current_run_loop);
} else {
let limit_date: id = msg_class![env; NSDate dateWithTimeIntervalSinceNow:seconds];
() = msg![env; current_run_loop runUntilDate:limit_date];
}
1 // kCFRunLoopRunFinished
}

Expand Down
62 changes: 43 additions & 19 deletions src/frameworks/foundation/ns_run_loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
//! Resources:
//! - Apple's [Threading Programming Guide](https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Multithreading/Introduction/Introduction.html)
use super::{ns_string, ns_timer, NSComparisonResult, NSOrderedAscending};
use super::{ns_string, ns_timer, NSTimeInterval};
use crate::dyld::{ConstantExports, HostConstant};
use crate::environment::ThreadId;
use crate::frameworks::audio_toolbox::audio_queue::{handle_audio_queue, AudioQueueRef};
Expand All @@ -18,9 +18,9 @@ use crate::frameworks::core_foundation::cf_run_loop::{
};
use crate::frameworks::{core_animation, media_player, uikit};
use crate::objc::{id, msg, objc_classes, release, retain, Class, ClassExports, HostObject};
use crate::{msg_class, Environment};
use crate::Environment;
use std::collections::HashMap;
use std::time::{Duration, Instant};
use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};

/// `NSString*`
pub type NSRunLoopMode = id;
Expand Down Expand Up @@ -107,18 +107,14 @@ pub const CLASSES: ClassExports = objc_classes! {
}

- (())run {
run_run_loop(env, this, /* single_iteration: */ false);
run_run_loop(env, this, /* single_iteration: */ false, None);
}
- (())runUntilDate:(id)date { // NSDate *
let now: id = msg_class![env; NSDate date];
let comp: NSComparisonResult = msg![env; date compare:now];
if comp == NSOrderedAscending {
// Limit date is in the past, run loop once and return
run_run_loop(env, this, /* single_iteration: */ true);
return;
}
todo!("Properly account the limit date")

- (())runUntilDate:(id)date {
let time_limit: NSTimeInterval = msg![env; date timeIntervalSince1970];
run_run_loop(env, this, /* single_iteration: */ false, Some(time_limit));
}

// TODO: other run methods

@end
Expand Down Expand Up @@ -185,17 +181,30 @@ pub(super) fn remove_timer(env: &mut Environment, run_loop: id, timer: id) {
}

/// Run the run loop for just a single iteration. This is a special mode just
/// for the app picker, since we don't have `runMode:beforeDate:` or
/// `runUntilDate:` yet. (TODO: implement those to replace this.)
/// for the app picker, since we don't have `runMode:beforeDate:` yet.
/// (TODO: implement those to replace this.)
pub fn run_run_loop_single_iteration(env: &mut Environment, run_loop: id) {
run_run_loop(env, run_loop, /* single_iteration: */ true)
run_run_loop(env, run_loop, /* single_iteration: */ true, None)
}

fn run_run_loop(env: &mut Environment, run_loop: id, single_iteration: bool) {
pub fn run_run_loop(
env: &mut Environment,
run_loop: id,
single_iteration: bool,
unix_time_limit: Option<f64>,
) {
if single_iteration {
log_dbg!("Entering run loop {:?} (single iteration)", run_loop);
log_dbg!(
"Entering run loop {:?} (single iteration), limit {:?}",
run_loop,
unix_time_limit
);
} else {
log_dbg!("Entering run loop {:?} (indefinitely)", run_loop);
log_dbg!(
"Entering run loop {:?} (indefinitely), limit {:?}",
run_loop,
unix_time_limit
);
}

if env.objc.borrow::<NSRunLoopHostObject>(run_loop).is_running {
Expand Down Expand Up @@ -311,6 +320,21 @@ fn run_run_loop(env: &mut Environment, run_loop: id, single_iteration: bool) {
if single_iteration {
break;
}

if let Some(limit) = unix_time_limit {
// We use Unix epoch as a convenience reference date.
// (Apple's epoch is less convenient in Rust. And "pure"
// Rust approach with Duration/Instant is just too troublesome
// and not worthy to convert back and forth)
if SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_secs_f64()
>= limit
{
break;
}
}
}

env.objc
Expand Down

0 comments on commit 3f2bdf3

Please sign in to comment.