Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Disable processing socket events in findIncompleteFiles() #7445

Merged
merged 1 commit into from
Oct 25, 2017

Conversation

Chocobo1
Copy link
Member

Related to bda5be8, but this time is from webUI.

glassez
glassez previously approved these changes Sep 14, 2017
@zeule
Copy link
Contributor

zeule commented Sep 14, 2017

Could you explain, please, why this is needed? Don't understand neither this change nor the mentioned bda5be8.

@Chocobo1
Copy link
Member Author

Could you explain, please, why this is needed? Don't understand neither this change nor the mentioned bda5be8.

You will need to see the crash report in #7327.
findIncompleteFiles() was called many times before the previous instance can complete and since we have bda5be8, it should make sense to make this change. Also in #7436, the op says it was crashing when adding torrent via webUI.
Said that much, however, I still cannot pinpoint the root cause of crash, only assuming this change could alleviate the problem.

@zeule
Copy link
Contributor

zeule commented Sep 14, 2017

Thank you. Don't understand anyway why and how findIncompleteFiles() was called repeatedly. Could you explain, please?

@Chocobo1
Copy link
Member Author

Don't understand anyway why and how findIncompleteFiles() was called repeatedly. Could you explain, please?

due to qApp->processEvents(). From what I know, users can add torrents via webUI fairly quick.

@zeule
Copy link
Contributor

zeule commented Sep 14, 2017

I still can't make any sense out of it :(. Why did it crash? What are the other kind of events that you want to process in the loop inside findIncompleteFiles()?

@Chocobo1
Copy link
Member Author

I still can't make any sense out of it :(. Why did it crash?

Thread safety issues? ¯\(ツ)

What are the other kind of events that you want to process in the loop inside findIncompleteFiles()?

No solid idea either, although I suspect some GUI painting events to make qbt appear active.

@zeule
Copy link
Contributor

zeule commented Sep 14, 2017

No solid idea either, although I suspect some GUI painting events to make qbt appear active.

When web UI and GUI are used simultaneously?

@glassez, could you tell us, why did it crash and why it was fixed by bda5be8?

@Chocobo1 Chocobo1 added this to the 3.4.0 milestone Sep 20, 2017
@glassez
Copy link
Member

glassez commented Oct 13, 2017

@glassez, could you tell us, why did it crash and why it was fixed by bda5be8?

Ok. Let's see the following sequence:

  1. User add torrent via dialog and press Ok button
  2. AddNewTorrentDialog::accept() is called and it reaches BitTorrent::Session::addTorrent()
  3. BitTorrent::Session::addTorrent() is called and it reaches BitTorrent::Session::findIncompleteFiles()
  4. If torrent has many files then QCoreApplication::processEvents() can be called and it can process another "OK button click" event (if user click OK one more time for any reason)
  5. Steps 2-4 can be repeated for each additional button click
  6. When most inner event returns from BitTorrent::Session::addTorrent() it reaches QDialog::accept() that (in the pass) destroys the dialog itself
  7. The next level event returns from BitTorrent::Session::addTorrent() into method of already destroyed object and voila!

Something like this... Maybe I missed some details, or mixed up something, but the GENERAL MEANING should be clear.

@zeule
Copy link
Contributor

zeule commented Oct 13, 2017

Thanks for the explanations! Just one more question: why can't this be solved by disabling the dialog in AddNewTorrentDialog::accept()? Additionally the user would get visual response from the button click,

@glassez
Copy link
Member

glassez commented Oct 13, 2017

Additionally the user would get visual response from the button click

This can be a good idea in itself. But we cannot ensure that more than one "button click" event will not appear in the queue before the first of them will begin to be processed.

@Chocobo1
Copy link
Member Author

Chocobo1 commented Oct 20, 2017

Now I've a new view on the stack dump in #7436.

It's happening a lot in these few lines I believe:

QIODevice::write()    // pointC
Http::Connection::sendResponse(response)[ base\http\connection.cpp : 93 ]  // pointB
Http::Connection::read()[ base\http\connection.cpp : 85 ]  // pointA

Some background: A webUI socket connect will disconnect after 7 sec of idleness, see: https://github.com/qbittorrent/qBittorrent/blob/master/src/base/http/server.cpp#L64

  • At pointA (time==0), qbt is receiving a new request (specifically action_command_download()), this for unknown reason (accessing network drive?) is taking a lot of time (say 10sec), and then it hits qApp->processEvents().
  • At time==7, the socket timeout occurs, it proceeds to erase all traces of it.
  • At time==10, the action_command_download() has completed, it continues to reply (pointB) its result to the client, but the resources was removed at time==7, thus at pointC it is a use-after-free sigfault.

Now I think adding QEventLoop::ExcludeSocketNotifiers flag won't solve anything, because the socket timeout is still being processed by the event loop.
The only solution is removing qApp->processEvents() here. I never liked its place at here, it reorders events which should be blocking.

@glassez
Copy link
Member

glassez commented Oct 21, 2017

The only solution is removing qApp->processEvents() here. I never liked its place at here, it reorders events which should be blocking.

I don't like processEvents here too. The only its goal is to prevent GUI freezing when user adding torrent with many files.

@Chocobo1
Copy link
Member Author

I don't like processEvents here too. The only its goal is to prevent GUI freezing when user adding torrent with many files.

Or maybe enable processEvents only when webUI isn't used:

if (!webUIEnabled())
  qApp->processEvents(QEventLoop::ExcludeUserInputEvents);

@Chocobo1
Copy link
Member Author

PR updated.

@@ -2217,7 +2219,7 @@ bool Session::findIncompleteFiles(TorrentInfo &torrentInfo, QString &savePath) c
found = true;
torrentInfo.renameFile(i, filePath + QB_EXT);
}
if ((i % 100) == 0)
if (!isWebUIEnabled && ((i % 100) == 0)) // mitigate webAPI connection might timeout and result in use-after-free sigfault
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sEgfault

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also fixed in commit msg, thank you.

@glassez
Copy link
Member

glassez commented Oct 21, 2017

Or maybe enable processEvents only when webUI isn't used:

I think we should get rid of it at all. This can be a source of some hard to find errors. E.g. when we run the application, we must first renew all the existing torrents, and nothing should interfere in this process. How can we ensure this? Besides, it affects only torrents with really many files (I think they should be in the thousands).

@glassez
Copy link
Member

glassez commented Oct 21, 2017

And yet... Your solution creates an unacceptable dependency (core shouldn't be dependent from the user interface).

webUI connection timeout & deletion might occur while
doing processEvents() and will result in use-after-free segfault.
@Chocobo1
Copy link
Member Author

I think we should get rid of it at all.

OK, PR updated.

@glassez glassez requested a review from zeule October 24, 2017 05:21
@Chocobo1
Copy link
Member Author

If no one objects it, I'll merge it after waiting 24hrs.

@zeule
Copy link
Contributor

zeule commented Oct 24, 2017

Just a question: why nobody discusses a multi-threaded approach?

@glassez
Copy link
Member

glassez commented Oct 24, 2017

I thought about it. Maybe later, unless someone want to do it too.

@Chocobo1
Copy link
Member Author

Just a question: why nobody discusses a multi-threaded approach?

Session::findIncompleteFiles() looks like IO bound to me.

@Chocobo1 Chocobo1 merged commit 1f73917 into qbittorrent:master Oct 25, 2017
@Chocobo1 Chocobo1 deleted the fixAddTorrent branch October 25, 2017 15:00
@Chocobo1
Copy link
Member Author

Everyone thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants