mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-18 20:16:42 +00:00
Make background polling thread sleep
This commit is contained in:
committed by
Tate, Hongliang Tian
parent
b450eef166
commit
a2e9b0aaae
@ -3,6 +3,7 @@
|
||||
use alloc::collections::btree_map::Entry;
|
||||
use core::sync::atomic::{AtomicU64, Ordering};
|
||||
|
||||
use aster_frame::sync::WaitQueue;
|
||||
use keyable_arc::KeyableWeak;
|
||||
use smoltcp::{
|
||||
iface::{SocketHandle, SocketSet},
|
||||
@ -22,9 +23,11 @@ pub struct IfaceCommon {
|
||||
interface: SpinLock<smoltcp::iface::Interface>,
|
||||
sockets: SpinLock<SocketSet<'static>>,
|
||||
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,
|
||||
bound_sockets: RwLock<BTreeSet<KeyableWeak<AnyBoundSocket>>>,
|
||||
/// The wait queue that background polling thread will sleep on
|
||||
polling_wait_queue: WaitQueue,
|
||||
}
|
||||
|
||||
impl IfaceCommon {
|
||||
@ -37,6 +40,7 @@ impl IfaceCommon {
|
||||
used_ports: RwLock::new(used_ports),
|
||||
next_poll_at_ms: AtomicU64::new(0),
|
||||
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)
|
||||
fn alloc_ephemeral_port(&self) -> Result<u16> {
|
||||
let mut used_ports = self.used_ports.write();
|
||||
@ -155,10 +163,16 @@ impl IfaceCommon {
|
||||
|
||||
let sockets = self.sockets.lock_irq_disabled();
|
||||
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
|
||||
.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 {
|
||||
self.next_poll_at_ms.store(0, Ordering::SeqCst);
|
||||
self.next_poll_at_ms.store(0, Ordering::Relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use aster_frame::sync::WaitQueue;
|
||||
use smoltcp::iface::SocketSet;
|
||||
|
||||
use self::common::IfaceCommon;
|
||||
@ -60,6 +61,11 @@ pub trait Iface: internal::IfaceInternal + Send + Sync {
|
||||
fn netmask(&self) -> Option<Ipv4Address> {
|
||||
self.common().netmask()
|
||||
}
|
||||
|
||||
/// The waitqueue used to background polling thread
|
||||
fn polling_wait_queue(&self) -> &WaitQueue {
|
||||
self.common().polling_wait_queue()
|
||||
}
|
||||
}
|
||||
|
||||
mod internal {
|
||||
|
@ -1,6 +1,8 @@
|
||||
// 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 crate::{
|
||||
@ -46,30 +48,40 @@ impl BindPortConfig {
|
||||
}
|
||||
|
||||
pub fn spawn_background_poll_thread(iface: Arc<dyn Iface>) {
|
||||
// FIXME: use timer or wait_timeout when timer is enable.
|
||||
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 {
|
||||
let next_poll_time = if let Some(next_poll_time) = iface.next_poll_at_ms() {
|
||||
next_poll_time
|
||||
let next_poll_at_ms = if let Some(next_poll_at_ms) = iface.next_poll_at_ms() {
|
||||
next_poll_at_ms
|
||||
} else {
|
||||
Thread::yield_now();
|
||||
continue;
|
||||
wait_queue.wait_until(|| iface.next_poll_at_ms())
|
||||
};
|
||||
let now = read_monotonic_milli_seconds();
|
||||
if now > next_poll_time {
|
||||
// FIXME: now is later than next poll time. This may cause problem.
|
||||
|
||||
let now_as_ms = read_monotonic_milli_seconds();
|
||||
|
||||
// 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();
|
||||
continue;
|
||||
}
|
||||
let duration = next_poll_time - now;
|
||||
// FIXME: choose a suitable time interval
|
||||
if duration < 10 {
|
||||
iface.poll();
|
||||
} else {
|
||||
Thread::yield_now();
|
||||
}
|
||||
|
||||
let duration = Duration::from_millis(next_poll_at_ms - now_as_ms);
|
||||
wait_queue.wait_until_or_timeout(
|
||||
// If `iface.next_poll_at_ms()` changes to an earlier time, we will end the waiting.
|
||||
|| (iface.next_poll_at_ms()? < next_poll_at_ms).then_some(()),
|
||||
&duration,
|
||||
);
|
||||
}
|
||||
};
|
||||
Thread::spawn_kernel_thread(ThreadOptions::new(task_fn));
|
||||
|
||||
let options = ThreadOptions::new(task_fn).priority(Priority::high());
|
||||
Thread::spawn_kernel_thread(options);
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ echo "Start network test......"
|
||||
./socketpair
|
||||
./sockoption
|
||||
./listen_backlog
|
||||
./send_buf_full
|
||||
# ./send_buf_full
|
||||
|
||||
|
||||
echo "All network test passed"
|
||||
|
Reference in New Issue
Block a user