]> sigrok.org Git - pulseview.git/commitdiff
Fix #1596 by making memory management more robust
authorSoeren Apel <redacted>
Thu, 3 Sep 2020 19:05:17 +0000 (21:05 +0200)
committerSoeren Apel <redacted>
Thu, 3 Sep 2020 19:09:35 +0000 (21:09 +0200)
1) Fixed use of raw pointers to shared_ptr-managed instances
2) Fixed bug due to newly-introduced shared_from_this
3) More nullptr checks
4) Add muxer thread interrupting

main.cpp
pv/data/decode/decoder.hpp
pv/data/decodesignal.cpp
pv/data/decodesignal.hpp
pv/data/logicsegment.cpp
pv/data/logicsegment.hpp
pv/data/segment.cpp
pv/data/signalbase.cpp
pv/data/signalbase.hpp
pv/views/trace/decodetrace.cpp

index b0e631f6382cbd4697d2171e30ebab8620065989..a307159c5005f92dfe26212e1c7f90fd1e7ca898 100644 (file)
--- a/main.cpp
+++ b/main.cpp
@@ -278,6 +278,7 @@ int main(int argc, char *argv[])
        qRegisterMetaType<uint64_t>("uint64_t");
        qRegisterMetaType<pv::util::Timestamp>("util::Timestamp");
        qRegisterMetaType<SharedPtrToSegment>("SharedPtrToSegment");
+       qRegisterMetaType<shared_ptr<pv::data::SignalBase>>("shared_ptr<SignalBase>");
 
        // Prepare the global settings since logging needs them early on
        pv::GlobalSettings settings;
index 6e11e83f374e23e4c4487ff2cd2148fbb0143ddc..86a371f26c67c11d72abfe80a3715cf2292921ae 100644 (file)
@@ -82,7 +82,7 @@ struct DecodeChannel
        uint16_t id;     ///< Global numerical ID for the decode channels in the stack
        uint16_t bit_id; ///< Tells which bit within a sample represents this channel
        const bool is_optional;
-       const pv::data::SignalBase *assigned_signal;
+       shared_ptr<const pv::data::SignalBase> assigned_signal;
        const QString name, desc;
        int initial_pin_state;
        const shared_ptr<Decoder> decoder_;
index b6166a4c603dee6a4548b4351a2dad7ed811408b..12077a96f5b766c14c5839196195585e0e745ca4 100644 (file)
@@ -309,7 +309,7 @@ void DecodeSignal::auto_assign_signals(const shared_ptr<Decoder> dec)
                }
 
                if (match) {
-                       ch.assigned_signal = match.get();
+                       ch.assigned_signal = match;
                        new_assignment = true;
                }
        }
@@ -322,7 +322,7 @@ void DecodeSignal::auto_assign_signals(const shared_ptr<Decoder> dec)
        }
 }
 
-void DecodeSignal::assign_signal(const uint16_t channel_id, const SignalBase *signal)
+void DecodeSignal::assign_signal(const uint16_t channel_id, shared_ptr<const SignalBase> signal)
 {
        for (decode::DecodeChannel& ch : channels_)
                if (ch.id == channel_id) {
@@ -340,7 +340,7 @@ int DecodeSignal::get_assigned_signal_count() const
 {
        // Count all channels that have a signal assigned to them
        return count_if(channels_.begin(), channels_.end(),
-               [](decode::DecodeChannel ch) { return ch.assigned_signal; });
+               [](decode::DecodeChannel ch) { return ch.assigned_signal.get(); });
 }
 
 void DecodeSignal::set_initial_pin_state(const uint16_t channel_id, const int init_state)
@@ -399,8 +399,9 @@ int64_t DecodeSignal::get_working_sample_count(uint32_t segment_id) const
                        if (segment_id >= logic_data->logic_segments().size())
                                return 0;
 
-                       const shared_ptr<LogicSegment> segment = logic_data->logic_segments()[segment_id];
-                       count = min(count, (int64_t)segment->get_sample_count());
+                       const shared_ptr<const LogicSegment> segment = logic_data->logic_segments()[segment_id]->get_shared_ptr();
+                       if (segment)
+                               count = min(count, (int64_t)segment->get_sample_count());
                }
 
        return (no_signals_assigned ? 0 : count);
@@ -807,7 +808,7 @@ void DecodeSignal::restore_settings(QSettings &settings)
 
                for (const shared_ptr<data::SignalBase>& signal : signalbases)
                        if ((signal->name() == assigned_signal_name) && (signal->type() != SignalBase::DecodeChannel))
-                               channel->assigned_signal = signal.get();
+                               channel->assigned_signal = signal;
 
                channel->initial_pin_state = settings.value("initial_pin_state").toInt();
 
@@ -838,8 +839,8 @@ bool DecodeSignal::all_input_segments_complete(uint32_t segment_id) const
                        if (segment_id >= logic_data->logic_segments().size())
                                return false;
 
-                       const shared_ptr<LogicSegment> segment = logic_data->logic_segments()[segment_id];
-                       if (!segment->is_complete())
+                       const shared_ptr<const LogicSegment> segment = logic_data->logic_segments()[segment_id]->get_shared_ptr();
+                       if (segment && !segment->is_complete())
                                all_complete = false;
                }
 
@@ -878,8 +879,10 @@ double DecodeSignal::get_input_samplerate(uint32_t segment_id) const
                                continue;
 
                        try {
-                               const shared_ptr<LogicSegment> segment = logic_data->logic_segments().at(segment_id);
-                               samplerate = segment->samplerate();
+                               const shared_ptr<const LogicSegment> segment =
+                                       logic_data->logic_segments().at(segment_id)->get_shared_ptr();
+                               if (segment)
+                                       samplerate = segment->samplerate();
                        } catch (out_of_range&) {
                                // Do nothing
                        }
@@ -1006,7 +1009,8 @@ void DecodeSignal::mux_logic_samples(uint32_t segment_id, const int64_t start, c
                return;
 
        // Fetch the channel segments and their data
-       vector<shared_ptr<LogicSegment> > segments;
+       bool segment_missing = false;
+       vector<shared_ptr<const LogicSegment> > segments;
        vector<const uint8_t*> signal_data;
        vector<uint8_t> signal_in_bytepos;
        vector<uint8_t> signal_in_bitpos;
@@ -1015,14 +1019,21 @@ void DecodeSignal::mux_logic_samples(uint32_t segment_id, const int64_t start, c
                if (ch.assigned_signal) {
                        const shared_ptr<Logic> logic_data = ch.assigned_signal->logic_data();
 
-                       shared_ptr<LogicSegment> segment;
+                       shared_ptr<const LogicSegment> segment;
                        try {
-                               segment = logic_data->logic_segments().at(segment_id);
+                               segment = logic_data->logic_segments().at(segment_id)->get_shared_ptr();
                        } catch (out_of_range&) {
                                qDebug() << "Muxer error for" << name() << ":" << ch.assigned_signal->name() \
                                        << "has no logic segment" << segment_id;
+                               logic_mux_interrupt_ = true;
                                return;
                        }
+
+                       if (!segment) {
+                               segment_missing = true;
+                               break;
+                       }
+
                        segments.push_back(segment);
 
                        uint8_t* data = new uint8_t[(end - start) * segment->unit_size()];
@@ -1034,6 +1045,8 @@ void DecodeSignal::mux_logic_samples(uint32_t segment_id, const int64_t start, c
                        signal_in_bitpos.push_back(bitpos % 8);
                }
 
+       if (segment_missing)
+               return;
 
        shared_ptr<LogicSegment> output_segment;
        try {
@@ -1042,6 +1055,7 @@ void DecodeSignal::mux_logic_samples(uint32_t segment_id, const int64_t start, c
                qDebug() << "Muxer error for" << name() << ": no logic mux segment" \
                        << segment_id << "in mux_logic_samples(), mux segments size is" \
                        << logic_mux_data_->logic_segments().size();
+               logic_mux_interrupt_ = true;
                return;
        }
 
@@ -1104,8 +1118,7 @@ void DecodeSignal::logic_mux_proc()
 
        // Create initial logic mux segment
        shared_ptr<LogicSegment> output_segment =
-               make_shared<LogicSegment>(*logic_mux_data_, segment_id,
-                       logic_mux_unit_size_, 0);
+               make_shared<LogicSegment>(*logic_mux_data_, segment_id, logic_mux_unit_size_, 0);
        logic_mux_data_->push_segment(output_segment);
 
        output_segment->set_samplerate(get_input_samplerate(0));
@@ -1172,7 +1185,7 @@ void DecodeSignal::logic_mux_proc()
 
 void DecodeSignal::decode_data(
        const int64_t abs_start_samplenum, const int64_t sample_count,
-       const shared_ptr<LogicSegment> input_segment)
+       const shared_ptr<const LogicSegment> input_segment)
 {
        const int64_t unit_size = input_segment->unit_size();
        const int64_t chunk_sample_count = DecodeChunkLength / unit_size;
@@ -1236,8 +1249,9 @@ void DecodeSignal::decode_proc()
        if (decode_interrupt_)
                return;
 
-       shared_ptr<LogicSegment> input_segment = logic_mux_data_->logic_segments().front();
-       assert(input_segment);
+       shared_ptr<const LogicSegment> input_segment = logic_mux_data_->logic_segments().front()->get_shared_ptr();
+       if (!input_segment)
+               return;
 
        // Create the initial segment and set its sample rate so that we can pass it to SRD
        create_decode_segment();
@@ -1418,7 +1432,7 @@ void DecodeSignal::connect_input_notifiers()
                if (!ch.assigned_signal)
                        continue;
 
-               const data::SignalBase *signal = ch.assigned_signal;
+               const data::SignalBase *signal = ch.assigned_signal.get();
                connect(signal, &data::SignalBase::samples_cleared,
                        this, &DecodeSignal::on_data_cleared);
                connect(signal, &data::SignalBase::samples_added,
index c9ebb2a69c4d00adea4e099e0c4df8de5c6a7d87..fad3db78548457d25e61f9b4f7dc0bf8042243cf 100644 (file)
@@ -117,7 +117,7 @@ public:
 
        const vector<decode::DecodeChannel> get_channels() const;
        void auto_assign_signals(const shared_ptr<Decoder> dec);
-       void assign_signal(const uint16_t channel_id, const SignalBase *signal);
+       void assign_signal(const uint16_t channel_id, shared_ptr<const SignalBase> signal);
        int get_assigned_signal_count() const;
 
        void set_initial_pin_state(const uint16_t channel_id, const int init_state);
@@ -201,7 +201,7 @@ private:
        void logic_mux_proc();
 
        void decode_data(const int64_t abs_start_samplenum, const int64_t sample_count,
-               const shared_ptr<LogicSegment> input_segment);
+               const shared_ptr<const LogicSegment> input_segment);
        void decode_proc();
 
        void start_srd_session();
index 22f1d38a0095565cabdeb807180c0e7aff3c934d..be56c1505263fd44c19901ce8b055112f6888378 100644 (file)
@@ -63,10 +63,24 @@ LogicSegment::LogicSegment(pv::data::Logic& owner, uint32_t segment_id,
 LogicSegment::~LogicSegment()
 {
        lock_guard<recursive_mutex> lock(mutex_);
+
        for (MipMapLevel &l : mip_map_)
                free(l.data);
 }
 
+shared_ptr<const LogicSegment> LogicSegment::get_shared_ptr() const
+{
+       shared_ptr<const Segment> ptr = nullptr;
+
+       try {
+               ptr = shared_from_this();
+       } catch (std::exception& e) {
+               /* Do nothing, ptr remains a null pointer */
+       }
+
+       return ptr ? std::dynamic_pointer_cast<const LogicSegment>(ptr) : nullptr;
+}
+
 template <class T>
 void LogicSegment::downsampleTmain(const T*&in, T &acc, T &prev)
 {
index 1f151eeb7a71e2e0f5e931f6ec0f3a7a08d7e0b4..2e37ed2d248df56c22d1e0d1cd703d7835425fcb 100644 (file)
@@ -75,6 +75,14 @@ public:
 
        virtual ~LogicSegment();
 
+       /**
+        * Using enable_shared_from_this prevents the normal use of shared_ptr
+        * instances by users of LogicSegment instances. Instead, shared_ptrs may
+        * only be created by the instance itself.
+        * See https://en.cppreference.com/w/cpp/memory/enable_shared_from_this
+        */
+       shared_ptr<const LogicSegment> get_shared_ptr() const;
+
        void append_payload(shared_ptr<sigrok::Logic> logic);
        void append_payload(void *data, uint64_t data_size);
 
index 18aabedbedd348bf0656635f60cb991757713e32..4533c9b41ef7eae4d5f092c05cb58970f8af7b8d 100644 (file)
@@ -46,7 +46,6 @@ Segment::Segment(uint32_t segment_id, uint64_t samplerate, unsigned int unit_siz
        mem_optimization_requested_(false),
        is_complete_(false)
 {
-       lock_guard<recursive_mutex> lock(mutex_);
        assert(unit_size_ > 0);
 
        // Determine the number of samples we can fit in one chunk
index 94e1f6a6c03a8f86318bfaf35d9c14fb572bb290..578d908f8d52a036b4e5c9f4a553a34865264bb3 100644 (file)
@@ -304,11 +304,17 @@ void SignalBase::clear_sample_data()
 
 shared_ptr<data::Analog> SignalBase::analog_data() const
 {
+       if (!data_)
+               return nullptr;
+
        return dynamic_pointer_cast<Analog>(data_);
 }
 
 shared_ptr<data::Logic> SignalBase::logic_data() const
 {
+       if (!data_)
+               return nullptr;
+
        shared_ptr<Logic> result = dynamic_pointer_cast<Logic>(data_);
 
        if (((conversion_type_ == A2LConversionByThreshold) ||
index a55b3fc2d3fa458c7d97f522d2e503674d421ecc..ee927aae27bf911c96f2c396a4a14fc42a22d33a 100644 (file)
@@ -422,4 +422,6 @@ protected:
 } // namespace data
 } // namespace pv
 
+Q_DECLARE_METATYPE(shared_ptr<pv::data::SignalBase>);
+
 #endif // PULSEVIEW_PV_DATA_SIGNALBASE_HPP
index 2902c936552757ee1c88160a32d0f2b8d24ca82a..5b9b3f36c586568111684bfbfc73fd491b1a208e 100644 (file)
@@ -1131,10 +1131,9 @@ QComboBox* DecodeTrace::create_channel_selector(QWidget *parent, const DecodeCha
        for (const shared_ptr<data::SignalBase> &b : sig_list) {
                assert(b);
                if (b->logic_data() && b->enabled()) {
-                       selector->addItem(b->name(),
-                               QVariant::fromValue((void*)b.get()));
+                       selector->addItem(b->name(), QVariant::fromValue(b));
 
-                       if (ch->assigned_signal == b.get())
+                       if (ch->assigned_signal == b)
                                selector->setCurrentIndex(selector->count() - 1);
                }
        }
@@ -1523,8 +1522,8 @@ void DecodeTrace::on_channel_selected(int)
        QComboBox *cb = qobject_cast<QComboBox*>(QObject::sender());
 
        // Determine signal that was selected
-       const data::SignalBase *signal =
-               (data::SignalBase*)cb->itemData(cb->currentIndex()).value<void*>();
+       shared_ptr<data::SignalBase> signal =
+               cb->itemData(cb->currentIndex()).value<shared_ptr<data::SignalBase>>();
 
        // Determine decode channel ID this combo box is the channel selector for
        const uint16_t id = channel_id_map_.at(cb);