简体   繁体   中英

Piston application crashes after a few minutes with a memory allocation error

I've developed a maze game using Piston in Rust in order to teach myself graphics/UI programming. The game mostly works fine, but when I run it with a large maze (eg 120 x 72 rectangles), the game crashes with a memory allocation error after a couple minutes.

A reduced example is as follows:

extern crate graphics;
extern crate opengl_graphics;
extern crate piston;
extern crate piston_window;

use opengl_graphics::OpenGL;
use piston::event_loop::*;
use piston::input::*;
use piston_window::{PistonWindow, WindowSettings};

const BLACK: [f32; 4] = [0.0, 0.0, 0.0, 1.0];
const WHITE: [f32; 4] = [1.0, 1.0, 1.0, 1.0];

fn main() {
    let opengl = OpenGL::V3_2;

    let mut window: PistonWindow = WindowSettings::new("maze", [800, 600])
        .opengl(opengl)
        .exit_on_esc(true)
        .build()
        .unwrap();

    let mut events = Events::new(EventSettings::new());

    while let Some(event) = events.next(&mut window) {
        if let Some(_args) = event.render_args() {
            window.draw_2d(&event, |c, gl| {
                graphics::clear(BLACK, gl);

                for _row in 0..72 {
                    for _col in 0..120 {
                        let color = WHITE;
                        let box_rect =
                            graphics::rectangle::rectangle_by_corners(0.0, 0.0, 10.0, 10.0);
                        graphics::rectangle(color, box_rect, c.transform, gl);
                    }
                }
            });
        }
    }
}

When I run this, I get a memory allocation error, followed by a process abort:

$ time cargo run --release
   Compiling maze v0.1.0 (/home/isaac/prog/rust/test-maze)
    Finished release [optimized] target(s) in 5.28s
     Running `target/release/maze`
memory allocation of 7927496704 bytes failedAborted (core dumped)

real    2m24.317s
user    1m29.515s
sys     0m3.644s

Running it in the debugger, I get the following backtrace:

(gdb) backtrace
#0  __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:51
#1  0x00007ffff71e1801 in __GI_abort () at abort.c:79
#2  0x0000555555708fc7 in std::sys::unix::abort_internal () at src/libstd/sys/unix/mod.rs:157
#3  0x000055555570419d in rust_oom () at src/libstd/alloc.rs:211
#4  0x0000555555717f37 in alloc::alloc::handle_alloc_error () at src/liballoc/alloc.rs:224
#5  0x00005555556eaf7d in <alloc::vec::Vec<T> as alloc::vec::SpecExtend<T, I>>::spec_extend ()
#6  0x00005555556e64aa in <gfx_core::handle::Manager<R>>::extend ()
#7  0x00005555556da204 in <gfx_device_gl::Device as gfx_core::Device>::pin_submitted_resources ()
#8  0x000055555559d654 in <gfx::encoder::Encoder<R, C>>::flush ()
#9  0x000055555558f8af in maze::main ()
#10 0x000055555558bb73 in std::rt::lang_start::{{closure}} ()
#11 0x0000555555704913 in std::rt::lang_start_internal::{{closure}} () at src/libstd/rt.rs:49
#12 std::panicking::try::do_call () at src/libstd/panicking.rs:297
#13 0x000055555570927a in __rust_maybe_catch_panic () at src/libpanic_unwind/lib.rs:92
#14 0x0000555555705466 in std::panicking::try () at src/libstd/panicking.rs:276
#15 std::panic::catch_unwind () at src/libstd/panic.rs:388
#16 std::rt::lang_start_internal () at src/libstd/rt.rs:48
#17 0x000055555558fae2 in main ()

I am using Ubuntu Linux 18.04.

Should my program be written differently to prevent this problem? Is there something wrong with Piston?

The problem is located here:

let mut events = Events::new(EventSettings::new());

while let Some(event) = events.next(&mut window) {

Instead, one should use this:

while let Some(event) = window.next() {

It turns out that window.next() runs some cleanup steps that events.next(&mut window) omits, and these cleanup steps free the relevant memory. As far as I can tell, the documentation makes no mention of this, and the examples use both patterns without indicating the difference.

This cleanup is mentioned in the comments here , as was pointed out by Tiriosaurus on reddit .

That's about an 8 GB allocation it's doing; way too large to be reasonable. 8 GB is probably related to your system RAM. One half, equal or double to it, depending on your system setup.

The problem has to be in the loop and it has to be small. The loop isn't very big, is it? Which is the nice thing about small examples. It looks like it was the event code. I have no idea why.

Here's a version that does not appear to have a memory leak:

extern crate graphics;
extern crate opengl_graphics;
extern crate piston;
extern crate piston_window;

use opengl_graphics::OpenGL;
use piston::event_loop::*;
use piston::input::*;
use piston_window::{PistonWindow, WindowSettings};

const BLACK: [f32; 4] = [0.0, 0.0, 0.0, 1.0];
const WHITE: [f32; 4] = [1.0, 1.0, 1.0, 1.0];

fn main() {
    let opengl = OpenGL::V3_2;

    let mut window: PistonWindow = WindowSettings::new("maze", [800, 600])
        .opengl(opengl)
        .exit_on_esc(true)
        .build()
        .unwrap();

    while let Some(event) = window.next() {
        window.draw_2d(&event, |c, gl| {
            graphics::clear(BLACK, gl);

            for _row in 0..72 {
                for _col in 0..120 {
                    let color = WHITE;
                    let box_rect =
                        graphics::rectangle::rectangle_by_corners(0.0, 0.0, 10.0, 10.0);
                    graphics::rectangle(color, box_rect, c.transform, gl);
                }
            }
        });
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM