Help needed to debug tokio thread/async model

I am facing a strange problem when spwaning threads in the rust code. After spawning a thread for processing, the code binds to a UDS and accepts connections.

This code prints the "Bind Successful " message but just hangs without accepting connection from the UDS.

If the code for spawning the thread is commented out, the code works, prints the BIND successful message and if there are connections from the client, accepts and processes it.

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
        // initializations

        let mut time_old = Instant::now();
        tokio::spawn(async move{
            loop {
                  let time_now = Instant::now();
                  match time_now.checked_duration_since(time_old)
                   {
                        time_old = Instant::now();
                        // process code here


                        
                  };
          }
    });


// more processing here

let mut listener_res =  UnixListener::bind(&socket);
        if listener_res.is_err() {
            // Socket is being used by somebody else 
        }
        match listener_res {
            Ok(listener)=>{
                info!("Bind successful{}!",socket.display());
                
                match listener.accept().await {  // **The code hangs here ** 
                	Ok(listener)=>{
                	       // Process this code
                	} 
                	Err => {
                		// Error on socket 
                	}
                }
           }
        }

Not able to pointout what is wrong in the thread model. Are there any ways to debug tokio/async code? Any help will be appreciated.

(I modified your post to properly format the code as a code-block.)

Well, for some types of issues, you can use tokio-console. Your code doesn't really have enough detail for me to offer further help.

I can see a couple of things that might cause what you're seeing.

The first issue is with the loop in tokio::spawn. It doesn't actually compile as written, but if it did compile, and there are no await statements in there, you don't give the async runtime a chance to execute any other code. The reason is that tokio::spawn does not produce a thread. It produces an async task which runs cooperatively in a thread with other tasks. Thus, this task needs to yield to other tasks with await or it will hog the cpu. Check the cpu usage while you run this program - it should be at 100% if you have a busy loop in there.

The second issue is that you might need to remove the socket file each time before calling UnixListener::bind. If the file is not removed, you get an "address in use" error, which is ignored in your code.

As for debugging, start by sprinkling println! in key places in your code to get feedback about which path it's taking. You could replace most of the comments in the above code with println! statements. On a bigger project I'll usually end up using the log crate so I can decouple logging concerns from the specific terminal window that stdout is printing to.

4 Likes

Oh, are you using checked_duration_since in a loop to sleep? Yeah, definitely don't do that. Please read this.