Skip to content

Conversation

@theuni
Copy link
Member

@theuni theuni commented Feb 25, 2017

Addresses #9854.

This installs a handler for failed allocations. Usually, a std::bad_alloc would be thrown, but we can instead opt to terminate immediately. See http://en.cppreference.com/w/cpp/memory/new/set_new_handler .

Our only manual malloc/free comes from prevector. Ideally we'd just use new[]/delete[] there, but the benchmark shows a noticeable difference, and I'm very hesitant to make that kind of change post-rc2.

As an alternative, this could even just be an assert() for 0.14.

For reference, here's the alternative using new/delete: theuni@0c1b410

Edit: g++/libstdc++ didn't support get_new_handler() yet, so assert it is.

@fanquake fanquake added this to the 0.14.0 milestone Feb 25, 2017
@sipa
Copy link
Member

sipa commented Feb 25, 2017

./prevector.h:178:21: error: ‘get_new_handler’ is not a member of ‘std’

                     std::get_new_handler()();

@theuni
Copy link
Member Author

theuni commented Feb 25, 2017

@sipa grr, works for me locally. I'll try adding the include, but I'm guessing that libstdc++ didn't support it for 4.8 :(

@theuni theuni force-pushed the bad_alloc_terminate2 branch 2 times, most recently from f4facff to faf7cb5 Compare February 25, 2017 06:08
@JeremyRubin
Copy link
Contributor

Why not make prevector throw std::bad_alloc or std::length_error? That way it's at least recoverable in future code, and makes it a bit more "API Consistent" with std::vector...

@theuni theuni force-pushed the bad_alloc_terminate2 branch from faf7cb5 to 420f170 Compare February 25, 2017 07:16
@theuni
Copy link
Member Author

theuni commented Feb 25, 2017

@JeremyRubin The issue in #9854 is that exactly that bad_alloc is thrown but not handled correctly. We don't actually attempt to catch bad_alloc specifically anywhere. And logically it doesn't make sense to... if we've got an allocation problem, we need to shut down.

@luke-jr
Copy link
Member

luke-jr commented Feb 25, 2017

The default action of exceptions is to abort the program, no? So we must be catching it at least indirectly... Why not just add an explicit catch for bad_alloc there? (or ideally, be specific about what exceptions we want to catch..)

@laanwj
Copy link
Member

laanwj commented Feb 25, 2017

ACK on the assert() in prevector.

I really don't like setting this global handler that crashes the program. I hope this is a temporary measure for 0.14?

@laanwj
Copy link
Member

laanwj commented Feb 25, 2017

Thinking of it, no, using assert for error handling is a bad idea. We should support compiling without assertions at some point.
If you really want to terminate the program immediatelly please log a message and call abort or so () :/

@TheBlueMatt
Copy link
Contributor

TheBlueMatt commented Feb 25, 2017 via email

@laanwj
Copy link
Member

laanwj commented Feb 25, 2017

@TheBlueMatt okay, yes, after discussion IRC I agree seems the least bad solution for now :/

Edit: so to be clear, my concern with a jackhammer solution like this is that not all allocations are created equal. An allocation of a 4 GB buffer somewhere in the program may fail, but that may be perfectly fine if it can continue with a smaller buffer. It doesn't mean the rest of the application, which will never allocate a big buffer like that, is in a bad state. Specificallly we care deeply about bad_allocs thrown by consensus and block/utxo handling code. Error handling in Bitcoin Core has always been a bit flakey, and in a way his feels a bit like throwing in the towel on reasoned error handling completely.So that's why my angry reply. Sorry for that. But we need this now to avoid corruption problems. I think we should continue with this.

@JeremyRubin
Copy link
Contributor

Ah, I see. I was going to suggest get_new_handler, then I actually read the above discussion ;)

I'm mostly just uncomfortable with using assert for error handling.

Can we not implement something similar to get_new_handler ourselves for unsupported platforms?

@TheBlueMatt
Copy link
Contributor

TheBlueMatt commented Feb 25, 2017 via email

src/init.cpp Outdated
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Before this attempt, you could do fputs(stderr, "Error: Out of memory. Terminating.\n"); which, I believe, does not call malloc. At least it didn't when I wrote my own malloc a few years ago, but it may be different per platform. A nice to have for when a node goes down with no log entries about why.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this is threadsafe (ie, a concurrent OOM could cause it to terminate in the middle of a LogPrintf or print two LogPrintfs). I would make the handler have some switch(atomic counter++). Then it is both re-entrant and threadsafe. (note that set_new_handler is threadsafe itself, just is threadsafe in this usage)

I think this sort of implementation should be ok:

static std::thread::id first_entrant{std::this_thread::get_id()};
static bool first_pass {true};
if (first_entrant == std::this_thread::get_id()) {
    if (first_pass) {
        first_pass = false;
        // Single faulted
        std::terminate();
    } else {
        // Double faulted
    }
} else {
    sleep();
}

Either of the above should be safe for concurrent OOM as well as double OOM faults.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Those are both reasonable suggestions, but imo they're overthinking this a bit. If we OOM, the important thing is to shutdown, logging should be best-effort. I'd rather not complicate this too much.

@sdaftuar
Copy link
Member

nit: Perhaps we should add a comment to the assert lines introduced in prevector, mentioning their purpose/rationale? (I'm pretty sure if I were reading the code in the future and hadn't seen this discussion, I'd definitely overlook the significance and implications.)

@theuni theuni force-pushed the bad_alloc_terminate2 branch from 420f170 to d4ee7ba Compare February 27, 2017 19:50
@sipa
Copy link
Member

sipa commented Feb 28, 2017

utACK

@laanwj laanwj merged commit d4ee7ba into bitcoin:master Feb 28, 2017
laanwj added a commit that referenced this pull request Feb 28, 2017
d4ee7ba prevector: assert successful allocation (Cory Fields)
c5f008a don't throw std::bad_alloc when out of memory. Instead, terminate immediately (Cory Fields)

Tree-SHA512: 699ce8df5b1775a99c71d3cfc952b45da1c0091e1a4b6adfac52d5be6144c3d98f88ac3af90e5c73fff2f74666a499feb4a34434683ce5979814e869c0aeddc3
laanwj pushed a commit that referenced this pull request Feb 28, 2017
laanwj pushed a commit that referenced this pull request Feb 28, 2017
codablock pushed a commit to codablock/dash that referenced this pull request Jan 26, 2018
d4ee7ba prevector: assert successful allocation (Cory Fields)
c5f008a don't throw std::bad_alloc when out of memory. Instead, terminate immediately (Cory Fields)

Tree-SHA512: 699ce8df5b1775a99c71d3cfc952b45da1c0091e1a4b6adfac52d5be6144c3d98f88ac3af90e5c73fff2f74666a499feb4a34434683ce5979814e869c0aeddc3
str4d added a commit to str4d/zcash that referenced this pull request Apr 26, 2018
zkbot added a commit to zcash/zcash that referenced this pull request May 1, 2018
Terminate immediately when allocation fails

Backport of bitcoin/bitcoin#9856

Closes #1498.
lateminer pushed a commit to lateminer/bitcoin that referenced this pull request Jan 4, 2019
Terminate immediately when allocation fails bitcoin#9856
andvgal pushed a commit to energicryptocurrency/gen2-energi that referenced this pull request Jan 6, 2019
d4ee7ba prevector: assert successful allocation (Cory Fields)
c5f008a don't throw std::bad_alloc when out of memory. Instead, terminate immediately (Cory Fields)

Tree-SHA512: 699ce8df5b1775a99c71d3cfc952b45da1c0091e1a4b6adfac52d5be6144c3d98f88ac3af90e5c73fff2f74666a499feb4a34434683ce5979814e869c0aeddc3
CryptoCentric pushed a commit to absolute-community/absolute that referenced this pull request Feb 27, 2019
d4ee7ba prevector: assert successful allocation (Cory Fields)
c5f008a don't throw std::bad_alloc when out of memory. Instead, terminate immediately (Cory Fields)

Tree-SHA512: 699ce8df5b1775a99c71d3cfc952b45da1c0091e1a4b6adfac52d5be6144c3d98f88ac3af90e5c73fff2f74666a499feb4a34434683ce5979814e869c0aeddc3
Fuzzbawls added a commit to PIVX-Project/PIVX that referenced this pull request May 19, 2020
a8b965e Explicitly initialize prevector _union (random-zebra)
bdd98e8 [Trivial] Add a comment on the use of prevector in script. (random-zebra)
bbaa6e3 Clarify prevector::erase and avoid swap-to-clear (random-zebra)
28a8435 prevector: assert successful allocation (random-zebra)
de41cb5 Only call clear on prevector if it isn't trivially destructible (random-zebra)
8784df3 Make CScript (and prevector) c++11 movable. (random-zebra)
83f9ac6 serialize: Deprecate `begin_ptr` / `end_ptr` (random-zebra)
c3ecc12 prevector: add C++11-like data() method (random-zebra)
5490aa0 test prevector::swap (random-zebra)
035760e prevector::swap: fix (unreached) data corruption (random-zebra)
0e71400 prevector: destroy elements only via erase() (random-zebra)
9811a68 [Core] Prevector type (random-zebra)

Pull request description:

  Based on:
  - [x] #1554

  This introduces `prevector<N, T>`, a new basic data type which is a fully API compatible drop-in replacement for `std::vector<T>` and uses it for the script, to reduce the considerable memory overhead of vectors in cases where they normally contain a small number of small elements.

  Original tests in Bitcoin showed a reduction in dbcache memory usage by **23%**, and made an initial sync **13%** faster.

  Backported from the following upstream's PRs, with only additional edits in the 2nd layer scripts (`masternode-payments`, `masternode-budget`) and in the unit tests (to address the differences in `insecure_rand`):

  - bitcoin#6914
  - bitcoin#7888
  - bitcoin#8850
  - bitcoin#9349
  - bitcoin#9505
  - bitcoin#9856
  - bitcoin#10534
  - bitcoin#11011
  - bitcoin#14028

  ~~NOTE: Updates to `memusage.h` needed as well, when #1531 is merged.~~

ACKs for top commit:
  furszy:
    Tested ACK a8b965e .
  Fuzzbawls:
    ACK a8b965e

Tree-SHA512: 203b660dd8d9f98780be660dae0ac951766ba438836bd6f0ec921e7749c1a84143f3b920fe49f2cc399f4aa7e763098f78746d3b6ff845bcf913bb13de984bc1
@bitcoin bitcoin locked as resolved and limited conversation to collaborators Sep 8, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

10 participants