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

Elvis Stansvik elvis.stansvik at orexplore.com
Wed Jul 6 02:50:17 EDT 2016


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.

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/31370992/attachment.html>


More information about the vtkusers mailing list