]> sigrok.org Git - pulseview.git/blobdiff - pv/data/segment.cpp
Fix #975 by overallocating memory to leave spare for PV to use
[pulseview.git] / pv / data / segment.cpp
index ae9788fc676724d06166f8ffd851767f5abe6362..7b8e611f271999d6b3a57948166322faccaa71bf 100644 (file)
@@ -24,6 +24,7 @@
 #include <cstdlib>
 #include <cstring>
 
 #include <cstdlib>
 #include <cstring>
 
+using std::bad_alloc;
 using std::lock_guard;
 using std::min;
 using std::recursive_mutex;
 using std::lock_guard;
 using std::min;
 using std::recursive_mutex;
@@ -33,13 +34,15 @@ namespace data {
 
 const uint64_t Segment::MaxChunkSize = 10 * 1024 * 1024;  /* 10MiB */
 
 
 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),
        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);
 {
        lock_guard<recursive_mutex> lock(mutex_);
        assert(unit_size_ > 0);
@@ -89,6 +92,21 @@ unsigned int Segment::unit_size() const
        return unit_size_;
 }
 
        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_);
 void Segment::free_unused_memory()
 {
        lock_guard<recursive_mutex> lock(mutex_);
@@ -99,15 +117,17 @@ void Segment::free_unused_memory()
                return;
        }
 
                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)
 }
 
 void Segment::append_single_sample(void *data)
@@ -160,8 +180,26 @@ void Segment::append_samples(void* data, uint64_t samples)
                data_offset += (copy_count * unit_size_);
 
                if (unused_samples_ == 0) {
                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_;
                        data_chunks_.push_back(current_chunk_);
                        used_samples_ = 0;
                        unused_samples_ = chunk_size_ / unit_size_;