From: Andrew Cox Date: Tue, 24 Jun 2014 15:36:25 +0000 (+0100) Subject: Enable late cancelation of resource loads already in flight X-Git-Tag: dali_1.0.1~6 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=refs%2Fchanges%2F43%2F24443%2F10;p=platform%2Fcore%2Fuifw%2Fdali-adaptor.git Enable late cancelation of resource loads already in flight Change-Id: If08cbfdc9d7dde4204c3b27f0d50db91e926784d Signed-off-by: Andrew Cox --- diff --git a/automated-tests/.gitignore b/automated-tests/.gitignore index f039d8a..94db43f 100644 --- a/automated-tests/.gitignore +++ b/automated-tests/.gitignore @@ -2,3 +2,4 @@ build build.log tct*core.h +rules.mk \ No newline at end of file diff --git a/automated-tests/src/dali-adaptor-internal/image-loaders.cpp b/automated-tests/src/dali-adaptor-internal/image-loaders.cpp index 8c01772..94a5ae3 100644 --- a/automated-tests/src/dali-adaptor-internal/image-loaders.cpp +++ b/automated-tests/src/dali-adaptor-internal/image-loaders.cpp @@ -18,6 +18,16 @@ #include "image-loaders.h" #include + +class StubImageLoaderClient : public Dali::SlpPlatform::ResourceLoadingClient +{ +public: + StubImageLoaderClient() {} + ~StubImageLoaderClient() {} + + virtual void InterruptionPoint() const {} +}; + AutoCloseFile::AutoCloseFile( FILE *fp ) : filePtr( fp ) { @@ -106,7 +116,7 @@ void TestImageLoading( const ImageDetails& image, const LoadFunctions& functions // Load Bitmap and check its return values. - DALI_TEST_CHECK( functions.loader( fp, *bitmap, attributes ) ); + DALI_TEST_CHECK( functions.loader( fp, *bitmap, attributes, StubImageLoaderClient() ) ); DALI_TEST_EQUALS( image.width, attributes.GetWidth(), TEST_LOCATION ); DALI_TEST_EQUALS( image.height, attributes.GetHeight(), TEST_LOCATION ); @@ -133,7 +143,7 @@ void DumpImageBufferToTempFile( std::string filename, std::string targetFilename Dali::Integration::BitmapPtr bitmapPtr( bitmap ); Dali::ImageAttributes attributes; - DALI_TEST_CHECK( functions.loader( fp, *bitmap, attributes ) ); + DALI_TEST_CHECK( functions.loader( fp, *bitmap, attributes, StubImageLoaderClient() ) ); Dali::PixelBuffer* bufferPtr( bitmapPtr->GetBuffer() ); diff --git a/automated-tests/src/dali-adaptor-internal/image-loaders.h b/automated-tests/src/dali-adaptor-internal/image-loaders.h index 6e4e14c..952d8f1 100644 --- a/automated-tests/src/dali-adaptor-internal/image-loaders.h +++ b/automated-tests/src/dali-adaptor-internal/image-loaders.h @@ -20,6 +20,7 @@ #include #include +#include "platform-abstractions/slp/resource-loader/resource-loading-client.h" // Simple structure to close the file when finished with it. struct AutoCloseFile @@ -81,7 +82,7 @@ private: */ struct LoadFunctions { - typedef bool (*LoadBitmapFunction)(FILE*, Dali::Integration::Bitmap&, Dali::ImageAttributes&); + typedef bool (*LoadBitmapFunction)(FILE*, Dali::Integration::Bitmap&, Dali::ImageAttributes&, const Dali::SlpPlatform::ResourceLoadingClient& client); typedef bool (*LoadBitmapHeaderFunction)(FILE*, const Dali::ImageAttributes& attrs, unsigned int& width, unsigned int& height ); LoadFunctions( LoadBitmapHeaderFunction _header, LoadBitmapFunction _loader ); diff --git a/automated-tests/src/dali-platform-abstraction/utc-image-loading.cpp b/automated-tests/src/dali-platform-abstraction/utc-image-loading.cpp index 8bcc96f..4e41883 100644 --- a/automated-tests/src/dali-platform-abstraction/utc-image-loading.cpp +++ b/automated-tests/src/dali-platform-abstraction/utc-image-loading.cpp @@ -15,6 +15,7 @@ * */ +#include #include #include #include @@ -34,6 +35,13 @@ namespace * A value of 1000 is enough to make load tests take tens of seconds each * on desktop. */ const unsigned NUM_LOAD_GROUPS_TO_ISSUE = 200; + +/** + * The number of loads to issue when they will be cancelled. + * Cancelled loads are cheap so we do a lot. + */ +const unsigned NUM_CANCELLED_LOAD_GROUPS_TO_ISSUE = NUM_LOAD_GROUPS_TO_ISSUE * 10; + /** The number of times to ask for resource load status. */ const unsigned MAX_NUM_RESOURCE_TRIES = 5; @@ -45,20 +53,83 @@ const char* const VALID_IMAGES[] = { TEST_IMAGE_DIR "/interlaced.gif", TEST_IMAGE_DIR "/pattern.gif" }; -const unsigned NUM_VALID_IMAGES = 5u; - +const unsigned NUM_VALID_IMAGES = sizeof(VALID_IMAGES) / sizeof(VALID_IMAGES[0]); ///@ToDo: Add valid ktx, ico, and wbmp image examples. /** Live platform abstraction recreated for each test case. */ Integration::PlatformAbstraction * gAbstraction = 0; +/** A variety of ImageAttributes to reach different code paths that have embedded code paths. */ +std::vector gCancelAttributes; + } // anon namespace void utc_dali_loading_startup(void) { test_return_value = TET_UNDEF; gAbstraction = CreatePlatformAbstraction(); + + // Setup some ImageAttributes to engage post-processing stages: + + ImageAttributes scaleToFillAttributes; + scaleToFillAttributes.SetScalingMode( ImageAttributes::ScaleToFill ); + scaleToFillAttributes.SetSize( 160, 120 ); + gCancelAttributes.push_back( scaleToFillAttributes ); + + // Hit the derived dimensions code: + ImageAttributes scaleToFillAttributesDeriveWidth = scaleToFillAttributes; + scaleToFillAttributesDeriveWidth.SetSize( 0, 120 ); + gCancelAttributes.push_back( scaleToFillAttributesDeriveWidth ); + + ImageAttributes scaleToFillAttributesDeriveHeight = scaleToFillAttributes; + scaleToFillAttributesDeriveHeight.SetSize( 160, 0 ); + gCancelAttributes.push_back( scaleToFillAttributesDeriveHeight ); + + // Try to push a tall crop: + ImageAttributes scaleToFillAttributesTall = scaleToFillAttributes; + scaleToFillAttributesTall.SetSize( 160, 480 ); + ImageAttributes scaleToFillAttributesTall2 = scaleToFillAttributes; + scaleToFillAttributesTall2.SetSize( 160, 509 ); + ImageAttributes scaleToFillAttributesTall3 = scaleToFillAttributes; + scaleToFillAttributesTall3.SetSize( 37, 251 ); + gCancelAttributes.push_back( scaleToFillAttributesTall ); + gCancelAttributes.push_back( scaleToFillAttributesTall2 ); + gCancelAttributes.push_back( scaleToFillAttributesTall3 ); + + // Try to push a wide crop: + ImageAttributes scaleToFillAttributesWide = scaleToFillAttributes; + scaleToFillAttributesWide.SetSize( 320, 60 ); + ImageAttributes scaleToFillAttributesWide2 = scaleToFillAttributes; + scaleToFillAttributesWide2.SetSize( 317, 60 ); + ImageAttributes scaleToFillAttributesWide3 = scaleToFillAttributes; + scaleToFillAttributesWide3.SetSize( 317, 53 ); + gCancelAttributes.push_back( scaleToFillAttributesWide ); + gCancelAttributes.push_back( scaleToFillAttributesWide2 ); + gCancelAttributes.push_back( scaleToFillAttributesWide3 ); + + ImageAttributes shrinkToFitAttributes = scaleToFillAttributes; + shrinkToFitAttributes.SetScalingMode( ImageAttributes::ShrinkToFit ); + gCancelAttributes.push_back( shrinkToFitAttributes ); + + ImageAttributes fitWidthAttributes = scaleToFillAttributes; + fitWidthAttributes.SetScalingMode( ImageAttributes::FitWidth ); + gCancelAttributes.push_back( fitWidthAttributes ); + + ImageAttributes fitHeightAttributes = scaleToFillAttributes; + fitHeightAttributes.SetScalingMode( ImageAttributes::FitHeight ); + gCancelAttributes.push_back( fitHeightAttributes ); + + ///@ToDo: Add attribute variants for all scale modes. + + // Pad the array to a prime number to mitigate any accidental periodic + // patterns in which image file has which attributes applied to its load: + srand48( 104729 ); + const float lastUniques = gCancelAttributes.size() - 0.001f; + while( gCancelAttributes.size() < 61u ) + { + gCancelAttributes.push_back( gCancelAttributes[unsigned(drand48() * lastUniques)] ); + } } void utc_dali_loading_cleanup(void) @@ -98,7 +169,8 @@ int UtcDaliLoadCompletion(void) for( unsigned i = 0; i < MAX_NUM_RESOURCE_TRIES && resourceSink.mGrandTotalCompletions < loadsLaunched; ++i ) { - sleep( 1 ); + tet_printf( "Draining sleep %u, at total completion count %u of %u.\n", i, resourceSink.mGrandTotalCompletions, loadsLaunched ); + usleep( 1200 * 1000 ); gAbstraction->GetResources( resourceSink ); } @@ -110,7 +182,7 @@ int UtcDaliLoadCompletion(void) DALI_TEST_CHECK( 0 == resourceSink.mFailureCounts.size() ); // Check that each success was reported exactly once: - for(ResourceCounterMap::const_iterator it = resourceSink.mSuccessCounts.begin(), end = resourceSink.mSuccessCounts.end(); it != end; ++it ) + for( ResourceCounterMap::const_iterator it = resourceSink.mSuccessCounts.begin(), end = resourceSink.mSuccessCounts.end(); it != end; ++it ) { DALI_TEST_CHECK( it->second == 1u ); } @@ -118,3 +190,156 @@ int UtcDaliLoadCompletion(void) END_TEST; } +/** + * @brief Test case for load cancellation. + * + * Load lots of images in batches, cancelling all in a batch after a small delay to + * allow the first of a batch to be launched before cancellation starts. + * Assert that all loads issued are either completed or cancelled. + */ +int UtcDaliCancelAllLoads(void) +{ + tet_printf( "Running load cancel-all test.\n" ); + + DALI_ASSERT_ALWAYS( gAbstraction != 0 ); + + // Start a bunch of loads that should work: + + Dali::Integration::LoadResourcePriority priority = LoadPriorityNormal; + unsigned loadsLaunched = 0; + + for( unsigned loadGroup = 0; loadGroup < NUM_CANCELLED_LOAD_GROUPS_TO_ISSUE; ++loadGroup ) + { + // Issue load requests for a batch of images: + for( unsigned validImage = 0; validImage < NUM_VALID_IMAGES; ++validImage ) + { + const BitmapResourceType bitmapResourceType( gCancelAttributes[ loadsLaunched % gCancelAttributes.size() ] ); + const ResourceId resourceId = loadGroup * NUM_VALID_IMAGES + validImage + 1; + gAbstraction->LoadResource( ResourceRequest( resourceId, bitmapResourceType, VALID_IMAGES[validImage], priority ) ); + loadsLaunched += 1; + } + + // Let the first image in the batch start to load: + usleep( 5000 ); // This number is tuned. Turn it up too much and all loads will complete and the test will take so long it seems to hang. + + // Cancel all the launched loads from oldest to newest: + for( unsigned validImage = 0; validImage < NUM_VALID_IMAGES; ++validImage ) + { + const ResourceId resourceId = loadGroup * NUM_VALID_IMAGES + validImage + 1; + gAbstraction->CancelLoad( resourceId, ResourceBitmap ); + } + } + + // Drain the completed loads: + Dali::Internal::Platform::ResourceCollector resourceSink; + + unsigned lastCompletions = -1; + for( unsigned i = 0; i < MAX_NUM_RESOURCE_TRIES && resourceSink.mGrandTotalCompletions < loadsLaunched && resourceSink.mGrandTotalCompletions != lastCompletions; ++i ) + { + lastCompletions = resourceSink.mGrandTotalCompletions; + gAbstraction->GetResources( resourceSink ); + tet_printf( "Draining sleep %u, at total completion count %u of %u.\n", i, resourceSink.mGrandTotalCompletions, loadsLaunched ); + usleep( 100 * 1000 ); + } + + // Check the loads completed as expected: + + tet_printf( "Issued Loads: %u, Completed Loads: %u, Successful Loads: %u, Failed Loads: %u \n", loadsLaunched, resourceSink.mGrandTotalCompletions, unsigned(resourceSink.mSuccessCounts.size()), unsigned(resourceSink.mFailureCounts.size()) ); + DALI_TEST_CHECK( loadsLaunched > resourceSink.mGrandTotalCompletions ); + DALI_TEST_CHECK( loadsLaunched > resourceSink.mSuccessCounts.size() ); + DALI_TEST_CHECK( 0 == resourceSink.mFailureCounts.size() ); + + // Check that each success was reported exactly once: + for( ResourceCounterMap::const_iterator it = resourceSink.mSuccessCounts.begin(), end = resourceSink.mSuccessCounts.end(); it != end; ++it ) + { + DALI_TEST_CHECK( it->second == 1u ); + } + + END_TEST; +} + +/** + * @brief Test case for load cancellation. + * + * Load lots, cancel a subset and be sure the wrong loads are never cancelled + * and that all loads issued are either completed or cancelled. + */ +int UtcDaliCancelSomeLoads(void) +{ + tet_printf( "Running load cancel load subset test.\n" ); + + DALI_ASSERT_ALWAYS( gAbstraction != 0 ); + + // Start a bunch of loads that should work: + + Dali::Integration::LoadResourcePriority priority = LoadPriorityNormal; + unsigned loadsLaunched = 0; + + std::set cancelledLoadSet; + + for( unsigned loadGroup = 0; loadGroup < NUM_LOAD_GROUPS_TO_ISSUE; ++loadGroup ) + { + // Issue load requests for a batch of images: + for( unsigned validImage = 0; validImage < NUM_VALID_IMAGES; ++validImage ) + { + const BitmapResourceType bitmapResourceType( gCancelAttributes[ loadsLaunched % gCancelAttributes.size() ] ); + const ResourceId resourceId = loadGroup * NUM_VALID_IMAGES + validImage + 1; + gAbstraction->LoadResource( ResourceRequest( resourceId, bitmapResourceType, VALID_IMAGES[validImage], priority ) ); + loadsLaunched += 1; + } + + // Let the first image in the batch start to load so we can try to cancel it in-flight: + usleep( 17000 ); + ///@Note: The log should show cancellations of many in-flight loads in desktop builds with info-level logging enabled (e.g., "INFO: DALI: : CheckForCancellation: Cancelled in-flight resource (21)."). If it doesn't, the above delay may need to be adjusted. + + // Cancel just two loads (hopefully one in-flight and one queued): + + // Cancel first load, hopefully while it is in-flight: + const ResourceId cancelledInFlight = loadGroup * NUM_VALID_IMAGES + 1; + gAbstraction->CancelLoad( cancelledInFlight, ResourceBitmap ); + cancelledLoadSet.insert( cancelledInFlight ); + + // Cancel second load, that is still queued: + const ResourceId cancelledFromQueue = loadGroup * NUM_VALID_IMAGES + NUM_VALID_IMAGES; + gAbstraction->CancelLoad( cancelledFromQueue, ResourceBitmap ); + cancelledLoadSet.insert( cancelledFromQueue ); + } + + // Drain the completed loads: + + Dali::Internal::Platform::ResourceCollector resourceSink; + + unsigned lastCompletions = -1; + for( unsigned i = 0; i < MAX_NUM_RESOURCE_TRIES && resourceSink.mGrandTotalCompletions < loadsLaunched && resourceSink.mGrandTotalCompletions != lastCompletions; ++i ) + { + lastCompletions = resourceSink.mGrandTotalCompletions; + gAbstraction->GetResources( resourceSink ); + tet_printf( "Draining sleep %u, at total completion count %u of %u.\n", i, resourceSink.mGrandTotalCompletions, loadsLaunched ); + usleep( 100 * 1000 ); + } + + // Check the loads completed as expected: + + tet_printf( "Issued Loads: %u, Completed Loads: %u, Successful Loads: %u, Failed Loads: %u \n", loadsLaunched, resourceSink.mGrandTotalCompletions, unsigned(resourceSink.mSuccessCounts.size()), unsigned(resourceSink.mFailureCounts.size()) ); + DALI_TEST_CHECK( loadsLaunched >= resourceSink.mGrandTotalCompletions ); + DALI_TEST_CHECK( loadsLaunched >= resourceSink.mSuccessCounts.size() ); + DALI_TEST_CHECK( 0 == resourceSink.mFailureCounts.size() ); + + // Check that if an image was not loaded, it is one of the ones that was cancelled: + // This is the main point of this test case. + for( unsigned resourceId = 1; resourceId <= NUM_LOAD_GROUPS_TO_ISSUE * NUM_VALID_IMAGES; ++resourceId ) + { + if( resourceSink.mCompletionStatuses.find( resourceId ) == resourceSink.mCompletionStatuses.end() ) + { + DALI_TEST_CHECK( cancelledLoadSet.find( resourceId ) != cancelledLoadSet.end() ); + } + } + + // Check that each success was reported exactly once: + for(ResourceCounterMap::const_iterator it = resourceSink.mSuccessCounts.begin(), end = resourceSink.mSuccessCounts.end(); it != end; ++it ) + { + DALI_TEST_CHECK( it->second == 1u ); + } + + END_TEST; +} diff --git a/platform-abstractions/portable/atomics.h b/platform-abstractions/portable/atomics.h new file mode 100644 index 0000000..bf5d8ce --- /dev/null +++ b/platform-abstractions/portable/atomics.h @@ -0,0 +1,73 @@ +#ifndef _DALI_INTERNAL_PLATFORM_ATOMICS_H_ +#define _DALI_INTERNAL_PLATFORM_ATOMICS_H_ + +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/* + * atomics.h + * + * Interface for atomic memory operations. + * There may be platform-specific versions of this file in other directories. + */ + +namespace Dali +{ +namespace Internal +{ + +/** + * @brief Atomic write to an aligned memory location in cacheable memory. + * + * For common platforms with coherent caches such as ARM mpcore and Intel CPUs, + * a cacheline can be in a writeable state in the L1 cache of exactly one core + * at a time. Therefore, a write to a location that does not require a read / + * modify / write cycle or cross a cacheline boundary is automatically + * atomic. + * + * @param address A pointer to a location in a cacheable memory region that is + * aligned to a 4 byte boundary. + * @param value A value to assign to the memory location in address. + */ +inline void AtomicWriteToCacheableAlignedAddress( volatile uint32_t * const address, const uint32_t value ) +{ + DALI_ASSERT_DEBUG( ptrdiff_t(address) % 4 == 0 && "Address must be 32 bit aligned" ); + *address = value; +} + +/** + * @brief Atomic read from an aligned memory location in cacheable memory. + * + * For common platforms with coherent caches such as ARM mpcore and Intel CPUs, + * a cacheline can be in a writeable state in the L1 cache of exactly one core + * at a time. Therefore, a read a location that does not cross a cacheline + * boundary is automatically atomic. + * + * @param address A pointer to a location in a cacheable memory region that is + * aligned to a 4 byte boundary. + * @return The value stored at the memory location in address. + */ +inline uint32_t AtomicReadFromCacheableAlignedAddress( const volatile uint32_t * const address ) +{ + DALI_ASSERT_DEBUG( ptrdiff_t(address) % 4 == 0 && "Address must be 32 bit aligned" ); + return *address; +} + +} /* namespace Internal */ +} /* namespace Dali */ + +#endif /* _DALI_INTERNAL_PLATFORM_ATOMICS_H_ */ diff --git a/platform-abstractions/slp/resource-loader/loader-bmp.cpp b/platform-abstractions/slp/resource-loader/loader-bmp.cpp index 3653b12..436986a 100644 --- a/platform-abstractions/slp/resource-loader/loader-bmp.cpp +++ b/platform-abstractions/slp/resource-loader/loader-bmp.cpp @@ -1050,7 +1050,7 @@ bool LoadBmpHeader(FILE *fp, const ImageAttributes& attributes, unsigned int &wi return ret; } -bool LoadBitmapFromBmp(FILE *fp, Bitmap& bitmap, ImageAttributes& attributes) +bool LoadBitmapFromBmp( FILE *fp, Bitmap& bitmap, ImageAttributes& attributes, const ResourceLoadingClient& client ) { DALI_ASSERT_DEBUG(bitmap.GetPackedPixelsProfile() != 0 && "Need a packed pixel bitmap to load into."); if(fp == NULL) diff --git a/platform-abstractions/slp/resource-loader/loader-bmp.h b/platform-abstractions/slp/resource-loader/loader-bmp.h index c8317b2..ec41983 100644 --- a/platform-abstractions/slp/resource-loader/loader-bmp.h +++ b/platform-abstractions/slp/resource-loader/loader-bmp.h @@ -25,7 +25,7 @@ namespace Dali namespace Integration { - class Bitmap; +class Bitmap; } struct ImageAttributes; @@ -33,6 +33,8 @@ struct ImageAttributes; namespace SlpPlatform { +class ResourceLoadingClient; + namespace Bmp { const unsigned char MAGIC_BYTE_1 = 0x42; @@ -47,7 +49,7 @@ const unsigned char MAGIC_BYTE_2 = 0x4D; * @param[in] attributes Describes the dimensions, pixel format and other details for loading the image data * @return true if file decoded successfully, false otherwise */ -bool LoadBitmapFromBmp(FILE *fp, Integration::Bitmap& bitmap, ImageAttributes& attributes); +bool LoadBitmapFromBmp( FILE *fp, Integration::Bitmap& bitmap, ImageAttributes& attributes, const ResourceLoadingClient& client ); /** * Loads the header of a BMP file and fills in the width and height appropriately. diff --git a/platform-abstractions/slp/resource-loader/loader-gif.cpp b/platform-abstractions/slp/resource-loader/loader-gif.cpp index 05312e4..c000f1c 100644 --- a/platform-abstractions/slp/resource-loader/loader-gif.cpp +++ b/platform-abstractions/slp/resource-loader/loader-gif.cpp @@ -272,7 +272,7 @@ bool LoadGifHeader(FILE *fp, const ImageAttributes& attributes, unsigned int &wi return LoadGifHeader(fp, width, height, &gifInfo); } -bool LoadBitmapFromGif(FILE *fp, Bitmap& bitmap, ImageAttributes& attributes) +bool LoadBitmapFromGif(FILE *fp, Bitmap& bitmap, ImageAttributes& attributes, const ResourceLoadingClient& client ) { // Load the GIF Header file. diff --git a/platform-abstractions/slp/resource-loader/loader-gif.h b/platform-abstractions/slp/resource-loader/loader-gif.h index 836ebdc..278ac37 100644 --- a/platform-abstractions/slp/resource-loader/loader-gif.h +++ b/platform-abstractions/slp/resource-loader/loader-gif.h @@ -33,6 +33,8 @@ struct ImageAttributes; namespace SlpPlatform { +class ResourceLoadingClient; + namespace Gif { const unsigned char MAGIC_BYTE_1 = 0x47; @@ -48,7 +50,7 @@ const unsigned char MAGIC_BYTE_2 = 0x49; * @param[in] attributes Describes the dimensions, pixel format and other details for loading the image data * @return true if file decoded successfully, false otherwise */ -bool LoadBitmapFromGif(FILE *fp, Integration::Bitmap& bitmap, ImageAttributes& attributes); +bool LoadBitmapFromGif( FILE *fp, Integration::Bitmap& bitmap, ImageAttributes& attributes, const ResourceLoadingClient& client ); /** * Loads the header of a GIF file and fills in the width and height appropriately. diff --git a/platform-abstractions/slp/resource-loader/loader-ico.cpp b/platform-abstractions/slp/resource-loader/loader-ico.cpp index 76f52b1..578ede6 100644 --- a/platform-abstractions/slp/resource-loader/loader-ico.cpp +++ b/platform-abstractions/slp/resource-loader/loader-ico.cpp @@ -325,7 +325,7 @@ bool LoadIcoHeader(FILE *fp, const ImageAttributes& attributes, unsigned int &wi return true; } -bool LoadBitmapFromIco(FILE *fp, Integration::Bitmap& bitmap, ImageAttributes& attributes) +bool LoadBitmapFromIco( FILE *fp, Integration::Bitmap& bitmap, ImageAttributes& attributes, const ResourceLoadingClient& client ) { IcoData chosen; Dali::Vector map; diff --git a/platform-abstractions/slp/resource-loader/loader-ico.h b/platform-abstractions/slp/resource-loader/loader-ico.h index 7228ee8..9b7155e 100644 --- a/platform-abstractions/slp/resource-loader/loader-ico.h +++ b/platform-abstractions/slp/resource-loader/loader-ico.h @@ -31,6 +31,9 @@ struct ImageAttributes; namespace SlpPlatform { + +class ResourceLoadingClient; + namespace Ico { //00 00 01 00 01 00 20 20 @@ -38,7 +41,7 @@ const unsigned char MAGIC_BYTE_1 = 0x00; const unsigned char MAGIC_BYTE_2 = 0x00; } -bool LoadBitmapFromIco(FILE *fp, Integration::Bitmap& bitmap, ImageAttributes& attributes); +bool LoadBitmapFromIco( FILE *fp, Integration::Bitmap& bitmap, ImageAttributes& attributes, const ResourceLoadingClient& client ); bool LoadIcoHeader(FILE *fp, const ImageAttributes& attributes, unsigned int &width, unsigned int &height ); diff --git a/platform-abstractions/slp/resource-loader/loader-jpeg-turbo.cpp b/platform-abstractions/slp/resource-loader/loader-jpeg-turbo.cpp index cdaa224..2951398 100755 --- a/platform-abstractions/slp/resource-loader/loader-jpeg-turbo.cpp +++ b/platform-abstractions/slp/resource-loader/loader-jpeg-turbo.cpp @@ -15,21 +15,23 @@ * */ +// INTERNAL HEADERS #include "loader-jpeg.h" -#include -#include -#include - -#include +#include "resource-loading-client.h" #include #include #include #include "platform-capabilities.h" + +// EXTERNAL HEADERS #include #include #include +#include +#include +#include #include -#include + namespace Dali { @@ -236,7 +238,7 @@ bool LoadJpegHeader( FILE *fp, unsigned int &width, unsigned int &height ) return true; } -bool LoadBitmapFromJpeg( FILE *fp, Bitmap& bitmap, ImageAttributes& attributes ) +bool LoadBitmapFromJpeg( FILE *fp, Bitmap& bitmap, ImageAttributes& attributes, const ResourceLoadingClient& client ) { int flags=(FORCEMMX ? TJ_FORCEMMX : 0) | (FORCESSE ? TJ_FORCESSE : 0) | @@ -293,7 +295,7 @@ bool LoadBitmapFromJpeg( FILE *fp, Bitmap& bitmap, ImageAttributes& attributes ) } // Allow early cancellation between the load and the decompress: - boost::this_thread::interruption_point(); ///@warning This can throw an exception. + client.InterruptionPoint(); AutoJpg autoJpg(tjInitDecompress()); @@ -353,7 +355,7 @@ bool LoadBitmapFromJpeg( FILE *fp, Bitmap& bitmap, ImageAttributes& attributes ) unsigned char * const bitmapPixelBuffer = bitmap.GetPackedPixelsProfile()->ReserveBuffer(Pixel::RGB888, scaledPostXformWidth, scaledPostXformHeight); // Allow early cancellation before decoding: - boost::this_thread::interruption_point(); ///@warning This can throw an exception. + client.InterruptionPoint(); const int pitch = scaledPreXformWidth * DECODED_PIXEL_SIZE; if( tjDecompress2( autoJpg.GetHandle(), jpegBufferPtr, jpegBufferSize, bitmapPixelBuffer, scaledPreXformWidth, pitch, scaledPreXformHeight, DECODED_PIXEL_LIBJPEG_TYPE, flags ) == -1 ) @@ -371,7 +373,7 @@ bool LoadBitmapFromJpeg( FILE *fp, Bitmap& bitmap, ImageAttributes& attributes ) if( transform != JPGFORM_NONE ) { // Allow early cancellation before shuffling pixels around on the CPU: - boost::this_thread::interruption_point(); ///@warning This can throw an exception. + client.InterruptionPoint(); } bool result = false; diff --git a/platform-abstractions/slp/resource-loader/loader-jpeg.h b/platform-abstractions/slp/resource-loader/loader-jpeg.h index ddb00c6..ec97537 100644 --- a/platform-abstractions/slp/resource-loader/loader-jpeg.h +++ b/platform-abstractions/slp/resource-loader/loader-jpeg.h @@ -36,6 +36,8 @@ struct ImageAttributes; namespace SlpPlatform { +class ResourceLoadingClient; + namespace Jpeg { const unsigned char MAGIC_BYTE_1 = 0xFF; @@ -50,7 +52,7 @@ const unsigned char MAGIC_BYTE_2 = 0xD8; * @param[in] attributes Describes the dimensions, pixel format and other details for loading the image data * @return true if file decoded successfully, false otherwise */ -bool LoadBitmapFromJpeg(FILE *fp, Integration::Bitmap& bitmap, ImageAttributes& attributes); +bool LoadBitmapFromJpeg( FILE *fp, Integration::Bitmap& bitmap, ImageAttributes& attributes, const ResourceLoadingClient& client ); /** * Loads the header of a JPEG file and fills in the width and height appropriately. diff --git a/platform-abstractions/slp/resource-loader/loader-ktx.cpp b/platform-abstractions/slp/resource-loader/loader-ktx.cpp index 22bdc57..da1c823 100755 --- a/platform-abstractions/slp/resource-loader/loader-ktx.cpp +++ b/platform-abstractions/slp/resource-loader/loader-ktx.cpp @@ -304,7 +304,7 @@ bool LoadKtxHeader(FILE * const fp, const ImageAttributes& attributes, unsigned } // File loading API entry-point: -bool LoadBitmapFromKtx(FILE * const fp, Bitmap& bitmap, ImageAttributes& attributes) +bool LoadBitmapFromKtx( FILE * const fp, Bitmap& bitmap, ImageAttributes& attributes, const ResourceLoadingClient& client ) { DALI_COMPILE_TIME_ASSERT( sizeof(Byte) == 1); DALI_COMPILE_TIME_ASSERT( sizeof(uint32_t) == 4); diff --git a/platform-abstractions/slp/resource-loader/loader-ktx.h b/platform-abstractions/slp/resource-loader/loader-ktx.h index a1354e6..9b18007 100644 --- a/platform-abstractions/slp/resource-loader/loader-ktx.h +++ b/platform-abstractions/slp/resource-loader/loader-ktx.h @@ -33,6 +33,8 @@ struct ImageAttributes; namespace SlpPlatform { +class ResourceLoadingClient; + namespace Ktx { const unsigned char MAGIC_BYTE_1 = 0xAB; @@ -48,7 +50,7 @@ const unsigned char MAGIC_BYTE_2 = 0x4B; * @param[in] attributes Describes the dimensions, pixel format and other details for loading the image data * @return true if file loaded successfully, false otherwise */ -bool LoadBitmapFromKtx(FILE * const fp, Integration::Bitmap& bitmap, ImageAttributes& attributes); +bool LoadBitmapFromKtx( FILE * const fp, Integration::Bitmap& bitmap, ImageAttributes& attributes, const ResourceLoadingClient& client ); /** * Loads the header of a KTX file and fills in the width and height appropriately. diff --git a/platform-abstractions/slp/resource-loader/loader-png.cpp b/platform-abstractions/slp/resource-loader/loader-png.cpp index 81d550a..eadc3da 100644 --- a/platform-abstractions/slp/resource-loader/loader-png.cpp +++ b/platform-abstractions/slp/resource-loader/loader-png.cpp @@ -141,7 +141,7 @@ bool LoadPngHeader(FILE *fp, const ImageAttributes& attributes, unsigned int &wi return success; } -bool LoadBitmapFromPng(FILE *fp, Bitmap& bitmap, ImageAttributes& attributes) +bool LoadBitmapFromPng( FILE *fp, Bitmap& bitmap, ImageAttributes& attributes, const ResourceLoadingClient& client ) { png_structp png = NULL; png_infop info = NULL; diff --git a/platform-abstractions/slp/resource-loader/loader-png.h b/platform-abstractions/slp/resource-loader/loader-png.h index 2217c68..698d6ad 100644 --- a/platform-abstractions/slp/resource-loader/loader-png.h +++ b/platform-abstractions/slp/resource-loader/loader-png.h @@ -35,6 +35,8 @@ struct ImageAttributes; namespace SlpPlatform { +class ResourceLoadingClient; + namespace Png { const unsigned char MAGIC_BYTE_1 = 0x89; @@ -49,7 +51,7 @@ const unsigned char MAGIC_BYTE_2 = 0x50; * @param[in] attributes Describes the dimensions, pixel format and other details for loading the image data * @return true if file decoded successfully, false otherwise */ -bool LoadBitmapFromPng(FILE *fp, Integration::Bitmap& bitmap, ImageAttributes& attributes); +bool LoadBitmapFromPng( FILE *fp, Integration::Bitmap& bitmap, ImageAttributes& attributes, const ResourceLoadingClient& client ); /** * Loads the header of a PNG file and fills in the width and height appropriately. diff --git a/platform-abstractions/slp/resource-loader/loader-wbmp.cpp b/platform-abstractions/slp/resource-loader/loader-wbmp.cpp index 1cac543..9759d8f 100755 --- a/platform-abstractions/slp/resource-loader/loader-wbmp.cpp +++ b/platform-abstractions/slp/resource-loader/loader-wbmp.cpp @@ -94,7 +94,7 @@ int extractMultiByteInteger(unsigned int *data, void *map, size_t length, size_t }// end unnamed namespace -bool LoadBitmapFromWbmp(FILE *fp, Integration::Bitmap& bitmap, ImageAttributes& attributes) +bool LoadBitmapFromWbmp( FILE *fp, Integration::Bitmap& bitmap, ImageAttributes& attributes, const ResourceLoadingClient& client ) { if(fp == NULL) diff --git a/platform-abstractions/slp/resource-loader/loader-wbmp.h b/platform-abstractions/slp/resource-loader/loader-wbmp.h index 8565310..dfc64cf 100755 --- a/platform-abstractions/slp/resource-loader/loader-wbmp.h +++ b/platform-abstractions/slp/resource-loader/loader-wbmp.h @@ -32,7 +32,9 @@ struct ImageAttributes; namespace SlpPlatform { -bool LoadBitmapFromWbmp(FILE *fp, Integration::Bitmap& bitmap, ImageAttributes& attributes); +class ResourceLoadingClient; + +bool LoadBitmapFromWbmp( FILE *fp, Integration::Bitmap& bitmap, ImageAttributes& attributes, const ResourceLoadingClient& client ); bool LoadWbmpHeader(FILE *fp, const ImageAttributes& attributes, unsigned int &width, unsigned int &height ); diff --git a/platform-abstractions/slp/resource-loader/resource-loading-client.h b/platform-abstractions/slp/resource-loader/resource-loading-client.h new file mode 100644 index 0000000..df38279 --- /dev/null +++ b/platform-abstractions/slp/resource-loader/resource-loading-client.h @@ -0,0 +1,60 @@ +#ifndef _DALI_PLATFORM_RESOURCE_LOADING_CLIENT_H_ +#define _DALI_PLATFORM_RESOURCE_LOADING_CLIENT_H_ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +// INTERNAL INCLUDES + +// EXTERNAL INCLUDES + +namespace Dali +{ +namespace SlpPlatform +{ + +/** + * @brief Abstract interface to the caller of a low-level resource loading + * function such as a loader for an image format. + */ +class ResourceLoadingClient +{ +public: + /** + * @brief Check whether the current request has been cancelled. + * + * This will throw an exception to unwind the stack if the current request + * has been cancelled. + * + * @note Only place calls to this function at exception-safe locations in loader code. + **/ + virtual void InterruptionPoint() const = 0; + +protected: + /** Construction is restricted to derived / implementing classes. */ + ResourceLoadingClient() {} + /** Destruction of an object through this interface is not allowed. */ + ~ResourceLoadingClient() {} + +private: + ResourceLoadingClient( const ResourceLoadingClient& rhs ); + ResourceLoadingClient& operator =( ResourceLoadingClient& rhs ); +}; + +} /* namespace SlpPlatform */ +} /* namespace Dali */ + +#endif /* _DALI_PLATFORM_RESOURCE_LOADING_CLIENT_H_ */ diff --git a/platform-abstractions/slp/resource-loader/resource-thread-base.cpp b/platform-abstractions/slp/resource-loader/resource-thread-base.cpp index 3a99323..b5f102d 100644 --- a/platform-abstractions/slp/resource-loader/resource-thread-base.cpp +++ b/platform-abstractions/slp/resource-loader/resource-thread-base.cpp @@ -18,6 +18,7 @@ #include #include "resource-thread-base.h" #include "slp-logging.h" +#include "atomics.h" using namespace std; using namespace Dali::Integration; @@ -38,8 +39,14 @@ namespace const char * const IDLE_PRIORITY_ENVIRONMENT_VARIABLE_NAME = "DALI_RESOURCE_THREAD_IDLE_PRIORITY"; ///@Todo Move this to somewhere that other environment variables are declared and document it there. } // unnamed namespace -ResourceThreadBase::ResourceThreadBase(ResourceLoader& resourceLoader) -: mResourceLoader( resourceLoader ), mCurrentRequestId( NO_REQUEST ), mPaused( false ) +/** Thrown by InterruptionPoint() to abort a request early. */ +class CancelRequestException {}; + +ResourceThreadBase::ResourceThreadBase( ResourceLoader& resourceLoader ) : + mResourceLoader( resourceLoader ), + mCurrentRequestId( NO_REQUEST ), + mCancelRequestId( NO_REQUEST ), + mPaused( false ) { #if defined(DEBUG_ENABLED) mLogFilter = Debug::Filter::New(Debug::Concise, false, "LOG_RESOURCE_THREAD_BASE"); @@ -94,32 +101,50 @@ void ResourceThreadBase::AddRequest(const ResourceRequest& request, const Reques } } -void ResourceThreadBase::CancelRequest( Integration::ResourceId resourceId ) +// Called from outer thread. +void ResourceThreadBase::CancelRequest( const Integration::ResourceId resourceId ) { + bool found = false; + DALI_LOG_INFO( mLogFilter, Debug::Verbose, "%s: %u.\n", __FUNCTION__, unsigned(resourceId) ); + + // Eliminate the cancelled request from the request queue if it is in there: { // Lock while searching and removing from the request queue: unique_lock lock( mMutex ); - // See if the request is already launched as the current job on the thread: - //if( mCurrentRequestId == resourceId ) - //{ - // mThread->interrupt(); - //} - // Check the pending requests to be cancelled: - //else + for( RequestQueueIter iterator = mQueue.begin(); + iterator != mQueue.end(); + ++iterator ) { - for( RequestQueueIter iterator = mQueue.begin(); - iterator != mQueue.end(); - ++iterator ) + if( ((*iterator).first).GetId() == resourceId ) { - if( ((*iterator).first).GetId() == resourceId ) - { - iterator = mQueue.erase( iterator ); - break; - } + iterator = mQueue.erase( iterator ); + found = true; + break; } } } + + // Remember the cancelled id for the worker thread to poll at one of its points + // of interruption: + if( !found ) + { + Dali::Internal::AtomicWriteToCacheableAlignedAddress( &mCancelRequestId, resourceId ); + DALI_LOG_INFO( mLogFilter, Debug::Concise, "%s: Cancelling in-flight resource (%u).\n", __FUNCTION__, unsigned(resourceId) ); + } +} + +// Called from worker thread. +void ResourceThreadBase::InterruptionPoint() const +{ + const Integration::ResourceId cancelled = Dali::Internal::AtomicReadFromCacheableAlignedAddress( &mCancelRequestId ); + const Integration::ResourceId current = mCurrentRequestId; + + if( current == cancelled ) + { + DALI_LOG_INFO( mLogFilter, Debug::Concise, "%s: Cancelled in-flight resource (%u).\n", __FUNCTION__, unsigned(cancelled) ); + throw CancelRequestException(); + } } void ResourceThreadBase::Pause() @@ -175,21 +200,20 @@ void ResourceThreadBase::ThreadLoop() } } - catch( boost::thread_interrupted& ex ) + catch( CancelRequestException& ex ) { - // No problem, thread was just interrupted from the outside to cancel an in-flight request. - boost::thread_interrupted* disableUnusedVarWarning = &ex; + // No problem: a derived class deliberately threw to abort an in-flight request + // that was cancelled. + DALI_LOG_INFO( mLogFilter, Debug::Concise, "%s: Caught cancellation exception for resource (%u).\n", __FUNCTION__, unsigned(mCurrentRequestId) ); + CancelRequestException* disableUnusedVarWarning = &ex; ex = *disableUnusedVarWarning; - // Temporary logging of an unexpected boost::thread_interrupted exception: - DALI_LOG_ERROR( "boost::thread_interrupted caught in resource thread in build with late cancellation disabled (should not happen). Aborting request with id %u.\n", unsigned(mCurrentRequestId) ); } - // Since we have an exception handler here anyway, lets catch everything to avoid killing the process: + // Catch all exceptions to avoid killing the process, and log the error: catch( std::exception& ex ) { const char * const what = ex.what(); DALI_LOG_ERROR( "std::exception caught in resource thread. Aborting request with id %u because of std::exception with reason, \"%s\".\n", unsigned(mCurrentRequestId), what ? what : "null" ); - } catch( Dali::DaliException& ex ) { @@ -207,9 +231,6 @@ void ResourceThreadBase::WaitForRequests() { unique_lock lock( mMutex ); - // Clear the previously current request: - mCurrentRequestId = NO_REQUEST; - if( mQueue.empty() || mPaused == true ) { // Waiting for a wake up from resource loader control thread @@ -261,13 +282,6 @@ void ResourceThreadBase::ProcessNextRequest() } break; } - - // Clear the interruption status for derived classes that don't implement on-the-fly cancellation yet: - boost::this_thread::interruption_point(); ///@warning This can throw an exception. - // To support cancellation of an in-flight resource, place the above line at key points in derived - // resource thread classes and the loading / decoding / saving code that they call. - // See resource-thread-image.cpp and loader-jpeg-turbo.cpp for a conservative example of its use. - ///@note: The above line will throw an exception so only place it in exception-safe locations. } } diff --git a/platform-abstractions/slp/resource-loader/resource-thread-base.h b/platform-abstractions/slp/resource-loader/resource-thread-base.h index ffabc67..aabbfa9 100644 --- a/platform-abstractions/slp/resource-loader/resource-thread-base.h +++ b/platform-abstractions/slp/resource-loader/resource-thread-base.h @@ -18,13 +18,16 @@ * */ +// INTERNAL INCLUDES #include "resource-loader.h" -#include -#include - +#include "resource-loading-client.h" #include #include +// EXTERNAL INCLUDES +#include +#include + namespace Dali { @@ -34,7 +37,7 @@ namespace SlpPlatform /** * Resource loader worker thread */ -class ResourceThreadBase +class ResourceThreadBase : public ResourceLoadingClient { public: // typedefs and enums @@ -134,13 +137,22 @@ protected: */ virtual void Save(const Integration::ResourceRequest& request) = 0; + /** + * @brief Cancels current resource request if it matches the one latched to be cancelled. + * + * @copydoc ResourceLoadingClient::InterruptionPoint + */ + virtual void InterruptionPoint() const; + protected: ResourceLoader& mResourceLoader; boost::thread* mThread; ///< thread instance boost::condition_variable mCondition; ///< condition variable boost::mutex mMutex; ///< used to protect mQueue RequestQueue mQueue; ///< Request queue - Integration::ResourceId mCurrentRequestId; ///< Request being processed on thread +private: + Integration::ResourceId mCurrentRequestId; ///< Current request, set by worker thread + volatile Integration::ResourceId mCancelRequestId; ///< Request to be cancelled on thread: written by external thread and read by worker. bool mPaused; ///< Whether to process work in mQueue #if defined(DEBUG_ENABLED) diff --git a/platform-abstractions/slp/resource-loader/resource-thread-distance-field.cpp b/platform-abstractions/slp/resource-loader/resource-thread-distance-field.cpp index 5cbada8..56231ae 100644 --- a/platform-abstractions/slp/resource-loader/resource-thread-distance-field.cpp +++ b/platform-abstractions/slp/resource-loader/resource-thread-distance-field.cpp @@ -41,7 +41,7 @@ namespace SlpPlatform namespace { -typedef bool (*LoadBitmapFunction)(FILE*, Bitmap&, ImageAttributes&); +typedef bool (*LoadBitmapFunction)(FILE*, Bitmap&, ImageAttributes&, const ResourceLoadingClient& ); typedef bool (*LoadBitmapHeaderFunction)(FILE*, const ImageAttributes&, unsigned int&, unsigned int&); /* @@ -164,7 +164,7 @@ void ResourceThreadDistanceField::Load(const ResourceRequest& request) if (GetBitmapLoaderFunctions(fp, function, header)) { - result = function(fp, *bitmap, attributes); + result = function(fp, *bitmap, attributes, *this); if (result) { @@ -264,7 +264,6 @@ void ResourceThreadDistanceField::Save(const Integration::ResourceRequest& reque DALI_ASSERT_DEBUG(request.GetType()->id == ResourceBitmap); } - } // namespace SlpPlatform } // namespace Dali diff --git a/platform-abstractions/slp/resource-loader/resource-thread-distance-field.h b/platform-abstractions/slp/resource-loader/resource-thread-distance-field.h index c74966e..8884c57 100644 --- a/platform-abstractions/slp/resource-loader/resource-thread-distance-field.h +++ b/platform-abstractions/slp/resource-loader/resource-thread-distance-field.h @@ -61,8 +61,6 @@ private: */ virtual void Save(const Integration::ResourceRequest& request); -private: - }; // class ResourceThreadImage } // namespace SlpPlatform diff --git a/platform-abstractions/slp/resource-loader/resource-thread-image.cpp b/platform-abstractions/slp/resource-loader/resource-thread-image.cpp index 8a5c9af..3d25b1b 100755 --- a/platform-abstractions/slp/resource-loader/resource-thread-image.cpp +++ b/platform-abstractions/slp/resource-loader/resource-thread-image.cpp @@ -46,7 +46,7 @@ namespace SlpPlatform namespace { -typedef bool (*LoadBitmapFunction)(FILE*, Bitmap&, ImageAttributes&); ///@ToDo: Make attributes a const reference? +typedef bool (*LoadBitmapFunction)( FILE*, Bitmap&, ImageAttributes&, const ResourceLoadingClient& ); ///@ToDo: Make attributes a const reference? typedef bool (*LoadBitmapHeaderFunction)(FILE*, const ImageAttributes& attrs, unsigned int& width, unsigned int& height ); /** @@ -382,7 +382,7 @@ void ResourceThreadImage::Load(const ResourceRequest& request) result = ConvertStreamToBitmap( *request.GetType(), request.GetPath(), fp, bitmap ); // Last chance to interrupt a cancelled load before it is reported back to clients // which have already stopped tracking it: - boost::this_thread::interruption_point(); // Note: This can throw an exception. + InterruptionPoint(); // Note: This can throw an exception. if( result && bitmap ) { // Construct LoadedResource and ResourcePointer for image data @@ -501,9 +501,9 @@ bool ResourceThreadImage::ConvertStreamToBitmap(const ResourceType& resourceType ImageAttributes attributes = resType.imageAttributes; // Check for cancellation now we have hit the filesystem, done some allocation, and burned some cycles: - boost::this_thread::interruption_point(); // Note: This can throw an exception. + InterruptionPoint(); // Note: This can throw an exception. - result = function( fp, *bitmap, attributes ); + result = function( fp, *bitmap, attributes, *this ); if (!result) { @@ -550,7 +550,7 @@ bool ResourceThreadImage::ConvertStreamToBitmap(const ResourceType& resourceType // Make a new bitmap with the central part of the loaded one if required: if( scanlinesToTrim > 0 || columnsToTrim > 0 ) ///@ToDo: Make this test a bit fuzzy (allow say a 5% difference). { - boost::this_thread::interruption_point(); //< Check for cancellation before doing the heavy lifting (Note: This can throw an exception.) + InterruptionPoint(); // Note: This can throw an exception. const unsigned newWidth = loadedWidth - 2 * columnsToTrim; const unsigned newHeight = loadedHeight - 2 * scanlinesToTrim; @@ -595,7 +595,6 @@ bool ResourceThreadImage::ConvertStreamToBitmap(const ResourceType& resourceType return result; } - } // namespace SlpPlatform } // namespace Dali diff --git a/platform-abstractions/slp/resource-loader/resource-thread-image.h b/platform-abstractions/slp/resource-loader/resource-thread-image.h index 60152a4..e9a4b36 100644 --- a/platform-abstractions/slp/resource-loader/resource-thread-image.h +++ b/platform-abstractions/slp/resource-loader/resource-thread-image.h @@ -103,7 +103,6 @@ private: FILE * const fp, Integration::BitmapPtr& ptr ); - }; // class ResourceThreadImage } // namespace SlpPlatform