mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-19 04:26:39 +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 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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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 {
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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"
|
||||||
|
Reference in New Issue
Block a user