Merge lp:~renatofilho/ubuntu-filemanager-app/fix-network-crash into lp:ubuntu-filemanager-app

Proposed by Renato Araujo Oliveira Filho
Status: Needs review
Proposed branch: lp:~renatofilho/ubuntu-filemanager-app/fix-network-crash
Merge into: lp:ubuntu-filemanager-app
Diff against target: 196 lines (+47/-4)
7 files modified
src/plugin/folderlistmodel/diriteminfo.cpp (+19/-0)
src/plugin/folderlistmodel/diriteminfo.h (+6/-0)
src/plugin/folderlistmodel/iorequestworker.cpp (+7/-0)
src/plugin/folderlistmodel/location.cpp (+0/-1)
src/plugin/folderlistmodel/location.h (+0/-1)
src/plugin/folderlistmodel/networklistworker.cpp (+10/-1)
src/plugin/folderlistmodel/networklistworker.h (+5/-1)
To merge this branch: bzr merge lp:~renatofilho/ubuntu-filemanager-app/fix-network-crash
Reviewer Review Type Date Requested Status
Jenkins Bot continuous-integration Needs Fixing
Renato Araujo Oliveira Filho (community) Disapprove
Carlos Jose Mazieri Needs Information
Review via email: mp+314870@code.launchpad.net

Commit message

Make sure that NetworkListWorker get invalidated if the parent DirItemInfo was destroyed.

App was crashing because DirItemInfo was destroyed and NetworkListWorker was trying to use an invalid pointer.

To post a comment you must log in.
587. By Renato Araujo Oliveira Filho

Fix grammar.

Revision history for this message
Pat McGowan (pat-mcgowan) wrote :

Can you explain how this change addresses the issue?

Revision history for this message
Carlos Jose Mazieri (carlos-mazieri) wrote :

Renato,

The bug description appears to say that it always crashes.

Can you confirm that it always crashes or it happens under some special condition?

If it happens under special condition can you describe that scenario?

review: Needs Information
Revision history for this message
Renato Araujo Oliveira Filho (renatofilho) wrote :

> Renato,
>
> The bug description appears to say that it always crashes.
>
> Can you confirm that it always crashes or it happens under some special
> condition?
>
> If it happens under special condition can you describe that scenario?

It always crash for me.

Revision history for this message
Carlos Jose Mazieri (carlos-mazieri) wrote :

Does it crash on both phone and desktop or just phone?

review: Needs Information
Revision history for this message
Carlos Jose Mazieri (carlos-mazieri) wrote :

OK, Let's have a review on it,

1. perhaps the slot onParentDestroyed() on location.h lines 94-96 is not being used.

2. on line 38 from networklistworker.cpp we should check if m_parent != 0,
   the default value for 'parent' is 0 on networklistworker.h

   38: parent->connectDestructionSignal(this, SLOT(onParentDestroyed()));

3. are you sure the code from networklistworker.cpp lines 57-58 are really necessary?
   the default value for 'parent' is 0 on networklistworker.h, and, if onParentDestroyed() is called
   m_parent is set to 0 and the "m_parent != 0" is checked on line 61.

   57: if (!m_parent)
   58: return netContent;

   61: bool is_parent_of_smb_url = m_parent != 0 && m_parent->urlPath().startsWith(LocationUrl::SmbURL);

   if you want keep lines 57-58, please change to "if (m_parent != 0)" as m_parent is not a boolean type.

4. can you explain why you removed the call to "refreshInfo()" from Location::currentInfo()
   on location.cpp line 329?

   I see that both Location::refreshInfo() and Location::becomeParent() delete the 'm_info',
   this is the 'parent' used in NetworkListWorker class on the second thread.
   That may be the real problem, perhaps just making an attribution into 'm_info'
   from the temporary item could solve the entire problem, see a grep output:

     ./src/plugin/folderlistmodel/trash/trashlocation.cpp:57: delete m_info;
     ./src/plugin/folderlistmodel/trash/trashlocation.cpp:176: delete m_info;
     ./src/plugin/folderlistmodel/location.cpp:64: delete m_info;
     ./src/plugin/folderlistmodel/location.cpp:96: delete m_info;
     ./src/plugin/folderlistmodel/location.cpp:238: delete m_info;
     ./src/plugin/folderlistmodel/location.cpp:252: delete m_info;

   Would you consider (adding this) or reconsider for this item #4 only?

review: Needs Information
Revision history for this message
Pat McGowan (pat-mcgowan) wrote :

It always crashes and on both phone and desktop

Revision history for this message
Renato Araujo Oliveira Filho (renatofilho) wrote :

Thanks for reviewing it.

> OK, Let's have a review on it,
>
> 1. perhaps the slot onParentDestroyed() on location.h lines 94-96 is not being
> used.
REMOVED

>
> 2. on line 38 from networklistworker.cpp we should check if m_parent != 0,
> the default value for 'parent' is 0 on networklistworker.h
>
> 38: parent->connectDestructionSignal(this, SLOT(onParentDestroyed()));
FIXED

>
> 3. are you sure the code from networklistworker.cpp lines 57-58 are really
> necessary?
> the default value for 'parent' is 0 on networklistworker.h, and, if
> onParentDestroyed() is called
> m_parent is set to 0 and the "m_parent != 0" is checked on line 61.
>
> 57: if (!m_parent)
> 58: return netContent;
>
> 61: bool is_parent_of_smb_url = m_parent != 0 &&
> m_parent->urlPath().startsWith(LocationUrl::SmbURL);
>
> if you want keep lines 57-58, please change to "if (m_parent != 0)" as
> m_parent is not a boolean type.
This is not necessary (REMOVED)

>
> 4. can you explain why you removed the call to "refreshInfo()" from
> Location::currentInfo()
> on location.cpp line 329?
>
> I see that both Location::refreshInfo() and Location::becomeParent() delete
> the 'm_info',
> this is the 'parent' used in NetworkListWorker class on the second thread.
> That may be the real problem, perhaps just making an attribution into
> 'm_info'
> from the temporary item could solve the entire problem, see a grep output:
>
> ./src/plugin/folderlistmodel/trash/trashlocation.cpp:57:
> delete m_info;
> ./src/plugin/folderlistmodel/trash/trashlocation.cpp:176: delete
> m_info;
> ./src/plugin/folderlistmodel/location.cpp:64: delete m_info;
> ./src/plugin/folderlistmodel/location.cpp:96: delete m_info;
> ./src/plugin/folderlistmodel/location.cpp:238: delete m_info;
> ./src/plugin/folderlistmodel/location.cpp:252: delete m_info;
>
> Would you consider (adding this) or reconsider for this item #4 only?

We not not have a pointer to the temporary object (NetworkWorker) from Location class. Or do you have a way to access it?

588. By Renato Araujo Oliveira Filho

Remove unused code.
Check for parent before use it.

Revision history for this message
Carlos Jose Mazieri (carlos-mazieri) wrote :

Renato,

by temporary objects I mean a temporary DirItemInfo created in Location?? classes, example:

void Location::refreshInfo()
{
    if (m_info)
    {
        DirItemInfo *item = newItemInfo(m_info->absoluteFilePath());
        delete m_info;
        m_info = item;
    }
}

Would be changed by:

void Location::refreshInfo()
{
    if (m_info)
    {
        DirItemInfo *item = newItemInfo(m_info->absoluteFilePath());
        *m_info = *item; // current m_info item receives current information
        delete item;
    }
}

I am not sure the operator "=" works properly for all DirItemInfo descendant classes.

Revision history for this message
Carlos Jose Mazieri (carlos-mazieri) wrote :

Another and easy approach would be if the class NetworkListWorker receive again a const DirItemInfo *parent, but sets m_parent as its own instance using the operator '=', in this case it never gets deleted outside the worker thread.

Suppose the code changed:

NetworkListWorker::NetworkListWorker(LocationItemDirIterator * dirIterator,
                                     DirItemInfo * mainItemInfo,
                                     const DirItemInfo *parent) :
    DirListWorker(dirIterator->path(),
                  dirIterator->filters(),
                  dirIterator->flags() == QDirIterator::Subdirectories ? true : false),
    m_dirIterator(dirIterator),
    m_mainItemInfo(mainItemInfo),
    m_parent(0)
{
     if (parent != 0)
     {
        m_parent = new UrlItemInfo(); // UrlItemInfo is for remote
        *m_parent = *parent; // not sure it works, needs be reviewed
     }
     mLoaderType = NetworkLoader;

     // this would not be necessary and can be removed
     parent->connectDestructionSignal(this, SLOT(onParentDestroyed()));
}

NetworkListWorker::~NetworkListWorker()
{
    delete m_dirIterator;
    delete m_mainItemInfo;
    if (m_parent != 0)
    {
       delete m_parent;
    }
}

Revision history for this message
Arto Jalkanen (ajalkane) wrote :

I'm a bit dumbfounded that this kind of Qt metaprogramming would be necessary for solving a problem as basic as what we have here. At the minimum I think this code would need to be commented, because if anyone stumbles upon this code in the future it'd be a big WTF moment without clarifying comments. Comments should explain what it fixes, why it had to be done this way.

Revision history for this message
Renato Araujo Oliveira Filho (renatofilho) wrote :

> Another and easy approach would be if the class NetworkListWorker receive
> again a const DirItemInfo *parent, but sets m_parent as its own instance using
> the operator '=', in this case it never gets deleted outside the worker
> thread.
>
> Suppose the code changed:
>
>
> NetworkListWorker::NetworkListWorker(LocationItemDirIterator * dirIterator,
> DirItemInfo * mainItemInfo,
> const DirItemInfo *parent) :
> DirListWorker(dirIterator->path(),
> dirIterator->filters(),
> dirIterator->flags() == QDirIterator::Subdirectories ? true
> : false),
> m_dirIterator(dirIterator),
> m_mainItemInfo(mainItemInfo),
> m_parent(0)
> {
> if (parent != 0)
> {
> m_parent = new UrlItemInfo(); // UrlItemInfo is for remote
> *m_parent = *parent; // not sure it works, needs be reviewed
> }
> mLoaderType = NetworkLoader;
>
> // this would not be necessary and can be removed
> parent->connectDestructionSignal(this, SLOT(onParentDestroyed()));
> }
>
>
> NetworkListWorker::~NetworkListWorker()
> {
> delete m_dirIterator;
> delete m_mainItemInfo;
> if (m_parent != 0)
> {
> delete m_parent;
> }
> }

With that code the operation will be executed even after the "parent" object be destroyed what we do not want. It is waste of resource.

Revision history for this message
Renato Araujo Oliveira Filho (renatofilho) wrote :

> I'm a bit dumbfounded that this kind of Qt metaprogramming would be necessary
> for solving a problem as basic as what we have here. At the minimum I think
> this code would need to be commented, because if anyone stumbles upon this
> code in the future it'd be a big WTF moment without clarifying comments.
> Comments should explain what it fixes, why it had to be done this way.

The Qt metaprogramming make things easy here. Avoid big changes. Without that we will need to keep a list of child objects on the parent and invalidate all the children when parent get destroyed.

Instead of that I am keep all the changes on child object and keeping track of parent life. Any suggestion are welcome.

Revision history for this message
Carlos Jose Mazieri (carlos-mazieri) wrote :

I would remove all the Qt metaprogramming as it is not necessary,

Only a small change in the NetworkListWorker as exposed above should solve the problem, NetworkListWorker class can create its own instance by doing a copy.

Revision history for this message
Carlos Jose Mazieri (carlos-mazieri) wrote :

Renato,

Can you please test that proposal of that NetworkListWorker creating its own instance of the parent item?

There is a proposal https://code.launchpad.net/~carlos-mazieri/ubuntu-filemanager-app//fix-network-crash-1609051

Also renamed "parent" to "parentItemInfo".

Revision history for this message
Renato Araujo Oliveira Filho (renatofilho) wrote :

> Renato,
>
> Can you please test that proposal of that NetworkListWorker creating its own
> instance of the parent item?
>
> There is a proposal https://code.launchpad.net/~carlos-mazieri/ubuntu-
> filemanager-app//fix-network-crash-1609051
>
> Also renamed "parent" to "parentItemInfo".

Thanks, Carlos will be a pleasure test that.

But as mentioned before if the parent get destroyed the operation will not be canceled and will be executed until the end. (In my opinion this is a wast of resources, but if you are ok with that)

Based on that information I do not think you need to check for (m_parentItemInfo != 0) in line 61. Since m_parentItemInfo will never be null.

Revision history for this message
Carlos Jose Mazieri (carlos-mazieri) wrote :

Renato,

The check in line 61 is necessary.

The 'parentItemInfo' keeps the information from the parent, which can be: directory/share/host/workgroup.

When the 'mainItemInfo' is root like "smb://" there is no 'parentItemInfo', it can be null.

Another reason it might be used in my regression tests (I am not sure if it is), you can use if you want its in "src/plugin/test_folderlistmodel/regression/regression_folderlilstmodel.pro"

Revision history for this message
Renato Araujo Oliveira Filho (renatofilho) wrote :
review: Disapprove
Revision history for this message
Jenkins Bot (ubuntu-core-apps-jenkins-bot) wrote :
review: Needs Fixing (continuous-integration)

Unmerged revisions

588. By Renato Araujo Oliveira Filho

Remove unused code.
Check for parent before use it.

587. By Renato Araujo Oliveira Filho

Fix grammar.

586. By Renato Araujo Oliveira Filho

Remove extra debug messages.

585. By Renato Araujo Oliveira Filho

Fixed memory leak;

Make sure that works are deleted.

584. By Renato Araujo Oliveira Filho

Avoid crash when aborting a network operation.

Clear parent pointer on worker if parent get destroyed.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'src/plugin/folderlistmodel/diriteminfo.cpp'
--- src/plugin/folderlistmodel/diriteminfo.cpp 2015-12-12 12:30:47 +0000
+++ src/plugin/folderlistmodel/diriteminfo.cpp 2017-01-25 16:54:04 +0000
@@ -23,6 +23,8 @@
23#include "locationurl.h"23#include "locationurl.h"
24#include <sys/types.h>24#include <sys/types.h>
25#include <sys/stat.h>25#include <sys/stat.h>
26#include <QMetaMethod>
27#include <QDebug>
2628
2729
28QMimeDatabase DirItemInfoPrivate::mimeDatabase;30QMimeDatabase DirItemInfoPrivate::mimeDatabase;
@@ -151,6 +153,13 @@
151153
152DirItemInfo::~DirItemInfo()154DirItemInfo::~DirItemInfo()
153{155{
156 Q_FOREACH(const DestructionSlot &slot, d_ptr->_destructionSlots) {
157 if (slot.first) {
158 const QMetaObject *mObject = slot.first->metaObject();
159 QMetaMethod method = mObject->method(slot.second);
160 method.invoke(slot.first.data(), Qt::DirectConnection);
161 }
162 }
154}163}
155164
156165
@@ -602,3 +611,13 @@
602{611{
603 d_ptr->_isNetworkShare = true;612 d_ptr->_isNetworkShare = true;
604}613}
614
615void DirItemInfo::connectDestructionSignal(QObject *obj, const QByteArray &slot)
616{
617 int index = obj->metaObject()->indexOfSlot(slot.constData()+1);
618 if (index == -1) {
619 qWarning() << "Object" << obj << "does not have slot named:" << slot;
620 } else {
621 d_ptr->_destructionSlots.append(qMakePair(QPointer<QObject>(obj), index));
622 }
623}
605624
=== modified file 'src/plugin/folderlistmodel/diriteminfo.h'
--- src/plugin/folderlistmodel/diriteminfo.h 2015-12-12 12:30:47 +0000
+++ src/plugin/folderlistmodel/diriteminfo.h 2017-01-25 16:54:04 +0000
@@ -30,6 +30,8 @@
30#include <QDir>30#include <QDir>
31#include <QMimeType>31#include <QMimeType>
32#include <QMimeDatabase>32#include <QMimeDatabase>
33#include <QPair>
34#include <QPointer>
3335
34class DirItemInfoPrivate;36class DirItemInfoPrivate;
3537
@@ -114,6 +116,7 @@
114 void fillFromStatBuf(const struct stat& statBuffer);116 void fillFromStatBuf(const struct stat& statBuffer);
115 void setAsHost();117 void setAsHost();
116 void setAsShare();118 void setAsShare();
119 void connectDestructionSignal(QObject *obj, const QByteArray &slot);
117120
118public:121public:
119 static QString removeExtraSlashes(const QString &url, int firstSlashIndex = -1);122 static QString removeExtraSlashes(const QString &url, int firstSlashIndex = -1);
@@ -134,6 +137,7 @@
134};137};
135138
136typedef QVector<DirItemInfo> DirItemInfoList;139typedef QVector<DirItemInfo> DirItemInfoList;
140typedef QPair<QPointer<QObject>, int> DestructionSlot;
137141
138Q_DECLARE_SHARED(DirItemInfo)142Q_DECLARE_SHARED(DirItemInfo)
139Q_DECLARE_METATYPE(DirItemInfo)143Q_DECLARE_METATYPE(DirItemInfo)
@@ -180,6 +184,8 @@
180 QString _normalizedPath;184 QString _normalizedPath;
181 QString _authenticationPath;185 QString _authenticationPath;
182186
187 QVector<DestructionSlot> _destructionSlots;
188
183 static QMimeDatabase mimeDatabase;189 static QMimeDatabase mimeDatabase;
184};190};
185191
186192
=== modified file 'src/plugin/folderlistmodel/iorequestworker.cpp'
--- src/plugin/folderlistmodel/iorequestworker.cpp 2013-10-12 19:25:17 +0000
+++ src/plugin/folderlistmodel/iorequestworker.cpp 2017-01-25 16:54:04 +0000
@@ -36,6 +36,8 @@
36#include <QDateTime>36#include <QDateTime>
37#include <QDebug>37#include <QDebug>
3838
39#define ORIGNAL_THREAD "ORIGINAL_THREAD"
40
39/*!41/*!
40 Lives on an IOWorkerThread.42 Lives on an IOWorkerThread.
4143
@@ -55,6 +57,7 @@
55 << Q_FUNC_INFO;57 << Q_FUNC_INFO;
56#endif58#endif
5759
60 request->setProperty(ORIGNAL_THREAD, QVariant::fromValue<QThread*>(request->thread()));
58 request->moveToThread(this);61 request->moveToThread(this);
5962
60 // TODO: queue requests so we run the most important one first63 // TODO: queue requests so we run the most important one first
@@ -82,7 +85,11 @@
82 lock.unlock();85 lock.unlock();
8386
84 request->run();87 request->run();
88
89 // transfer back to main loop to make sure that will be destoyed by deleteLater
90 request->moveToThread(request->property(ORIGNAL_THREAD).value<QThread*>());
85 request->deleteLater();91 request->deleteLater();
92
86 lock.relock();93 lock.relock();
87 }94 }
88 }95 }
8996
=== modified file 'src/plugin/folderlistmodel/location.cpp'
--- src/plugin/folderlistmodel/location.cpp 2015-12-08 16:55:41 +0000
+++ src/plugin/folderlistmodel/location.cpp 2017-01-25 16:54:04 +0000
@@ -326,6 +326,5 @@
326 {326 {
327 m_info = new DirItemInfo();327 m_info = new DirItemInfo();
328 }328 }
329 refreshInfo(); //update information
330 return m_info;329 return m_info;
331}330}
332331
=== modified file 'src/plugin/folderlistmodel/location.h'
--- src/plugin/folderlistmodel/location.h 2015-12-12 13:59:49 +0000
+++ src/plugin/folderlistmodel/location.h 2017-01-25 16:54:04 +0000
@@ -91,7 +91,6 @@
91 virtual void setAuthentication(const QString& user,91 virtual void setAuthentication(const QString& user,
92 const QString& password);92 const QString& password);
9393
94
95public: //pure functions94public: //pure functions
96 /*!95 /*!
97 * \brief newItemInfo() returns a Location suitable DirItemInfo object96 * \brief newItemInfo() returns a Location suitable DirItemInfo object
9897
=== modified file 'src/plugin/folderlistmodel/networklistworker.cpp'
--- src/plugin/folderlistmodel/networklistworker.cpp 2015-12-12 14:32:18 +0000
+++ src/plugin/folderlistmodel/networklistworker.cpp 2017-01-25 16:54:04 +0000
@@ -23,8 +23,10 @@
23#include "locationitemdiriterator.h"23#include "locationitemdiriterator.h"
24#include "locationurl.h"24#include "locationurl.h"
2525
26#include <QDebug>
27
26NetworkListWorker::NetworkListWorker(LocationItemDirIterator * dirIterator,28NetworkListWorker::NetworkListWorker(LocationItemDirIterator * dirIterator,
27 DirItemInfo * mainItemInfo, const DirItemInfo *parent) :29 DirItemInfo * mainItemInfo, DirItemInfo *parent) :
28 DirListWorker(dirIterator->path(),30 DirListWorker(dirIterator->path(),
29 dirIterator->filters(),31 dirIterator->filters(),
30 dirIterator->flags() == QDirIterator::Subdirectories ? true : false),32 dirIterator->flags() == QDirIterator::Subdirectories ? true : false),
@@ -33,6 +35,8 @@
33 m_parent(parent)35 m_parent(parent)
34{36{
35 mLoaderType = NetworkLoader;37 mLoaderType = NetworkLoader;
38 if (parent)
39 parent->connectDestructionSignal(this, SLOT(onParentDestroyed()));
36}40}
3741
3842
@@ -42,10 +46,15 @@
42 delete m_mainItemInfo;46 delete m_mainItemInfo;
43}47}
4448
49void NetworkListWorker::onParentDestroyed()
50{
51 m_parent = 0;
52}
4553
46DirItemInfoList NetworkListWorker::getNetworkContent()54DirItemInfoList NetworkListWorker::getNetworkContent()
47{55{
48 DirItemInfoList netContent;56 DirItemInfoList netContent;
57
49 m_dirIterator->load();58 m_dirIterator->load();
50 bool is_parent_of_smb_url = m_parent != 0 && m_parent->urlPath().startsWith(LocationUrl::SmbURL);59 bool is_parent_of_smb_url = m_parent != 0 && m_parent->urlPath().startsWith(LocationUrl::SmbURL);
51 while (m_dirIterator->hasNext())60 while (m_dirIterator->hasNext())
5261
=== modified file 'src/plugin/folderlistmodel/networklistworker.h'
--- src/plugin/folderlistmodel/networklistworker.h 2015-12-12 14:32:18 +0000
+++ src/plugin/folderlistmodel/networklistworker.h 2017-01-25 16:54:04 +0000
@@ -40,11 +40,15 @@
40public:40public:
41 NetworkListWorker(LocationItemDirIterator * dirIterator,41 NetworkListWorker(LocationItemDirIterator * dirIterator,
42 DirItemInfo * mainItemInfo,42 DirItemInfo * mainItemInfo,
43 const DirItemInfo * parent = 0);43 DirItemInfo * parent = 0);
44 ~NetworkListWorker();44 ~NetworkListWorker();
45public slots:
46 void onParentDestroyed();
47
45protected:48protected:
46 virtual DirItemInfoList getNetworkContent();49 virtual DirItemInfoList getNetworkContent();
47 void setSmbItemAttributes();50 void setSmbItemAttributes();
51
48protected:52protected:
49 LocationItemDirIterator * m_dirIterator;53 LocationItemDirIterator * m_dirIterator;
50 DirItemInfo * m_mainItemInfo;54 DirItemInfo * m_mainItemInfo;

Subscribers

People subscribed via source and target branches