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()
122 // std::cout << "#### MultiThreadImageReader::Start()"
124 if (mNumberOfThreadedReadersRunning > 0) return true;
126 ThreadedImageReaderListType::iterator i;
127 for (i =mThreadedImageReaderList.begin();
128 i!=mThreadedImageReaderList.end();
132 if ( (*i)->Run() != wxTHREAD_NO_ERROR )
134 std::cout << "ERROR starting a thread"<< std::endl;
139 std::cout << " ===> Thread "<<(*i)->GetCurrentId()
140 <<" successfully created"<< std::endl;
144 wxMutexLocker locker(GetMultiThreadImageReaderUserMutex());
145 // std::cout << "EO Start : #Threads running = "
146 // << mNumberOfThreadedReadersRunning<<std::endl;
150 //=====================================================================
152 //=====================================================================
153 void MultiThreadImageReader::Stop()
155 // std::cout << "#### MultiThreadImageReader::Stop()"
157 // std::cout << "Sending stop order to the threads..."<<std::endl;
160 ThreadedImageReaderListType::iterator i;
161 for (i =mThreadedImageReaderList.begin();
162 i!=mThreadedImageReaderList.end();
164 { std::cout << " ===> Thread "<<(*i)->GetCurrentId()
165 <<" successfully stopped"<< std::endl;
172 mThreadedImageReaderList.clear();
173 // Wait a little to be sure that all threads have stopped
174 // A better way to do this ?
175 // wxMilliSleep(1000);
176 // New method : the threads generate a stop event when they have finished
177 // We wait until all threads have stopped
178 // std::cout << "Waiting for stop signals..."<<std::endl;
185 wxMutexLocker locker(GetMultiThreadImageReaderUserMutex());
186 // std::cout << "#Threads running = "
187 // << mNumberOfThreadedReadersRunning<<std::endl;
188 // Break if all readers have stopped
189 if (mNumberOfThreadedReadersRunning <= 0)
196 // std::cout << "All threads stopped : OK "<<std::endl;
198 ImageMapType::iterator j;
199 for (j =mImages.begin();
209 //=====================================================================
211 //=====================================================================
212 MultiThreadImageReader::~MultiThreadImageReader()
214 // std::cout << "#### MultiThreadImageReader::~MultiThreadImageReader()"
217 if (mReader) delete mReader;
218 mThreadedImageReaderList.clear();
220 //=====================================================================
222 //=====================================================================
223 void MultiThreadImageReader::UpdateUnloadPriority(ImageToLoadPtr p,
226 // not in unload queue : ciao
227 if (p->UnloadIndex()<0) return;
228 int old_prio = p->GetPriority();
229 if (priority > old_prio)
231 p->SetPriority(priority);
232 mUnloadQueue.downsort(p->UnloadIndex());
234 else if ( old_prio > priority )
236 p->SetPriority(priority);
237 mUnloadQueue.upsort(p->UnloadIndex());
240 //=====================================================================
241 // function to read attributes for a file
242 void MultiThreadImageReader::getAttributes(const std::string filename,
243 std::map <std::string , std::string> &infos,std::vector<std::string> i_attr)
245 mReader->getAttributes(filename, infos, i_attr);
248 //=====================================================================
249 void MultiThreadImageReader::Request( MultiThreadImageReaderUser* user,
250 const std::string& filename,
253 wxMutexLocker lock(GetMultiThreadImageReaderUserMutex()); //mMutex);
255 if (mNumberOfThreadedReadersRunning==0)
256 // if (mThreadedImageReaderList.size()==0)
258 // no detached reader : use self reader
259 ImageToLoad itl(user,filename);
260 ImageMapType::iterator i = mImages.find(&itl);
261 if (i!=mImages.end())
263 ImageToLoadPtr pitl = const_cast<ImageToLoadPtr>(i->first);
265 if (pitl->GetImage() != 0)
269 UpdateUnloadPriority(pitl,priority);
270 SignalImageRead(pitl,false);
271 return; // pitl->GetImage();
274 ImageToLoadPtr pitl = new ImageToLoad(user,filename,0);
276 pitl->SetImage(mReader->ReadImage(filename));
277 UpdateUnloadPriority(pitl,priority);
278 SignalImageRead(pitl,true);
279 // return pitl->GetImage();
283 ImageToLoad itl(user,filename);
284 ImageMapType::iterator i = mImages.find(&itl);
285 if (i!=mImages.end())
288 if (i->first->GetImage() != 0)
290 // Already read : ok :signal the user
291 UpdateUnloadPriority(i->first,priority);
292 SignalImageRead(i->first,false);
295 /// Already requested : change the priority
296 ImageToLoadPtr pitl = const_cast<ImageToLoadPtr>(i->first);
297 pitl->SetPriority(priority);
299 if (pitl->Index()>=0)
302 mQueue.upsort(pitl->Index());
304 // Not read but not in queue = being read = ok
312 // Never requested before or unloaded
313 ImageToLoadPtr pitl = new ImageToLoad(user,filename,priority);
318 //=====================================================================
320 //=====================================================================
321 void MultiThreadImageReader::OnMultiThreadImageReaderEvent
322 (const std::string& filename,
323 MultiThreadImageReaderUser::EventType e,
326 if ((e==MultiThreadImageReaderUser::ImageLoaded) &&
327 (filename == mRequestedFilename))
329 mRequestedImage = image;
331 else if (e==MultiThreadImageReaderUser::ThreadedReaderStarted)
333 mNumberOfThreadedReadersRunning++;
334 // std::cout << "#TR=" << mNumberOfThreadedReadersRunning << std::endl;
336 else if (e==MultiThreadImageReaderUser::ThreadedReaderStopped)
339 mNumberOfThreadedReadersRunning--;
340 // std::cout << "#TR=" << mNumberOfThreadedReadersRunning << std::endl;
343 //=====================================================================
345 //=====================================================================
346 vtkImageData* MultiThreadImageReader::GetImage(const std::string& filename)
349 // std::cout << "** MultiThreadImageReader::GetImage('"<<filename<<"')"
354 // wxMutexLocker lock(GetMultiThreadImageReaderUserMutex()); //mMutex);
356 // std::cout << "** MultiThreadImageReader::GetImage('"<<filename
360 // if (mNumberOfThreadedReadersRunning==0)
361 // if (mThreadedImageReaderList.size()==0)
364 ImageToLoad itl(this,filename);
365 ImageMapType::iterator i = mImages.find(&itl);
366 if (i!=mImages.end())
368 ImageToLoadPtr pitl = const_cast<ImageToLoadPtr>(i->first);
370 if (pitl->GetImage() != 0)
373 UpdateUnloadPriority(pitl,
374 GetMaximalPriorityWithoutLocking()+1);
375 return pitl->GetImage();
378 ImageToLoadPtr pitl = new ImageToLoad(this,filename,0);
380 pitl->SetImage(mReader->ReadImage(filename));
381 UpdateUnloadPriority(pitl,
382 GetMaximalPriorityWithoutLocking()+1);
383 return pitl->GetImage();
387 mRequestedFilename = filename;
389 ImageToLoad itl(this,filename);
390 ImageMapType::iterator i = mImages.find(&itl);
391 if (i!=mImages.end())
393 // Already inserted in queue
394 if (i->first->GetImage() != 0)
396 // Already read : ok : return it
397 return i->first->GetImage();
399 /// Already requested : change the priority
400 ImageToLoadPtr pitl = const_cast<ImageToLoadPtr>(i->first);
401 pitl->SetPriority( GetMaximalPriorityWithoutLocking() + 1 );
402 pitl->SetUser( this );
404 if (pitl->Index()>=0)
407 mQueue.upsort(pitl->Index());
409 // Not read but not in queue = being read = ok
412 pitl->SetUser( this );
418 // Never requested before or unloaded
419 ImageToLoadPtr pitl =
420 new ImageToLoad(this,filename,
421 GetMaximalPriorityWithoutLocking() + 1);
429 // std::cout << "Waiting..."<<std::endl;
432 // Waiting that it is read
436 // std::cout << n++ << std::endl;
440 // wxMutexLocker lock(mMutex);
441 wxMutexLocker lock(GetMultiThreadImageReaderUserMutex());
442 if (mRequestedImage!=0)
444 return mRequestedImage;
453 //=====================================================================
455 //=====================================================================
456 void MultiThreadImageReader::SignalImageRead(ImageToLoadPtr p,
460 // std::cout << "MultiThreadImageReader::SignalImageRead" <<std::endl;
461 // std::cout << "this="<<this <<std::endl;
462 // std::cout << "user="<<p->GetUser() <<std::endl;
464 if ( p->GetUser() == this )
466 GetMultiThreadImageReaderUserMutex().Unlock();
469 p->GetUser()->MultiThreadImageReaderSendEvent
471 MultiThreadImageReaderUser::ImageLoaded,
475 AN ATTEMPT TO UNLOAD OLDEST IMAGE IF EXCEEDED A CERTAIN MEMORY QUOTA
479 GimmickMessage(5,"Image '"<<p->GetFilename()<<"' read"<<std::endl);
481 // wxMutexLocker lock(GetMultiThreadImageReaderUserMutex());
483 mUnloadQueue.insert(p);
486 //EED 2017-01-01 Migration VTK7
487 #if VTK_MAJOR_VERSION <= 5
488 p->GetImage()->UpdateInformation();
489 p->GetImage()->PropagateUpdateExtent();
490 long ImMem = p->GetImage()->GetEstimatedMemorySize();
494 p->GetImage()->GetExtent(ext);
495 dim[0] = ext[1]-ext[0]+1;
496 dim[1] = ext[3]-ext[2]+1;
497 dim[2] = ext[5]-ext[4]+1;
498 long ImMem = dim[0]*dim[1]*dim[2]*p->GetImage()->GetScalarSize();;
502 GimmickMessage(5,"==> Image in memory = "<<mUnloadQueue.size()<<std::endl);
503 GimmickMessage(5,"==> Total mem = "<<mTotalMem<<" Ko"<<std::endl);
507 while (mTotalMem > mTotalMemMax)
510 " ! Exceeded max of "
511 << mTotalMemMax << " Ko : unloading oldest image ... "
513 if ( mUnloadQueue.size() <= 1 )
516 " Only one image : cannot load AND unload it !!"
521 ImageToLoadPtr unload = mUnloadQueue.remove_top();
522 MultiThreadImageReaderUser* user = unload->GetUser();
525 if ((user!=0)&&(user!=this))
527 user->GetMultiThreadImageReaderUserMutex().Lock();
531 std::string filename = unload->GetFilename();
533 GimmickMessage(5,"'" << filename << "'" << std::endl);
535 //EED 2017-01-01 Migration VTK7
536 #if VTK_MAJOR_VERSION <= 5
537 mTotalMem -= unload->GetImage()->GetEstimatedMemorySize();
541 unload->GetImage()->GetExtent(ext);
542 dim[0] = ext[1]-ext[0]+1;
543 dim[1] = ext[3]-ext[2]+1;
544 dim[2] = ext[5]-ext[4]+1;
545 mTotalMem -= dim[0]*dim[1]*dim[2]*unload->GetImage()->GetScalarSize();
548 GimmickMessage(5," ==> Total mem = "<<mTotalMem<<" Ko "<<std::endl);
552 // std::cout << "unlock..."<<std::endl;
553 // user->GetMultiThreadImageReaderUserMutex().Unlock();
554 // std::cout << "event"<<std::endl;
555 user->MultiThreadImageReaderSendEvent
557 MultiThreadImageReaderUser::ImageUnloaded,
559 // std::cout << "event ok"<<std::endl;
562 if (unload->Index()>=0)
564 // GimmickMessage(5,"still in queue"<<std::endl);
566 unload->Index() = -1;
569 ImageMapType::iterator it = mImages.find(unload);
570 if (it!=mImages.end())
574 // std::cout << "delete..."<<std::endl;
576 // std::cout << "delete ok."<<std::endl;
580 //=====================================================================
582 //=====================================================================
583 int MultiThreadImageReader::GetMaximalPriority()
585 wxMutexLocker lock(GetMultiThreadImageReaderUserMutex()); //mMutex);
586 return GetMaximalPriorityWithoutLocking();
588 //=====================================================================
591 //=====================================================================
592 int MultiThreadImageReader::GetMaximalPriorityWithoutLocking()
597 max = mQueue.top()->GetPriority();
599 if (mUnloadQueue.size()>0)
601 int max2 = mUnloadQueue.top()->GetPriority();
602 if (max2>max) max=max2;
606 //=====================================================================
609 //=====================================================================
610 //=====================================================================
611 //=====================================================================
612 //=====================================================================
614 //=====================================================================
615 void* ThreadedImageReader::Entry()
617 // std::cout << "### Thread "<<GetCurrentId()<<"::Entry()"
620 mMultiThreadImageReader->MultiThreadImageReaderSendEvent
622 MultiThreadImageReaderUser::ThreadedReaderStarted,
625 // While was not deleted
626 while (!TestDestroy())
628 //std::cout << "### Thread "<<GetCurrentId()<<" still alive" << std::endl;
631 mMultiThreadImageReader->MultiThreadImageReaderEventLock();
634 if (mMultiThreadImageReader->mQueue.size()>0)
636 MultiThreadImageReader::ImageToLoadPtr i =
637 mMultiThreadImageReader->mQueue.remove_top();
639 mMultiThreadImageReader->MultiThreadImageReaderEventUnlock();
643 // std::cout << "### Thread "<<GetCurrentId()<<" : reading '"
644 // << i->GetFilename() << "'" << std::endl;
647 vtkImageData* im = Read(i->GetFilename());
649 // Store it in the map
650 mMultiThreadImageReader->MultiThreadImageReaderEventLock();
652 MultiThreadImageReader::ImageToLoad itl(0,i->GetFilename());
653 MultiThreadImageReader::ImageMapType::iterator it =
654 mMultiThreadImageReader->mImages.find(&itl);
655 MultiThreadImageReader::ImageToLoadPtr
656 pitl = const_cast<MultiThreadImageReader::ImageToLoadPtr>
659 mMultiThreadImageReader->SignalImageRead(pitl,true);//i->GetFilename());
660 mMultiThreadImageReader->MultiThreadImageReaderEventUnlock(); //mMutex.Unlock();
662 // std::cout << "### Thread "<<GetCurrentId()<<" : reading '"
663 // << i->GetFilename() << "' : DONE" << std::endl;
668 mMultiThreadImageReader->MultiThreadImageReaderEventUnlock();
670 // Wait a little to avoid blocking
674 // std::cout << "### Thread "<<GetCurrentId()<<" stopping"
679 //=====================================================================
681 //=====================================================================
682 void ThreadedImageReader::OnExit()
684 mMultiThreadImageReader->MultiThreadImageReaderSendEvent
686 MultiThreadImageReaderUser::ThreadedReaderStopped,
689 //=====================================================================
691 //=====================================================================
692 vtkImageData* ThreadedImageReader::Read(const std::string& filename)
694 return mReader.ReadImage(filename);
696 //=====================================================================
698 } // namespace creaImageIO