[vtkusers] Returning filter output from thread using shallow copying vs smart pointer

Elvis Stansvik elvis.stansvik at orexplore.com
Wed Jul 6 03:05:41 EDT 2016


2016-07-06 8:50 GMT+02:00 Elvis Stansvik <elvis.stansvik at orexplore.com>:

> 2016-07-05 23:57 GMT+02:00 Shawn Waldon <shawn.waldon at kitware.com>:
>
>>
>>
>> On Tue, Jul 5, 2016 at 5:45 PM, Elvis Stansvik <
>> elvis.stansvik at orexplore.com> wrote:
>>
>>> 2016-07-05 23:30 GMT+02:00 Shawn Waldon <shawn.waldon at kitware.com>:
>>>
>>>> Hi Elvis,
>>>>
>>>> I've done something fairly similar in one of my applications, returning
>>>> a vtkSmartPointer<vtkImageData> via a Qt signal/slot from a background
>>>> thread.  And as far as I know the reference counting should work fine since
>>>> the reference count is an atomic integer.  It has worked fine for me so far
>>>> and VTK_DEBUG_LEAKS doesn't complain so the reference counts are working
>>>> somehow.
>>>>
>>>> My guess about your reference count question:  You are returning the
>>>> smart pointer by value.  The QFuture must be storing the smart pointer so
>>>> that it can return it from the result() function.  And when you return it
>>>> that way it creates a second smart pointer, which would increment the
>>>> reference count to 2.
>>>>
>>>
>>> Ah, of course. Thanks! So then it's not a problem, my QFuture will be
>>> destroyed shortly thereafter as well.
>>>
>>> And thanks for also bringing up VTK_DEBUG_LEAKS, I didn't know about.
>>>
>>> But, so I can assume that both approaches (raw pointer, return a shallow
>>> copy) and returning a vtkSmartPointer are safe ways of doing this in this
>>> case? Nothing in the VTK ref counting machinery that could go wrong?
>>>
>>
>> The main thing to avoid is the reference count of the vtk object going to
>> 0.  So I think the safest way to do this is to pass the vtkSmartPointer
>> around.  You could probably pass the raw pointer if you tweaked the
>> reference count manually, but the whole point of smart pointers is to avoid
>> having to do that.  I don't really see a reason to do a shallow copy.  If
>> the background thread was going to hang onto the object to do further
>> changes to it, you would want a deep copy since most vtk objects are not
>> threadsafe.  Keep in mind that if the filter that produced it is
>> re-executed then the data object will get overwritten (most filters reuse
>> output objects when re-executed).  But it sounds like you are letting the
>> pipeline that produced it be deleted while just keeping the object itself.
>>
>
> Yes, that seemed like the safest thing to do. To have it as a one-off
> thread which just produces a result, not one that is reused.
>

I realize now that one thing that I do lose with the approach I took is
requests for subextents (e.g. using vtkExtractVOI), which my reader is
ready to handle. I always have to read the entire volume as it is now.

What would be the best thing to do if I want to keep support for requesting
subextents? To make a vtkExtractVOI work downstream, would I have to make a
special "source" filter that spins of a thread to do the reading? That
wouldn't work anyway I think, because then I'd block in the RequestData of
the filter that is one down from that special source filter, while the
thread is doing its work... not sure there is a solution for this..?

Elvis


> The shallow copying is something I found from Berk in this old thread (no
> pun intended) from 2014:
>
>     http://public.kitware.com/pipermail/vtkusers/2014-March/083477.html
>
> In his example there he passes by raw pointer across threads, and use
> double shallow copying (shallow copying both in the producer and consumer
> thread).
>
> But maybe that was just something that had to be done in the past, when
> the refcounting machinery was more susceptible to corruption in a
> multi-thread scenario?
>
> Berk, do you remember the reason for the shallow copying? Because like
> Shawn above, I don't quite see the reason for it either, now that I think
> about it.
>
>
>>
>>>
>>> Do you know if there are any things/gotchas or parts of VTK to watch out
>>> for when putting parts of a pipeline in a separate thread like this?
>>>
>>
>> The only thing I know of is that rendering and associated things
>> (mappers, actors, etc) have to be done in the foreground thread.  If others
>> on the list know about more, I would be glad to learn about them too.
>>
>
> Alright, yes that I've gathered as well.
>
> Another thing I've seen warned against is having pipeline connections
> across threads. That is, a filter must not have its input connection set to
> the output port of a filter living in another thread. Is that also still
> generally true?
>
> Elvis
>
>
>>
>> HTH,
>> Shawn
>>
>>
>>> Elvis
>>>
>>>
>>>>
>>>> HTH,
>>>> Shawn
>>>>
>>>> On Tue, Jul 5, 2016 at 5:11 PM, Elvis Stansvik <
>>>> elvis.stansvik at orexplore.com> wrote:
>>>>
>>>>> Just as I was going to ask a threading question on this list, someone
>>>>> else did :) Oh well here's another one:
>>>>>
>>>>> I'm rendering a compressed volume, and I've noticed that a big chunk
>>>>> of the time-to-first-render for me is the initial reading of the volume. To
>>>>> keep the UI responsive while the reading takes place, I decided to give
>>>>> QtConcurrent::run + QFuture + QFutureWatcher a try as a mechanism to put
>>>>> the reading in a different thread.
>>>>>
>>>>> Here's a shortened version of what I did:
>>>>>
>>>>>     auto future = QtConcurrent::run([&]() -> vtkImageData * {
>>>>>         auto reader = vtkSmartPointer<HDF5ImageReader>::New();
>>>>>         reader->setFileName("big_compressed_volume.hdf5");
>>>>>         reader->Update();
>>>>>
>>>>>         auto output = reader->GetOutput();
>>>>>         auto result = output->NewInstance();
>>>>>         result->ShallowCopy(output);
>>>>>
>>>>>         return result;
>>>>>     });
>>>>>
>>>>>     auto futureWatcher = new QFutureWatcher<vtkImageData *>(this);
>>>>>
>>>>>     connect(futureWatcher, &QFutureWatcher<vtkImageData *>::finished,
>>>>> [=]() {
>>>>>         auto imageData = futureWatcher->result();
>>>>>
>>>>>         // ... Create the rendering part of the pipeline
>>>>>
>>>>>         GetRenderWindow()->Render();
>>>>>     });
>>>>>
>>>>>     futureWatcher->setFuture(future);
>>>>>
>>>>> This works great. Using QtConcurrent::run, I spin up a thread in which
>>>>> the reading takes place. When the resulting vtkImageData is ready, I return
>>>>> a shallow copy of it, which in turn causes the QFutureWatcher I set up to
>>>>> emit the finished signal, upon which I construct the remaining (rendering)
>>>>> part of the pipeline (which doesn't take much time compared to the reading,
>>>>> about ~1/6 of the time).
>>>>>
>>>>> Now to my questions:
>>>>>
>>>>> 1. Is this safe? Since the reader is destroyed by the time the rest of
>>>>> the pipeline takes over (courtesy of using Qt signal delivery as a
>>>>> synchronization point), there's no risk of concurrent access to VTK objects
>>>>> here, right?
>>>>>
>>>>> 2. Can I always use this approach for segments of my pipeline up until
>>>>> the rendering parts (up to the mapper?), or are there non-rendering parts
>>>>> of VTK which simply cannot run in a separate thread?
>>>>>
>>>>> 3. Regarding the shallow copying, I first tried to return just
>>>>>
>>>>>         return vtkSmartPointer<vtkImageData>(reader->GetOutput());
>>>>>
>>>>> from the reader function. My thinking with this was that
>>>>>
>>>>>    * I create a smart pointer, which should raise the ref count of the
>>>>> object to 2, since the reader itself is still alive at that point and also
>>>>> holds a reference,
>>>>>    * a copy is then made of the smart pointer (for the return value of
>>>>> the function), raising the refcount to 3,
>>>>>    * when the function finally returns, the vtkSmartPointer I created,
>>>>> as well as the vtkSmartPointer holding the reader goes out of scope and
>>>>> destructs, which means the ref count goes down to 1.
>>>>>
>>>>> But that's not what happens, I had a look at the ref count of the
>>>>> image data I finally got in my slot (from futureWatcher->result()), and it
>>>>> was 2, not 1 like I expected.
>>>>>
>>>>> I'm obviously misunderstanding something here, anyone care to
>>>>> enlighten me? Does the ref counting mechanism not work across thread
>>>>> boundaries?
>>>>>
>>>>> Many thanks in advance,
>>>>>
>>>>> Elvis
>>>>>
>>>>> _______________________________________________
>>>>> Powered by www.kitware.com
>>>>>
>>>>> Visit other Kitware open-source projects at
>>>>> http://www.kitware.com/opensource/opensource.html
>>>>>
>>>>> Please keep messages on-topic and check the VTK FAQ at:
>>>>> http://www.vtk.org/Wiki/VTK_FAQ
>>>>>
>>>>> Search the list archives at: http://markmail.org/search/?q=vtkusers
>>>>>
>>>>> Follow this link to subscribe/unsubscribe:
>>>>> http://public.kitware.com/mailman/listinfo/vtkusers
>>>>>
>>>>>
>>>>
>>>
>>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://public.kitware.com/pipermail/vtkusers/attachments/20160706/438abad1/attachment.html>


More information about the vtkusers mailing list