Open Jayonas opened 6 years ago
Yes, I think that I didn't expect it to be thread safe to avoid costly lock for non multi-threading scenarios... but I think that's ok to add a lock in ToCallbackPtr
, PR welcome.
I've recently updated SharpDX, and I can confirm that this issue still exists in 4.2.0.
I had an issue where I was occasionally getting an
E_INVALIDARG
exception from my call toTextLayout.Draw
, and it happened more frequently the moreDraw
calls were happening around the same time, pointing to some kind of multi-threading style issue. It turned out that DWrite was receivingnullptr
for theIDWriteTextRenderer
parameter toIDWriteTextLayout::Draw
, and I verified that I always passed a non-null
managedTextRenderer
instance.Looks like the problem is in
CppObject.ToCallbackPtr
where anICallbackable
'sShadow
is created/initialized if necessary, because the check/creation/initialization isn't thread-safe. I'm pretty sure what happened was that thread A saw thatcallback.Shadow
wasnull
and proceeded to create aShadowContainer
and callInitialize
on it. The first thingInitialize
does is assign the new container to the callback'sShadow
, making it non-null
, and beforeInitialize
could finish (might easily block the thread due to acquiring a lock shortly thereafter) thread B came along and saw that the callback'sShadow
was non-null
, so proceeded to callFind
on it. Since it hadn't actually been initialized yet no native callback pointer was available soFind
returnednull
, and thread B passed thatnull
on down to DWrite.I was able to work around the problem by just manually calling
CppObject.ToCallbackPtr
in the constructor of any class that implementsICallbackable
(e.g. myTextRenderer
implementation). I just made a helper base type that inherits fromCallbackBase
and does that call in its constructor, and all my callback types derive from that instead ofCallbackBase
. That call ensures that each object'sShadow
is initialized as part of its construction process, so by the time multiple threads have their hands on the object and are trying to use it, they won't be racing to be the first one to initialize theShadow
.(Note that I'm basing this off my usage of SharpDX 3.1.1, which I realize is out of date, but I diagnosed the issue based on the latest SharpDX code.)