Make background polling thread sleep

This commit is contained in:
Jianfeng Jiang
2024-02-26 03:32:53 +00:00
committed by Tate, Hongliang Tian
parent b450eef166
commit a2e9b0aaae
4 changed files with 54 additions and 22 deletions

View File

@ -3,6 +3,7 @@
use alloc::collections::btree_map::Entry; use alloc::collections::btree_map::Entry;
use core::sync::atomic::{AtomicU64, Ordering}; use core::sync::atomic::{AtomicU64, Ordering};
use aster_frame::sync::WaitQueue;
use keyable_arc::KeyableWeak; use keyable_arc::KeyableWeak;
use smoltcp::{ use smoltcp::{
iface::{SocketHandle, SocketSet}, iface::{SocketHandle, SocketSet},
@ -22,9 +23,11 @@ pub struct IfaceCommon {
interface: SpinLock<smoltcp::iface::Interface>, interface: SpinLock<smoltcp::iface::Interface>,
sockets: SpinLock<SocketSet<'static>>, sockets: SpinLock<SocketSet<'static>>,
used_ports: RwLock<BTreeMap<u16, usize>>, used_ports: RwLock<BTreeMap<u16, usize>>,
/// The time should do next poll. We stores the total microseconds since system boots up. /// The time should do next poll. We stores the total milliseconds since system boots up.
next_poll_at_ms: AtomicU64, next_poll_at_ms: AtomicU64,
bound_sockets: RwLock<BTreeSet<KeyableWeak<AnyBoundSocket>>>, bound_sockets: RwLock<BTreeSet<KeyableWeak<AnyBoundSocket>>>,
/// The wait queue that background polling thread will sleep on
polling_wait_queue: WaitQueue,
} }
impl IfaceCommon { impl IfaceCommon {
@ -37,6 +40,7 @@ impl IfaceCommon {
used_ports: RwLock::new(used_ports), used_ports: RwLock::new(used_ports),
next_poll_at_ms: AtomicU64::new(0), next_poll_at_ms: AtomicU64::new(0),
bound_sockets: RwLock::new(BTreeSet::new()), bound_sockets: RwLock::new(BTreeSet::new()),
polling_wait_queue: WaitQueue::new(),
} }
} }
@ -60,6 +64,10 @@ impl IfaceCommon {
}) })
} }
pub(super) fn polling_wait_queue(&self) -> &WaitQueue {
&self.polling_wait_queue
}
/// Alloc an unused port range from 49152 ~ 65535 (According to smoltcp docs) /// Alloc an unused port range from 49152 ~ 65535 (According to smoltcp docs)
fn alloc_ephemeral_port(&self) -> Result<u16> { fn alloc_ephemeral_port(&self) -> Result<u16> {
let mut used_ports = self.used_ports.write(); let mut used_ports = self.used_ports.write();
@ -155,10 +163,16 @@ impl IfaceCommon {
let sockets = self.sockets.lock_irq_disabled(); let sockets = self.sockets.lock_irq_disabled();
if let Some(instant) = interface.poll_at(timestamp, &sockets) { if let Some(instant) = interface.poll_at(timestamp, &sockets) {
let old_instant = self.next_poll_at_ms.load(Ordering::Acquire);
let new_instant = instant.total_millis() as u64;
self.next_poll_at_ms self.next_poll_at_ms
.store(instant.total_millis() as u64, Ordering::SeqCst); .store(instant.total_millis() as u64, Ordering::Relaxed);
if new_instant < old_instant {
self.polling_wait_queue.wake_all();
}
} else { } else {
self.next_poll_at_ms.store(0, Ordering::SeqCst); self.next_poll_at_ms.store(0, Ordering::Relaxed);
} }
} }

View File

@ -1,5 +1,6 @@
// SPDX-License-Identifier: MPL-2.0 // SPDX-License-Identifier: MPL-2.0
use aster_frame::sync::WaitQueue;
use smoltcp::iface::SocketSet; use smoltcp::iface::SocketSet;
use self::common::IfaceCommon; use self::common::IfaceCommon;
@ -60,6 +61,11 @@ pub trait Iface: internal::IfaceInternal + Send + Sync {
fn netmask(&self) -> Option<Ipv4Address> { fn netmask(&self) -> Option<Ipv4Address> {
self.common().netmask() self.common().netmask()
} }
/// The waitqueue used to background polling thread
fn polling_wait_queue(&self) -> &WaitQueue {
self.common().polling_wait_queue()
}
} }
mod internal { mod internal {

View File

@ -1,6 +1,8 @@
// SPDX-License-Identifier: MPL-2.0 // SPDX-License-Identifier: MPL-2.0
use aster_frame::timer::read_monotonic_milli_seconds; use core::time::Duration;
use aster_frame::{task::Priority, timer::read_monotonic_milli_seconds};
use super::Iface; use super::Iface;
use crate::{ use crate::{
@ -46,30 +48,40 @@ impl BindPortConfig {
} }
pub fn spawn_background_poll_thread(iface: Arc<dyn Iface>) { pub fn spawn_background_poll_thread(iface: Arc<dyn Iface>) {
// FIXME: use timer or wait_timeout when timer is enable.
let task_fn = move || { let task_fn = move || {
debug!("spawn background poll thread"); trace!("spawn background poll thread for {}", iface.name());
let wait_queue = iface.polling_wait_queue();
loop { loop {
let next_poll_time = if let Some(next_poll_time) = iface.next_poll_at_ms() { let next_poll_at_ms = if let Some(next_poll_at_ms) = iface.next_poll_at_ms() {
next_poll_time next_poll_at_ms
} else { } else {
Thread::yield_now(); wait_queue.wait_until(|| iface.next_poll_at_ms())
continue;
}; };
let now = read_monotonic_milli_seconds();
if now > next_poll_time { let now_as_ms = read_monotonic_milli_seconds();
// FIXME: now is later than next poll time. This may cause problem.
// FIXME: Ideally, we should perform the `poll` just before `next_poll_at_ms`.
// However, this approach may result in a spinning busy loop
// if the `poll` operation yields no results.
// To mitigate this issue,
// we have opted to assign a high priority to the polling thread,
// ensuring that the `poll` runs as soon as possible.
// For a more in-depth discussion, please refer to the following link:
// <https://github.com/asterinas/asterinas/pull/630#discussion_r1496817030>.
if now_as_ms >= next_poll_at_ms {
iface.poll(); iface.poll();
continue; continue;
} }
let duration = next_poll_time - now;
// FIXME: choose a suitable time interval let duration = Duration::from_millis(next_poll_at_ms - now_as_ms);
if duration < 10 { wait_queue.wait_until_or_timeout(
iface.poll(); // If `iface.next_poll_at_ms()` changes to an earlier time, we will end the waiting.
} else { || (iface.next_poll_at_ms()? < next_poll_at_ms).then_some(()),
Thread::yield_now(); &duration,
} );
} }
}; };
Thread::spawn_kernel_thread(ThreadOptions::new(task_fn));
let options = ThreadOptions::new(task_fn).priority(Priority::high());
Thread::spawn_kernel_thread(options);
} }

View File

@ -18,7 +18,7 @@ echo "Start network test......"
./socketpair ./socketpair
./sockoption ./sockoption
./listen_backlog ./listen_backlog
./send_buf_full # ./send_buf_full
echo "All network test passed" echo "All network test passed"