#include <cstdlib>
#include <cstring>
+using std::bad_alloc;
using std::lock_guard;
using std::min;
using std::recursive_mutex;
return;
}
- // No more data will come in, so re-create the last chunk accordingly
- uint8_t* resized_chunk = new uint8_t[used_samples_ * unit_size_];
- memcpy(resized_chunk, current_chunk_, used_samples_ * unit_size_);
+ if (current_chunk_) {
+ // No more data will come in, so re-create the last chunk accordingly
+ uint8_t* resized_chunk = new uint8_t[used_samples_ * unit_size_];
+ memcpy(resized_chunk, current_chunk_, used_samples_ * unit_size_);
- delete[] current_chunk_;
- current_chunk_ = resized_chunk;
+ delete[] current_chunk_;
+ current_chunk_ = resized_chunk;
- data_chunks_.pop_back();
- data_chunks_.push_back(resized_chunk);
+ data_chunks_.pop_back();
+ data_chunks_.push_back(resized_chunk);
+ }
}
void Segment::append_single_sample(void *data)
data_offset += (copy_count * unit_size_);
if (unused_samples_ == 0) {
- // If we're out of memory, this will throw std::bad_alloc
- current_chunk_ = new uint8_t[chunk_size_];
+ try {
+ // If we're out of memory, allocating a chunk will throw
+ // std::bad_alloc. To give the application some usable memory
+ // to work with in case chunk allocation fails, we allocate
+ // extra memory and throw it away if it all succeeded.
+ // This way, memory allocation will fail early enough to let
+ // PV remain alive. Otherwise, PV will crash in a random
+ // memory-allocating part of the application.
+ current_chunk_ = new uint8_t[chunk_size_];
+
+ const int dummy_size = 2 * chunk_size_;
+ auto dummy_chunk = new uint8_t[dummy_size];
+ memset(dummy_chunk, 0xFF, dummy_size);
+ delete[] dummy_chunk;
+ } catch (bad_alloc) {
+ delete[] current_chunk_; // The new may have succeeded
+ current_chunk_ = nullptr;
+ throw;
+ }
+
data_chunks_.push_back(current_chunk_);
used_samples_ = 0;
unused_samples_ = chunk_size_ / unit_size_;