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 # ------------------------------------------------------------------------
28 #include <creaImageIOQMultiThreadImageReader.h>
29 #include <creaImageIOImageReader.h>
30 //#include <wx/utils.h>
32 #include <creaImageIOSystem.h>
34 #include <creaImageIOGimmick.h>
41 //=====================================================================
42 void MultiThreadImageReaderUser::MultiThreadImageReaderSendEvent
43 ( const std::string& filename,
47 QMutexLocker lock(&mMultiThreadImageReaderUserMutex);
49 this->OnMultiThreadImageReaderEvent(filename,type,image);
51 //=====================================================================
53 //=====================================================================
54 class ThreadedImageReader: public QThread
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)->isRunning() )
135 std::cout << "ERROR starting a thread"<< std::endl;
140 std::cout << " ===> Thread "<<(*i)->currentThreadId()
141 <<" successfully created"<< std::endl;
145 QMutexLocker 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)->currentThreadId()
166 <<" successfully stopped"<< std::endl;
167 if(!(*i)->isFinished())
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;
183 // QThread::msleep(10);
186 QMutexLocker 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 QMutexLocker 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 )
466 GetMultiThreadImageReaderUserMutex()->unlock();
468 p->GetUser()->MultiThreadImageReaderSendEvent
470 MultiThreadImageReaderUser::ImageLoaded,
474 AN ATTEMPT TO UNLOAD OLDEST IMAGE IF EXCEEDED A CERTAIN MEMORY QUOTA
478 GimmickMessage(5,"Image '"<<p->GetFilename()<<"' read"<<std::endl);
480 // wxMutexLocker lock(GetMultiThreadImageReaderUserMutex());
482 mUnloadQueue.insert(p);
483 p->GetImage()->UpdateInformation();
484 p->GetImage()->PropagateUpdateExtent();
485 long ImMem = p->GetImage()->GetEstimatedMemorySize();
488 GimmickMessage(5,"==> Image in memory = "<<mUnloadQueue.size()<<std::endl);
489 GimmickMessage(5,"==> Total mem = "<<mTotalMem<<" Ko"<<std::endl);
493 while (mTotalMem > mTotalMemMax)
496 " ! Exceeded max of "
497 << mTotalMemMax << " Ko : unloading oldest image ... "
499 if ( mUnloadQueue.size() <= 1 )
502 " Only one image : cannot load AND unload it !!"
507 ImageToLoadPtr unload = mUnloadQueue.remove_top();
508 MultiThreadImageReaderUser* user = unload->GetUser();
511 if ((user!=0)&&(user!=this))
513 user->GetMultiThreadImageReaderUserMutex().Lock();
517 std::string filename = unload->GetFilename();
519 GimmickMessage(5,"'" << filename << "'" << std::endl);
520 mTotalMem -= unload->GetImage()->GetEstimatedMemorySize();
522 GimmickMessage(5," ==> Total mem = "<<mTotalMem<<" Ko "<<std::endl);
526 // std::cout << "unlock..."<<std::endl;
527 // user->GetMultiThreadImageReaderUserMutex().Unlock();
528 // std::cout << "event"<<std::endl;
529 user->MultiThreadImageReaderSendEvent
531 MultiThreadImageReaderUser::ImageUnloaded,
533 // std::cout << "event ok"<<std::endl;
536 if (unload->Index()>=0)
538 // GimmickMessage(5,"still in queue"<<std::endl);
540 unload->Index() = -1;
543 ImageMapType::iterator it = mImages.find(unload);
544 if (it!=mImages.end())
548 // std::cout << "delete..."<<std::endl;
550 // std::cout << "delete ok."<<std::endl;
554 //=====================================================================
556 //=====================================================================
557 int MultiThreadImageReader::GetMaximalPriority()
559 QMutexLocker lock(GetMultiThreadImageReaderUserMutex()); //mMutex);
560 return GetMaximalPriorityWithoutLocking();
562 //=====================================================================
565 //=====================================================================
566 int MultiThreadImageReader::GetMaximalPriorityWithoutLocking()
571 max = mQueue.top()->GetPriority();
573 if (mUnloadQueue.size()>0)
575 int max2 = mUnloadQueue.top()->GetPriority();
576 if (max2>max) max=max2;
580 //=====================================================================
583 //=====================================================================
584 //=====================================================================
585 //=====================================================================
586 //=====================================================================
588 //=====================================================================
589 void* ThreadedImageReader::Entry()
591 // std::cout << "### Thread "<<GetCurrentId()<<"::Entry()"
594 mMultiThreadImageReader->MultiThreadImageReaderSendEvent
596 MultiThreadImageReaderUser::ThreadedReaderStarted,
599 // While was not deleted
600 while (!isFinished())
602 //std::cout << "### Thread "<<GetCurrentId()<<" still alive" << std::endl;
605 mMultiThreadImageReader->MultiThreadImageReaderEventLock();
608 if (mMultiThreadImageReader->mQueue.size()>0)
610 MultiThreadImageReader::ImageToLoadPtr i =
611 mMultiThreadImageReader->mQueue.remove_top();
613 mMultiThreadImageReader->MultiThreadImageReaderEventUnlock();
617 // std::cout << "### Thread "<<GetCurrentId()<<" : reading '"
618 // << i->GetFilename() << "'" << std::endl;
621 vtkImageData* im = Read(i->GetFilename());
623 // Store it in the map
624 mMultiThreadImageReader->MultiThreadImageReaderEventLock();
626 MultiThreadImageReader::ImageToLoad itl(0,i->GetFilename());
627 MultiThreadImageReader::ImageMapType::iterator it =
628 mMultiThreadImageReader->mImages.find(&itl);
629 MultiThreadImageReader::ImageToLoadPtr
630 pitl = const_cast<MultiThreadImageReader::ImageToLoadPtr>
633 mMultiThreadImageReader->SignalImageRead(pitl,true);//i->GetFilename());
634 mMultiThreadImageReader->MultiThreadImageReaderEventUnlock(); //mMutex.Unlock();
636 // std::cout << "### Thread "<<GetCurrentId()<<" : reading '"
637 // << i->GetFilename() << "' : DONE" << std::endl;
642 mMultiThreadImageReader->MultiThreadImageReaderEventUnlock();
644 // Wait a little to avoid blocking
648 // std::cout << "### Thread "<<GetCurrentId()<<" stopping"
653 //=====================================================================
655 //=====================================================================
656 void ThreadedImageReader::OnExit()
658 mMultiThreadImageReader->MultiThreadImageReaderSendEvent
660 MultiThreadImageReaderUser::ThreadedReaderStopped,
663 //=====================================================================
665 //=====================================================================
666 vtkImageData* ThreadedImageReader::Read(const std::string& filename)
668 return mReader.ReadImage(filename);
670 //=====================================================================
672 } // namespace creaImageIO