> On April 24, 2016, 1:57 p.m., David Faure wrote:
> > Better, but I'm still wary of the reentrancy due to the nested event loop 
> > usage.
> > 
> > I bet this leads to a complete deadlock:
> > 
> >     QTimer::singleShot(0, this, SLOT(launchConversion()));
> >     launchConversion();
> > 
> > where launchConversion() triggers the KUnitConversion code that this patch 
> > is about.
> > 
> > While waiting for the first conversion, the timer will fire (given the 
> > nested QEventLoop usage), and we'll enter this code again, and hit 
> > mutex.lock(), and then wait forever, since we'll never go back to the event 
> > loop to finish the first conversion and unlock the mutex.
> > 
> > Maybe the mutex can be removed, actually. All that needs to be done is for 
> > m_update to be turned into a local variable (there's no reason for it to be 
> > a member variable, right?). The QSaveFile usage fixes the case where two 
> > threads would write to m_cache at the same time, or the case where one 
> > writes and one reads. Or is the mutex needed for the if() at the top? In 
> > any case I recommend not holding the mutex while being in the event loop.
> > 
> > Sorry for not realizing this earlier.
> 
> Kai Uwe Broulik wrote:
>     From what I can tell the m_update is so it updates it either on first run 
> or when the cache file is outdated/missing. I don't know the internals of 
> KUnitConversion, ie. if an instance of Currency is shared or not, and how to 
> ensure that for subsequent runs of convert() I don't needlessly probe the 
> cache file again.
>     
>     The m_cache variable could also be made local to the convert() function.

I don't know much about kunitconversion either, so I had a look, and the reason 
that code leads to multithreading issue is indeed because all the instances of 
everything are shared between all threads (the entry point is the Converter 
class, and all Converter instances share the same global Private, which has 
maps to unit categories, which have maps to units. So yes, instances of 
CurrencyPrivate (which is the one that matters, not Currency) are shared 
(AFAICS).

The m_cache variable is a constant value set in the constructor, there's no 
problem with it being a member, and it doesn't need to be read with the mutex 
locked. The instance of CurrencyPrivate is created by the first thread which 
creates a Converter... hmm it's converter.cpp itself that is missing a mutex to 
protect m_categories, but that's another story.

I see the point about m_update, I was wrong about it not being useful as a 
member. Forgot the case where the cache already exists on disk, so we need to 
parse it on first run. That should however be done with the mutex locked, 
otherwise two threads might be loading the cache and setting the conversion 
factors into Units at the same time.

Anyway we're also back to: we need to handle the case of same-thread 
re-entrancy into this same code, due to the nested event loop. I don't see a 
way to wait for the download, from a nested event loop. So I see two solutions:
- starting another download and waiting for it (which requires that the mutex 
is not locked during the event loop; it has to be relocked before setting 
m_update though)
- or returning an error in this case (could be detected by keeping a list of 
QThread pointers, and adding currentThread to that list before starting the 
download). It's a corner case hopefully, but an error would certainly be better 
than a deadlock.

>From a correctness point of view the first solution seems better. It's also 
>simpler, it just means moving the use of the mutex around the data that is 
>written and read by multiple threads (as per definition of mutexes)... and 
>that's only m_update and m_unitMap, AFAICS.


- David


-----------------------------------------------------------
This is an automatically generated e-mail. To reply, visit:
https://git.reviewboard.kde.org/r/127251/#review94808
-----------------------------------------------------------


On April 24, 2016, 1:26 p.m., Kai Uwe Broulik wrote:
> 
> -----------------------------------------------------------
> This is an automatically generated e-mail. To reply, visit:
> https://git.reviewboard.kde.org/r/127251/
> -----------------------------------------------------------
> 
> (Updated April 24, 2016, 1:26 p.m.)
> 
> 
> Review request for KDE Frameworks and Vishesh Handa.
> 
> 
> Bugs: 345750
>     https://bugs.kde.org/show_bug.cgi?id=345750
> 
> 
> Repository: kunitconversion
> 
> 
> Description
> -------
> 
> QNetworkReply does not implement waitForReadyRead
> 
> Also, the code never actually created the cache dir it was trying to create a 
> file in.
> 
> 
> Diffs
> -----
> 
>   src/currency.cpp ad325d8 
> 
> Diff: https://git.reviewboard.kde.org/r/127251/diff/
> 
> 
> Testing
> -------
> 
> Works now. It's downloaded once and then taken from cache file in 
> ~/.local/share/libkunitconversion/currency.xml
> 
> Given it's a Tier 2 framework doesn't make sense to add KIO now, also failed 
> to reproduce the crashes mentioned in the code.
> 
> Tests pass (only if I run them on English locale btw)
> 
> Obviously not happy with this being sync but alas that's how the API works.
> 
> Not sure if this is a KRunner bug or KUnitConverison but if I enter "5 USD" 
> it converts fine, if I enter "5 usd" it returns zero for all the currencies 
> it converted to.
> 
> 
> Thanks,
> 
> Kai Uwe Broulik
> 
>

_______________________________________________
Kde-frameworks-devel mailing list
Kde-frameworks-devel@kde.org
https://mail.kde.org/mailman/listinfo/kde-frameworks-devel

Reply via email to