#include <cstdlib>
#include <cstring>
+using std::bad_alloc;
using std::lock_guard;
using std::min;
using std::recursive_mutex;
const uint64_t Segment::MaxChunkSize = 10 * 1024 * 1024; /* 10MiB */
-Segment::Segment(uint64_t samplerate, unsigned int unit_size) :
+Segment::Segment(uint32_t segment_id, uint64_t samplerate, unsigned int unit_size) :
+ segment_id_(segment_id),
sample_count_(0),
start_time_(0),
samplerate_(samplerate),
unit_size_(unit_size),
iterator_count_(0),
- mem_optimization_requested_(false)
+ mem_optimization_requested_(false),
+ is_complete_(false)
{
lock_guard<recursive_mutex> lock(mutex_);
assert(unit_size_ > 0);
return unit_size_;
}
+uint32_t Segment::segment_id() const
+{
+ return segment_id_;
+}
+
+void Segment::set_complete()
+{
+ is_complete_ = true;
+}
+
+bool Segment::is_complete() const
+{
+ return is_complete_;
+}
+
void Segment::free_unused_memory()
{
lock_guard<recursive_mutex> lock(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_;
sample_count_ += samples;
}
-uint8_t* Segment::get_raw_samples(uint64_t start, uint64_t count) const
+void Segment::get_raw_samples(uint64_t start, uint64_t count,
+ uint8_t* dest) const
{
assert(start < sample_count_);
assert(start + count <= sample_count_);
assert(count > 0);
+ assert(dest != nullptr);
lock_guard<recursive_mutex> lock(mutex_);
- uint8_t* dest = new uint8_t[count * unit_size_];
uint8_t* dest_ptr = dest;
uint64_t chunk_num = (start * unit_size_) / chunk_size_;
chunk_num++;
chunk_offs = 0;
}
-
- return dest;
}
SegmentRawDataIterator* Segment::begin_raw_sample_iteration(uint64_t start)