[Insight-developers] itk::LightObject non-threaded

Miller, James V (Research) millerjv@crd.ge.com
Fri, 5 Apr 2002 11:29:53 -0500


Ken, 

ITK has pretty much followed the VTK pattern of instance level thread
safety.  There is one complication in ITK.  In a multithreaded 
filter, the threaded method can call something like GetInput().
GetInput() returns a SmartPointer which modifies the reference
count on the data object. This reference count needs to be
protected by a mutex since all the threads may call GetInput()
at the same time. (This is different from VTK which requires
the developer to call Register() explictly if they want to 
hold onto a handle to the data object).

We could work around this issue with GetInput() (by passing
the data objects down to the threads explictly) other instances
of this problem exist.  Basically any threaded method that
calls anything that returns a smart pointer could have multiple
threads trying to modify the same reference count.

A solution to this that we discussed at the last developers
meeting that has not been implemented yet is to (almost)
never return smart pointers from a routine but return raw
pointers and give the developer the option of using that
raw pointer for a scope duration task or assign it to a
smart pointer is they need more persistence.

So, while the overall goal is to use instance level thread
safety supplemented with "read-access" thread
safety (where multiple threads can read from a shared object
without worrying about locking the resource (which also 
assumes that no one is writing to the shared resource)), 
the ReferenceCount/SmartPointer mechanism requires at
least a minimal support for write access thread safety to the
reference count ivar.



-----Original Message-----
From: Ken Martin [mailto:ken.martin@kitware.com]
Sent: Friday, April 05, 2002 9:39 AM
To: Luis Ibanez; Miller, James V (Research); Insight Developers
Subject: RE: [Insight-developers] itk::LightObject non-threaded


If you do not already have a convention for multithreading in ITK,
then you must first determine your convention before anything else.
There are a couple options I know about and they have serious
consequences. I've only worked a little with this so others may have
better information. I'm sure there is some formal terminology as well.

1) No thread safety - clearly this is easy to implement but prevents
anyone from using the advantages of multithreaded programming.

2 A) What I call instance level thread safety. This is what VTK tries
to do although it is not done that well. In this multiple threads can
be used but a single instance can only be safely accessed from a
single thread. This means that your code must protect all static data
with critical sections. If the static data is accessed in inner loops
this will cause a noticeable hit in performance. B) If an instance
wants to use multithreading within a method then it must be careful
not to write to any storage location that another thread could read
from or write to. This takes some extra effort on the part of the
developer.

3) Full multithread safety. In this case an instance may be accessed
by multiple threads at the same time. This is the easiest to work with
for an application developer. The toolkit development isn't that bad
either because almost everything will have the same thread based mutex
protection in it. (static methods being one exception) Specifically,
every method in the toolkit that reads from an ivar or writes to an
ivar must be put into a thread based critical section. For a toolkit
like ITK that means pretty much every method will have to check a
critical section.  This includes methods such as operator[] (it does a
read) etc. I mention thread based critical sections because many
methods do something like...

void foo::bar()
{
  this->GetCriticalSectionForThread();
  for (i = 0; i < this->NumberOfWidgets; ++i)
    {
    ...
    this->RealizeWidget(this->Widgets[i]);
    }
  this->ReleaseCriticalSectionForThread();
}

void foo::RealizeWidget(widgets *w)
{
  this->GetCriticalSectionForThread();
  // do something
  this->ReleaseCriticalSectionForThread();
}


The GetCriticalSectionForThread in RealizeWidget, when called from
bar(), will determine that the current thread already owns the
critical section for this instance so it will not block, but instead
increment that instance's CriticalSection count for that thread. The
Release does the opposite. The other approach is to require that all
ivar accesses (even within methods of the class) go through Set/Get
methods that have the critical sections in them and they return all
native data types by reference. (no float *GetPosition() code unless
the float * returned is a copy of the instance's ivar that was
allocated on the heap and will be freed by the caller of
GetPosition())

If you decide to go with a full multithread safe toolkit you will have
to make it a compile time option because the performance penalty will
be enormous. Even multithreaded languages like Java require special
keywords for methods that need to be multithread safe.

The key is to come up with a formal statement on how ITK will support
multithreading. Once that has been decided the implementation details
will easily follow.

Thanks
Ken

> -----Original Message-----
> From: insight-developers-admin@public.kitware.com
> [mailto:insight-developers-admin@public.kitware.com]On
> Behalf Of Luis
> Ibanez
> Sent: Thursday, April 04, 2002 8:28 PM
> To: Miller, James V (CRD); Insight Developers
> Subject: [Insight-developers] itk::LightObject non-threaded
>
>
>
> Jim,
>
> Following up on your clarification of the itkLightObject
> SmartPointer and their relation with the Mutex....
>
> How about adding a class "itkLightObjectNonThreaded" ?
>
> It will basically be the itkLightObject without the Mutex
> in the Register/Unregister methods (and without the
> mutex lock ivar itself), only the reference counter.
>
> Objects deriving from this new class will still be able to
> use smart pointers and enjoy of the convenience of
> automatic memory collection while still being fast in
> pointer copying.
>
> BTW it is worth to clarify that the access itself is *not*
> penalized by the mutexlock, only the copy of the SmartPointer
> into another SmartPointer is.  Dereferencing the object doesn't
> require to modify the reference counting and it is done by an
> inlined method in the SmartPointer.  Any access of type:
>
>             smartPointer->      or       *smartPointer
>
> should be as direct as using the normal pointer.
> (except when compiling with debugging, which will prevent
> this functions from being inlined...)
>
>
> In order to avoid code duplication with this new
> itk::LightObjectNonThreaded  class we can make:
>
>      itk::LightObject
>
>           derive from
>
>    itk::LightObjectNonThreaded
>
> that will leave itk::LightObject with only the additional
> code required for the Mutexlock: namely the mutexlock
> ivar and the overload for the virtual methods:
>
>         Register() / UnRegister() / SetReferenceCount()
>
>
> With this combination, classes that are expected to be shared
> from diferent threads should derive from itk::LightObject (or
> itk::Object)  while classes that are limited to be used from inside
> a single thread will derive from itk::LightObjectNonThreaded
> and still be able to use SmartPointers as strategy for memory
> collection.  Current SmartPointers could be used without change.
>
>
> How do you find this option ?
>
>
> Luis
>
>
>
>
>
>
> _______________________________________________
> Insight-developers mailing list
> Insight-developers@public.kitware.com
> http://public.kitware.com/mailman/listinfo/insight-developers