Why doesn't the memory seem to be released?

I want ot build a C shared library with rust. My codes are like these:

use std;
use std::alloc::{dealloc, Layout};
use libc::c_void;

struct TestState {
    mode: i8,
}

#[no_mangle]
pub extern fn skynet_test_create() -> *mut c_void {
    let inst = Box::new(TestState { mode: 1, });
    return Box::into_raw(inst) as *mut c_void;
}

#[no_mangle]
pub extern fn skynet_test_release(ptr: *mut c_void) {
    unsafe {
        std::ptr::drop_in_place(ptr);
        dealloc(ptr as *mut u8, Layout::new::<String>());
    }
    println!("State released ------>>");
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::{thread, time};

    #[test]
    fn it_works() {
        let ptr = skynet_test_create();
        skynet_test_release(ptr);
        let second = time::Duration::from_secs(1);
        thread::sleep(second);
        unsafe {
            let st = ptr as *mut TestState;
            println!("mode is {}", st.read().mode);
            let state = Box::from_raw(ptr as *mut TestState);
            println!("mode is {}", state.mode);
        }
        println!("test again");
        thread::sleep(second);
        unsafe {
            let state = Box::from_raw(ptr as *mut TestState);
            println!("mod is {}", state.mode);
        }
        assert_eq!(2 + 2, 4);
    }
}

When I run Cargo test -- --nocapture, I got these output:

running 1 test
mode released ------>>
mode is 1
mode is 1
skynet_test-0ebc32eff4488211(1606,0x70000ac5b000) malloc: *** error for object 0x7f877bd04250: pointer being freed was not allocated
skynet_test-0ebc32eff4488211(1606,0x70000ac5b000) malloc: *** set a breakpoint in malloc_error_break to debug
error: test failed, to rerun pass '--lib'

I wanna malloc a memory by calling skynet_test_create() with C, and free the memory by calling skynet_test_release(ptr). But in my test codes, I had called the the skynet_test_release(ptr) function, I think the memory should be cleared. If so, these test codes:

        unsafe {
            let st = ptr as *mut TestState;
            println!("mode is {}", st.read().mode);
            let state = Box::from_raw(ptr as *mut TestState);
            println!("mode is {}", state.mode);
        }

The codes above should be invalid. But the test result

mode is 1
mode is 1

shows that they are executed correctly. The first question is why the memory seems not be released after calling skynet_test_release(ptr).

And then, the test case gives me these error:

skynet_test-0ebc32eff4488211(1606,0x70000ac5b000) malloc: *** error for object 0x7f877bd04250: pointer being freed was not allocated
skynet_test-0ebc32eff4488211(1606,0x70000ac5b000) malloc: *** set a breakpoint in malloc_error_break to debug

The second question is what is the cause of the crash?

Can someone give me advice? Thanks.

You might want to use Box::<TestState>::from_raw(ptr) here to mirror the into_raw() call earlier. That will reconstruct the box you destroyed and then drop it normally.


Deallocation doesn’t normally clear the memory, it just adds it to the allocator’s free list. Because no other allocations are performed between the free and rereading the pointer, the old data is still available (though you shouldn’t rely on this ever).

This crash is due to you double-freeing the memory: the first time inside skynet_test_release and the second when the state variable goes out of scope and its Box is deconstructed.

2 Likes

Check out the example code in the docs. You don't need to manually convert to and from pointers at all since Box really is a pointer already. As described there, the Option for the delete function is to make it accept a null pointer, too. (I guess this is not too useful, actually, so you maybe don't need that Option around the argument.)

2 Likes

If I implement the skynet_test_release function like this, is it right?

Yes; dropping the Box will deallocate the memory for the value it points to. Also, the drop(...) is unnecessary but harmless: temporary values get automatically dropped at the end of the statement.

Thanks for your help!

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.