-
Notifications
You must be signed in to change notification settings - Fork 296
Description
The CanvasEffect::GetD2DImage function contains some code to detect a cycle in the effect graph. The cycle detection is outside the lock, because, as commented in the code, the lock is not recursive. However, the code also has the comment Cycle checks don't need to be threadsafe because that's just a developer error., which is completely false, because calling the function simultaneously on 2 different threads triggers a cycle detection! Here is the offending code.
ComPtr<ID2D1Image> CanvasEffect::GetD2DImage(ICanvasDevice* device, ID2D1DeviceContext* deviceContext, WIN2D_GET_D2D_IMAGE_FLAGS flags, float targetDpi, float* realizedDpi)
{
ThrowIfClosed();
// Check for graph cycles
if (m_insideGetImage)
ThrowHR(D2DERR_CYCLIC_GRAPH);
m_insideGetImage = true;
auto clearFlagWarden = MakeScopeWarden([&] { m_insideGetImage = false; });
// Lock after the cycle detection, because m_mutex is not recursive.
// Cycle checks don't need to be threadsafe because that's just a developer error.
auto lock = Lock(m_mutex);
//... code continuesI will create a PR to fix it but @Sergio0694 I would like your advice. I think the best fix is to change the lock to a recursive lock then move the lock before the cycle detection. Are you happy with that? Obviously the lock is used elsewhere but I can't see a problem with using a recursive lock instead of a non-recursive one (I know some purists don't like using recursive locks, but I really don't see why you would want to increase the risk of undefined behaviour by using a non-recursive lock, given the performance difference is probably tiny).
Also, I don't really know how to do PRs in this repo with the different branches. I am actually still using UWP personally so I will probably stick to that branch. Also, I'm a bit hesitant to update my fork to the latest version because of the previous deadlock fixes I made an potential conflicts that I don't have time to deal with right now, so I might base the PR on an old Win2D version. Probably this method hasn't been touched since then so it should probably be OK.
I guess with this change if there is a cycle and we call from 2 threads simultaneously, then there may be deadlock (instead of the current technically undefined but in practice cycle detection error throwing behaviour), but cycles are an error anyway so we probably shouldn't worry too much about that.