diff --git a/Core/Code/DataManagement/mitkImageAccessorBase.cpp b/Core/Code/DataManagement/mitkImageAccessorBase.cpp index c08a6d709f..2f7e93099a 100644 --- a/Core/Code/DataManagement/mitkImageAccessorBase.cpp +++ b/Core/Code/DataManagement/mitkImageAccessorBase.cpp @@ -1,177 +1,181 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkImageAccessorBase.h" #include "mitkImage.h" mitk::ImageAccessorBase::ThreadIDType mitk::ImageAccessorBase::CurrentThreadHandle() { #ifdef ITK_USE_SPROC return GetCurrentThreadId(); #endif #ifdef ITK_USE_PTHREADS return pthread_self(); #endif #ifdef ITK_USE_WIN32_THREADS return GetCurrentThreadId(); #endif } bool mitk::ImageAccessorBase::CompareThreadHandles(mitk::ImageAccessorBase::ThreadIDType handle1, mitk::ImageAccessorBase::ThreadIDType handle2) { return handle1 == handle2; } mitk::ImageAccessorBase::ImageAccessorBase( ImagePointer iP, ImageDataItem* imageDataItem, int OptionFlags ) : m_Image(iP), // imageDataItem(iDI), m_SubRegion(NULL), m_Options(OptionFlags), m_CoherentMemory(false) { m_Thread = CurrentThreadHandle(); // Initialize WaitLock m_WaitLock = new ImageAccessorWaitLock(); m_WaitLock->m_WaiterCount = 0; // Check validity of ImageAccessor // Is there an Image? /* if(!m_Image) { mitkThrow() << "Invalid ImageAccessor: No Image was specified in constructor of ImageAccessor"; } */ if(m_Image) { // Make sure, that the Image is initialized properly if(m_Image->m_Initialized==false) { if(m_Image->GetSource().IsNull()) mitkThrow() << "ImageAccessor: No image source is defined"; + m_Image->m_ReadWriteLock.Lock(); if(m_Image->GetSource()->Updating()==false) m_Image->GetSource()->UpdateOutputInformation(); + m_Image->m_ReadWriteLock.Unlock(); } } // Investigate 4 cases of possible image parts/regions // Case 1: No ImageDataItem and no Subregion => Whole Image is accessed if(imageDataItem == NULL && m_SubRegion == NULL) { m_CoherentMemory = true; // Organize first image channel + m_Image->m_ReadWriteLock.Lock(); imageDataItem = m_Image->GetChannelData(); + m_Image->m_ReadWriteLock.Unlock(); // Set memory area m_AddressBegin = imageDataItem->m_Data; m_AddressEnd = (unsigned char*) m_AddressBegin + imageDataItem->m_Size; } // Case 2: ImageDataItem but no Subregion if(imageDataItem && m_SubRegion == NULL) { m_CoherentMemory = true; // Set memory area m_AddressBegin = imageDataItem->m_Data; m_AddressEnd = (unsigned char*) m_AddressBegin + imageDataItem->m_Size; } // Case 3: No ImageDataItem but a SubRegion if(imageDataItem == NULL && m_SubRegion) { mitkThrow() << "Invalid ImageAccessor: The use of a SubRegion is not supported (yet)."; } // Case 4: ImageDataItem and SubRegion if(imageDataItem == NULL && m_SubRegion) { mitkThrow() << "Invalid ImageAccessor: The use of a SubRegion is not supported (yet)."; } } /** \brief Computes if there is an Overlap of the image part between this instantiation and another ImageAccessor object * \throws mitk::Exception if memory area is incoherent (not supported yet) */ bool mitk::ImageAccessorBase::Overlap(const ImageAccessorBase* iAB) { if(m_CoherentMemory) { if((iAB->m_AddressBegin >= m_AddressBegin && iAB->m_AddressBegin < m_AddressEnd) || (iAB->m_AddressEnd > m_AddressBegin && iAB->m_AddressEnd <= m_AddressEnd)) { return true; } if((m_AddressBegin >= iAB->m_AddressBegin && m_AddressBegin < iAB->m_AddressEnd) || (m_AddressEnd > iAB->m_AddressBegin && m_AddressEnd <= iAB->m_AddressEnd)) { return true; } } else { m_Image->m_ReadWriteLock.Unlock(); mitkThrow() << "ImageAccessor: incoherent memory area is not supported yet"; } return false; } /** \brief Uses the WaitLock to wait for another ImageAccessor*/ void mitk::ImageAccessorBase::WaitForReleaseOf(ImageAccessorWaitLock* wL) { wL->m_Mutex.Lock(); // Decrement wL->m_WaiterCount -= 1; // If there are no more waiting ImageAccessors, delete the Mutex // (Der Letzte macht das Licht aus!) if(wL->m_WaiterCount <= 0) { wL->m_Mutex.Unlock(); delete wL; } else { wL->m_Mutex.Unlock(); } } void mitk::ImageAccessorBase::PreventRecursiveMutexLock(mitk::ImageAccessorBase* iAB) { #ifdef MITK_USE_RECURSIVE_MUTEX_PREVENTION // Prevent deadlock ThreadIDType id = CurrentThreadHandle(); if(CompareThreadHandles(id,iAB->m_Thread)) { m_Image->m_ReadWriteLock.Unlock(); mitkThrow() << "Prohibited image access: the requested image part is already in use and cannot be requested recursively!"; } #endif } diff --git a/Core/Code/DataManagement/mitkImagePixelAccessor.h b/Core/Code/DataManagement/mitkImagePixelAccessor.h index c8cb0d2e22..47e3cefe0e 100644 --- a/Core/Code/DataManagement/mitkImagePixelAccessor.h +++ b/Core/Code/DataManagement/mitkImagePixelAccessor.h @@ -1,122 +1,115 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef MITKIMAGEPIXELACCESSOR_H #define MITKIMAGEPIXELACCESSOR_H #include #include #include #include #include #include "mitkImageAccessorBase.h" #include "mitkImageDataItem.h" #include "mitkPixelType.h" #include "mitkImage.h" namespace mitk { class Image; //##Documentation //## @brief Provides templated image access for all inheriting classes //## @tparam TPixel defines the PixelType //## @tparam VDimension defines the dimension for accessing data //## @ingroup Data template class ImagePixelAccessor { friend class Image; public: typedef itk::Index IndexType; typedef ImagePixelAccessor ImagePixelAccessorType; /** Get Dimensions from ImageDataItem */ int GetDimension (int i) const { return m_ImageDataItem->GetDimension(i); } protected: /** \param ImageDataItem* specifies the allocated image part */ ImagePixelAccessor(mitk::Image::Pointer iP, mitk::ImageDataItem* iDI) : m_ImageDataItem(iDI) { - - // let image organise its channel data - iP->GetData(); - - if(iDI == NULL) - m_ImageDataItem = iP->GetChannelData(); - } /** Destructor */ virtual ~ImagePixelAccessor() { } protected: // protected members /** Holds the specified ImageDataItem */ ImageDataItem* m_ImageDataItem; /** \brief Pointer to the used Geometry. * Since Geometry can be different to the Image (if memory was forced to be coherent) it is necessary to store Geometry separately. */ Geometry3D::Pointer m_Geometry; /** \brief A Subregion defines an arbitrary area within the image. * If no SubRegion is defined, the whole ImageDataItem or Image is regarded. * A subregion (e.g. subvolume) can lead to non-coherent memory access where every dimension has a start- and end-offset. */ itk::ImageRegion* m_SubRegion; /** \brief Stores all extended properties of an ImageAccessor. * The different flags in mitk::ImageAccessorBase::Options can be unified by bitwise operations. */ int m_Options; /** Get memory offset for a given image index */ unsigned int GetOffset(const IndexType & idx) const { const unsigned int * imageDims = m_ImageDataItem->m_Dimensions; unsigned int offset = 0; switch(VDimension) { case 4: offset += idx[3]*imageDims[0]*imageDims[1]*imageDims[2]; case 3: offset += idx[2]*imageDims[0]*imageDims[1]; case 2: offset += idx[0] + idx[1]*imageDims[0]; break; } return offset; } private: }; } #endif // MITKIMAGEACCESSOR_H diff --git a/Core/Code/Testing/mitkImageAccessorTest.cpp b/Core/Code/Testing/mitkImageAccessorTest.cpp index ca98be2dcd..d38476d840 100644 --- a/Core/Code/Testing/mitkImageAccessorTest.cpp +++ b/Core/Code/Testing/mitkImageAccessorTest.cpp @@ -1,254 +1,261 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkImage.h" #include "mitkImageReadAccessor.h" #include "mitkImagePixelReadAccessor.h" #include "mitkImagePixelWriteAccessor.h" #include "mitkImageWriteAccessor.h" #include "mitkDataNodeFactory.h" #include "mitkImageTimeSelector.h" #include #include "itkBarrier.h" #include #include #include #include #include struct ThreadData { itk::Barrier::Pointer m_Barrier; // holds a pointer to the used barrier mitk::Image::Pointer data; // some random data int m_NoOfThreads; // holds the number of generated threads bool m_Successful; // to check if everything worked }; +itk::SimpleFastMutexLock testMutex; + ITK_THREAD_RETURN_TYPE ThreadMethod(void* data) { /* extract data pointer from Thread Info structure */ struct itk::MultiThreader::ThreadInfoStruct * pInfo = (struct itk::MultiThreader::ThreadInfoStruct*)data; // some data validity checking if (pInfo == NULL) { return ITK_THREAD_RETURN_VALUE; } if (pInfo->UserData == NULL) { return ITK_THREAD_RETURN_VALUE; } // obtain user data for processing ThreadData* threadData = (ThreadData*) pInfo->UserData; srand( pInfo->ThreadID ); mitk::Image::Pointer im = threadData->data; int nrSlices = im->GetDimension(2); // Create randomly a PixelRead- or PixelWriteAccessor for a slice and access all pixels in it. try { if(rand() % 2) { + testMutex.Lock(); mitk::ImageDataItem* iDi = im->GetSliceData(rand() % nrSlices); + testMutex.Unlock(); while(!iDi->IsComplete()) {} //MITK_INFO << "pixeltype: " << im->GetPixelType().GetComponentTypeAsString(); if ((im->GetPixelType().GetComponentTypeAsString() == "short") && (im->GetDimension() == 3) ) { // Use pixeltype&dimension specific read accessor int xlength = im->GetDimension(0); int ylength = im->GetDimension(1); mitk::ImagePixelReadAccessor readAccessor(im, iDi); itk::Index<2> idx; for(int i=0; iGetSliceData(rand() % nrSlices); + testMutex.Unlock(); while(!iDi->IsComplete()) {} if ((im->GetPixelType().GetComponentTypeAsString() == "short") && (im->GetDimension() == 3) ) { // Use pixeltype&dimension specific read accessor int xlength = im->GetDimension(0); int ylength = im->GetDimension(1); mitk::ImagePixelWriteAccessor writeAccessor(im, iDi); itk::Index<2> idx; for(int i=0; im_Successful = false; } } } } else { // use general accessor mitk::ImageWriteAccessor iB(im,iDi); void* pointer = iB.GetData(); *((char*) pointer) = 0; } } } catch(mitk::MemoryIsLockedException e) { threadData->m_Successful = false; e.Print(std::cout); } catch(mitk::Exception e) { threadData->m_Successful = false; e.Print(std::cout); } // data processing end! threadData->m_Barrier->Wait(); return ITK_THREAD_RETURN_VALUE; } int mitkImageAccessorTest(int argc, char* argv[]) { MITK_TEST_BEGIN("mitkImageAccessorTest"); std::cout << "Loading file: "; if(argc==0) { std::cout<<"no file specified [FAILED]"<SetFileName( argv[1] ); factory->Update(); if(factory->GetNumberOfOutputs()<1) { std::cout<<"file could not be loaded [FAILED]"<GetOutput( 0 ); image = dynamic_cast(node->GetData()); if(image.IsNull()) { std::cout<<"file not an image - test will not be applied [PASSED]"<GetGeometry()->Initialize(); itk::MultiThreader::Pointer threader = itk::MultiThreader::New(); - unsigned int noOfThreads = 1; + unsigned int noOfThreads = 100; // initialize barrier itk::Barrier::Pointer barrier = itk::Barrier::New(); barrier->Initialize( noOfThreads + 1); // add one for we stop the base thread when the worker threads are processing ThreadData* threadData = new ThreadData; threadData->m_Barrier = barrier; threadData->m_NoOfThreads = noOfThreads; threadData->data = image; threadData->m_Successful = true; // spawn threads for(unsigned int i=0; i < noOfThreads; ++i) { threader->SpawnThread(ThreadMethod, threadData); } // stop the base thread during worker thread execution barrier->Wait(); // terminate threads for(unsigned int j=0; j < noOfThreads; ++j) { threader->TerminateThread(j); } bool TestSuccessful = threadData->m_Successful ; delete threadData; MITK_TEST_CONDITION_REQUIRED( TestSuccessful, "Testing image access from multiple threads"); MITK_TEST_END(); }