2 # ---------------------------------------------------------------------
4 # Copyright (c) CREATIS (Centre de Recherche en Acquisition et Traitement de l'Image
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
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.
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
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 # ------------------------------------------------------------------------
29 #include <creaImageIOMultiThreadImageReader.h>
30 #include <creaImageIOImageReader.h>
32 #include <creaImageIOSystem.h>
34 #include <creaImageIOGimmick.h>
41 //=====================================================================
42 void MultiThreadImageReaderUser::MultiThreadImageReaderSendEvent
43 ( const std::string& filename,
47 wxMutexLocker lock(mMultiThreadImageReaderUserMutex);
49 this->OnMultiThreadImageReaderEvent(filename,type,image);
51 //=====================================================================
53 //=====================================================================
54 class ThreadedImageReader: public wxThread
57 ThreadedImageReader(MultiThreadImageReader* tir) :
58 mMultiThreadImageReader(tir)
64 vtkImageData* Read(const std::string& filename);
68 void operator()(ThreadedImageReader* p)
73 friend struct deleter;
78 MultiThreadImageReader* mMultiThreadImageReader;
82 //=====================================================================
85 //=====================================================================
86 MultiThreadImageReader::MultiThreadImageReader(int number_of_threads)
87 : //mDoNotSignal(false),
92 // std::cout << "#### MultiThreadImageReader::MultiThreadImageReader("
93 // << " #threads= " << number_of_threads <<" )"<<std::endl;
97 for (int i=0; i<number_of_threads; i++)
99 //ThreadedImageReader* t = new ThreadedImageReader(this);
100 boost::shared_ptr<ThreadedImageReader> t(new ThreadedImageReader(this), ThreadedImageReader::deleter());
101 mThreadedImageReaderList.push_back(t);
102 std::cout << " ===> Thread "<<i
103 <<" successfully added"<< std::endl;
105 mNumberOfThreadedReadersRunning = 0;
107 mQueue.set(mComparator);
108 mQueue.set(mIndexer);
110 // no thread : alloc self reader
111 // if (number_of_threads==0)
113 mReader = new ImageReader();
116 //=====================================================================
119 //=====================================================================
120 bool MultiThreadImageReader::Start()
123 // std::cout << "#### MultiThreadImageReader::Start()"
125 if (mNumberOfThreadedReadersRunning > 0) return true;
127 ThreadedImageReaderListType::iterator i;
128 for (i =mThreadedImageReaderList.begin();
129 i!=mThreadedImageReaderList.end();
133 if ( (*i)->Run() != wxTHREAD_NO_ERROR )
135 std::cout << "ERROR starting a thread"<< std::endl;
140 std::cout << " ===> Thread "<<(*i)->GetCurrentId()
141 <<" successfully created"<< std::endl;
145 wxMutexLocker locker(GetMultiThreadImageReaderUserMutex());
146 // std::cout << "EO Start : #Threads running = "
147 // << mNumberOfThreadedReadersRunning<<std::endl;
151 //=====================================================================
153 //=====================================================================
154 void MultiThreadImageReader::Stop()
156 // std::cout << "#### MultiThreadImageReader::Stop()"
158 // std::cout << "Sending stop order to the threads..."<<std::endl;
161 ThreadedImageReaderListType::iterator i;
162 for (i =mThreadedImageReaderList.begin();
163 i!=mThreadedImageReaderList.end();
165 { std::cout << " ===> Thread "<<(*i)->GetCurrentId()
166 <<" successfully stopped"<< std::endl;
173 mThreadedImageReaderList.clear();
174 // Wait a little to be sure that all threads have stopped
175 // A better way to do this ?
176 // wxMilliSleep(1000);
177 // New method : the threads generate a stop event when they have finished
178 // We wait until all threads have stopped
179 // std::cout << "Waiting for stop signals..."<<std::endl;
186 wxMutexLocker locker(GetMultiThreadImageReaderUserMutex());
187 // std::cout << "#Threads running = "
188 // << mNumberOfThreadedReadersRunning<<std::endl;
189 // Break if all readers have stopped
190 if (mNumberOfThreadedReadersRunning <= 0)
197 // std::cout << "All threads stopped : OK "<<std::endl;
199 ImageMapType::iterator j;
200 for (j =mImages.begin();
210 //=====================================================================
212 //=====================================================================
213 MultiThreadImageReader::~MultiThreadImageReader()
215 // std::cout << "#### MultiThreadImageReader::~MultiThreadImageReader()"
218 if (mReader) delete mReader;
219 mThreadedImageReaderList.clear();
221 //=====================================================================
223 //=====================================================================
224 void MultiThreadImageReader::UpdateUnloadPriority(ImageToLoadPtr p,
227 // not in unload queue : ciao
228 if (p->UnloadIndex()<0) return;
229 int old_prio = p->GetPriority();
230 if (priority > old_prio)
232 p->SetPriority(priority);
233 mUnloadQueue.downsort(p->UnloadIndex());
235 else if ( old_prio > priority )
237 p->SetPriority(priority);
238 mUnloadQueue.upsort(p->UnloadIndex());
241 //=====================================================================
242 // function to read attributes for a file
243 void MultiThreadImageReader::getAttributes(const std::string filename,
244 std::map <std::string , std::string> &infos,std::vector<std::string> i_attr)
246 mReader->getAttributes(filename, infos, i_attr);
249 //=====================================================================
250 void MultiThreadImageReader::Request( MultiThreadImageReaderUser* user,
251 const std::string& filename,
254 wxMutexLocker lock(GetMultiThreadImageReaderUserMutex()); //mMutex);
256 if (mNumberOfThreadedReadersRunning==0)
257 // if (mThreadedImageReaderList.size()==0)
259 // no detached reader : use self reader
260 ImageToLoad itl(user,filename);
261 ImageMapType::iterator i = mImages.find(&itl);
262 if (i!=mImages.end())
264 ImageToLoadPtr pitl = const_cast<ImageToLoadPtr>(i->first);
266 if (pitl->GetImage() != 0)
270 UpdateUnloadPriority(pitl,priority);
271 SignalImageRead(pitl,false);
272 return; // pitl->GetImage();
275 ImageToLoadPtr pitl = new ImageToLoad(user,filename,0);
277 pitl->SetImage(mReader->ReadImage(filename));
278 UpdateUnloadPriority(pitl,priority);
279 SignalImageRead(pitl,true);
280 // return pitl->GetImage();
284 ImageToLoad itl(user,filename);
285 ImageMapType::iterator i = mImages.find(&itl);
286 if (i!=mImages.end())
289 if (i->first->GetImage() != 0)
291 // Already read : ok :signal the user
292 UpdateUnloadPriority(i->first,priority);
293 SignalImageRead(i->first,false);
296 /// Already requested : change the priority
297 ImageToLoadPtr pitl = const_cast<ImageToLoadPtr>(i->first);
298 pitl->SetPriority(priority);
300 if (pitl->Index()>=0)
303 mQueue.upsort(pitl->Index());
305 // Not read but not in queue = being read = ok
313 // Never requested before or unloaded
314 ImageToLoadPtr pitl = new ImageToLoad(user,filename,priority);
319 //=====================================================================
321 //=====================================================================
322 void MultiThreadImageReader::OnMultiThreadImageReaderEvent
323 (const std::string& filename,
324 MultiThreadImageReaderUser::EventType e,
327 if ((e==MultiThreadImageReaderUser::ImageLoaded) &&
328 (filename == mRequestedFilename))
330 mRequestedImage = image;
332 else if (e==MultiThreadImageReaderUser::ThreadedReaderStarted)
334 mNumberOfThreadedReadersRunning++;
335 // std::cout << "#TR=" << mNumberOfThreadedReadersRunning << std::endl;
337 else if (e==MultiThreadImageReaderUser::ThreadedReaderStopped)
340 mNumberOfThreadedReadersRunning--;
341 // std::cout << "#TR=" << mNumberOfThreadedReadersRunning << std::endl;
344 //=====================================================================
346 //=====================================================================
347 vtkImageData* MultiThreadImageReader::GetImage(const std::string& filename)
350 // std::cout << "** MultiThreadImageReader::GetImage('"<<filename<<"')"
355 // wxMutexLocker lock(GetMultiThreadImageReaderUserMutex()); //mMutex);
357 // std::cout << "** MultiThreadImageReader::GetImage('"<<filename
361 // if (mNumberOfThreadedReadersRunning==0)
362 // if (mThreadedImageReaderList.size()==0)
365 ImageToLoad itl(this,filename);
366 ImageMapType::iterator i = mImages.find(&itl);
367 if (i!=mImages.end())
369 ImageToLoadPtr pitl = const_cast<ImageToLoadPtr>(i->first);
371 if (pitl->GetImage() != 0)
374 UpdateUnloadPriority(pitl,
375 GetMaximalPriorityWithoutLocking()+1);
376 return pitl->GetImage();
379 ImageToLoadPtr pitl = new ImageToLoad(this,filename,0);
381 pitl->SetImage(mReader->ReadImage(filename));
382 UpdateUnloadPriority(pitl,
383 GetMaximalPriorityWithoutLocking()+1);
384 return pitl->GetImage();
388 mRequestedFilename = filename;
390 ImageToLoad itl(this,filename);
391 ImageMapType::iterator i = mImages.find(&itl);
392 if (i!=mImages.end())
394 // Already inserted in queue
395 if (i->first->GetImage() != 0)
397 // Already read : ok : return it
398 return i->first->GetImage();
400 /// Already requested : change the priority
401 ImageToLoadPtr pitl = const_cast<ImageToLoadPtr>(i->first);
402 pitl->SetPriority( GetMaximalPriorityWithoutLocking() + 1 );
403 pitl->SetUser( this );
405 if (pitl->Index()>=0)
408 mQueue.upsort(pitl->Index());
410 // Not read but not in queue = being read = ok
413 pitl->SetUser( this );
419 // Never requested before or unloaded
420 ImageToLoadPtr pitl =
421 new ImageToLoad(this,filename,
422 GetMaximalPriorityWithoutLocking() + 1);
430 // std::cout << "Waiting..."<<std::endl;
433 // Waiting that it is read
437 // std::cout << n++ << std::endl;
441 // wxMutexLocker lock(mMutex);
442 wxMutexLocker lock(GetMultiThreadImageReaderUserMutex());
443 if (mRequestedImage!=0)
445 return mRequestedImage;
454 //=====================================================================
456 //=====================================================================
457 void MultiThreadImageReader::SignalImageRead(ImageToLoadPtr p,
461 // std::cout << "MultiThreadImageReader::SignalImageRead" <<std::endl;
462 // std::cout << "this="<<this <<std::endl;
463 // std::cout << "user="<<p->GetUser() <<std::endl;
465 if ( p->GetUser() == this )
467 GetMultiThreadImageReaderUserMutex().Unlock();
470 p->GetUser()->MultiThreadImageReaderSendEvent
472 MultiThreadImageReaderUser::ImageLoaded,
476 AN ATTEMPT TO UNLOAD OLDEST IMAGE IF EXCEEDED A CERTAIN MEMORY QUOTA
480 GimmickMessage(5,"Image '"<<p->GetFilename()<<"' read"<<std::endl);
482 // wxMutexLocker lock(GetMultiThreadImageReaderUserMutex());
484 mUnloadQueue.insert(p);
487 //EED 2017-01-01 Migration VTK7
488 #if VTK_MAJOR_VERSION <= 5
489 p->GetImage()->UpdateInformation();
490 p->GetImage()->PropagateUpdateExtent();
491 long ImMem = p->GetImage()->GetEstimatedMemorySize();
495 p->GetImage()->GetExtent(ext);
496 dim[0] = ext[1]-ext[0]+1;
497 dim[1] = ext[3]-ext[2]+1;
498 dim[2] = ext[5]-ext[4]+1;
499 long ImMem = dim[0]*dim[1]*dim[2]*p->GetImage()->GetScalarSize();;
503 GimmickMessage(5,"==> Image in memory = "<<mUnloadQueue.size()<<std::endl);
504 GimmickMessage(5,"==> Total mem = "<<mTotalMem<<" Ko"<<std::endl);
508 while (mTotalMem > mTotalMemMax)
511 " ! Exceeded max of "
512 << mTotalMemMax << " Ko : unloading oldest image ... "
514 if ( mUnloadQueue.size() <= 1 )
517 " Only one image : cannot load AND unload it !!"
522 ImageToLoadPtr unload = mUnloadQueue.remove_top();
523 MultiThreadImageReaderUser* user = unload->GetUser();
526 if ((user!=0)&&(user!=this))
528 user->GetMultiThreadImageReaderUserMutex().Lock();
532 std::string filename = unload->GetFilename();
534 GimmickMessage(5,"'" << filename << "'" << std::endl);
536 //EED 2017-01-01 Migration VTK7
537 #if VTK_MAJOR_VERSION <= 5
538 mTotalMem -= unload->GetImage()->GetEstimatedMemorySize();
542 unload->GetImage()->GetExtent(ext);
543 dim[0] = ext[1]-ext[0]+1;
544 dim[1] = ext[3]-ext[2]+1;
545 dim[2] = ext[5]-ext[4]+1;
546 mTotalMem -= dim[0]*dim[1]*dim[2]*unload->GetImage()->GetScalarSize();
549 GimmickMessage(5," ==> Total mem = "<<mTotalMem<<" Ko "<<std::endl);
553 // std::cout << "unlock..."<<std::endl;
554 // user->GetMultiThreadImageReaderUserMutex().Unlock();
555 // std::cout << "event"<<std::endl;
556 user->MultiThreadImageReaderSendEvent
558 MultiThreadImageReaderUser::ImageUnloaded,
560 // std::cout << "event ok"<<std::endl;
563 if (unload->Index()>=0)
565 // GimmickMessage(5,"still in queue"<<std::endl);
567 unload->Index() = -1;
570 ImageMapType::iterator it = mImages.find(unload);
571 if (it!=mImages.end())
575 // std::cout << "delete..."<<std::endl;
577 // std::cout << "delete ok."<<std::endl;
581 //=====================================================================
583 //=====================================================================
584 int MultiThreadImageReader::GetMaximalPriority()
586 wxMutexLocker lock(GetMultiThreadImageReaderUserMutex()); //mMutex);
587 return GetMaximalPriorityWithoutLocking();
589 //=====================================================================
592 //=====================================================================
593 int MultiThreadImageReader::GetMaximalPriorityWithoutLocking()
598 max = mQueue.top()->GetPriority();
600 if (mUnloadQueue.size()>0)
602 int max2 = mUnloadQueue.top()->GetPriority();
603 if (max2>max) max=max2;
607 //=====================================================================
610 //=====================================================================
611 //=====================================================================
612 //=====================================================================
613 //=====================================================================
615 //=====================================================================
616 void* ThreadedImageReader::Entry()
618 // std::cout << "### Thread "<<GetCurrentId()<<"::Entry()"
621 mMultiThreadImageReader->MultiThreadImageReaderSendEvent
623 MultiThreadImageReaderUser::ThreadedReaderStarted,
626 // While was not deleted
627 while (!TestDestroy())
629 //std::cout << "### Thread "<<GetCurrentId()<<" still alive" << std::endl;
632 mMultiThreadImageReader->MultiThreadImageReaderEventLock();
635 if (mMultiThreadImageReader->mQueue.size()>0)
637 MultiThreadImageReader::ImageToLoadPtr i =
638 mMultiThreadImageReader->mQueue.remove_top();
640 mMultiThreadImageReader->MultiThreadImageReaderEventUnlock();
644 // std::cout << "### Thread "<<GetCurrentId()<<" : reading '"
645 // << i->GetFilename() << "'" << std::endl;
648 vtkImageData* im = Read(i->GetFilename());
650 // Store it in the map
651 mMultiThreadImageReader->MultiThreadImageReaderEventLock();
653 MultiThreadImageReader::ImageToLoad itl(0,i->GetFilename());
654 MultiThreadImageReader::ImageMapType::iterator it =
655 mMultiThreadImageReader->mImages.find(&itl);
656 MultiThreadImageReader::ImageToLoadPtr
657 pitl = const_cast<MultiThreadImageReader::ImageToLoadPtr>
660 mMultiThreadImageReader->SignalImageRead(pitl,true);//i->GetFilename());
661 mMultiThreadImageReader->MultiThreadImageReaderEventUnlock(); //mMutex.Unlock();
663 // std::cout << "### Thread "<<GetCurrentId()<<" : reading '"
664 // << i->GetFilename() << "' : DONE" << std::endl;
669 mMultiThreadImageReader->MultiThreadImageReaderEventUnlock();
671 // Wait a little to avoid blocking
675 // std::cout << "### Thread "<<GetCurrentId()<<" stopping"
680 //=====================================================================
682 //=====================================================================
683 void ThreadedImageReader::OnExit()
685 mMultiThreadImageReader->MultiThreadImageReaderSendEvent
687 MultiThreadImageReaderUser::ThreadedReaderStopped,
690 //=====================================================================
692 //=====================================================================
693 vtkImageData* ThreadedImageReader::Read(const std::string& filename)
695 return mReader.ReadImage(filename);
697 //=====================================================================
699 } // namespace creaImageIO