Possible memory leak with mem::swap in tokio routine

Viewed 60

The process will killed by OOM, it seems memory can't be released in somewhere, where does the problem arise? static, routine or mem::swap? Or should I solve it.

#[macro_use]
extern crate lazy_static;

use std::mem;
use std::sync::Arc;
use parking_lot::Mutex;

lazy_static! {
    pub static ref Buffer: Arc<Mutex<Vec<i64>>> = Arc::new(Mutex::new(Vec::with_capacity(100)));
}

pub async fn consume() {
    let mut lk = Buffer.lock();
    lk.clear();
    drop(lk);
}

#[tokio::main]
async fn main() {
    let mut vec: Vec<i64> = Vec::with_capacity(100);
    loop {
        vec.push(1);
        if vec.len() == 100 {
            let mut lk = Buffer.lock();
            mem::swap(&mut vec, &mut *lk);
            drop(lk);
            tokio::spawn(async move {
                consume().await
            });
        }
    }
}
1 Answers

I think this is what you expect this code to do (please correct me if this is wrong):

  1. vec is filled up to 100 elements and put into buffer.
  2. while it is being filled up again, the background task frees the buffer.
  3. rinse and repeat.

But consider what happens when your main loop managed to acquire the lock twice in a row: then the task didn't have time to clear the buffer and after the mem::swap call, the vec still has 100 elements. From this point on, vec.len() == 100 will never be true and your code simply appends to the vector in a loop. Note that this only needs to happen once and then your code never recovers.

The fix to this is to, instead of only trying to clear the vector when it has exactly 100 elements, do so whenever it has at least 100 elements:

if vec.len() >= 100 {
Related