> On April 24, 2016, 1:57 nachm., 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.
> 
> David Faure wrote:
>     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.

So, in a nutshell I just lock the mutex before writing to m_update / m_unitMap 
and unlock again rather than always locking at the start of the method that may 
spawn a nested event loop?


- Kai Uwe


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


On April 24, 2016, 1:26 nachm., 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 nachm.)
> 
> 
> 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