Hi,
I'm confused about Tokio and shared state. Consider this code (note the use of &self. records
within the loop):
pub struct DnsServer {
port: u16,
records: HashMap<String, Ipv4Addr>,
}
impl DnsServer {
pub async fn new(port: u16) -> Result<Self> {
let config = DotLocalDNSConfig::new().await?;
Ok(Self {
port,
records: config.records,
})
}
pub async fn run(&self) -> Result<()> {
let addr = SocketAddr::from((Ipv4Addr::LOCALHOST, self.port));
let socket = mk_udp_socket(&addr).await?;
println!("Listening on: localhost:{}", self.port);
loop {
let mut req_buffer = BytePacketBuffer::new();
let (_len, peer) = socket.recv_from(&mut req_buffer.buf).await?;
let request = DnsPacket::from_buffer(&mut req_buffer).await?;
let mut response = lookup(&request, &self.records)?;
let mut res_buffer = BytePacketBuffer::new();
response.write(&mut res_buffer)?;
let pos = res_buffer.pos();
let data = res_buffer.get_range(0, pos)?;
let _ = socket.send_to(data, peer).await?;
}
}
}
This works fine when I do some simple queries (even repeatedly) but I'm wondering If there is a scenario where the future will switch to another thread and then it'll break? What I'm asking is if I can always trust the compiler to be right about types in async/tokio environment?
If not, do I have to wrap every DnsServer
field that is accessed from within the run loop with Arc
or maybe only the ones I need to mutate with Arc<Mutex/RWLock>
?
Thanks