Wow the discussion thread flooded with so many "borrow checker" "borrow semantic" argument~ Hey guys, don't waste time on terminology. It doesn't matter the spirit / concept of borrow checking applied to runtime / std libs shall be called "borrow checker" authentically. The doc already said it is "Enforcing Borrowing Rules at Runtime with RefCell<T>" in chapter 15.5. When one said "there are borrow checker in Rust runtime" casually, it is about the spirit, not the exact borrow checker module in compiler.
Announcement
Collapse
No announcement yet.
The Latest Progress On Rust For The Linux Kernel
Collapse
X
-
Originally posted by ssokolow View Post
On various platforms (Linux included), you don't really have any choice, because the kernel defaults to overcommit semantics to compensate for applications that waste a ton of memory on Windows by mapping much more memory than they actually write to.
That means that, even if you're handling malloc failure perfectly well, malloc will return success but, when you try to write to that memory, the kernel may suddenly realize it's overpromised what memory is available and have to kill something after all the branches for handling allocation failure in the software have already been told it was successful.
Anyone know if my idea make sense or not???
- Likes 1
Comment
-
Originally posted by billyswong View Post
Interesting knowledge! This might make Rust memory allocation failure being panic a rational choice! Here memory allocation error may trigger not when it is allocated but when it is actually used, where it is not possible to do a try_read() or try_write(). So it may be best practice to isolate the program into processes / threads / whatever grouping such that when a group of code may allocate and access a new big chunk of memory, the panic is isolated and the unwinding won't fall through the bottom.
Anyone know if my idea make sense or not???
Comment
-
Originally posted by billyswong View Post
Interesting knowledge! This might make Rust memory allocation failure being panic a rational choice! Here memory allocation error may trigger not when it is allocated but when it is actually used, where it is not possible to do a try_read() or try_write(). So it may be best practice to isolate the program into processes / threads / whatever grouping such that when a group of code may allocate and access a new big chunk of memory, the panic is isolated and the unwinding won't fall through the bottom.
Anyone know if my idea make sense or not???
As for isolation, you can certainly wrap catch_unwind around your unit of work (eg. an individual image in a batch thumbnailing operation, or the handling of an individual network request) and rely on Rust's ownership semantics and things like Mutex poisoning to ensure that stuff that's been left in an inconsistent state is not observable from outside the panicking thread before it gets cleaned up.
Comment
-
Originally posted by ultimA View Post
Yes, we don't agree. Just because I wasn't able to allocate a larger block, I shouldn't be allowed to even try allocating some smaller one because it *might* not succeed? This is crazy and bogus. Then why should I be allowed to allocate anything at all? There is always a chance a larger allocation might have failed. In fact, any allocation, even if it is the first one, might fail. And what about not being able to allocate 100MB for an image? Then there is a very high likelyhood I could still allocate 100 bytes for displaying an error message. This has nothing to do with undefined behavior, as long as the allocation function always returns a well-defined result (even in the case of failure), all is well. I could argue a forced termination is less defined than an error-result, because the result of a program- or thread-termination could be undefined at the application level (undefined hardware-state, loss of unsaved work, corruption of calculation result etc...), while the app developer would have been able to properly handle the failed allocation (just not continue).
On the other topic: Yes, I know about unwindable panics, and that the global allocator is used by default in most if not all crates. I wrote about this in an earlier post here in this thread, and I can only say the same: The problem is this facility is unstable/experimental/not yet released/call it whatever you want, and progress on it has been very slow over the years. And even when it gets officially stable, another part of the same Rust proposal is the introduction of fallible APIs, and those are the ones which won't get automatically used by crates, and will have to trickle down to them over a long period of time.
On the other hand your point about allocating 100MB for images is kind of bogus, because if you need that, then 1) you would set your hardware requirements accordingly and 2) if it fails, the program wouldn't be able to perform its primary function of processing images, and the rest is essentially irrelevant. If you are so concerned about being able to display an error message, you can also allocate and build the pop-up first, just not show it, and ONLY THEN allocate your 100MB for images (and display the popup from catch_unwind if it fails). Another way to look at it, Rust currently HAS fallible allocation in exactly the same way as C++ or Java: through runtime exceptions (because of course panic/catch_unwind is nothing but exceptions propagation). What it doesn't have in stable versions is idiomatic fallible allocation that would return Option<T> or Result<T,E>.
There is also a fundamental difference between a panicking allocation and runtime assertions such as that in RefCell. The latter is a development tool to detect a condition that is supposed to never happen in a well designed program, and when it occurs it signals a bug that must be corrected. It's the same as assert() in C, which is and should be used, but there is no way to recover from it and that's not its purpose. On the other hand, the former (allocation and other similar problems) result from external conditions that cannot be predicted or fixed during development and in one way or another, they prevent the program from doing what it's supposed to do. If that happens, the program *MAY* be able to do something to warn the user etc., but it's absolutely not guaranteed, because the idea that if you fail to allocate 100MB, you could still allocate at least 2Kb is nothing more or less than keeping fingers crossed with zero guarantees as to what the outcome will be. In the best case, it would work, in the worst case, you would get a cascade of exceptions and/or null pointers that will result in a crash that will be extremely difficult to diagnose and even more so to reproduce reliably.
- Likes 1
Comment
-
Originally posted by billyswong View PostWow the discussion thread flooded with so many "borrow checker" "borrow semantic" argument~ Hey guys, don't waste time on terminology. It doesn't matter the spirit / concept of borrow checking applied to runtime / std libs shall be called "borrow checker" authentically. The doc already said it is "Enforcing Borrowing Rules at Runtime with RefCell<T>" in chapter 15.5. When one said "there are borrow checker in Rust runtime" casually, it is about the spirit, not the exact borrow checker module in compiler.
Yes the problem here that we have people making the rust compiler putting a false divide between what is compiler functionality and what is standard library functionality. This false divide makes them not see that the compiler should be checking bits of the standard libraries functionality to pick up human errors in coding before they cause end user problems.
You could argue that gcc should not check for use after free in C because that is the C standard library functionality yes people use to argue this with C but if you attempt that today when writing a C compiler everyone would think you are a idiot. Yet that exactly the same argue rust people make to me over RefCell. Yes the rust people need to wake up on this point.
- Likes 1
Comment
-
Originally posted by oiaohm View Post
The problem I have here is that the "borrow checker" module in the complier could be doing like what gcc does with printf scanf ... with C where it checking that the runtime RefCell usage looks sane at the build time. From my point of view the borrow checker in the compiler of rust is defective because it has not be implemented out to cover everything it should. Yes some cases with RefCell the borrwer checker module in the complier should not cause the build to fail but should throw a warning that the code looks dicey.
Yes the problem here that we have people making the rust compiler putting a false divide between what is compiler functionality and what is standard library functionality. This false divide makes them not see that the compiler should be checking bits of the standard libraries functionality to pick up human errors in coding before they cause end user problems.
You could argue that gcc should not check for use after free in C because that is the C standard library functionality yes people use to argue this with C but if you attempt that today when writing a C compiler everyone would think you are a idiot. Yet that exactly the same argue rust people make to me over RefCell. Yes the rust people need to wake up on this point.- In practice it would likely not cover much more than what you can use normal mutable references for, which the static borrow checker already handles;
- It would be poor use of developer's time and resources to expand considerably the already very advanced static analysis capabilities of rustc just to handle a comparatively obscure standard library feature that is rarely used (but the added complexity, slowness and potential bugs of the compiler would affect everyone, even developers of low level code that doesn't actually use the standard library at all)
- The only tangible result would be to remove a test and conditional jump from the generated code, which is hardly noticeable - even with Spectre etc mitigations, the effect is not even measurable unless you have that in some tight running inner loop, in which case you have some major design problems anyway.
Last edited by jacob; 16 September 2021, 02:38 AM.
Comment
-
Originally posted by jacob View PostThe problem is that RefCell can only be checked at runtime.
Originally posted by jacob View PostCould the compiler have some logic to deal with certain simple cases where it can prove statically that multiple mutating borrows are impossible, and optimise the runtime assertion out?
There is simple panic code here.
Code:use std::cell::RefCell; let c = RefCell::new(5); let m = c.borrow_mut(); let b = c.borrow(); // this causes a panic
Originally posted by jacob View PostAbsolutely, but from a practical point of view implementing it would be lots of work for a fairly minimal benefit, because- In practice it would likely not cover much more than what you can use normal mutable references for, which the static borrow checker already handles;
- It would be poor use of developer's time and resources to expand considerably the already very advanced static analysis capabilities of rustc just to handle a comparatively obscure standard library feature that is rarely used (but the added complexity, slowness and potential bugs of the compiler would affect everyone, even developers of low level code that doesn't actually use the standard library at all)
- The only tangible result would be to remove a test and conditional jump from the generated code, which is hardly noticeable - even with Spectre etc mitigations, the effect is not even measurable unless you have that in some tight running inner loop, in which case you have some major design problems anyway.
Yes the result would not be a performance fix. The result would developer being informed that have coded something that could panic on the end user at runtime that currently builds without warning or error. This is not about making the program faster this is making the program less crash prone.
I like how you call it obscure standard library feature. This is a feature of the rust standard library that as part of standard operation says it perfectly fine to crash the application and cause user to loss all their work.
Performance issues are one thing. RefCell was not coded to make sure that you have to include a proper handler to deal with the case it goes wrong either.
Please note I am not saying deal with the RefCell problem is simple. Those making advanced static analysis for C and C++ don't have a easy time either.
- Likes 1
Comment
-
Originally posted by oiaohm View PostCode:use std::cell::RefCell; let c = RefCell::new(5); let m = c.borrow_mut(); let b = c.borrow(); // this causes a panic
Code:let mut c = 5; let m = &mut c; let b = &c;
Originally posted by oiaohm View PostI like how you call it obscure standard library feature. This is a feature of the rust standard library that as part of standard operation says it perfectly fine to crash the application and cause user to loss all their work.
Originally posted by oiaohm View PostPlease note I am not saying deal with the RefCell problem is simple. Those making advanced static analysis for C and C++ don't have a easy time either.
Comment
-
Originally posted by oiaohm View PostYes the problem here that we have people making the rust compiler putting a false divide between what is compiler functionality and what is standard library functionality. This false divide makes them not see that the compiler should be checking bits of the standard libraries functionality to pick up human errors in coding before they cause end user problems.
Heck, that's why one of the ongoing efforts is to get to a point where the functionality of Xargo (a Cargo wrapper which automates building for platforms without precompiled standard library distributions on offer) is merged into Cargo.
Checking bad uses of RefCell is allowed... it just belongs as a lint in Clippy, rather than as part of rustc.
It's basically the same mindset that has them artificially limit type inference. Yes, rustc's type inference is capable of inferring function argument types like Haskell's, as is demonstrated by that working for Rust's closure syntax... but normal functions are intentionally required to explicitly specify their argument types because that's part of their interface and Rust's designers made a conscious decision that changes to the innards of a function should not implicitly alter the interface it exposes.
Clippy has plenty of lints for stuff like that which can be turned into errors using annotations right within your source code, while rustc does not. It also has various lints that default to being errors, like out_of_bounds_indexing. (And remember that Clippy is part of the core Rust distribution, just like rustc and rustdoc.)
Last edited by ssokolow; 16 September 2021, 09:47 AM.
- Likes 1
Comment
Comment