diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 00000000..279afadd --- /dev/null +++ b/.travis.yml @@ -0,0 +1,24 @@ +language: cpp +compiler: +- gcc +before_install: +- wget https://raw.githubusercontent.com/smessmer/travis-utils/master/update_gcc_version.sh + && chmod +x update_gcc_version.sh + && ./update_gcc_version.sh 4.9 + && rm update_gcc_version.sh +before_script: +- wget https://raw.githubusercontent.com/smessmer/travis-utils/master/setup_biicode_project.sh + && chmod +x setup_biicode_project.sh + && ./setup_biicode_project.sh + && rm setup_biicode_project.sh +script: +- bii cpp:build --target messmer_cachingstore_test_main -- -j2 +- "./bin/messmer_cachingstore_test_main" +deploy: + provider: biicode + user: ${BII_USERNAME} + password: + secure: ${BII_PASSWORD} + on: + branch: develop + diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 00000000..63322e29 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,91 @@ +INCLUDE(messmer/cmake/tools) + +ADD_BIICODE_TARGETS() + +ACTIVATE_CPP14() + +# You can safely delete lines from here... + +############################################################################### +# REFERENCE # +############################################################################### +# +# This CMakeLists.txt file helps defining your block building and compiling +# To learn more about the CMake use with biicode, visit http://docs.biicode.com/c++.html +# +# ---------------------------------------------------- +# NEW FEATURE! Include cmake files from remote blocks: +# ----------------------------------------------------- +# Now you can handle cmake dependencies alike you do with c/c++: +# +# INCLUDE(user/block/myrecipe) # include myrecipe.cmake from remote user/block +# +# > EXAMPLE: Include our recipes and activate C++11 in your block (http://www.biicode.com/biicode/cmake) +# +# INCLUDE(biicode/cmake/tools) # Include tools.cmake file from cmake block from the "biicode" user +# ACTIVATE_CPP11(INTERFACE ${BII_BLOCK_TARGET}) +# +# Remember to run "bii find" to download out cmake tools file +# +# --------------------- +# INIT_BIICODE_BLOCK() +# --------------------- +# This function creates several helper variables as ${BII_BLOCK_NAME} and ${BII_BLOCK_USER} +# Also it loads variables from the cmake/bii_user_block_vars.cmake +# ${BII_LIB_SRC} File list to create the library +# ${BII_LIB_TYPE} Empty (default, STATIC most casess) STATIC or SHARED +# ${BII_LIB_DEPS} Dependencies to other libraries (user2_block2, user3_blockX) +# ${BII_LIB_SYSTEM_HEADERS} System linking requirements as windows.h, pthread.h, etc +# +# You can use or modify them here, for example, to add or remove files from targets based on OS +# Or use typical cmake configurations done BEFORE defining targets. Examples: +# ADD_DEFINITIONS(-DFOO) +# FIND_PACKAGE(OpenGL QUIET) +# You can add INCLUDE_DIRECTORIES here too +# +# --------------------- +# ADD_BIICODE_TARGETS() +# --------------------- +# +# This function creates the following variables: +# ${BII_BLOCK_TARGET} Interface (no files) target for convenient configuration of all +# targets in this block, as the rest of targets always depend on it +# has name in the form "user_block_interface" +# ${BII_LIB_TARGET} Target library name, usually in the form "user_block". May not exist +# if BII_LIB_SRC is empty +# ${BII_BLOCK_TARGETS} List of all targets defined in this block +# ${BII_BLOCK_EXES} List of executables targets defined in this block +# ${BII_exe_name_TARGET}: Executable target (e.g. ${BII_main_TARGET}. You can also use +# directly the name of the executable target (e.g. user_block_main) +# +# > EXAMPLE: Add include directories to all targets of this block +# +# TARGET_INCLUDE_DIRECTORIES(${BII_BLOCK_TARGET} INTERFACE myincludedir) +# +# You can add private include directories to the Lib (if existing) +# +# > EXAMPLE: Link with pthread: +# +# TARGET_LINK_LIBRARIES(${BII_BLOCK_TARGET} INTERFACE pthread) +# or link against library: +# TARGET_LINK_LIBRARIES(${BII_LIB_TARGET} PUBLIC pthread) +# or directly use the library target name: +# TARGET_LINK_LIBRARIES(user_block PUBLIC pthread) +# +# NOTE: This can be also done adding pthread to ${BII_LIB_DEPS} +# BEFORE calling ADD_BIICODE_TARGETS() +# +# > EXAMPLE: how to activate C++11 +# +# IF(APPLE) +# TARGET_COMPILE_OPTIONS(${BII_BLOCK_TARGET} INTERFACE "-std=c++11 -stdlib=libc++") +# ELSEIF (WIN32 OR UNIX) +# TARGET_COMPILE_OPTIONS(${BII_BLOCK_TARGET} INTERFACE "-std=c++11") +# ENDIF(APPLE) +# +# > EXAMPLE: Set properties to target +# +# SET_TARGET_PROPERTIES(${BII_BLOCK_TARGET} PROPERTIES COMPILE_DEFINITIONS "IOV_MAX=255") +# + + diff --git a/CachingBaseStore.h b/CachingBaseStore.h new file mode 100644 index 00000000..3a7d1ed3 --- /dev/null +++ b/CachingBaseStore.h @@ -0,0 +1,18 @@ +#ifndef MESSMER_CACHINGSTORE_CACHINGBASESTORE_H_ +#define MESSMER_CACHINGSTORE_CACHINGBASESTORE_H_ + +#include + +namespace cachingstore { + +template +class CachingBaseStore { +public: + virtual ~CachingBaseStore() {} + virtual std::unique_ptr loadFromBaseStore(const Key &key) = 0; + virtual void removeFromBaseStore(std::unique_ptr block) = 0; +}; + +} + +#endif diff --git a/CachingStore.h b/CachingStore.h new file mode 100644 index 00000000..0e19159a --- /dev/null +++ b/CachingStore.h @@ -0,0 +1,156 @@ +#ifndef MESSMER_BLOCKSTORE_IMPLEMENTATIONS_CACHING_CACHINGSTORE_H_ +#define MESSMER_BLOCKSTORE_IMPLEMENTATIONS_CACHING_CACHINGSTORE_H_ + +#include +#include +#include +#include +#include +#include + +#include "CachingBaseStore.h" + +//TODO Refactor +//TODO Test cases + +namespace cachingstore { + +template +class CachingStore { +public: + CachingStore(std::unique_ptr> baseStore) + : _mutex(), + _baseStore(std::move(baseStore)), + _openResources(), + _resourcesToRemove() { + } + + //TODO Enforce CachedResourceRef inherits from CachedResource + + class CachedResource { + public: + //TODO Better way to initialize + CachedResource(): _cachingStore(nullptr), _key(Key::CreateRandomKey()) {} + void init(CachingStore *cachingStore, const Key &key) { + _cachingStore = cachingStore; + _key = key; + } + virtual ~CachedResource() { + _cachingStore->release(_key); + } + private: + CachingStore *_cachingStore; + //TODO We're storing Key twice (here and in the base resource). Rather use getKey() on the base resource if possible somehow. + Key _key; + }; + + std::unique_ptr add(const Key &key, std::unique_ptr resource); + std::unique_ptr load(const Key &key); + void remove(const Key &key, std::unique_ptr block); + +private: + class OpenResource { + public: + OpenResource(std::unique_ptr resource): _resource(std::move(resource)), _refCount(0) {} + + Resource *getReference() { + ++_refCount; + return _resource.get(); + } + + void releaseReference() { + --_refCount; + } + + bool refCountIsZero() const { + return 0 == _refCount; + } + + std::unique_ptr moveResourceOut() { + return std::move(_resource); + } + private: + std::unique_ptr _resource; + uint32_t _refCount; + }; + + std::mutex _mutex; + std::unique_ptr> _baseStore; + + std::map _openResources; + std::map>> _resourcesToRemove; + + std::unique_ptr _add(const Key &key, std::unique_ptr resource); + std::unique_ptr _createCachedResourceRef(Resource *resource, const Key &key); + + void release(const Key &key); + friend class CachedResource; + + DISALLOW_COPY_AND_ASSIGN(CachingStore); +}; + +template +std::unique_ptr CachingStore::add(const Key &key, std::unique_ptr resource) { + std::lock_guard lock(_mutex); + return _add(key, std::move(resource)); +} + +template +std::unique_ptr CachingStore::_add(const Key &key, std::unique_ptr resource) { + auto insertResult = _openResources.emplace(key, std::move(resource)); + assert(true == insertResult.second); + return _createCachedResourceRef(insertResult.first->second.getReference(), key); +} + +template +std::unique_ptr CachingStore::_createCachedResourceRef(Resource *resource, const Key &key) { + auto resourceRef = std::make_unique(resource); + resourceRef->init(this, key); + return std::move(resourceRef); +} + +template +std::unique_ptr CachingStore::load(const Key &key) { + std::lock_guard lock(_mutex); + auto found = _openResources.find(key); + if (found == _openResources.end()) { + auto resource = _baseStore->loadFromBaseStore(key); + if (resource.get() == nullptr) { + return nullptr; + } + return _add(key, std::move(resource)); + } else { + return _createCachedResourceRef(found->second.getReference(), key); + } +} + +template +void CachingStore::remove(const Key &key, std::unique_ptr resource) { + auto insertResult = _resourcesToRemove.emplace(key, std::promise>()); + assert(true == insertResult.second); + resource.reset(); + + //Wait for last resource user to release it + auto resourceToRemove = insertResult.first->second.get_future().get(); + + _baseStore->removeFromBaseStore(std::move(resourceToRemove)); +} + +template +void CachingStore::release(const Key &key) { + std::lock_guard lock(_mutex); + auto found = _openResources.find(key); + assert (found != _openResources.end()); + found->second.releaseReference(); + if (found->second.refCountIsZero()) { + auto foundToRemove = _resourcesToRemove.find(key); + if (foundToRemove != _resourcesToRemove.end()) { + foundToRemove->second.set_value(found->second.moveResourceOut()); + } + _openResources.erase(found); + } +} + +} + +#endif diff --git a/biicode.conf b/biicode.conf new file mode 100644 index 00000000..2beadd49 --- /dev/null +++ b/biicode.conf @@ -0,0 +1,49 @@ +# Biicode configuration file + +[requirements] + google/gtest: 10 + messmer/cmake: 3 + messmer/cpp-utils: 2 + +[parent] + # The parent version of this block. Must match folder name. E.g. + # user/block # No version number means not published yet + # You can change it to publish to a different track, and change version, e.g. + # user/block(track): 7 + +[paths] + # Local directories to look for headers (within block) + # / + # include + +[dependencies] + # Manual adjust file implicit dependencies, add (+), remove (-), or overwrite (=) + # hello.h + hello_imp.cpp hello_imp2.cpp + # *.h + *.cpp + test/main.cpp + test/*.cpp + +[mains] + # Manual adjust of files that define an executable + # !main.cpp # Do not build executable from this file + # main2.cpp # Build it (it doesnt have a main() function, but maybe it includes it) + +[tests] + # Manual adjust of files that define a CTest test + # test/* pattern to evaluate this test/ folder sources like tests + test/* + +[hooks] + # These are defined equal to [dependencies],files names matching bii*stage*hook.py + # will be launched as python scripts at stage = {post_process, clean} + # CMakeLists.txt + bii/my_post_process1_hook.py bii_clean_hook.py + +[includes] + # Mapping of include patterns to external blocks + # hello*.h: user3/depblock # includes will be processed as user3/depblock/hello*.h + +[data] + # Manually define data files dependencies, that will be copied to bin for execution + # By default they are copied to bin/user/block/... which should be taken into account + # when loading from disk such data + # image.cpp + image.jpg # code should write open("user/block/image.jpg") + diff --git a/test/CachingBaseStoreTest.cpp b/test/CachingBaseStoreTest.cpp new file mode 100644 index 00000000..9e05e76b --- /dev/null +++ b/test/CachingBaseStoreTest.cpp @@ -0,0 +1,3 @@ +#include "../CachingBaseStore.h" + +// Test that CachingBaseStore.h can be included without errors diff --git a/test/DummyTest.cpp b/test/DummyTest.cpp new file mode 100644 index 00000000..c0f770b3 --- /dev/null +++ b/test/DummyTest.cpp @@ -0,0 +1,5 @@ +#include + +TEST(Dummy, DummyTest) { +} + diff --git a/test/main.cpp b/test/main.cpp new file mode 100644 index 00000000..f7a14877 --- /dev/null +++ b/test/main.cpp @@ -0,0 +1,6 @@ +#include "google/gtest/gtest.h" + +int main(int argc, char **argv) { + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +}