]> Creatis software - creaImageIO.git/blob - src/creaImageIOMultiThreadImageReader.cpp
#3218 creaImageIO Feature New Normal - vtk8itk4wx3-mingw64
[creaImageIO.git] / src / creaImageIOMultiThreadImageReader.cpp
1 /*
2 # ---------------------------------------------------------------------
3 #
4 # Copyright (c) CREATIS (Centre de Recherche en Acquisition et Traitement de l'Image 
5 #                        pour la Santé)
6 # Authors : Eduardo Davila, Frederic Cervenansky, Claire Mouton
7 # Previous Authors : Laurent Guigues, Jean-Pierre Roux
8 # CreaTools website : www.creatis.insa-lyon.fr/site/fr/creatools_accueil
9 #
10 #  This software is governed by the CeCILL-B license under French law and 
11 #  abiding by the rules of distribution of free software. You can  use, 
12 #  modify and/ or redistribute the software under the terms of the CeCILL-B 
13 #  license as circulated by CEA, CNRS and INRIA at the following URL 
14 #  http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html 
15 #  or in the file LICENSE.txt.
16 #
17 #  As a counterpart to the access to the source code and  rights to copy,
18 #  modify and redistribute granted by the license, users are provided only
19 #  with a limited warranty  and the software's author,  the holder of the
20 #  economic rights,  and the successive licensors  have only  limited
21 #  liability. 
22 #
23 #  The fact that you are presently reading this means that you have had
24 #  knowledge of the CeCILL-B license and that you accept its terms.
25 # ------------------------------------------------------------------------
26 */
27
28
29 #include <creaImageIOMultiThreadImageReader.h>
30 #include <creaImageIOImageReader.h>
31 #include <wx/utils.h>
32 #include <creaImageIOSystem.h>
33
34 #include <creaImageIOGimmick.h>
35 #ifdef _DEBUG
36 #define new DEBUG_NEW
37 #endif
38 namespace creaImageIO
39 {
40
41   //=====================================================================
42   void MultiThreadImageReaderUser::MultiThreadImageReaderSendEvent
43   ( const std::string& filename,
44     EventType type,
45     vtkImageData* image)
46   {
47     wxMutexLocker lock(mMultiThreadImageReaderUserMutex);
48     this->OnMultiThreadImageReaderEvent(filename,type,image);
49   }
50   //=====================================================================
51
52   //=====================================================================
53   class ThreadedImageReader: public wxThread
54   {
55   public:
56     ThreadedImageReader(MultiThreadImageReader* tir) :
57       mMultiThreadImageReader(tir)
58     {}
59     void* Entry();
60     void  OnExit();
61     vtkImageData* Read(const std::string& filename);
62         struct deleter
63         {
64                 void operator()(ThreadedImageReader* p)
65                 {
66                         p->Delete();
67                 }
68         };
69         friend struct deleter;
70
71   private:
72     ImageReader mReader;
73     MultiThreadImageReader* mMultiThreadImageReader;
74   };
75
76   //=====================================================================
77
78   
79   //=====================================================================
80   MultiThreadImageReader::MultiThreadImageReader(int number_of_threads)
81     : //mDoNotSignal(false),
82       mReader(0),
83       mTotalMem(0),
84       mTotalMemMax(1000000)
85   {
86     //    std::cout << "#### MultiThreadImageReader::MultiThreadImageReader("
87     //        << " #threads= " << number_of_threads <<" )"<<std::endl;
88
89           mDone = false;
90     // Create the threads
91         
92 printf("EED MultiThreadImageReader::MultiThreadImageReader %d \n", number_of_threads);  
93
94     for (int i=0; i<number_of_threads; i++) 
95       {
96                   //ThreadedImageReader* t = new ThreadedImageReader(this);
97                   boost::shared_ptr<ThreadedImageReader> t(new ThreadedImageReader(this), ThreadedImageReader::deleter());
98         mThreadedImageReaderList.push_back(t);
99          std::cout << "  ===> Thread "<<i
100                       <<" successfully added"<< std::endl;
101       }
102     mNumberOfThreadedReadersRunning = 0;
103     // Init the queue
104     mQueue.set(mComparator);
105     mQueue.set(mIndexer);
106     // 
107     // no thread : alloc self reader
108 //    if (number_of_threads==0)
109 //      {
110         mReader = new ImageReader();
111 //      }
112   }
113   //=====================================================================
114
115
116   //=====================================================================
117   bool MultiThreadImageReader::Start()
118   {
119 printf("EED MultiThreadImageReader::Start  Start\n");
120     //    std::cout << "#### MultiThreadImageReader::Start()"
121     //                <<std::endl;
122           if (mNumberOfThreadedReadersRunning > 0) return true;
123           
124     ThreadedImageReaderListType::iterator i;
125     for (i =mThreadedImageReaderList.begin();
126          i!=mThreadedImageReaderList.end();
127          i++)
128     {
129                 (*i)->Create();
130                 if ( (*i)->Run() != wxTHREAD_NO_ERROR )
131                   {
132                         std::cout << "ERROR starting a thread"<< std::endl;
133                         return false;
134                   }     else  {
135                                         std::cout << "  ===> Thread "<<(*i)->GetCurrentId()
136                                                   <<" successfully created"<< std::endl;
137                   } // if
138                 } // for
139                 wxMutexLocker locker(GetMultiThreadImageReaderUserMutex());
140                 //    std::cout << "EO Start : #Threads running = "
141                 //                    << mNumberOfThreadedReadersRunning<<std::endl;
142 printf("EED MultiThreadImageReader::Start  Start\n");
143     return true;
144   }
145   //=====================================================================
146
147   //=====================================================================
148   void MultiThreadImageReader::Stop()
149   { 
150 printf("EED MultiThreadImageReader::Stop  Start\n");
151 //                  std::cout << "#### MultiThreadImageReader::Stop()"
152 //            <<std::endl;
153   //  std::cout << "Sending stop order to the threads..."<<std::endl;
154   
155           if (mDone) return;
156
157     ThreadedImageReaderListType::iterator i;
158     for (i =mThreadedImageReaderList.begin();
159          i!=mThreadedImageReaderList.end();
160          i++)
161     { 
162                 std::cout << "  ===> Thread "<<(*i)->GetCurrentId()
163                               <<" successfully stopped"<< std::endl;
164                   if((*i)->IsAlive())
165                   {
166                           (*i)->Pause();
167                           (*i).reset();
168 //                        (*i)->Delete();
169                   } // if i
170     } // for
171    mThreadedImageReaderList.clear();
172     // Wait a little to be sure that all threads have stopped
173     // A better way to do this ?
174     //    wxMilliSleep(1000);
175     // New method : the threads generate a stop event when they have finished
176     // We wait until all threads have stopped
177 //        std::cout << "Waiting for stop signals..."<<std::endl;
178     do 
179       {
180         // Sleep a little
181                 wxMilliSleep(10);
182         // Lock
183         {
184           wxMutexLocker locker(GetMultiThreadImageReaderUserMutex());
185 //                std::cout << "#Threads running = "
186 //                          << mNumberOfThreadedReadersRunning<<std::endl;
187           // Break if all readers have stopped
188           if (mNumberOfThreadedReadersRunning <= 0) 
189             {
190               break;
191             }
192         }
193       } 
194     while (true);
195 //        std::cout << "All threads stopped : OK "<<std::endl;
196
197     ImageMapType::iterator j;
198     for (j =mImages.begin(); j!=mImages.end(); ++j)
199         {
200                 delete j->first;
201     } //for 
202     mImages.clear();
203         mDone = true;
204 printf("EED MultiThreadImageReader::Stop  End\n");
205   }
206   //=====================================================================
207
208   //=====================================================================
209   MultiThreadImageReader::~MultiThreadImageReader()
210   {
211 printf("EED MultiThreadImageReader::~MultiThreadImageReader  Start\n");
212     //    std::cout << "#### MultiThreadImageReader::~MultiThreadImageReader()"
213     //        <<std::endl;
214     Stop();
215     if (mReader) delete mReader;
216         mThreadedImageReaderList.clear();
217 printf("EED MultiThreadImageReader::~MultiThreadImageReader  End\n");
218   }
219   //=====================================================================
220
221   //=====================================================================
222   void MultiThreadImageReader::UpdateUnloadPriority(ImageToLoadPtr p, 
223                                                     int priority)
224   {
225     // not in unload queue : ciao
226     if (p->UnloadIndex()<0) return;
227     int old_prio = p->GetPriority();
228     if (priority > old_prio) 
229       {
230         p->SetPriority(priority);
231         mUnloadQueue.downsort(p->UnloadIndex());
232       }
233     else if ( old_prio > priority )
234       {
235         p->SetPriority(priority);
236         mUnloadQueue.upsort(p->UnloadIndex());
237      }
238   }
239   //=====================================================================
240   // function to read attributes for a file
241   void MultiThreadImageReader::getAttributes(const std::string filename, 
242           std::map <std::string , std::string> &infos,std::vector<std::string> i_attr)
243   {
244           mReader->getAttributes(filename, infos, i_attr);
245   }
246
247   //=====================================================================
248   void MultiThreadImageReader::Request( MultiThreadImageReaderUser* user,
249                                         const std::string& filename, 
250                                         int priority )
251   {
252         wxMutexLocker lock(GetMultiThreadImageReaderUserMutex()); //mMutex);
253
254           if (mNumberOfThreadedReadersRunning==0)
255 //    if (mThreadedImageReaderList.size()==0) 
256       {
257         // no detached reader : use self reader
258         ImageToLoad itl(user,filename);
259         ImageMapType::iterator i = mImages.find(&itl);
260         if (i!=mImages.end())
261           {
262             ImageToLoadPtr pitl = const_cast<ImageToLoadPtr>(i->first);
263             // Already inserted
264             if (pitl->GetImage() != 0)
265               {
266                 // Already read
267                 pitl->SetUser(user);
268                 UpdateUnloadPriority(pitl,priority);
269                 SignalImageRead(pitl,false);
270                 return; // pitl->GetImage();
271               }
272           }
273         ImageToLoadPtr pitl = new ImageToLoad(user,filename,0);
274         mImages[pitl] = 0;
275         pitl->SetImage(mReader->ReadImage(filename));
276         UpdateUnloadPriority(pitl,priority);
277         SignalImageRead(pitl,true);
278         //      return pitl->GetImage();
279         return;
280       }
281
282     ImageToLoad itl(user,filename);
283     ImageMapType::iterator i = mImages.find(&itl);
284     if (i!=mImages.end())
285       {
286         // Already inserted
287         if (i->first->GetImage() != 0)
288           {
289             // Already read : ok :signal the user
290             UpdateUnloadPriority(i->first,priority);
291             SignalImageRead(i->first,false);
292             return;
293           }
294         /// Already requested : change the priority
295         ImageToLoadPtr pitl = const_cast<ImageToLoadPtr>(i->first);
296         pitl->SetPriority(priority);
297         // Already in queue
298         if (pitl->Index()>=0) 
299           {
300             // Re-sort the queue
301             mQueue.upsort(pitl->Index());
302           }
303         // Not read but not in queue = being read = ok
304         else 
305           {
306             
307           }
308       }
309     else 
310       {
311         // Never requested before or unloaded 
312         ImageToLoadPtr pitl = new ImageToLoad(user,filename,priority);
313         mImages[pitl] = 0;
314         mQueue.insert(pitl);
315       }
316   }
317   //=====================================================================
318
319   //=====================================================================
320   void MultiThreadImageReader::OnMultiThreadImageReaderEvent
321   (const std::string& filename,
322    MultiThreadImageReaderUser::EventType e,
323    vtkImageData* image)
324   {
325     if ((e==MultiThreadImageReaderUser::ImageLoaded) &&
326         (filename == mRequestedFilename))
327       {
328         mRequestedImage = image;
329       }
330     else if (e==MultiThreadImageReaderUser::ThreadedReaderStarted)
331       {
332         mNumberOfThreadedReadersRunning++;
333         //      std::cout << "#TR=" << mNumberOfThreadedReadersRunning << std::endl;
334       }
335     else if (e==MultiThreadImageReaderUser::ThreadedReaderStopped)
336       {
337         
338                  mNumberOfThreadedReadersRunning--;
339         //      std::cout << "#TR=" << mNumberOfThreadedReadersRunning << std::endl;
340       }
341   }
342   //=====================================================================
343
344   //=====================================================================
345   vtkImageData* MultiThreadImageReader::GetImage(const std::string& filename)
346   {
347          // Start();
348     //       std::cout << "** MultiThreadImageReader::GetImage('"<<filename<<"')"
349     //           <<std::endl;
350     
351     do 
352       {
353         //      wxMutexLocker lock(GetMultiThreadImageReaderUserMutex()); //mMutex);
354                 
355         //     std::cout << "** MultiThreadImageReader::GetImage('"<<filename
356         //             <<"') lock ok"
357         //               <<std::endl;
358     
359         //                if (mNumberOfThreadedReadersRunning==0)
360         //      if (mThreadedImageReaderList.size()==0)
361         if (true)
362           {
363             ImageToLoad itl(this,filename);
364             ImageMapType::iterator i = mImages.find(&itl);
365             if (i!=mImages.end())
366               {
367                 ImageToLoadPtr pitl = const_cast<ImageToLoadPtr>(i->first);
368                 // Already inserted
369                 if (pitl->GetImage() != 0)
370                   {
371                     // Already read
372                     UpdateUnloadPriority(pitl,
373                                          GetMaximalPriorityWithoutLocking()+1);
374                     return pitl->GetImage();
375                   }
376               }
377             ImageToLoadPtr pitl = new ImageToLoad(this,filename,0);
378             mImages[pitl] = 0;
379             pitl->SetImage(mReader->ReadImage(filename));
380             UpdateUnloadPriority(pitl,
381                                  GetMaximalPriorityWithoutLocking()+1);
382             return pitl->GetImage();
383           }
384
385         /*      
386         mRequestedFilename = filename;
387         mRequestedImage = 0;
388         ImageToLoad itl(this,filename);
389         ImageMapType::iterator i = mImages.find(&itl);
390         if (i!=mImages.end())
391           {
392             // Already inserted in queue
393             if (i->first->GetImage() != 0)
394               {
395                 // Already read : ok : return it 
396                 return i->first->GetImage();
397               }
398             /// Already requested : change the priority
399               ImageToLoadPtr pitl = const_cast<ImageToLoadPtr>(i->first);
400               pitl->SetPriority( GetMaximalPriorityWithoutLocking() + 1 );
401               pitl->SetUser( this );
402               // Already in queue
403               if (pitl->Index()>=0) 
404                 {
405                   // Re-sort the queue
406                   mQueue.upsort(pitl->Index());
407                 }
408               // Not read but not in queue = being read = ok
409               else 
410                 {
411                   pitl->SetUser( this );
412                 }
413           }
414         else 
415           {
416             
417             // Never requested before or unloaded 
418             ImageToLoadPtr pitl = 
419               new ImageToLoad(this,filename,
420                               GetMaximalPriorityWithoutLocking() + 1);
421             mImages[pitl] = 0;
422             mQueue.insert(pitl);
423           }
424         */
425       }
426     while (0);
427
428     //    std::cout << "Waiting..."<<std::endl;
429
430     /*
431     // Waiting that it is read
432     int n = 0;
433     do 
434       {
435         //      std::cout << n++ << std::endl;
436         wxMilliSleep(10);
437         do 
438           {
439             //      wxMutexLocker lock(mMutex);
440             wxMutexLocker lock(GetMultiThreadImageReaderUserMutex());
441             if (mRequestedImage!=0) 
442               {
443                 return mRequestedImage;
444               } 
445           }
446         while (0);
447       }
448     while (true);
449     // 
450     */
451   }
452   //=====================================================================
453   
454   //=====================================================================
455   void MultiThreadImageReader::SignalImageRead(ImageToLoadPtr p, 
456                                                bool purge)
457   {
458     
459 //    std::cout << "MultiThreadImageReader::SignalImageRead" <<std::endl;
460     //    std::cout << "this="<<this <<std::endl;
461     //    std::cout << "user="<<p->GetUser() <<std::endl;
462
463     if ( p->GetUser() == this ) 
464         {
465       GetMultiThreadImageReaderUserMutex().Unlock();
466         }
467
468     p->GetUser()->MultiThreadImageReaderSendEvent
469       (p->GetFilename(),
470        MultiThreadImageReaderUser::ImageLoaded,
471        p->GetImage());
472
473     /*
474       AN ATTEMPT TO UNLOAD OLDEST IMAGE IF EXCEEDED A CERTAIN MEMORY QUOTA
475       BUGGY : TO FIX 
476     */
477     if (!purge)  return;
478     GimmickMessage(5,"Image '"<<p->GetFilename()<<"' read"<<std::endl);
479
480     //    wxMutexLocker lock(GetMultiThreadImageReaderUserMutex());
481            
482     mUnloadQueue.insert(p);
483
484
485 //EED 2017-01-01 Migration VTK7
486 #if VTK_MAJOR_VERSION <= 5
487     p->GetImage()->UpdateInformation();
488     p->GetImage()->PropagateUpdateExtent();
489     long ImMem = p->GetImage()->GetEstimatedMemorySize();
490 #else
491         int ext[6];
492         int dim[3];
493         p->GetImage()->GetExtent(ext);
494         dim[0]          = ext[1]-ext[0]+1;
495         dim[1]          = ext[3]-ext[2]+1;
496         dim[2]          = ext[5]-ext[4]+1;
497     long ImMem  = dim[0]*dim[1]*dim[2]*p->GetImage()->GetScalarSize();;
498 #endif
499     mTotalMem += ImMem;
500
501     GimmickMessage(5,"==> Image in memory = "<<mUnloadQueue.size()<<std::endl);
502     GimmickMessage(5,"==> Total mem       = "<<mTotalMem<<" Ko"<<std::endl);
503
504     //  return;
505
506     while (mTotalMem > mTotalMemMax)
507       {
508         GimmickMessage(5,
509                        "   ! Exceeded max of "
510                        << mTotalMemMax << " Ko : unloading oldest image ... "
511                        << std::endl);
512         if ( mUnloadQueue.size() <= 1 ) 
513           {
514              GimmickMessage(5,
515                             "   Only one image : cannot load AND unload it !!"
516                             <<std::endl);
517             break; 
518             
519           }
520         ImageToLoadPtr unload = mUnloadQueue.remove_top();
521         MultiThreadImageReaderUser* user = unload->GetUser();
522
523         /*
524         if ((user!=0)&&(user!=this)) 
525           {
526             user->GetMultiThreadImageReaderUserMutex().Lock();
527           }
528         */
529
530         std::string filename = unload->GetFilename();
531
532         GimmickMessage(5,"'" << filename << "'" << std::endl);
533
534 //EED 2017-01-01 Migration VTK7
535 #if VTK_MAJOR_VERSION <= 5
536         mTotalMem -= unload->GetImage()->GetEstimatedMemorySize();
537 #else
538         int ext[6];
539         int dim[3];
540         unload->GetImage()->GetExtent(ext);
541         dim[0]          = ext[1]-ext[0]+1;
542         dim[1]          = ext[3]-ext[2]+1;
543         dim[2]          = ext[5]-ext[4]+1;
544         mTotalMem -= dim[0]*dim[1]*dim[2]*unload->GetImage()->GetScalarSize();
545 #endif
546
547         GimmickMessage(5," ==> Total mem = "<<mTotalMem<<" Ko "<<std::endl);
548
549         if (user!=0) 
550           {
551             //      std::cout << "unlock..."<<std::endl;
552             //   user->GetMultiThreadImageReaderUserMutex().Unlock();
553             //      std::cout << "event"<<std::endl;
554             user->MultiThreadImageReaderSendEvent
555               (filename,
556                MultiThreadImageReaderUser::ImageUnloaded,
557                0);
558             //      std::cout << "event ok"<<std::endl;
559           }     
560
561         if (unload->Index()>=0)
562           {
563             // GimmickMessage(5,"still in queue"<<std::endl);
564           }
565         unload->Index() = -1;
566
567
568         ImageMapType::iterator it = mImages.find(unload);
569         if (it!=mImages.end())
570           {
571             mImages.erase(it);
572           }
573         //          std::cout << "delete..."<<std::endl;
574         delete unload;
575         //          std::cout << "delete ok."<<std::endl;
576
577       }
578   }
579   //=====================================================================
580
581   //=====================================================================
582   int MultiThreadImageReader::GetMaximalPriority()
583   { 
584     wxMutexLocker lock(GetMultiThreadImageReaderUserMutex()); //mMutex);
585     return GetMaximalPriorityWithoutLocking();
586   }
587   //=====================================================================
588
589
590   //=====================================================================
591   int MultiThreadImageReader::GetMaximalPriorityWithoutLocking()
592   { 
593     long max = 0;
594     if (mQueue.size()>0) 
595       {
596         max = mQueue.top()->GetPriority();
597       }
598     if (mUnloadQueue.size()>0)
599       {
600         int max2 = mUnloadQueue.top()->GetPriority();
601         if (max2>max) max=max2;
602       }
603     return max;
604   }
605   //=====================================================================
606
607
608   //=====================================================================
609   //=====================================================================
610   //=====================================================================
611   //=====================================================================
612
613   //=====================================================================
614   void*  ThreadedImageReader::Entry()
615   {
616     //    std::cout << "### Thread "<<GetCurrentId()<<"::Entry()"
617     //                << std::endl;
618
619     mMultiThreadImageReader->MultiThreadImageReaderSendEvent
620       ("",
621        MultiThreadImageReaderUser::ThreadedReaderStarted,
622        0);
623
624     // While was not deleted 
625     while (!TestDestroy())
626       {
627                 //std::cout << "### Thread "<<GetCurrentId()<<" still alive"  << std::endl;
628           
629         // Lock the mutex
630         mMultiThreadImageReader->MultiThreadImageReaderEventLock();
631         //mMutex.Lock();
632         // If image in queue
633         if (mMultiThreadImageReader->mQueue.size()>0)
634           {
635             MultiThreadImageReader::ImageToLoadPtr i = 
636               mMultiThreadImageReader->mQueue.remove_top();
637
638             mMultiThreadImageReader->MultiThreadImageReaderEventUnlock();
639             //mMutex.Unlock();
640
641             
642             //      std::cout << "### Thread "<<GetCurrentId()<<" : reading '"
643             //                << i->GetFilename() << "'" << std::endl;
644             
645             // Do the job
646             vtkImageData* im = Read(i->GetFilename());
647
648             // Store it in the map
649             mMultiThreadImageReader->MultiThreadImageReaderEventLock();
650             //mMutex.Lock();
651             MultiThreadImageReader::ImageToLoad itl(0,i->GetFilename());
652             MultiThreadImageReader::ImageMapType::iterator it = 
653               mMultiThreadImageReader->mImages.find(&itl);
654             MultiThreadImageReader::ImageToLoadPtr 
655               pitl = const_cast<MultiThreadImageReader::ImageToLoadPtr>
656               (it->first);
657             pitl->SetImage(im);
658             mMultiThreadImageReader->SignalImageRead(pitl,true);//i->GetFilename());
659             mMultiThreadImageReader->MultiThreadImageReaderEventUnlock();           //mMutex.Unlock();
660             
661             //      std::cout << "### Thread "<<GetCurrentId()<<" : reading '"
662             //                << i->GetFilename() << "' : DONE" << std::endl;
663             
664           }     else  {
665             mMultiThreadImageReader->MultiThreadImageReaderEventUnlock();
666             //mMutex.Unlock();
667             // Wait a little to avoid blocking 
668             Sleep(10);
669           }
670       };
671     //    std::cout << "### Thread "<<GetCurrentId()<<" stopping"
672     //                << std::endl;
673        
674     return 0;
675   }
676   //=====================================================================
677
678   //=====================================================================
679   void ThreadedImageReader::OnExit()
680   {
681     mMultiThreadImageReader->MultiThreadImageReaderSendEvent
682       ("",
683        MultiThreadImageReaderUser::ThreadedReaderStopped,
684        0);
685   }
686   //=====================================================================
687
688   //=====================================================================
689   vtkImageData* ThreadedImageReader::Read(const std::string& filename)
690   {
691     return mReader.ReadImage(filename);
692   }
693   //=====================================================================
694
695 } // namespace creaImageIO