Re: Multithreading with GM

Chris Healer <[email protected]> Fri, 14 Oct 2011 10:38:17 -0400 (EDT)
Newsgroups gmane.comp.video.graphicsmagick.bugs
Message-ID <695636407.59.1318603097114.JavaMail.root@krypton>
Thanks so much for your response...

I agree, multithreaded programming is formidably different than single threaded!

I've tried compiling my application on Ubuntu with mp turned on, as well as recompiled gm on osx with lots of different options... and compiling my app with lots of different options, namely -pthread , which `GraphicsMagic++-config --cxxflags` seems to imply.

I also updated to the most recent gm source to see if the blob reference counting you mentioned was fixed.... No dice.

I found another post you made somewhere which mentions writing the image out to an unsigned char array...

Trying this worked!

I am able to save out the uncompressed 8-bit rgb data in the main thread and then change colorspaces and do compression in the scanline threads, essentially bypass multithreaded access to a loaded Image.

This allows the image to fall out of scope after being loaded and its cache be deallocated safely, at the expense of memory (which is an issue for later, but not a huge one because I actually want the data to be resident in memory most of the time... ie until a sequence expires or memory is exhausted.)


It seems that there may be some lurking reference counting bug for Blobs, or perhaps I was missing something that I was doing wrong.  Not sure.

If it's any help for the eventual debugging of this (if anyone is interested):

My setup:
-There is a frame cache (QHash container) in the main thread that owns the Images (well, now, just their uncompressed data), so that any thread can access any loaded Image's data, and I have a central place to decide when things can be deallocated or expire.  Threading seems to work well, and the Mutex locking seems very stable.
-The server spawns connection threads
-The connection threads spawn scanline threads (ie, lots of threads, and lots of work queues)

The thing:
-I'm having multiple users access the server (multiple connection threads)
-When images are loaded, it's lots of them.  I'm loading HD or 4K framesequences from DPX or Tiff, each 100-200 or more frames.
-Each frame also has a dynamically created proxy version at half-res.
-The machine that I'm running on has tons of RAM (256GB), 64-bit, and I've been watching to make sure that simple inability to allocate more memory is the problem... it doesn't seem to be.
-I was assigning a non-class member to the Image (or its proxy version) before using get() or getConst() in the scanline thread.  That Image falls out of scope after the scanline is created and cached.  I think that when that Image falls out of scope the blob reference count is decremented improperly, eventaully making the reference count zero, and the blob a candidate for removal.
-The sheer quantity of images seems strain the cache, causing the cache to search for de-allocatable things...


I would post code, but it's kind of a lot... I could post something if anyone's interested... but meanwhile it is working, so thank you very much...!

You've done an excellent job with GM and provided and amazing tool to the community!

Thanks again!

Chris





The Molecule
594 Broadway Suite 906
New York, NY 10012
917.977.1122

On Oct 12, 2011, at 12:53 AM, Bob Friesenhahn <[email protected]> wrote:

> On Tue, 11 Oct 2011, Chris Healer wrote:
>> 
>> The problem I'm having is that no combination of Mutexes seems to
>> protect the image cache from flushing unpredictably.  As an example, there
>> may be 8 or more threads trying to call something like:
>> 
>> Pixels view(img);
>> const PixelPacket *pixels = view.getConst(0,row,img.columns(),1);
> 
> The Pixels view is just a handle to a heap-allocated structure.  It does not maintain a reference count to ensure that the underlying image is not destroyed if there are still allocated views.  If the underlying pixel cache is destroyed, then it is important not to use any still-allocated pixel view handles since bad things would surely happen.  It is wise to scope/order things so that allocated views are released before the referenced image.  In other words, deallocation should be in reverse order of allocation.
> 
> Besides the Image reference counting that the Magick++ Image class does, the underlying Image structure uses reference counting to the pixel cache representation which actually stores the pixels.  Both of these forms of reference counting are designed to be thread safe.
> 
>> concurrently.  It works fine for a while but eventually it crashes.
>> Running with MAGIC_DEBUG=all reveals:
>> 
>> destroy cache [path]
> 
> This message comes right before:
> 
>  cache_info->signature=0;
>  MagickFreeMemory(cache_info);
> 
>> Destroy blob, image=0x10282b200, filename="[path]"
> 
> The blob is a structure referenced by the Image.  This one is also reference counted.  I believe that a bug was fixed which sometimes broke the blob reference counting since the last release.
> 
>> are reliably the culprits, and:
>> 
>> Assertion failed: (image->signature == MagickSignature), function
>> OpenCacheView, file magick/pixel_cache.c, line 3193.
> 
> This particular assertion would happen if the underlying C API Image structure was already deallocated (or corrupted somehow) when the cache view was opened.
> 
>> This makes sense... anyone know of a way to control the flushing, or create
>> a mutex of some kind so I can grant permission for the cache to do its thing?
>> I can't seem to find anything in the documentation like this...
> 
> The cache is not destroyed until all of the Image references are gone. There is nothing similar to garbage collection used.
> 
> Multithreaded programming takes more discipline than normal programming.  While you reported an error with OpenCacheView(), I do think that you need to check to make sure that all Pixel view references to an image are destroyed before the referenced Image.
> 
> It is certainly possible that Magick++ or GraphicsMagick has a bug. Make sure that your build only disabled OpenMP and not the thread locking support.
> 
> What GraphicsMagick version are you using?
> 
> If your system is able to run valgrind (does support AMD64/Darwin) then its 'helgrind' tool (and 'drd' tool) can be very useful to discover any locking bugs.  As delivered, the development version of GraphicsMagick completely passes the valgrind 'memcheck' and 'helgrind' tests when driven by OpenMP.
> 
> See "http://www.valgrind.org/" for valgrind.
> 
> Bob
> -- 
> Bob Friesenhahn
> [email protected], http://www.simplesystems.org/users/bfriesen/
> GraphicsMagick Maintainer,    http://www.GraphicsMagick.org/

------------------------------------------------------------------------------
All the data continuously generated in your IT infrastructure contains a
definitive record of customers, application performance, security
threats, fraudulent activity and more. Splunk takes this data and makes
sense of it. Business sense. IT sense. Common sense.
http://p.sf.net/sfu/splunk-d2d-oct