Fighting with the borrow checker again

So I'm getting this error -- again:

error[E0716]: temporary value dropped while borrowed
   --> libk\src\nvme\mod.rs:688:38
    |
688 |        register_interrupt_handler(int, &move |_| {
    |   _____________________________________-^
    |  |_____________________________________|
    | ||
689 | ||     for (i, (qid, queue)) in QUEUES.lock().iter().enumerate() {
690 | ||     if *qid == id {
691 | ||     queue.enqueue(true).unwrap_or_else(|_| warn!("Queue {} lost interrupt", i));
692 | ||     }
693 | ||     }
694 | ||     });
    | ||     ^ - temporary value is freed at the end of this statement
    | ||_____|
    | |______creates a temporary which is freed while still in use
    |        cast requires that borrow lasts for `'static`

Now, the interesting thing to note is that QUEUES is a static declared using lazy_static. I've tried this same thing with iterators and it gives me the same message. I've tried it with and without move and no go either, and I'm really confused on what's creating the supposed 'temporary value'. I've tried cloning/copying those that are outside the loop (except for QUEUES because that's static) and it still complains. If its the iterators, I don't really understand why that's so much of an issue, because I'd be going from an iterator to a Filter to a for_each and then the iterator is fully consumed -- is that not right?
I've even tried doing what the explanation says and creating a temporary variable for the mutex guard, everything that's outside the closure... It still doesn't like it. So I'm not sure what to try now. The code is:

    let id = dev.unique_dev_id;
    let int = dev.int_line.clone();
    register_interrupt_handler(int, &move |_| {
    for (i, (qid, queue)) in QUEUES.lock().iter().enumerate() {
    if *qid == id {
    queue.enqueue(true).unwrap_or_else(|_| warn!("Queue {} lost interrupt", i));
    }
    }
    });

Why is there a "&" before move? I suspect that's the issue.

The temporary in question is not a QUEUES variable, it's the closure itself. Why do you need to take a reference to it?

1 Like

What's its function header? The error message suggests something like &'static dyn Fn(…), which sounds interesting, to say the least. In that case, you'd have to box and leak the closure

Box::leak(Box::new(move |_| …)

before passing it as an argument.

Also, is that the full error message? There are usually help and note messages below the error hinting at what to do.

1 Like

Yep, that's the full error message. The header for the function is:

pub type InterruptHandler = &'static (dyn Fn(InterruptStackFrameValue) + Send + Sync);

With the function signature being:

pub fn register_interrupt_handler(interrupt: u8, func: InterruptHandler) -> usize

So that's why I was taking a reference to it. Since I'm loading these functions into a static list of them, would it be better to pin them to ensure the compiler doesn't move them around? Something like:

pub type InterruptHandler = Pin<Box<&'static (dyn Fn(InterruptStackFrameValue) + Send + Sync)>>;

If I did that could I still call these? I'm storing them in a MiniVec<T> stored within a Mutex<T>.

Why not

pub type InterruptHandler = Box<dyn Fn(InterruptStackFrameValue) + Send + Sync>;

?

(You could put the boxing inside register_interrupt_handler so that the caller can just pass a plain closure)

1 Like

Yep, that worked. Ran into a little compiler error though (updating to see if that solvs it):

   Compiling libk v0.1.0 (C:\Users\ethin\source\kernel\libk)
error: could not compile `libk`

Caused by:
  process didn't exit successfully: `rustc --crate-name libk --edition=2018 libk\src\lib.rs --error-format=json --json=diagnostic-rendered-ansi --crate-type lib --emit=dep-info,metadata,link -C panic=abort -C embed-bitcode=no -C codegen-units=64 -C debuginfo=2 --cfg "feature=\"default\"" --cfg "feature=\"nvme\"" -C metadata=4bd721d67899f00e -C extra-filename=-4bd721d67899f00e --out-dir C:\Users\ethin\source\kernel\target\x86_64-kernel-none\debug\deps --target \\?\C:\Users\ethin\source\kernel\x86_64-kernel-none.json -C incremental=C:\Users\ethin\source\kernel\target\x86_64-kernel-none\debug\incremental -L dependency=C:\Users\ethin\source\kernel\target\x86_64-kernel-none\debug\deps -L dependency=C:\Users\ethin\source\kernel\target\debug\deps --extern acpi=C:\Users\ethin\source\kernel\target\x86_64-kernel-none\debug\deps\libacpi-efad077d7ec36226.rmeta --extern noprelude:alloc=C:\Users\ethin\source\kernel\target\x86_64-kernel-none\debug\deps\liballoc-d1865a98d5f9bf9b.rmeta --extern bit_field=C:\Users\ethin\source\kernel\target\x86_64-kernel-none\debug\deps\libbit_field-ccd2311a70e48359.rmeta --extern bitflags=C:\Users\ethin\source\kernel\target\x86_64-kernel-none\debug\deps\libbitflags-f8f0414bae7e2c27.rmeta --extern block_device=C:\Users\ethin\source\kernel\target\x86_64-kernel-none\debug\deps\libblock_device-8f431d64cb9b27ae.rmeta --extern bootloader=C:\Users\ethin\source\kernel\target\x86_64-kernel-none\debug\deps\libbootloader-d8abd844d46cf313.rmeta --extern byteorder=C:\Users\ethin\source\kernel\target\x86_64-kernel-none\debug\deps\libbyteorder-98be04c51d5e90c5.rmeta --extern noprelude:compiler_builtins=C:\Users\ethin\source\kernel\target\x86_64-kernel-none\debug\deps\libcompiler_builtins-66aa1306f28a7f7c.rmeta --extern noprelude:core=C:\Users\ethin\source\kernel\target\x86_64-kernel-none\debug\deps\libcore-af4f4e5bfcb0e59c.rmeta --extern cpuio=C:\Users\ethin\source\kernel\target\x86_64-kernel-none\debug\deps\libcpuio-9930b93ed144d1a7.rmeta --extern crossbeam_queue=C:\Users\ethin\source\kernel\target\x86_64-kernel-none\debug\deps\libcrossbeam_queue-580655f93cc08ae6.rmeta --extern dia_semver=C:\Users\ethin\source\kernel\target\x86_64-kernel-none\debug\deps\libdia_semver-32f575449794634d.rmeta --extern hashbrown=C:\Users\ethin\source\kernel\target\x86_64-kernel-none\debug\deps\libhashbrown-1faa84aff47a7e04.rmeta --extern heapless=C:\Users\ethin\source\kernel\target\x86_64-kernel-none\debug\deps\libheapless-eb3a0fee0db8a73a.rmeta --extern iced_x86=C:\Users\ethin\source\kernel\target\x86_64-kernel-none\debug\deps\libiced_x86-00d371cb78607e38.rmeta --extern lazy_static=C:\Users\ethin\source\kernel\target\x86_64-kernel-none\debug\deps\liblazy_static-a9074d75edc38795.rmeta --extern linked_list_allocator=C:\Users\ethin\source\kernel\target\x86_64-kernel-none\debug\deps\liblinked_list_allocator-1f6e0d5ee5401778.rmeta --extern log=C:\Users\ethin\source\kernel\target\x86_64-kernel-none\debug\deps\liblog-2a42d1cea634d96f.rmeta --extern minivec=C:\Users\ethin\source\kernel\target\x86_64-kernel-none\debug\deps\libminivec-eb14ee2455aa3a4d.rmeta --extern rand_core=C:\Users\ethin\source\kernel\target\x86_64-kernel-none\debug\deps\librand_core-a646e0b0ae446e1b.rmeta --extern rand_hc=C:\Users\ethin\source\kernel\target\x86_64-kernel-none\debug\deps\librand_hc-d83d139e6e9e690b.rmeta --extern raw_cpuid=C:\Users\ethin\source\kernel\target\x86_64-kernel-none\debug\deps\libraw_cpuid-ce2e9d9c9505cffc.rmeta --extern spin=C:\Users\ethin\source\kernel\target\x86_64-kernel-none\debug\deps\libspin-15c476d4161c24bd.rmeta --extern static_assertions=C:\Users\ethin\source\kernel\target\x86_64-kernel-none\debug\deps\libstatic_assertions-49fb235a8caf6bce.rmeta --extern voladdress=C:\Users\ethin\source\kernel\target\x86_64-kernel-none\debug\deps\libvoladdress-c2f18ee6b36f4e30.rmeta --extern x86=C:\Users\ethin\source\kernel\target\x86_64-kernel-none\debug\deps\libx86-a739ea20a63b10f2.rmeta --extern x86_64=C:\Users\ethin\source\kernel\target\x86_64-kernel-none\debug\deps\libx86_64-04da3eaf6e562b63.rmeta --extern zerocopy=C:\Users\ethin\source\kernel\target\x86_64-kernel-none\debug\deps\libzerocopy-7f9bd23a2a2d7923.rmeta -Z unstable-options` (exit code: 0xc0000409, STATUS_STACK_BUFFER_OVERRUN)

Edit: Okay, so it doesn't like that, apparently. I have a function for unregistering handlers when a driver is ready to be shut down:

pub fn unregister_interrupt_handler(int: u8, id: usize) -> bool {
    x86_64::instructions::interrupts::disable();
    debug!("Unregistering handler for int. {:X} (id {:X})", int, id);
    let mut tbl = IRQ_FUNCS.write();
    let irq = 32_u8.saturating_add(int);
    if let Some(funcs) = tbl.get_mut(&irq) {
        if funcs.len() >= id {
            let _ = funcs.remove(id);
        } else {
        x86_64::instructions::interrupts::enable();
            return false;
        }
    }
    x86_64::instructions::interrupts::enable();
    true
}

I've tried that, and this:

pub fn unregister_interrupt_handler(int: u8, id: usize) -> bool {
    x86_64::instructions::interrupts::disable();
    debug!("Unregistering handler for int. {:X} (id {:X})", int, id);
    let mut tbl = IRQ_FUNCS.write();
    let irq = 32_u8.saturating_add(int);
    if let Some(funcs) = tbl.get_mut(&irq) {
        if funcs.len() >= id {
            let b = funcs.remove(id);
drop(b);
        } else {
        x86_64::instructions::interrupts::enable();
            return false;
        }
    }
    x86_64::instructions::interrupts::enable();
    true
}

It really hates both of these, I think. I don't know because its not syntactically or semantically incorrect, but it did this after it complained that I needed to use the result of Box<T>.

Fixed the problem. My target triplet file contained +sse in my features list, and apparently the compiler_builtins crate doesn't like that. (Or something doesn't.)

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.