rust-lang / libs-team

The home of the library team
Apache License 2.0
127 stars 19 forks source link

Report allocation errors through the panic handler #192

Open Amanieu opened 1 year ago

Amanieu commented 1 year ago

Proposal

Problem statement

We currently use a completely separate mechanism to handle allocation errors (OOM) than panics, despite both of these being very similar. However both are very similar: they involve handling runtime failures that are generally unrecoverable. It would be better to unify them under a single mechanism which is already stable: panic handlers (no_std) and panic hooks (std).

Motivation, use-cases

This change has already been done for no_std in https://github.com/rust-lang/rust/issues/66741: it was noticed that the vast majority of users of #[alloc_error_handler] were just calling panic anyways. This avoided the need to stabilize the alloc_error_handler attribute by defaulting to a panic if one was not specified.

The OOM hook API has also remained unstable for a very long time with no clear path to stabilization. The best way forward would be to unify it with the panic hook API just like it has been done in no_std.

Some use cases (firefox) still need access to the size of the failed allocation, so this is provided as a custom panic payload.

Solution sketches

// alloc::alloc

pub struct AllocErrorPanicPayload { .. }

impl AllocErrorPanicPayload {
    pub fn layout(&self) -> Layout;
}

This ACP proposes to make the following changes:

In the standard library, no additional allocations are made as part of the OOM handling process, except in 1 specific situation:

Links and related work

Tracking issues that will be closed by this change:

What happens now?

This issue is part of the libs-api team API change proposal process. Once this issue is filed the libs-api team will review open proposals in its weekly meeting. You should receive feedback within a week or two.

m-ou-se commented 1 year ago

I don't think this is the right thing to do.

Currently, a (no-std) #[panic_handler] never gets a payload(), not even a &'static str or String, even for something as simple as panic!("hello"). Payloads are currently a std-only feature. It is true that one can call info.payload() in a #[panic_handler], but that's likely a mistake (see https://github.com/rust-lang/rust/pull/115974). That method has never done anything useful in a panic handler. Currently, any time anyone implements a #[panic_handler] by downcasting a payload() they're just adding dead code: downcasting there always returns None.

So, I disagree this proposal is a way "to unify them under a single mechanism". &dyn Any panic payloads are currently a std-only feature, that just happens to also compile (but not work) in no-std.

Amanieu commented 1 year ago

I don't see this as a strong argument to avoid using payloads, it's just that payloads are underused today. Treating OOM as a panic is almost always what people want, and allows the resulting error message to be properly integrated into whatever error reporting mechanism an application is using (e.g. uploading crash reports).

m-ou-se commented 1 year ago

Using payloads in a std panic hook is fine. I'm saying that using payloads in a #[panic_handler] would be a entirely new concept. It's true that we already have a .payload() method available in #[panic_handler]s, but it doesn't do anything.

A (std) panic hook is expected to do .payload().downcast_ref<String>() (and &str) to get the panic message, but that will always return None in a #[panic_handler]. So, we can never get the concept of a 'payload' to mean the same thing in panic handlers and panic hooks. So I disagree this is a 'unified single mechanism'.

It's a valid discussion to have whether we should start exposing the concept of a panic payload in #[panic_handler], but that would be an entirely new thing, not an existing (unified) mechanism.

(And on that discussion: I don't think we should start using payloads in #[panic_handler], because it will never mean the same thing as a payload in a panic hook: it will not have a payload for formatted panic messages. And std will need to take ownership of a panic payload to be able to unwind/catch, which isn't possible when just borrowing the payload like a #[panic_handler] does.)

m-ou-se commented 1 year ago

Treating OOM as a panic is almost always what people want, and allows the resulting error message to be properly integrated into whatever error reporting mechanism an application is using (e.g. uploading crash reports).

That will happen regardless of whether we have a AllocErrorPanicPayload payload mechanism or a default alloc_error_handler that panics, right?

RalfJung commented 6 months ago

Using payloads in a std panic hook is fine. I'm saying that using payloads in a #[panic_handler] would be a entirely new concept. It's true that we already have a .payload() method available in #[panic_handler]s, but it doesn't do anything.

I've been wondering before why panic hooks and panic handlers take the same type as argument. In a sense there's really two different types here that are just merged into one -- "PanicInfo with a message" and "PanicInfo with a payload".