From 9e595d59eb6d5245c37d3f701cca10205841b206 Mon Sep 17 00:00:00 2001 From: tcormackMW <113473781+tcormackMW@users.noreply.github.com> Date: Fri, 8 Dec 2023 08:49:24 -0500 Subject: [PATCH] [ci-skip] Merge branch 'Release-3.8' (#974) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Default any map type (#844) * Fixed undefined behavior in Any::operator==(value) * Revert "Fixed undefined behavior in Any::operator==(value)" This reverts commit 3066fe1128da0aad1215e811be7ddf211b56be77. * Default AnyMap(map_type) constructor to UNORDERED_MAP_CASINSENSITIVE_KEYS * Remove CoreBundleContext.dataStorage clear (#845) Fixes #733 * Removing std::move use with const object [ci skip] (#848) * Removing std::move use with const [ci skip] Signed-off-by: Tahar Touati * Addressing PR feedback, removing const from complex types and adding std move back [ci skip] Signed-off-by: Tahar Touati --------- Signed-off-by: Tahar Touati * [ci skip] Update Changelog for #845 (#847) * Update Chagelog for #845 [ci skip] * Update Changelog Unreleased section [ci skip] Signed-off-by: ShivamNegi --------- Signed-off-by: ShivamNegi * Fix warning "Use of BITWISE AND to check if a flag is set" (#849) Signed-off-by: Tahar Touati * include cstdint in FileSystem.cpp (#850) vcpkg installation is failing, with this error: PATH/v3.6.0-e25b133cd3.clean/util/src/FileSystem.cpp:122:3: error: ‘uint32_t’ was not declared in this scope PATH/v3.6.0-e25b133cd3.clean/util/src/FileSystem.cpp:71:1: note: ‘uint32_t’ is defined in header ‘’; did you forget to ‘#include ’? Sure enough, when patched with this it will build and install correctly with vcpkg. Other errors are reported but this is the culprit. * Fixes #840: removes manual reference counting (#841) * converted registration * compiled, not passeD * fixed weak_ptr error, now leaking mock objects * fixed bug, need to decrement ref count * updats from last week, test cases failing because of out of date expectations * all tests are passing with shared and weak pointers to serviceRegistrationBasePrivate * still passing tests, updated comments and cleaned up * updating to share dependents * new issue with dying service * added coreInfo, maybe passing * passing tests, removed manual ref counting from referenceBasePrivate, repeated all fast tests to ensure no sporadic failures * updated comments * updates before PR * Removed manual reference counting The manual reference counting in ServiceRegistrationBasePrivate and ServiceReferenceBasePrivate were removed. Additionally, some properties of ServiceRegistrationBasePrivate were offloaded to a new class ServiceRegistrationCoreInfo which both ServiceRegistrationBasePrivate and ServiceReferenceBasePrivate can access allowing ServiceReferenceBasePrivate to give up ownership of ServiceRegistrationBasePrivate. Signed-off-by: Toby Cormack * Removed manual reference counting and merged with upstream (#840) The manual reference counting in ServiceRegistrationBasePrivate and ServiceReferenceBasePrivate were removed. Additionally, some properties of ServiceRegistrationBasePrivate were offloaded to a new class ServiceRegistrationCoreInfo which both ServiceRegistrationBasePrivate and ServiceReferenceBasePrivate can access allowing ServiceReferenceBasePrivate to give up ownership of ServiceRegistrationBasePrivate. Signed-off-by: Toby Cormack * ServiceRegistrationCoreInfo now default destructor * Updated based on Patty's comments #840 * updated ServiceReferenceBase Constructors for clarity with shared_ptrs * removed 'move' from serviceRegistry * attempt at solving mac issue * updates for lock type and removing unneccessary functions from reference * lost lock * changed to custom atomic load * LockSet addition * threading support in LockSet * LockSet not threaded * no names in func dec * Incoorporated Jeff's Comments * Assignment operator didn't fail on my computer, did in github * updated for Jeff's 5/16 comments * clang update * mikes comments and fixes for multithreaded support * remove ifdefs from BundleRegistry, abide by rule of (0,3,5), and add comments --------- Signed-off-by: Toby Cormack * Fixes #852: adds a [[nodiscard]] to BundleContext::RegisterService (#863) * noDiscard update first push * Jeff's comments, c++14 in cmakelists for makefile test, comments to reference nodiscard * [ci skip] Updated README to reflect correct compiler/OS versions (#862) [ci skip] * Fix code scanning alerts (#861) Fixes #860, #859, #858, #857, #856, #855, #853 * Ensure multiple listeners for the same factory PID are honoured by `ConfigurationNotifier::AnyListenersForPid` (#865) * Ensure multiple listeners for the same PID are honoured Signed-off-by: Conor Burgess * Fix formatting Signed-off-by: Conor Burgess --------- Signed-off-by: Conor Burgess Co-authored-by: cburgess * Code scanning alert fix: Use of a moved from object #864 (#866) Fixes #864 * tests passing on linux, all files updated * Mike and Toby backat it again * changed second instance of warning? * reverted naming, final submit * Fix race condition when concurrently adding to SCRExtensionRegistry (#870) * Fix race condition when concurrently adding to SCRExtensionRegistry Fixed a race that can happen when multiple threads are trying to add to the extension registry container. Fixed a bug with the tests for SCRExtensionRegsitry class never being compiled and run. Signed-off-by: The MathWorks, Inc. * Fix #868: Recoups some of performance losses from PR #841 (#869) * fixed lockReg shared_ptr * down to 5% increase from original * get logs from github to verify behavior * reverting performance yml and adding move constructor (default) for RegistrationLocks : * #Issue873: Redundant calls to .Load() (#874) * first commit * test Case * revert test * test fix * naming * Fixes #718 (#876) Signed-off-by: Toby Cormack * Fix #489 - Char Const* property (#877) * Added case for char const* Signed-off-by: tcormack * formatting --------- Signed-off-by: tcormack * Fix #879 Change TPP extensions (#880) Signed-off-by: tcormack * Fix #881 Reformat `.hpp` files with Clang * Fix #846 (#878) * firstCommit * events is all wait for false flags * removed txt files Signed-off-by: tcormack * serviceTracker tsan fixes Signed-off-by: tcormack * deleted txt Signed-off-by: tcormack * tsanSupppressions updated for various tests Signed-off-by: tcormack * removed text files Signed-off-by: tcormack * removed test * added comment Signed-off-by: tcormack * updated suppresssions file with commentss * add sleep to stop spin * add unused flag to lock Signed-off-by: tcormack * revert reformat * formatting * formatting * reformat * updating comments * update comment --------- Signed-off-by: tcormack * Fix #710 ServiceTracker TSAN warnings (#883) * fixed tracker race * fixing tsanSupp * fixing tsan mistakes * fixed tracker race * tsan_suppresssions mistype * fixed comments * typos * Fix #872 (#875) Remove Statics in MultipleListenersTest.cpp and ServiceHooksTest.cpp Signed-off-by: tcormack * Remove unwanted branches from workflow (#902) Co-authored-by: aadityap * Fix #920 serviceTracker segfault on concurrent `tracker.Close()` and `framework.Stop()` (#922) Signed-off-by: tcormack * Add condition on development branch (#916) * Add condition on development branch Signed-off-by: The MathWorks, Inc. aadityap@mathworks.com Co-authored-by: aadityap * Fix #926 Custom CppMicroServices Boost Namespace (#929) Signed-off-by: tcormack * Update 3rd party dependencies to versions from c++14-compliant branch (#930) * update googletest to release 1.14.0 * update spdlog to version 1.12.0 * update boost nowide to latest commit from standalone branch * update absl to Abseil LTS 20230125.3 * Updated amalgamated jsoncpp to 1.9.5 This also fixes compiler warnings Signed-off-by: Ingmar Sittl * Upgrade miniz to 3.0.2 release Signed-off-by: Ingmar Sittl --------- Signed-off-by: Ingmar Sittl * Fix issues with gcc-12 and clang-16 (#932) * Remove unused variable to fix clang-16 warning * Addd missing include when using libstdc++ headers from gcc-12 Signed-off-by: Ingmar Sittl * Fix Redundant Bundle Validation checks (#921) Signed-off-by: Shivam Negi * Fix #913 ServiceTracker deadlock (#915) Signed-off-by: tcormack * Adding support for multiple cardinality for service references. (#871) * Adding support for multiple cardinality for service references. Signed-off-by: The MathWorks, Inc. mphadnis@mathworks.com * Fixing build failures on Linux/Maci platform * Addressing feedbacks: minor code changes + modifications in unitttests. Signed-off-by: The MathWorks, Inc. * Modified test point to check for compile time errors. Fixed formatting in test file. Signed-off-by: The MathWorks, Inc. mphadnis@mathworks.com * Changes to automate existing ServiceComponent tests using CMake as compile-only tests. Signed-off-by: The MathWorks, Inc. mphadnis@mathworks.com * Fixing clang formatting for test files. Signed-off-by: The MathWorks, Inc. mphadnis@mathworks.com * Improvising compile time tests based on feedbacks. Signed-off-by: The MathWorks, Inc. mphadnis@mathworks.com * Added doxygen comments to new methods in ReferenceManager Signed-off-by: The MathWorks, Inc. mphadnis@mathworks.com --------- Co-authored-by: mphadnis * Fix #938 Boost namespace Versioning and remove absl dependency (#939) * Revert "Fix #926 Custom CppMicroServices Boost Namespace (#929)" This reverts commit aab89e36401b6ec84e121597a517dfb5c2e2263e. Remove commit to reset to original boost namespace * Change Cmake Infrastructure to allow custom Boost library use and remove old commit that changed boost namespace * abseil dependency removal * fix double forward -- maybe, this may be slow * references instead of copies * cmake update * Fix #937 `GetServiceReferences` ordering guarantee (#943) Signed-off-by: tcormack * BundleTracker Implementation (#726) * Bundle tracker (#709) * BundleTracker stubs * Generalize BundleAbstractTracked * Implement BundleTracker * Implement BundleTracker in DeclarativeServices using raw pointers * Use std::optional in BundleTracker API * BundleTracker template instantiations * Create initial tests * Move BundleTrackerTest.cpp * Update BundleTrackerTest.cpp * Implement TestGetTrackingCountClosed * Implement GetTrackingCount tests * Modify tests relating to unknown expected behavior * Implement TestGetTracked and TestOpenOpened * Update build_and_test_windows.yml * Implement and update tests * Update tests * Use shared ptr for BundleTrackerCustomizer * Implement and update tests * Implement and update tests * Rename method test file and fixture * Update method tests * Add TODOs * Add branch to ci * Update CXX_STANDARD to 17 for doc * various fixes * Update CMakeLIsts * Create BundleTrackerCustomCallbackTest.cpp * Implement and update custom callback tests * Update build_and_test_nix.yml * Update custom callback tests * Implement test subset * Resolve cross-platform build issues * Implement and update tests * Implement and update tests * Further CI fixes * Update method tests * Update method tests to handle build shared libraries off * Update custom callback tests to handle build shared libraries off * Implement CreateStateMask variadic * Implement and update method tests * Implement test * Update tests to use state mask creator * Implement custom callback tests * Create concurrency tests * Use std::optional for GetObject * Update custom callback tests * Ignore bundleEvents without state changes * Use std::optional for GetCustomizedObject * Update GetObject test * Do not imply implicit cast from std::optional * Update custom callback tests * Add lock checkout for Remove() * Update method tests * Update tests * Hide _CreateStateMask * Remove DS integration * Snap changes * Implement concurrency tests * Amend CreateStateMask * Update CreateStateMask calls * Change .yml files * Update formatting * Update tests * Move listener removal inside syncronised region * Add BundleTracker to workflow * Additional standardization * Drop workflows changes * Implement performance tests * Update tests with unclear expectations - Adds comments explaining the EXPECT_CALL statements expecting any number of RemovedBundle callbacks - Updates where the expected behavior of "Close() calling RemovedBundle on tracked bundles" is tested, also adding a comment to make this fact clear * Add newline at end of file * Review updates * Change API types * Avoid frivolous using statements in BundleTracker * Remove BundleTracker tpp file * Add forward declarations * Clear cross-platform build warnings * Bundle-Tracker OSGi TrackingCount behavior * Remove implementation tpp files * Adjust TrackingCount() based on 702 Co-authored-by: shane-riley (The Mathworks Inc.) Co-authored-by: xru192 Co-authored-by: xru192 <56376151+xru192@users.noreply.github.com> Co-authored-by: Alexander Christoforides <38366659+achristoforides@users.noreply.github.com> * Upgrade jsoncpp (#773) Fixes #772 Upgrade version of jsoncpp to 1.9.5 to include fix for deprecated sprintf usage. Upgrading introduced a new unneeded-internal-declaration warning which is set as a warning instead of error. Signed-off-by: The MathWorks, Inc. Signed-off-by: The MathWorks, Inc. Co-authored-by: Jeff DiClemente * Fixed sporadic race conditions during framework shutdown (#725) Fixed a number of races that can occur while one thread is performing various framework operations and another thread is shutting down the framework. Signed-off-by: The MathWorks, Inc. Signed-off-by: The MathWorks, Inc. Co-authored-by: Jeff * Fix race that results in a missed config updated event (#727) * Fix race that results in a missed config updated event Fixed races that can cause a ManagedService or ManagedServiceFactory to miss being called when a configuraiton object is updated. Signed-off-by: The MathWorks, Inc. * Update ComponentInstanceImpl.hpp (#724) * Update ComponentInstanceImpl.hpp Improve error message that is generated when an appropriate constructor isn't found for the Service Instance. Signed-off-by, The MathWorks, Inc. * Factory Configuration Bug Fix (#731) * Factory Configuration Bug Fix When factory configurations are created before the bundle containing the factory component is installed and started, DS should find those configurations in the Configuration Admin repository and register them as part of the factory component startup. Signed-off-by The MathWorks, Inc. * Factory Configuration Bug fix Addressed code review feedback. Signed-off-by The MathWorks, Inc. * Update ComponentConfigurationImpl.cpp Fixed compilation failure in Ubuntu Minimum Gcc build. Signed-off-by The MathWorks, Inc. * Update TestFactoryPid.cpp Change auto const to constexpr. Signed-off-by The MathWorks, Inc. * Revert PR #713 (#744) This change is causing an untenable amount of noise in log output. Backing out for now. A better solution to the problem of logging service dependency errors is needed. Signed-off-by: The MathWorks, Inc. * Update version numbers for cppms upgrade (#748) Update version numbers for cppms upgrade to version 3.7.4. Signed-off-by The MathWorks, Inc. * Fix deadlock in ConfigurationAdminImpl::RemoveConfigurations (#745) * Fix deadlock in ConfigurationAdminImpl::RemoveConfigurations This is a fix for a deadlock bug caused by the WaitForAllAsync in ConfigurationAdmin::RemoveConfigurations. The Use Case is as follows: A configuration object is defined in the manifest.json file. The User's main thread stopped the bundle which causes the ConfigurationAdminImp::RemoveConfigurations method to remove the configuration object from the ConfigurationAdmin repository and to send an Updated notification to the service instance. RemoveConfigurations would then execute a WaitForAllAsync to wait for all asynchronous threads to complete including the asynchronous thread that was launched as part of the Updated notification. The user's Updated method also tried to stop the bundle. This means that it had to wait for the RemoveConfigurations method to complete. Each thread was waiting for the other to complete. This fix removes the WaitForAllAsync and adds a test case to confirm the deadlock is gone. Signed-off-by The MathWorks, Inc. * Fix compiler error Fix compiler error in Minimum Gcc build. Signed-off-by The MathWorks, Inc. * Change ConfigAdminTests.testManagedServiceRemoveConfigurationsDeadlock Change ConfigAdminTests.testManagedServiceRemoveConfigurationsDeadlock so that it more closely matches the use case that caused the deadlock. Signed-off-by The MathWorks, Inc. * Update TestConfigAdmin.cpp testManagedServiceRemoveConfigurationsDeadlock test. Changed Updated method to make sure that it only stops the bundle containing the cm.testdeadlock configuration object. Signed-off-by The MathWorks, Inc. * Update TestConfigAdmin.cpp Added error checking for the Updated method used in testManagedServiceRemoveConfigurationsDeadlock. Signed-off-by The MathWorks, Inc. * configurations using the same pid are not updated properly (#754) * configurations using the same pid are not updated properly Fixed an issue whereby re-using a configuration pid did not cause the configuration to be sent to the ManagedService/ManagedServiceFactory correctly. * Remove last change count instead of setting it to zero Co-authored-by: jdicleme * Ensure ~SCRBundleExtension does not throw (#761) * Ensure ~SCRBundleExtension does not throw It's possible for the bundle to be stopped before ~SCRBundleExtension accesses the bundle, causing an exception to be thrown and an abort. This change ensures the destructor does not throw. * Fix valgrind errors Moved cleanup of SCRBundleExtension members to make sure these objects are destroyed when an exception is thrown from DisableAndRemoveAllComponentManagers. Signed-off-by: The MathWorks, Inc. * Fix valgrind error Missed another error path where cleanup is necessary Signed-off-by: The MathWorks, Inc. Signed-off-by: The MathWorks, Inc. Co-authored-by: jdicleme * clang-format ran for all files (#759) Signed-off-by: The MathWorks, Inc. Signed-off-by: The MathWorks, Inc. * Rev DS and ConfigAdmin versions for release (#762) prepare ds and configadmin for a bug fix release Signed-off-by: The MathWorks, Inc. Signed-off-by: The MathWorks, Inc. Co-authored-by: jdicleme * Clang-format git hook pre-commit enforcement (#760) * Added clang-format git pre-commit hook Signed-off-by: The MathWorks, Inc. * Updated README * Updated help instructions to include '--style=file' * Added '--style=file' flag in actual pre-commit hook check * Updated README * Removed unnecessary code in pre-commit hook * Updated README Signed-off-by: The MathWorks, Inc. * Update changelog [ci skip] (#765) * Update Changelog to include missing changes [ci skip] Signed-off-by: The MathWorks, Inc. * rebase onto development. * Fix broken static build configurations on macOS (#774) * Fix broken static build configurations on macOS Fixes #738 Removed the ability to append ZIP files to the end of archives files for all supported OS platforms. Specifically the clang linker no longer tolerates linking archive files with arbitrary data appended to the end. Signed-off-by: The MathWorks, Inc. * Fix examples tests the tutorial example has tests which use an installed CppMicroServices SDK to test whether the example code can be compiled using the SDK. In this case, the CppMicroServices target property doesn't have the path to the metadata zip set. To resolve this issue: 1. install the metadata zip files alongside the static libraries for all CppMicroServices components only if building static libraries. 2. Set this property for cases where the CppMicroServices SDK is used by downstream clients. Signed-off-by: The MathWorks, Inc. Signed-off-by: The MathWorks, Inc. Co-authored-by: Jeff * Support arm64 on macOS (#778) Fixes #674 Add support for arm64 on macOS Signed-off-by: The MathWorks, Inc. Signed-off-by: The MathWorks, Inc. * Performance improvement from `brian-performance` branch (#728) * Prototype performance improvements * Reverted AnyMap hash change Signed-off-by: The MathWorks, Inc. * Fixed unused variable warnings * Made changes requested by reviewers * Made changes requested by reviewer * Back out a performance improvement Backing out a performance improvement as it creates a deadlock caused by mutex order locking. Signed-off-by: The MathWorks, Inc. Signed-off-by: The MathWorks, Inc. Signed-off-by: The MathWorks, Inc. Co-authored-by: BrianWeed Co-authored-by: jdicleme Co-authored-by: Jeff * Fix ub in any (#777) * Fixed undefined behavior in Any::operator==(value) * Revert "Fixed undefined behavior in Any::operator==(value)" This reverts commit 3066fe1128da0aad1215e811be7ddf211b56be77. * Fixed undefined behavior in Any::operator==(value) * removed unnecessary forward declaration. * Fixed undefined behavior in Any::operator==(value) * removed unnecessary forward declaration. * Fix race with DS service object construction (#801) * Fix race with DS service object construction A race occurs when DS is constructing a service object with injectable mandatory service dependencies whereby any bound service dependencies could no longer be available because another thread unregistered any of the bound service dependencies. Given that a ComponentContext is constructed only after all the service dependencies has been satisfied, if during ComponetContext construction any of the bound service dependencies are no nullptrs then throw and fail to construct the service object. This fixes a failure mode where a service constructor can be called with a nullptr service object when the service dependency is defined as mandatory and injection is true. Also noticed that one of the test bundle project was exceeding the path limit on Windows, so shortened the project name to mitigate that problem. Signed-off-by: The MathWorks, Inc. * Add more tests Added more tests and fixed up some error messaging Signed-off-by: The MathWorks, Inc. --------- Signed-off-by: The MathWorks, Inc. * Make a small performance improvement (#808) stop copying the std::pair in the range-based for. This helps improve the speed of RegisterService Signed-off-by: The MathWorks, Inc. * Nested ldap queries (#794) Added the capability to do ldap queries with nested keys. Queries are first checked against the compound key and if a value is found it is returned. If a value is not found, the key is split into segments on the "." character and then the AnyMap is "walked down" to look for a path to the value in sub-maps addressed by the segments of the key. So, if the value of the key "a.b.c" is queried, 1. Split the key into ["a","b","c"] 2. Check the top level map for a sub-map at key "a". If one is found, look in that map for a key "b" with a sub-map, and in that sub-map for a value with key "c". If found, return it. 3. If the top level map does NOT contain a key "a", look for a sub-map at key "a.b". If one is found, look in that sub-map for a value with a key of "c". If one is found, return it. 4. continue this algorithm down as many levels as there are segments until either the value for the last segment is found (in which case it is returned), or one is not found, in which case the lookup fails. * clang-tidy improvement for CMakeResourceDependencies (#812) CMakeResourceDependencies is often built by CMake in the context of users of CppMicroServices - if you've configured clang-tidy to run in the user project, it complains about the "old-style" constructor and requests a modernized "= default". Use --std=c++17 as minimum compile flag for explicit compilation of this file for MacOS. Signed-off-by: Ingmar Sittl * [ci skip] Update CHANGELOG.rst (#817) * [ci skip] Update CHANGELOG.rst [ci skip] Fixed 3.7.4 release notes * Update CHANGELOG.rst * [ci skip] Update release information for version 3.7.5 (#818) [ci skip] update versions and changelog * Updated MSVC Analysis to 0.1.1 and checkout to v3 (#824) * Nested ldap queries (#811) * disable support for nested filtering set -DSUPPORT_NESTED_LOOKUP to re-enable the algorithm. * Added comment describing enabling nested ldap queries. --------- Co-authored-by: Alexander Christoforides <38366659+achristoforides@users.noreply.github.com> * Add benchmark test infrastructure to DS (#813) * Added benchmark suite for DS - Added benchmark test for GetService for service provided by DS Signed-off-by: The MathWorks, Inc. * Removed unnecessary lines from CMakeLists.txt for DS benchmark tests * Trying again * Refactored DS test directory to mimic 'framework' test structure * Fixed shadowed function * Actually fix the function shadowing this time? * Removed unnecessary bundles/link-time deps * Updated CHANGELOG.rst --------- Signed-off-by: The MathWorks, Inc. Co-authored-by: alchrist * Upgrade Build: Add Ubuntu 22.04, remove Ubuntu 18.04 (#810) Fixes #758 * Automate Performance Testing and Deploy Results (#829) * Create performance_windows.yml Adding new workflow for running performance tests Signed-off-by: The MathWorks, Inc. * Using personal token Signed-off-by: The MathWorks, Inc. * Revert "Using personal token" This reverts commit d672a801b6d11f9b8d72a54ae1de849179523a06. * Uploading results to fork Signed-off-by: The MathWorks, Inc. * removing https Signed-off-by: The MathWorks, Inc. * Changing ph branch Signed-off-by: The MathWorks, Inc. * Trying to deploy on another branch Signed-off-by: The MathWorks, Inc. * Commit results to gh-pages branch Signed-off-by: The MathWorks, Inc. * Added Performance Badge Signed-off-by: The MathWorks, Inc. * Added target for performance badge Signed-off-by: The MathWorks, Inc. * Update performance_windows.yml Signed-off-by: The MathWorks, Inc. * Removing hardcoded paths - Using ENV instead of hard coded build paths - Increased threshold to 20% Signed-off-by: The MathWorks, Inc. * Removed Cache operation Signed-off-by: The MathWorks, Inc. * Removing performance branch from workflow Signed-off-by: The MathWorks, Inc. * Name changed to CppMicroServices Benchmarks Signed-off-by: The MathWorks, Inc. --------- Signed-off-by: The MathWorks, Inc. Co-authored-by: Aaditya * [ci skip] Update CHANGELOG.rst (#832) [ci skip] bring change log up to date with latest commits on the development branch * Fix undefined behavior in LDAPExpr::Trim (#835) if there is no space in the string on the second str.erase, calling str.find_last_not_of() returns std::string::npos. Trying to add 1 to npos leads to undefined behavior reported by UBSAN. Signed-off-by: Ingmar Sittl * Brian decl services (#833) * Performance micro-optimizations Signed-off-by: The MathWorks, Inc. * Fix formatting * More performance improvements * fixed up compilation errors. * deal with unused variable. * restore API ServiceRegistrationBase::SetProperties(ServiceProperties const&) --------- Signed-off-by: The MathWorks, Inc. Co-authored-by: Michael Carney * Update CHANGELOG.rst [ci skip] (#837) [ci skip] added https://github.com/CppMicroServices/CppMicroServices/pull/833 * ComponentManagermemory reallocation bug fix (#834) * ComponentManagermemory reallocation bug fix The ComponentManagerImpl object contained a shared_ptr to the vector of all ComponentManagerImpl objects. When adding an item to this vector, the resizing of the vector caused a memory allocation error. Fixed by removing the vector from the ComponentManagerImpl object. * Fix for ConfigurationNotifier::CreateFactoryComponent memory allocation error When a ComponentManager is created, it must be added to the managers map in SCRBundleExtension. This is necessary so that when the bundle is stopped, the ComponentManager can be destroyed. The old code was holding a shared_ptr to the managers map in the ComponentManagerImpl object. This caused a memory reallocation error when a ComponentManager was added to the map and the map needed to be resized. This fix adds an AddComponentManager method to the SCRBundleExtension object so that CreateFactoryComponent can add a manager to the managers map when it is created. In order to find the SCRBundleExtension, the map of SCRBundleExtensions was moved out of SCRActivator and into a new class called SCRExtensionRegistry. * Rest of the ConfigurationNotifier fix Signed-off-by: The MathWorks, Inc. * Fix errors in minimum gcc build Signed-off by: The MathWorks Inc. < pelliott@mathworks.com> * Responded to code review comments Responded to code review comments. Added tests for SCRBundleExtension and SCRExtensionRegistry. Signed off by - The MathWorks, Inc. * Update ConfigurationNotifier.cpp Update error message. Signed off by The MathWorks, Inc. * Update SCRExtensionRegistry.hpp Formatting issue. signed-off by The MathWorks, Inc. * CreateFactoryComponent changes ConfigurationNotifier::CreateFactoryComponent will log an exception if it can't find the SCRBundleExtension in the ExtensionRegistry instead of throwing a std::runtime_error exception. Signed-off-by: The MathWorks, Inc. * Update ConfigurationNotifier.cpp Fixed call to std::exception. Signed-off-by The MathWorks, Inc. * Update ConfigurationNotifier.cpp Removed throw in CreateFactoryComponent. Error message is already logged, no throw needed. Signed-off-by - The MathWorks, Inc. --------- Signed-off-by: The MathWorks, Inc. * [ci skip] Update CHANGELOG.rst (#839) [ci skip] Added bug fix to fix sporadic crash in DS caused by concurrent access to ComponentMgrImpl vector. Pull Request 834. * [ci skip] rev version (#842) Co-authored-by: Jeff DiClemente * Default any map type (#844) * Fixed undefined behavior in Any::operator==(value) * Revert "Fixed undefined behavior in Any::operator==(value)" This reverts commit 3066fe1128da0aad1215e811be7ddf211b56be77. * Default AnyMap(map_type) constructor to UNORDERED_MAP_CASINSENSITIVE_KEYS * Remove CoreBundleContext.dataStorage clear (#845) Fixes #733 * Removing std::move use with const object [ci skip] (#848) * Removing std::move use with const [ci skip] Signed-off-by: Tahar Touati * Addressing PR feedback, removing const from complex types and adding std move back [ci skip] Signed-off-by: Tahar Touati --------- Signed-off-by: Tahar Touati * [ci skip] Update Changelog for #845 (#847) * Update Chagelog for #845 [ci skip] * Update Changelog Unreleased section [ci skip] Signed-off-by: ShivamNegi --------- Signed-off-by: ShivamNegi * Fix warning "Use of BITWISE AND to check if a flag is set" (#849) Signed-off-by: Tahar Touati * include cstdint in FileSystem.cpp (#850) vcpkg installation is failing, with this error: PATH/v3.6.0-e25b133cd3.clean/util/src/FileSystem.cpp:122:3: error: ‘uint32_t’ was not declared in this scope PATH/v3.6.0-e25b133cd3.clean/util/src/FileSystem.cpp:71:1: note: ‘uint32_t’ is defined in header ‘’; did you forget to ‘#include ’? Sure enough, when patched with this it will build and install correctly with vcpkg. Other errors are reported but this is the culprit. * Fixes #840: removes manual reference counting (#841) * converted registration * compiled, not passeD * fixed weak_ptr error, now leaking mock objects * fixed bug, need to decrement ref count * updats from last week, test cases failing because of out of date expectations * all tests are passing with shared and weak pointers to serviceRegistrationBasePrivate * still passing tests, updated comments and cleaned up * updating to share dependents * new issue with dying service * added coreInfo, maybe passing * passing tests, removed manual ref counting from referenceBasePrivate, repeated all fast tests to ensure no sporadic failures * updated comments * updates before PR * Removed manual reference counting The manual reference counting in ServiceRegistrationBasePrivate and ServiceReferenceBasePrivate were removed. Additionally, some properties of ServiceRegistrationBasePrivate were offloaded to a new class ServiceRegistrationCoreInfo which both ServiceRegistrationBasePrivate and ServiceReferenceBasePrivate can access allowing ServiceReferenceBasePrivate to give up ownership of ServiceRegistrationBasePrivate. Signed-off-by: Toby Cormack * Removed manual reference counting and merged with upstream (#840) The manual reference counting in ServiceRegistrationBasePrivate and ServiceReferenceBasePrivate were removed. Additionally, some properties of ServiceRegistrationBasePrivate were offloaded to a new class ServiceRegistrationCoreInfo which both ServiceRegistrationBasePrivate and ServiceReferenceBasePrivate can access allowing ServiceReferenceBasePrivate to give up ownership of ServiceRegistrationBasePrivate. Signed-off-by: Toby Cormack * ServiceRegistrationCoreInfo now default destructor * Updated based on Patty's comments #840 * updated ServiceReferenceBase Constructors for clarity with shared_ptrs * removed 'move' from serviceRegistry * attempt at solving mac issue * updates for lock type and removing unneccessary functions from reference * lost lock * changed to custom atomic load * LockSet addition * threading support in LockSet * LockSet not threaded * no names in func dec * Incoorporated Jeff's Comments * Assignment operator didn't fail on my computer, did in github * updated for Jeff's 5/16 comments * clang update * mikes comments and fixes for multithreaded support * remove ifdefs from BundleRegistry, abide by rule of (0,3,5), and add comments --------- Signed-off-by: Toby Cormack * Fixes #852: adds a [[nodiscard]] to BundleContext::RegisterService (#863) * noDiscard update first push * Jeff's comments, c++14 in cmakelists for makefile test, comments to reference nodiscard * [ci skip] Updated README to reflect correct compiler/OS versions (#862) [ci skip] * Fix code scanning alerts (#861) Fixes #860, #859, #858, #857, #856, #855, #853 * Ensure multiple listeners for the same factory PID are honoured by `ConfigurationNotifier::AnyListenersForPid` (#865) * Ensure multiple listeners for the same PID are honoured Signed-off-by: Conor Burgess * Fix formatting Signed-off-by: Conor Burgess --------- Signed-off-by: Conor Burgess Co-authored-by: cburgess * Code scanning alert fix: Use of a moved from object #864 (#866) Fixes #864 * tests passing on linux, all files updated * Mike and Toby backat it again * changed second instance of warning? * reverted naming, final submit * Fix race condition when concurrently adding to SCRExtensionRegistry (#870) * Fix race condition when concurrently adding to SCRExtensionRegistry Fixed a race that can happen when multiple threads are trying to add to the extension registry container. Fixed a bug with the tests for SCRExtensionRegsitry class never being compiled and run. Signed-off-by: The MathWorks, Inc. * Fix #868: Recoups some of performance losses from PR #841 (#869) * fixed lockReg shared_ptr * down to 5% increase from original * get logs from github to verify behavior * reverting performance yml and adding move constructor (default) for RegistrationLocks : * #Issue873: Redundant calls to .Load() (#874) * first commit * test Case * revert test * test fix * naming * Fixes #718 (#876) Signed-off-by: Toby Cormack * removed an empty line addition. * fixup some merge issues. * respond to code review feedback * code review feedback. * code review feedback. * Update TrackedService.h Remove underscore from names. * fixing merge * update _var naming convention * GetCustomizer_unlocked() addition * remove getTrackerAsCustomizer --------- Signed-off-by: The MathWorks, Inc. Signed-off-by: The MathWorks, Inc. Signed-off-by: Ingmar Sittl Signed-off-by: The MathWorks, Inc. Signed-off-by: Ingmar Sittl Signed-off-by: The MathWorks, Inc. Signed-off-by: The MathWorks, Inc. Signed-off-by: Tahar Touati Signed-off-by: ShivamNegi Signed-off-by: Toby Cormack Signed-off-by: Conor Burgess Signed-off-by: Toby Cormack Co-authored-by: Shane Riley <52603694+shane-riley@users.noreply.github.com> Co-authored-by: shane-riley (The Mathworks Inc.) Co-authored-by: xru192 Co-authored-by: xru192 <56376151+xru192@users.noreply.github.com> Co-authored-by: Jeff DiClemente Co-authored-by: Jeff DiClemente Co-authored-by: pelliott-mathworks <67922241+pelliott-mathworks@users.noreply.github.com> Co-authored-by: BrianWeed Co-authored-by: carneyweb Co-authored-by: Ingmar Sittl <79453136+insi-eb@users.noreply.github.com> Co-authored-by: alchrist Co-authored-by: Shivam Negi Co-authored-by: Aaditya Sakharam Patil <36245341+aadityap-mathworks@users.noreply.github.com> Co-authored-by: Aaditya Co-authored-by: thrtouati <124062546+thrtouati@users.noreply.github.com> Co-authored-by: Tristan Ayala Co-authored-by: tcormackMW <113473781+tcormackMW@users.noreply.github.com> Co-authored-by: Conor Burgess Co-authored-by: cburgess Co-authored-by: tcormack * CMake Updates for Boost Versioning Fix (#945) * cmake updates * behavior put in cmake function * usFunctionBoostPath update * Initializer list support (#942) * get latest on development * Added support for std::initializer_list constructurs Updated tests to use new initializer simplifying the code. * new clone. * fixed up some test code to make it compile on older compilers. * fixup. * updated some tests to check operator= * check. * fixed typo in comment. * added specific tests for initializer list support. * Revert "get latest on development" This reverts commit 639f61d44fa2ed989a1add2c8f8a2a774caf7a19. * test. * Revert "test." This reverts commit dd56818941337379f83a0aba6a101f6642f6e5ef. * Revert "Revert "get latest on development"" This reverts commit 7ef96570ec26a15668f9500aca26b063b9416412. * Revert "Revert "Revert "get latest on development""" This reverts commit 6f4d982d565df5959795719bd27308654bd721a7. * restore. * update google test to submodule version on development * update nowide version to match development * fixed up some comments. * ServiceComponent build tests cmake configuration (#949) * use cmake build type * set to debug by default * Fix #961 -- hashable references after destruction of RegistrationBase object (#962) * update hash function * test case working * update version --------- Signed-off-by: Tahar Touati Signed-off-by: ShivamNegi Signed-off-by: Toby Cormack Signed-off-by: Conor Burgess Signed-off-by: The MathWorks, Inc. Signed-off-by: Toby Cormack Signed-off-by: tcormack Signed-off-by: The MathWorks, Inc. aadityap@mathworks.com Signed-off-by: Ingmar Sittl Signed-off-by: Ingmar Sittl Signed-off-by: Shivam Negi Signed-off-by: The MathWorks, Inc. Signed-off-by: The MathWorks, Inc. Signed-off-by: The MathWorks, Inc. Signed-off-by: The MathWorks, Inc. Co-authored-by: carneyweb Co-authored-by: Shivam Negi Co-authored-by: thrtouati <124062546+thrtouati@users.noreply.github.com> Co-authored-by: Tristan Ayala Co-authored-by: Jeff DiClemente Co-authored-by: Conor Burgess Co-authored-by: cburgess Co-authored-by: Aaditya Sakharam Patil <36245341+aadityap-mathworks@users.noreply.github.com> Co-authored-by: aadityap Co-authored-by: Ingmar Sittl <79453136+insi-eb@users.noreply.github.com> Co-authored-by: Monika032 Co-authored-by: mphadnis Co-authored-by: Alexander Christoforides <38366659+achristoforides@users.noreply.github.com> Co-authored-by: Shane Riley <52603694+shane-riley@users.noreply.github.com> Co-authored-by: shane-riley (The Mathworks Inc.) Co-authored-by: xru192 Co-authored-by: xru192 <56376151+xru192@users.noreply.github.com> Co-authored-by: Jeff DiClemente Co-authored-by: pelliott-mathworks <67922241+pelliott-mathworks@users.noreply.github.com> Co-authored-by: BrianWeed Co-authored-by: alchrist --- .clang-format | 1 + .github/workflows/performance_windows.yml | 16 +- .gitignore | 2 +- CHANGELOG.rst | 36 +- CMakeLists.txt | 29 +- CppMicroServicesConfig.cmake.in | 1 + README.rst | 14 +- VERSION | 2 +- cmake/usFunctionBoostPath.cmake | 17 + .../cm/ConfigurationListener.hpp | 6 +- compendium/ConfigurationAdmin/CMakeLists.txt | 6 +- .../ConfigurationAdmin/src/CMActivator.cpp | 6 +- .../src/CMAsyncWorkService.cpp | 1 - .../ConfigurationAdmin/src/CMakeLists.txt | 4 +- .../ConfigurationAdmin/test/CMakeLists.txt | 4 +- compendium/DeclarativeServices/CMakeLists.txt | 6 +- .../DeclarativeServices/src/CMakeLists.txt | 4 +- .../DeclarativeServices/src/SCRActivator.cpp | 6 +- .../src/SCRAsyncWorkService.cpp | 1 - .../src/SCRExtensionRegistry.cpp | 10 +- .../src/SCRExtensionRegistry.hpp | 6 +- .../src/manager/BindingPolicy.cpp | 24 +- .../manager/BindingPolicyDynamicGreedy.cpp | 25 +- .../manager/BindingPolicyDynamicReluctant.cpp | 25 +- .../src/manager/BindingPolicyStaticGreedy.cpp | 22 +- .../manager/BindingPolicyStaticReluctant.cpp | 4 +- .../src/manager/BundleLoader.cpp | 24 +- .../manager/ComponentConfigurationImpl.cpp | 24 - .../manager/ComponentConfigurationImpl.hpp | 2 +- .../src/manager/ConfigurationNotifier.cpp | 23 +- .../src/manager/ReferenceManager.hpp | 10 + .../src/manager/ReferenceManagerImpl.cpp | 42 +- .../src/manager/ReferenceManagerImpl.hpp | 24 +- .../test/bench/CMakeLists.txt | 4 +- .../test/gtest/CMakeLists.txt | 5 +- .../DeclarativeServices/test/gtest/Mocks.hpp | 2 + .../test/gtest/TestBindingPolicies.cpp | 401 +- .../test/gtest/TestBundleValidation.cpp | 165 +- .../test/gtest/TestSCRExtensionRegistry.cpp | 140 +- .../gtest/TestServiceComponentRuntimeImpl.cpp | 3 +- .../detail/ComponentInstanceImpl.hpp | 54 +- .../ServiceComponent/test/CMakeLists.txt | 50 +- .../test/TestCompInst_InvalidInheritance.cpp | 6 +- .../test/TestCompInst_NoDepsNoDefaultCtor.cpp | 5 +- .../TestCompInst_NoInjectNoDefaultCtor.cpp | 7 +- .../test/TestCompInst_RefCount.cpp | 13 +- .../test/TestCompInst_RefOrder.cpp | 14 +- ...stCompInst_RefOrderMultipleCardinality.cpp | 138 + .../test/TestComponentInstance.cpp | 287 +- compendium/test_bundles/CMakeLists.txt | 12 + .../TestBundleDSTBV1/CMakeLists.txt | 7 + .../TestBundleDSTBV1/resources/manifest.json | 18 + .../src/ServiceComponents.hpp | 6 + .../TestBundleDSTBV1/src/ServiceImpl.cpp | 33 + .../TestBundleDSTBV1/src/ServiceImpl.hpp | 17 + .../TestBundleDSTBV1_1/CMakeLists.txt | 8 + .../resources/manifest.json | 21 + .../src/ServiceComponents.hpp | 6 + .../TestBundleDSTBV1_1/src/ServiceImpl.cpp | 48 + .../TestBundleDSTBV1_1/src/ServiceImpl.hpp | 28 + .../TestBundleDSTBV1_2/CMakeLists.txt | 8 + .../resources/manifest.json | 23 + .../src/ServiceComponents.hpp | 6 + .../TestBundleDSTBV1_2/src/ServiceImpl.cpp | 48 + .../TestBundleDSTBV1_2/src/ServiceImpl.hpp | 30 + .../TestBundleDSTBV1_3/CMakeLists.txt | 7 + .../resources/manifest.json | 13 + .../src/ServiceComponents.hpp | 6 + .../TestBundleDSTBV1_3/src/ServiceImpl.cpp | 33 + .../TestBundleDSTBV1_3/src/ServiceImpl.hpp | 17 + .../TestBundleDSTBV1_4/CMakeLists.txt | 7 + .../resources/manifest.json | 19 + .../src/ServiceComponents.hpp | 6 + .../TestBundleDSTBV1_4/src/ServiceImpl.cpp | 23 + .../TestBundleDSTBV1_4/src/ServiceImpl.hpp | 27 + .../TestBundleDSTBV1_5/CMakeLists.txt | 7 + .../resources/manifest.json | 17 + .../src/ServiceComponents.hpp | 6 + .../TestBundleDSTBV1_5/src/ServiceImpl.cpp | 14 + .../TestBundleDSTBV1_5/src/ServiceImpl.hpp | 27 + .../TestBundleDSTBV2/CMakeLists.txt | 7 + .../TestBundleDSTBV2/resources/manifest.json | 18 + .../src/ServiceComponents.hpp | 6 + .../TestBundleDSTBV2/src/ServiceImpl.cpp | 33 + .../TestBundleDSTBV2/src/ServiceImpl.hpp | 17 + .../TestBundleDSTBV3/CMakeLists.txt | 7 + .../TestBundleDSTBV3/resources/manifest.json | 18 + .../src/ServiceComponents.hpp | 6 + .../TestBundleDSTBV3/src/ServiceImpl.cpp | 33 + .../TestBundleDSTBV3/src/ServiceImpl.hpp | 17 + .../TestBundleDSTBV3_1/CMakeLists.txt | 7 + .../resources/manifest.json | 17 + .../src/ServiceComponents.hpp | 6 + .../TestBundleDSTBV3_1/src/ServiceImpl.cpp | 48 + .../TestBundleDSTBV3_1/src/ServiceImpl.hpp | 28 + .../TestBundleDSTBV4/CMakeLists.txt | 7 + .../TestBundleDSTBV4/resources/manifest.json | 26 + .../src/ServiceComponents.hpp | 6 + .../TestBundleDSTBV4/src/ServiceImpl.cpp | 41 + .../TestBundleDSTBV4/src/ServiceImpl.hpp | 25 + .../TestBundleDSTBV5/CMakeLists.txt | 7 + .../TestBundleDSTBV5/resources/manifest.json | 18 + .../src/ServiceComponents.hpp | 6 + .../TestBundleDSTBV5/src/ServiceImpl.cpp | 33 + .../TestBundleDSTBV5/src/ServiceImpl.hpp | 17 + .../SCRCodeGen/ComponentCallbackGenerator.hpp | 7 +- compendium/tools/SCRCodeGen/ComponentInfo.cpp | 10 +- compendium/tools/SCRCodeGen/ComponentInfo.hpp | 1 + .../SCRCodeGen/test/ReferenceAutogenFiles.hpp | 28 +- .../SCRCodeGen/test/TestCodeGenerator.cpp | 29 +- doc/src/CMakeLists.txt | 2 +- framework/CMakeLists.txt | 6 +- framework/include/CMakeLists.txt | 13 +- framework/include/cppmicroservices/AnyMap.h | 60 +- framework/include/cppmicroservices/Bundle.h | 7 + .../include/cppmicroservices/BundleContext.h | 44 +- .../include/cppmicroservices/BundleTracker.h | 472 ++ .../BundleTrackerCustomizer.h | 122 + .../cppmicroservices/ServiceReferenceBase.h | 10 +- .../ServiceRegistrationBase.h | 37 +- .../include/cppmicroservices/ServiceTracker.h | 9 +- .../detail/BundleAbstractTracked.h | 26 +- .../detail/BundleAbstractTracked.hpp | 332 + .../detail/BundleAbstractTracked.tpp | 323 - .../detail/BundleTrackerPrivate.h | 167 + .../detail/ServiceTracker.hpp | 553 ++ .../detail/ServiceTracker.tpp | 470 -- .../detail/ServiceTrackerPrivate.h | 2 +- .../detail/ServiceTrackerPrivate.hpp | 177 + .../detail/ServiceTrackerPrivate.tpp | 163 - .../include/cppmicroservices/detail/Threads.h | 3 +- .../cppmicroservices/detail/TrackedBundle.h | 198 + .../detail/TrackedBundleListener.h | 53 + .../cppmicroservices/detail/TrackedService.h | 18 +- .../detail/TrackedService.hpp | 171 + .../detail/TrackedService.tpp | 156 - framework/src/CMakeLists.txt | 4 + framework/src/bundle/BundleContext.cpp | 61 +- framework/src/bundle/BundleHooks.cpp | 4 +- framework/src/bundle/BundlePrivate.cpp | 9 +- framework/src/bundle/BundleResource.cpp | 10 +- .../src/bundle/BundleResourceContainer.cpp | 6 +- framework/src/bundle/CoreBundleContext.cpp | 1 - framework/src/service/ServiceHooks.cpp | 4 +- framework/src/service/ServiceListeners.cpp | 7 +- framework/src/service/ServiceObjects.cpp | 8 +- .../src/service/ServiceReferenceBase.cpp | 100 +- .../service/ServiceReferenceBasePrivate.cpp | 192 +- .../src/service/ServiceReferenceBasePrivate.h | 20 +- .../src/service/ServiceRegistrationBase.cpp | 188 +- .../ServiceRegistrationBasePrivate.cpp | 32 +- .../service/ServiceRegistrationBasePrivate.h | 63 +- .../service/ServiceRegistrationCoreInfo.cpp | 47 + .../src/service/ServiceRegistrationCoreInfo.h | 103 + framework/src/service/ServiceRegistry.cpp | 12 +- framework/src/util/AnyMap.cpp | 21 +- framework/src/util/FrameworkPrivate.cpp | 4 +- framework/src/util/LDAPExpr.cpp | 57 +- framework/src/util/LDAPFilter.cpp | 3 +- .../src/util/ServiceRegistrationLocks.cpp | 37 + framework/src/util/ServiceRegistrationLocks.h | 53 + framework/src/util/Utils.cpp | 12 +- framework/src/util/Utils.h | 114 +- framework/test/bench/BundleTrackerTest.cpp | 298 + framework/test/bench/CMakeLists.txt | 1 + framework/test/bench/ServiceRegistryTest.cpp | 15 +- framework/test/bundles/libC1/TestBundleC1.cpp | 23 +- framework/test/gtest/AnyMapTest.cpp | 114 +- framework/test/gtest/BundleContextTest.cpp | 143 +- .../gtest/BundleTrackerConcurrencyTest.cpp | 211 + .../gtest/BundleTrackerCustomCallbackTest.cpp | 684 ++ .../test/gtest/BundleTrackerMethodTest.cpp | 407 ++ framework/test/gtest/CMakeLists.txt | 3 + .../test/gtest/MultipleListenersTest.cpp | 7 +- framework/test/gtest/ServiceHooksTest.cpp | 267 +- framework/test/gtest/ServiceReferenceTest.cpp | 27 +- framework/test/gtest/ServiceTrackerTest.cpp | 96 + third_party/README | 26 +- third_party/absl/CMakeLists.txt | 26 +- third_party/absl/absl/base/attributes.h | 306 +- third_party/absl/absl/base/casts.h | 128 +- third_party/absl/absl/base/config.h | 602 +- .../absl/absl/base/internal/atomic_hook.h | 55 +- third_party/absl/absl/base/internal/endian.h | 282 + .../absl/absl/base/internal/errno_saver.h | 43 + .../absl/absl/base/internal/identity.h | 4 + .../absl/absl/base/internal/inline_variable.h | 107 + third_party/absl/absl/base/internal/invoke.h | 241 + .../absl/absl/base/internal/raw_logging.cc | 126 +- .../absl/absl/base/internal/raw_logging.h | 88 +- .../absl/absl/base/internal/throw_delegate.cc | 120 +- .../absl/absl/base/internal/throw_delegate.h | 4 + .../absl/base/internal/unaligned_access.h | 82 + third_party/absl/absl/base/log_severity.cc | 30 + third_party/absl/absl/base/log_severity.h | 113 +- third_party/absl/absl/base/macros.h | 146 +- third_party/absl/absl/base/optimization.h | 141 +- third_party/absl/absl/base/options.h | 232 + third_party/absl/absl/base/policy_checks.h | 40 +- third_party/absl/absl/base/port.h | 1 - third_party/absl/absl/memory/memory.h | 278 + third_party/absl/absl/meta/type_traits.h | 239 +- third_party/absl/absl/numeric/bits.h | 177 + third_party/absl/absl/numeric/int128.cc | 208 +- third_party/absl/absl/numeric/int128.h | 605 +- .../absl/numeric/int128_have_intrinsic.inc | 278 + .../absl/absl/numeric/int128_no_intrinsic.inc | 293 + third_party/absl/absl/numeric/internal/bits.h | 358 + third_party/absl/absl/strings/ascii.cc | 18 +- third_party/absl/absl/strings/ascii.h | 17 +- third_party/absl/absl/strings/charconv.cc | 697 +- third_party/absl/absl/strings/charconv.h | 9 +- third_party/absl/absl/strings/escaping.h | 169 + .../absl/strings/internal/charconv_bigint.cc | 36 +- .../absl/strings/internal/charconv_bigint.h | 12 +- .../absl/strings/internal/charconv_parse.cc | 106 +- .../absl/strings/internal/charconv_parse.h | 3 + .../strings/internal/has_absl_stringify.h | 55 + .../absl/absl/strings/internal/memutil.cc | 17 +- .../absl/absl/strings/internal/memutil.h | 2 + .../absl/strings/internal/ostringstream.h | 114 + .../strings/internal/resize_uninitialized.h | 55 +- .../absl/strings/internal/str_join_internal.h | 317 + .../absl/strings/internal/stringify_sink.h | 57 + third_party/absl/absl/strings/match.cc | 11 +- third_party/absl/absl/strings/match.h | 26 +- third_party/absl/absl/strings/numbers.cc | 405 +- third_party/absl/absl/strings/numbers.h | 172 +- third_party/absl/absl/strings/str_cat.cc | 66 +- third_party/absl/absl/strings/str_cat.h | 93 +- third_party/absl/absl/strings/str_join.h | 287 + third_party/absl/absl/strings/string_view.cc | 76 +- third_party/absl/absl/strings/string_view.h | 316 +- .../absl/absl/types/bad_optional_access.cc | 48 + .../absl/absl/types/bad_optional_access.h | 78 + .../absl/absl/types/internal/optional.h | 404 ++ third_party/absl/absl/types/optional.h | 779 +++ third_party/absl/absl/utility/utility.h | 350 + third_party/boost/nowide | 2 +- third_party/googletest | 2 +- third_party/jsoncpp.cpp | 4 +- third_party/miniz.c | 22 +- third_party/miniz.h | 6 +- third_party/patches/jsconcpp_1.9.5.patch | 22 + third_party/patches/jsoncpp_0.10.6.patch | 13 - ...{miniz_76b3a87.patch => miniz_3.0.2.patch} | 28 +- third_party/patches/miniz_v115_r4.patch | 160 - third_party/patches/miniz_v115_r5.patch | 49 - third_party/patches/miniz_v115_r6.patch | 106 - third_party/patches/spdlogv1.x.patch | 26 - third_party/spdlog/include/spdlog/async.h | 22 +- .../spdlog/include/spdlog/async_logger-inl.h | 40 +- .../spdlog/include/spdlog/async_logger.h | 2 +- third_party/spdlog/include/spdlog/cfg/argv.h | 7 +- third_party/spdlog/include/spdlog/cfg/env.h | 10 +- .../spdlog/include/spdlog/cfg/helpers-inl.h | 31 +- .../spdlog/include/spdlog/cfg/helpers.h | 7 +- .../spdlog/include/spdlog/common-inl.h | 32 +- third_party/spdlog/include/spdlog/common.h | 308 +- .../include/spdlog/details/backtracer-inl.h | 8 +- .../include/spdlog/details/backtracer.h | 5 +- .../include/spdlog/details/circular_q.h | 5 + .../include/spdlog/details/file_helper-inl.h | 56 +- .../include/spdlog/details/file_helper.h | 9 +- .../include/spdlog/details/fmt_helper.h | 70 +- .../include/spdlog/details/log_msg-inl.h | 2 +- .../spdlog/include/spdlog/details/log_msg.h | 4 +- .../spdlog/details/log_msg_buffer-inl.h | 2 +- .../include/spdlog/details/log_msg_buffer.h | 2 +- .../include/spdlog/details/mpmc_blocking_q.h | 38 +- .../include/spdlog/details/null_mutex.h | 4 - .../spdlog/include/spdlog/details/os-inl.h | 267 +- .../spdlog/include/spdlog/details/os.h | 37 +- .../spdlog/details/periodic_worker-inl.h | 23 +- .../include/spdlog/details/periodic_worker.h | 24 +- .../include/spdlog/details/registry-inl.h | 68 +- .../spdlog/include/spdlog/details/registry.h | 27 +- .../spdlog/details/synchronous_factory.h | 4 +- .../spdlog/details/tcp_client-windows.h | 47 +- .../include/spdlog/details/tcp_client.h | 29 +- .../include/spdlog/details/thread_pool-inl.h | 33 +- .../include/spdlog/details/thread_pool.h | 8 +- .../spdlog/details/udp_client-windows.h | 113 + .../include/spdlog/details/udp_client.h | 94 + .../include/spdlog/details/windows_include.h | 4 +- .../spdlog/include/spdlog/fmt/bin_to_hex.h | 72 +- .../spdlog/include/spdlog/fmt/bundled/args.h | 234 + .../include/spdlog/fmt/bundled/chrono.h | 1514 ++++- .../spdlog/include/spdlog/fmt/bundled/color.h | 347 +- .../include/spdlog/fmt/bundled/compile.h | 822 +-- .../spdlog/include/spdlog/fmt/bundled/core.h | 3459 +++++++--- .../spdlog/fmt/bundled/fmt.license.rst | 27 + .../include/spdlog/fmt/bundled/format-inl.h | 2686 ++++---- .../include/spdlog/fmt/bundled/format.h | 6045 +++++++++-------- .../include/spdlog/fmt/bundled/locale.h | 80 +- .../spdlog/include/spdlog/fmt/bundled/os.h | 478 ++ .../include/spdlog/fmt/bundled/ostream.h | 247 +- .../include/spdlog/fmt/bundled/printf.h | 538 +- .../include/spdlog/fmt/bundled/ranges.h | 687 +- .../spdlog/include/spdlog/fmt/bundled/std.h | 171 + .../spdlog/include/spdlog/fmt/bundled/xchar.h | 229 + .../spdlog/include/spdlog/fmt/chrono.h | 22 + .../spdlog/include/spdlog/fmt/compile.h | 22 + third_party/spdlog/include/spdlog/fmt/fmt.h | 32 +- third_party/spdlog/include/spdlog/fmt/ostr.h | 20 +- .../spdlog/include/spdlog/fmt/ranges.h | 22 + third_party/spdlog/include/spdlog/fmt/std.h | 23 + third_party/spdlog/include/spdlog/fmt/xchar.h | 22 + third_party/spdlog/include/spdlog/fwd.h | 4 + .../spdlog/include/spdlog/logger-inl.h | 14 +- third_party/spdlog/include/spdlog/logger.h | 291 +- .../include/spdlog/pattern_formatter-inl.h | 116 +- .../spdlog/include/spdlog/pattern_formatter.h | 14 +- .../include/spdlog/sinks/android_sink.h | 69 +- .../include/spdlog/sinks/ansicolor_sink-inl.h | 22 +- .../include/spdlog/sinks/ansicolor_sink.h | 2 +- .../include/spdlog/sinks/base_sink-inl.h | 2 +- .../spdlog/include/spdlog/sinks/base_sink.h | 4 +- .../spdlog/sinks/basic_file_sink-inl.h | 5 +- .../include/spdlog/sinks/basic_file_sink.h | 16 +- .../include/spdlog/sinks/callback_sink.h | 61 + .../include/spdlog/sinks/daily_file_sink.h | 63 +- .../spdlog/include/spdlog/sinks/dist_sink.h | 22 +- .../include/spdlog/sinks/dup_filter_sink.h | 18 +- .../include/spdlog/sinks/hourly_file_sink.h | 204 + .../spdlog/include/spdlog/sinks/kafka_sink.h | 133 + .../spdlog/include/spdlog/sinks/mongo_sink.h | 107 + .../spdlog/include/spdlog/sinks/msvc_sink.h | 42 +- .../spdlog/include/spdlog/sinks/qt_sinks.h | 292 + .../include/spdlog/sinks/ringbuffer_sink.h | 2 +- .../spdlog/sinks/rotating_file_sink-inl.h | 35 +- .../include/spdlog/sinks/rotating_file_sink.h | 19 +- .../spdlog/include/spdlog/sinks/sink-inl.h | 2 +- .../spdlog/include/spdlog/sinks/sink.h | 2 +- .../spdlog/sinks/stdout_color_sinks-inl.h | 4 +- .../include/spdlog/sinks/stdout_color_sinks.h | 6 +- .../include/spdlog/sinks/stdout_sinks-inl.h | 52 +- .../include/spdlog/sinks/stdout_sinks.h | 9 +- .../include/spdlog/sinks/systemd_sink.h | 53 +- .../spdlog/include/spdlog/sinks/tcp_sink.h | 4 +- .../spdlog/include/spdlog/sinks/udp_sink.h | 74 + .../include/spdlog/sinks/win_eventlog_sink.h | 57 +- .../include/spdlog/sinks/wincolor_sink-inl.h | 129 +- .../include/spdlog/sinks/wincolor_sink.h | 27 +- .../spdlog/include/spdlog/spdlog-inl.h | 24 +- third_party/spdlog/include/spdlog/spdlog.h | 180 +- third_party/spdlog/include/spdlog/stopwatch.h | 69 + third_party/spdlog/include/spdlog/tweakme.h | 32 +- third_party/spdlog/include/spdlog/version.h | 2 +- tsan_suppressions.txt | 38 +- util/src/FileSystem.cpp | 1 + 351 files changed, 31144 insertions(+), 10893 deletions(-) create mode 100644 cmake/usFunctionBoostPath.cmake create mode 100644 compendium/ServiceComponent/test/TestCompInst_RefOrderMultipleCardinality.cpp create mode 100644 compendium/test_bundles/TestBundleDSTBV1/CMakeLists.txt create mode 100644 compendium/test_bundles/TestBundleDSTBV1/resources/manifest.json create mode 100644 compendium/test_bundles/TestBundleDSTBV1/src/ServiceComponents.hpp create mode 100644 compendium/test_bundles/TestBundleDSTBV1/src/ServiceImpl.cpp create mode 100644 compendium/test_bundles/TestBundleDSTBV1/src/ServiceImpl.hpp create mode 100644 compendium/test_bundles/TestBundleDSTBV1_1/CMakeLists.txt create mode 100644 compendium/test_bundles/TestBundleDSTBV1_1/resources/manifest.json create mode 100644 compendium/test_bundles/TestBundleDSTBV1_1/src/ServiceComponents.hpp create mode 100644 compendium/test_bundles/TestBundleDSTBV1_1/src/ServiceImpl.cpp create mode 100644 compendium/test_bundles/TestBundleDSTBV1_1/src/ServiceImpl.hpp create mode 100644 compendium/test_bundles/TestBundleDSTBV1_2/CMakeLists.txt create mode 100644 compendium/test_bundles/TestBundleDSTBV1_2/resources/manifest.json create mode 100644 compendium/test_bundles/TestBundleDSTBV1_2/src/ServiceComponents.hpp create mode 100644 compendium/test_bundles/TestBundleDSTBV1_2/src/ServiceImpl.cpp create mode 100644 compendium/test_bundles/TestBundleDSTBV1_2/src/ServiceImpl.hpp create mode 100644 compendium/test_bundles/TestBundleDSTBV1_3/CMakeLists.txt create mode 100644 compendium/test_bundles/TestBundleDSTBV1_3/resources/manifest.json create mode 100644 compendium/test_bundles/TestBundleDSTBV1_3/src/ServiceComponents.hpp create mode 100644 compendium/test_bundles/TestBundleDSTBV1_3/src/ServiceImpl.cpp create mode 100644 compendium/test_bundles/TestBundleDSTBV1_3/src/ServiceImpl.hpp create mode 100644 compendium/test_bundles/TestBundleDSTBV1_4/CMakeLists.txt create mode 100644 compendium/test_bundles/TestBundleDSTBV1_4/resources/manifest.json create mode 100644 compendium/test_bundles/TestBundleDSTBV1_4/src/ServiceComponents.hpp create mode 100644 compendium/test_bundles/TestBundleDSTBV1_4/src/ServiceImpl.cpp create mode 100644 compendium/test_bundles/TestBundleDSTBV1_4/src/ServiceImpl.hpp create mode 100644 compendium/test_bundles/TestBundleDSTBV1_5/CMakeLists.txt create mode 100644 compendium/test_bundles/TestBundleDSTBV1_5/resources/manifest.json create mode 100644 compendium/test_bundles/TestBundleDSTBV1_5/src/ServiceComponents.hpp create mode 100644 compendium/test_bundles/TestBundleDSTBV1_5/src/ServiceImpl.cpp create mode 100644 compendium/test_bundles/TestBundleDSTBV1_5/src/ServiceImpl.hpp create mode 100644 compendium/test_bundles/TestBundleDSTBV2/CMakeLists.txt create mode 100644 compendium/test_bundles/TestBundleDSTBV2/resources/manifest.json create mode 100644 compendium/test_bundles/TestBundleDSTBV2/src/ServiceComponents.hpp create mode 100644 compendium/test_bundles/TestBundleDSTBV2/src/ServiceImpl.cpp create mode 100644 compendium/test_bundles/TestBundleDSTBV2/src/ServiceImpl.hpp create mode 100644 compendium/test_bundles/TestBundleDSTBV3/CMakeLists.txt create mode 100644 compendium/test_bundles/TestBundleDSTBV3/resources/manifest.json create mode 100644 compendium/test_bundles/TestBundleDSTBV3/src/ServiceComponents.hpp create mode 100644 compendium/test_bundles/TestBundleDSTBV3/src/ServiceImpl.cpp create mode 100644 compendium/test_bundles/TestBundleDSTBV3/src/ServiceImpl.hpp create mode 100644 compendium/test_bundles/TestBundleDSTBV3_1/CMakeLists.txt create mode 100644 compendium/test_bundles/TestBundleDSTBV3_1/resources/manifest.json create mode 100644 compendium/test_bundles/TestBundleDSTBV3_1/src/ServiceComponents.hpp create mode 100644 compendium/test_bundles/TestBundleDSTBV3_1/src/ServiceImpl.cpp create mode 100644 compendium/test_bundles/TestBundleDSTBV3_1/src/ServiceImpl.hpp create mode 100644 compendium/test_bundles/TestBundleDSTBV4/CMakeLists.txt create mode 100644 compendium/test_bundles/TestBundleDSTBV4/resources/manifest.json create mode 100644 compendium/test_bundles/TestBundleDSTBV4/src/ServiceComponents.hpp create mode 100644 compendium/test_bundles/TestBundleDSTBV4/src/ServiceImpl.cpp create mode 100644 compendium/test_bundles/TestBundleDSTBV4/src/ServiceImpl.hpp create mode 100644 compendium/test_bundles/TestBundleDSTBV5/CMakeLists.txt create mode 100644 compendium/test_bundles/TestBundleDSTBV5/resources/manifest.json create mode 100644 compendium/test_bundles/TestBundleDSTBV5/src/ServiceComponents.hpp create mode 100644 compendium/test_bundles/TestBundleDSTBV5/src/ServiceImpl.cpp create mode 100644 compendium/test_bundles/TestBundleDSTBV5/src/ServiceImpl.hpp create mode 100644 framework/include/cppmicroservices/BundleTracker.h create mode 100644 framework/include/cppmicroservices/BundleTrackerCustomizer.h create mode 100644 framework/include/cppmicroservices/detail/BundleAbstractTracked.hpp delete mode 100644 framework/include/cppmicroservices/detail/BundleAbstractTracked.tpp create mode 100644 framework/include/cppmicroservices/detail/BundleTrackerPrivate.h create mode 100755 framework/include/cppmicroservices/detail/ServiceTracker.hpp delete mode 100755 framework/include/cppmicroservices/detail/ServiceTracker.tpp create mode 100644 framework/include/cppmicroservices/detail/ServiceTrackerPrivate.hpp delete mode 100644 framework/include/cppmicroservices/detail/ServiceTrackerPrivate.tpp create mode 100644 framework/include/cppmicroservices/detail/TrackedBundle.h create mode 100644 framework/include/cppmicroservices/detail/TrackedBundleListener.h create mode 100644 framework/include/cppmicroservices/detail/TrackedService.hpp delete mode 100644 framework/include/cppmicroservices/detail/TrackedService.tpp create mode 100644 framework/src/service/ServiceRegistrationCoreInfo.cpp create mode 100644 framework/src/service/ServiceRegistrationCoreInfo.h create mode 100644 framework/src/util/ServiceRegistrationLocks.cpp create mode 100644 framework/src/util/ServiceRegistrationLocks.h create mode 100644 framework/test/bench/BundleTrackerTest.cpp create mode 100644 framework/test/gtest/BundleTrackerConcurrencyTest.cpp create mode 100644 framework/test/gtest/BundleTrackerCustomCallbackTest.cpp create mode 100644 framework/test/gtest/BundleTrackerMethodTest.cpp create mode 100644 third_party/absl/absl/base/internal/endian.h create mode 100644 third_party/absl/absl/base/internal/errno_saver.h create mode 100644 third_party/absl/absl/base/internal/inline_variable.h create mode 100644 third_party/absl/absl/base/internal/invoke.h create mode 100644 third_party/absl/absl/base/internal/unaligned_access.h create mode 100644 third_party/absl/absl/base/options.h create mode 100644 third_party/absl/absl/memory/memory.h create mode 100644 third_party/absl/absl/numeric/bits.h create mode 100644 third_party/absl/absl/numeric/internal/bits.h create mode 100644 third_party/absl/absl/strings/escaping.h create mode 100644 third_party/absl/absl/strings/internal/has_absl_stringify.h create mode 100644 third_party/absl/absl/strings/internal/ostringstream.h create mode 100644 third_party/absl/absl/strings/internal/str_join_internal.h create mode 100644 third_party/absl/absl/strings/internal/stringify_sink.h create mode 100644 third_party/absl/absl/strings/str_join.h create mode 100644 third_party/absl/absl/types/bad_optional_access.cc create mode 100644 third_party/absl/absl/types/bad_optional_access.h create mode 100644 third_party/absl/absl/types/internal/optional.h create mode 100644 third_party/absl/absl/types/optional.h create mode 100644 third_party/absl/absl/utility/utility.h create mode 100644 third_party/patches/jsconcpp_1.9.5.patch delete mode 100644 third_party/patches/jsoncpp_0.10.6.patch rename third_party/patches/{miniz_76b3a87.patch => miniz_3.0.2.patch} (90%) delete mode 100644 third_party/patches/miniz_v115_r4.patch delete mode 100644 third_party/patches/miniz_v115_r5.patch delete mode 100644 third_party/patches/miniz_v115_r6.patch delete mode 100644 third_party/patches/spdlogv1.x.patch create mode 100644 third_party/spdlog/include/spdlog/details/udp_client-windows.h create mode 100644 third_party/spdlog/include/spdlog/details/udp_client.h create mode 100644 third_party/spdlog/include/spdlog/fmt/bundled/args.h create mode 100644 third_party/spdlog/include/spdlog/fmt/bundled/fmt.license.rst create mode 100644 third_party/spdlog/include/spdlog/fmt/bundled/os.h create mode 100644 third_party/spdlog/include/spdlog/fmt/bundled/std.h create mode 100644 third_party/spdlog/include/spdlog/fmt/bundled/xchar.h create mode 100644 third_party/spdlog/include/spdlog/fmt/chrono.h create mode 100644 third_party/spdlog/include/spdlog/fmt/compile.h create mode 100644 third_party/spdlog/include/spdlog/fmt/ranges.h create mode 100644 third_party/spdlog/include/spdlog/fmt/std.h create mode 100644 third_party/spdlog/include/spdlog/fmt/xchar.h create mode 100644 third_party/spdlog/include/spdlog/sinks/callback_sink.h create mode 100644 third_party/spdlog/include/spdlog/sinks/hourly_file_sink.h create mode 100644 third_party/spdlog/include/spdlog/sinks/kafka_sink.h create mode 100644 third_party/spdlog/include/spdlog/sinks/mongo_sink.h create mode 100644 third_party/spdlog/include/spdlog/sinks/qt_sinks.h create mode 100644 third_party/spdlog/include/spdlog/sinks/udp_sink.h create mode 100644 third_party/spdlog/include/spdlog/stopwatch.h diff --git a/.clang-format b/.clang-format index 0d2ccd608..a4139324a 100644 --- a/.clang-format +++ b/.clang-format @@ -33,6 +33,7 @@ Cpp11BracedListStyle: false IndentCaseLabels: true IndentGotoLabels: false IndentPPDirectives: AfterHash +InsertBraces: true NamespaceIndentation: All PackConstructorInitializers: CurrentLine PointerAlignment: Left diff --git a/.github/workflows/performance_windows.yml b/.github/workflows/performance_windows.yml index 679269279..496e5f72b 100644 --- a/.github/workflows/performance_windows.yml +++ b/.github/workflows/performance_windows.yml @@ -2,7 +2,10 @@ name: PerformanceWindows on: push: - branches: [ development, c\+\+14-compliant, master ] + #This workflow runs only on development branch + #If you want to run performance tests on a particular branch + #Please add your branch name in branches, e.g. branches: [ development, your_branch_name ] + branches: [ development ] workflow_dispatch: permissions: @@ -54,7 +57,15 @@ jobs: - name: Run benchmark and store result in json format run: ${{ env.BUILD_DIR }}\bin\Release\usFrameworkBenchTests.exe --benchmark_format=json | tee ${{ env.BUILD_DIR }}\bin\Release\benchmark_result.json + - name: Upload Performance Results as Artifact + uses: actions/upload-artifact@v3 + with: + name: Performance Results - ${{ github.event.head_commit.author.name }} - ${{ github.sha }} + path: ${{ env.BUILD_DIR }}\bin\Release\benchmark_result.json + - name: Deploy results + # Condition the step on the 'development' branch only + if: github.ref == 'refs/heads/development' uses: benchmark-action/github-action-benchmark@v1.16.1 with: name: C++ Benchmark @@ -66,5 +77,4 @@ jobs: alert-threshold: '20%' comment-on-alert: true fail-on-alert: false - - \ No newline at end of file + diff --git a/.gitignore b/.gitignore index a52458161..e487a46a8 100644 --- a/.gitignore +++ b/.gitignore @@ -40,4 +40,4 @@ doc/src/examples/makefile/main *.pyc /build* .vscode/ -.venv/ +.venv/ \ No newline at end of file diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 9fd089a34..58238905f 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -6,25 +6,57 @@ All notable changes to this project will be documented in this file. The format is based on `Keep a Changelog `_ and this project adheres to `Semantic Versioning `_. -`(UNRELEASED) vX.X.X `_ (20XX-XX-XX) +`v3.8.0 `_ (2023-12-06) --------------------------------------------------------------------------------------------------------- -`Full Changelog `_ +`Full Changelog `_ Added ----- +- `[Core Framework] Guarentee ordering by rank from GetServiceReferences `_ +- `[Core Framework] Add BundleTracker `_ +- `[Core Framework] Initializer list support for AnyMap `_ Changed ------- +- `[Core Framework] Remove manual reference counting for serviceRegistrations `_ +- `[Core Framework] Ensure ServiceRegistrationU objects are not discarded from call to RegisterService `_ +- `[Core Framework] Update README to reflect correct compiler/OS versions `_ +- `[Declarative Services] Ensure multiple listeners for same factory PID are honored `_ +- `Update github workflows `_ +- `Use custom boost namespace to avoid symbol collision `_ +- `Update 3rd party dependency versions `_ +- `[Core Framework] Guarentee hash of serviceReference is conserved after destruction of serviceRegistrationBase object `_ Removed ------- +- `Update to allow custom boost namespace and remove absl dependency `_ Deprecated ---------- Fixed ----- +- `[Core Framework] Data Race Condition fix for Bundles dataStorage location `_ +- `[Core Framework] Remove problematic std::move calls. `_ +- `[Core Framework] Flag Checking `_ +- `[Core Framework] Include cstdint in FileSystem.cpp `_ +- `[Core Framework] Fix code scanning alerts `_ +- `[Config Admin, Declarative Services] Fix code scanning alerts `_ +- `[Declarative Services] Fix race condition when addint SCRExtensionRegistry `_ +- `[Core Framework] Recoup performance losses `_ +- `[Core Framework] Recoup performance losses `_ +- `[Core Framework] BundleContextTest.NoSegfaultWithServiceFactory sporadic failure fix `_ +- `[Core Framework] Allow char const* properties in LDAPFilters `_ +- `[Core Framework] Reformat hpp FileSystem `_ +- `[Core Framework] Disable incorrect TSAN warnings `_ +- `[Core Framework] Fix potential deadlock in ServiceTracker `_ +- `[Core Framework] Update tests to remove unnecessary globals `_ +- `[Core Framework] Fix serviceTracker deadlock on close() `_ +- `[Core Framework] Update github workflows `_ +- `[Core Framework] Remove unused variable and add missing include `_ +- `[Declarative Services] Fix redundant bundle validation checks `_ +- `[Core Framework] Fix serviceTracker deadlock `_ `v3.7.6 `_ (2023-04-25) --------------------------------------------------------------------------------------------------------- diff --git a/CMakeLists.txt b/CMakeLists.txt index 49d0cd952..983c9cd32 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -101,7 +101,7 @@ set(US_COMPILER_AppleClang_MINIMUM_VERSION_PRODUCT "Xcode 7.3 (Clang ${US_COMPIL set(US_COMPILER_MSVC_MINIMUM_VERSION_PRODUCT "Visual Studio 2015 (MSVC ${US_COMPILER_MSVC_MINIMUM_VERSION})") set(US_COMPILER_GNU_MINIMUM_RECOMMENDED_VERSION 9.4.0 ) -set(US_COMPILER_Clang_MINIMUM_RECOMMENDED_VERSION 9.0 ) +set(US_COMPILER_Clang_MINIMUM_RECOMMENDED_VERSION 9.0.0 ) set(US_COMPILER_AppleClang_MINIMUM_RECOMMENDED_VERSION 12.0.1 ) set(US_COMPILER_MSVC_MINIMUM_RECOMMENDED_VERSION 19.20 ) @@ -171,6 +171,7 @@ include(usFunctionGenerateBundleInit) include(usMacroCreateBundle) include(usFunctionCreateTestBundle) include(usFunctionCreateDSTestBundle) +include(usFunctionBoostPath) if (US_COMPILER_CLANG OR US_COMPILER_APPLE_CLANG) check_cxx_compiler_flag(-Wno-missing-braces HAS_MISSING_BRACES_FLAG) @@ -201,13 +202,13 @@ endforeach() # Set a default build type if none was specified #----------------------------------------------------------------------------- -if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) +if(NOT CMAKE_BUILD_TYPE) message(STATUS "Setting build type to 'Debug' as none was specified.") set(CMAKE_BUILD_TYPE Debug CACHE STRING "Choose the type of build." FORCE) # Set the possible values of build type for cmake-gui set_property(CACHE CMAKE_BUILD_TYPE PROPERTY - STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo") + STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo") endif() #----------------------------------------------------------------------------- @@ -252,6 +253,7 @@ us_cache_var(US_ENABLE_COVERAGE OFF BOOL "Enable code coverage" ADVANCED) us_cache_var(US_BUILD_TESTING OFF BOOL "Build tests") us_cache_var(US_BUILD_EXAMPLES OFF BOOL "Build example projects") us_cache_var(US_USE_SYSTEM_GTEST OFF BOOL "Build using an external GTest installation" ADVANCED) +us_cache_var(US_USE_SYSTEM_BOOST OFF BOOL "Build using an external Boost installation" ADVANCED) set(_us_build_shared ${BUILD_SHARED_LIBS}) @@ -291,7 +293,7 @@ endif() # with clang and lld if ((NOT(US_COMPILER_CLANG) OR NOT(US_USING_LLD)) AND (US_ENABLE_MSAN OR US_ENABLE_UBSAN)) message(FATAL_ERROR "You are trying to use msan or ubsan either without clang as your compiler or \ - lld as your linker. Please use clang and lld if you wish to use msan or ubsan") + lld as your linker. Please use clang and lld if you wish to use msan or ubsan") endif() # Display the selected sanitizer if one was enabled @@ -361,6 +363,14 @@ if (APPLE AND US_COMPILER_APPLE_CLANG) set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -stdlib=libc++ -lc++abi") endif() +#----------------------------------------------------------------------------- +# 3rd Party Library configuration +#----------------------------------------------------------------------------- + +if(US_USE_SYSTEM_BOOST) + find_package(Boost 1.74.0 REQUIRED) +endif() + #----------------------------------------------------------------------------- # Testing configuration #----------------------------------------------------------------------------- @@ -378,7 +388,7 @@ function(us_add_tests test_driver) # By default, these tests are not active until the concurrent bundle # start / stop mechanism has been properly implemented (GitHub Issue #25) add_test(NAME helgrind_${test} COMMAND ${US_MEMCHECK_COMMAND} --tool=helgrind --error-exitcode=1 ${US_RUNTIME_OUTPUT_DIRECTORY}/${test_driver} ${test} - WORKING_DIRECTORY ${CppMicroServices_BINARY_DIR}) + WORKING_DIRECTORY ${CppMicroServices_BINARY_DIR}) set_property(TEST helgrind_${test} PROPERTY LABELS valgrind helgrind) endif() endif() @@ -387,8 +397,8 @@ function(us_add_tests test_driver) file(TO_NATIVE_PATH ${US_RUNTIME_OUTPUT_DIRECTORY} US_RUNTIME_OUTPUT_NATIVE_DIRECTORY) # skip the unicode test bundle because opencppcoverage tool fails if a module is located in a path with unicode characters add_test(NAME coverage_${test} - COMMAND ${US_COVERAGE_COMMAND} --verbose --cover_children --sources=${PROJECT_SOURCE_NATIVE_PATH} --export_type=binary:${test}.cov --continue_after_cpp_exception --excluded_modules *TestBundleU*.dll -- $ ${test} - WORKING_DIRECTORY ${CppMicroServices_BINARY_DIR}) + COMMAND ${US_COVERAGE_COMMAND} --verbose --cover_children --sources=${PROJECT_SOURCE_NATIVE_PATH} --export_type=binary:${test}.cov --continue_after_cpp_exception --excluded_modules *TestBundleU*.dll -- $ ${test} + WORKING_DIRECTORY ${CppMicroServices_BINARY_DIR}) set_property(TEST coverage_${test} PROPERTY LABELS opencppcoverage) endif() endforeach() @@ -524,8 +534,8 @@ else() endif() foreach(_cxxflag -Werror -Wall -Wextra -Wpointer-arith -Winvalid-pch -Wcast-align - -Wwrite-strings -Woverloaded-virtual -Wnon-virtual-dtor -Wold-style-cast - -Wstrict-null-sentinel -Wsign-promo -fdiagnostics-show-option ) + -Wwrite-strings -Woverloaded-virtual -Wnon-virtual-dtor -Wold-style-cast + -Wstrict-null-sentinel -Wsign-promo -fdiagnostics-show-option ) usFunctionCheckCompilerFlags(${_cxxflag} US_CXX_FLAGS) endforeach() @@ -870,6 +880,7 @@ install(EXPORT ${PROJECT_NAME}Targets ${US_CMAKE_DIR}/usFunctionGetResourceSource.cmake ${US_CMAKE_DIR}/usFunctionCheckResourceLinking.cmake ${US_CMAKE_DIR}/usFunctionCheckCompilerFlags.cmake + ${US_CMAKE_DIR}/usFunctionBoostPath.cmake ) install(FILES ${_install_cmake_scripts} diff --git a/CppMicroServicesConfig.cmake.in b/CppMicroServicesConfig.cmake.in index 05f6c1652..6183d49ff 100644 --- a/CppMicroServicesConfig.cmake.in +++ b/CppMicroServicesConfig.cmake.in @@ -69,6 +69,7 @@ include("@PACKAGE_CONFIG_CMAKE_DIR@/usFunctionGenerateBundleInit.cmake") include("@PACKAGE_CONFIG_CMAKE_DIR@/usFunctionAddResources.cmake") include("@PACKAGE_CONFIG_CMAKE_DIR@/usFunctionCheckCompilerFlags.cmake") include("@PACKAGE_CONFIG_CMAKE_DIR@/usFunctionEmbedResources.cmake") +include("@PACKAGE_CONFIG_CMAKE_DIR@/usFunctionBoostPath.cmake") include("@PACKAGE_CONFIG_CMAKE_DIR@/usFunctionCheckResourceLinking.cmake") include("@PACKAGE_CONFIG_CMAKE_DIR@/usFunctionGetResourceSource.cmake") diff --git a/README.rst b/README.rst index 3089de64b..624c89deb 100644 --- a/README.rst +++ b/README.rst @@ -66,8 +66,8 @@ Recommended absolute minimum required compiler versions: Not all of the absolute minimum compiler versions are tested (as noted). We test and recommend the following compilers: -- GCC 9.4.0 -- Clang 9.0 +- GCC 11.3.0 +- Clang 11.0 - Clang from Xcode 13.2 and 13.4 - Visual Studio 2019 and 2022 @@ -91,11 +91,11 @@ use, please see the following resources: Below is a list of tested compiler/OS combinations: -- GCC 9.4.0 (Ubuntu 20.04) -- GCC 9.5.0 (Ubuntu 22.04) -- Clang 12.0.1 (Ubuntu 22.04) -- Apple Clang, Xcode 13.2.0 (OS X 11.6.8) -- Apple Clang, Xcode 13.4.0 (OS X 12.5.0) +- GCC 7.5.0 (Ubuntu 20.04) +- GCC 11.3.0 (Ubuntu 22.04) +- Clang 11.0.0 (Ubuntu 20.04) +- Apple Clang 13.0.0.13000029, Xcode 13.2.0 (OS X 11.7.6) +- Apple Clang 14.0.0.14000029, Xcode 13.4.0 (OS X 12.6.5) - Visual Studio 2019 - Visual Studio 2022 - MinGW-w64 diff --git a/VERSION b/VERSION index 299fad1f2..6641052db 100644 --- a/VERSION +++ b/VERSION @@ -1,2 +1,2 @@ -3.7.6 +3.8.0 diff --git a/cmake/usFunctionBoostPath.cmake b/cmake/usFunctionBoostPath.cmake new file mode 100644 index 000000000..6f161bcde --- /dev/null +++ b/cmake/usFunctionBoostPath.cmake @@ -0,0 +1,17 @@ +# +# Helper macro allowing to return path to boost library +# +# Usage: +# usFunctionBoostPath(USE_SYSTEM ${US_USE_SYSTEM_BOOST} CPPMS_SOURCE_DIR ${CppMicroServices_SOURCE_DIR} +# BOOST_DIR ${BOOST_INCLUDEDIR}) + + +function(usFunctionBoostPath) + cmake_parse_arguments(Boost_Path "" "BOOST_SYSTEM;CPPMS_SOURCE_DIR;BOOST_DIR" "" ${ARGN}) + + if (Boost_Path_BOOST_SYSTEM) + set(_boost_library ${Boost_Path_BOOST_DIR} PARENT_SCOPE) + else() + set(_boost_library ${Boost_Path_CPPMS_SOURCE_DIR}/third_party/boost/include PARENT_SCOPE) + endif() +endfunction() diff --git a/compendium/CM/include/cppmicroservices/cm/ConfigurationListener.hpp b/compendium/CM/include/cppmicroservices/cm/ConfigurationListener.hpp index 309ac6d8b..86bef9390 100644 --- a/compendium/CM/include/cppmicroservices/cm/ConfigurationListener.hpp +++ b/compendium/CM/include/cppmicroservices/cm/ConfigurationListener.hpp @@ -58,10 +58,10 @@ namespace cppmicroservices class ConfigurationEvent { public: - ConfigurationEvent(ServiceReference const configAdmin, + ConfigurationEvent(ServiceReference configAdmin, const ConfigurationEventType type, - const std::string factoryPid, - const std::string pid) + std::string factoryPid, + std::string pid) : configAdmin(std::move(configAdmin)) , type(type) , factoryPid(std::move(factoryPid)) diff --git a/compendium/ConfigurationAdmin/CMakeLists.txt b/compendium/ConfigurationAdmin/CMakeLists.txt index 8d92d9be1..6e4094695 100644 --- a/compendium/ConfigurationAdmin/CMakeLists.txt +++ b/compendium/ConfigurationAdmin/CMakeLists.txt @@ -37,7 +37,7 @@ add_compile_definitions(BOOST_DATE_TIME_NO_LIB) add_compile_definitions(BOOST_REGEX_NO_LIB) usMacroCreateBundle(ConfigurationAdmin - VERSION "1.3.4" + VERSION "1.3.6" DEPENDS Framework TARGET ConfigurationAdmin SYMBOLIC_NAME configuration_admin @@ -65,6 +65,8 @@ target_include_directories(ConfigurationAdmin PRIVATE ${CppMicroServices_SOURCE_DIR}/third_party/googletest/googlemock/include ) +usFunctionBoostPath(BOOST_SYSTEM ${US_USE_SYSTEM_BOOST} CPPMS_SOURCE_DIR ${CppMicroServices_SOURCE_DIR} BOOST_DIR ${BOOST_INCLUDEDIR}) + # There are warnings in the boost asio headers which are flagged as errors. Include the boost # asio headers as system headers to ignore these warnings and not treat them as errors. -include_directories(SYSTEM ${CppMicroServices_SOURCE_DIR}/third_party/boost/include) +include_directories(SYSTEM ${_boost_library}) \ No newline at end of file diff --git a/compendium/ConfigurationAdmin/src/CMActivator.cpp b/compendium/ConfigurationAdmin/src/CMActivator.cpp index d0413efa6..64d138a25 100644 --- a/compendium/ConfigurationAdmin/src/CMActivator.cpp +++ b/compendium/ConfigurationAdmin/src/CMActivator.cpp @@ -50,7 +50,7 @@ namespace cppmicroservices // manually for (auto const& bundle : context.GetBundles()) { - if (cppmicroservices::Bundle::State::STATE_ACTIVE == bundle.GetState()) + if (cppmicroservices::Bundle::State::STATE_ACTIVE & bundle.GetState()) { cppmicroservices::BundleEvent evt(cppmicroservices::BundleEvent::BUNDLE_STARTED, bundle); BundleChanged(evt); @@ -178,11 +178,11 @@ namespace cppmicroservices } // TODO: revisit to include LAZY_ACTIVATION when supported by the framework - if (cppmicroservices::BundleEvent::BUNDLE_STARTED == eventType) + if (cppmicroservices::BundleEvent::BUNDLE_STARTED & eventType) { CreateExtension(bundle); } - else if (cppmicroservices::BundleEvent::BUNDLE_STOPPING == eventType) + else if (cppmicroservices::BundleEvent::BUNDLE_STOPPING & eventType) { RemoveExtension(bundle); } diff --git a/compendium/ConfigurationAdmin/src/CMAsyncWorkService.cpp b/compendium/ConfigurationAdmin/src/CMAsyncWorkService.cpp index cb0116051..50f02e6e1 100644 --- a/compendium/ConfigurationAdmin/src/CMAsyncWorkService.cpp +++ b/compendium/ConfigurationAdmin/src/CMAsyncWorkService.cpp @@ -88,7 +88,6 @@ namespace cppmicroservices using Handler = typename Result::completion_handler_type; Handler handler(std::forward(task)); - Result result(handler); boost::asio::post(threadpool->get_executor(), [handler = std::move(handler)]() mutable { handler(); }); diff --git a/compendium/ConfigurationAdmin/src/CMakeLists.txt b/compendium/ConfigurationAdmin/src/CMakeLists.txt index 251566880..f2eca66ce 100644 --- a/compendium/ConfigurationAdmin/src/CMakeLists.txt +++ b/compendium/ConfigurationAdmin/src/CMakeLists.txt @@ -46,6 +46,8 @@ target_include_directories(ConfigurationAdminObjs PRIVATE ${CppMicroServices_SOURCE_DIR}/third_party/googletest/googlemock/include ) +usFunctionBoostPath(BOOST_SYSTEM ${US_USE_SYSTEM_BOOST} CPPMS_SOURCE_DIR ${CppMicroServices_SOURCE_DIR} BOOST_DIR ${BOOST_INCLUDEDIR}) + # There are warnings in the boost asio headers which are flagged as errors. Include the boost # asio headers as system headers to ignore these warnings and not treat them as errors. -include_directories(SYSTEM ${CppMicroServices_SOURCE_DIR}/third_party/boost/include) +include_directories(SYSTEM ${_boost_library}) diff --git a/compendium/ConfigurationAdmin/test/CMakeLists.txt b/compendium/ConfigurationAdmin/test/CMakeLists.txt index 17738d72a..5f53974c4 100644 --- a/compendium/ConfigurationAdmin/test/CMakeLists.txt +++ b/compendium/ConfigurationAdmin/test/CMakeLists.txt @@ -22,9 +22,11 @@ include_directories( ${GMOCK_INCLUDE_DIRS} ) +usFunctionBoostPath(BOOST_SYSTEM ${US_USE_SYSTEM_BOOST} CPPMS_SOURCE_DIR ${CppMicroServices_SOURCE_DIR} BOOST_DIR ${BOOST_INCLUDEDIR}) + # There are warnings in the boost asio headers which are flagged as errors. Include the boost # asio headers as system headers to ignore these warnings and not treat them as errors. -include_directories(SYSTEM ${CppMicroServices_SOURCE_DIR}/third_party/boost/include) +include_directories(SYSTEM ${_boost_library}) if (US_COMPILER_CLANG OR US_COMPILER_APPLE_CLANG) check_cxx_compiler_flag(-Wno-inconsistent-missing-override HAS_MISSING_OVERRIDE_FLAG) diff --git a/compendium/DeclarativeServices/CMakeLists.txt b/compendium/DeclarativeServices/CMakeLists.txt index 4486c7644..f73dd85de 100755 --- a/compendium/DeclarativeServices/CMakeLists.txt +++ b/compendium/DeclarativeServices/CMakeLists.txt @@ -39,7 +39,7 @@ add_compile_definitions(BOOST_REGEX_NO_LIB) usMacroCreateBundle(DeclarativeServices - VERSION "1.5.3" + VERSION "1.5.6" DEPENDS Framework TARGET DeclarativeServices SYMBOLIC_NAME declarative_services @@ -67,6 +67,8 @@ include_directories(${CppMicroServices_SOURCE_DIR}/framework/include ${CppMicroServices_SOURCE_DIR}/cppmicroservices/cm/include ) +usFunctionBoostPath(BOOST_SYSTEM ${US_USE_SYSTEM_BOOST} CPPMS_SOURCE_DIR ${CppMicroServices_SOURCE_DIR} BOOST_DIR ${BOOST_INCLUDEDIR}) + # There are warnings in the boost asio headers which are flagged as errors. Include the boost # asio headers as system headers to ignore these warnings and not treat them as errors. -include_directories(SYSTEM ${CppMicroServices_SOURCE_DIR}/third_party/boost/include) +include_directories(SYSTEM ${_boost_library}) diff --git a/compendium/DeclarativeServices/src/CMakeLists.txt b/compendium/DeclarativeServices/src/CMakeLists.txt index 8d30540c3..b1656318a 100644 --- a/compendium/DeclarativeServices/src/CMakeLists.txt +++ b/compendium/DeclarativeServices/src/CMakeLists.txt @@ -103,6 +103,8 @@ include_directories(${CppMicroServices_SOURCE_DIR}/framework/include ${CppMicroServices_SOURCE_DIR}/third_party/googletest/googlemock/include ) +usFunctionBoostPath(BOOST_SYSTEM ${US_USE_SYSTEM_BOOST} CPPMS_SOURCE_DIR ${CppMicroServices_SOURCE_DIR} BOOST_DIR ${BOOST_INCLUDEDIR}) + # There are warnings in the boost asio headers which are flagged as errors. Include the boost # asio headers as system headers to ignore these warnings and not treat them as errors. -include_directories(SYSTEM ${CppMicroServices_SOURCE_DIR}/third_party/boost/include) +include_directories(SYSTEM ${_boost_library}) diff --git a/compendium/DeclarativeServices/src/SCRActivator.cpp b/compendium/DeclarativeServices/src/SCRActivator.cpp index 7f600b952..7be65af6d 100644 --- a/compendium/DeclarativeServices/src/SCRActivator.cpp +++ b/compendium/DeclarativeServices/src/SCRActivator.cpp @@ -77,7 +77,7 @@ namespace cppmicroservices // manually for (auto const& bundle : context.GetBundles()) { - if (bundle.GetState() == cppmicroservices::Bundle::State::STATE_ACTIVE) + if (bundle.GetState() & cppmicroservices::Bundle::State::STATE_ACTIVE) { cppmicroservices::BundleEvent evt(cppmicroservices::BundleEvent::BUNDLE_STARTED, bundle); BundleChanged(evt); @@ -215,11 +215,11 @@ namespace cppmicroservices } // TODO: revisit to include LAZY_ACTIVATION when supported by the framework - if (eventType == cppmicroservices::BundleEvent::BUNDLE_STARTED) + if (eventType & cppmicroservices::BundleEvent::BUNDLE_STARTED) { CreateExtension(bundle); } - else if (eventType == cppmicroservices::BundleEvent::BUNDLE_STOPPING) + else if (eventType & cppmicroservices::BundleEvent::BUNDLE_STOPPING) { DisposeExtension(bundle); } diff --git a/compendium/DeclarativeServices/src/SCRAsyncWorkService.cpp b/compendium/DeclarativeServices/src/SCRAsyncWorkService.cpp index f4cf4c42e..ffc93e9e8 100644 --- a/compendium/DeclarativeServices/src/SCRAsyncWorkService.cpp +++ b/compendium/DeclarativeServices/src/SCRAsyncWorkService.cpp @@ -88,7 +88,6 @@ namespace cppmicroservices using Handler = typename Result::completion_handler_type; Handler handler(std::forward(task)); - Result result(handler); boost::asio::post(threadpool->get_executor(), [handler = std::move(handler)]() mutable { handler(); }); diff --git a/compendium/DeclarativeServices/src/SCRExtensionRegistry.cpp b/compendium/DeclarativeServices/src/SCRExtensionRegistry.cpp index e938d0e93..88f3c0dfd 100644 --- a/compendium/DeclarativeServices/src/SCRExtensionRegistry.cpp +++ b/compendium/DeclarativeServices/src/SCRExtensionRegistry.cpp @@ -25,7 +25,7 @@ namespace cppmicroservices { namespace scrimpl { - SCRExtensionRegistry::SCRExtensionRegistry(std::shared_ptr logger) + SCRExtensionRegistry::SCRExtensionRegistry(std::shared_ptr logger) : logger(std::move(logger)) { if (!(this->logger)) @@ -49,11 +49,13 @@ namespace cppmicroservices void SCRExtensionRegistry::Add(long bundleId, std::shared_ptr extension) { - if (!extension) { + if (!extension) + { throw std::invalid_argument("SCRExtensionRegistry::Add invalid extension"); } - if (extensionRegistry.find(bundleId) == extensionRegistry.end()){ - std::lock_guard l(extensionRegMutex); + std::lock_guard l(extensionRegMutex); + if (extensionRegistry.find(bundleId) == extensionRegistry.end()) + { extensionRegistry.insert(std::make_pair(bundleId, std::move(extension))); } } diff --git a/compendium/DeclarativeServices/src/SCRExtensionRegistry.hpp b/compendium/DeclarativeServices/src/SCRExtensionRegistry.hpp index 6a28ddabc..3bdae067e 100644 --- a/compendium/DeclarativeServices/src/SCRExtensionRegistry.hpp +++ b/compendium/DeclarativeServices/src/SCRExtensionRegistry.hpp @@ -24,7 +24,7 @@ #define __CPPMICROSERVICES_SCRIMPL_SCREXTENSIONREGISTRY_HPP__ #include "SCRBundleExtension.hpp" -#include "SCRLogger.hpp" +#include "cppmicroservices/logservice/LogService.hpp" #include #include @@ -46,7 +46,7 @@ namespace cppmicroservices /** * @throws std::invalid_argument exception if any of the params is a nullptr */ - SCRExtensionRegistry(std::shared_ptr logger); + SCRExtensionRegistry(std::shared_ptr logger); SCRExtensionRegistry(SCRExtensionRegistry const&) = delete; SCRExtensionRegistry(SCRExtensionRegistry&&) = delete; @@ -93,7 +93,7 @@ namespace cppmicroservices void Clear(); private: - std::shared_ptr logger; + std::shared_ptr logger; std::mutex extensionRegMutex; //protects the extensionRegistry std::unordered_map> extensionRegistry; }; diff --git a/compendium/DeclarativeServices/src/manager/BindingPolicy.cpp b/compendium/DeclarativeServices/src/manager/BindingPolicy.cpp index 584928fcd..792522530 100644 --- a/compendium/DeclarativeServices/src/manager/BindingPolicy.cpp +++ b/compendium/DeclarativeServices/src/manager/BindingPolicy.cpp @@ -32,7 +32,7 @@ namespace cppmicroservices ReferenceManagerBaseImpl::BindingPolicy::Log(std::string const& logStr, cppmicroservices::logservice::SeverityLevel logLevel) { - mgr.logger->Log(logLevel, logStr); + mgr.logger_->Log(logLevel, logStr); } bool @@ -62,14 +62,14 @@ namespace cppmicroservices std::vector notifications; if (ShouldClearBoundRefs(reference)) { - Log("Notify UNSATISFIED for reference " + mgr.metadata.name); - notifications.emplace_back(mgr.metadata.name, RefEvent::BECAME_UNSATISFIED); + Log("Notify UNSATISFIED for reference " + mgr.metadata_.name); + notifications.emplace_back(mgr.metadata_.name, RefEvent::BECAME_UNSATISFIED); ClearBoundRefs(); if (mgr.UpdateBoundRefs()) { - Log("Notify SATISFIED for reference " + mgr.metadata.name); - notifications.emplace_back(mgr.metadata.name, RefEvent::BECAME_SATISFIED); + Log("Notify SATISFIED for reference " + mgr.metadata_.name); + notifications.emplace_back(mgr.metadata_.name, RefEvent::BECAME_SATISFIED); } mgr.BatchNotifyAllListeners(notifications); @@ -104,21 +104,23 @@ namespace cppmicroservices ServiceReference svcRefToBind; { auto boundRefsHandle = mgr.boundRefs.lock(); // acquires lock on boundRefs - if (!boundRefsHandle->empty()) + // we only need to rebind if maxCardinality is unary, in case of multiple + // bound references remain as is after removing current reference + if (!boundRefsHandle->empty() && mgr.IsUnary()) { svcRefToBind = *(boundRefsHandle->begin()); - Log("Notify BIND for reference " + mgr.metadata.name); + Log("Notify BIND for reference " + mgr.metadata_.name); } } - Log("Notify UNBIND for reference " + mgr.metadata.name); - notifications.emplace_back(mgr.metadata.name, RefEvent::REBIND, svcRefToBind, reference); + Log("Notify UNBIND for reference " + mgr.metadata_.name); + notifications.emplace_back(mgr.metadata_.name, RefEvent::REBIND, svcRefToBind, reference); } if (!mgr.IsSatisfied()) { - Log("Notify UNSATISFIED for reference " + mgr.metadata.name); - notifications.emplace_back(mgr.metadata.name, RefEvent::BECAME_UNSATISFIED); + Log("Notify UNSATISFIED for reference " + mgr.metadata_.name); + notifications.emplace_back(mgr.metadata_.name, RefEvent::BECAME_UNSATISFIED); } mgr.BatchNotifyAllListeners(notifications); diff --git a/compendium/DeclarativeServices/src/manager/BindingPolicyDynamicGreedy.cpp b/compendium/DeclarativeServices/src/manager/BindingPolicyDynamicGreedy.cpp index 2416d8dfb..495d6b902 100644 --- a/compendium/DeclarativeServices/src/manager/BindingPolicyDynamicGreedy.cpp +++ b/compendium/DeclarativeServices/src/manager/BindingPolicyDynamicGreedy.cpp @@ -26,6 +26,7 @@ namespace cppmicroservices { namespace scrimpl { + using namespace cppmicroservices::logservice; /** * If no service is bound, bind to better target service. @@ -65,13 +66,25 @@ namespace cppmicroservices auto boundRefsHandle = mgr.boundRefs.lock(); // acquires lock on boundRefs if (boundRefsHandle->empty()) { - notifications.emplace_back(mgr.metadata.name, RefEvent::REBIND, reference); + notifications.emplace_back(mgr.metadata_.name, RefEvent::REBIND, reference); } - else - { // there are bound refs, determine whether to rebind + // there are bound refs, determine whether to rebind based on multiplicity of reference + else if (mgr.IsUnary()) { + //rebind new and unbind older reference if maxCardinality is unary svcRefToUnBind = *(boundRefsHandle->begin()); needRebind = svcRefToUnBind < reference; } + else if (mgr.IsMultiple()) { + //bind to new reference if maxCardinality is multiple + if (boundRefsHandle->size() < mgr.metadata_.maxCardinality) { + //number of bound references are within maxCardinality limit + notifications.emplace_back(mgr.metadata_.name, RefEvent::REBIND, reference); + } + else { + //Maximum limit of multiple references has reached, no new references will be bound + Log("Number of multiple references has reached its maximum limit. New reference(s) will not be bound.", SeverityLevel::LOG_WARNING); + } + } } ClearBoundRefs(); @@ -83,14 +96,14 @@ namespace cppmicroservices // to eliminate any gaps between unbinding the current bound target service // and binding to the new bound target service. notifications.push_back( - RefChangeNotification { mgr.metadata.name, RefEvent::REBIND, reference, svcRefToUnBind }); + RefChangeNotification { mgr.metadata_.name, RefEvent::REBIND, reference, svcRefToUnBind }); } } if (notifySatisfied) { - Log("Notify SATISFIED for reference " + mgr.metadata.name); - notifications.emplace_back(mgr.metadata.name, RefEvent::BECAME_SATISFIED); + Log("Notify SATISFIED for reference " + mgr.metadata_.name); + notifications.emplace_back(mgr.metadata_.name, RefEvent::BECAME_SATISFIED); } mgr.BatchNotifyAllListeners(notifications); } diff --git a/compendium/DeclarativeServices/src/manager/BindingPolicyDynamicReluctant.cpp b/compendium/DeclarativeServices/src/manager/BindingPolicyDynamicReluctant.cpp index 326c282d9..be8baf578 100644 --- a/compendium/DeclarativeServices/src/manager/BindingPolicyDynamicReluctant.cpp +++ b/compendium/DeclarativeServices/src/manager/BindingPolicyDynamicReluctant.cpp @@ -54,19 +54,36 @@ namespace cppmicroservices // is optional and there are no bound refs. if (0 == mgr.GetBoundReferences().size()) { - Log("Notify BIND for reference " + mgr.metadata.name); + Log("Notify BIND for reference " + mgr.metadata_.name); ClearBoundRefs(); mgr.UpdateBoundRefs(); - notifications.emplace_back(mgr.metadata.name, RefEvent::REBIND, reference); + notifications.emplace_back(mgr.metadata_.name, RefEvent::REBIND, reference); + } + + // for multiple cardinality rebind to new reference if number of + // bound references is within limit of maxCardinality value + // otherwise log to the user that further bind is not possible + if (mgr.IsMultiple()) { + if (mgr.GetBoundReferences().size() < mgr.metadata_.maxCardinality) { + Log("Notify BIND for reference " + mgr.metadata_.name); + + ClearBoundRefs(); + mgr.UpdateBoundRefs(); + + notifications.emplace_back(mgr.metadata_.name, RefEvent::REBIND, reference); + } + else { + Log("Number of multiple references has reached its maximum limit. New reference(s) will not be bound.", SeverityLevel::LOG_WARNING); + } } } if (notifySatisfied) { - Log("Notify SATISFIED for reference " + mgr.metadata.name); - notifications.emplace_back(mgr.metadata.name, RefEvent::BECAME_SATISFIED); + Log("Notify SATISFIED for reference " + mgr.metadata_.name); + notifications.emplace_back(mgr.metadata_.name, RefEvent::BECAME_SATISFIED); } mgr.BatchNotifyAllListeners(notifications); } diff --git a/compendium/DeclarativeServices/src/manager/BindingPolicyStaticGreedy.cpp b/compendium/DeclarativeServices/src/manager/BindingPolicyStaticGreedy.cpp index 9d2dfed26..332a99d97 100644 --- a/compendium/DeclarativeServices/src/manager/BindingPolicyStaticGreedy.cpp +++ b/compendium/DeclarativeServices/src/manager/BindingPolicyStaticGreedy.cpp @@ -58,16 +58,26 @@ namespace cppmicroservices // old service and then bind to the new service. if (!boundRefsHandle->empty()) { - // We only need to unbind if there's actually a bound ref. ServiceReferenceBase const& minBound = *(boundRefsHandle->begin()); - if (minBound < reference) + if (mgr.IsUnary() && minBound < reference) { + // We only need to unbind if there's actually a bound ref. // And we only need to unbind if the new reference is a better match than the // current best match (i.e. boundRefs are stored in reverse order with the best // match in the first position). replacementNeeded = true; serviceToUnbind = minBound; // remember which service to unbind. } + else { + // in case of multiple cardinality, always bind if number of bound references + // is within limit of maxCardinality value + if (boundRefsHandle->size() < mgr.metadata_.maxCardinality) { + replacementNeeded = true; + } + else { + Log("Number of multiple references has reached its maximum limit. New reference(s) will not be bound.", SeverityLevel::LOG_WARNING); + } + } } else { @@ -82,8 +92,8 @@ namespace cppmicroservices std::vector notifications; if (replacementNeeded) { - Log("Notify UNSATISFIED for reference " + mgr.metadata.name); - notifications.emplace_back(mgr.metadata.name, RefEvent::BECAME_UNSATISFIED, reference); + Log("Notify UNSATISFIED for reference " + mgr.metadata_.name); + notifications.emplace_back(mgr.metadata_.name, RefEvent::BECAME_UNSATISFIED, reference); // The following "clear and copy" strategy is sufficient for // updating the boundRefs for static binding policy if (serviceToUnbind) @@ -94,8 +104,8 @@ namespace cppmicroservices } if (notifySatisfied) { - Log("Notify SATISFIED for reference " + mgr.metadata.name); - notifications.emplace_back(mgr.metadata.name, RefEvent::BECAME_SATISFIED, reference); + Log("Notify SATISFIED for reference " + mgr.metadata_.name); + notifications.emplace_back(mgr.metadata_.name, RefEvent::BECAME_SATISFIED, reference); } mgr.BatchNotifyAllListeners(notifications); } diff --git a/compendium/DeclarativeServices/src/manager/BindingPolicyStaticReluctant.cpp b/compendium/DeclarativeServices/src/manager/BindingPolicyStaticReluctant.cpp index 1a3bb2a4b..740d7e670 100644 --- a/compendium/DeclarativeServices/src/manager/BindingPolicyStaticReluctant.cpp +++ b/compendium/DeclarativeServices/src/manager/BindingPolicyStaticReluctant.cpp @@ -45,8 +45,8 @@ namespace cppmicroservices auto notifySatisfied = ShouldNotifySatisfied(); if (notifySatisfied) { - Log("Notify SATISFIED for reference " + mgr.metadata.name); - notifications.emplace_back(mgr.metadata.name, RefEvent::BECAME_SATISFIED, reference); + Log("Notify SATISFIED for reference " + mgr.metadata_.name); + notifications.emplace_back(mgr.metadata_.name, RefEvent::BECAME_SATISFIED, reference); } mgr.BatchNotifyAllListeners(notifications); diff --git a/compendium/DeclarativeServices/src/manager/BundleLoader.cpp b/compendium/DeclarativeServices/src/manager/BundleLoader.cpp index d55224d81..2eb4301d9 100644 --- a/compendium/DeclarativeServices/src/manager/BundleLoader.cpp +++ b/compendium/DeclarativeServices/src/manager/BundleLoader.cpp @@ -20,6 +20,8 @@ =============================================================================*/ +#include "cppmicroservices/SecurityException.h" + #include "cppmicroservices/Bundle.h" #include "cppmicroservices/Constants.h" #include "cppmicroservices/SharedLibrary.h" @@ -78,7 +80,7 @@ namespace cppmicroservices wchar_count = MultiByteToWideChar(CP_UTF8, 0, inStr.c_str(), -1, wBuf.get(), wchar_count); if (wchar_count == 0) { - std::invalid_argument("Failed to convert " + inStr + " to UTF16."); + throw std::invalid_argument("Failed to convert " + inStr + " to UTF16."); } return wBuf.get(); } @@ -102,6 +104,26 @@ namespace cppmicroservices } else { + Any func = fromBundle.GetBundleContext().GetProperty( + cppmicroservices::Constants::FRAMEWORK_BUNDLE_VALIDATION_FUNC); + try + { + if (!func.Empty() + && !any_cast>(func)(fromBundle)) + { + std::string errMsg("Bundle at location " + bundleLoc + " failed bundle validation."); + throw SecurityException{ std::move(errMsg), fromBundle }; + } + } + catch (cppmicroservices::SecurityException const&) + { + throw; + } + catch (...) + { + throw SecurityException { "The bundle validation callback threw an exception", fromBundle}; + } + SharedLibrary sh(bundleLoc); try { diff --git a/compendium/DeclarativeServices/src/manager/ComponentConfigurationImpl.cpp b/compendium/DeclarativeServices/src/manager/ComponentConfigurationImpl.cpp index b45b81afd..ca81c5232 100644 --- a/compendium/DeclarativeServices/src/manager/ComponentConfigurationImpl.cpp +++ b/compendium/DeclarativeServices/src/manager/ComponentConfigurationImpl.cpp @@ -21,7 +21,6 @@ =============================================================================*/ #include "cppmicroservices/FrameworkFactory.h" -#include "cppmicroservices/SecurityException.h" #include "../ConfigurationListenerImpl.hpp" #include "BundleLoader.hpp" @@ -488,29 +487,6 @@ namespace cppmicroservices InstanceContextPair ComponentConfigurationImpl::CreateAndActivateComponentInstanceHelper(cppmicroservices::Bundle const& bundle) { - Any func = this->bundle.GetBundleContext().GetProperty( - cppmicroservices::Constants::FRAMEWORK_BUNDLE_VALIDATION_FUNC); - - try - { - if (!func.Empty() - && !any_cast>(func)(this->bundle)) - { - std::string errMsg("Bundle at location "); - errMsg += this->bundle.GetLocation(); - errMsg += " failed bundle validation."; - throw SecurityException { std::move(errMsg), this->bundle }; - } - } - catch (cppmicroservices::SecurityException const&) - { - throw; - } - catch (...) - { - throw SecurityException { "The bundle validation callback threw an exception", this->bundle }; - } - auto componentInstance = CreateComponentInstance(); auto ctxt = std::make_shared(shared_from_this(), bundle); /* diff --git a/compendium/DeclarativeServices/src/manager/ComponentConfigurationImpl.hpp b/compendium/DeclarativeServices/src/manager/ComponentConfigurationImpl.hpp index 69a675480..93dc5e5c8 100644 --- a/compendium/DeclarativeServices/src/manager/ComponentConfigurationImpl.hpp +++ b/compendium/DeclarativeServices/src/manager/ComponentConfigurationImpl.hpp @@ -56,7 +56,7 @@ namespace cppmicroservices { ListenerToken(std::string pid, const ListenerTokenId tokenId) : pid(std::move(pid)) - , tokenId(std::move(tokenId)) + , tokenId(tokenId) { } std::string pid; diff --git a/compendium/DeclarativeServices/src/manager/ConfigurationNotifier.cpp b/compendium/DeclarativeServices/src/manager/ConfigurationNotifier.cpp index d75f43648..f650005a9 100644 --- a/compendium/DeclarativeServices/src/manager/ConfigurationNotifier.cpp +++ b/compendium/DeclarativeServices/src/manager/ConfigurationNotifier.cpp @@ -116,7 +116,7 @@ namespace cppmicroservices ConfigurationNotifier::AnyListenersForPid(std::string const& pid) noexcept { std::string factoryName; - std::shared_ptr mgr; + std::vector> mgrs; { auto listenersMapHandle = listenersMap.lock(); if (listenersMapHandle->empty() || pid.empty()) @@ -148,16 +148,27 @@ namespace cppmicroservices { return false; } - auto listener = iter->second->begin(); + auto tokenMapPtr = iter->second; + for (auto const& tokenEntry : (*tokenMapPtr)) + { + auto listener = tokenEntry.second; - mgr = listener->second.mgr; - if (mgr->GetMetadata()->factoryComponentID.empty()) + if (!listener.mgr->GetMetadata()->factoryComponentID.empty()) + { + // The component in our listeners map is a factory component. + mgrs.emplace_back(listener.mgr); + } + } + if (mgrs.empty()) { - // The component in our listener's map is not a factory component. + // None of the components in our listeners map is a factory component. return false; } } // release listenersMapHandle lock - CreateFactoryComponent(pid, mgr); + for (auto & mgr : mgrs) + { + CreateFactoryComponent(pid, mgr); + } return true; } void diff --git a/compendium/DeclarativeServices/src/manager/ReferenceManager.hpp b/compendium/DeclarativeServices/src/manager/ReferenceManager.hpp index 3962eaf15..a54f8e61c 100644 --- a/compendium/DeclarativeServices/src/manager/ReferenceManager.hpp +++ b/compendium/DeclarativeServices/src/manager/ReferenceManager.hpp @@ -141,6 +141,16 @@ namespace cppmicroservices */ virtual void StopTracking() = 0; + /** + * Method returns true if maxCardinality is unary (1) + */ + virtual bool IsUnary() const = 0; + + /** + * Method returns true if maxCardinality is multiple (n) + */ + virtual bool IsMultiple() const = 0; + protected: ReferenceManager() = default; }; diff --git a/compendium/DeclarativeServices/src/manager/ReferenceManagerImpl.cpp b/compendium/DeclarativeServices/src/manager/ReferenceManagerImpl.cpp index ecca52487..3b1972096 100644 --- a/compendium/DeclarativeServices/src/manager/ReferenceManagerImpl.cpp +++ b/compendium/DeclarativeServices/src/manager/ReferenceManagerImpl.cpp @@ -79,25 +79,25 @@ namespace cppmicroservices std::shared_ptr logger, std::string const& configName, std::unique_ptr policy) - : metadata(metadata) + : metadata_(metadata) , tracker(nullptr) - , logger(std::move(logger)) - , configName(configName) + , logger_(std::move(logger)) + , configName_(configName) , bindingPolicy(std::move(policy)) { - if (!bc || !this->logger) + if (!bc || !logger_) { throw std::invalid_argument("Failed to create object, Invalid arguments passed to constructor"); } try { - tracker = std::make_unique>(bc, GetReferenceLDAPFilter(metadata), this); + tracker = std::make_unique>(bc, GetReferenceLDAPFilter(metadata_), this); tracker->Open(); } catch (...) { - logger->Log(SeverityLevel::LOG_ERROR, - "could not open service tracker for " + metadata.interfaceName, + logger_->Log(SeverityLevel::LOG_ERROR, + "could not open service tracker for " + metadata_.interfaceName, std::current_exception()); tracker.reset(); throw std::current_exception(); @@ -113,8 +113,8 @@ namespace cppmicroservices } catch (...) { - logger->Log(SeverityLevel::LOG_ERROR, - "Exception caught while closing service tracker for " + metadata.interfaceName, + logger_->Log(SeverityLevel::LOG_ERROR, + "Exception caught while closing service tracker for " + metadata_.interfaceName, std::current_exception()); } } @@ -137,13 +137,23 @@ namespace cppmicroservices bool ReferenceManagerBaseImpl::IsOptional() const { - return (metadata.minCardinality == 0); + return (metadata_.minCardinality == 0); } bool ReferenceManagerBaseImpl::IsSatisfied() const { - return (boundRefs.lock()->size() >= metadata.minCardinality); + return (boundRefs.lock()->size() >= metadata_.minCardinality); + } + + bool ReferenceManagerBaseImpl::IsUnary() const + { + return (metadata_.maxCardinality == 1); + } + + bool ReferenceManagerBaseImpl::IsMultiple() const + { + return (metadata_.maxCardinality > 1); } ReferenceManagerBaseImpl::~ReferenceManagerBaseImpl() { StopTracking(); } @@ -157,12 +167,12 @@ namespace cppmicroservices { auto matchedRefsHandle = matchedRefs.lock(); // acquires lock on matchedRefs auto const matchedRefsHandleSize = matchedRefsHandle->size(); - if (matchedRefsHandleSize >= metadata.minCardinality) + if (matchedRefsHandleSize >= metadata_.minCardinality) { auto boundRefsHandle = boundRefs.lock(); // acquires lock on boundRefs boundRefsHandle->clear(); std::copy_n(matchedRefsHandle->rbegin(), - std::min(metadata.maxCardinality, matchedRefsHandleSize), + std::min(metadata_.maxCardinality, matchedRefsHandleSize), std::inserter(*(boundRefsHandle), boundRefsHandle->begin())); return true; } @@ -181,7 +191,7 @@ namespace cppmicroservices // ASSUMPTION: If there is no component configuration name then its assumed this service was not registered // by DS and could not satisfy itself since it is not managed by DS. auto const compConfigName = reference.GetProperty(COMPONENT_NAME); - if ((true == compConfigName.Empty()) || (configName != compConfigName.ToStringNoExcept())) + if ((true == compConfigName.Empty()) || (configName_ != compConfigName.ToStringNoExcept())) { // acquire lock on matchedRefs auto matchedRefsHandle = matchedRefs.lock(); @@ -239,7 +249,7 @@ namespace cppmicroservices auto notifySatisfied = UpdateBoundRefs(); if (notifySatisfied) { - RefChangeNotification notification { metadata.name, RefEvent::BECAME_SATISFIED }; + RefChangeNotification notification { metadata_.name, RefEvent::BECAME_SATISFIED }; notify(notification); } @@ -289,7 +299,7 @@ namespace cppmicroservices } catch (...) { - logger->Log(SeverityLevel::LOG_ERROR, + logger_->Log(SeverityLevel::LOG_ERROR, "Exception caught while notifying service reference " "listeners for reference name " + notification.senderName, diff --git a/compendium/DeclarativeServices/src/manager/ReferenceManagerImpl.hpp b/compendium/DeclarativeServices/src/manager/ReferenceManagerImpl.hpp index 10a47452f..35da7429a 100644 --- a/compendium/DeclarativeServices/src/manager/ReferenceManagerImpl.hpp +++ b/compendium/DeclarativeServices/src/manager/ReferenceManagerImpl.hpp @@ -82,7 +82,7 @@ namespace cppmicroservices std::string GetReferenceName() const override { - return metadata.name; + return metadata_.name; } /** @@ -91,7 +91,7 @@ namespace cppmicroservices std::string GetReferenceScope() const override { - return metadata.scope; + return metadata_.scope; } /** @@ -100,7 +100,7 @@ namespace cppmicroservices std::string GetLDAPString() const override { - return metadata.target; + return metadata_.target; } /** @@ -131,7 +131,7 @@ namespace cppmicroservices metadata::ReferenceMetadata const& GetMetadata() const { - return metadata; + return metadata_; } /** @@ -176,6 +176,16 @@ namespace cppmicroservices */ void StopTracking() override; + /** + * Method returns true if maxCardinality is unary (1) + */ + bool IsUnary() const override; + + /** + * Method returns true if maxCardinality is multiple (n) + */ + bool IsMultiple() const override; + class BindingPolicy { public: @@ -269,11 +279,11 @@ namespace cppmicroservices */ void BatchNotifyAllListeners(std::vector const& notification) noexcept; - const metadata::ReferenceMetadata metadata; ///< reference information from the component description + const metadata::ReferenceMetadata metadata_; ///< reference information from the component description std::unique_ptr> tracker; ///< used to track service availability - std::shared_ptr logger; ///< logger for this runtime + std::shared_ptr logger_; ///< logger for this runtime const std::string - configName; ///< Keep track of which component configuration object this reference manager belongs to. + configName_; ///< Keep track of which component configuration object this reference manager belongs to. mutable Guarded> boundRefs; ///< guarded set of bound references diff --git a/compendium/DeclarativeServices/test/bench/CMakeLists.txt b/compendium/DeclarativeServices/test/bench/CMakeLists.txt index 36f68de4c..2056a4441 100644 --- a/compendium/DeclarativeServices/test/bench/CMakeLists.txt +++ b/compendium/DeclarativeServices/test/bench/CMakeLists.txt @@ -23,9 +23,11 @@ include_directories( ${CMAKE_SOURCE_DIR}/third_party/benchmark/include ) +usFunctionBoostPath(BOOST_SYSTEM ${US_USE_SYSTEM_BOOST} CPPMS_SOURCE_DIR ${CppMicroServices_SOURCE_DIR} BOOST_DIR ${BOOST_INCLUDEDIR}) + # There are warnings in the boost asio headers which are flagged as errors. Include the boost # asio headers as system headers to ignore these warnings and not treat them as errors. -include_directories(SYSTEM ${CppMicroServices_SOURCE_DIR}/third_party/boost/include) +include_directories(SYSTEM ${_boost_library}) if (US_COMPILER_CLANG OR US_COMPILER_APPLE_CLANG) diff --git a/compendium/DeclarativeServices/test/gtest/CMakeLists.txt b/compendium/DeclarativeServices/test/gtest/CMakeLists.txt index d924861b7..e57d4adb3 100644 --- a/compendium/DeclarativeServices/test/gtest/CMakeLists.txt +++ b/compendium/DeclarativeServices/test/gtest/CMakeLists.txt @@ -22,9 +22,11 @@ include_directories( ${GMOCK_INCLUDE_DIRS} ) +usFunctionBoostPath(BOOST_SYSTEM ${US_USE_SYSTEM_BOOST} CPPMS_SOURCE_DIR ${CppMicroServices_SOURCE_DIR} BOOST_DIR ${BOOST_INCLUDEDIR}) + # There are warnings in the boost asio headers which are flagged as errors. Include the boost # asio headers as system headers to ignore these warnings and not treat them as errors. -include_directories(SYSTEM ${CppMicroServices_SOURCE_DIR}/third_party/boost/include) +include_directories(SYSTEM ${_boost_library}) if (US_COMPILER_CLANG OR US_COMPILER_APPLE_CLANG) @@ -77,6 +79,7 @@ set(_declarativeservices_tests TestReferenceSelfSatisfyDeadLock.cpp TestRegistrationManager.cpp TestSCRBundleExtension.cpp + TestSCRExtensionRegistry.cpp TestServiceComponentRuntimeImpl.cpp TestServiceMetadataParserV1.cpp TestSetConfiguration.cpp diff --git a/compendium/DeclarativeServices/test/gtest/Mocks.hpp b/compendium/DeclarativeServices/test/gtest/Mocks.hpp index 0272deb26..003970d54 100644 --- a/compendium/DeclarativeServices/test/gtest/Mocks.hpp +++ b/compendium/DeclarativeServices/test/gtest/Mocks.hpp @@ -227,6 +227,8 @@ namespace cppmicroservices cppmicroservices::ListenerTokenId(std::function)); MOCK_METHOD1(UnregisterListener, void(cppmicroservices::ListenerTokenId)); MOCK_METHOD0(StopTracking, void(void)); + MOCK_CONST_METHOD0(IsUnary, bool(void)); + MOCK_CONST_METHOD0(IsMultiple, bool(void)); }; class MockReferenceManagerBaseImpl : public ReferenceManagerBaseImpl diff --git a/compendium/DeclarativeServices/test/gtest/TestBindingPolicies.cpp b/compendium/DeclarativeServices/test/gtest/TestBindingPolicies.cpp index eb419638e..df4a94d16 100644 --- a/compendium/DeclarativeServices/test/gtest/TestBindingPolicies.cpp +++ b/compendium/DeclarativeServices/test/gtest/TestBindingPolicies.cpp @@ -190,16 +190,20 @@ namespace cppmicroservices // utility method for creating different types of reference metadata objects used in testing metadata::ReferenceMetadata - CreateFakeReferenceMetadata(std::string const& policy, std::string const& policyOption) + CreateFakeReferenceMetadata(std::string const& policy, std::string const& policyOption, std::string const& cardinality = "0..1") { metadata::ReferenceMetadata fakeMetadata {}; fakeMetadata.name = "ref"; fakeMetadata.interfaceName = us_service_interface_iid(); fakeMetadata.policy = policy; fakeMetadata.policyOption = policyOption; - fakeMetadata.cardinality = "0..1"; - fakeMetadata.minCardinality = 0; - fakeMetadata.maxCardinality = 1; + fakeMetadata.cardinality = !cardinality.empty() ? cardinality : "0..1"; + + auto cardLimits = metadata::GetReferenceCardinalityExtents(fakeMetadata.cardinality); + + fakeMetadata.minCardinality = std::get<0>(cardLimits); + fakeMetadata.maxCardinality = std::get<1>(cardLimits); + return fakeMetadata; } @@ -251,7 +255,7 @@ namespace cppmicroservices INSTANTIATE_TEST_SUITE_P( DynamicReferencePolicies, DynamicRefPolicyTest, - testing::Values(DynamicRefPolicy { "TestBundleDSDRMU", + testing::Values(DynamicRefPolicy { "TestBundleDSDRMU", "ServiceComponentDynamicReluctantMandatoryUnary depends on " "ServiceComponentDynamicReluctantMandatoryUnary Interface1", "", @@ -514,7 +518,7 @@ namespace cppmicroservices EXPECT_FALSE(bc.GetServiceReference()) << "Service should NOT be available"; EXPECT_THROW(svc->ExtendedDescription(), std::runtime_error); testBundle.Stop(); - } + } // test that: // a new higher ranked service causes a re-bind @@ -866,5 +870,388 @@ namespace cppmicroservices testBundle.Stop(); } + struct MultipleCardinalityDynamicPolicy { + metadata::ReferenceMetadata fakeMetadata; + + int numBindNotifs[3]; + int numUnbindNotifs[3]; + int numLoggerCalls; + + friend std::ostream& + operator<<(std::ostream& os, MultipleCardinalityDynamicPolicy const& obj) + { + return os << "Metadata name: " << obj.fakeMetadata.name << "\n" + << "Bind notification count: " << obj.numBindNotifs << "\n" + << "Unbind notification count: " << obj.numUnbindNotifs << "\n" + << "Logger calls count: " << obj.numLoggerCalls << "\n"; + } + }; + + class DynamicPolicyMultipleCardinalityTest : public ::testing::TestWithParam + { + protected: + DynamicPolicyMultipleCardinalityTest() : framework(cppmicroservices::FrameworkFactory().NewFramework()) {} + + virtual ~DynamicPolicyMultipleCardinalityTest() = default; + + virtual void + SetUp() + { + framework.Start(); + mockLogger = std::make_shared(); + } + + virtual void + TearDown() + { + framework.Stop(); + framework.WaitForStop(std::chrono::milliseconds::zero()); + } + + cppmicroservices::Framework& + GetFramework() + { + return framework; + } + + ListenerTokenId CreateListener(ReferenceManagerImpl& refManager) { + return refManager.RegisterListener( + [&](RefChangeNotification const& notification) + { + switch (notification.event) + { + case RefEvent::BECAME_SATISFIED: + satisfiedNotificationCount++; + break; + case RefEvent::BECAME_UNSATISFIED: + unsatisfiedNotificationCount++; + break; + case RefEvent::REBIND: + if (notification.serviceRefToBind) + { + bindNotificationCount++; + } + if (notification.serviceRefToUnbind) + { + unbindNotificationCount++; + } + break; + default: + break; + } + }); + } + + cppmicroservices::Framework framework; + std::shared_ptr mockLogger; + ListenerTokenId listenerToken{ 0 }; + + int satisfiedNotificationCount{ 0 }; + int unsatisfiedNotificationCount{ 0 }; + int bindNotificationCount{ 0 }; + int unbindNotificationCount{ 0 }; + }; + + INSTANTIATE_TEST_SUITE_P( + MultipleCardinalityTests, + DynamicPolicyMultipleCardinalityTest, + testing::Values( + MultipleCardinalityDynamicPolicy{ CreateFakeReferenceMetadata("dynamic", "greedy", "1..n"), {0, 1, 2}, {1,2,2}, 4 }, + MultipleCardinalityDynamicPolicy{ CreateFakeReferenceMetadata("dynamic", "greedy", "0..n"), {1, 2, 3}, {1,2,3}, 3 }, + MultipleCardinalityDynamicPolicy{ CreateFakeReferenceMetadata("dynamic", "reluctant", "1..n"), {0, 1, 2}, {1, 2, 2}, 6 }, + MultipleCardinalityDynamicPolicy{ CreateFakeReferenceMetadata("dynamic", "reluctant", "0..n"), {2, 3, 4}, {1, 2, 3}, 7 })); + + // test that: + // any new service causes a re-bind + // unregistering services keeps other bound services intact + TEST_P(DynamicPolicyMultipleCardinalityTest, TestDynamicPolicyWithMultipleCardinality) + { + auto bc = GetFramework().GetBundleContext(); + + ReferenceManagerImpl refManager(GetParam().fakeMetadata, + bc, + mockLogger, + "foo"); + + CreateListener(refManager); + + EXPECT_CALL(*(mockLogger).get(), Log(cppmicroservices::logservice::SeverityLevel::LOG_DEBUG, testing::_)) + .Times(GetParam().numLoggerCalls); + + auto depSvcReg = bc.RegisterService( + std::make_shared(), + { + {Constants::SERVICE_RANKING, Any(10000)} + }); + ASSERT_TRUE(depSvcReg); + + EXPECT_EQ(refManager.GetBoundReferences().size(), 1); + + auto depSvcReg1 = bc.RegisterService( + std::make_shared(), + { + {Constants::SERVICE_RANKING, Any(1)} + }); + ASSERT_TRUE(depSvcReg1); + + EXPECT_EQ(refManager.GetBoundReferences().size(), 2); + EXPECT_EQ(bindNotificationCount, GetParam().numBindNotifs[1]); + + auto depSvcReg2 = bc.RegisterService( + std::make_shared(), + { + {Constants::SERVICE_RANKING, Any(100)} + }); + ASSERT_TRUE(depSvcReg2); + + EXPECT_EQ(refManager.GetBoundReferences().size(), 3); + EXPECT_EQ(bindNotificationCount, GetParam().numBindNotifs[2]); + + depSvcReg2.Unregister(); + + EXPECT_EQ(refManager.GetBoundReferences().size(), 2); + EXPECT_EQ(unbindNotificationCount, GetParam().numUnbindNotifs[0]); + + depSvcReg1.Unregister(); + + EXPECT_EQ(refManager.GetBoundReferences().size(), 1); + EXPECT_EQ(unbindNotificationCount, GetParam().numUnbindNotifs[1]); + + depSvcReg.Unregister(); + + EXPECT_EQ(refManager.GetBoundReferences().size(), 0); + EXPECT_EQ(unbindNotificationCount, GetParam().numUnbindNotifs[2]); + + refManager.UnregisterListener(listenerToken); + } + + struct MultipleCardinalityStaticPolicy { + metadata::ReferenceMetadata fakeMetadata; + + int numSatisfiedNotifs[6]; + int numUnsatisfiedNotifs[6]; + int numLoggerCalls; + + friend std::ostream& + operator<<(std::ostream& os, MultipleCardinalityStaticPolicy const& obj) + { + return os << "Metadata name: " << obj.fakeMetadata.name << "\n" + << "Bind notification count: " << obj.numSatisfiedNotifs << "\n" + << "Unbind notification count: " << obj.numUnsatisfiedNotifs << "\n" + << "Logger calls count: " << obj.numLoggerCalls << "\n"; + } + }; + + class StaticPolicyMultipleCardinalityTest : public ::testing::TestWithParam + { + protected: + StaticPolicyMultipleCardinalityTest() : framework(cppmicroservices::FrameworkFactory().NewFramework()) {} + + virtual ~StaticPolicyMultipleCardinalityTest() = default; + + virtual void + SetUp() + { + framework.Start(); + mockLogger = std::make_shared(); + } + + virtual void + TearDown() + { + framework.Stop(); + framework.WaitForStop(std::chrono::milliseconds::zero()); + } + + cppmicroservices::Framework& + GetFramework() + { + return framework; + } + + ListenerTokenId CreateListener(ReferenceManagerImpl & refManager) { + return refManager.RegisterListener( + [&](RefChangeNotification const& notification) + { + switch (notification.event) + { + case RefEvent::BECAME_SATISFIED: + satisfiedNotificationCount++; + break; + case RefEvent::BECAME_UNSATISFIED: + unsatisfiedNotificationCount++; + break; + default: + break; + } + }); + } + + cppmicroservices::Framework framework; + std::shared_ptr mockLogger; + ListenerTokenId listenerToken{ 0 }; + + int satisfiedNotificationCount{ 0 }; + int unsatisfiedNotificationCount{ 0 }; + }; + + INSTANTIATE_TEST_SUITE_P( + MultipleCardinalityTests, + StaticPolicyMultipleCardinalityTest, + testing::Values( + MultipleCardinalityStaticPolicy{ CreateFakeReferenceMetadata("static", "greedy", "1..n"), {1, 2, 3, 1, 2, 2}, {0, 1, 2, 1, 2, 3}, 10 }, + MultipleCardinalityStaticPolicy{ CreateFakeReferenceMetadata("static", "greedy", "0..n"), {2, 3, 4, 1, 2, 3}, {1, 2, 3, 1, 2, 3}, 12 } + )); + + // test that: + // any new service causes a reactivation of component + // unregistering services keeps other bound services intact and reactivates component + TEST_P(StaticPolicyMultipleCardinalityTest, TestStaticPolicyWithMultipleCardinality) { + + auto bc = GetFramework().GetBundleContext(); + + ReferenceManagerImpl refManager(GetParam().fakeMetadata, + bc, + mockLogger, + "foo"); + + CreateListener(refManager); + + EXPECT_CALL(*mockLogger.get(), Log(cppmicroservices::logservice::SeverityLevel::LOG_DEBUG, testing::_)) + .Times(GetParam().numLoggerCalls); + + auto depSvcReg = bc.RegisterService( + std::make_shared(), + { + {Constants::SERVICE_RANKING, Any(10000)} + }); + ASSERT_TRUE(depSvcReg); + + EXPECT_EQ(refManager.GetBoundReferences().size(), 1); + EXPECT_EQ(unsatisfiedNotificationCount, GetParam().numUnsatisfiedNotifs[0]); + EXPECT_EQ(satisfiedNotificationCount, GetParam().numSatisfiedNotifs[0]); + + auto depSvcReg1 = bc.RegisterService( + std::make_shared(), + { + {Constants::SERVICE_RANKING, Any(1)} + }); + ASSERT_TRUE(depSvcReg1); + + EXPECT_EQ(refManager.GetBoundReferences().size(), 2); + EXPECT_EQ(unsatisfiedNotificationCount, GetParam().numUnsatisfiedNotifs[1]); + EXPECT_EQ(satisfiedNotificationCount, GetParam().numSatisfiedNotifs[1]); + + auto depSvcReg2 = bc.RegisterService( + std::make_shared(), + { + {Constants::SERVICE_RANKING, Any(100)} + }); + ASSERT_TRUE(depSvcReg2); + + EXPECT_EQ(refManager.GetBoundReferences().size(), 3); + EXPECT_EQ(unsatisfiedNotificationCount, GetParam().numUnsatisfiedNotifs[2]); + EXPECT_EQ(satisfiedNotificationCount, GetParam().numSatisfiedNotifs[2]); + + satisfiedNotificationCount = 0; + unsatisfiedNotificationCount = 0; + + depSvcReg2.Unregister(); + + EXPECT_EQ(refManager.GetBoundReferences().size(), 2); + EXPECT_EQ(unsatisfiedNotificationCount, GetParam().numUnsatisfiedNotifs[3]); + EXPECT_EQ(satisfiedNotificationCount, GetParam().numSatisfiedNotifs[3]); + + depSvcReg1.Unregister(); + + EXPECT_EQ(refManager.GetBoundReferences().size(), 1); + EXPECT_EQ(unsatisfiedNotificationCount, GetParam().numUnsatisfiedNotifs[4]); + EXPECT_EQ(satisfiedNotificationCount, GetParam().numSatisfiedNotifs[4]); + + depSvcReg.Unregister(); + + EXPECT_EQ(refManager.GetBoundReferences().size(), 0); + EXPECT_EQ(unsatisfiedNotificationCount, GetParam().numUnsatisfiedNotifs[5]); + EXPECT_EQ(satisfiedNotificationCount, GetParam().numSatisfiedNotifs[5]); + + refManager.UnregisterListener(listenerToken); + } + + TEST_P(BindingPolicyTest, TestMaxLimitOfMultipleReferences) { + auto bc = GetFramework().GetBundleContext(); + auto const& param = GetParam(); + + auto fakeMetadata = CreateFakeReferenceMetadata(param.policy, param.policyOption, "0..n"); + fakeMetadata.maxCardinality = 10; //overriding default values set for max cardinality for testing purpose + + auto mockLogger = std::make_shared(); + ReferenceManagerImpl refManager(fakeMetadata, + bc, + mockLogger, + "foo"); + + for (int i = 0; i < 15; i++) { + auto depSvcReg = bc.RegisterService( + std::make_shared(), + { + {Constants::SERVICE_RANKING, Any(i)} + }); + ASSERT_TRUE(depSvcReg); + } + + if (std::string(param.policy) == "static" && std::string(param.policyOption) == "reluctant") { + // in case of static reluctant, there's no bind even with multiple cardinality + EXPECT_EQ(refManager.GetBoundReferences().size(), 0); + } + else { + EXPECT_EQ(refManager.GetBoundReferences().size(), 10); + } + } + + // test that concurrent service registrations and unregistrations with multiple cardinality + // do not cause crashes + TEST_F(BindingPolicyTest, TestConcurrentBindUnbindWithMultipleCardinality) + { + auto bc = GetFramework().GetBundleContext(); + + auto fakeMetadata = CreateFakeReferenceMetadata("dynamic", "greedy", "0..n"); + auto mockLogger = std::make_shared(); + ReferenceManagerImpl refManager(fakeMetadata, + bc, + mockLogger, + "foo"); + + std::function func = [&bc]() -> bool + { + auto depSvcReg = bc.RegisterService( + std::make_shared(), + { + {Constants::SERVICE_RANKING, Any(10000)} + }); + + + auto depSvcReg1 = bc.RegisterService( + std::make_shared(), + { + {Constants::SERVICE_RANKING, Any(1)} + }); + + auto depSvcReg2 = bc.RegisterService( + std::make_shared(), + { + {Constants::SERVICE_RANKING, Any(100)} + }); + + depSvcReg.Unregister(); + depSvcReg1.Unregister(); + depSvcReg2.Unregister(); + return true; + }; + + auto results = ConcurrentInvoke(func); + EXPECT_TRUE(!results.empty()); + EXPECT_TRUE(std::all_of(results.cbegin(), results.cend(), [](bool result) { return result; })); + } + } // namespace scrimpl -} // namespace cppmicroservices +} // namespace cppmicroservices \ No newline at end of file diff --git a/compendium/DeclarativeServices/test/gtest/TestBundleValidation.cpp b/compendium/DeclarativeServices/test/gtest/TestBundleValidation.cpp index 16a86796b..e5c5ad8a6 100644 --- a/compendium/DeclarativeServices/test/gtest/TestBundleValidation.cpp +++ b/compendium/DeclarativeServices/test/gtest/TestBundleValidation.cpp @@ -40,6 +40,8 @@ limitations under the License. #include "gtest/gtest.h" +#include + TEST(TestBundleValidation, BundleValidationFailure) { using validationFuncType = std::function; @@ -71,12 +73,12 @@ TEST(TestBundleValidation, BundleValidationFailure) // test starting an "immediate" ds component // in this case, starting the bundle causes the shared library to load - test::InstallLib(f.GetBundleContext(), "TestBundleDSTOI1"); + test::InstallLib(f.GetBundleContext(), "TestBundleDSTBV1"); auto bundles = f.GetBundleContext().GetBundles(); auto bundleIter = std::find_if(bundles.begin(), bundles.end(), - [](cppmicroservices::Bundle const& b) { return (b.GetSymbolicName() == "TestBundleDSTOI1"); }); + [](cppmicroservices::Bundle const& b) { return (b.GetSymbolicName() == "TestBundleDSTBV1"); }); ASSERT_THROW(bundleIter->Start(), cppmicroservices::SecurityException); // a bundle validation function which returns false must cause the @@ -87,12 +89,12 @@ TEST(TestBundleValidation, BundleValidationFailure) // test starting a delayed activation ds component with a service dependency // in this case, starting the bundle does not cause the shared library to load // the shared library is loaded on the first call to "GetService" - test::InstallLib(f.GetBundleContext(), "TestBundleDSTOI6"); + test::InstallLib(f.GetBundleContext(), "TestBundleDSTBV1_1"); bundles = f.GetBundleContext().GetBundles(); bundleIter = std::find_if(bundles.begin(), bundles.end(), - [](cppmicroservices::Bundle const& b) { return (b.GetSymbolicName() == "TestBundleDSTOI6"); }); + [](cppmicroservices::Bundle const& b) { return (b.GetSymbolicName() == "TestBundleDSTBV1_1"); }); ASSERT_NO_THROW(bundleIter->Start()); @@ -112,7 +114,7 @@ TEST(TestBundleValidation, BundleValidationFailure) // a bundle validation function which returns false must cause the // service component not to be enabled - auto compDesc = dsRuntimeService->GetComponentDescriptionDTO(*bundleIter, "sample::ServiceComponent6"); + auto compDesc = dsRuntimeService->GetComponentDescriptionDTO(*bundleIter, "sample::ServiceComponentBV1_1"); ASSERT_FALSE(dsRuntimeService->IsComponentEnabled(compDesc)); // delayed components won't throw when enabled. they should throw on first @@ -124,23 +126,23 @@ TEST(TestBundleValidation, BundleValidationFailure) ASSERT_TRUE(svcRef); ASSERT_THROW(auto svcObj = f.GetBundleContext().GetService(svcRef), cppmicroservices::SecurityException); - compDesc = dsRuntimeService->GetComponentDescriptionDTO(*bundleIter, "sample::ServiceComponent6"); + compDesc = dsRuntimeService->GetComponentDescriptionDTO(*bundleIter, "sample::ServiceComponentBV1_1"); ASSERT_FALSE(dsRuntimeService->IsComponentEnabled(compDesc)); Interface1SvcReg.Unregister(); // test starting an immediate activation ds component with a service reference - test::InstallLib(f.GetBundleContext(), "TestBundleDSTOI7"); + test::InstallLib(f.GetBundleContext(), "TestBundleDSTBV1_2"); bundles = f.GetBundleContext().GetBundles(); bundleIter = std::find_if(bundles.begin(), bundles.end(), - [](cppmicroservices::Bundle const& b) { return (b.GetSymbolicName() == "TestBundleDSTOI7"); }); + [](cppmicroservices::Bundle const& b) { return (b.GetSymbolicName() == "TestBundleDSTBV1_2"); }); // on bundle start, the ds component will not be activated immediately since // it's service reference is unsatisfied. Registering a service which satisfies // the reference should cause an exception and no service should be registered. ASSERT_NO_THROW(bundleIter->Start()); - compDesc = dsRuntimeService->GetComponentDescriptionDTO(*bundleIter, "sample::ServiceComponent7"); + compDesc = dsRuntimeService->GetComponentDescriptionDTO(*bundleIter, "sample::ServiceComponentBV1_2"); ASSERT_TRUE(dsRuntimeService->IsComponentEnabled(compDesc)); // trying to enable the component will result in an exception from // the future since the ds component was immediately activated @@ -154,14 +156,14 @@ TEST(TestBundleValidation, BundleValidationFailure) Interface1SvcReg.Unregister(); // test starting a prototype scope service component - test::InstallLib(f.GetBundleContext(), "TestBundleDSTOI15"); + test::InstallLib(f.GetBundleContext(), "TestBundleDSTBV1_3"); bundles = f.GetBundleContext().GetBundles(); bundleIter = std::find_if(bundles.begin(), bundles.end(), - [](cppmicroservices::Bundle const& b) { return (b.GetSymbolicName() == "TestBundleDSTOI15"); }); + [](cppmicroservices::Bundle const& b) { return (b.GetSymbolicName() == "TestBundleDSTBV1_3"); }); ASSERT_NO_THROW(bundleIter->Start()); - compDesc = dsRuntimeService->GetComponentDescriptionDTO(*bundleIter, "sample::ServiceComponent15"); + compDesc = dsRuntimeService->GetComponentDescriptionDTO(*bundleIter, "sample::ServiceComponentBV1_3"); ASSERT_TRUE(dsRuntimeService->IsComponentEnabled(compDesc)); svcRef = f.GetBundleContext().GetServiceReference(); ASSERT_TRUE(svcRef); @@ -169,14 +171,14 @@ TEST(TestBundleValidation, BundleValidationFailure) ASSERT_FALSE(dsRuntimeService->IsComponentEnabled(compDesc)); // test starting a delayed activation ds component with a required configuration policy - test::InstallLib(f.GetBundleContext(), "TestBundleDSCA02"); + test::InstallLib(f.GetBundleContext(), "TestBundleDSTBV1_4"); bundles = f.GetBundleContext().GetBundles(); bundleIter = std::find_if(bundles.begin(), bundles.end(), - [](cppmicroservices::Bundle const& b) { return (b.GetSymbolicName() == "TestBundleDSCA02"); }); + [](cppmicroservices::Bundle const& b) { return (b.GetSymbolicName() == "TestBundleDSTBV1_4"); }); ASSERT_NO_THROW(bundleIter->Start()); - compDesc = dsRuntimeService->GetComponentDescriptionDTO(*bundleIter, "sample::ServiceComponentCA02"); + compDesc = dsRuntimeService->GetComponentDescriptionDTO(*bundleIter, "sample::ServiceComponentBV1_4"); ASSERT_TRUE(dsRuntimeService->IsComponentEnabled(compDesc)); auto cmBundlePath = test::GetConfigAdminRuntimePluginFilePath(); @@ -190,7 +192,7 @@ TEST(TestBundleValidation, BundleValidationFailure) = f.GetBundleContext().GetService(sCMSvcRef); ASSERT_TRUE(cmRuntimeService); - auto config = cmRuntimeService->GetConfiguration("sample::ServiceComponentCA02"); + auto config = cmRuntimeService->GetConfiguration("sample::ServiceComponentBV1_4"); cppmicroservices::AnyMap configObj(cppmicroservices::AnyMap::UNORDERED_MAP); configObj["foo"] = std::string("bar"); auto updateFuture = config->Update(configObj); @@ -204,21 +206,21 @@ TEST(TestBundleValidation, BundleValidationFailure) ASSERT_TRUE(svcRef); ASSERT_THROW(auto svcObj = f.GetBundleContext().GetService(svcRef), cppmicroservices::SecurityException); - compDesc = dsRuntimeService->GetComponentDescriptionDTO(*bundleIter, "sample::ServiceComponentCA02"); + compDesc = dsRuntimeService->GetComponentDescriptionDTO(*bundleIter, "sample::ServiceComponentBV1_4"); ASSERT_FALSE(dsRuntimeService->IsComponentEnabled(compDesc)); config->Remove().get(); // test starting an immediate activation ds component with a required configuration policy - test::InstallLib(f.GetBundleContext(), "TestBundleDSCA03"); + test::InstallLib(f.GetBundleContext(), "TestBundleDSTBV1_5"); bundles = f.GetBundleContext().GetBundles(); bundleIter = std::find_if(bundles.begin(), bundles.end(), - [](cppmicroservices::Bundle const& b) { return (b.GetSymbolicName() == "TestBundleDSCA03"); }); + [](cppmicroservices::Bundle const& b) { return (b.GetSymbolicName() == "TestBundleDSTBV1_5"); }); ASSERT_NO_THROW(bundleIter->Start()); - compDesc = dsRuntimeService->GetComponentDescriptionDTO(*bundleIter, "sample::ServiceComponentCA03"); + compDesc = dsRuntimeService->GetComponentDescriptionDTO(*bundleIter, "sample::ServiceComponentBV1_5"); ASSERT_TRUE(dsRuntimeService->IsComponentEnabled(compDesc)); - config = cmRuntimeService->GetConfiguration("sample::ServiceComponentCA03"); + config = cmRuntimeService->GetConfiguration("sample::ServiceComponentBV1_5"); configObj.clear(); configObj["foo"] = std::string("bar"); ASSERT_THROW(config->Update(configObj).get(), cppmicroservices::SecurityException); @@ -230,7 +232,7 @@ TEST(TestBundleValidation, BundleValidationFailure) ASSERT_TRUE(svcRef); ASSERT_THROW(auto svcObj = f.GetBundleContext().GetService(svcRef), cppmicroservices::SecurityException); - compDesc = dsRuntimeService->GetComponentDescriptionDTO(*bundleIter, "sample::ServiceComponentCA03"); + compDesc = dsRuntimeService->GetComponentDescriptionDTO(*bundleIter, "sample::ServiceComponentBV1_5"); ASSERT_FALSE(dsRuntimeService->IsComponentEnabled(compDesc)); f.Stop(); @@ -251,12 +253,12 @@ TEST(TestBundleValidation, BundleValidationSuccess) test::InstallAndStartDS(f.GetBundleContext()); - test::InstallLib(f.GetBundleContext(), "TestBundleDSTOI1"); + test::InstallLib(f.GetBundleContext(), "TestBundleDSTBV2"); auto bundles = f.GetBundleContext().GetBundles(); auto bundleIter = std::find_if(bundles.begin(), bundles.end(), - [](cppmicroservices::Bundle const& b) { return (b.GetSymbolicName() == "TestBundleDSTOI1"); }); + [](cppmicroservices::Bundle const& b) { return (b.GetSymbolicName() == "TestBundleDSTBV2"); }); ASSERT_NO_THROW(bundleIter->Start()); // a bundle validation function which returns true must cause the @@ -294,13 +296,13 @@ TEST(TestBundleValidation, BundleValidationFunctionException) &receivedSecondBundleValidationErrorEvent](cppmicroservices::FrameworkEvent const& evt) { if (evt.GetType() == cppmicroservices::FrameworkEvent::Type::FRAMEWORK_ERROR - && evt.GetBundle().GetSymbolicName() == "TestBundleDSTOI1") + && evt.GetBundle().GetSymbolicName() == "TestBundleDSTBV3") { receivedBundleValidationErrorEvent = true; } if (evt.GetType() == cppmicroservices::FrameworkEvent::Type::FRAMEWORK_ERROR - && evt.GetBundle().GetSymbolicName() == "TestBundleDSTOI6") + && evt.GetBundle().GetSymbolicName() == "TestBundleDSTBV3_1") { receivedSecondBundleValidationErrorEvent = true; } @@ -316,12 +318,12 @@ TEST(TestBundleValidation, BundleValidationFunctionException) sDSSvcRef); ASSERT_TRUE(dsRuntimeService); - test::InstallLib(f.GetBundleContext(), "TestBundleDSTOI1"); + test::InstallLib(f.GetBundleContext(), "TestBundleDSTBV3"); auto bundles = f.GetBundleContext().GetBundles(); auto bundleIter = std::find_if(bundles.begin(), bundles.end(), - [](cppmicroservices::Bundle const& b) { return (b.GetSymbolicName() == "TestBundleDSTOI1"); }); + [](cppmicroservices::Bundle const& b) { return (b.GetSymbolicName() == "TestBundleDSTBV3"); }); ASSERT_THROW(bundleIter->Start(), cppmicroservices::SecurityException); // a bundle validation function which returns false must cause the @@ -333,12 +335,12 @@ TEST(TestBundleValidation, BundleValidationFunctionException) // test starting a delayed activation ds component // in this case, starting the bundle does not cause the shared library to load // the shared library is loaded on the first call to "GetService" - test::InstallLib(f.GetBundleContext(), "TestBundleDSTOI6"); + test::InstallLib(f.GetBundleContext(), "TestBundleDSTBV3_1"); bundles = f.GetBundleContext().GetBundles(); bundleIter = std::find_if(bundles.begin(), bundles.end(), - [](cppmicroservices::Bundle const& b) { return (b.GetSymbolicName() == "TestBundleDSTOI6"); }); + [](cppmicroservices::Bundle const& b) { return (b.GetSymbolicName() == "TestBundleDSTBV3_1"); }); ASSERT_NO_THROW(bundleIter->Start()); @@ -358,7 +360,7 @@ TEST(TestBundleValidation, BundleValidationFunctionException) // a bundle validation function which returns false must cause the // service component not to be enabled - auto compDesc = dsRuntimeService->GetComponentDescriptionDTO(*bundleIter, "sample::ServiceComponent6"); + auto compDesc = dsRuntimeService->GetComponentDescriptionDTO(*bundleIter, "sample::ServiceComponentBV3_1"); ASSERT_FALSE(dsRuntimeService->IsComponentEnabled(compDesc)); ASSERT_TRUE(receivedSecondBundleValidationErrorEvent); @@ -366,3 +368,104 @@ TEST(TestBundleValidation, BundleValidationFunctionException) f.Stop(); f.WaitForStop(std::chrono::milliseconds::zero()); } + +/* +* Verify the absence of redundant bundle validation checks by installing a +* bundle with two services. +*/ +TEST(TestBundleValidation, BundleValidationMultipleSrc) +{ + using validationFuncType = std::function; + + // Check for multiple calls for the same bundle + validationFuncType validationFunc = [](cppmicroservices::Bundle const& bundle) -> bool { + static std::unordered_set validBundles; + auto const bundleLoc = bundle.GetLocation(); + + if (validBundles.count(bundleLoc) == 1) { + return false; + } + else { + validBundles.emplace(bundleLoc); + return true; + } + }; + cppmicroservices::FrameworkConfiguration frameworkConfiguration { + {cppmicroservices::Constants::FRAMEWORK_BUNDLE_VALIDATION_FUNC, validationFunc} + }; + auto framework = cppmicroservices::FrameworkFactory().NewFramework(std::move(frameworkConfiguration)); + + ASSERT_NO_THROW(framework.Start()); + + test::InstallAndStartDS(framework.GetBundleContext()); + test::InstallLib(framework.GetBundleContext(), "TestBundleDSTBV4"); + + auto bundles = framework.GetBundleContext().GetBundles(); + auto bundleIter + = std::find_if(bundles.begin(), + bundles.end(), + [](cppmicroservices::Bundle const& b) { return (b.GetSymbolicName() == "TestBundleDSTBV4"); }); + + ASSERT_NO_THROW(bundleIter->Start()); + ASSERT_EQ(bundleIter->GetState(), cppmicroservices::Bundle::State::STATE_ACTIVE); + + framework.Stop(); + framework.WaitForStop(std::chrono::milliseconds::zero()); +} + +/* +* Verfiy the absence of redundant bundle validation on reinstalling a bundle. +*/ +TEST(TestBundleValidation, BundleValidationReinstall) { + using validationFuncType = std::function; + + // Check for multiple calls for the same bundle + validationFuncType validationFunc = [](cppmicroservices::Bundle const& bundle) -> bool { + static std::unordered_set validBundles; + auto const bundleLoc = bundle.GetLocation(); + + if (validBundles.count(bundleLoc) == 1) { + return false; + } + else { + validBundles.emplace(bundleLoc); + return true; + } + }; + cppmicroservices::FrameworkConfiguration frameworkConfiguration { + {cppmicroservices::Constants::FRAMEWORK_BUNDLE_VALIDATION_FUNC, validationFunc} + }; + auto framework = cppmicroservices::FrameworkFactory().NewFramework(std::move(frameworkConfiguration)); + + ASSERT_NO_THROW(framework.Start()); + + test::InstallAndStartDS(framework.GetBundleContext()); + test::InstallLib(framework.GetBundleContext(), "TestBundleDSTBV5"); + + auto bundles = framework.GetBundleContext().GetBundles(); + auto bundleIter + = std::find_if(bundles.begin(), + bundles.end(), + [](cppmicroservices::Bundle const& b) { return (b.GetSymbolicName() == "TestBundleDSTBV5"); }); + + ASSERT_NO_THROW(bundleIter->Start()); + ASSERT_EQ(bundleIter->GetState(), cppmicroservices::Bundle::State::STATE_ACTIVE); + // Uninstalling Bundle + ASSERT_NO_THROW(bundleIter->Uninstall()); + ASSERT_EQ(bundleIter->GetState(), cppmicroservices::Bundle::State::STATE_UNINSTALLED); + + test::InstallLib(framework.GetBundleContext(), "TestBundleDSTBV5"); + + bundles = framework.GetBundleContext().GetBundles(); + bundleIter + = std::find_if(bundles.begin(), + bundles.end(), + [](cppmicroservices::Bundle const& b) { return (b.GetSymbolicName() == "TestBundleDSTBV5"); }); + + // Reinstalling Bundle + ASSERT_NO_THROW(bundleIter->Start()); + ASSERT_EQ(bundleIter->GetState(), cppmicroservices::Bundle::State::STATE_ACTIVE); + + framework.Stop(); + framework.WaitForStop(std::chrono::milliseconds::zero()); +} diff --git a/compendium/DeclarativeServices/test/gtest/TestSCRExtensionRegistry.cpp b/compendium/DeclarativeServices/test/gtest/TestSCRExtensionRegistry.cpp index 19a911804..d595a82ea 100644 --- a/compendium/DeclarativeServices/test/gtest/TestSCRExtensionRegistry.cpp +++ b/compendium/DeclarativeServices/test/gtest/TestSCRExtensionRegistry.cpp @@ -30,6 +30,7 @@ #include #include #include +#include "ConcurrencyTestUtil.hpp" #include "gmock/gmock.h" #include "Mocks.hpp" @@ -73,66 +74,6 @@ namespace cppmicroservices return framework; } - void - ConcurrentInvoke(std::vector allBundles, bool addOperation) - { - std::promise go; - std::shared_future ready(go.get_future()); - int numCalls = allBundles.size(); - std::vector> readies(numCalls); - std::vector> bundle_result(numCalls); - try - { - if (addOperation) - { - for (int i = 0; i < numCalls; i++) - { - - bundle_result[i] - = std::async(std::launch::async, - [&readies, &ready, &allBundles, this, i]() - { - readies[i].set_value(); - ready.wait(); - auto ba = std::make_shared(allBundles[i], - this->fakeRegistry, - this->logger, - this->notifier); - extRegistry->Add(allBundles[i].GetBundleId(), ba); - }); - } - } - else // remove operation - { - for (int i = 0; i < numCalls; i++) - { - bundle_result[i] = std::async(std::launch::async, - [&readies, &ready, &allBundles, this, i]() - { - readies[i].set_value(); - ready.wait(); - this->extRegistry->Remove(allBundles[i].GetBundleId()); - }); - } - } - - for (int i = 0; i < numCalls; i++) - { - readies[i].get_future().wait(); - } - go.set_value(); - for (int i = 0; i < numCalls; i++) - { - bundle_result[i].wait(); - } - } - catch (std::exception const& e) - { - EXPECT_TRUE(false) << "Error: exception received ... " << e.what() << std::endl; - go.set_value(); - throw std::current_exception(); - } - } protected: cppmicroservices::Framework framework; std::shared_ptr fakeRegistry; @@ -185,78 +126,71 @@ namespace cppmicroservices asyncWorkService->StopTracking(); fakeRegistry->Clear(); } - // Test to test concurrent additions of bundle extensions to the SCRExtensionRegistry and + // Test concurrent additions of bundle extensions to the SCRExtensionRegistry and // concurrent removals. TEST_F(SCRExtensionRegistryTest, VerifyConcurrentAddRemove) { - int count = 4; - test::InstallAndStartBundle(GetFramework().GetBundleContext(), "TestBundleDSTOI1"); - test::InstallAndStartBundle(GetFramework().GetBundleContext(), "TestBundleDSTOI2"); - test::InstallAndStartBundle(GetFramework().GetBundleContext(), "TestBundleDSTOI3"); - test::InstallAndStartBundle(GetFramework().GetBundleContext(), "TestBundleDSTOI5"); - + constexpr int fakeBundleCount{100}; auto bundleContext = GetFramework().GetBundleContext(); - auto allBundles = bundleContext.GetBundles(); - ASSERT_TRUE(allBundles.size() > count) << "All bundles not installed."; + // This test doesn't require unique or even functional Bundle objects. Use the + // same bundle object for the purpose of testing thread safety of the SCRExtensionRegistry + // methods. + const auto bundle = test::InstallAndStartBundle(bundleContext, "TestBundleDSTOI1"); // Add a bundle extension object for each bundle in the allBundles vector to the // extension registry - ConcurrentInvoke(allBundles, true); - for (auto const& item : allBundles) { - ASSERT_TRUE(extRegistry->Find(item.GetBundleId())) - << "bundle " << item.GetSymbolicName() << " not found."; - } + std::function addFunc = [&]() -> bool { + for(int fakeBundleId = 0; fakeBundleId <= fakeBundleCount; ++fakeBundleId) + { + extRegistry->Add(fakeBundleId, std::make_shared(bundle, fakeRegistry, logger, notifier)); + } + return true; + }; + + ASSERT_NO_THROW((void)ConcurrentInvoke(std::move(addFunc))); // Remove the bundle extension for all bundles in the allBundles vector from // the extension registry. - ConcurrentInvoke(allBundles, false); - for (auto const& item : allBundles) - { - ASSERT_TRUE(!extRegistry->Find(item.GetBundleId())) - << "bundle " << item.GetSymbolicName() << " should have been removed."; - } + std::function removeFunc = [&]() -> bool { + for(int fakeBundleId = 0; fakeBundleId <= fakeBundleCount; ++fakeBundleId) + { + extRegistry->Remove(fakeBundleId); + } + return true; + }; + + ASSERT_NO_THROW((void)ConcurrentInvoke(std::move(removeFunc))); + asyncWorkService->StopTracking(); fakeRegistry->Clear(); } - //Mock SCRExtensionRegistry class used by the testException class - class MockSCRExtensionRegistry : public cppmicroservices::scrimpl::SCRExtensionRegistry - { - public: - MockSCRExtensionRegistry(std::shared_ptr const& logger) - : cppmicroservices::scrimpl::SCRExtensionRegistry (logger) - { - } - virtual ~MockSCRExtensionRegistry() = default; - MOCK_METHOD1(Find, std::shared_ptr(long bundleId)); - }; - // Tests the CreateFactoryComponent method of the ConfigurationNotifier class. If the bundle extension // cannot be found in the SCRExtensionRegistry when creating a factory component then an // exception will be logged. - TEST_F(SCRExtensionRegistryTest, testException) + TEST_F(SCRExtensionRegistryTest, testCreateFactoryComponentFailure) { auto bundle = test::InstallAndStartBundle(GetFramework().GetBundleContext(), "TestBundleDSTOI1"); ASSERT_TRUE(static_cast(bundle)); - auto mockExtRegistry = std::make_shared(logger); auto mockLogger = std::make_shared(); auto notifier = std::make_shared(GetFramework().GetBundleContext(), mockLogger, asyncWorkService, - mockExtRegistry); - - EXPECT_CALL(*mockExtRegistry, Find (bundle.GetBundleId())).Times(1).WillOnce(::testing::Return(nullptr)); - EXPECT_CALL(*(mockLogger.get()), - Log(cppmicroservices::logservice::SeverityLevel::LOG_ERROR, testing::_, testing::_)) + extRegistry); + + EXPECT_CALL(*mockLogger, Log(cppmicroservices::logservice::SeverityLevel::LOG_ERROR, testing::_)) .Times(1); - auto mockMetadata = std::make_shared(); - + auto mockMetadata = std::make_shared(); std::shared_ptr compConfigImpl - = std::make_shared(mockMetadata, bundle, fakeRegistry, logger, notifier); + = std::make_shared(mockMetadata, bundle, fakeRegistry, mockLogger, notifier); std::string pid { 123 }; - EXPECT_NO_THROW({ notifier->CreateFactoryComponent(pid, compConfigImpl); }); + EXPECT_NO_THROW({ notifier->CreateFactoryComponent(pid, compConfigImpl); }); + + asyncWorkService->StopTracking(); + fakeRegistry->Clear(); + compConfigImpl->Deactivate(); } } // namespace scrimpl } // namespace cppmicroservices diff --git a/compendium/DeclarativeServices/test/gtest/TestServiceComponentRuntimeImpl.cpp b/compendium/DeclarativeServices/test/gtest/TestServiceComponentRuntimeImpl.cpp index 37f184b0e..d245b9008 100644 --- a/compendium/DeclarativeServices/test/gtest/TestServiceComponentRuntimeImpl.cpp +++ b/compendium/DeclarativeServices/test/gtest/TestServiceComponentRuntimeImpl.cpp @@ -317,7 +317,8 @@ namespace cppmicroservices auto iMap = std::make_shared(); auto obj = std::make_shared(); iMap->insert(std::make_pair("double", std::static_pointer_cast(obj))); - fc.RegisterService(iMap); + auto reg = fc.RegisterService(iMap); + US_UNUSED(reg); auto sRef = fc.GetServiceReference("double"); EXPECT_TRUE(static_cast(sRef)); diff --git a/compendium/ServiceComponent/include/cppmicroservices/servicecomponent/detail/ComponentInstanceImpl.hpp b/compendium/ServiceComponent/include/cppmicroservices/servicecomponent/detail/ComponentInstanceImpl.hpp index 228f1a717..4abc2398d 100644 --- a/compendium/ServiceComponent/include/cppmicroservices/servicecomponent/detail/ComponentInstanceImpl.hpp +++ b/compendium/ServiceComponent/include/cppmicroservices/servicecomponent/detail/ComponentInstanceImpl.hpp @@ -455,17 +455,17 @@ namespace cppmicroservices class I = Injection, class InjectionTrue = typename std::enable_if::type, class HasNoConstructorWithReferences = typename std::enable_if< - std::is_constructible const&...>::value + std::is_constructible::value == false>::type, class HasNoConstructorWithRefAndConfig = typename std::enable_if< std::is_constructible const&, - std::shared_ptr const&...>::value + CtorInjectedRefs const&...>::value == false>::type> std::shared_ptr DoCreate(bool const&) { - static_assert(std::is_constructible const&...>::value, + static_assert(std::is_constructible::value, "An appropriate constructor was not found " "and/or the service implementation does not implement all of the " "service interface's methods. A constructor with service reference " @@ -481,11 +481,11 @@ namespace cppmicroservices class I = Injection, class InjectionTrue = typename std::enable_if::type, class HasConstructorWithReferences = typename std::enable_if< - std::is_constructible const&...>::value>::type> + std::is_constructible::value>::type> std::shared_ptr DoCreate(bool& injected) { - std::tuple...> depObjs = GetAllDependencies( + std::tuple depObjs = GetAllDependencies( std::make_index_sequence>::value> {}); std::shared_ptr implObj = call_make_shared_with_tuple( depObjs, @@ -503,11 +503,11 @@ namespace cppmicroservices class HasConstructorWithRefAndConfig = typename std::enable_if< std::is_constructible const&, - std::shared_ptr const&...>::value>::type> + CtorInjectedRefs const&...>::value>::type> std::shared_ptr DoCreate(bool& injected, bool = true) { - std::tuple...> depObjs = GetAllDependencies( + std::tuple depObjs = GetAllDependencies( std::make_index_sequence>::value> {}); auto props = std::make_shared(this->mContext->GetProperties()); std::shared_ptr implObj = call_make_shared_with_tuple_and_props( @@ -521,7 +521,7 @@ namespace cppmicroservices template std::shared_ptr - call_make_shared_with_tuple(std::tuple const&...> const& tuple, + call_make_shared_with_tuple(std::tuple const& tuple, std::index_sequence) { return std::make_shared(std::get(tuple)...); @@ -531,14 +531,26 @@ namespace cppmicroservices std::shared_ptr call_make_shared_with_tuple_and_props( std::shared_ptr const& props, - std::tuple const&...> const& tuple, + std::tuple const& tuple, std::index_sequence) { return std::make_shared(props, std::get(tuple)...); } + // Type detector to see if given template parameter is std::vector type (multiple cardinality reference) + // or not (unary cardinality reference) + template + struct is_vector_type { + static const bool value = false; + }; + + template + struct is_vector_type > { + static const bool value = true; + }; + template - std::tuple...> + std::tuple GetAllDependencies(std::index_sequence) { return std::make_tuple( @@ -546,11 +558,25 @@ namespace cppmicroservices std::get(mStaticRefNames))...); } - template - std::shared_ptr - GetDependency(std::string const& name) + template ::value == true>::type + > + R GetDependency(std::string const& name) + { + // Overload to be invoked for references using multiple cardinality + using VectorElementType = typename R::value_type; + using RefType = typename VectorElementType::element_type; + return this->mContext->template LocateServices(name); + } + + template ::value == false>::type + > + R GetDependency(std::string const& name, bool = true) { - return this->mContext->template LocateService(name); + // Overload to be used for references using unary cardinality + using RefType = typename R::element_type; + return this->mContext->template LocateService(name); } private: diff --git a/compendium/ServiceComponent/test/CMakeLists.txt b/compendium/ServiceComponent/test/CMakeLists.txt index ee10c32a0..2d597f863 100644 --- a/compendium/ServiceComponent/test/CMakeLists.txt +++ b/compendium/ServiceComponent/test/CMakeLists.txt @@ -1,7 +1,6 @@ #----------------------------------------------------------------------------- # Build and run the GTest Suite of tests #----------------------------------------------------------------------------- - set(us_servicecomponent_test_exe_name usServiceComponentTests) include_directories( @@ -20,11 +19,6 @@ endif() # Add test source files #----------------------------------------------------------------------------- set(_servicecomponent_tests - TestCompInst_InvalidInheritance.cpp - TestCompInst_NoDepsNoDefaultCtor.cpp - TestCompInst_NoInjectNoDefaultCtor.cpp - TestCompInst_RefCount.cpp - TestCompInst_RefOrder.cpp TestComponentInstance.cpp suite_registration.cpp ) @@ -112,3 +106,47 @@ if (WIN32 AND US_USE_SYSTEM_GTEST) $) endforeach(lib_fullpath) endif() + +#Compile-only tests addition +#Adding executables for compile-only test files +add_executable(usServiceComponentRefOrderTest TestCompInst_RefOrder.cpp) +add_executable(usServiceComponentRefOrderMultipleCardinalityTest TestCompInst_RefOrderMultipleCardinality.cpp) +add_executable(usServiceComponentInvalidInheritanceTest TestCompInst_InvalidInheritance.cpp) +add_executable(usServiceComponentNoDepsNoDefaultCtorTest TestCompInst_NoDepsNoDefaultCtor.cpp) +add_executable(usServiceComponentNoDefaultCtorTest TestCompInst_NoInjectNoDefaultCtor.cpp) +add_executable(usServiceComponentRefCountTest TestCompInst_RefCount.cpp) + +set(compile_only_exe_names usServiceComponentRefOrderTest usServiceComponentRefOrderMultipleCardinalityTest usServiceComponentInvalidInheritanceTest usServiceComponentNoDepsNoDefaultCtorTest usServiceComponentNoDefaultCtorTest usServiceComponentRefCountTest) + +foreach(exe_name ${compile_only_exe_names}) + + if (US_COMPILER_MSVC AND BUILD_SHARED_LIBS) + target_compile_options(${exe_name} PRIVATE -DGTEST_LINKED_AS_SHARED_LIBRARY) + endif() + + target_link_libraries(${exe_name} + PRIVATE + ${GTEST_BOTH_LIBRARIES} + ${GMOCK_BOTH_LIBRARIES} + CppMicroServices + usServiceComponent + ) + + #Excluding compile-only tests from default build to avoid build failure + set_target_properties(${exe_name} PROPERTIES EXCLUDE_FROM_ALL TRUE EXCLUDE_FROM_DEFAULT_BUILD TRUE) + + #For compile-only tests we add a test with Cmake command to build the target only as we expect a build failure + add_test(NAME ${exe_name} + COMMAND ${CMAKE_COMMAND} --build . --config ${CMAKE_BUILD_TYPE} --target ${exe_name} + WORKING_DIRECTORY ${CppMicroServices_BINARY_DIR} + ) + +endforeach() + +#Set test properties for compile-only tests to check if compilation fails and corresponding errors are generated in the log +set_tests_properties(usServiceComponentRefOrderTest PROPERTIES PASS_REGULAR_EXPRESSION ".*An appropriate constructor was not found and/or the service implementation does not implement all of the service interface's methods. A constructor with service reference input parameters or a constructor with an AnyMap input parameter for configuration properties and service reference input parameters is required when inject-references is true.*") +set_tests_properties(usServiceComponentRefOrderMultipleCardinalityTest PROPERTIES PASS_REGULAR_EXPRESSION ".*An appropriate constructor was not found and/or the service implementation does not implement all of the service interface's methods. A constructor with service reference input parameters or a constructor with an AnyMap input parameter for configuration properties and service reference input parameters is required when inject-references is true.*") +set_tests_properties(usServiceComponentInvalidInheritanceTest PROPERTIES PASS_REGULAR_EXPRESSION ".*['static_cast' cannot convert].* [Types pointed to are unrelated] .* [conversion requires reinterpret_cast, C-style cast or function-style cast] .*") +set_tests_properties(usServiceComponentNoDepsNoDefaultCtorTest PROPERTIES PASS_REGULAR_EXPRESSION ".*An appropriate constructor was not found and/or the service implementation does not implement all of the service interface's methods. A default constructor or a constructor with an AnyMap input parameter for the configuration properties is required when inject-references is false..*") +set_tests_properties(usServiceComponentNoDefaultCtorTest PROPERTIES PASS_REGULAR_EXPRESSION ".*An appropriate constructor was not found and/or the service implementation does not implement all of the service interface's methods. A constructor with service reference input parameters or a constructor with an AnyMap input parameter for configuration properties and service reference input parameters is required when inject-references is true.*") +set_tests_properties(usServiceComponentRefCountTest PROPERTIES PASS_REGULAR_EXPRESSION ".*An appropriate constructor was not found and/or the service implementation does not implement all of the service interface's methods. A constructor with service reference input parameters or a constructor with an AnyMap input parameter for configuration properties and service reference input parameters is required when inject-references is true.*") diff --git a/compendium/ServiceComponent/test/TestCompInst_InvalidInheritance.cpp b/compendium/ServiceComponent/test/TestCompInst_InvalidInheritance.cpp index a40ca3ebd..78992c7be 100644 --- a/compendium/ServiceComponent/test/TestCompInst_InvalidInheritance.cpp +++ b/compendium/ServiceComponent/test/TestCompInst_InvalidInheritance.cpp @@ -19,7 +19,7 @@ limitations under the License. =============================================================================*/ -#if NEVER + # include # include # include @@ -53,7 +53,7 @@ namespace * This test point is used to verify a compile error is generated when a service component * description specifies an interface that is not implemented by the implementation class * - * @todo Automate this test point. Currently interactive is the only way to verify compilation failures. + * CMake tests will build this file to catch the expected compile time error */ TEST(ComponentInstance, ValidateInheritance) { @@ -67,5 +67,3 @@ main(int argc, char** argv) ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } - -#endif diff --git a/compendium/ServiceComponent/test/TestCompInst_NoDepsNoDefaultCtor.cpp b/compendium/ServiceComponent/test/TestCompInst_NoDepsNoDefaultCtor.cpp index 0fe14ee2c..cfaf8119f 100644 --- a/compendium/ServiceComponent/test/TestCompInst_NoDepsNoDefaultCtor.cpp +++ b/compendium/ServiceComponent/test/TestCompInst_NoDepsNoDefaultCtor.cpp @@ -19,7 +19,7 @@ limitations under the License. =============================================================================*/ -#if NEVER + # include # include # include @@ -48,7 +48,7 @@ namespace * This test point is used to verify a compile error is generated when a service component * does not have any dependencies and it does not provide a default constructor. * - * @todo Automate this test point. Currently interactive is the only way to verify compilation failures. + * CMake tests will build this file to catch the expected compile time error */ TEST(ComponentInstance, CheckNoDefaultCtor) { @@ -62,4 +62,3 @@ main(int argc, char** argv) ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } -#endif diff --git a/compendium/ServiceComponent/test/TestCompInst_NoInjectNoDefaultCtor.cpp b/compendium/ServiceComponent/test/TestCompInst_NoInjectNoDefaultCtor.cpp index a2c085045..8fdc937d5 100644 --- a/compendium/ServiceComponent/test/TestCompInst_NoInjectNoDefaultCtor.cpp +++ b/compendium/ServiceComponent/test/TestCompInst_NoInjectNoDefaultCtor.cpp @@ -19,7 +19,7 @@ limitations under the License. =============================================================================*/ -#if NEVER + # include # include # include @@ -52,14 +52,14 @@ namespace * This test point is used to verify a compile error is generated when a service component * has dependencies, opted out of constructor injection and it does not provide a default constructor. * - * @todo Automate this test point. Currently interactive is the only way to verify compilation failures. + * CMake tests will build this file to catch the expected compile time error */ TEST(ComponentInstance, CheckNoDefaultCtorNoInjection) { ComponentInstanceImpl, std::false_type, - ServiceDependency> + std::shared_ptr> compInstance; // compile error } } // namespace @@ -70,4 +70,3 @@ main(int argc, char** argv) ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } -#endif diff --git a/compendium/ServiceComponent/test/TestCompInst_RefCount.cpp b/compendium/ServiceComponent/test/TestCompInst_RefCount.cpp index 898eb73b4..d2a0947b1 100644 --- a/compendium/ServiceComponent/test/TestCompInst_RefCount.cpp +++ b/compendium/ServiceComponent/test/TestCompInst_RefCount.cpp @@ -19,7 +19,7 @@ limitations under the License. =============================================================================*/ -#if NEVER + # include # include # include @@ -97,7 +97,7 @@ namespace * the service component description does not match the dependencies in the service component implementation * class constructor. * - * @todo Automate this test point. Currently interactive is the only way to verify compilation failures. + * CMake tests will build this file to catch the expected compile time error */ TEST(ComponentInstance, VerifyDependencyCount) { @@ -111,10 +111,10 @@ namespace ComponentInstanceImpl, std::true_type, - ServiceDependency1, - ServiceDependency2, - ServiceDependency2> - compInstance(binders); // compile error + std::shared_ptr, + std::shared_ptr, + std::shared_ptr> + compInstance({ "foo", "bar", "bar2" }, binders); // compile error } } // namespace @@ -124,4 +124,3 @@ main(int argc, char** argv) ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } -#endif diff --git a/compendium/ServiceComponent/test/TestCompInst_RefOrder.cpp b/compendium/ServiceComponent/test/TestCompInst_RefOrder.cpp index 29637405a..4c7fe342f 100644 --- a/compendium/ServiceComponent/test/TestCompInst_RefOrder.cpp +++ b/compendium/ServiceComponent/test/TestCompInst_RefOrder.cpp @@ -35,7 +35,6 @@ using cppmicroservices::service::component::detail::ComponentInstanceImpl; using cppmicroservices::service::component::detail::DynamicBinder; using cppmicroservices::service::component::detail::StaticBinder; -#if NEVER namespace { @@ -47,7 +46,7 @@ namespace { }; - // Test class to simulate a servcie component + // Test class to simulate a service component class TestServiceImpl1 final { public: @@ -94,11 +93,11 @@ namespace }; /** - * This test point is used to verify a compile error is generated when the order of dependencies specified in + * This test points are used to verify a compile error is generated when the order of dependencies specified in * the service component description does not match the dependencies in the service component implementation * class constructor. * - * @todo Automate this test point. Currently interactive is the only way to verify compilation failures. + * CMake tests will build this file to catch the expected compile time error */ TEST(ComponentInstance, VerifyDependencyOrder) { @@ -111,9 +110,9 @@ namespace ComponentInstanceImpl, std::true_type, - ServiceDependency2, - ServiceDependency1> - compInstance(binders); // compile error + std::shared_ptr, + std::shared_ptr> + compInstance({ "bar", "foo" }, binders); // compile error } } // namespace @@ -123,4 +122,3 @@ main(int argc, char** argv) ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } -#endif diff --git a/compendium/ServiceComponent/test/TestCompInst_RefOrderMultipleCardinality.cpp b/compendium/ServiceComponent/test/TestCompInst_RefOrderMultipleCardinality.cpp new file mode 100644 index 000000000..fc6d8a3fb --- /dev/null +++ b/compendium/ServiceComponent/test/TestCompInst_RefOrderMultipleCardinality.cpp @@ -0,0 +1,138 @@ +/*============================================================================= + + Library: CppMicroServices + + Copyright (c) The CppMicroServices developers. See the COPYRIGHT + file at the top-level directory of this distribution and at + https://github.com/CppMicroServices/CppMicroServices/COPYRIGHT . + + 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. + + =============================================================================*/ + +#include +#include +#include +#include + +#include "gtest/gtest.h" + +#include "cppmicroservices/servicecomponent/detail/ComponentInstanceImpl.hpp" +#include + +using cppmicroservices::service::component::detail::Binder; +using cppmicroservices::service::component::detail::ComponentInstanceImpl; +using cppmicroservices::service::component::detail::DynamicBinder; +using cppmicroservices::service::component::detail::StaticBinder; + +namespace +{ + + // dummy types used for testing + struct ServiceDependency1 + { + }; + struct ServiceDependency2 + { + }; + struct ServiceDependency3 + { + }; + + // Test class to simulate a service component + class TestServiceImpl1 final + { + public: + TestServiceImpl1() = default; + + TestServiceImpl1(std::shared_ptr const& f, + std::shared_ptr const& b, + std::vector> const& c) + : foo(f) + , bar(b) + , baz(c) + { + } + + virtual ~TestServiceImpl1() = default; + + void + BindFoo(std::shared_ptr const& f) + { + foo = f; + } + + void + UnbindFoo(std::shared_ptr const& f) + { + if (foo == f) + { + foo = nullptr; + } + } + + std::shared_ptr + GetFoo() const + { + return foo; + } + std::shared_ptr + GetBar() const + { + return bar; + } + std::vector> + GetBaz() const + { + return baz; + } + + private: + std::shared_ptr foo; // dynamic dependency - can change during the lifetime of this object + const std::shared_ptr + bar; // static dependency - does not change during the lifetime of this object + const std::vector> baz; // static dependency with multiple cardinality + }; + + /** + * This test points are used to verify a compile error is generated when the order of dependencies specified in + * the service component description does not match the dependencies in the service component implementation + * class constructor. + * + * CMake tests will build this file to catch the expected compile time error + */ + + TEST(ComponentInstance, VerifyDependencyOrder_MultipleCardinality) + { + std::vector>> binders; + binders.push_back(std::make_shared>("bar")); + binders.push_back( + std::make_shared>("foo", + &TestServiceImpl1::BindFoo, + &TestServiceImpl1::UnbindFoo)); + ComponentInstanceImpl, + std::true_type, + std::shared_ptr, + std::vector>, + std::shared_ptr> + compInstance({ "bar", "baz", "foo" }, binders); // compile error + } +} // namespace + +int +main(int argc, char** argv) +{ + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/compendium/ServiceComponent/test/TestComponentInstance.cpp b/compendium/ServiceComponent/test/TestComponentInstance.cpp index 1b20810e7..410b71e5f 100644 --- a/compendium/ServiceComponent/test/TestComponentInstance.cpp +++ b/compendium/ServiceComponent/test/TestComponentInstance.cpp @@ -56,6 +56,9 @@ namespace struct TestServiceInterface1 { }; + struct ServiceDependency3 + { + }; struct TestServiceInterface2 { }; @@ -63,7 +66,7 @@ namespace { }; - // Test class to simulate a servcie component + // Test class to simulate a service component // TODO: Use Mock to add more behavior verification class TestServiceImpl1 final : public TestServiceInterface1 @@ -146,6 +149,85 @@ namespace bool activated; }; + // Test class to simulate a service component with service dependencies using multiple cardinality + class TestServiceImpl2 + { + public: + TestServiceImpl2(std::vector> const& d1, + std::shared_ptr const& d2, + std::vector> const& d3) + : dep1Refs(d1) + , dep2(d2) + , dep3Refs(d3) + { + } + + TestServiceImpl2(std::vector> const& d1, + std::shared_ptr const& d2) + : dep1Refs(d1) + , dep2(d2) + , dep3Refs({}) + { + } + + virtual ~TestServiceImpl2() {} + + void + BindDep3(std::shared_ptr const& d3) + { + if (std::find(dep3Refs.begin(), dep3Refs.end(), d3) == dep3Refs.end()) + { + dep3Refs.push_back(d3); + } + } + + void + UnbindDep3(std::shared_ptr const& d3) + { + auto pos = std::find(dep3Refs.begin(), dep3Refs.end(), d3); + if (pos != dep3Refs.end()) + { + *pos = nullptr; + dep3Refs.erase(pos); + } + } + + void + Activate(std::shared_ptr const&) + { + // activated = true; + } + + void + Deactivate(std::shared_ptr const&) + { + // activated = false; + } + + std::vector> + GetDep1() const + { + return dep1Refs; + } + + std::shared_ptr + GetDep2() const + { + return dep2; + } + + std::vector> + GetDep3() const + { + return dep3Refs; + } + + private: + const std::vector> dep1Refs; // static dependency with multiple cardinality + const std::shared_ptr dep2; // static dependency with unary cardinality + std::vector> dep3Refs; // dynamic dependency with multiple cardinality + }; + /** * This class is used to mock the behavior of a ComponentContext object * created by the declarative services runtime implementation. @@ -302,9 +384,11 @@ namespace auto reg = fc.RegisterService(std::make_shared()); auto reg1 = fc.RegisterService(std::make_shared()); - ComponentInstanceImpl, ServiceDependency1, ServiceDependency2> compInstance( - { ("foo"), ("bar") }, - {}); + ComponentInstanceImpl, + std::shared_ptr, + std::shared_ptr> + compInstance({ ("foo"), ("bar") }, {}); auto iMap = compInstance.GetInterfaceMap(); ASSERT_FALSE(iMap); @@ -370,7 +454,9 @@ namespace &TestServiceImpl1::BindFoo, &TestServiceImpl1::UnbindFoo)); - ComponentInstanceImpl, ServiceDependency2> compInstance({ ("bar") }, binders); + ComponentInstanceImpl, std::shared_ptr> compInstance( + { ("bar") }, + binders); auto iMap = compInstance.GetInterfaceMap(); ASSERT_FALSE(iMap); ASSERT_EQ(compInstance.GetInstance(), nullptr); @@ -468,4 +554,195 @@ namespace compInstance.Deactivate(); ASSERT_FALSE(compObj->IsActivated()); } + + TEST(ComponentInstanceImpl, VerifyWithStaticDependenciesMultipleCardinality) + { + auto f = cppmicroservices::FrameworkFactory().NewFramework(); + f.Start(); + auto fc = f.GetBundleContext(); + auto reg = fc.RegisterService(std::make_shared()); + auto reg1 = fc.RegisterService(std::make_shared()); + auto reg2 = fc.RegisterService(std::make_shared()); + + ComponentInstanceImpl, + std::vector>, + std::shared_ptr, + std::vector>> + compInstance({ ("dep1"), ("dep2"), ("dep3") }, {}); + auto iMap = compInstance.GetInterfaceMap(); + ASSERT_FALSE(iMap); + + auto mockContext = std::make_shared(); + auto locateService = [&fc](std::string const& type) -> std::shared_ptr + { + auto sRef = fc.GetServiceReference(type); + if (sRef) + { + auto serv = fc.GetService(sRef); + return serv->at(type); + } + else + { + return nullptr; + } + }; + + auto locateServices = [&fc](std::string const& type) -> std::vector> + { + auto sRef = fc.GetServiceReference(type); + if (sRef) + { + auto serv = fc.GetService(sRef); + return { serv->at(type) }; + } + else + { + return {}; + } + }; + + EXPECT_CALL(*(mockContext.get()), GetBundleContext()).WillRepeatedly(testing::Invoke([&fc]() { return fc; })); + + EXPECT_CALL(*(mockContext.get()), LocateServices("dep1", us_service_interface_iid())) + .Times(1) + .WillRepeatedly(testing::WithArg<1>(testing::Invoke( + locateServices))); // ensure the mock context received a call to LocateService for dependency dep1 + + EXPECT_CALL(*(mockContext.get()), LocateService("dep2", us_service_interface_iid())) + .Times(1) + .WillRepeatedly(testing::WithArg<1>(testing::Invoke( + locateService))); // ensure the mock context received a call to LocateService for dependency dep2 + + EXPECT_CALL(*(mockContext.get()), LocateServices("dep3", us_service_interface_iid())) + .Times(1) + .WillRepeatedly(testing::WithArg<1>(testing::Invoke( + locateServices))); // ensure the mock context received a call to LocateService for dependency dep3 + + compInstance.CreateInstance(mockContext); + compInstance.BindReferences(mockContext); + auto compObj = compInstance.GetInstance(); + ASSERT_TRUE(compObj); + // ensure the dependencies are bound when the object is constructed + ASSERT_TRUE(compObj->GetDep1().size() > 0); + ASSERT_TRUE(compObj->GetDep2()); + ASSERT_TRUE(compObj->GetDep3().size() > 0); + + EXPECT_THROW(compInstance.InvokeBindMethod("dep1", fc.GetServiceReference()), + std::out_of_range); + EXPECT_THROW(compInstance.InvokeBindMethod("dep2", fc.GetServiceReference()), + std::out_of_range); + EXPECT_THROW(compInstance.InvokeBindMethod("dep3", fc.GetServiceReference()), + std::out_of_range); + EXPECT_THROW(compInstance.InvokeUnbindMethod("dep1", fc.GetServiceReference()), + std::out_of_range); + EXPECT_THROW(compInstance.InvokeUnbindMethod("dep2", fc.GetServiceReference()), + std::out_of_range); + EXPECT_THROW(compInstance.InvokeUnbindMethod("dep3", fc.GetServiceReference()), + std::out_of_range); + + f.Stop(); + f.WaitForStop(std::chrono::milliseconds::zero()); + } + + TEST(ComponentInstanceImpl, VerifyWithDynamicDependenciesMultipleCardinality) + { + auto f = cppmicroservices::FrameworkFactory().NewFramework(); + f.Start(); + auto fc = f.GetBundleContext(); + auto reg = fc.RegisterService(std::make_shared()); + auto reg1 = fc.RegisterService(std::make_shared()); + auto reg2 = fc.RegisterService(std::make_shared()); + + std::vector>> binders; + binders.push_back( + std::make_shared>("dep3", + &TestServiceImpl2::BindDep3, + &TestServiceImpl2::UnbindDep3)); + + ComponentInstanceImpl, + std::vector>, + std::shared_ptr> + compInstance({ ("dep1"), ("dep2") }, binders); + auto iMap = compInstance.GetInterfaceMap(); + ASSERT_FALSE(iMap); + ASSERT_EQ(compInstance.GetInstance(), nullptr); + + // create a mock context and setup expectations on the context + auto mockContext = std::make_shared(); + auto locateService = [&fc](std::string const& type) -> std::shared_ptr + { + auto sRef = fc.GetServiceReference(type); + if (sRef) + { + auto serv = fc.GetService(sRef); + return serv->at(type); + } + else + { + return nullptr; + } + }; + + auto locateServices = [&fc](std::string const& type) -> std::vector> + { + auto sRef = fc.GetServiceReference(type); + if (sRef) + { + auto serv = fc.GetService(sRef); + return { serv->at(type) }; + } + else + { + return {}; + } + }; + + EXPECT_CALL(*(mockContext.get()), GetBundleContext()).WillRepeatedly(testing::Invoke([&fc]() { return fc; })); + + EXPECT_CALL(*(mockContext.get()), LocateServices("dep1", us_service_interface_iid())) + .Times(1) + .WillRepeatedly(testing::WithArg<1>(testing::Invoke( + locateServices))); // ensure the mock context received a call to LocateServices for dependency dep1 + + EXPECT_CALL(*(mockContext.get()), LocateService("dep2", us_service_interface_iid())) + .Times(1) + .WillRepeatedly(testing::WithArg<1>(testing::Invoke( + locateService))); // ensure the mock context received a call to LocateService for dependency dep2 + + EXPECT_CALL(*(mockContext.get()), LocateService("dep3", us_service_interface_iid())) + .Times(1) + .WillRepeatedly(testing::WithArg<1>(testing::Invoke( + locateService))); // ensure the mock context received a call to LocateServices for dependency dep3 + + // use mock context to create an instance of the component implementation class. + compInstance.CreateInstance(mockContext); + compInstance.BindReferences(mockContext); + auto compObj = compInstance.GetInstance(); + ASSERT_TRUE(compObj); + // ensure the dependencies are bound when the object is constructed + ASSERT_NE(compObj->GetDep1().size(), 0); + ASSERT_NE(compObj->GetDep2(), nullptr); + ASSERT_NE(compObj->GetDep3().size(), 0); + + // ensure only dynamic dependencies can be re-bound. + // The runtime calls the wrapper object with the name of the reference and the ServiceReference object to use + // for binding. + EXPECT_THROW(compInstance.InvokeUnbindMethod("dep1", fc.GetServiceReference()), + std::out_of_range); + EXPECT_THROW(compInstance.InvokeBindMethod("dep1", fc.GetServiceReference()), + std::out_of_range); + EXPECT_THROW(compInstance.InvokeUnbindMethod("dep2", fc.GetServiceReference()), + std::out_of_range); + EXPECT_THROW(compInstance.InvokeBindMethod("dep2", fc.GetServiceReference()), + std::out_of_range); + EXPECT_NO_THROW(compInstance.InvokeUnbindMethod("dep3", fc.GetServiceReference())); + ASSERT_EQ(compObj->GetDep3().size(), 0); + EXPECT_NO_THROW(compInstance.InvokeBindMethod("dep3", fc.GetServiceReference())); + ASSERT_EQ(compObj->GetDep3().size(), 1); + + f.Stop(); + f.WaitForStop(std::chrono::milliseconds::zero()); + } } // namespace diff --git a/compendium/test_bundles/CMakeLists.txt b/compendium/test_bundles/CMakeLists.txt index 8497d0a40..ab00a07b4 100644 --- a/compendium/test_bundles/CMakeLists.txt +++ b/compendium/test_bundles/CMakeLists.txt @@ -16,6 +16,17 @@ add_subdirectory(TestBundleDSDGMU) add_subdirectory(TestBundleDSDGOU) add_subdirectory(TestBundleDSSLE1) add_subdirectory(TestBundleDSSLE2) +add_subdirectory(TestBundleDSTBV1) +add_subdirectory(TestBundleDSTBV1_1) +add_subdirectory(TestBundleDSTBV1_2) +add_subdirectory(TestBundleDSTBV1_3) +add_subdirectory(TestBundleDSTBV1_4) +add_subdirectory(TestBundleDSTBV1_5) +add_subdirectory(TestBundleDSTBV2) +add_subdirectory(TestBundleDSTBV3) +add_subdirectory(TestBundleDSTBV3_1) +add_subdirectory(TestBundleDSTBV4) +add_subdirectory(TestBundleDSTBV5) add_subdirectory(TestBundleDSTOI1) add_subdirectory(TestBundleDSTOI10) add_subdirectory(TestBundleDSTOI12) @@ -64,3 +75,4 @@ add_subdirectory(TestBundleManagedServiceFactory) add_subdirectory(TestBundleNestedStartManagedSvc) add_subdirectory(TestBundleMultipleManagedService) add_subdirectory(TestBundleMultipleManagedServiceFactory) + diff --git a/compendium/test_bundles/TestBundleDSTBV1/CMakeLists.txt b/compendium/test_bundles/TestBundleDSTBV1/CMakeLists.txt new file mode 100644 index 000000000..d778b6308 --- /dev/null +++ b/compendium/test_bundles/TestBundleDSTBV1/CMakeLists.txt @@ -0,0 +1,7 @@ +usFunctionCreateDSTestBundle(TestBundleDSTBV1) + +usFunctionCreateTestBundleWithResources(TestBundleDSTBV1 + SOURCES src/ServiceImpl.cpp ${_glue_file} + RESOURCES manifest.json + BUNDLE_SYMBOLIC_NAME TestBundleDSTBV1 + OTHER_LIBRARIES usTestInterfaces usServiceComponent) diff --git a/compendium/test_bundles/TestBundleDSTBV1/resources/manifest.json b/compendium/test_bundles/TestBundleDSTBV1/resources/manifest.json new file mode 100644 index 000000000..a0a466309 --- /dev/null +++ b/compendium/test_bundles/TestBundleDSTBV1/resources/manifest.json @@ -0,0 +1,18 @@ +{ + "bundle.symbolic_name": "TestBundleDSTBV1", + "bundle.name": "TestBundleDSTBV1", + "bundle.activator": false, + "scr": { + "version": 1, + "components": [ + { + "enabled": true, + "immediate": true, + "implementation-class": "sample::ServiceComponentBV1", + "service": { + "interfaces": [ "test::Interface1" ] + } + } + ] + } +} diff --git a/compendium/test_bundles/TestBundleDSTBV1/src/ServiceComponents.hpp b/compendium/test_bundles/TestBundleDSTBV1/src/ServiceComponents.hpp new file mode 100644 index 000000000..45ccb19e7 --- /dev/null +++ b/compendium/test_bundles/TestBundleDSTBV1/src/ServiceComponents.hpp @@ -0,0 +1,6 @@ +#ifndef SERVICECOMPONENTS_HPP +#define SERVICECOMPONENTS_HPP + +#include "ServiceImpl.hpp" + +#endif diff --git a/compendium/test_bundles/TestBundleDSTBV1/src/ServiceImpl.cpp b/compendium/test_bundles/TestBundleDSTBV1/src/ServiceImpl.cpp new file mode 100644 index 000000000..9138f110e --- /dev/null +++ b/compendium/test_bundles/TestBundleDSTBV1/src/ServiceImpl.cpp @@ -0,0 +1,33 @@ +/*============================================================================= + + Library: CppMicroServices + + Copyright (c) The CppMicroServices developers. See the COPYRIGHT + file at the top-level directory of this distribution and at + https://github.com/CppMicroServices/CppMicroServices/COPYRIGHT . + + 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. + +=============================================================================*/ +#include "ServiceImpl.hpp" + +namespace sample +{ + ServiceComponentBV1::~ServiceComponentBV1() {} + + std::string + ServiceComponentBV1::Description() + { + return STRINGIZE(US_BUNDLE_NAME); + } +} // namespace sample diff --git a/compendium/test_bundles/TestBundleDSTBV1/src/ServiceImpl.hpp b/compendium/test_bundles/TestBundleDSTBV1/src/ServiceImpl.hpp new file mode 100644 index 000000000..9b11f8dec --- /dev/null +++ b/compendium/test_bundles/TestBundleDSTBV1/src/ServiceImpl.hpp @@ -0,0 +1,17 @@ +#ifndef _SERVICE_IMPL_HPP_ +#define _SERVICE_IMPL_HPP_ + +#include "TestInterfaces/Interfaces.hpp" + +namespace sample +{ + class ServiceComponentBV1 : public test::Interface1 + { + public: + ServiceComponentBV1() = default; + ~ServiceComponentBV1() override; + std::string Description() override; + }; +} // namespace sample + +#endif // _SERVICE_IMPL_HPP_ diff --git a/compendium/test_bundles/TestBundleDSTBV1_1/CMakeLists.txt b/compendium/test_bundles/TestBundleDSTBV1_1/CMakeLists.txt new file mode 100644 index 000000000..80c71210e --- /dev/null +++ b/compendium/test_bundles/TestBundleDSTBV1_1/CMakeLists.txt @@ -0,0 +1,8 @@ +usFunctionCreateDSTestBundle(TestBundleDSTBV1_1) + +usFunctionCreateTestBundleWithResources(TestBundleDSTBV1_1 + SOURCES src/ServiceImpl.cpp ${_glue_file} + RESOURCES manifest.json + BUNDLE_SYMBOLIC_NAME TestBundleDSTBV1_1 + OTHER_LIBRARIES usTestInterfaces usServiceComponent) + diff --git a/compendium/test_bundles/TestBundleDSTBV1_1/resources/manifest.json b/compendium/test_bundles/TestBundleDSTBV1_1/resources/manifest.json new file mode 100644 index 000000000..32aded74d --- /dev/null +++ b/compendium/test_bundles/TestBundleDSTBV1_1/resources/manifest.json @@ -0,0 +1,21 @@ +{ + "bundle.symbolic_name": "TestBundleDSTBV1_1", + "scr": { + "version": 1, + "components": [ + { + "implementation-class": "sample::ServiceComponentBV1_1", + "service": { + "interfaces": [ "test::Interface2" ] + }, + "references": [ + { + "name": "foo", + "interface": "test::Interface1" + } + ], + "inject-references": false + } + ] + } +} diff --git a/compendium/test_bundles/TestBundleDSTBV1_1/src/ServiceComponents.hpp b/compendium/test_bundles/TestBundleDSTBV1_1/src/ServiceComponents.hpp new file mode 100644 index 000000000..45ccb19e7 --- /dev/null +++ b/compendium/test_bundles/TestBundleDSTBV1_1/src/ServiceComponents.hpp @@ -0,0 +1,6 @@ +#ifndef SERVICECOMPONENTS_HPP +#define SERVICECOMPONENTS_HPP + +#include "ServiceImpl.hpp" + +#endif diff --git a/compendium/test_bundles/TestBundleDSTBV1_1/src/ServiceImpl.cpp b/compendium/test_bundles/TestBundleDSTBV1_1/src/ServiceImpl.cpp new file mode 100644 index 000000000..1bc5009f6 --- /dev/null +++ b/compendium/test_bundles/TestBundleDSTBV1_1/src/ServiceImpl.cpp @@ -0,0 +1,48 @@ +#include "ServiceImpl.hpp" + +namespace sample +{ + void + ServiceComponentBV1_1::Activate(std::shared_ptr const& ctxt) + { + foo = ctxt->LocateService("foo"); + } + + void + ServiceComponentBV1_1::Deactivate(std::shared_ptr const&) + { + foo = nullptr; + } + + std::string + ServiceComponentBV1_1::ExtendedDescription() + { + if (!foo) + { + throw std::runtime_error("Dependency not available"); + } + std::string result(STRINGIZE(US_BUNDLE_NAME)); + result.append("depends on "); + result.append(foo->Description()); + return result; + } + + void + ServiceComponentBV1_1::Bindfoo(std::shared_ptr const& theFoo) + { + if (foo != theFoo) + { + foo = theFoo; + } + } + + void + ServiceComponentBV1_1::Unbindfoo(std::shared_ptr const& theFoo) + { + if (foo == theFoo) + { + foo = nullptr; + } + } + +} // namespace sample diff --git a/compendium/test_bundles/TestBundleDSTBV1_1/src/ServiceImpl.hpp b/compendium/test_bundles/TestBundleDSTBV1_1/src/ServiceImpl.hpp new file mode 100644 index 000000000..eb01940fd --- /dev/null +++ b/compendium/test_bundles/TestBundleDSTBV1_1/src/ServiceImpl.hpp @@ -0,0 +1,28 @@ +#ifndef _SERVICE_IMPL_HPP_ +#define _SERVICE_IMPL_HPP_ + +#include "TestInterfaces/Interfaces.hpp" +#include "cppmicroservices/servicecomponent/ComponentContext.hpp" + +using ComponentContext = cppmicroservices::service::component::ComponentContext; + +namespace sample +{ + class ServiceComponentBV1_1 : public test::Interface2 + { + public: + ServiceComponentBV1_1() = default; + std::string ExtendedDescription() override; + void Activate(std::shared_ptr const&); + void Deactivate(std::shared_ptr const&); + ~ServiceComponentBV1_1() = default; + + void Bindfoo(std::shared_ptr const&); + void Unbindfoo(std::shared_ptr const&); + + private: + std::shared_ptr foo; + }; +} // namespace sample + +#endif // _SERVICE_IMPL_HPP_ diff --git a/compendium/test_bundles/TestBundleDSTBV1_2/CMakeLists.txt b/compendium/test_bundles/TestBundleDSTBV1_2/CMakeLists.txt new file mode 100644 index 000000000..7c9172ab4 --- /dev/null +++ b/compendium/test_bundles/TestBundleDSTBV1_2/CMakeLists.txt @@ -0,0 +1,8 @@ +usFunctionCreateDSTestBundle(TestBundleDSTBV1_2) + +usFunctionCreateTestBundleWithResources(TestBundleDSTBV1_2 + SOURCES src/ServiceImpl.cpp ${_glue_file} + RESOURCES manifest.json + BUNDLE_SYMBOLIC_NAME TestBundleDSTBV1_2 + OTHER_LIBRARIES usTestInterfaces usServiceComponent usServiceComponent) + diff --git a/compendium/test_bundles/TestBundleDSTBV1_2/resources/manifest.json b/compendium/test_bundles/TestBundleDSTBV1_2/resources/manifest.json new file mode 100644 index 000000000..8e4b199b9 --- /dev/null +++ b/compendium/test_bundles/TestBundleDSTBV1_2/resources/manifest.json @@ -0,0 +1,23 @@ +{ + "bundle.symbolic_name": "TestBundleDSTBV1_2", + "scr": { + "version": 1, + "components": [ + { + "immediate": true, + "implementation-class": "sample::ServiceComponentBV1_2", + "service": { + "interfaces": [ "test::Interface2" ] + }, + "references": [ + { + "name": "foo", + "policy": "Dynamic", + "interface": "test::Interface1" + } + ], + "inject-references": false + } + ] + } +} diff --git a/compendium/test_bundles/TestBundleDSTBV1_2/src/ServiceComponents.hpp b/compendium/test_bundles/TestBundleDSTBV1_2/src/ServiceComponents.hpp new file mode 100644 index 000000000..45ccb19e7 --- /dev/null +++ b/compendium/test_bundles/TestBundleDSTBV1_2/src/ServiceComponents.hpp @@ -0,0 +1,6 @@ +#ifndef SERVICECOMPONENTS_HPP +#define SERVICECOMPONENTS_HPP + +#include "ServiceImpl.hpp" + +#endif diff --git a/compendium/test_bundles/TestBundleDSTBV1_2/src/ServiceImpl.cpp b/compendium/test_bundles/TestBundleDSTBV1_2/src/ServiceImpl.cpp new file mode 100644 index 000000000..11e0d71eb --- /dev/null +++ b/compendium/test_bundles/TestBundleDSTBV1_2/src/ServiceImpl.cpp @@ -0,0 +1,48 @@ +#include "ServiceImpl.hpp" +#include + +namespace sample +{ + + void + ServiceComponentBV1_2::Activate(std::shared_ptr const& /*ctxt*/) + { + } + + void + ServiceComponentBV1_2::Deactivate(std::shared_ptr const&) + { + } + + std::string + ServiceComponentBV1_2::ExtendedDescription() + { + if (!foo) + { + throw std::runtime_error("Dependency not available"); + } + std::string result(STRINGIZE(US_BUNDLE_NAME)); + result.append("depends on "); + result.append(foo->Description()); + return result; + } + + void + ServiceComponentBV1_2::Bindfoo(std::shared_ptr const& theFoo) + { + if (foo != theFoo) + { + foo = theFoo; + } + } + + void + ServiceComponentBV1_2::Unbindfoo(std::shared_ptr const& theFoo) + { + if (foo == theFoo) + { + foo = nullptr; + } + } + +} // namespace sample diff --git a/compendium/test_bundles/TestBundleDSTBV1_2/src/ServiceImpl.hpp b/compendium/test_bundles/TestBundleDSTBV1_2/src/ServiceImpl.hpp new file mode 100644 index 000000000..93d7a2272 --- /dev/null +++ b/compendium/test_bundles/TestBundleDSTBV1_2/src/ServiceImpl.hpp @@ -0,0 +1,30 @@ +#ifndef _SERVICE_IMPL_HPP_ +#define _SERVICE_IMPL_HPP_ + +#include "TestInterfaces/Interfaces.hpp" +#include "cppmicroservices/servicecomponent/ComponentContext.hpp" + +using ComponentContext = cppmicroservices::service::component::ComponentContext; + +namespace sample +{ + + class ServiceComponentBV1_2 : public test::Interface2 + { + public: + ServiceComponentBV1_2() = default; + std::string ExtendedDescription() override; + void Activate(std::shared_ptr const&); + void Deactivate(std::shared_ptr const&); + ~ServiceComponentBV1_2() = default; + + void Bindfoo(std::shared_ptr const&); + void Unbindfoo(std::shared_ptr const&); + + private: + std::shared_ptr foo; + }; + +} // namespace sample + +#endif // _SERVICE_IMPL_HPP_ diff --git a/compendium/test_bundles/TestBundleDSTBV1_3/CMakeLists.txt b/compendium/test_bundles/TestBundleDSTBV1_3/CMakeLists.txt new file mode 100644 index 000000000..af90dc492 --- /dev/null +++ b/compendium/test_bundles/TestBundleDSTBV1_3/CMakeLists.txt @@ -0,0 +1,7 @@ +usFunctionCreateDSTestBundle(TestBundleDSTBV1_3) + +usFunctionCreateTestBundleWithResources(TestBundleDSTBV1_3 + SOURCES src/ServiceImpl.cpp ${_glue_file} + RESOURCES manifest.json + BUNDLE_SYMBOLIC_NAME TestBundleDSTBV1_3 + OTHER_LIBRARIES usTestInterfaces usServiceComponent) diff --git a/compendium/test_bundles/TestBundleDSTBV1_3/resources/manifest.json b/compendium/test_bundles/TestBundleDSTBV1_3/resources/manifest.json new file mode 100644 index 000000000..5a2481c72 --- /dev/null +++ b/compendium/test_bundles/TestBundleDSTBV1_3/resources/manifest.json @@ -0,0 +1,13 @@ +{ + "bundle.symbolic_name" : "TestBundleDSTBV1_3", + "scr" : { + "version" : 1, + "components" : [{ + "implementation-class": "sample::ServiceComponentBV1_3", + "service" : { + "scope" : "prototype", + "interfaces" : ["test::Interface1"] + } + }] + } +} diff --git a/compendium/test_bundles/TestBundleDSTBV1_3/src/ServiceComponents.hpp b/compendium/test_bundles/TestBundleDSTBV1_3/src/ServiceComponents.hpp new file mode 100644 index 000000000..45ccb19e7 --- /dev/null +++ b/compendium/test_bundles/TestBundleDSTBV1_3/src/ServiceComponents.hpp @@ -0,0 +1,6 @@ +#ifndef SERVICECOMPONENTS_HPP +#define SERVICECOMPONENTS_HPP + +#include "ServiceImpl.hpp" + +#endif diff --git a/compendium/test_bundles/TestBundleDSTBV1_3/src/ServiceImpl.cpp b/compendium/test_bundles/TestBundleDSTBV1_3/src/ServiceImpl.cpp new file mode 100644 index 000000000..a0b8ffe84 --- /dev/null +++ b/compendium/test_bundles/TestBundleDSTBV1_3/src/ServiceImpl.cpp @@ -0,0 +1,33 @@ +/*============================================================================= + + Library: CppMicroServices + + Copyright (c) The CppMicroServices developers. See the COPYRIGHT + file at the top-level directory of this distribution and at + https://github.com/CppMicroServices/CppMicroServices/COPYRIGHT . + + 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. + +=============================================================================*/ +#include "ServiceImpl.hpp" + +namespace sample +{ + ServiceComponentBV1_3::~ServiceComponentBV1_3() {} + + std::string + ServiceComponentBV1_3::Description() + { + return STRINGIZE(US_BUNDLE_NAME); + } +} // namespace sample diff --git a/compendium/test_bundles/TestBundleDSTBV1_3/src/ServiceImpl.hpp b/compendium/test_bundles/TestBundleDSTBV1_3/src/ServiceImpl.hpp new file mode 100644 index 000000000..1f6063e18 --- /dev/null +++ b/compendium/test_bundles/TestBundleDSTBV1_3/src/ServiceImpl.hpp @@ -0,0 +1,17 @@ +#ifndef _SERVICE_IMPL_HPP_ +#define _SERVICE_IMPL_HPP_ + +#include "TestInterfaces/Interfaces.hpp" + +namespace sample +{ + class ServiceComponentBV1_3 : public test::Interface1 + { + public: + ServiceComponentBV1_3() = default; + ~ServiceComponentBV1_3() override; + std::string Description() override; + }; +} // namespace sample + +#endif // _SERVICE_IMPL_HPP_ diff --git a/compendium/test_bundles/TestBundleDSTBV1_4/CMakeLists.txt b/compendium/test_bundles/TestBundleDSTBV1_4/CMakeLists.txt new file mode 100644 index 000000000..3adea3b8c --- /dev/null +++ b/compendium/test_bundles/TestBundleDSTBV1_4/CMakeLists.txt @@ -0,0 +1,7 @@ +usFunctionCreateDSTestBundle(TestBundleDSTBV1_4) + +usFunctionCreateTestBundleWithResources(TestBundleDSTBV1_4 + SOURCES src/ServiceImpl.cpp ${_glue_file} + RESOURCES manifest.json + BUNDLE_SYMBOLIC_NAME TestBundleDSTBV1_4 + OTHER_LIBRARIES usTestInterfaces usServiceComponent) diff --git a/compendium/test_bundles/TestBundleDSTBV1_4/resources/manifest.json b/compendium/test_bundles/TestBundleDSTBV1_4/resources/manifest.json new file mode 100644 index 000000000..fefe562bb --- /dev/null +++ b/compendium/test_bundles/TestBundleDSTBV1_4/resources/manifest.json @@ -0,0 +1,19 @@ +{ + "bundle.symbolic_name": "TestBundleDSTBV1_4", + "bundle.activator": false, + "scr": { + "version": 1, + "components": [ + { + "enabled": true, + "immediate": false, + "implementation-class": "sample::ServiceComponentBV1_4", + "configuration-policy": "require", + "configuration-pid": [ "sample::ServiceComponentBV1_4" ], + "service": { + "interfaces": [ "test::CAInterface" ] + } + } + ] + } +} diff --git a/compendium/test_bundles/TestBundleDSTBV1_4/src/ServiceComponents.hpp b/compendium/test_bundles/TestBundleDSTBV1_4/src/ServiceComponents.hpp new file mode 100644 index 000000000..45ccb19e7 --- /dev/null +++ b/compendium/test_bundles/TestBundleDSTBV1_4/src/ServiceComponents.hpp @@ -0,0 +1,6 @@ +#ifndef SERVICECOMPONENTS_HPP +#define SERVICECOMPONENTS_HPP + +#include "ServiceImpl.hpp" + +#endif diff --git a/compendium/test_bundles/TestBundleDSTBV1_4/src/ServiceImpl.cpp b/compendium/test_bundles/TestBundleDSTBV1_4/src/ServiceImpl.cpp new file mode 100644 index 000000000..ad41a34f6 --- /dev/null +++ b/compendium/test_bundles/TestBundleDSTBV1_4/src/ServiceImpl.cpp @@ -0,0 +1,23 @@ +#include "ServiceImpl.hpp" +#include + +namespace sample +{ + + void + ServiceComponentBV1_4::Modified(std::shared_ptr const& /*context*/, + std::shared_ptr const& configuration) + { + throw std::runtime_error("Modified method exception"); + std::lock_guard lock(propertiesLock); + properties = configuration; + } + + cppmicroservices::AnyMap + ServiceComponentBV1_4::GetProperties() + { + std::lock_guard lock(propertiesLock); + return *properties; + } + +} // namespace sample diff --git a/compendium/test_bundles/TestBundleDSTBV1_4/src/ServiceImpl.hpp b/compendium/test_bundles/TestBundleDSTBV1_4/src/ServiceImpl.hpp new file mode 100644 index 000000000..085097807 --- /dev/null +++ b/compendium/test_bundles/TestBundleDSTBV1_4/src/ServiceImpl.hpp @@ -0,0 +1,27 @@ +#ifndef _SERVICE_IMPL_HPP_ +#define _SERVICE_IMPL_HPP_ + +#include "TestInterfaces/Interfaces.hpp" +#include "cppmicroservices/servicecomponent/ComponentContext.hpp" +#include + +using ComponentContext = cppmicroservices::service::component::ComponentContext; + +namespace sample +{ + class ServiceComponentBV1_4 : public test::CAInterface + { + public: + ServiceComponentBV1_4(std::shared_ptr const& props) : properties(props) {} + void Modified(std::shared_ptr const& context, + std::shared_ptr const& configuration); + cppmicroservices::AnyMap GetProperties(); + ~ServiceComponentBV1_4() = default; + + private: + std::mutex propertiesLock; + std::shared_ptr properties; + }; +} // namespace sample + +#endif // _SERVICE_IMPL_HPP_ diff --git a/compendium/test_bundles/TestBundleDSTBV1_5/CMakeLists.txt b/compendium/test_bundles/TestBundleDSTBV1_5/CMakeLists.txt new file mode 100644 index 000000000..ccb6ac853 --- /dev/null +++ b/compendium/test_bundles/TestBundleDSTBV1_5/CMakeLists.txt @@ -0,0 +1,7 @@ +usFunctionCreateDSTestBundle(TestBundleDSTBV1_5) + +usFunctionCreateTestBundleWithResources(TestBundleDSTBV1_5 + SOURCES src/ServiceImpl.cpp ${_glue_file} + RESOURCES manifest.json + BUNDLE_SYMBOLIC_NAME TestBundleDSTBV1_5 + OTHER_LIBRARIES usTestInterfaces usServiceComponent) diff --git a/compendium/test_bundles/TestBundleDSTBV1_5/resources/manifest.json b/compendium/test_bundles/TestBundleDSTBV1_5/resources/manifest.json new file mode 100644 index 000000000..9439ef434 --- /dev/null +++ b/compendium/test_bundles/TestBundleDSTBV1_5/resources/manifest.json @@ -0,0 +1,17 @@ +{ + "bundle.symbolic_name" : "TestBundleDSTBV1_5", + "scr" : { + "version" : 1, + "components" : [{ + "enabled" : true, + "immediate": true, + "implementation-class": "sample::ServiceComponentBV1_5", + "configuration-policy" : "require", + "configuration-pid" : ["sample::ServiceComponentBV1_5"], + "inject-references" : false, + "service": { + "interfaces": ["test::CAInterface"] + } + }] + } +} diff --git a/compendium/test_bundles/TestBundleDSTBV1_5/src/ServiceComponents.hpp b/compendium/test_bundles/TestBundleDSTBV1_5/src/ServiceComponents.hpp new file mode 100644 index 000000000..45ccb19e7 --- /dev/null +++ b/compendium/test_bundles/TestBundleDSTBV1_5/src/ServiceComponents.hpp @@ -0,0 +1,6 @@ +#ifndef SERVICECOMPONENTS_HPP +#define SERVICECOMPONENTS_HPP + +#include "ServiceImpl.hpp" + +#endif diff --git a/compendium/test_bundles/TestBundleDSTBV1_5/src/ServiceImpl.cpp b/compendium/test_bundles/TestBundleDSTBV1_5/src/ServiceImpl.cpp new file mode 100644 index 000000000..4b0032e16 --- /dev/null +++ b/compendium/test_bundles/TestBundleDSTBV1_5/src/ServiceImpl.cpp @@ -0,0 +1,14 @@ +#include "ServiceImpl.hpp" +#include + +namespace sample +{ + + cppmicroservices::AnyMap + ServiceComponentBV1_5::GetProperties() + { + std::lock_guard lock(propertiesLock); + return *properties; + } + +} // namespace sample diff --git a/compendium/test_bundles/TestBundleDSTBV1_5/src/ServiceImpl.hpp b/compendium/test_bundles/TestBundleDSTBV1_5/src/ServiceImpl.hpp new file mode 100644 index 000000000..d3c081a6e --- /dev/null +++ b/compendium/test_bundles/TestBundleDSTBV1_5/src/ServiceImpl.hpp @@ -0,0 +1,27 @@ +#ifndef _SERVICE_IMPL_HPP_ +#define _SERVICE_IMPL_HPP_ + +#include "TestInterfaces/Interfaces.hpp" +#include "cppmicroservices/servicecomponent/ComponentContext.hpp" +#include + +using ComponentContext = cppmicroservices::service::component::ComponentContext; + +namespace sample +{ + class ServiceComponentBV1_5 : public test::CAInterface + { + public: + ServiceComponentBV1_5(std::shared_ptr const& props) : properties(props) {} + + cppmicroservices::AnyMap GetProperties(); + + ~ServiceComponentBV1_5() = default; + + private: + std::mutex propertiesLock; + std::shared_ptr properties; + }; +} // namespace sample + +#endif // _SERVICE_IMPL_HPP_ diff --git a/compendium/test_bundles/TestBundleDSTBV2/CMakeLists.txt b/compendium/test_bundles/TestBundleDSTBV2/CMakeLists.txt new file mode 100644 index 000000000..873b431f0 --- /dev/null +++ b/compendium/test_bundles/TestBundleDSTBV2/CMakeLists.txt @@ -0,0 +1,7 @@ +usFunctionCreateDSTestBundle(TestBundleDSTBV2) + +usFunctionCreateTestBundleWithResources(TestBundleDSTBV2 + SOURCES src/ServiceImpl.cpp ${_glue_file} + RESOURCES manifest.json + BUNDLE_SYMBOLIC_NAME TestBundleDSTBV2 + OTHER_LIBRARIES usTestInterfaces usServiceComponent) diff --git a/compendium/test_bundles/TestBundleDSTBV2/resources/manifest.json b/compendium/test_bundles/TestBundleDSTBV2/resources/manifest.json new file mode 100644 index 000000000..5bbf5ca36 --- /dev/null +++ b/compendium/test_bundles/TestBundleDSTBV2/resources/manifest.json @@ -0,0 +1,18 @@ +{ + "bundle.symbolic_name": "TestBundleDSTBV2", + "bundle.name": "TestBundleDSTBV2", + "bundle.activator": false, + "scr": { + "version": 1, + "components": [ + { + "enabled": true, + "immediate": true, + "implementation-class": "sample::ServiceComponentBV2", + "service": { + "interfaces": [ "test::Interface1" ] + } + } + ] + } +} diff --git a/compendium/test_bundles/TestBundleDSTBV2/src/ServiceComponents.hpp b/compendium/test_bundles/TestBundleDSTBV2/src/ServiceComponents.hpp new file mode 100644 index 000000000..45ccb19e7 --- /dev/null +++ b/compendium/test_bundles/TestBundleDSTBV2/src/ServiceComponents.hpp @@ -0,0 +1,6 @@ +#ifndef SERVICECOMPONENTS_HPP +#define SERVICECOMPONENTS_HPP + +#include "ServiceImpl.hpp" + +#endif diff --git a/compendium/test_bundles/TestBundleDSTBV2/src/ServiceImpl.cpp b/compendium/test_bundles/TestBundleDSTBV2/src/ServiceImpl.cpp new file mode 100644 index 000000000..c88cf0478 --- /dev/null +++ b/compendium/test_bundles/TestBundleDSTBV2/src/ServiceImpl.cpp @@ -0,0 +1,33 @@ +/*============================================================================= + + Library: CppMicroServices + + Copyright (c) The CppMicroServices developers. See the COPYRIGHT + file at the top-level directory of this distribution and at + https://github.com/CppMicroServices/CppMicroServices/COPYRIGHT . + + 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. + +=============================================================================*/ +#include "ServiceImpl.hpp" + +namespace sample +{ + ServiceComponentBV2::~ServiceComponentBV2() {} + + std::string + ServiceComponentBV2::Description() + { + return STRINGIZE(US_BUNDLE_NAME); + } +} // namespace sample diff --git a/compendium/test_bundles/TestBundleDSTBV2/src/ServiceImpl.hpp b/compendium/test_bundles/TestBundleDSTBV2/src/ServiceImpl.hpp new file mode 100644 index 000000000..198a9840e --- /dev/null +++ b/compendium/test_bundles/TestBundleDSTBV2/src/ServiceImpl.hpp @@ -0,0 +1,17 @@ +#ifndef _SERVICE_IMPL_HPP_ +#define _SERVICE_IMPL_HPP_ + +#include "TestInterfaces/Interfaces.hpp" + +namespace sample +{ + class ServiceComponentBV2 : public test::Interface1 + { + public: + ServiceComponentBV2() = default; + ~ServiceComponentBV2() override; + std::string Description() override; + }; +} // namespace sample + +#endif // _SERVICE_IMPL_HPP_ diff --git a/compendium/test_bundles/TestBundleDSTBV3/CMakeLists.txt b/compendium/test_bundles/TestBundleDSTBV3/CMakeLists.txt new file mode 100644 index 000000000..b17714588 --- /dev/null +++ b/compendium/test_bundles/TestBundleDSTBV3/CMakeLists.txt @@ -0,0 +1,7 @@ +usFunctionCreateDSTestBundle(TestBundleDSTBV3) + +usFunctionCreateTestBundleWithResources(TestBundleDSTBV3 + SOURCES src/ServiceImpl.cpp ${_glue_file} + RESOURCES manifest.json + BUNDLE_SYMBOLIC_NAME TestBundleDSTBV3 + OTHER_LIBRARIES usTestInterfaces usServiceComponent) diff --git a/compendium/test_bundles/TestBundleDSTBV3/resources/manifest.json b/compendium/test_bundles/TestBundleDSTBV3/resources/manifest.json new file mode 100644 index 000000000..0123e1b48 --- /dev/null +++ b/compendium/test_bundles/TestBundleDSTBV3/resources/manifest.json @@ -0,0 +1,18 @@ +{ + "bundle.symbolic_name": "TestBundleDSTBV3", + "bundle.name": "TestBundleDSTBV3", + "bundle.activator": false, + "scr": { + "version": 1, + "components": [ + { + "enabled": true, + "immediate": true, + "implementation-class": "sample::ServiceComponentBV3", + "service": { + "interfaces": [ "test::Interface1" ] + } + } + ] + } +} diff --git a/compendium/test_bundles/TestBundleDSTBV3/src/ServiceComponents.hpp b/compendium/test_bundles/TestBundleDSTBV3/src/ServiceComponents.hpp new file mode 100644 index 000000000..45ccb19e7 --- /dev/null +++ b/compendium/test_bundles/TestBundleDSTBV3/src/ServiceComponents.hpp @@ -0,0 +1,6 @@ +#ifndef SERVICECOMPONENTS_HPP +#define SERVICECOMPONENTS_HPP + +#include "ServiceImpl.hpp" + +#endif diff --git a/compendium/test_bundles/TestBundleDSTBV3/src/ServiceImpl.cpp b/compendium/test_bundles/TestBundleDSTBV3/src/ServiceImpl.cpp new file mode 100644 index 000000000..82054f93d --- /dev/null +++ b/compendium/test_bundles/TestBundleDSTBV3/src/ServiceImpl.cpp @@ -0,0 +1,33 @@ +/*============================================================================= + + Library: CppMicroServices + + Copyright (c) The CppMicroServices developers. See the COPYRIGHT + file at the top-level directory of this distribution and at + https://github.com/CppMicroServices/CppMicroServices/COPYRIGHT . + + 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. + +=============================================================================*/ +#include "ServiceImpl.hpp" + +namespace sample +{ + ServiceComponentBV3::~ServiceComponentBV3() {} + + std::string + ServiceComponentBV3::Description() + { + return STRINGIZE(US_BUNDLE_NAME); + } +} // namespace sample diff --git a/compendium/test_bundles/TestBundleDSTBV3/src/ServiceImpl.hpp b/compendium/test_bundles/TestBundleDSTBV3/src/ServiceImpl.hpp new file mode 100644 index 000000000..85a25eff8 --- /dev/null +++ b/compendium/test_bundles/TestBundleDSTBV3/src/ServiceImpl.hpp @@ -0,0 +1,17 @@ +#ifndef _SERVICE_IMPL_HPP_ +#define _SERVICE_IMPL_HPP_ + +#include "TestInterfaces/Interfaces.hpp" + +namespace sample +{ + class ServiceComponentBV3 : public test::Interface1 + { + public: + ServiceComponentBV3() = default; + ~ServiceComponentBV3() override; + std::string Description() override; + }; +} // namespace sample + +#endif // _SERVICE_IMPL_HPP_ diff --git a/compendium/test_bundles/TestBundleDSTBV3_1/CMakeLists.txt b/compendium/test_bundles/TestBundleDSTBV3_1/CMakeLists.txt new file mode 100644 index 000000000..6d605157c --- /dev/null +++ b/compendium/test_bundles/TestBundleDSTBV3_1/CMakeLists.txt @@ -0,0 +1,7 @@ +usFunctionCreateDSTestBundle(TestBundleDSTBV3_1) + +usFunctionCreateTestBundleWithResources(TestBundleDSTBV3_1 + SOURCES src/ServiceImpl.cpp ${_glue_file} + RESOURCES manifest.json + BUNDLE_SYMBOLIC_NAME TestBundleDSTBV3_1 + OTHER_LIBRARIES usTestInterfaces usServiceComponent) diff --git a/compendium/test_bundles/TestBundleDSTBV3_1/resources/manifest.json b/compendium/test_bundles/TestBundleDSTBV3_1/resources/manifest.json new file mode 100644 index 000000000..db7b947db --- /dev/null +++ b/compendium/test_bundles/TestBundleDSTBV3_1/resources/manifest.json @@ -0,0 +1,17 @@ +{ + "bundle.symbolic_name" : "TestBundleDSTBV3_1", + "scr" : { + "version" : 1, + "components" : [{ + "implementation-class": "sample::ServiceComponentBV3_1", + "service": { + "interfaces": ["test::Interface2"] + }, + "references":[{ + "name" : "foo", + "interface" : "test::Interface1" + }], + "inject-references": false + }] + } +} diff --git a/compendium/test_bundles/TestBundleDSTBV3_1/src/ServiceComponents.hpp b/compendium/test_bundles/TestBundleDSTBV3_1/src/ServiceComponents.hpp new file mode 100644 index 000000000..45ccb19e7 --- /dev/null +++ b/compendium/test_bundles/TestBundleDSTBV3_1/src/ServiceComponents.hpp @@ -0,0 +1,6 @@ +#ifndef SERVICECOMPONENTS_HPP +#define SERVICECOMPONENTS_HPP + +#include "ServiceImpl.hpp" + +#endif diff --git a/compendium/test_bundles/TestBundleDSTBV3_1/src/ServiceImpl.cpp b/compendium/test_bundles/TestBundleDSTBV3_1/src/ServiceImpl.cpp new file mode 100644 index 000000000..58aef1b05 --- /dev/null +++ b/compendium/test_bundles/TestBundleDSTBV3_1/src/ServiceImpl.cpp @@ -0,0 +1,48 @@ +#include "ServiceImpl.hpp" + +namespace sample +{ + void + ServiceComponentBV3_1::Activate(std::shared_ptr const& ctxt) + { + foo = ctxt->LocateService("foo"); + } + + void + ServiceComponentBV3_1::Deactivate(std::shared_ptr const&) + { + foo = nullptr; + } + + std::string + ServiceComponentBV3_1::ExtendedDescription() + { + if (!foo) + { + throw std::runtime_error("Dependency not available"); + } + std::string result(STRINGIZE(US_BUNDLE_NAME)); + result.append("depends on "); + result.append(foo->Description()); + return result; + } + + void + ServiceComponentBV3_1::Bindfoo(std::shared_ptr const& theFoo) + { + if (foo != theFoo) + { + foo = theFoo; + } + } + + void + ServiceComponentBV3_1::Unbindfoo(std::shared_ptr const& theFoo) + { + if (foo == theFoo) + { + foo = nullptr; + } + } + +} // namespace sample diff --git a/compendium/test_bundles/TestBundleDSTBV3_1/src/ServiceImpl.hpp b/compendium/test_bundles/TestBundleDSTBV3_1/src/ServiceImpl.hpp new file mode 100644 index 000000000..e0735ca37 --- /dev/null +++ b/compendium/test_bundles/TestBundleDSTBV3_1/src/ServiceImpl.hpp @@ -0,0 +1,28 @@ +#ifndef _SERVICE_IMPL_HPP_ +#define _SERVICE_IMPL_HPP_ + +#include "TestInterfaces/Interfaces.hpp" +#include "cppmicroservices/servicecomponent/ComponentContext.hpp" + +using ComponentContext = cppmicroservices::service::component::ComponentContext; + +namespace sample +{ + class ServiceComponentBV3_1 : public test::Interface2 + { + public: + ServiceComponentBV3_1() = default; + std::string ExtendedDescription() override; + void Activate(std::shared_ptr const&); + void Deactivate(std::shared_ptr const&); + ~ServiceComponentBV3_1() = default; + + void Bindfoo(std::shared_ptr const&); + void Unbindfoo(std::shared_ptr const&); + + private: + std::shared_ptr foo; + }; +} // namespace sample + +#endif // _SERVICE_IMPL_HPP_ diff --git a/compendium/test_bundles/TestBundleDSTBV4/CMakeLists.txt b/compendium/test_bundles/TestBundleDSTBV4/CMakeLists.txt new file mode 100644 index 000000000..12f0900fa --- /dev/null +++ b/compendium/test_bundles/TestBundleDSTBV4/CMakeLists.txt @@ -0,0 +1,7 @@ +usFunctionCreateDSTestBundle(TestBundleDSTBV4) + +usFunctionCreateTestBundleWithResources(TestBundleDSTBV4 + SOURCES src/ServiceImpl.cpp ${_glue_file} + RESOURCES manifest.json + BUNDLE_SYMBOLIC_NAME TestBundleDSTBV4 + OTHER_LIBRARIES usTestInterfaces usServiceComponent) diff --git a/compendium/test_bundles/TestBundleDSTBV4/resources/manifest.json b/compendium/test_bundles/TestBundleDSTBV4/resources/manifest.json new file mode 100644 index 000000000..18fb1c92e --- /dev/null +++ b/compendium/test_bundles/TestBundleDSTBV4/resources/manifest.json @@ -0,0 +1,26 @@ +{ + "bundle.symbolic_name": "TestBundleDSTBV4", + "bundle.name": "TestBundleDSTBV4", + "bundle.activator": false, + "scr": { + "version": 1, + "components": [ + { + "enabled": true, + "immediate": true, + "implementation-class": "sample::ServiceComponentBV41", + "service": { + "interfaces": [ "test::Interface1" ] + } + }, + { + "enabled": true, + "immediate": true, + "implementation-class": "sample::ServiceComponentBV42", + "service": { + "interfaces": [ "test::Interface1" ] + } + } + ] + } +} diff --git a/compendium/test_bundles/TestBundleDSTBV4/src/ServiceComponents.hpp b/compendium/test_bundles/TestBundleDSTBV4/src/ServiceComponents.hpp new file mode 100644 index 000000000..45ccb19e7 --- /dev/null +++ b/compendium/test_bundles/TestBundleDSTBV4/src/ServiceComponents.hpp @@ -0,0 +1,6 @@ +#ifndef SERVICECOMPONENTS_HPP +#define SERVICECOMPONENTS_HPP + +#include "ServiceImpl.hpp" + +#endif diff --git a/compendium/test_bundles/TestBundleDSTBV4/src/ServiceImpl.cpp b/compendium/test_bundles/TestBundleDSTBV4/src/ServiceImpl.cpp new file mode 100644 index 000000000..4727bc4cc --- /dev/null +++ b/compendium/test_bundles/TestBundleDSTBV4/src/ServiceImpl.cpp @@ -0,0 +1,41 @@ +/*============================================================================= + + Library: CppMicroServices + + Copyright (c) The CppMicroServices developers. See the COPYRIGHT + file at the top-level directory of this distribution and at + https://github.com/CppMicroServices/CppMicroServices/COPYRIGHT . + + 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. + +=============================================================================*/ +#include "ServiceImpl.hpp" + +namespace sample +{ + ServiceComponentBV41::~ServiceComponentBV41() {} + + std::string + ServiceComponentBV41::Description() + { + return STRINGIZE(US_BUNDLE_NAME); + } + + ServiceComponentBV42::~ServiceComponentBV42() {} + + std::string + ServiceComponentBV42::Description() + { + return STRINGIZE(US_BUNDLE_NAME); + } +} // namespace sample diff --git a/compendium/test_bundles/TestBundleDSTBV4/src/ServiceImpl.hpp b/compendium/test_bundles/TestBundleDSTBV4/src/ServiceImpl.hpp new file mode 100644 index 000000000..010ea556b --- /dev/null +++ b/compendium/test_bundles/TestBundleDSTBV4/src/ServiceImpl.hpp @@ -0,0 +1,25 @@ +#ifndef _SERVICE_IMPL_HPP_ +#define _SERVICE_IMPL_HPP_ + +#include "TestInterfaces/Interfaces.hpp" + +namespace sample +{ + class ServiceComponentBV41 : public test::Interface1 + { + public: + ServiceComponentBV41() = default; + ~ServiceComponentBV41() override; + std::string Description() override; + }; + + class ServiceComponentBV42 : public test::Interface1 + { + public: + ServiceComponentBV42() = default; + ~ServiceComponentBV42() override; + std::string Description() override; + }; +} // namespace sample + +#endif // _SERVICE_IMPL_HPP_ diff --git a/compendium/test_bundles/TestBundleDSTBV5/CMakeLists.txt b/compendium/test_bundles/TestBundleDSTBV5/CMakeLists.txt new file mode 100644 index 000000000..9f54d9675 --- /dev/null +++ b/compendium/test_bundles/TestBundleDSTBV5/CMakeLists.txt @@ -0,0 +1,7 @@ +usFunctionCreateDSTestBundle(TestBundleDSTBV5) + +usFunctionCreateTestBundleWithResources(TestBundleDSTBV5 + SOURCES src/ServiceImpl.cpp ${_glue_file} + RESOURCES manifest.json + BUNDLE_SYMBOLIC_NAME TestBundleDSTBV5 + OTHER_LIBRARIES usTestInterfaces usServiceComponent) diff --git a/compendium/test_bundles/TestBundleDSTBV5/resources/manifest.json b/compendium/test_bundles/TestBundleDSTBV5/resources/manifest.json new file mode 100644 index 000000000..1b45501eb --- /dev/null +++ b/compendium/test_bundles/TestBundleDSTBV5/resources/manifest.json @@ -0,0 +1,18 @@ +{ + "bundle.symbolic_name": "TestBundleDSTBV5", + "bundle.name": "TestBundleDSTBV5", + "bundle.activator": false, + "scr": { + "version": 1, + "components": [ + { + "enabled": true, + "immediate": true, + "implementation-class": "sample::ServiceComponentBV5", + "service": { + "interfaces": [ "test::Interface1" ] + } + } + ] + } +} diff --git a/compendium/test_bundles/TestBundleDSTBV5/src/ServiceComponents.hpp b/compendium/test_bundles/TestBundleDSTBV5/src/ServiceComponents.hpp new file mode 100644 index 000000000..45ccb19e7 --- /dev/null +++ b/compendium/test_bundles/TestBundleDSTBV5/src/ServiceComponents.hpp @@ -0,0 +1,6 @@ +#ifndef SERVICECOMPONENTS_HPP +#define SERVICECOMPONENTS_HPP + +#include "ServiceImpl.hpp" + +#endif diff --git a/compendium/test_bundles/TestBundleDSTBV5/src/ServiceImpl.cpp b/compendium/test_bundles/TestBundleDSTBV5/src/ServiceImpl.cpp new file mode 100644 index 000000000..9543e1616 --- /dev/null +++ b/compendium/test_bundles/TestBundleDSTBV5/src/ServiceImpl.cpp @@ -0,0 +1,33 @@ +/*============================================================================= + + Library: CppMicroServices + + Copyright (c) The CppMicroServices developers. See the COPYRIGHT + file at the top-level directory of this distribution and at + https://github.com/CppMicroServices/CppMicroServices/COPYRIGHT . + + 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. + +=============================================================================*/ +#include "ServiceImpl.hpp" + +namespace sample +{ + ServiceComponentBV5::~ServiceComponentBV5() {} + + std::string + ServiceComponentBV5::Description() + { + return STRINGIZE(US_BUNDLE_NAME); + } +} // namespace sample diff --git a/compendium/test_bundles/TestBundleDSTBV5/src/ServiceImpl.hpp b/compendium/test_bundles/TestBundleDSTBV5/src/ServiceImpl.hpp new file mode 100644 index 000000000..0b435b7ee --- /dev/null +++ b/compendium/test_bundles/TestBundleDSTBV5/src/ServiceImpl.hpp @@ -0,0 +1,17 @@ +#ifndef _SERVICE_IMPL_HPP_ +#define _SERVICE_IMPL_HPP_ + +#include "TestInterfaces/Interfaces.hpp" + +namespace sample +{ + class ServiceComponentBV5 : public test::Interface1 + { + public: + ServiceComponentBV5() = default; + ~ServiceComponentBV5() override; + std::string Description() override; + }; +} // namespace sample + +#endif // _SERVICE_IMPL_HPP_ diff --git a/compendium/tools/SCRCodeGen/ComponentCallbackGenerator.hpp b/compendium/tools/SCRCodeGen/ComponentCallbackGenerator.hpp index bac0ed636..f3a4094b9 100644 --- a/compendium/tools/SCRCodeGen/ComponentCallbackGenerator.hpp +++ b/compendium/tools/SCRCodeGen/ComponentCallbackGenerator.hpp @@ -129,9 +129,10 @@ namespace codegen } else { - mStrStream << datamodel::GetCtorInjectedRefTypes(componentInfo) << ">(" - << datamodel::GetCtorInjectedRefNames(componentInfo) << ", binders" - << ");"; + + mStrStream << datamodel::GetCtorInjectedRefParameters(componentInfo) << ">(" + << datamodel::GetCtorInjectedRefNames(componentInfo) << ", binders" + << ");"; } mStrStream << std::endl << std::endl << " return componentInstance;" << std::endl << "}" << std::endl; diff --git a/compendium/tools/SCRCodeGen/ComponentInfo.cpp b/compendium/tools/SCRCodeGen/ComponentInfo.cpp index 678213354..c335116c1 100644 --- a/compendium/tools/SCRCodeGen/ComponentInfo.cpp +++ b/compendium/tools/SCRCodeGen/ComponentInfo.cpp @@ -20,6 +20,7 @@ =============================================================================*/ +#include #include #include @@ -57,7 +58,7 @@ namespace codegen } std::string - GetCtorInjectedRefTypes(ComponentInfo const& compInfo) + GetCtorInjectedRefParameters(ComponentInfo const& compInfo) { std::string result; auto sep = ", "; @@ -65,7 +66,12 @@ namespace codegen { if ((true == compInfo.injectReferences) && (reference.policy == "static")) { - result += (sep + reference.interface); + if (reference.cardinality == "0..n" || reference.cardinality == "1..n") { + result += (sep + std::string("std::vector>"); + } + else { + result += (sep + std::string("std::shared_ptr<") + reference.interface + ">"); + } } } return result; diff --git a/compendium/tools/SCRCodeGen/ComponentInfo.hpp b/compendium/tools/SCRCodeGen/ComponentInfo.hpp index 2c279bacf..ee71deb93 100644 --- a/compendium/tools/SCRCodeGen/ComponentInfo.hpp +++ b/compendium/tools/SCRCodeGen/ComponentInfo.hpp @@ -67,6 +67,7 @@ namespace codegen std::string GetCtorInjectedRefTypes(ComponentInfo const& compInfo); std::string GetCtorInjectedRefNames(ComponentInfo const& compInfo); std::string GetReferenceBinderStr(ReferenceInfo const& ref, bool injectReferences); + std::string GetCtorInjectedRefParameters(ComponentInfo const& compInfo); } // namespace datamodel } // namespace codegen diff --git a/compendium/tools/SCRCodeGen/test/ReferenceAutogenFiles.hpp b/compendium/tools/SCRCodeGen/test/ReferenceAutogenFiles.hpp index b74eb80a6..949501de4 100644 --- a/compendium/tools/SCRCodeGen/test/ReferenceAutogenFiles.hpp +++ b/compendium/tools/SCRCodeGen/test/ReferenceAutogenFiles.hpp @@ -41,7 +41,7 @@ using scd::DynamicBinder; extern "C" US_ABI_EXPORT scd::ComponentInstance* NewInstance_DSSpellCheck_SpellCheckImpl() { std::vector>> binders; - scd::ComponentInstance* componentInstance = new (std::nothrow) scd::ComponentInstanceImpl, test::IDictionaryService>({{"dictionary"}}, binders); + scd::ComponentInstance* componentInstance = new (std::nothrow) scd::ComponentInstanceImpl, std::shared_ptr>({{"dictionary"}}, binders); return componentInstance; } @@ -65,7 +65,7 @@ namespace scd = cppmicroservices::service::component::detail; extern "C" US_ABI_EXPORT scd::ComponentInstance* NewInstance_DSSpellCheck_SpellCheckImpl() { std::vector>> binders; - scd::ComponentInstance* componentInstance = new (std::nothrow) scd::ComponentInstanceImpl, DictionaryService::IDictionaryService>({{"dictionary"}}, binders); + scd::ComponentInstance* componentInstance = new (std::nothrow) scd::ComponentInstanceImpl, std::shared_ptr>({{"dictionary"}}, binders); return componentInstance; } @@ -177,6 +177,30 @@ extern "C" US_ABI_EXPORT void DeleteInstance_FooImpl2(scd::ComponentInstance* co delete componentInstance; } +)manifestsrc"; + + const std::string REF_MULT_CARD = R"manifestsrc( +#include +#include +#include "cppmicroservices/servicecomponent/detail/ComponentInstanceImpl.hpp" +#include "SpellCheckerImpl.hpp" + +namespace sc = cppmicroservices::service::component; +namespace scd = cppmicroservices::service::component::detail; + +extern "C" US_ABI_EXPORT scd::ComponentInstance* NewInstance_DSSpellCheck_SpellCheckImpl() +{ + std::vector>> binders; + scd::ComponentInstance* componentInstance = new (std::nothrow) scd::ComponentInstanceImpl, std::vector>, std::shared_ptr>({{"dictionary", "foo"}}, binders); + + return componentInstance; +} + +extern "C" US_ABI_EXPORT void DeleteInstance_DSSpellCheck_SpellCheckImpl(scd::ComponentInstance* componentInstance) +{ + delete componentInstance; +} + )manifestsrc"; } // namespace codegen diff --git a/compendium/tools/SCRCodeGen/test/TestCodeGenerator.cpp b/compendium/tools/SCRCodeGen/test/TestCodeGenerator.cpp index 3ada67304..96e37f468 100644 --- a/compendium/tools/SCRCodeGen/test/TestCodeGenerator.cpp +++ b/compendium/tools/SCRCodeGen/test/TestCodeGenerator.cpp @@ -117,6 +117,30 @@ namespace codegen }] } } + )manifest"; + + const std::string manifest_multiple_cardinality_ref = R"manifest( + { + "scr" : { "version" : 1, + "components": [{ + "implementation-class": "DSSpellCheck::SpellCheckImpl", + "inject-references" : true, + "service": { + "interfaces": ["SpellCheck::ISpellCheckService"] + }, + "references": [{ + "name": "dictionary", + "interface": "DictionaryService::IDictionaryService", + "cardinality": "1..n" + }, + { + "name": "foo", + "interface": "Foo::Interface", + "cardinality": "1..1" + }] + }] + } + } )manifest"; const std::string manifest_no_scr = R"manifest( @@ -623,8 +647,9 @@ namespace codegen CodegenValidManifestState(manifest_mult_comp, { "A.hpp", "B.hpp", "C.hpp" }, REF_MULT_COMPS), // valid manifest with multiple components of the same implementation class CodegenValidManifestState(manifest_mult_comp_same_impl, - { "A.hpp", "B.hpp", "C.hpp" }, - REF_MULT_COMPS_SAME_IMPL))); + { "A.hpp", "B.hpp", "C.hpp" }, + REF_MULT_COMPS_SAME_IMPL), + CodegenValidManifestState(manifest_multiple_cardinality_ref, { "SpellCheckerImpl.hpp" }, REF_MULT_CARD))); // For the manifest specified in the member manifest, we expect the exception message // output by the code-generator to be exactly errorOutput. diff --git a/doc/src/CMakeLists.txt b/doc/src/CMakeLists.txt index a60517f0a..c34f08ea8 100644 --- a/doc/src/CMakeLists.txt +++ b/doc/src/CMakeLists.txt @@ -176,7 +176,7 @@ if(US_BUILD_TESTING) if(UNIX AND NOT APPLE AND BUILD_SHARED_LIBS) set(US_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${US_CXX_FLAGS}") # We need to supply the correct language flag to the Makefile - set(US_CXX_VER_FLAGS -std=c++14) + set(US_CXX_VER_FLAGS -std=c++17) usFunctionCheckCompilerFlags(${US_CXX_VER_FLAGS} _have_cxx14) if(NOT _have_cxx14) set(US_CXX_VER_FLAGS -std=c++0x) diff --git a/framework/CMakeLists.txt b/framework/CMakeLists.txt index 26d34424e..e34f8b81c 100644 --- a/framework/CMakeLists.txt +++ b/framework/CMakeLists.txt @@ -35,11 +35,11 @@ usMacroCreateBundle(Framework SYMBOLIC_NAME system_bundle SKIP_INIT # we do it manually EMBED_RESOURCE_METHOD LINK - PRIVATE_INCLUDE_DIRS src/util src/service src/bundle ../third_party ../third_party/absl + PRIVATE_INCLUDE_DIRS src/util src/service src/bundle ../third_party LINK_LIBRARIES ${_link_libraries} - SOURCES $ ${_framework_srcs} + SOURCES ${_framework_srcs} PUBLIC_HEADERS ${_framework_public_headers} - PRIVATE_HEADERS $ ${_framework_private_headers} + PRIVATE_HEADERS ${_framework_private_headers} BINARY_RESOURCES manifest.json ) diff --git a/framework/include/CMakeLists.txt b/framework/include/CMakeLists.txt index c7c4aa2c3..dd098e5c1 100644 --- a/framework/include/CMakeLists.txt +++ b/framework/include/CMakeLists.txt @@ -37,11 +37,13 @@ set(_public_headers cppmicroservices/ServiceRegistrationBase.h cppmicroservices/ServiceTracker.h cppmicroservices/ServiceTrackerCustomizer.h - cppmicroservices/detail/ServiceTracker.tpp + cppmicroservices/detail/ServiceTracker.hpp cppmicroservices/detail/ServiceTrackerPrivate.h - cppmicroservices/detail/ServiceTrackerPrivate.tpp + cppmicroservices/detail/ServiceTrackerPrivate.hpp + cppmicroservices/detail/TrackedBundle.h + cppmicroservices/detail/TrackedBundleListener.h cppmicroservices/detail/TrackedService.h - cppmicroservices/detail/TrackedService.tpp + cppmicroservices/detail/TrackedService.hpp cppmicroservices/detail/TrackedServiceListener.h cppmicroservices/Bundle.h @@ -55,10 +57,13 @@ set(_public_headers cppmicroservices/BundleResource.h cppmicroservices/BundleResourceStream.h cppmicroservices/BundleVersion.h + cppmicroservices/BundleTracker.h + cppmicroservices/BundleTrackerCustomizer.h cppmicroservices/Constants.h cppmicroservices/GetBundleContext.h + cppmicroservices/detail/BundleTrackerPrivate.h cppmicroservices/detail/BundleAbstractTracked.h - cppmicroservices/detail/BundleAbstractTracked.tpp + cppmicroservices/detail/BundleAbstractTracked.hpp cppmicroservices/detail/BundleResourceBuffer.h cppmicroservices/detail/ScopeGuard.h cppmicroservices/detail/CounterLatch.h diff --git a/framework/include/cppmicroservices/AnyMap.h b/framework/include/cppmicroservices/AnyMap.h index 5936d033d..74703286b 100644 --- a/framework/include/cppmicroservices/AnyMap.h +++ b/framework/include/cppmicroservices/AnyMap.h @@ -24,7 +24,7 @@ #define CPPMICROSERVICES_ANYMAP_H #include "cppmicroservices/Any.h" - +#include #include #include @@ -219,7 +219,28 @@ namespace cppmicroservices using iterator = iter; using const_iterator = const_iter; - any_map(map_type type); + /** + * @brief initializer_list constructor + * + * Construct an AnyMap of type "type" with the content of the + * initialized with the content of the the initializer list. Allows for inline + * initialization akin to: + * + * any_map cache_bundle1 { + * any_map::ORDERED_MAP, { + * {"a", std::string("A")}, + * {"b", std::string("B")}, + * {"c", std::string("C")} + * } + * }; + * + * The primary purpose of this constructor in any_map is in support of the initializer_list + * constructors in the AnyMap subclass. + * + * @param type the map_type of the AnyMap + * @param l a std::initializer_list used to initialize the content of the AnyMap. + */ + any_map(map_type type, std::initializer_list l = {}); any_map(ordered_any_map const& m); any_map(ordered_any_map&& m); any_map(unordered_any_map const& m); @@ -364,7 +385,40 @@ namespace cppmicroservices class US_Framework_EXPORT AnyMap : public any_map { public: - AnyMap(map_type type); + /** + * @brief initializer_list constructor + * + * Construct an AnyMap of type UNORDERED_MAP_CASEINSENSITIVE_KEYS with the content of the + * the initializer list. Allows for inline initialization akin to: + * + * AnyMap cache_bundle1 { + * {"a", std::string("A")}, + * {"b", std::string("B")}, + * {"c", std::string("C")} + * }; + * @param l a std::initializer_list used to initialize the content of the AnyMap. + * + */ + AnyMap(std::initializer_list l = {}); + /** + * @brief initializer_list constructor + * + * Construct an AnyMap of type "type" with the content of the the initializer list. Allows + * for inline initialization akin to: + * + * AnyMap cache_bundle1 { + * AnyMap::ORDERED_MAP, { + * {"a", std::string("A")}, + * {"b", std::string("B")}, + * {"c", std::string("C")} + * } + * }; + * + * @param type the map_type of the AnyMap + * @param l a std::initializer_list used to initialize the content of the AnyMap. + * + */ + AnyMap(map_type type, std::initializer_list l = {}); AnyMap(ordered_any_map const& m); AnyMap(ordered_any_map&& m); AnyMap(unordered_any_map const& m); diff --git a/framework/include/cppmicroservices/Bundle.h b/framework/include/cppmicroservices/Bundle.h index d75e4d6d6..a79023808 100644 --- a/framework/include/cppmicroservices/Bundle.h +++ b/framework/include/cppmicroservices/Bundle.h @@ -28,8 +28,10 @@ #include "cppmicroservices/GlobalConfig.h" #include +#include #include #include +#include #include namespace cppmicroservices @@ -865,4 +867,9 @@ namespace cppmicroservices US_Framework_EXPORT std::ostream& operator<<(std::ostream& os, Bundle::State state); } // namespace cppmicroservices +// Hashing behavior for Bundles +US_HASH_FUNCTION_BEGIN(cppmicroservices::Bundle) +return hash()(arg.GetSymbolicName()); +US_HASH_FUNCTION_END + #endif // CPPMICROSERVICES_BUNDLE_H diff --git a/framework/include/cppmicroservices/BundleContext.h b/framework/include/cppmicroservices/BundleContext.h index 0e4c33240..ce09c1732 100644 --- a/framework/include/cppmicroservices/BundleContext.h +++ b/framework/include/cppmicroservices/BundleContext.h @@ -49,6 +49,10 @@ namespace cppmicroservices class ServiceTrackerPrivate; template class TrackedService; + template + class BundleTrackerPrivate; + template + class TrackedBundle; } // namespace detail template @@ -292,7 +296,9 @@ namespace cppmicroservices * no properties. * @return A ServiceRegistration object for use by the bundle * registering the service to update the service's properties or to - * unregister the service. + * unregister the service. This object cannot be called from a discard-value + * expression as the intent is for the ServiceRegistration + * object is intended to be stored by the caller * * @throws std::runtime_error If this BundleContext is no longer valid, or if there are case variants of the same key in the supplied properties map. @@ -303,8 +309,8 @@ namespace cppmicroservices * @see ServiceFactory * @see PrototypeServiceFactory */ - ServiceRegistrationU RegisterService(InterfaceMapConstPtr const& service, - ServiceProperties const& properties = ServiceProperties()); + [[nodiscard]] ServiceRegistrationU RegisterService(InterfaceMapConstPtr const& service, + ServiceProperties const& properties = ServiceProperties()); /** * Registers the specified service object with the specified properties @@ -378,9 +384,9 @@ namespace cppmicroservices } /** - * Returns a list of ServiceReference objects. The returned - * list contains services that were registered under the specified class - * and match the specified filter expression. + * Returns a list of ServiceReference objects ordered + * by rank. The returned list contains services that were registered under + * the specified class and match the specified filter expression. * *

* The list is valid at the time of the call to this method. However, since @@ -416,7 +422,7 @@ namespace cppmicroservices * services. * @return A list of ServiceReference objects or * an empty list if no services are registered that satisfy the - * search. + * search. These objects will be in decreasing order of their rank. * @throws std::invalid_argument If the specified filter * contains an invalid filter expression that cannot be parsed. * @throws std::runtime_error If this BundleContext is no longer valid. @@ -427,10 +433,9 @@ namespace cppmicroservices std::string const& filter = std::string()); /** - * Returns a list of ServiceReference objects. The returned - * list contains services that - * were registered under the interface id of the template argument S - * and match the specified filter expression. + * Returns a list of ServiceReference objects ordered by rank. The returned + * list contains services that were registered under the interface id of + * the template argument S and match the specified filter expression. * *

* This method is identical to GetServiceReferences(const std::string&, const std::string&) except that @@ -441,7 +446,7 @@ namespace cppmicroservices * services. * @return A list of ServiceReference objects or * an empty list if no services are registered which satisfy the - * search. + * search. These objects will be in decreasing order of their rank. * @throws std::invalid_argument If the specified filter * contains an invalid filter expression that cannot be parsed. * @throws std::runtime_error If this BundleContext is no longer valid. @@ -455,8 +460,10 @@ namespace cppmicroservices { auto& clazz = us_service_interface_iid(); if (clazz.empty()) + { throw ServiceException("The service interface class has no " "CPPMICROSERVICES_DECLARE_SERVICE_INTERFACE macro"); + } using BaseVectorT = std::vector; BaseVectorT serviceRefs = GetServiceReferences(clazz, filter); std::vector> result; @@ -521,8 +528,10 @@ namespace cppmicroservices { auto& clazz = us_service_interface_iid(); if (clazz.empty()) + { throw ServiceException("The service interface class has no " "CPPMICROSERVICES_DECLARE_SERVICE_INTERFACE macro"); + } return ServiceReference(GetServiceReference(clazz)); } @@ -1127,14 +1136,15 @@ namespace cppmicroservices friend class detail::ServiceTrackerPrivate; template friend class detail::TrackedService; + template + friend class BundleTracker; + template + friend class detail::BundleTrackerPrivate; + template + friend class detail::TrackedBundle; friend class BundleResource; friend class BundleResourceContainer; - // Not for use by clients of the Framework. - // Provides access to the Framework's log sink to allow templated code - // to log diagnostic information. - std::shared_ptr GetLogSink() const; - ListenerToken AddServiceListener(ServiceListener const& delegate, void* data, std::string const& filter); void RemoveServiceListener(ServiceListener const& delegate, void* data); diff --git a/framework/include/cppmicroservices/BundleTracker.h b/framework/include/cppmicroservices/BundleTracker.h new file mode 100644 index 000000000..ffa7c7afd --- /dev/null +++ b/framework/include/cppmicroservices/BundleTracker.h @@ -0,0 +1,472 @@ +/*============================================================================= + + Library: CppMicroServices + + Copyright (c) The CppMicroServices developers. See the COPYRIGHT + file at the top-level directory of this distribution and at + https://github.com/CppMicroServices/CppMicroServices/COPYRIGHT . + + 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. + +=============================================================================*/ + +#ifndef CPPMICROSERVICES_BUNDLETRACKER_H +#define CPPMICROSERVICES_BUNDLETRACKER_H + +#include +#include +#include +#include +#include +#include + +#include "cppmicroservices/Bundle.h" +#include "cppmicroservices/BundleContext.h" +#include "cppmicroservices/BundleEvent.h" +#include "cppmicroservices/BundleTrackerCustomizer.h" +#include "cppmicroservices/detail/BundleTrackerPrivate.h" +#include "cppmicroservices/detail/TrackedBundle.h" + +namespace cppmicroservices +{ + + namespace detail + { + template + class TrackedBundle; + template + class BundleTrackerPrivate; + } // namespace detail + + class BundleContext; + + /** + \defgroup gr_bundletracker BundleTracker + + \brief Groups BundleTracker related symbols. + */ + + /** + * \ingroup MicroServices + * \ingroup gr_bundletracker + * + * The BundleTracker class allows for keeping an accurate record of bundles and handling + * state changes, including bundles started before the tracker is opened. + * + *

+ * A BundleTracker is constructed with a state mask and a BundleTrackerCustomizer object. + * The BundleTracker can select certain bundles to be tracked and trigger custom callbacks + * based on the provided BundleTrackerCustomizer or a BundleTracker subclass. + * Additionally, the template parameter allows for the use of custom tracked objects. Once the + * BundleTracker is opened, it will begin tracking all bundles that fall within the state mask. + * + *

+ * Use the getBundles method to get the tracked Bundle objects, and getObject + * to get the customized object. + * + *

+ * The BundleTracker class is thread-safe. It does not call BundleTrackerCustomizer + * methods while holding any locks. BundleTrackerCustomizer implementations must be thread-safe. + * + * @tparam T The type of tracked object. + * @remarks This class is thread safe. + */ + template + class BundleTracker : protected BundleTrackerCustomizer + { + public: + // The type of the tracking map + using TrackingMap = typename std::unordered_map; + + // The type of the state mask + using BundleStateMaskType = std::underlying_type_t; + + /** + * Create a BundleStateMaskType stateMask for a BundleTracker + * + * @param s0 the Bundle::State. + * @param s the subsequent Bundle::States. + * + * @return The state mask. + * + */ + template + static constexpr typename BundleTracker::BundleStateMaskType + CreateStateMask(S0 const& s0, S const&... s) + { + static_assert((std::is_enum_v && ... && std::is_enum_v), "The function requires enumerations."); + static_assert((std::is_same_v> && ... + && std::is_same_v>), + "All values must be Bundle States."); + return (s0 | ... | s); + } + + /** + * Create a BundleTracker that tracks bundles through states covered by the state mask. + * + * @param context The BundleContext from which tracking occurs. + * @param stateMask The bit mask which defines the bundle states to be tracked. + * @param customizer The customizer to call when bundles are added, modified, or removed. + * If the customizer is nullptr, then the callbacks in this BundleTracker will + * be used instead (default or can be overridden). + */ + BundleTracker(BundleContext const& context, + const BundleStateMaskType stateMask, + const std::shared_ptr> customizer = nullptr) noexcept + : d(std::make_unique>(this, context, stateMask, customizer)) + { + } + + /** + * Automatically closes the BundleTracker + */ + ~BundleTracker() override + { + try + { + Close(); + } + catch (...) + { + } + } + + /** + * Close this BundleTracker. + * + * Removes all tracked bundles from this BundleTracker, calling RemovedBundle on all + * of the currently tracked bundles. Also resets the tracking count. + */ + void + Close() noexcept + { + ListenerToken swappedToken; + { + auto l = d->Lock(); + US_UNUSED(l); + std::swap(d->listenerToken, swappedToken); + } + try + { + d->_context.RemoveListener(std::move(swappedToken)); + } + catch (std::runtime_error const&) + { + /* Rescue if context is stopped or invalid */ + } + + std::shared_ptr> outgoing = d->trackedBundle.Load(); + { + auto l = d->Lock(); + US_UNUSED(l); + + if (outgoing == nullptr) + { + return; + } + + // Check for already closed + if (d->Tracked()->closed) + { + return; + } + + outgoing->Close(); + d->Modified(); /* log message */ + outgoing->NotifyAll(); /* wake up any waiters */ + } + + try + { + outgoing->WaitOnCustomizersToFinish(); + } + catch (std::exception const&) + { + // Rescue CounterLatch issues + } + + auto bundles = GetBundles(); + for (auto& bundle : bundles) + { + outgoing->Untrack(bundle, BundleEvent()); + } + } + + /** + * Returns a vector of all the tracked bundles. + * + * @return A vector of Bundles (could be empty). + */ + std::vector + GetBundles() const noexcept + { + std::vector bundles; + auto t = d->Tracked(); + if (!t) + { /* If BundleTracker is not open */ + return bundles; + } + { + auto l = t->Lock(); + US_UNUSED(l); + d->GetBundles_unlocked(bundles, t.get()); + } + return bundles; + } + + /** + * Returns the custom object for the given Bundle if the given Bundle is tracked. + * Otherwise std::nullopt. + * + * @param bundle The Bundle paired with the object + * @return The custom object paired with the given Bundle or std::nullopt if the + * Bundle is not being tracked. + */ + std::optional + GetObject(Bundle const& bundle) const noexcept + { + auto t = d->Tracked(); + if (!t || !bundle) + { + /* If BundleTracker is not open or if bundle is invalid */ + return std::nullopt; + } + return t->Lock(), t->GetCustomizedObject_unlocked(bundle); + } + /** + * Returns an unordered map containing all of the currently tracked Bundles to their custom objects. + * + * @return An unordered map containing all of the Bundles currently tracked by this + * BundleTracker to their custom objects. + */ + TrackingMap + GetTracked() const noexcept + { + BundleTracker::TrackingMap trackingMap; + auto t = d->Tracked(); + if (!t) + { /* If BundleTracker is not open */ + return trackingMap; + } + { + auto l = t->Lock(); + US_UNUSED(l); + t->CopyEntries_unlocked(trackingMap); + } + return trackingMap; + } + + /** + * Returns the tracking count for this BundleTracker. + * + * The tracking count is set to 0 when the BundleTracker is opened. + * The tracking count increases by 1 anytime a Bundle is added, + * modified, or removed from the BundleTracker. + * Tracking counts from different times can be compared + * to determine whether any bundles have changed. + * If the BundleTracker is closed, return -1. + * + * @return The current tracking count, or -1 if the BundleTracker is closed. + */ + int + GetTrackingCount() const noexcept + { + auto t = d->Tracked(); + if (!t) + { /* if BundleTracker has not been opened */ + return -1; + } + { + auto l = t->Lock(); + US_UNUSED(l); + if (t->closed) + { /* if BundleTracker was closed */ + return -1; + } + return t->GetTrackingCount(); + } + } + + /** + * Returns true if and only if this BundleTracker is tracking no bundles. + * + * @return true if and only if this BundleTracker is tracking no bundles. + */ + bool + IsEmpty() const noexcept + { + auto t = d->Tracked(); + if (!t) + { /* If BundleTracker is not open */ + return true; + } + return (t->Lock(), t->IsEmpty_unlocked()); + } + + /** + * Open this BundleTracker to begin tracking bundles. + * + * Bundles that match the state mask will be tracked by this BundleTracker. + * + * @throws std::logic_error If the BundleContext used in the creation of this + * BundleTracker is no longer valid. + */ + void + Open() + { + std::shared_ptr> t; + { + auto l = d->Lock(); + US_UNUSED(l); + if (d->trackedBundle.Load() && !d->Tracked()->closed) + { /* If BundleTracker is open */ + return; + } + + t = std::make_shared>(this, d->GetCustomizer_unlocked()); + try + { + // Attempt to drop old listener + d->_context.RemoveListener(std::move(d->listenerToken)); + // Make new listener + d->listenerToken = d->_context.AddBundleListener( + std::bind(&detail::TrackedBundle::BundleChanged, t, std::placeholders::_1)); + + std::vector bundles = d->GetInitialBundles(d->_stateMask); + t->SetInitial(bundles); + } + catch (std::invalid_argument const& e) + { + // Remove listener and rethrow + d->_context.RemoveListener(std::move(d->listenerToken)); + throw std::runtime_error(std::string("unexpected std::invalid_argument exception: ") + e.what()); + } + d->trackedBundle.Store(t); + } + t->TrackInitial(); + } + + /** + * Remove a bundle from this BundleTracker. + * + * @param bundle the Bundle to be removed + * + * @see BundleTrackerCustomizer:RemovedBundle(Bundle, BundleEvent, T) + */ + void + Remove(Bundle const& bundle) noexcept + { + auto t = d->Tracked(); + if (!t) + { /* If BundleTracker is not open */ + return; + } + t->Untrack(bundle, BundleEvent()); + } + + /** + * Return the number of bundles being tracked by this BundleTracker. + * + * @return The number of tracked bundles. + */ + size_t + Size() const noexcept + { + auto t = d->Tracked(); + if (!t) + { /* If BundleTracker is not open */ + return 0; + } + return (t->Lock(), t->Size_unlocked()); + } + + /** + * Called when a Bundle is being added to the BundleTracker + * + * When a Bundle enters a state covered by the BundleTracker's state mask + * and the Bundle is not currently tracked, this method is called. + * This method is also called if the Bundle's state is covered by the state mask when + * the BundleTracker is opened. + * + * This method should return the object to be tracked for the specified Bundle + * if the BundleTracker is being extended. + * Otherwise, return the Bundle itself. If the return is std::nullopt, the Bundle is not tracked. + * + * The default, uncustomized behavior is to track the Bundle. + * + * @param bundle The Bundle being added to the BundleTracker. + * @param event the BundleEvent which was caught by the BundleTracker. + * + * @return The object to be tracked for the specified Bundle object or nullptr to avoid tracking + * the Bundle. + * + * @see BundleTrackerCustomizer::AddingBundle(Bundle, BundleEvent) + */ + virtual std::optional + AddingBundle(Bundle const& bundle, BundleEvent const&) override + { + return BundleTrackerCustomizer::ConvertToTrackedType(bundle); + } + + /** + * Called when a Bundle is modified that is being tracked by this BundleTracker. + * + * When a Bundle enters a state covered by the BundleTracker's state mask + * and the Bundle is currently tracked, this method is called. + * + * The default, uncustomized behavior is to no-op. + * + * @param bundle The tracked Bundle whose state has changed. + * @param event The BundleEvent which was caught by the BundleTracker. + * @param object The tracked object corresponding to the tracked Bundle. + * + * @see BundleTrackerCustomizer:ModifiedBundle(Bundle, BundleEvent, T) + */ + virtual void + ModifiedBundle(Bundle const&, BundleEvent const&, T const&) override + { + /* do nothing */ + } + + /** + * Called when a Bundle is removed that is being tracked by this BundleTracker. + * + * When a Bundle enters a state not covered by the BundleTracker's state mask + * and the Bundle is curently tracked, this method is called. + * This method is also called if the Bundle is currently being tracked when + * the BundleTracker is closed, or if Remove(Bundle) is called. + * + * The default, uncustomized behavior is to no-op. + * + * @param bundle The tracked Bundle that is being removed. + * @param event The BundleEvent which was caught by the BundleTracker. Can be null. + * @param object The tracked object corresponding to the tracked Bundle. + * + * @see BundleTrackerCustomizer:RemovedBundle(Bundle, BundleEvent, T) + */ + virtual void + RemovedBundle(Bundle const&, BundleEvent const&, T const&) override + { + /* do nothing */ + } + + private: + friend class detail::TrackedBundle; + friend class detail::BundleTrackerPrivate; + + std::unique_ptr> d; + }; + +} // namespace cppmicroservices + +#endif // CPPMICROSERVICES_BUNDLETRACKER_H diff --git a/framework/include/cppmicroservices/BundleTrackerCustomizer.h b/framework/include/cppmicroservices/BundleTrackerCustomizer.h new file mode 100644 index 000000000..0e49fbfa4 --- /dev/null +++ b/framework/include/cppmicroservices/BundleTrackerCustomizer.h @@ -0,0 +1,122 @@ +/*============================================================================= + + Library: CppMicroServices + + Copyright (c) The CppMicroServices developers. See the COPYRIGHT + file at the top-level directory of this distribution and at + https://github.com/CppMicroServices/CppMicroServices/COPYRIGHT . + + 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. + +=============================================================================*/ + +#ifndef CPPMICROSERVICES_BUNDLETRACKERCUSTOMIZER_H +#define CPPMICROSERVICES_BUNDLETRACKERCUSTOMIZER_H + +#include "cppmicroservices/Bundle.h" +#include +#include + +namespace cppmicroservices +{ + + /** + * \ingroup MicroServices + * \ingroup gr_bundletracker + * + * The BundleTrackerCustomizer interface allows for user callbacks to be included in a + * BundleTracker. These callback methods can customize the objects that are tracked, + * or trigger other behavior. + * A BundleTrackerCustomizer is called when a Bundle is being added to a + * BundleTracker, and it can then return an object for that tracked bundle. A + * BundleTrackerCustomizer, is also called when a tracked bundle is modified or has been removed from a + * BundleTracker. + * + *

+ * Bundle events are received synchronously by the BundleTracker, so it is recommended that + * implementations of the BundleTrackerCustomizer do not alter bundle states while being synchronized + * on any object. + * + * @tparam T The type of the tracked object. Defaults to Bundle. + * @remarks This class is thread safe. All implementations should also be thread safe. + */ + template + struct BundleTrackerCustomizer + { + + static T + ConvertToTrackedType(Bundle const& b) + { + if constexpr (std::is_same_v) + { + return b; + } + else + { + throw std::runtime_error("A custom BundleTrackerCustomizer instance is " + "required for custom tracked objects."); + } + } + + virtual ~BundleTrackerCustomizer() = default; + + /** + * Called when a Bundle is being added to the BundleTracker + * + * When a Bundle enters a state covered by the BundleTracker's state mask + * and the Bundle is not currently tracked, this method is called. + * This method is also called if the Bundle's state is covered by the state mask when + * the BundleTracker is opened. + * + * This method should return the object to be tracked for the specified Bundle + * if the BundleTracker is being extended. + * Otherwise, return the Bundle itself. If the return is std::nullopt, the Bundle is not tracked. + * + * @param bundle The Bundle being added to the BundleTracker. + * @param event the BundleEvent which was caught by the BundleTracker. + * + * @return The object to be tracked for the specified Bundle object or std::nullopt to avoid + * tracking the Bundle. + */ + virtual std::optional AddingBundle(Bundle const& bundle, BundleEvent const& event) = 0; + + /** + * Called when a Bundle is modified that is being tracked by this BundleTracker. + * + * When a Bundle enters a state covered by the BundleTracker's state mask + * and the Bundle is currently tracked, this method is called. + * + * @param bundle The tracked Bundle whose state has changed. + * @param event The BundleEvent which was caught by the BundleTracker. + * @param object The tracked object corresponding to the tracked Bundle. + */ + virtual void ModifiedBundle(Bundle const& bundle, BundleEvent const& event, T const& object) = 0; + + /** + * Called when a Bundle is removed that is being tracked by this BundleTracker. + * + * When a Bundle enters a state not covered by the BundleTracker's state mask + * and the Bundle is curently tracked, this method is called. + * This method is also called if the Bundle is currently being tracked when + * the BundleTracker is closed, or if Remove(Bundle) is called. + * + * @param bundle The tracked Bundle whose state has changed. + * @param event The BundleEvent which was caught by the BundleTracker. Can be null. + * @param object The tracked object corresponding to the tracked Bundle + */ + virtual void RemovedBundle(Bundle const& bundle, BundleEvent const& event, T const& object) = 0; + }; + +} // namespace cppmicroservices + +#endif // CPPMICROSERVICES_BUNDLETRACKERCUSTOMIZER_H diff --git a/framework/include/cppmicroservices/ServiceReferenceBase.h b/framework/include/cppmicroservices/ServiceReferenceBase.h index be0c66007..2c4f507ce 100644 --- a/framework/include/cppmicroservices/ServiceReferenceBase.h +++ b/framework/include/cppmicroservices/ServiceReferenceBase.h @@ -24,6 +24,7 @@ #define CPPMICROSERVICES_SERVICEREFERENCEBASE_H #include "cppmicroservices/Any.h" +#include "cppmicroservices/detail/Threads.h" #include #include @@ -232,13 +233,16 @@ namespace cppmicroservices */ ServiceReferenceBase(); - ServiceReferenceBase(ServiceRegistrationBasePrivate* reg); + ServiceReferenceBase(std::shared_ptr reg); void SetInterfaceId(std::string const& interfaceId); // This class is not thread-safe, but we support thread-safe - // copying and assignment. - std::atomic d; + // copying and assignment + // This was changed to a std::shared_ptr and is accessed through + // Load(), but when this repository uses c++20, + // this will be changed to std::atomic + cppmicroservices::detail::Atomic> d; }; /** diff --git a/framework/include/cppmicroservices/ServiceRegistrationBase.h b/framework/include/cppmicroservices/ServiceRegistrationBase.h index cd5605deb..a9a90d579 100644 --- a/framework/include/cppmicroservices/ServiceRegistrationBase.h +++ b/framework/include/cppmicroservices/ServiceRegistrationBase.h @@ -1,22 +1,22 @@ - /*============================================================================= +/*============================================================================= - Library: CppMicroServices + Library: CppMicroServices - Copyright (c) The CppMicroServices developers. See the COPYRIGHT - file at the top-level directory of this distribution and at - https://github.com/CppMicroServices/CppMicroServices/COPYRIGHT . + Copyright (c) The CppMicroServices developers. See the COPYRIGHT + file at the top-level directory of this distribution and at + https://github.com/CppMicroServices/CppMicroServices/COPYRIGHT . - 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 + 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 + 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. + 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. =============================================================================*/ @@ -30,6 +30,7 @@ namespace cppmicroservices { + class ServiceRegistrationLocks; class BundlePrivate; class ServiceRegistrationBasePrivate; class Properties; @@ -196,11 +197,13 @@ namespace cppmicroservices */ ServiceRegistrationBase(); - ServiceRegistrationBase(ServiceRegistrationBasePrivate* registrationPrivate); + ServiceRegistrationBase(std::shared_ptr registrationPrivate); ServiceRegistrationBase(BundlePrivate* bundle, InterfaceMapConstPtr const& service, Properties&& props); - ServiceRegistrationBasePrivate* d { nullptr }; + ServiceRegistrationLocks LockServiceRegistration() const; + + std::shared_ptr d; }; /** @@ -224,7 +227,7 @@ namespace cppmicroservices */ US_HASH_FUNCTION_BEGIN(cppmicroservices::ServiceRegistrationBase) -return std::hash()(arg.d); +return std::hash>()(arg.d); US_HASH_FUNCTION_END #endif // CPPMICROSERVICES_SERVICEREGISTRATIONBASE_H diff --git a/framework/include/cppmicroservices/ServiceTracker.h b/framework/include/cppmicroservices/ServiceTracker.h index 127cb0fb8..760db4f45 100644 --- a/framework/include/cppmicroservices/ServiceTracker.h +++ b/framework/include/cppmicroservices/ServiceTracker.h @@ -242,6 +242,10 @@ namespace cppmicroservices *

* This implementation calls GetService() to determine if a service * is being tracked. + * + * @throws std::logic_error If the BundleContext + * with which this ServiceTracker was created is no + * longer valid or became invalid while waiting on Service. * * @return The result of GetService(). */ @@ -265,6 +269,9 @@ namespace cppmicroservices * @param rel_time The relative time duration to wait for a service. If * zero, the method will wait indefinitely. * @throws std::invalid_argument exception if \c rel_time is negative. + * @throws std::logic_error If the BundleContext + * with which this ServiceTracker was created is no + * longer valid or became invalid while waiting on Service. * @return The result of GetService(). */ template @@ -485,6 +492,6 @@ namespace cppmicroservices }; } // namespace cppmicroservices -#include "cppmicroservices/detail/ServiceTracker.tpp" +#include "cppmicroservices/detail/ServiceTracker.hpp" #endif // CPPMICROSERVICES_SERVICETRACKER_H diff --git a/framework/include/cppmicroservices/detail/BundleAbstractTracked.h b/framework/include/cppmicroservices/detail/BundleAbstractTracked.h index c26907d65..ed3c6b397 100644 --- a/framework/include/cppmicroservices/detail/BundleAbstractTracked.h +++ b/framework/include/cppmicroservices/detail/BundleAbstractTracked.h @@ -28,6 +28,7 @@ #include "cppmicroservices/detail/WaitCondition.h" #include +#include #include namespace cppmicroservices @@ -48,19 +49,16 @@ namespace cppmicroservices * implementation of the Tracker class. * * @tparam S The tracked item. It is the key. - * @tparam TTT Type traits with TTT::TrackedType representing the value type mapped to the tracked item. + * @tparam T the value type mapped to the tracked item. * @tparam R The reason the tracked item is being tracked or untracked. * @remarks This class is thread safe. */ - template + template class BundleAbstractTracked : public MultiThreaded, WaitCondition> { public: - using T = typename TTT::TrackedType; - using TrackedParamType = typename TTT::TrackedParamType; - - using TrackingMap = std::unordered_map>; + using TrackingMap = std::unordered_map; /** * BundleAbstractTracked constructor. @@ -139,7 +137,7 @@ namespace cppmicroservices * * @GuardedBy this */ - std::shared_ptr GetCustomizedObject_unlocked(S item) const; + std::optional GetCustomizedObject_unlocked(S item) const; /** * Return the list of tracked items. @@ -189,7 +187,7 @@ namespace cppmicroservices * @return Customized object for the tracked item or null if * the item is not to be tracked. */ - virtual std::shared_ptr CustomizerAdding(S item, R const& related) = 0; + virtual std::optional CustomizerAdding(S item, R const& related) = 0; /** * Call the specific customizer modified method. This method must not be @@ -199,8 +197,7 @@ namespace cppmicroservices * @param related Action related object. * @param object Customized object for the tracked item. */ - virtual void CustomizerModified(S item, R const& related, std::shared_ptr const& object) - = 0; + virtual void CustomizerModified(S item, R const& related, T const& object) = 0; /** * Call the specific customizer removed method. This method must not be @@ -210,8 +207,7 @@ namespace cppmicroservices * @param related Action related object. * @param object Customized object for the tracked item. */ - virtual void CustomizerRemoved(S item, R const& related, std::shared_ptr const& object) - = 0; + virtual void CustomizerRemoved(S item, R const& related, T const& object) = 0; /** * List of items in the process of being added. This is used to deal with @@ -264,7 +260,7 @@ namespace cppmicroservices void TrackAdding(S item, R related); private: - using Self = BundleAbstractTracked; + using Self = BundleAbstractTracked; /** * Map of tracked items to customized objects. @@ -281,13 +277,13 @@ namespace cppmicroservices BundleContext bc; - bool CustomizerAddingFinal(S item, std::shared_ptr const& custom); + bool CustomizerAddingFinal(S item, std::optional const& custom); }; } // namespace detail } // namespace cppmicroservices -#include "cppmicroservices/detail/BundleAbstractTracked.tpp" +#include "cppmicroservices/detail/BundleAbstractTracked.hpp" #endif // CPPMICROSERVICES_BUNDLEABSTRACTTRACKED_H diff --git a/framework/include/cppmicroservices/detail/BundleAbstractTracked.hpp b/framework/include/cppmicroservices/detail/BundleAbstractTracked.hpp new file mode 100644 index 000000000..c9a4d20b3 --- /dev/null +++ b/framework/include/cppmicroservices/detail/BundleAbstractTracked.hpp @@ -0,0 +1,332 @@ +/*============================================================================= + + Library: CppMicroServices + + Copyright (c) The CppMicroServices developers. See the COPYRIGHT + file at the top-level directory of this distribution and at + https://github.com/CppMicroServices/CppMicroServices/COPYRIGHT . + + 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. + +=============================================================================*/ + +#include "cppmicroservices/BundleContext.h" +#include "cppmicroservices/detail/Log.h" + +#include + +namespace cppmicroservices +{ + + namespace detail + { + + template + BundleAbstractTracked::BundleAbstractTracked(BundleContext context) + : closed(false) + , trackingCount(0) + , bc(std::move(context)) + { + } + + template + BundleAbstractTracked::~BundleAbstractTracked() = default; + + template + void + BundleAbstractTracked::SetInitial(std::vector const& initiallist) + { + std::copy(initiallist.begin(), initiallist.end(), std::back_inserter(initial)); + } + + template + void + BundleAbstractTracked::TrackInitial() + { + while (true) + { + S item; + { + auto l = this->Lock(); + US_UNUSED(l); + if (closed || (initial.size() == 0)) + { + /* + * if there are no more initial items + */ + return; /* we are done */ + } + /* + * move the first item from the initial list to the adding list + * within this synchronized block. + */ + item = initial.front(); + initial.pop_front(); + if (tracked.end() != tracked.find(item)) + { + /* if we are already tracking this item */ + continue; /* skip this item */ + } + if (std::find(adding.begin(), adding.end(), item) != adding.end()) + { + /* + * if this item is already in the process of being added. + */ + continue; /* skip this item */ + } + adding.push_back(item); + } + TrackAdding(item, R()); + /* + * Begin tracking it. We call trackAdding + * since we have already put the item in the + * adding list. + */ + } + } + + template + void + BundleAbstractTracked::Close() + { + closed = true; + } + + template + void + BundleAbstractTracked::Track(S item, R related) + { + bool isInMap = false; + T object; + { + auto l = this->Lock(); + US_UNUSED(l); + if (closed) + { + return; + } + auto trackedItemIter = tracked.find(item); + if (trackedItemIter != tracked.end()) + { + object = trackedItemIter->second; + isInMap = true; + } + if (!isInMap) + { /* we are not tracking the item */ + if (std::find(adding.begin(), adding.end(), item) != adding.end()) + { + /* if this item is already in the process of being added. */ + return; + } + adding.push_back(item); /* mark this item is being added */ + } + else + { /* we are currently tracking this item */ + Modified(); /* increment modification count */ + } + } + + if (!isInMap) + { /* we are not tracking the item */ + TrackAdding(item, related); + } + else + { + /* Call customizer outside of synchronized region */ + CustomizerModified(item, related, object); + /* + * If the customizer throws an unchecked exception, it is safe to + * let it propagate + */ + } + } + + template + void + BundleAbstractTracked::Untrack(S item, R related) + { + T object; + { + auto l = this->Lock(); + US_UNUSED(l); + std::size_t initialSize = initial.size(); + initial.remove(item); + if (initialSize != initial.size()) + { /* if this item is already in the list + * of initial references to process + */ + return; /* we have removed it from the list and it will not be + * processed + */ + } + + std::size_t addingSize = adding.size(); + adding.remove(item); + if (addingSize != adding.size()) + { /* if the item is in the process of + * being added + */ + return; /* + * in case the item is untracked while in the process of + * adding + */ + } + auto trackedItemIter = tracked.find(item); + // nothing to do, no item is being tracked. + if (trackedItemIter == tracked.end()) + { + return; + } + /* + * must remove from tracker before + * calling customizer callback + */ + object = trackedItemIter->second; + tracked.erase(item); + Modified(); /* increment modification count */ + } + /* Call customizer outside of synchronized region */ + CustomizerRemoved(item, related, object); + /* + * If the customizer throws an unchecked exception, it is safe to let it + * propagate + */ + } + + template + std::size_t + BundleAbstractTracked::Size_unlocked() const + { + return tracked.size(); + } + + template + bool + BundleAbstractTracked::IsEmpty_unlocked() const + { + return tracked.empty(); + } + + template + std::optional + BundleAbstractTracked::GetCustomizedObject_unlocked(S item) const + { + typename TrackingMap::const_iterator i = tracked.find(item); + if (i != tracked.end()) + { + return std::optional(i->second); + } + + return std::nullopt; + } + + template + void + BundleAbstractTracked::GetTracked_unlocked(std::vector& items) const + { + for (auto& i : tracked) + { + items.push_back(i.first); + } + } + + template + void + BundleAbstractTracked::Modified() + { + // atomic + ++trackingCount; + } + + template + int + BundleAbstractTracked::GetTrackingCount() const + { + // atomic + return trackingCount; + } + + template + void + BundleAbstractTracked::CopyEntries_unlocked(TrackingMap& map) const + { + map.insert(tracked.begin(), tracked.end()); + } + + template + bool + BundleAbstractTracked::CustomizerAddingFinal(S item, std::optional const& custom) + { + auto l = this->Lock(); + US_UNUSED(l); + std::size_t addingSize = adding.size(); + adding.remove(item); + if (addingSize != adding.size() && !closed) + { + /* + * if the item was not untracked during the customizer + * callback + */ + if (custom) + { + tracked[item] = custom.value(); + Modified(); /* increment modification count */ + this->NotifyAll(); /* notify any waiters */ + } + return false; + } + else + { + return true; + } + } + + template + void + BundleAbstractTracked::TrackAdding(S item, R related) + { + std::optional object; + bool becameUntracked = false; + /* Call customizer outside of synchronized region */ + try + { + object = CustomizerAdding(item, related); + becameUntracked = this->CustomizerAddingFinal(item, object); + } + catch (...) + { + /* + * If the customizer throws an exception, it will + * propagate after the cleanup code. + */ + this->CustomizerAddingFinal(item, object); + throw; + } + + /* + * The item became untracked during the customizer callback. + */ + if (becameUntracked && object) + { + /* Call customizer outside of synchronized region */ + CustomizerRemoved(item, related, object.value()); + /* + * If the customizer throws an unchecked exception, it is safe to + * let it propagate + */ + } + } + + } // namespace detail + +} // namespace cppmicroservices diff --git a/framework/include/cppmicroservices/detail/BundleAbstractTracked.tpp b/framework/include/cppmicroservices/detail/BundleAbstractTracked.tpp deleted file mode 100644 index c67d6bc68..000000000 --- a/framework/include/cppmicroservices/detail/BundleAbstractTracked.tpp +++ /dev/null @@ -1,323 +0,0 @@ -/*============================================================================= - - Library: CppMicroServices - - Copyright (c) The CppMicroServices developers. See the COPYRIGHT - file at the top-level directory of this distribution and at - https://github.com/CppMicroServices/CppMicroServices/COPYRIGHT . - - 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. - -=============================================================================*/ - -#include "cppmicroservices/BundleContext.h" -#include "cppmicroservices/detail/Log.h" - -#include - -namespace cppmicroservices { - -namespace detail { - -template -BundleAbstractTracked::BundleAbstractTracked(BundleContext bc) - : closed(false), trackingCount(0), bc(bc) -{ -} - -template -BundleAbstractTracked::~BundleAbstractTracked() -= default; - -template -void BundleAbstractTracked::SetInitial(const std::vector& initiallist) -{ - std::copy(initiallist.begin(), initiallist.end(), std::back_inserter(initial)); - - if (bc.GetLogSink()->Enabled()) - { - for(typename std::list::const_iterator item = initial.begin(); - item != initial.end(); ++item) - { - DIAG_LOG(*bc.GetLogSink()) << "BundleAbstractTracked::setInitial: " << (*item); - } - } -} - -template -void BundleAbstractTracked::TrackInitial() -{ - while (true) - { - S item; - { - auto l = this->Lock(); US_UNUSED(l); - if (closed || (initial.size() == 0)) - { - /* - * if there are no more initial items - */ - return; /* we are done */ - } - /* - * move the first item from the initial list to the adding list - * within this synchronized block. - */ - item = initial.front(); - initial.pop_front(); - if (tracked.end() != tracked.find(item)) - { - /* if we are already tracking this item */ - DIAG_LOG(*bc.GetLogSink()) << "BundleAbstractTracked::trackInitial[already tracked]: " << item; - continue; /* skip this item */ - } - if (std::find(adding.begin(), adding.end(), item) != adding.end()) - { - /* - * if this item is already in the process of being added. - */ - DIAG_LOG(*bc.GetLogSink()) << "BundleAbstractTracked::trackInitial[already adding]: " << item; - continue; /* skip this item */ - } - adding.push_back(item); - } - DIAG_LOG(*bc.GetLogSink()) << "BundleAbstractTracked::trackInitial: " << item; - TrackAdding(item, R()); - /* - * Begin tracking it. We call trackAdding - * since we have already put the item in the - * adding list. - */ - } -} - -template -void BundleAbstractTracked::Close() -{ - closed = true; -} - -template -void BundleAbstractTracked::Track(S item, R related) -{ - std::shared_ptr object; - { - auto l = this->Lock(); US_UNUSED(l); - if (closed) - { - return; - } - auto trackedItemIter = tracked.find(item); - if (trackedItemIter != tracked.end()) { - object = trackedItemIter->second; - } - if (!object) - { /* we are not tracking the item */ - if (std::find(adding.begin(), adding.end(), item) != adding.end()) - { - /* if this item is already in the process of being added. */ - DIAG_LOG(*bc.GetLogSink()) << "BundleAbstractTracked::track[already adding]: " << item; - return; - } - adding.push_back(item); /* mark this item is being added */ - } - else - { /* we are currently tracking this item */ - DIAG_LOG(*bc.GetLogSink()) << "BundleAbstractTracked::track[modified]: " << item; - Modified(); /* increment modification count */ - } - } - - if (!object) - { /* we are not tracking the item */ - TrackAdding(item, related); - } - else - { - /* Call customizer outside of synchronized region */ - CustomizerModified(item, related, object); - /* - * If the customizer throws an unchecked exception, it is safe to - * let it propagate - */ - } -} - -template -void BundleAbstractTracked::Untrack(S item, R related) -{ - std::shared_ptr object; - { - auto l = this->Lock(); US_UNUSED(l); - std::size_t initialSize = initial.size(); - initial.remove(item); - if (initialSize != initial.size()) - { /* if this item is already in the list - * of initial references to process - */ - DIAG_LOG(*bc.GetLogSink()) << "BundleAbstractTracked::untrack[removed from initial]: " << item; - return; /* we have removed it from the list and it will not be - * processed - */ - } - - std::size_t addingSize = adding.size(); - adding.remove(item); - if (addingSize != adding.size()) - { /* if the item is in the process of - * being added - */ - DIAG_LOG(*bc.GetLogSink()) << "BundleAbstractTracked::untrack[being added]: " << item; - return; /* - * in case the item is untracked while in the process of - * adding - */ - } - auto trackedItemIter = tracked.find(item); - // nothing to do, no item is being tracked. - if (trackedItemIter == tracked.end()) { - return; - } - /* - * must remove from tracker before - * calling customizer callback - */ - object = trackedItemIter->second; - tracked.erase(item); - Modified(); /* increment modification count */ - } - DIAG_LOG(*bc.GetLogSink()) << "BundleAbstractTracked::untrack[removed]: " << item; - /* Call customizer outside of synchronized region */ - CustomizerRemoved(item, related, object); - /* - * If the customizer throws an unchecked exception, it is safe to let it - * propagate - */ -} - -template -std::size_t BundleAbstractTracked::Size_unlocked() const -{ - return tracked.size(); -} - -template -bool BundleAbstractTracked::IsEmpty_unlocked() const -{ - return tracked.empty(); -} - -template -std::shared_ptr::TrackedParamType> -BundleAbstractTracked::GetCustomizedObject_unlocked(S item) const -{ - typename TrackingMap::const_iterator i = tracked.find(item); - if (i != tracked.end()) return i->second; - return std::shared_ptr(); -} - -template -void BundleAbstractTracked::GetTracked_unlocked(std::vector& items) const -{ - for (auto& i : tracked) - { - items.push_back(i.first); - } -} - -template -void BundleAbstractTracked::Modified() -{ - // atomic - ++trackingCount; -} - -template -int BundleAbstractTracked::GetTrackingCount() const -{ - // atomic - return trackingCount; -} - -template -void BundleAbstractTracked::CopyEntries_unlocked(TrackingMap& map) const -{ - map.insert(tracked.begin(), tracked.end()); -} - -template -bool BundleAbstractTracked::CustomizerAddingFinal(S item, const std::shared_ptr& custom) -{ - auto l = this->Lock(); US_UNUSED(l); - std::size_t addingSize = adding.size(); - adding.remove(item); - if (addingSize != adding.size() && !closed) - { - /* - * if the item was not untracked during the customizer - * callback - */ - if (custom) - { - tracked[item] = custom; - Modified(); /* increment modification count */ - this->NotifyAll(); /* notify any waiters */ - } - return false; - } - else - { - return true; - } -} - -template -void BundleAbstractTracked::TrackAdding(S item, R related) -{ - DIAG_LOG(*bc.GetLogSink()) << "BundleAbstractTracked::trackAdding:" << item; - std::shared_ptr object; - bool becameUntracked = false; - /* Call customizer outside of synchronized region */ - try - { - object = CustomizerAdding(item, related); - becameUntracked = this->CustomizerAddingFinal(item, object); - } - catch (...) - { - /* - * If the customizer throws an exception, it will - * propagate after the cleanup code. - */ - this->CustomizerAddingFinal(item, object); - throw; - } - - /* - * The item became untracked during the customizer callback. - */ - if (becameUntracked && object) - { - DIAG_LOG(*bc.GetLogSink()) << "BundleAbstractTracked::trackAdding[removed]: " << item; - /* Call customizer outside of synchronized region */ - CustomizerRemoved(item, related, object); - /* - * If the customizer throws an unchecked exception, it is safe to - * let it propagate - */ - } -} - -} // namespace detail - -} // namespace cppmicroservices diff --git a/framework/include/cppmicroservices/detail/BundleTrackerPrivate.h b/framework/include/cppmicroservices/detail/BundleTrackerPrivate.h new file mode 100644 index 000000000..29463f6cf --- /dev/null +++ b/framework/include/cppmicroservices/detail/BundleTrackerPrivate.h @@ -0,0 +1,167 @@ +/*============================================================================= + + Library: CppMicroServices + + Copyright (c) The CppMicroServices developers. See the COPYRIGHT + file at the top-level directory of this distribution and at + https://github.com/CppMicroServices/CppMicroServices/COPYRIGHT . + + 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. + +=============================================================================*/ + +#ifndef CPPMICROSERVICES_BUNDLETRACKERPRIVATE_H +#define CPPMICROSERVICES_BUNDLETRACKERPRIVATE_H + +#include "cppmicroservices/Bundle.h" +#include "cppmicroservices/BundleContext.h" +#include "cppmicroservices/Constants.h" +#include "cppmicroservices/detail/Threads.h" +#include "cppmicroservices/detail/TrackedBundle.h" + +#include +#include +#include + +namespace cppmicroservices +{ + + template + class BundleTracker; + + namespace detail + { + + /** + * \ingroup MicroServices + */ + template + class BundleTrackerPrivate : MultiThreaded<> + { + + public: + using BundleStateMaskType = std::underlying_type_t; + + BundleTrackerPrivate(BundleTracker* bundleTracker, + BundleContext const& context, + BundleStateMaskType const stateMask, + std::shared_ptr> const customizer) + : _context(context) + , _stateMask(stateMask) + , _customizer(customizer) + , listenerToken() + , trackedBundle() + , _bundleTracker(bundleTracker) + { + } + ~BundleTrackerPrivate() = default; + + /** + * Returns the list of initial Bundles that will be + * tracked by the BundleTracker. + * + * @param stateMask The mask of states to filter bundles + * + * @return The list of initial Bundles. + */ + std::vector + GetInitialBundles(BundleStateMaskType stateMask) + { + std::vector result; + auto contextBundles = _context.GetBundles(); + for (Bundle bundle : contextBundles) + { + if (bundle.GetState() & stateMask) + { + result.push_back(bundle); + } + } + return result; + } + + void + GetBundles_unlocked(std::vector& refs, TrackedBundle* t) const + { + if (t->Size_unlocked() == 0) + { + return; + } + t->GetTracked_unlocked(refs); + } + + /** + * The Bundle Context used by this BundleTracker. + */ + BundleContext _context; + + /** + * State mask for tracked bundles. + */ + BundleStateMaskType const _stateMask; + + /** + * The BundleTrackerCustomizer for this tracker. + */ + std::shared_ptr> _customizer; + + /** + * This token corresponds to the BundleListener, whenever it is added. + * Otherwise, it represents an invalid token. + */ + ListenerToken listenerToken; + + /** + * Tracked bundles: Bundle -> custom Object + */ + Atomic>> trackedBundle; + + /** + * Accessor method for the current TrackedBundle object. This method is only + * intended to be used by the unsynchronized methods which do not modify the + * trackedBundle field. + * + * @return The current Tracked object. + */ + std::shared_ptr> + Tracked() const + { + return trackedBundle.Load(); + } + + /** + * Called by the TrackedBundle object whenever the set of tracked bundles is + * modified. + */ + void + Modified() + { + // No cache to clear + } + + inline BundleTrackerCustomizer* + GetCustomizer_unlocked() + { + return (_customizer ? _customizer.get() : static_cast*>(_bundleTracker)); + } + + private: + friend class BundleTracker; + + BundleTracker* const _bundleTracker; + }; + + } // namespace detail + +} // namespace cppmicroservices + +#endif // CPPMICROSERVICES_BUNDLETRACKERPRIVATE_H diff --git a/framework/include/cppmicroservices/detail/ServiceTracker.hpp b/framework/include/cppmicroservices/detail/ServiceTracker.hpp new file mode 100755 index 000000000..9387a1612 --- /dev/null +++ b/framework/include/cppmicroservices/detail/ServiceTracker.hpp @@ -0,0 +1,553 @@ +/*============================================================================= + + Library: CppMicroServices + + Copyright (c) The CppMicroServices developers. See the COPYRIGHT + file at the top-level directory of this distribution and at + https://github.com/CppMicroServices/CppMicroServices/COPYRIGHT . + + 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. + +=============================================================================*/ + +#include "cppmicroservices/Bundle.h" +#include "cppmicroservices/BundleContext.h" +#include "cppmicroservices/ServiceException.h" + +#include "cppmicroservices/detail/ServiceTrackerPrivate.h" +#include "cppmicroservices/detail/TrackedService.h" + +#include +#include +#include +#include +#include + +namespace cppmicroservices +{ + + template + ServiceTracker::~ServiceTracker() + { + try + { + Close(); + } + catch (...) + { + } + } + +#ifdef _MSC_VER +# pragma warning(push) +# pragma warning(disable : 4355) +#endif + + template + ServiceTracker::ServiceTracker(BundleContext const& context, + ServiceReference const& reference, + _ServiceTrackerCustomizer* customizer) + : d(new _ServiceTrackerPrivate(this, context, reference, customizer)) + { + } + + template + ServiceTracker::ServiceTracker(BundleContext const& context, + std::string const& clazz, + _ServiceTrackerCustomizer* customizer) + : d(new _ServiceTrackerPrivate(this, context, clazz, customizer)) + { + } + + template + ServiceTracker::ServiceTracker(BundleContext const& context, + LDAPFilter const& filter, + _ServiceTrackerCustomizer* customizer) + : d(new _ServiceTrackerPrivate(this, context, filter, customizer)) + { + } + + template + ServiceTracker::ServiceTracker(BundleContext const& context, _ServiceTrackerCustomizer* customizer) + : d(new _ServiceTrackerPrivate(this, context, us_service_interface_iid(), customizer)) + { + std::string clazz = us_service_interface_iid(); + if (clazz.empty()) + { + throw ServiceException("The service interface class has no " + "CPPMICROSERVICES_DECLARE_SERVICE_INTERFACE macro"); + } + } + +#ifdef _MSC_VER +# pragma warning(pop) +#endif + + template + void + ServiceTracker::Open() + { + std::shared_ptr<_TrackedService> t; + { + auto l = d->Lock(); + US_UNUSED(l); + if (d->trackedService.Load() && !d->Tracked()->closed) + { + return; + } + + t.reset(new _TrackedService(this, d->customizer)); + try + { + /* Remove if already exists. No-op if it's an invalid (default) token */ + d->context.RemoveListener(std::move(d->listenerToken)); + d->listenerToken = d->context.AddServiceListener( + std::bind(&_TrackedService::ServiceChanged, t, std::placeholders::_1), + d->listenerFilter); + std::vector> references; + if (!d->trackClass.empty()) + { + references = d->GetInitialReferences(d->trackClass, std::string()); + } + else + { + if (d->trackReference.GetBundle()) + { + references.push_back(d->trackReference); + } + else + { /* user supplied filter */ + references = d->GetInitialReferences(std::string(), + (d->listenerFilter.empty()) ? d->filter.ToString() + : d->listenerFilter); + } + } + /* set tracked with the initial references */ + t->SetInitial(references); + } + catch (std::invalid_argument const& e) + { + d->context.RemoveListener(std::move(d->listenerToken)); + throw std::runtime_error(std::string("unexpected std::invalid_argument exception: ") + e.what()); + } + d->trackedService.Store(t); + } + /* Call tracked outside of synchronized region */ + t->TrackInitial(); /* process the initial references */ + } + + template + void + ServiceTracker::Close() + { + + /* + The call to RemoveListener() below must be done while the ServiceTracker object is unlocked because of a + possibility for reentry from customer code. Therefore, we have to swap d->listenerToken to a local variable and + replace it with a default constructed ListenerToken object. + */ + ListenerToken swappedToken; + { + auto l = d->Lock(); + US_UNUSED(l); + std::swap(d->listenerToken, swappedToken); + } + try + { + d->context.RemoveListener(std::move(swappedToken)); + } + catch (std::runtime_error const& /*e*/) + { + /* In case the context was stopped or invalid. */ + } + + std::shared_ptr<_TrackedService> outgoing = d->trackedService.Load(); + { + auto l = d->Lock(); + US_UNUSED(l); + + if (outgoing == nullptr) + { + return; + } + + if (d->Tracked()->closed) + { + return; + } + + outgoing->Close(); + + d->Modified(); /* clear the cache */ + outgoing->NotifyAll(); /* wake up any waiters */ + } + + try + { + outgoing->WaitOnCustomizersToFinish(); + } + catch (std::exception const&) + { + // this can throw if the latch's count + // is negative, which means the latch + // cannot be used anymore. This can occur + // when multiple threads are opening + // and closing the same service tracker + // repeatedly. + } + + auto references = GetServiceReferences(); + for (auto& ref : references) + { + outgoing->Untrack(ref, ServiceEvent()); + } + } + + template + std::shared_ptr::TrackedParamType> + ServiceTracker::WaitForService() + { + return WaitForService(std::chrono::milliseconds::zero()); + } + + template + template + std::shared_ptr::TrackedParamType> + ServiceTracker::WaitForService(std::chrono::duration const& rel_time) + { + if (rel_time.count() < 0) + { + throw std::invalid_argument("negative timeout"); + } + + auto object = GetService(); + if (object) + { + return object; + } + + using D = std::chrono::duration; + + auto timeout = rel_time; + auto endTime = (rel_time == D::zero()) ? std::chrono::steady_clock::time_point() + : (std::chrono::steady_clock::now() + rel_time); + do + { + auto t = d->Tracked(); + if (!t) + { /* if ServiceTracker is not open */ + return std::shared_ptr(); + } + + { + auto l = t->Lock(); + if (t->Size_unlocked() == 0) + { + BundleContext a = d->context; + if (!a) + { + throw std::logic_error("The bundle context cannot be null."); + } + + if (rel_time == std::chrono::milliseconds::zero()) + { + while (!t->WaitFor(l, + std::chrono::milliseconds(500), + [&t, &a] { return (t->Size_unlocked() > 0 || t->closed || !a); })) + { + // if bundle becomes invalid while waiting for service, an indefinite time WaitFor will + // never be woken and will thus deadlock. So, we wait on definite time and check for invalid + // bundle periodically. + } + + // predicate evaluates to true + if (!a) + { + // bundle is invalid, throw + throw std::logic_error("The bundle context became null."); + } + // bundle is valid, other condition met + } + else + { + t->WaitFor(l, rel_time, [&t] { return (t->Size_unlocked() > 0 || t->closed); }); + } + } + } + object = GetService(); + // Adapt the timeout in case we "missed" the object after having + // been notified within the timeout. + if (!object && endTime > std::chrono::steady_clock::time_point()) + { + timeout = std::chrono::duration_cast(endTime - std::chrono::steady_clock::now()); + if (timeout.count() <= 0) + { + break; // timed out + } + } + } while (!object && !d->Tracked()->closed); + + return object; + } + + template + std::vector> + ServiceTracker::GetServiceReferences() const + { + std::vector> refs; + auto t = d->Tracked(); + if (!t) + { /* if ServiceTracker is not open */ + return refs; + } + { + auto l = t->Lock(); + US_UNUSED(l); + d->GetServiceReferences_unlocked(refs, t.get()); + } + return refs; + } + + template + ServiceReference + ServiceTracker::GetServiceReference() const + { + ServiceReference reference = d->cachedReference.Load(); + if (reference.GetBundle()) + { + return reference; + } + auto references = GetServiceReferences(); + std::size_t length = references.size(); + if (length == 0) + { /* if no service is being tracked */ + throw ServiceException("No service is being tracked"); + } + auto selectedRef = references.begin(); + if (length > 1) + { /* if more than one service, select highest ranking */ + std::vector rankings(length); + int count = 0; + int maxRanking = (std::numeric_limits::min)(); + auto refIter = references.begin(); + for (std::size_t i = 0; i < length; i++) + { + Any rankingAny = refIter->GetProperty(Constants::SERVICE_RANKING); + int ranking = 0; + if (rankingAny.Type() == typeid(int)) + { + ranking = any_cast(rankingAny); + } + + rankings[i] = ranking; + if (ranking > maxRanking) + { + selectedRef = refIter; + maxRanking = ranking; + count = 1; + } + else + { + if (ranking == maxRanking) + { + count++; + } + } + ++refIter; + } + if (count > 1) + { /* if still more than one service, select lowest id */ + long int minId = (std::numeric_limits::max)(); + refIter = references.begin(); + for (std::size_t i = 0; i < length; i++) + { + if (rankings[i] == maxRanking) + { + Any idAny = refIter->GetProperty(Constants::SERVICE_ID); + long int id = 0; + if (idAny.Type() == typeid(long int)) + { + id = any_cast(idAny); + } + if (id < minId) + { + selectedRef = refIter; + minId = id; + } + } + ++refIter; + } + } + } + + d->cachedReference.Store(*selectedRef); + return *selectedRef; + } + + template + std::shared_ptr::TrackedParamType> + ServiceTracker::GetService(ServiceReference const& reference) const + { + auto t = d->Tracked(); + if (!t) + { /* if ServiceTracker is not open */ + return std::shared_ptr(); + } + auto l = t->Lock(); + US_UNUSED(l); + auto customObject = t->GetCustomizedObject_unlocked(reference); + return customObject.value_or(nullptr); + } + + template + std::vector::TrackedParamType>> + ServiceTracker::GetServices() const + { + std::vector> services; + auto t = d->Tracked(); + if (!t) + { /* if ServiceTracker is not open */ + return services; + } + { + auto l = t->Lock(); + US_UNUSED(l); + std::vector> references; + d->GetServiceReferences_unlocked(references, t.get()); + for (auto& ref : references) + { + services.push_back(t->GetCustomizedObject_unlocked(ref).value_or(nullptr)); + } + } + return services; + } + + template + std::shared_ptr::TrackedParamType> + ServiceTracker::GetService() const + { + auto service = d->cachedService.Load(); + if (service) + { + return service; + } + + try + { + auto reference = GetServiceReference(); + if (!reference.GetBundle()) + { + return std::shared_ptr(); + } + service = GetService(reference); + d->cachedService.Store(service); + return service; + } + catch (ServiceException const&) + { + return std::shared_ptr(); + } + } + + template + void + ServiceTracker::Remove(ServiceReference const& reference) + { + auto t = d->Tracked(); + if (!t) + { /* if ServiceTracker is not open */ + return; + } + t->Untrack(reference, ServiceEvent()); + } + + template + int + ServiceTracker::Size() const + { + auto t = d->Tracked(); + if (!t) + { /* if ServiceTracker is not open */ + return 0; + } + return (t->Lock(), static_cast(t->Size_unlocked())); + } + + template + int + ServiceTracker::GetTrackingCount() const + { + auto t = d->Tracked(); + if (!t) + { /* if ServiceTracker has not been opened */ + return -1; + } + { + auto l = t->Lock(); + US_UNUSED(l); + if (t->closed) + { /* if ServiceTracker was closed */ + return -1; + } + return t->GetTrackingCount(); + } + } + + template + void + ServiceTracker::GetTracked(TrackingMap& map) const + { + auto t = d->Tracked(); + if (!t) + { /* if ServiceTracker is not open */ + return; + } + t->Lock(), t->CopyEntries_unlocked(map); + } + + template + bool + ServiceTracker::IsEmpty() const + { + auto t = d->Tracked(); + if (!t) + { /* if ServiceTracker is not open */ + return true; + } + return (t->Lock(), t->IsEmpty_unlocked()); + } + + template + std::shared_ptr::TrackedParamType> + ServiceTracker::AddingService(ServiceReference const& reference) + { + return TypeTraits::ConvertToTrackedType(d->context.GetService(reference)); + } + + template + void + ServiceTracker::ModifiedService(ServiceReference const& /*reference*/, + std::shared_ptr const& /*service*/) + { + /* do nothing */ + } + + template + void + ServiceTracker::RemovedService(ServiceReference const& /*reference*/, + std::shared_ptr const& /*service*/) + { + /* do nothing */ + } + +} // namespace cppmicroservices diff --git a/framework/include/cppmicroservices/detail/ServiceTracker.tpp b/framework/include/cppmicroservices/detail/ServiceTracker.tpp deleted file mode 100755 index eb5b26147..000000000 --- a/framework/include/cppmicroservices/detail/ServiceTracker.tpp +++ /dev/null @@ -1,470 +0,0 @@ -/*============================================================================= - - Library: CppMicroServices - - Copyright (c) The CppMicroServices developers. See the COPYRIGHT - file at the top-level directory of this distribution and at - https://github.com/CppMicroServices/CppMicroServices/COPYRIGHT . - - 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. - -=============================================================================*/ - -#include "cppmicroservices/Bundle.h" -#include "cppmicroservices/BundleContext.h" -#include "cppmicroservices/ServiceException.h" - -#include "cppmicroservices/detail/ServiceTrackerPrivate.h" -#include "cppmicroservices/detail/TrackedService.h" - -#include -#include -#include -#include - - -namespace cppmicroservices { - -template -ServiceTracker::~ServiceTracker() -{ - try { - Close(); - } catch (...) { - } -} - -#ifdef _MSC_VER -# pragma warning(push) -# pragma warning(disable : 4355) -#endif - -template -ServiceTracker::ServiceTracker(const BundleContext& context, - const ServiceReference& reference, - _ServiceTrackerCustomizer* customizer) - : d(new _ServiceTrackerPrivate(this, context, reference, customizer)) -{} - -template -ServiceTracker::ServiceTracker(const BundleContext& context, - const std::string& clazz, - _ServiceTrackerCustomizer* customizer) - : d(new _ServiceTrackerPrivate(this, context, clazz, customizer)) -{} - -template -ServiceTracker::ServiceTracker(const BundleContext& context, - const LDAPFilter& filter, - _ServiceTrackerCustomizer* customizer) - : d(new _ServiceTrackerPrivate(this, context, filter, customizer)) -{} - -template -ServiceTracker::ServiceTracker(const BundleContext& context, - _ServiceTrackerCustomizer* customizer) - : d(new _ServiceTrackerPrivate(this, - context, - us_service_interface_iid(), - customizer)) -{ - std::string clazz = us_service_interface_iid(); - if (clazz.empty()) { - throw ServiceException("The service interface class has no " - "CPPMICROSERVICES_DECLARE_SERVICE_INTERFACE macro"); - } -} - -#ifdef _MSC_VER -# pragma warning(pop) -#endif - -template -void ServiceTracker::Open() -{ - std::shared_ptr<_TrackedService> t; - { - auto l = d->Lock(); - US_UNUSED(l); - if (d->trackedService.Load() && !d->Tracked()->closed) { - return; - } - - DIAG_LOG(*d->context.GetLogSink()) - << "ServiceTracker::Open: " << d->filter; - - t.reset(new _TrackedService(this, d->customizer)); - try { - /* Remove if already exists. No-op if it's an invalid (default) token */ - d->context.RemoveListener(std::move(d->listenerToken)); - d->listenerToken = d->context.AddServiceListener( - std::bind(&_TrackedService::ServiceChanged, t, std::placeholders::_1), - d->listenerFilter); - std::vector> references; - if (!d->trackClass.empty()) { - references = d->GetInitialReferences(d->trackClass, std::string()); - } else { - if (d->trackReference.GetBundle()) { - references.push_back(d->trackReference); - } else { /* user supplied filter */ - references = d->GetInitialReferences(std::string(), - (d->listenerFilter.empty()) - ? d->filter.ToString() - : d->listenerFilter); - } - } - /* set tracked with the initial references */ - t->SetInitial(references); - } catch (const std::invalid_argument& e) { - d->context.RemoveListener(std::move(d->listenerToken)); - throw std::runtime_error( - std::string("unexpected std::invalid_argument exception: ") + e.what()); - } - d->trackedService.Store(t); - } - /* Call tracked outside of synchronized region */ - t->TrackInitial(); /* process the initial references */ -} - -template -void ServiceTracker::Close() -{ - try { - d->context.RemoveListener(std::move(d->listenerToken)); - } catch (const std::runtime_error& /*e*/) { - /* In case the context was stopped or invalid. */ - } - - std::shared_ptr<_TrackedService> outgoing = d->trackedService.Load(); - { - auto l = d->Lock(); - US_UNUSED(l); - - if (outgoing == nullptr) { - return; - } - - if (d->Tracked()->closed) { - return; - } - - DIAG_LOG(*d->context.GetLogSink()) - << "ServiceTracker::close:" << d->filter; - outgoing->Close(); - - d->Modified(); /* clear the cache */ - outgoing->NotifyAll(); /* wake up any waiters */ - } - - try { - outgoing->WaitOnCustomizersToFinish(); - } catch (const std::exception&) { - // this can throw if the latch's count - // is negative, which means the latch - // cannot be used anymore. This can occur - // when multiple threads are opening - // and closing the same service tracker - // repeatedly. - } - - auto references = GetServiceReferences(); - for (auto& ref : references) { - outgoing->Untrack(ref, ServiceEvent()); - } - - if (d->context.GetLogSink()->Enabled()) { - if (!d->cachedReference.Load().GetBundle() && - d->cachedService.Load() == nullptr) { - DIAG_LOG(*d->context.GetLogSink()) - << "ServiceTracker::close[cached cleared]:" << d->filter; - } - } -} - -template -std::shared_ptr::TrackedParamType> -ServiceTracker::WaitForService() -{ - return WaitForService(std::chrono::milliseconds::zero()); -} - -template -template -std::shared_ptr::TrackedParamType> -ServiceTracker::WaitForService( - const std::chrono::duration& rel_time) -{ - if (rel_time.count() < 0) { - throw std::invalid_argument("negative timeout"); - } - - auto object = GetService(); - if (object) { - return object; - } - - using D = std::chrono::duration; - - auto timeout = rel_time; - auto endTime = (rel_time == D::zero()) - ? std::chrono::steady_clock::time_point() - : (std::chrono::steady_clock::now() + rel_time); - do { - auto t = d->Tracked(); - if (!t) { /* if ServiceTracker is not open */ - return std::shared_ptr(); - } - - { - auto l = t->Lock(); - if (t->Size_unlocked() == 0) { - t->WaitFor( - l, rel_time, [&t] { return t->Size_unlocked() > 0 || t->closed; }); - } - } - object = GetService(); - // Adapt the timeout in case we "missed" the object after having - // been notified within the timeout. - if (!object && endTime > std::chrono::steady_clock::time_point()) { - timeout = std::chrono::duration_cast(endTime - - std::chrono::steady_clock::now()); - if (timeout.count() <= 0) { - break; // timed out - } - } - } while (!object && !d->Tracked()->closed); - - return object; -} - -template -std::vector> ServiceTracker::GetServiceReferences() - const -{ - std::vector> refs; - auto t = d->Tracked(); - if (!t) { /* if ServiceTracker is not open */ - return refs; - } - { - auto l = t->Lock(); - US_UNUSED(l); - d->GetServiceReferences_unlocked(refs, t.get()); - } - return refs; -} - -template -ServiceReference ServiceTracker::GetServiceReference() const -{ - ServiceReference reference = d->cachedReference.Load(); - if (reference.GetBundle()) { - DIAG_LOG(*d->context.GetLogSink()) - << "ServiceTracker::getServiceReference[cached]:" << d->filter; - return reference; - } - DIAG_LOG(*d->context.GetLogSink()) - << "ServiceTracker::getServiceReference:" << d->filter; - auto references = GetServiceReferences(); - std::size_t length = references.size(); - if (length == 0) { /* if no service is being tracked */ - throw ServiceException("No service is being tracked"); - } - auto selectedRef = references.begin(); - if (length > 1) { /* if more than one service, select highest ranking */ - std::vector rankings(length); - int count = 0; - int maxRanking = (std::numeric_limits::min)(); - auto refIter = references.begin(); - for (std::size_t i = 0; i < length; i++) { - Any rankingAny = refIter->GetProperty(Constants::SERVICE_RANKING); - int ranking = 0; - if (rankingAny.Type() == typeid(int)) { - ranking = any_cast(rankingAny); - } - - rankings[i] = ranking; - if (ranking > maxRanking) { - selectedRef = refIter; - maxRanking = ranking; - count = 1; - } else { - if (ranking == maxRanking) { - count++; - } - } - ++refIter; - } - if (count > 1) { /* if still more than one service, select lowest id */ - long int minId = (std::numeric_limits::max)(); - refIter = references.begin(); - for (std::size_t i = 0; i < length; i++) { - if (rankings[i] == maxRanking) { - Any idAny = refIter->GetProperty(Constants::SERVICE_ID); - long int id = 0; - if (idAny.Type() == typeid(long int)) { - id = any_cast(idAny); - } - if (id < minId) { - selectedRef = refIter; - minId = id; - } - } - ++refIter; - } - } - } - - d->cachedReference.Store(*selectedRef); - return *selectedRef; -} - -template -std::shared_ptr::TrackedParamType> -ServiceTracker::GetService(const ServiceReference& reference) const -{ - auto t = d->Tracked(); - if (!t) { /* if ServiceTracker is not open */ - return std::shared_ptr(); - } - return (t->Lock(), t->GetCustomizedObject_unlocked(reference)); -} - -template -std::vector::TrackedParamType>> -ServiceTracker::GetServices() const -{ - std::vector> services; - auto t = d->Tracked(); - if (!t) { /* if ServiceTracker is not open */ - return services; - } - { - auto l = t->Lock(); - US_UNUSED(l); - std::vector> references; - d->GetServiceReferences_unlocked(references, t.get()); - for (auto& ref : references) { - services.push_back(t->GetCustomizedObject_unlocked(ref)); - } - } - return services; -} - -template -std::shared_ptr::TrackedParamType> -ServiceTracker::GetService() const -{ - auto service = d->cachedService.Load(); - if (service) { - DIAG_LOG(*d->context.GetLogSink()) - << "ServiceTracker::getService[cached]:" << d->filter; - return service; - } - DIAG_LOG(*d->context.GetLogSink()) - << "ServiceTracker::getService:" << d->filter; - - try { - auto reference = GetServiceReference(); - if (!reference.GetBundle()) { - return std::shared_ptr(); - } - service = GetService(reference); - d->cachedService.Store(service); - return service; - } catch (const ServiceException&) { - return std::shared_ptr(); - } -} - -template -void ServiceTracker::Remove(const ServiceReference& reference) -{ - auto t = d->Tracked(); - if (!t) { /* if ServiceTracker is not open */ - return; - } - t->Untrack(reference, ServiceEvent()); -} - -template -int ServiceTracker::Size() const -{ - auto t = d->Tracked(); - if (!t) { /* if ServiceTracker is not open */ - return 0; - } - return (t->Lock(), static_cast(t->Size_unlocked())); -} - -template -int ServiceTracker::GetTrackingCount() const -{ - auto t = d->Tracked(); - if (!t) { /* if ServiceTracker has not been opened */ - return -1; - } - { - auto l = t->Lock(); - US_UNUSED(l); - if (t->closed) { /* if ServiceTracker was closed */ - return -1; - } - return t->GetTrackingCount(); - } -} - -template -void ServiceTracker::GetTracked(TrackingMap& map) const -{ - auto t = d->Tracked(); - if (!t) { /* if ServiceTracker is not open */ - return; - } - t->Lock(), t->CopyEntries_unlocked(map); -} - -template -bool ServiceTracker::IsEmpty() const -{ - auto t = d->Tracked(); - if (!t) { /* if ServiceTracker is not open */ - return true; - } - return (t->Lock(), t->IsEmpty_unlocked()); -} - -template -std::shared_ptr::TrackedParamType> -ServiceTracker::AddingService(const ServiceReference& reference) -{ - return TypeTraits::ConvertToTrackedType(d->context.GetService(reference)); -} - -template -void ServiceTracker::ModifiedService( - const ServiceReference& /*reference*/, - const std::shared_ptr& /*service*/) -{ - /* do nothing */ -} - -template -void ServiceTracker::RemovedService( - const ServiceReference& /*reference*/, - const std::shared_ptr& /*service*/) -{ - /* do nothing */ -} - -} // namespace cppmicroservices diff --git a/framework/include/cppmicroservices/detail/ServiceTrackerPrivate.h b/framework/include/cppmicroservices/detail/ServiceTrackerPrivate.h index 3781e7f6d..7f73ca007 100644 --- a/framework/include/cppmicroservices/detail/ServiceTrackerPrivate.h +++ b/framework/include/cppmicroservices/detail/ServiceTrackerPrivate.h @@ -178,6 +178,6 @@ namespace cppmicroservices } // namespace cppmicroservices -#include "cppmicroservices/detail/ServiceTrackerPrivate.tpp" +#include "cppmicroservices/detail/ServiceTrackerPrivate.hpp" #endif // CPPMICROSERVICES_SERVICETRACKERPRIVATE_H diff --git a/framework/include/cppmicroservices/detail/ServiceTrackerPrivate.hpp b/framework/include/cppmicroservices/detail/ServiceTrackerPrivate.hpp new file mode 100644 index 000000000..df32abc76 --- /dev/null +++ b/framework/include/cppmicroservices/detail/ServiceTrackerPrivate.hpp @@ -0,0 +1,177 @@ +/*============================================================================= + + Library: CppMicroServices + + Copyright (c) The CppMicroServices developers. See the COPYRIGHT + file at the top-level directory of this distribution and at + https://github.com/CppMicroServices/CppMicroServices/COPYRIGHT . + + 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. + +=============================================================================*/ + +#include "cppmicroservices/detail/TrackedService.h" + +#include "cppmicroservices/BundleContext.h" +#include "cppmicroservices/Constants.h" +#include "cppmicroservices/LDAPFilter.h" + +#include +#include + +namespace cppmicroservices +{ + + namespace detail + { + + template + ServiceTrackerPrivate::ServiceTrackerPrivate(ServiceTracker* st, + BundleContext context, + ServiceReference const& reference, + ServiceTrackerCustomizer* customizer) + : context(std::move(context)) + , customizer(customizer) + , listenerToken() + , trackReference(reference) + , trackedService() + , cachedReference() + , cachedService() + , q_ptr(st) + { + this->customizer = customizer ? customizer : q_func(); + std::stringstream ss; + ss << "(" << Constants::SERVICE_ID << "=" << any_cast(reference.GetProperty(Constants::SERVICE_ID)) + << ")"; + this->listenerFilter = ss.str(); + try + { + this->filter = LDAPFilter(listenerFilter); + } + catch (std::invalid_argument const& e) + { + /* + * we could only get this exception if the ServiceReference was + * invalid + */ + std::invalid_argument ia(std::string("unexpected std::invalid_argument exception: ") + e.what()); + throw ia; + } + } + + template + ServiceTrackerPrivate::ServiceTrackerPrivate(ServiceTracker* st, + BundleContext context, + std::string const& clazz, + ServiceTrackerCustomizer* customizer) + : context(std::move(context)) + , customizer(customizer) + , listenerToken() + , trackClass(clazz) + , trackReference() + , trackedService() + , cachedReference() + , cachedService() + , q_ptr(st) + { + this->customizer = customizer ? customizer : q_func(); + this->listenerFilter = std::string("(") + cppmicroservices::Constants::OBJECTCLASS + "=" + clazz + ")"; + try + { + this->filter = LDAPFilter(listenerFilter); + } + catch (std::invalid_argument const& e) + { + /* + * we could only get this exception if the clazz argument was + * malformed + */ + std::invalid_argument ia(std::string("unexpected std::invalid_argument exception: ") + e.what()); + throw ia; + } + } + + template + ServiceTrackerPrivate::ServiceTrackerPrivate(ServiceTracker* st, + BundleContext const& context, + LDAPFilter const& filter, + ServiceTrackerCustomizer* customizer) + : context(context) + , filter(filter) + , customizer(customizer) + , listenerFilter(filter.ToString()) + , listenerToken() + , trackReference() + , trackedService() + , cachedReference() + , cachedService() + , q_ptr(st) + { + this->customizer = customizer ? customizer : q_func(); + if (!context) + { + throw std::invalid_argument("The bundle context cannot be null."); + } + } + + template + ServiceTrackerPrivate::~ServiceTrackerPrivate() = default; + + template + std::vector> + ServiceTrackerPrivate::GetInitialReferences(std::string const& className, + std::string const& filterString) + { + std::vector> result; + std::vector refs = context.GetServiceReferences(className, filterString); + for (std::vector::const_iterator iter = refs.begin(); iter != refs.end(); ++iter) + { + ServiceReference ref(*iter); + if (ref) + { + result.push_back(ref); + } + } + return result; + } + + template + void + ServiceTrackerPrivate::GetServiceReferences_unlocked(std::vector>& refs, + detail::TrackedService* t) const + { + if (t->Size_unlocked() == 0) + { + return; + } + t->GetTracked_unlocked(refs); + } + + template + std::shared_ptr> + ServiceTrackerPrivate::Tracked() const + { + return trackedService.Load(); + } + + template + void + ServiceTrackerPrivate::Modified() + { + cachedReference.Store(ServiceReference()); /* clear cached value */ + cachedService.Store(std::shared_ptr()); /* clear cached value */ + } + + } // namespace detail + +} // namespace cppmicroservices diff --git a/framework/include/cppmicroservices/detail/ServiceTrackerPrivate.tpp b/framework/include/cppmicroservices/detail/ServiceTrackerPrivate.tpp deleted file mode 100644 index 400c8ce1b..000000000 --- a/framework/include/cppmicroservices/detail/ServiceTrackerPrivate.tpp +++ /dev/null @@ -1,163 +0,0 @@ -/*============================================================================= - - Library: CppMicroServices - - Copyright (c) The CppMicroServices developers. See the COPYRIGHT - file at the top-level directory of this distribution and at - https://github.com/CppMicroServices/CppMicroServices/COPYRIGHT . - - 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. - -=============================================================================*/ - - -#include "cppmicroservices/detail/TrackedService.h" - -#include "cppmicroservices/BundleContext.h" -#include "cppmicroservices/Constants.h" -#include "cppmicroservices/LDAPFilter.h" - -#include -#include - -namespace cppmicroservices { - -namespace detail { - -template -ServiceTrackerPrivate::ServiceTrackerPrivate( - ServiceTracker* st, - BundleContext context, - const ServiceReference& reference, - ServiceTrackerCustomizer* customizer - ) - : context(std::move(context)), customizer(customizer), listenerToken(), trackReference(reference), - trackedService(), cachedReference(), cachedService(), q_ptr(st) -{ - this->customizer = customizer ? customizer : q_func(); - std::stringstream ss; - ss << "(" << Constants::SERVICE_ID << "=" - << any_cast(reference.GetProperty(Constants::SERVICE_ID)) << ")"; - this->listenerFilter = ss.str(); - try - { - this->filter = LDAPFilter(listenerFilter); - } - catch (const std::invalid_argument& e) - { - /* - * we could only get this exception if the ServiceReference was - * invalid - */ - std::invalid_argument ia(std::string("unexpected std::invalid_argument exception: ") + e.what()); - throw ia; - } -} - -template -ServiceTrackerPrivate::ServiceTrackerPrivate( - ServiceTracker* st, - BundleContext context, - const std::string& clazz, - ServiceTrackerCustomizer* customizer - ) - : context(std::move(context)), customizer(customizer), listenerToken(), trackClass(clazz), - trackReference(), trackedService(), cachedReference(), - cachedService(), q_ptr(st) -{ - this->customizer = customizer ? customizer : q_func(); - this->listenerFilter = std::string("(") + cppmicroservices::Constants::OBJECTCLASS + "=" - + clazz + ")"; - try - { - this->filter = LDAPFilter(listenerFilter); - } - catch (const std::invalid_argument& e) - { - /* - * we could only get this exception if the clazz argument was - * malformed - */ - std::invalid_argument ia( - std::string("unexpected std::invalid_argument exception: ") + e.what()); - throw ia; - } -} - -template -ServiceTrackerPrivate::ServiceTrackerPrivate( - ServiceTracker* st, - const BundleContext& context, - const LDAPFilter& filter, - ServiceTrackerCustomizer* customizer - ) - : context(context), filter(filter), customizer(customizer), - listenerFilter(filter.ToString()), listenerToken(), trackReference(), - trackedService(), cachedReference(), cachedService(), q_ptr(st) -{ - this->customizer = customizer ? customizer : q_func(); - if (!context) - { - throw std::invalid_argument("The bundle context cannot be null."); - } -} - -template -ServiceTrackerPrivate::~ServiceTrackerPrivate() -= default; - -template -std::vector > ServiceTrackerPrivate::GetInitialReferences( - const std::string& className, const std::string& filterString) -{ - std::vector > result; - std::vector refs = context.GetServiceReferences(className, filterString); - for(std::vector::const_iterator iter = refs.begin(); - iter != refs.end(); ++iter) - { - ServiceReference ref(*iter); - if (ref) - { - result.push_back(ref); - } - } - return result; -} - -template -void ServiceTrackerPrivate::GetServiceReferences_unlocked(std::vector >& refs, detail::TrackedService* t) const -{ - if (t->Size_unlocked() == 0) - { - return; - } - t->GetTracked_unlocked(refs); -} - -template -std::shared_ptr> ServiceTrackerPrivate::Tracked() const -{ - return trackedService.Load(); -} - -template -void ServiceTrackerPrivate::Modified() -{ - cachedReference.Store(ServiceReference()); /* clear cached value */ - cachedService.Store(std::shared_ptr()); /* clear cached value */ - DIAG_LOG(*context.GetLogSink()) << "ServiceTracker::Modified(): " << filter; -} - -} // namespace detail - -} // namespace cppmicroservices diff --git a/framework/include/cppmicroservices/detail/Threads.h b/framework/include/cppmicroservices/detail/Threads.h index 21e028d4a..7502c0e85 100644 --- a/framework/include/cppmicroservices/detail/Threads.h +++ b/framework/include/cppmicroservices/detail/Threads.h @@ -68,7 +68,7 @@ namespace cppmicroservices UniqueLock(UniqueLock&& o) noexcept : m_Lock(std::move(o.m_Lock)) {} UniqueLock& - operator=(UniqueLock&& o) + operator=(UniqueLock&& o) noexcept { m_Lock = std::move(o.m_Lock); return *this; @@ -106,6 +106,7 @@ namespace cppmicroservices UniqueLock& operator=(UniqueLock&&) { + return *this; } explicit UniqueLock(MutexLockingStrategy const&) {} explicit UniqueLock(MutexLockingStrategy const*) {} diff --git a/framework/include/cppmicroservices/detail/TrackedBundle.h b/framework/include/cppmicroservices/detail/TrackedBundle.h new file mode 100644 index 000000000..2b83b352a --- /dev/null +++ b/framework/include/cppmicroservices/detail/TrackedBundle.h @@ -0,0 +1,198 @@ +/*============================================================================= + + Library: CppMicroServices + + Copyright (c) The CppMicroServices developers. See the COPYRIGHT + file at the top-level directory of this distribution and at + https://github.com/CppMicroServices/CppMicroServices/COPYRIGHT . + + 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. + +=============================================================================*/ + +#ifndef CPPMICROSERVICES_TRACKEDBUNDLE_H +#define CPPMICROSERVICES_TRACKEDBUNDLE_H + +#include "cppmicroservices/BundleEvent.h" +#include "cppmicroservices/detail/BundleAbstractTracked.h" +#include "cppmicroservices/detail/TrackedBundleListener.h" + +#include "cppmicroservices/detail/CounterLatch.h" +#include "cppmicroservices/detail/ScopeGuard.h" + +namespace cppmicroservices { + +template +class BundleTracker; + +namespace detail { + +/** + * This class is not intended to be used directly. It is exported to support + * the CppMicroServices bundle system. + */ +template +class TrackedBundle + : public TrackedBundleListener + , public BundleAbstractTracked +{ + +public: + TrackedBundle(BundleTracker* bundleTracker, + BundleTrackerCustomizer* customizer) + : BundleAbstractTracked(bundleTracker->d->_context) + , _bundleTracker(bundleTracker) + , _customizer(customizer) + , latch{} + {} + + /** + * Method connected to bundle events for the + * BundleTracker class. This method must NOT be + * synchronized to avoid deadlock potential. + * + * @param event BundleEvent object from the framework. + */ + void BundleChanged(const BundleEvent& event) override + { + // Call track or untrack based on state mask + + (void)latch.CountUp(); + ScopeGuard sg([this]() { + // By using try/catch here, we ensure that this lambda function doesn't + // throw inside ScopeGuard + try { + latch.CountDown(); + } catch (...) { + } + }); + + // Check trivial calls + Bundle bundle = event.GetBundle(); + if (!bundle) { + return; + } + Bundle::State state = bundle.GetState(); + if (!state) { + return; + } + // Ignore events that do not correspond with + // Bundle state changes + BundleEvent::Type eventType = event.GetType(); + if (eventType == BundleEvent::Type::BUNDLE_UNRESOLVED) { + return; + } + { + auto l = this->Lock(); + US_UNUSED(l); + + // Check for delayed call + if (this->closed) { + return; + } + } + + // Track iff state in mask + if (state & _bundleTracker->d->_stateMask) { + /* + * The below method will throw if a customizer throws, + * and the exception will propagate to the listener. + */ + this->Track(bundle, event); + } else { + /* + * The below method will throw if a customizer throws, + * and the exception will propagate to the listener. + */ + this->Untrack(bundle, event); + } + } + + void WaitOnCustomizersToFinish() { latch.Wait(); } + +private: + BundleTracker* _bundleTracker; + BundleTrackerCustomizer* _customizer; + + CounterLatch latch; + + /** + * Increment the tracking count and tell the tracker there was a + * modification. + * + * @GuardedBy this + */ + void Modified() override + { + BundleAbstractTracked:: + Modified(); /* increment the modification count */ + _bundleTracker->d->Modified(); + } + + /** + * Call the specific customizer adding method. This method must not be + * called while synchronized on this object. + * + * @param bundle Bundle to be tracked. + * @param related Action related object. + * @return Customized object for the tracked bundle or null + * if the bundle is not to be tracked. + * + * @see BundleTrackerCustomizer::AddingBundle(Bundle, BundleEvent) + */ + std::optional CustomizerAdding(Bundle bundle, + const BundleEvent& related) override + { + return _customizer->AddingBundle(bundle, related); + } + + /** + * Call the specific customizer modified method. This method must not be + * called while synchronized on this object. + * + * @param bundle Tracked bundle. + * @param related Action related object. + * @param object Customized object for the tracked bundle. + * + * @see BundleTrackerCustomizer::ModifiedBundle(Bundle, BundleEvent, T) + */ + void CustomizerModified(Bundle bundle, + const BundleEvent& related, + const T& object) override + { + _customizer->ModifiedBundle(bundle, related, object); + } + + /** + * Call the specific customizer removed method. This method must not be + * called while synchronized on this object. + * + * @param bundle Tracked bundle. + * @param related Action related object. + * @param object Customized object for the tracked bundle. + * + * @see BundleTrackerCustomizer::RemovedBundle(Bundle, BundleEvent, T) + */ + void CustomizerRemoved(Bundle bundle, + const BundleEvent& related, + const T& object) override + { + _customizer->RemovedBundle(bundle, related, object); + } +}; + +} // namespace detail + +} // namespace cppmicroservices + +#endif // CPPMICROSERVICES_TRACKEDBUNDLE_H diff --git a/framework/include/cppmicroservices/detail/TrackedBundleListener.h b/framework/include/cppmicroservices/detail/TrackedBundleListener.h new file mode 100644 index 000000000..654a1192d --- /dev/null +++ b/framework/include/cppmicroservices/detail/TrackedBundleListener.h @@ -0,0 +1,53 @@ +/*============================================================================= + + Library: CppMicroServices + + Copyright (c) The CppMicroServices developers. See the COPYRIGHT + file at the top-level directory of this distribution and at + https://github.com/CppMicroServices/CppMicroServices/COPYRIGHT . + + 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. + +=============================================================================*/ + +#ifndef CPPMICROSERVICES_TRACKEDBUNDLELISTENER_H +#define CPPMICROSERVICES_TRACKEDBUNDLELISTENER_H + +#include "cppmicroservices/BundleEvent.h" + +namespace cppmicroservices { + +namespace detail { + +/** + * This class is not intended to be used directly. It is exported to support + * the CppMicroServices bundle system. + */ +struct TrackedBundleListener +{ + virtual ~TrackedBundleListener() = default; + + /** + * Slot connected to bundle events for the + * BundleTracker class. + * + * @param event BundleEvent object from the framework. + */ + virtual void BundleChanged(BundleEvent const& event) = 0; +}; + +} // namespace detail + +} // namespace cppmicroservices + +#endif // CPPMICROSERVICES_TRACKEDBUNDLELISTENER_H diff --git a/framework/include/cppmicroservices/detail/TrackedService.h b/framework/include/cppmicroservices/detail/TrackedService.h index 804539d06..616c560ff 100644 --- a/framework/include/cppmicroservices/detail/TrackedService.h +++ b/framework/include/cppmicroservices/detail/TrackedService.h @@ -30,6 +30,8 @@ #include "cppmicroservices/detail/CounterLatch.h" #include "cppmicroservices/detail/ScopeGuard.h" +#include + namespace cppmicroservices { @@ -43,14 +45,16 @@ namespace cppmicroservices template class TrackedService : public TrackedServiceListener - , public BundleAbstractTracked, TTT, ServiceEvent> + , public BundleAbstractTracked, + std::shared_ptr, + ServiceEvent> { public: using T = typename TTT::TrackedType; using TrackedParamType = typename TTT::TrackedParamType; - TrackedService(ServiceTracker* serviceTracker, ServiceTrackerCustomizer* customizer); + TrackedService(ServiceTracker* tracker, ServiceTrackerCustomizer* customizer); /** * Method connected to service events for the @@ -64,7 +68,9 @@ namespace cppmicroservices void WaitOnCustomizersToFinish(); private: - using Superclass = BundleAbstractTracked, TTT, ServiceEvent>; + using Superclass = BundleAbstractTracked, + std::shared_ptr, + ServiceEvent>; ServiceTracker* serviceTracker; ServiceTrackerCustomizer* customizer; @@ -88,8 +94,8 @@ namespace cppmicroservices * @return Customized object for the tracked item or null * if the item is not to be tracked. */ - std::shared_ptr CustomizerAdding(ServiceReference item, - ServiceEvent const& related) override; + std::optional> CustomizerAdding(ServiceReference item, + ServiceEvent const& related) override; /** * Call the specific customizer modified method. This method must not be @@ -120,6 +126,6 @@ namespace cppmicroservices } // namespace cppmicroservices -#include "cppmicroservices/detail/TrackedService.tpp" +#include "cppmicroservices/detail/TrackedService.hpp" #endif // CPPMICROSERVICES_TRACKEDSERVICE_H diff --git a/framework/include/cppmicroservices/detail/TrackedService.hpp b/framework/include/cppmicroservices/detail/TrackedService.hpp new file mode 100644 index 000000000..3afa690d4 --- /dev/null +++ b/framework/include/cppmicroservices/detail/TrackedService.hpp @@ -0,0 +1,171 @@ +/*============================================================================= + + Library: CppMicroServices + + Copyright (c) The CppMicroServices developers. See the COPYRIGHT + file at the top-level directory of this distribution and at + https://github.com/CppMicroServices/CppMicroServices/COPYRIGHT . + + 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. + +=============================================================================*/ + +namespace cppmicroservices +{ + + namespace detail + { + + template + TrackedService::TrackedService(ServiceTracker* tracker, + ServiceTrackerCustomizer* customizer) + : Superclass(tracker->d->context) + , serviceTracker(tracker) + , customizer(customizer) + , latch {} + { + } + + template + void + TrackedService::WaitOnCustomizersToFinish() + { + latch.Wait(); + } + + template + void + TrackedService::ServiceChanged(ServiceEvent const& event) + { + (void)latch.CountUp(); + ScopeGuard sg( + [this]() + { + // By using try/catch here, we ensure that this lambda function doesn't + // throw inside ScopeGuard's dtor. + try + { + latch.CountDown(); + } + catch (...) + { + } + }); + + ServiceReference reference; + { + auto l = this->Lock(); + US_UNUSED(l); + /* + * Check if we had a delayed call (which could happen when we + * close). + */ + if (this->closed) + { + return; + } + + reference = event.GetServiceReference(); + + if (!reference) + { + return; + } + } + + switch (event.GetType()) + { + case ServiceEvent::SERVICE_REGISTERED: + case ServiceEvent::SERVICE_MODIFIED: + { + if (!serviceTracker->d->listenerFilter.empty()) + { // service listener added with filter + this->Track(reference, event); + /* + * If the customizer throws an unchecked exception, it + * is safe to let it propagate + */ + } + else + { // service listener added without filter + if (serviceTracker->d->filter.Match(reference)) + { + this->Track(reference, event); + /* + * If the customizer throws an unchecked exception, + * it is safe to let it propagate + */ + } + else + { + this->Untrack(reference, event); + /* + * If the customizer throws an unchecked exception, + * it is safe to let it propagate + */ + } + } + break; + } + case ServiceEvent::SERVICE_MODIFIED_ENDMATCH: + case ServiceEvent::SERVICE_UNREGISTERING: + this->Untrack(reference, event); + /* + * If the customizer throws an unchecked exception, it is + * safe to let it propagate + */ + break; + } + } + + template + void + TrackedService::Modified() + { + Superclass::Modified(); /* increment the modification count */ + serviceTracker->d->Modified(); + } + + template + std::optional::TrackedParamType>> + TrackedService::CustomizerAdding(ServiceReference item, ServiceEvent const& /*related*/) + { + auto serviceObjectPointer = customizer->AddingService(item); + + // Convert the shared pointer to an optional + return serviceObjectPointer ? std::optional< + std::shared_ptr::TrackedParamType>> { serviceObjectPointer } + : std::nullopt; + } + + template + void + TrackedService::CustomizerModified(ServiceReference item, + ServiceEvent const& /*related*/, + std::shared_ptr const& object) + { + customizer->ModifiedService(item, object); + } + + template + void + TrackedService::CustomizerRemoved(ServiceReference item, + ServiceEvent const& /*related*/, + std::shared_ptr const& object) + { + customizer->RemovedService(item, object); + } + + } // namespace detail + +} // namespace cppmicroservices diff --git a/framework/include/cppmicroservices/detail/TrackedService.tpp b/framework/include/cppmicroservices/detail/TrackedService.tpp deleted file mode 100644 index 367f1c685..000000000 --- a/framework/include/cppmicroservices/detail/TrackedService.tpp +++ /dev/null @@ -1,156 +0,0 @@ -/*============================================================================= - - Library: CppMicroServices - - Copyright (c) The CppMicroServices developers. See the COPYRIGHT - file at the top-level directory of this distribution and at - https://github.com/CppMicroServices/CppMicroServices/COPYRIGHT . - - 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. - -=============================================================================*/ - -namespace cppmicroservices { - -namespace detail { - -template -TrackedService::TrackedService(ServiceTracker* serviceTracker, - ServiceTrackerCustomizer* customizer) - : Superclass(serviceTracker->d->context) - , serviceTracker(serviceTracker) - , customizer(customizer) - , latch{} -{ - -} - -template -void TrackedService::WaitOnCustomizersToFinish() { - latch.Wait(); -} - -template -void TrackedService::ServiceChanged(const ServiceEvent& event) -{ - (void)latch.CountUp(); - ScopeGuard sg([this]() { - // By using try/catch here, we ensure that this lambda function doesn't - // throw inside ScopeGuard's dtor. - try { - latch.CountDown(); - } catch (...) { - } - }); - - ServiceReference reference; - { - auto l = this->Lock(); - US_UNUSED(l); - /* - * Check if we had a delayed call (which could happen when we - * close). - */ - if (this->closed) { - return; - } - - reference = event.GetServiceReference(); - - DIAG_LOG(*serviceTracker->d->context.GetLogSink()) - << "TrackedService::ServiceChanged[" << event.GetType() - << "]: " << reference; - if (!reference) { - return; - } - } - - switch (event.GetType()) - { - case ServiceEvent::SERVICE_REGISTERED : - case ServiceEvent::SERVICE_MODIFIED : - { - if (!serviceTracker->d->listenerFilter.empty()) - { // service listener added with filter - this->Track(reference, event); - /* - * If the customizer throws an unchecked exception, it - * is safe to let it propagate - */ - } - else - { // service listener added without filter - if (serviceTracker->d->filter.Match(reference)) - { - this->Track(reference, event); - /* - * If the customizer throws an unchecked exception, - * it is safe to let it propagate - */ - } - else - { - this->Untrack(reference, event); - /* - * If the customizer throws an unchecked exception, - * it is safe to let it propagate - */ - } - } - break; - } - case ServiceEvent::SERVICE_MODIFIED_ENDMATCH : - case ServiceEvent::SERVICE_UNREGISTERING : - this->Untrack(reference, event); - /* - * If the customizer throws an unchecked exception, it is - * safe to let it propagate - */ - break; - } -} - -template -void TrackedService::Modified() -{ - Superclass::Modified(); /* increment the modification count */ - serviceTracker->d->Modified(); -} - -template -std::shared_ptr::TrackedParamType> -TrackedService::CustomizerAdding(ServiceReference item, - const ServiceEvent& /*related*/) -{ - return customizer->AddingService(item); -} - -template -void TrackedService::CustomizerModified(ServiceReference item, - const ServiceEvent& /*related*/, - const std::shared_ptr& object) -{ - customizer->ModifiedService(item, object); -} - -template -void TrackedService::CustomizerRemoved(ServiceReference item, - const ServiceEvent& /*related*/, - const std::shared_ptr& object) -{ - customizer->RemovedService(item, object); -} - -} // namespace detail - -} // namespace cppmicroservices diff --git a/framework/src/CMakeLists.txt b/framework/src/CMakeLists.txt index a38d9fb46..518fd341a 100755 --- a/framework/src/CMakeLists.txt +++ b/framework/src/CMakeLists.txt @@ -19,6 +19,7 @@ set(_srcs util/SharedLibrary.cpp util/SharedLibraryException.cpp util/Utils.cpp + util/ServiceRegistrationLocks.cpp service/ListenerToken.cpp service/ServiceException.cpp @@ -34,6 +35,7 @@ set(_srcs service/ServiceReferenceBasePrivate.cpp service/ServiceRegistrationBase.cpp service/ServiceRegistrationBasePrivate.cpp + service/ServiceRegistrationCoreInfo.cpp service/ServiceRegistry.cpp bundle/Bundle.cpp @@ -67,6 +69,7 @@ set(_private_headers util/Properties.h util/PropsCheck.h util/Utils.h + util/ServiceRegistrationLocks.h service/ServiceHooks.h service/ServiceListenerEntry.h @@ -74,6 +77,7 @@ set(_private_headers service/ServiceListeners.h service/ServiceReferenceBasePrivate.h service/ServiceRegistrationBasePrivate.h + service/ServiceRegistrationCoreInfo.h service/ServiceRegistry.h bundle/BundleArchive.h diff --git a/framework/src/bundle/BundleContext.cpp b/framework/src/bundle/BundleContext.cpp index ced7fda7c..b3b457464 100644 --- a/framework/src/bundle/BundleContext.cpp +++ b/framework/src/bundle/BundleContext.cpp @@ -1,22 +1,22 @@ - /*============================================================================= +/*============================================================================= - Library: CppMicroServices + Library: CppMicroServices - Copyright (c) The CppMicroServices developers. See the COPYRIGHT - file at the top-level directory of this distribution and at - https://github.com/CppMicroServices/CppMicroServices/COPYRIGHT . + Copyright (c) The CppMicroServices developers. See the COPYRIGHT + file at the top-level directory of this distribution and at + https://github.com/CppMicroServices/CppMicroServices/COPYRIGHT . - 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 + 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 + 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. + 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. =============================================================================*/ @@ -87,26 +87,6 @@ namespace cppmicroservices return *this; } - std::shared_ptr - BundleContext::GetLogSink() const - { - if (!d) - { - throw std::runtime_error("The bundle context is no longer valid"); - } - - d->CheckValid(); - - if (auto bundle_ = d->bundle.lock()) - { - return bundle_->coreCtx->sink->shared_from_this(); - } - else - { - throw std::runtime_error("The bundle context is no longer valid"); - } - } - Any BundleContext::GetProperty(std::string const& key) const { @@ -259,9 +239,9 @@ namespace cppmicroservices template struct ServiceHolder { - const std::weak_ptr b; - const ServiceReferenceBase sref; - const std::shared_ptr service; + std::weak_ptr const b; + ServiceReferenceBase const sref; + std::shared_ptr const service; ServiceHolder(std::shared_ptr const& b, ServiceReferenceBase const& sr, std::shared_ptr s) : b(b) @@ -274,8 +254,7 @@ namespace cppmicroservices { try { - sref.d.load()->UngetService(b.lock(), true); - //sref.d.load()->UngetService(b.lock(), true); + sref.d.Load()->UngetService(b.lock(), true); } catch (...) { @@ -312,7 +291,7 @@ namespace cppmicroservices auto b = GetAndCheckBundlePrivate(d); std::shared_ptr> h( - new ServiceHolder(b, reference, reference.d.load()->GetService(b.get()))); + new ServiceHolder(b, reference, reference.d.Load()->GetService(b.get()))); return std::shared_ptr(h, h->service.get()); } @@ -333,7 +312,7 @@ namespace cppmicroservices d->CheckValid(); auto b = GetAndCheckBundlePrivate(d); - auto serviceInterfaceMap = reference.d.load()->GetServiceInterfaceMap(b.get()); + auto serviceInterfaceMap = reference.d.Load()->GetServiceInterfaceMap(b.get()); std::shared_ptr> h( new ServiceHolder(b, reference, serviceInterfaceMap)); return InterfaceMapConstPtr(h, h->service.get()); diff --git a/framework/src/bundle/BundleHooks.cpp b/framework/src/bundle/BundleHooks.cpp index e901123ba..dc0f92d1d 100644 --- a/framework/src/bundle/BundleHooks.cpp +++ b/framework/src/bundle/BundleHooks.cpp @@ -74,7 +74,7 @@ namespace cppmicroservices { ServiceReference sr = srBaseIter->GetReference(); std::shared_ptr fh - = std::static_pointer_cast(sr.d.load()->GetService(GetPrivate(selfBundle).get())); + = std::static_pointer_cast(sr.d.Load()->GetService(GetPrivate(selfBundle).get())); if (fh) { try @@ -139,7 +139,7 @@ namespace cppmicroservices } std::shared_ptr eh = std::static_pointer_cast( - sr.d.load()->GetService(GetPrivate(GetBundleContext().GetBundle()).get())); + sr.d.Load()->GetService(GetPrivate(GetBundleContext().GetBundle()).get())); if (eh) { try diff --git a/framework/src/bundle/BundlePrivate.cpp b/framework/src/bundle/BundlePrivate.cpp index cbe1b6ae4..1301a492d 100644 --- a/framework/src/bundle/BundlePrivate.cpp +++ b/framework/src/bundle/BundlePrivate.cpp @@ -263,7 +263,7 @@ namespace cppmicroservices return; } } - // INTENTIONALLY FALLS THROUGH - in case of lazy activation. + [[fallthrough]]; case Bundle::STATE_RESOLVED: { state = Bundle::STATE_STARTING; @@ -337,7 +337,7 @@ namespace cppmicroservices } } } - // INTENTIONALLY FALLS THROUGH + [[fallthrough]]; case Bundle::STATE_RESOLVED: case Bundle::STATE_INSTALLED: { @@ -384,7 +384,9 @@ namespace cppmicroservices try { if (util::Exists(bundleDir)) + { util::RemoveDirectoryRecursive(bundleDir); + } } catch (...) { @@ -823,7 +825,8 @@ namespace cppmicroservices coreCtx->services.GetUsedByBundle(this, srs); for (std::vector::const_iterator i = srs.begin(); i != srs.end(); ++i) { - i->GetReference(std::string()).d.load()->UngetService(this->shared_from_this(), false); + auto ref = i->GetReference(std::string()); + ref.d.Load()->UngetService(this->shared_from_this(), false); } } diff --git a/framework/src/bundle/BundleResource.cpp b/framework/src/bundle/BundleResource.cpp index df84db8e4..eb99c76ba 100644 --- a/framework/src/bundle/BundleResource.cpp +++ b/framework/src/bundle/BundleResource.cpp @@ -288,15 +288,7 @@ namespace cppmicroservices return { nullptr, ::free }; } - auto data = d->archive->GetResourceContainer()->GetData(d->stat.index); - if (!data) - { - auto sink = GetBundleContext().GetLogSink(); - DIAG_LOG(*sink) << "Error uncompressing resource data for " << this->GetResourcePath() << " from " - << d->archive->GetBundleLocation(); - } - - return data; + return d->archive->GetResourceContainer()->GetData(d->stat.index); } std::ostream& diff --git a/framework/src/bundle/BundleResourceContainer.cpp b/framework/src/bundle/BundleResourceContainer.cpp index 67f1d831c..1885b5d1f 100644 --- a/framework/src/bundle/BundleResourceContainer.cpp +++ b/framework/src/bundle/BundleResourceContainer.cpp @@ -162,7 +162,9 @@ namespace cppmicroservices for (++iter; iter != m_SortedEntries.end(); ++iter) { if (resourcePath.size() > iter->first.size()) + { break; + } if (iter->first.compare(0, resourcePath.size(), resourcePath) == 0) { std::size_t pos = iter->first.find_first_of('/', resourcePath.size()); @@ -221,10 +223,8 @@ namespace cppmicroservices m_ObjFile = BundleObjFactory().CreateBundleFileObj(m_Location); rawBundleResourceData = m_ObjFile->GetRawBundleResourceContainer(); } - catch (std::exception const& ex) + catch (std::exception const&) { - auto sink = GetBundleContext().GetLogSink(); - DIAG_LOG(*sink) << "Exception thrown creating BundleFileObj : " << ex.what(); } if (!rawBundleResourceData || !rawBundleResourceData->GetData() diff --git a/framework/src/bundle/CoreBundleContext.cpp b/framework/src/bundle/CoreBundleContext.cpp index 519c7c277..a1e1d57df 100644 --- a/framework/src/bundle/CoreBundleContext.cpp +++ b/framework/src/bundle/CoreBundleContext.cpp @@ -239,7 +239,6 @@ namespace cppmicroservices listeners.Clear(); resolver.Clear(); - dataStorage.clear(); storage->Close(); } diff --git a/framework/src/service/ServiceHooks.cpp b/framework/src/service/ServiceHooks.cpp index dca8695ed..79e0e1067 100644 --- a/framework/src/service/ServiceHooks.cpp +++ b/framework/src/service/ServiceHooks.cpp @@ -125,7 +125,7 @@ namespace cppmicroservices { ServiceReference sr = fhrIter->GetReference(); auto fh - = std::static_pointer_cast(sr.d.load()->GetService(GetPrivate(selfBundle).get())); + = std::static_pointer_cast(sr.d.Load()->GetService(GetPrivate(selfBundle).get())); if (fh) { try @@ -177,7 +177,7 @@ namespace cppmicroservices { ServiceReference sr = sriIter->GetReference(); auto elh = std::static_pointer_cast( - sr.d.load()->GetService(GetPrivate(selfBundle).get())); + sr.d.Load()->GetService(GetPrivate(selfBundle).get())); if (elh) { try diff --git a/framework/src/service/ServiceListeners.cpp b/framework/src/service/ServiceListeners.cpp index f556b42ff..218836618 100644 --- a/framework/src/service/ServiceListeners.cpp +++ b/framework/src/service/ServiceListeners.cpp @@ -417,8 +417,6 @@ namespace cppmicroservices ServiceEvent const& evt, ServiceListenerEntries& matchBefore) { - int n = 0; - if (!matchBefore.empty()) { for (auto& l : receivers) @@ -433,7 +431,6 @@ namespace cppmicroservices { try { - ++n; l.CallDelegate(evt); } catch (...) @@ -460,7 +457,7 @@ namespace cppmicroservices // Get a copy of the service reference and keep it until we are // done with its properties. auto ref = evt.GetServiceReference(); - auto props = ref.d.load()->GetProperties(); + auto props = ref.d.Load()->GetProperties(); { auto l = this->Lock(); @@ -469,7 +466,9 @@ namespace cppmicroservices for (auto& sse : complicatedListeners) { if (receivers.count(sse) == 0) + { continue; + } LDAPExpr const& ldapExpr = sse.GetLDAPExpr(); if (ldapExpr.IsNull() || ldapExpr.Evaluate(props, false)) { diff --git a/framework/src/service/ServiceObjects.cpp b/framework/src/service/ServiceObjects.cpp index 2df59323a..5540475e6 100644 --- a/framework/src/service/ServiceObjects.cpp +++ b/framework/src/service/ServiceObjects.cpp @@ -60,11 +60,11 @@ namespace cppmicroservices if (isPrototypeScope) { - result = m_reference.d.load()->GetPrototypeService(MakeBundleContext(m_context).GetBundle()); + result = m_reference.d.Load()->GetPrototypeService(MakeBundleContext(m_context).GetBundle()); } else { - result = m_reference.d.load()->GetServiceInterfaceMap( + result = m_reference.d.Load()->GetServiceInterfaceMap( GetPrivate(MakeBundleContext(m_context).GetBundle()).get()); } @@ -115,11 +115,11 @@ namespace cppmicroservices if (isPrototypeScope) { - sref.d.load()->UngetPrototypeService(bundle, interfaceMap); + sref.d.Load()->UngetPrototypeService(bundle, interfaceMap); } else { - sref.d.load()->UngetService(bundle, true); + sref.d.Load()->UngetService(bundle, true); } } } diff --git a/framework/src/service/ServiceReferenceBase.cpp b/framework/src/service/ServiceReferenceBase.cpp index f75407b3e..0e9c7364c 100644 --- a/framework/src/service/ServiceReferenceBase.cpp +++ b/framework/src/service/ServiceReferenceBase.cpp @@ -28,31 +28,30 @@ #include "BundlePrivate.h" #include "ServiceReferenceBasePrivate.h" #include "ServiceRegistrationBasePrivate.h" - +#include "ServiceRegistrationLocks.h" +#include "Utils.h" #include namespace cppmicroservices { - ServiceReferenceBase::ServiceReferenceBase() : d(new ServiceReferenceBasePrivate(nullptr)) {} + ServiceReferenceBase::ServiceReferenceBase() + { + d.Exchange(std::make_shared(std::weak_ptr())); + } - ServiceReferenceBase::ServiceReferenceBase(ServiceReferenceBase const& ref) : d(ref.d.load()) { ++d.load()->ref; } + ServiceReferenceBase::ServiceReferenceBase(ServiceReferenceBase const& ref) { d.Exchange(ref.d.Load()); } - ServiceReferenceBase::ServiceReferenceBase(ServiceRegistrationBasePrivate* reg) - : d(new ServiceReferenceBasePrivate(reg)) + ServiceReferenceBase::ServiceReferenceBase(std::shared_ptr reg) { + d.Exchange(std::make_shared(reg)); } void ServiceReferenceBase::SetInterfaceId(std::string const& interfaceId) { - if (d.load()->ref > 1) - { - // detach - --d.load()->ref; - d = new ServiceReferenceBasePrivate(d.load()->registration); - } - d.load()->interfaceId = interfaceId; + d.Exchange(std::make_shared(d.Load()->registration)); + d.Load()->interfaceId = interfaceId; } ServiceReferenceBase::operator bool() const { return static_cast(GetBundle()); } @@ -60,24 +59,19 @@ namespace cppmicroservices ServiceReferenceBase& ServiceReferenceBase::operator=(std::nullptr_t) { - if (!--d.load()->ref) - delete d.load(); - d = new ServiceReferenceBasePrivate(nullptr); + d.Exchange(std::make_shared(std::weak_ptr())); return *this; } - ServiceReferenceBase::~ServiceReferenceBase() - { - if (!--d.load()->ref) - delete d.load(); - } + ServiceReferenceBase::~ServiceReferenceBase() = default; Any ServiceReferenceBase::GetProperty(std::string const& key) const { - auto l = d.load()->registration->properties.Lock(); + auto refP = d.Load(); + auto l = refP->coreInfo->properties.Lock(); US_UNUSED(l); - return d.load()->registration->properties.Value_unlocked(key).first; + return refP->coreInfo->properties.Value_unlocked(key).first; } void @@ -89,36 +83,39 @@ namespace cppmicroservices std::vector ServiceReferenceBase::GetPropertyKeys() const { - auto l = d.load()->registration->properties.Lock(); + auto refP = d.Load(); + auto l = refP->coreInfo->properties.Lock(); US_UNUSED(l); - return d.load()->registration->properties.Keys_unlocked(); + return refP->coreInfo->properties.Keys_unlocked(); } Bundle ServiceReferenceBase::GetBundle() const { - auto p = d.load(); - if (p->registration == nullptr) + auto refP = d.Load(); + auto reg = refP->registration.lock(); + if (!reg) { return Bundle(); } - auto l = p->registration->Lock(); + auto l = refP->LockServiceRegistration(); US_UNUSED(l); - if (p->registration->bundle.lock() == nullptr) + if (refP->coreInfo->bundle_.lock() == nullptr) { return Bundle(); } - return MakeBundle(p->registration->bundle.lock()->shared_from_this()); + return MakeBundle(refP->coreInfo->bundle_.lock()->shared_from_this()); } std::vector ServiceReferenceBase::GetUsingBundles() const { std::vector bundles; - auto l = d.load()->registration->Lock(); + auto refP = d.Load(); + auto l = refP->LockServiceRegistration(); US_UNUSED(l); - for (auto& iter : d.load()->registration->dependents) + for (auto const& iter : refP->coreInfo->dependents) { bundles.push_back(MakeBundle(iter.first->shared_from_this())); } @@ -128,7 +125,7 @@ namespace cppmicroservices bool ServiceReferenceBase::operator<(ServiceReferenceBase const& reference) const { - if (d.load() == reference.d.load()) + if (d.Load() == reference.d.Load()) { return false; } @@ -142,8 +139,9 @@ namespace cppmicroservices { return false; } - - if (d.load()->registration == reference.d.load()->registration) + auto self = d.Load(); + auto ref = reference.d.Load(); + if (self->registration.lock() == ref->registration.lock()) { return false; } @@ -156,22 +154,22 @@ namespace cppmicroservices Any anyR1; Any anyId1; { - auto l1 = d.load()->registration->properties.Lock(); + auto l1 = self->coreInfo->properties.Lock(); US_UNUSED(l1); - anyR1 = d.load()->registration->properties.Value_unlocked(Constants::SERVICE_RANKING).first; + anyR1 = self->coreInfo->properties.Value_unlocked(Constants::SERVICE_RANKING).first; assert(anyR1.Empty() || anyR1.Type() == typeid(int)); - anyId1 = d.load()->registration->properties.Value_unlocked(Constants::SERVICE_ID).first; + anyId1 = self->coreInfo->properties.Value_unlocked(Constants::SERVICE_ID).first; assert(anyId1.Empty() || anyId1.Type() == typeid(long int)); } Any anyR2; Any anyId2; { - auto l2 = reference.d.load()->registration->properties.Lock(); + auto l2 = ref->coreInfo->properties.Lock(); US_UNUSED(l2); - anyR2 = reference.d.load()->registration->properties.Value_unlocked(Constants::SERVICE_RANKING).first; + anyR2 = ref->coreInfo->properties.Value_unlocked(Constants::SERVICE_RANKING).first; assert(anyR2.Empty() || anyR2.Type() == typeid(int)); - anyId2 = reference.d.load()->registration->properties.Value_unlocked(Constants::SERVICE_ID).first; + anyId2 = ref->coreInfo->properties.Value_unlocked(Constants::SERVICE_ID).first; assert(anyId2.Empty() || anyId2.Type() == typeid(long int)); } @@ -197,22 +195,18 @@ namespace cppmicroservices bool ServiceReferenceBase::operator==(ServiceReferenceBase const& reference) const { - return d.load()->registration == reference.d.load()->registration; + return d.Load()->registration.lock() == reference.d.Load()->registration.lock(); } ServiceReferenceBase& ServiceReferenceBase::operator=(ServiceReferenceBase const& reference) { - if (d == reference.d.load()) + if (d.Load() == reference.d.Load()) + { return *this; + } - ServiceReferenceBasePrivate* old_d = d; - ServiceReferenceBasePrivate* new_d = reference.d; - ++new_d->ref; - d = new_d; - - if (!--old_d->ref) - delete old_d; + d.Exchange(reference.d.Load()); return *this; } @@ -220,19 +214,19 @@ namespace cppmicroservices bool ServiceReferenceBase::IsConvertibleTo(std::string const& interfaceId) const { - return d.load()->IsConvertibleTo(interfaceId); + return d.Load()->IsConvertibleTo(interfaceId); } std::string ServiceReferenceBase::GetInterfaceId() const { - return d.load()->interfaceId; + return d.Load()->interfaceId; } std::size_t ServiceReferenceBase::Hash() const { - return std::hash()(this->d.load()->registration); + return std::hash>()(this->d.Load()->coreInfo); } std::ostream& @@ -250,7 +244,9 @@ namespace cppmicroservices { os << keys[i] << "=" << serviceRef.GetProperty(keys[i]).ToString(); if (i < keySize - 1) + { os << ","; + } } os << ")"; } diff --git a/framework/src/service/ServiceReferenceBasePrivate.cpp b/framework/src/service/ServiceReferenceBasePrivate.cpp index 177c81050..58ab5f7f3 100644 --- a/framework/src/service/ServiceReferenceBasePrivate.cpp +++ b/framework/src/service/ServiceReferenceBasePrivate.cpp @@ -1,22 +1,22 @@ - /*============================================================================= +/*============================================================================= - Library: CppMicroServices + Library: CppMicroServices - Copyright (c) The CppMicroServices developers. See the COPYRIGHT - file at the top-level directory of this distribution and at - https://github.com/CppMicroServices/CppMicroServices/COPYRIGHT . + Copyright (c) The CppMicroServices developers. See the COPYRIGHT + file at the top-level directory of this distribution and at + https://github.com/CppMicroServices/CppMicroServices/COPYRIGHT . - 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 + 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 + 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. + 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. =============================================================================*/ @@ -43,20 +43,21 @@ namespace cppmicroservices using ThreadMarksMapType = std::unordered_map>; - ServiceReferenceBasePrivate::ServiceReferenceBasePrivate(ServiceRegistrationBasePrivate* reg) - : ref(1) - , registration(reg) + ServiceReferenceBasePrivate::ServiceReferenceBasePrivate(std::weak_ptr reg) + : registration(reg) { - if (registration) - ++registration->ref; + if (auto reg = registration.lock(); reg) + { + coreInfo = reg->coreInfo; + } } - ServiceReferenceBasePrivate::~ServiceReferenceBasePrivate() + ServiceReferenceBasePrivate::~ServiceReferenceBasePrivate() = default; + + ServiceRegistrationLocks + ServiceReferenceBasePrivate::LockServiceRegistration() const { - if (registration && !--registration->ref) - { - delete registration; - } + return ServiceRegistrationLocks(registration.lock(), coreInfo); } InterfaceMapConstPtr @@ -66,11 +67,11 @@ namespace cppmicroservices assert(factory && "Factory service pointer is nullptr"); try { - InterfaceMapConstPtr smap - = factory->GetService(MakeBundle(bundle->shared_from_this()), ServiceRegistrationBase(registration)); + InterfaceMapConstPtr smap = factory->GetService(MakeBundle(bundle->shared_from_this()), + ServiceRegistrationBase(registration.lock())); if (!smap || smap->empty()) { - if (auto bundle_ = registration->bundle.lock()) + if (auto bundle_ = coreInfo->bundle_.lock()) { std::string message = "ServiceFactory returned an empty or nullptr interface map."; bundle_->coreCtx->listeners.SendFrameworkEvent(FrameworkEvent( @@ -82,14 +83,14 @@ namespace cppmicroservices } } { - auto l = registration->properties.Lock(); + auto l = coreInfo->properties.Lock(); US_UNUSED(l); for (auto const& clazz : ref_any_cast>( - registration->properties.ValueByRef_unlocked(Constants::OBJECTCLASS))) + coreInfo->properties.ValueByRef_unlocked(Constants::OBJECTCLASS))) { if (smap->find(clazz) == smap->end() && clazz != "org.cppmicroservices.factory") { - if (auto bundle_ = registration->bundle.lock()) + if (auto bundle_ = coreInfo->bundle_.lock()) { std::string message("ServiceFactory produced an object that did not implement: " + clazz); bundle_->coreCtx->listeners.SendFrameworkEvent( @@ -106,7 +107,7 @@ namespace cppmicroservices } catch (cppmicroservices::SharedLibraryException const& ex) { - if (auto bundle = registration->bundle.lock()) + if (auto bundle = coreInfo->bundle_.lock()) { bundle->coreCtx->listeners.SendFrameworkEvent(FrameworkEvent(FrameworkEvent::Type::FRAMEWORK_ERROR, ex.GetBundle(), @@ -130,7 +131,7 @@ namespace cppmicroservices catch (std::exception const& ex) { std::string message = "ServiceFactory threw an unknown exception."; - if (auto bundle_ = registration->bundle.lock()) + if (auto bundle_ = coreInfo->bundle_.lock()) { bundle_->coreCtx->listeners.SendFrameworkEvent(FrameworkEvent( FrameworkEvent::Type::FRAMEWORK_ERROR, @@ -147,12 +148,13 @@ namespace cppmicroservices { InterfaceMapConstPtr s; { - if (registration->available) + auto reg = registration.lock(); + if (coreInfo->available && reg) { - auto factory = std::static_pointer_cast( - registration->GetService("org.cppmicroservices.factory")); + auto factory + = std::static_pointer_cast(reg->GetService("org.cppmicroservices.factory")); s = GetServiceFromFactory(GetPrivate(bundle).get(), factory); - registration->Lock(), registration->prototypeServiceInstances[GetPrivate(bundle).get()].push_back(s); + LockServiceRegistration(), coreInfo->prototypeServiceInstances[GetPrivate(bundle).get()].push_back(s); } } return s; @@ -183,39 +185,50 @@ namespace cppmicroservices #endif InterfaceMapConstPtr s; - if (!registration->available) + if (!coreInfo->available) + { return s; + } std::shared_ptr serviceFactory; std::unordered_set* marks = nullptr; - + ServiceRegistrationBasePrivate* registrationPtr = registration.lock().get(); struct Unmark { ~Unmark() { if (s) + { s->erase(r); + } } std::unordered_set*& s; ServiceRegistrationBasePrivate* r; - } unmark { marks, registration }; + } unmark { marks, registrationPtr }; US_UNUSED(unmark); { - auto l = registration->Lock(); + auto reg = registration.lock(); + if (!reg) + { + return s; + } + auto l = LockServiceRegistration(); US_UNUSED(l); - if (!registration->available) + if (!coreInfo->available) + { return s; - serviceFactory = std::static_pointer_cast( - registration->GetService_unlocked("org.cppmicroservices.factory")); + } + serviceFactory + = std::static_pointer_cast(reg->GetService_unlocked("org.cppmicroservices.factory")); - auto res = registration->dependents.insert(std::make_pair(bundle, 0)); + auto res = coreInfo->dependents.insert(std::make_pair(bundle, 0)); auto& depCounter = res.first->second; // No service factory, just return the registered service directly. if (!serviceFactory) { - s = registration->service; + s = coreInfo->service; if (s && !s->empty()) { ++depCounter; @@ -223,15 +236,15 @@ namespace cppmicroservices return s; } - auto serviceIter = registration->bundleServiceInstance.find(bundle); - if (registration->bundleServiceInstance.end() != serviceIter) + auto serviceIter = coreInfo->bundleServiceInstance.find(bundle); + if (coreInfo->bundleServiceInstance.end() != serviceIter) { ++depCounter; return serviceIter->second; } marks = &threadMarks[bundle]; - if (marks->find(registration) != marks->end()) + if (marks->find(registrationPtr) != marks->end()) { // Prevent recursive service factory calls from the same thread // for the same bundle. @@ -242,7 +255,7 @@ namespace cppmicroservices msg, std::make_exception_ptr(ServiceException(msg, ServiceException::FACTORY_RECURSION))); - if (auto bundle = registration->bundle.lock()) + if (auto bundle = coreInfo->bundle_.lock()) { bundle->coreCtx->listeners.SendFrameworkEvent(fwEvent); } @@ -250,7 +263,7 @@ namespace cppmicroservices return nullptr; } - marks->insert(registration); + marks->insert(registrationPtr); } // Calling into a service factory could cause re-entrancy into the @@ -260,32 +273,32 @@ namespace cppmicroservices // possibility of infinite recursion. { - auto l = registration->Lock(); + auto l = LockServiceRegistration(); US_UNUSED(l); - if (registration->bundleServiceInstance.end() != registration->bundleServiceInstance.find(bundle)) + if (coreInfo->bundleServiceInstance.end() != coreInfo->bundleServiceInstance.find(bundle)) { - ++registration->dependents.at(bundle); - return registration->bundleServiceInstance.at(bundle); + ++coreInfo->dependents.at(bundle); + return coreInfo->bundleServiceInstance.at(bundle); } } s = GetServiceFromFactory(bundle, serviceFactory); { - auto l = registration->Lock(); + auto l = LockServiceRegistration(); US_UNUSED(l); - registration->dependents.insert(std::make_pair(bundle, 0)); + coreInfo->dependents.insert(std::make_pair(bundle, 0)); if (s && !s->empty()) { // Insert a cached service object instance only if one isn't already cached. If another thread // already inserted a cached service object, discard the service object returned by // GetServiceFromFactory and return the cached one. - auto insertResultPair = registration->bundleServiceInstance.insert(std::make_pair(bundle, s)); + auto insertResultPair = coreInfo->bundleServiceInstance.insert(std::make_pair(bundle, s)); s = insertResultPair.first->second; - ++registration->dependents.at(bundle); + ++coreInfo->dependents.at(bundle); } } return s; @@ -299,21 +312,23 @@ namespace cppmicroservices std::shared_ptr sf; { - auto l = registration->Lock(); + auto reg = registration.lock(); + auto l = LockServiceRegistration(); US_UNUSED(l); - auto iter = registration->prototypeServiceInstances.find(bundle.get()); - if (iter == registration->prototypeServiceInstances.end()) + auto iter = coreInfo->prototypeServiceInstances.find(bundle.get()); + if (iter == coreInfo->prototypeServiceInstances.end()) { return false; } prototypeServiceMaps = iter->second; - sf = std::static_pointer_cast( - registration->GetService_unlocked("org.cppmicroservices.factory")); + sf = std::static_pointer_cast(reg->GetService_unlocked("org.cppmicroservices.factory")); } if (!sf) + { return false; + } for (auto& prototypeServiceMap : prototypeServiceMaps) { @@ -322,11 +337,11 @@ namespace cppmicroservices { try { - sf->UngetService(MakeBundle(bundle), ServiceRegistrationBase(registration), service); + sf->UngetService(MakeBundle(bundle), ServiceRegistrationBase(registration.lock()), service); } catch (std::exception const& ex) { - if (auto bundle_ = registration->bundle.lock()) + if (auto bundle_ = coreInfo->bundle_.lock()) { std::string message("ServiceFactory threw an exception"); bundle_->coreCtx->listeners.SendFrameworkEvent(FrameworkEvent( @@ -338,18 +353,22 @@ namespace cppmicroservices } } - auto l = registration->Lock(); + auto l = LockServiceRegistration(); US_UNUSED(l); - auto iter = registration->prototypeServiceInstances.find(bundle.get()); - if (iter == registration->prototypeServiceInstances.end()) + auto iter = coreInfo->prototypeServiceInstances.find(bundle.get()); + if (iter == coreInfo->prototypeServiceInstances.end()) + { return true; + } auto serviceIter = std::find(iter->second.begin(), iter->second.end(), service); if (serviceIter != iter->second.end()) + { iter->second.erase(serviceIter); + } if (iter->second.empty()) { - registration->prototypeServiceInstances.erase(iter); + coreInfo->prototypeServiceInstances.erase(iter); } return true; } @@ -367,10 +386,11 @@ namespace cppmicroservices std::shared_ptr sf; { - auto l = registration->Lock(); + auto reg = registration.lock(); + auto l = LockServiceRegistration(); US_UNUSED(l); - auto depIter = registration->dependents.find(bundle.get()); - if (registration->dependents.end() == depIter) + auto depIter = coreInfo->dependents.find(bundle.get()); + if (coreInfo->dependents.end() == depIter) { return hadReferences && removeService; } @@ -399,19 +419,19 @@ namespace cppmicroservices if (removeService) { - auto serviceIter = registration->bundleServiceInstance.find(bundle.get()); - if (registration->bundleServiceInstance.end() != serviceIter) + auto serviceIter = coreInfo->bundleServiceInstance.find(bundle.get()); + if (coreInfo->bundleServiceInstance.end() != serviceIter) { sfi = serviceIter->second; } - if (sfi && !sfi->empty()) + if (sfi && !sfi->empty() && reg) { sf = std::static_pointer_cast( - registration->GetService_unlocked("org.cppmicroservices.factory")); + reg->GetService_unlocked("org.cppmicroservices.factory")); } - registration->bundleServiceInstance.erase(bundle.get()); - registration->dependents.erase(bundle.get()); + coreInfo->bundleServiceInstance.erase(bundle.get()); + coreInfo->dependents.erase(bundle.get()); } } @@ -419,11 +439,14 @@ namespace cppmicroservices { try { - sf->UngetService(MakeBundle(bundle), ServiceRegistrationBase(registration), sfi); + if (auto reg = registration.lock(); reg) + { + sf->UngetService(MakeBundle(bundle), ServiceRegistrationBase(registration.lock()), sfi); + } } catch (std::exception const& ex) { - if (auto bundle_ = registration->bundle.lock()) + if (auto bundle_ = coreInfo->bundle_.lock()) { std::string message("ServiceFactory threw an exception"); bundle_->coreCtx->listeners.SendFrameworkEvent( @@ -442,18 +465,17 @@ namespace cppmicroservices PropertiesHandle ServiceReferenceBasePrivate::GetProperties() const { - return PropertiesHandle(registration->properties, true); + return PropertiesHandle(coreInfo->properties, true); } bool ServiceReferenceBasePrivate::IsConvertibleTo(std::string const& interfaceId) const { - if (registration) + if (auto reg = registration.lock(); reg) { - auto l = registration->Lock(); + auto l = LockServiceRegistration(); US_UNUSED(l); - return registration->service ? registration->service->find(interfaceId) != registration->service->end() - : false; + return coreInfo->service ? coreInfo->service->find(interfaceId) != coreInfo->service->end() : false; } return false; } diff --git a/framework/src/service/ServiceReferenceBasePrivate.h b/framework/src/service/ServiceReferenceBasePrivate.h index 45d3721e6..e75e5a79b 100644 --- a/framework/src/service/ServiceReferenceBasePrivate.h +++ b/framework/src/service/ServiceReferenceBasePrivate.h @@ -23,8 +23,12 @@ #ifndef CPPMICROSERVICES_SERVICEREFERENCEBASEPRIVATE_H #define CPPMICROSERVICES_SERVICEREFERENCEBASEPRIVATE_H +#include "ServiceRegistrationCoreInfo.h" #include "cppmicroservices/ServiceInterface.h" +#include "Properties.h" +#include "ServiceRegistrationLocks.h" + #include #include @@ -47,10 +51,12 @@ namespace cppmicroservices ServiceReferenceBasePrivate(ServiceReferenceBasePrivate const&) = delete; ServiceReferenceBasePrivate& operator=(ServiceReferenceBasePrivate const&) = delete; - ServiceReferenceBasePrivate(ServiceRegistrationBasePrivate* reg); + ServiceReferenceBasePrivate(std::weak_ptr reg); ~ServiceReferenceBasePrivate(); + ServiceRegistrationLocks LockServiceRegistration() const; + /** * Get the service object. * @@ -99,21 +105,21 @@ namespace cppmicroservices bool IsConvertibleTo(std::string const& interfaceId) const; - /** - * Reference count for implicitly shared private implementation. - */ - std::atomic ref; - /** * Link to registration object for this reference. */ - ServiceRegistrationBasePrivate* const registration; + std::weak_ptr const registration; /** * The service interface id for this reference. */ std::string interfaceId; + /** + * Core Information for the service used by ServiceReferenceBasePrivate + */ + std::shared_ptr coreInfo; + private: InterfaceMapConstPtr GetServiceFromFactory(BundlePrivate* bundle, std::shared_ptr const& factory); diff --git a/framework/src/service/ServiceRegistrationBase.cpp b/framework/src/service/ServiceRegistrationBase.cpp index 0679b57ff..76a1b369d 100644 --- a/framework/src/service/ServiceRegistrationBase.cpp +++ b/framework/src/service/ServiceRegistrationBase.cpp @@ -1,22 +1,22 @@ - /*============================================================================= +/*============================================================================= - Library: CppMicroServices + Library: CppMicroServices - Copyright (c) The CppMicroServices developers. See the COPYRIGHT - file at the top-level directory of this distribution and at - https://github.com/CppMicroServices/CppMicroServices/COPYRIGHT . + Copyright (c) The CppMicroServices developers. See the COPYRIGHT + file at the top-level directory of this distribution and at + https://github.com/CppMicroServices/CppMicroServices/COPYRIGHT . - 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 + 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 + 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. + 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. =============================================================================*/ @@ -30,6 +30,7 @@ #include "CoreBundleContext.h" #include "ServiceListenerEntry.h" #include "ServiceRegistrationBasePrivate.h" +#include "ServiceRegistrationLocks.h" #include "ServiceRegistry.h" #include @@ -39,31 +40,27 @@ US_MSVC_DISABLE_WARNING(4503) // decorated name length exceeded, name was trunca namespace cppmicroservices { - ServiceRegistrationBase::ServiceRegistrationBase() {} + ServiceRegistrationBase::ServiceRegistrationBase() = default; - ServiceRegistrationBase::ServiceRegistrationBase(ServiceRegistrationBase const& reg) : d(reg.d) - { - if (d) - ++d->ref; - } + ServiceRegistrationBase::ServiceRegistrationBase(ServiceRegistrationBase const& reg) = default; - ServiceRegistrationBase::ServiceRegistrationBase(ServiceRegistrationBase&& reg) noexcept : d(nullptr) - { - std::swap(d, reg.d); - } + ServiceRegistrationBase::ServiceRegistrationBase(ServiceRegistrationBase&& reg) noexcept = default; - ServiceRegistrationBase::ServiceRegistrationBase(ServiceRegistrationBasePrivate* registrationPrivate) + ServiceRegistrationBase::ServiceRegistrationBase( + std::shared_ptr registrationPrivate) : d(registrationPrivate) { - if (d) - ++d->ref; } ServiceRegistrationBase::ServiceRegistrationBase(BundlePrivate* bundle, InterfaceMapConstPtr const& service, Properties&& props) - : d(new ServiceRegistrationBasePrivate(bundle, service, std::move(props))) + : d(std::make_shared(bundle, service, std::move(props))) { + // Constructor of ServiceRegistrationBasePrivate does not take in the reference back to the + // ServiceRegistrationBasePrivate. It is created after construction in CreateReference because the sharedPtr + // back to ServiceRegistrationBasePrivate must be fully constructecd before shared_from_this can be called. + d->CreateReference(); } ServiceRegistrationBase::operator bool() const { return d != nullptr; } @@ -71,29 +68,25 @@ namespace cppmicroservices ServiceRegistrationBase& ServiceRegistrationBase::operator=(std::nullptr_t) { - if (d && !--d->ref) - { - delete d; - } d = nullptr; return *this; } - ServiceRegistrationBase::~ServiceRegistrationBase() - { - if (d && !--d->ref) - delete d; - } + ServiceRegistrationBase::~ServiceRegistrationBase() = default; ServiceReferenceBase ServiceRegistrationBase::GetReference(std::string const& interfaceId) const { if (!d) + { throw std::logic_error("ServiceRegistrationBase object invalid"); - if (!d->available) + } + if (!d->coreInfo->available) + { throw std::logic_error("Service is unregistered"); + } - auto l = d->Lock(); + auto l = LockServiceRegistration(); US_UNUSED(l); ServiceReferenceBase ref = d->reference; ref.SetInterfaceId(interfaceId); @@ -120,22 +113,24 @@ namespace cppmicroservices ServiceListeners::ServiceListenerEntries before; - if (!d->available) + if (!d->coreInfo->available) { throw std::logic_error("Service is unregistered"); } { - auto l = d->Lock(); + auto l = LockServiceRegistration(); US_UNUSED(l); - if (!d->available) + if (!d->coreInfo->available) + { throw std::logic_error("Service is unregistered"); + } modifiedEndMatchEvent = ServiceEvent(ServiceEvent::SERVICE_MODIFIED_ENDMATCH, d->reference); modifiedEvent = ServiceEvent(ServiceEvent::SERVICE_MODIFIED, d->reference); } // This calls into service event listener hooks. We must not hold any locks here - if (auto bundle = d->bundle.lock()) + if (auto bundle = d->coreInfo->bundle_.lock()) { bundle->coreCtx->listeners.GetMatchingServiceListeners(modifiedEndMatchEvent, before); } @@ -144,21 +139,22 @@ namespace cppmicroservices int new_rank = 0; Any objectClasses; { - auto l = d->Lock(); + auto l = LockServiceRegistration(); US_UNUSED(l); - if (!d->available) + if (!d->coreInfo->available) { throw std::logic_error("Service is unregistered"); } - auto l2 = d->properties.Lock(); + auto l2 = d->coreInfo->properties.Lock(); US_UNUSED(l2); - propsCopy[Constants::SERVICE_ID] = std::move(d->properties.Value_unlocked(Constants::SERVICE_ID).first); - objectClasses = std::move(d->properties.Value_unlocked(Constants::OBJECTCLASS).first); + propsCopy[Constants::SERVICE_ID] + = std::move(d->coreInfo->properties.Value_unlocked(Constants::SERVICE_ID).first); + objectClasses = std::move(d->coreInfo->properties.Value_unlocked(Constants::OBJECTCLASS).first); propsCopy[Constants::OBJECTCLASS] = objectClasses; propsCopy[Constants::SERVICE_SCOPE] - = std::move(d->properties.Value_unlocked(Constants::SERVICE_SCOPE).first); + = std::move(d->coreInfo->properties.Value_unlocked(Constants::SERVICE_SCOPE).first); auto itr = propsCopy.find(Constants::SERVICE_RANKING); if (itr != propsCopy.end()) @@ -175,19 +171,19 @@ namespace cppmicroservices } } - auto oldRankAny = d->properties.Value_unlocked(Constants::SERVICE_RANKING).first; + auto oldRankAny = d->coreInfo->properties.Value_unlocked(Constants::SERVICE_RANKING).first; if (!oldRankAny.Empty()) { // since the old ranking is extracted from existing service properties // stored in the service registry, no need to type check before casting old_rank = any_cast(oldRankAny); } - d->properties = Properties(AnyMap(std::move(propsCopy))); + d->coreInfo->properties = Properties(AnyMap(std::move(propsCopy))); } if (old_rank != new_rank) { auto const& classes = ref_any_cast>(objectClasses); - if (auto bundle = d->bundle.lock()) + if (auto bundle = d->coreInfo->bundle_.lock()) { bundle->coreCtx->services.UpdateServiceRegistrationOrder(classes); } @@ -195,7 +191,7 @@ namespace cppmicroservices // Notify listeners, we must not hold any locks here ServiceListeners::ServiceListenerEntries matchingListeners; - if (auto bundle = d->bundle.lock()) + if (auto bundle = d->coreInfo->bundle_.lock()) { bundle->coreCtx->listeners.GetMatchingServiceListeners(modifiedEvent, matchingListeners); bundle->coreCtx->listeners.ServiceChanged(matchingListeners, modifiedEvent, before); @@ -213,14 +209,14 @@ namespace cppmicroservices CoreBundleContext* coreContext = nullptr; - if (!d->available) + if (!d->coreInfo->available) { throw std::logic_error("Service is unregistered"); } bool isUnregistering(false); // expected state - if (atomic_compare_exchange_strong(&d->unregistering, &isUnregistering, true)) + if (atomic_compare_exchange_strong(&d->coreInfo->unregistering, &isUnregistering, true)) { - if (auto bundle = d->bundle.lock()) + if (auto bundle = d->coreInfo->bundle_.lock()) { { auto l1 = bundle->coreCtx->services.Lock(); @@ -251,11 +247,11 @@ namespace cppmicroservices ServiceRegistrationBasePrivate::BundleToServiceMap bundleServiceInstance; { - auto l = d->Lock(); + auto l = LockServiceRegistration(); US_UNUSED(l); - d->available = false; - auto factoryIter = d->service->find("org.cppmicroservices.factory"); - if (auto bundle = d->bundle.lock() && factoryIter != d->service->end()) + d->coreInfo->available = false; + auto factoryIter = d->coreInfo->service->find("org.cppmicroservices.factory"); + if (auto bundle = d->coreInfo->bundle_.lock() && factoryIter != d->coreInfo->service->end()) { if (bundle) { @@ -264,8 +260,8 @@ namespace cppmicroservices } if (serviceFactory) { - prototypeServiceInstances = d->prototypeServiceInstances; - bundleServiceInstance = d->bundleServiceInstance; + prototypeServiceInstances = d->coreInfo->prototypeServiceInstances; + bundleServiceInstance = d->coreInfo->bundleServiceInstance; } } @@ -283,7 +279,7 @@ namespace cppmicroservices catch (std::exception const& ex) { std::string message("ServiceFactory UngetService implementation threw an exception"); - if (auto bundle = d->bundle.lock()) + if (auto bundle = d->coreInfo->bundle_.lock()) { bundle->coreCtx->listeners.SendFrameworkEvent(FrameworkEvent( FrameworkEvent::Type::FRAMEWORK_ERROR, @@ -306,7 +302,7 @@ namespace cppmicroservices catch (std::exception const& ex) { std::string message("ServiceFactory UngetService implementation threw an exception"); - if (auto bundle = d->bundle.lock()) + if (auto bundle = d->coreInfo->bundle_.lock()) { bundle->coreCtx->listeners.SendFrameworkEvent(FrameworkEvent( FrameworkEvent::Type::FRAMEWORK_ERROR, @@ -320,39 +316,46 @@ namespace cppmicroservices } { - auto l = d->Lock(); + auto l = LockServiceRegistration(); US_UNUSED(l); - d->bundle.reset(); - d->dependents.clear(); - d->service.reset(); - d->prototypeServiceInstances.clear(); - d->bundleServiceInstance.clear(); - // increment the reference count, since "d->reference" was used originally - // to keep d alive. - ++d->ref; + d->coreInfo->bundle_.reset(); + d->coreInfo->dependents.clear(); + d->coreInfo->service.reset(); + d->coreInfo->prototypeServiceInstances.clear(); + d->coreInfo->bundleServiceInstance.clear(); + d->reference = nullptr; - d->unregistering = false; + d->coreInfo->unregistering = false; } } + ServiceRegistrationLocks + ServiceRegistrationBase::LockServiceRegistration() const + { + return ServiceRegistrationLocks(d, d->coreInfo); + } + bool ServiceRegistrationBase::operator<(ServiceRegistrationBase const& o) const { if (this == &o || d == o.d) + { return false; + } if ((!d && !o.d) || !o.d) + { return false; + } if (!d) - return true; - - ServiceReferenceBase sr1; - ServiceReferenceBase sr2; { - d->Lock(), sr1 = d->reference; - o.d->Lock(), sr2 = o.d->reference; + return true; } + + ServiceReferenceBase sr1 = (LockServiceRegistration(), d->reference); + ServiceReferenceBase sr2 = (o.LockServiceRegistration(), o.d->reference); + return sr1 < sr2; } @@ -362,29 +365,10 @@ namespace cppmicroservices return d == registration.d; } - ServiceRegistrationBase& - ServiceRegistrationBase::operator=(ServiceRegistrationBase const& registration) - { - ServiceRegistrationBasePrivate* curr_d = d; - d = registration.d; - if (d) - ++d->ref; + ServiceRegistrationBase& ServiceRegistrationBase::operator=(ServiceRegistrationBase const& registration) = default; - if (curr_d && !--curr_d->ref) - delete curr_d; - - return *this; - } - - ServiceRegistrationBase& - ServiceRegistrationBase::operator=(ServiceRegistrationBase&& registration) noexcept - { - if (d && !--d->ref) - delete d; - d = nullptr; - std::swap(d, registration.d); - return *this; - } + ServiceRegistrationBase& ServiceRegistrationBase::operator=(ServiceRegistrationBase&& registration) noexcept + = default; std::ostream& operator<<(std::ostream& os, ServiceRegistrationBase const&) diff --git a/framework/src/service/ServiceRegistrationBasePrivate.cpp b/framework/src/service/ServiceRegistrationBasePrivate.cpp index c70a69873..c04862d42 100644 --- a/framework/src/service/ServiceRegistrationBasePrivate.cpp +++ b/framework/src/service/ServiceRegistrationBasePrivate.cpp @@ -33,24 +33,20 @@ namespace cppmicroservices { - ServiceRegistrationBasePrivate::ServiceRegistrationBasePrivate(BundlePrivate* bundle_, + ServiceRegistrationBasePrivate::ServiceRegistrationBasePrivate(BundlePrivate* bundle, InterfaceMapConstPtr service, Properties&& props) - : ref(0) - , service(std::move(service)) - , bundle(bundle_->shared_from_this()) - , reference(this) - , properties(std::move(props)) - , available(true) - , unregistering(false) + : coreInfo(std::make_shared(bundle, service, std::move(props))) { - // The reference counter is initialized to 0 because it will be - // incremented by the "reference" member. } - ServiceRegistrationBasePrivate::~ServiceRegistrationBasePrivate() + ServiceRegistrationBasePrivate::~ServiceRegistrationBasePrivate() = default; + + // Need to first create shared_ptr to registration before duplicating for reference + void + ServiceRegistrationBasePrivate::CreateReference() { - properties.Lock(), properties.Clear_unlocked(); + reference = shared_from_this(); } bool @@ -58,26 +54,28 @@ namespace cppmicroservices { auto l = this->Lock(); US_UNUSED(l); - return (dependents.find(bundle) != dependents.end()) - || (prototypeServiceInstances.find(bundle) != prototypeServiceInstances.end()); + auto l1 = coreInfo->Lock(); + US_UNUSED(l1); + return (coreInfo->dependents.find(bundle) != coreInfo->dependents.end()) + || (coreInfo->prototypeServiceInstances.find(bundle) != coreInfo->prototypeServiceInstances.end()); } InterfaceMapConstPtr ServiceRegistrationBasePrivate::GetInterfaces() const { - return (this->Lock(), service); + return (this->Lock(), coreInfo->Lock(), coreInfo->service); } std::shared_ptr ServiceRegistrationBasePrivate::GetService(std::string const& interfaceId) const { - return this->Lock(), GetService_unlocked(interfaceId); + return this->Lock(), coreInfo->Lock(), GetService_unlocked(interfaceId); } std::shared_ptr ServiceRegistrationBasePrivate::GetService_unlocked(std::string const& interfaceId) const { - return ExtractInterface(service, interfaceId); + return ExtractInterface(coreInfo->service, interfaceId); } } // namespace cppmicroservices diff --git a/framework/src/service/ServiceRegistrationBasePrivate.h b/framework/src/service/ServiceRegistrationBasePrivate.h index ac829fe64..48e7830a9 100644 --- a/framework/src/service/ServiceRegistrationBasePrivate.h +++ b/framework/src/service/ServiceRegistrationBasePrivate.h @@ -28,6 +28,7 @@ #include "cppmicroservices/detail/Threads.h" #include "Properties.h" +#include "ServiceRegistrationCoreInfo.h" #include @@ -40,29 +41,14 @@ namespace cppmicroservices /** * \ingroup MicroServices */ - class ServiceRegistrationBasePrivate : public detail::MultiThreaded<> + class ServiceRegistrationBasePrivate + : public detail::MultiThreaded<> + , public std::enable_shared_from_this { protected: friend class ServiceRegistrationBase; - // The ServiceReferenceBasePrivate class holds a pointer to a - // ServiceRegistrationBasePrivate instance and needs to manipulate - // its reference count. This way it can keep the ServiceRegistrationBasePrivate - // instance alive and keep returning service properties for - // unregistered service instances. - friend class ServiceReferenceBasePrivate; - - /** - * Reference count for implicitly shared private implementation. - */ - std::atomic ref; - - /** - * Service or ServiceFactory object. - */ - InterfaceMapConstPtr service; - public: using BundleToRefsMap = std::unordered_map; using BundleToServiceMap = std::unordered_map; @@ -71,54 +57,23 @@ namespace cppmicroservices ServiceRegistrationBasePrivate(ServiceRegistrationBasePrivate const&) = delete; ServiceRegistrationBasePrivate& operator=(ServiceRegistrationBasePrivate const&) = delete; - /** - * Bundles dependent on this service. Integer is used as - * reference counter, counting number of unbalanced getService(). - */ - BundleToRefsMap dependents; - - /** - * Object instances that a prototype factory has produced. - */ - BundleToServicesMap prototypeServiceInstances; - - /** - * Object instance with bundle scope that a factory may have produced. - */ - BundleToServiceMap bundleServiceInstance; - - /** - * Bundle registering this service. - */ - std::weak_ptr bundle; - /** * Reference object to this service registration. */ ServiceReferenceBase reference; /** - * Service properties. + * Pointer to CoreInfo object for this registration. */ - Properties properties; - - /** - * Is service available. I.e., if true then holders - * of a ServiceReference for the service are allowed to get it. - */ - std::atomic available; - - /** - * Avoid recursive unregistrations. I.e., if true then - * unregistration of this service has started but is not yet - * finished. - */ - std::atomic unregistering; + std::shared_ptr coreInfo; ServiceRegistrationBasePrivate(BundlePrivate* bundle, InterfaceMapConstPtr service, Properties&& props); ~ServiceRegistrationBasePrivate(); + // Avoid Weak Ptr errors creating reference + void CreateReference(); + /** * Check if a bundle uses this service * diff --git a/framework/src/service/ServiceRegistrationCoreInfo.cpp b/framework/src/service/ServiceRegistrationCoreInfo.cpp new file mode 100644 index 000000000..edcf3a10c --- /dev/null +++ b/framework/src/service/ServiceRegistrationCoreInfo.cpp @@ -0,0 +1,47 @@ +/*============================================================================= + + Library: CppMicroServices + + Copyright (c) The CppMicroServices developers. See the COPYRIGHT + file at the top-level directory of this distribution and at + https://github.com/CppMicroServices/CppMicroServices/COPYRIGHT . + + 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. + +=============================================================================*/ + +#include "ServiceRegistrationCoreInfo.h" + +#ifdef _MSC_VER +# pragma warning(push) +# pragma warning(disable : 4355) +#endif + +namespace cppmicroservices +{ + + ServiceRegistrationCoreInfo::ServiceRegistrationCoreInfo(BundlePrivate* bundle, + InterfaceMapConstPtr service, + Properties&& props) + : service(std::move(service)) + , bundle_(bundle->shared_from_this()) + , properties(std::move(props)) + , available(true) + , unregistering(false) + { + } +} // namespace cppmicroservices + +#ifdef _MSC_VER +# pragma warning(pop) +#endif diff --git a/framework/src/service/ServiceRegistrationCoreInfo.h b/framework/src/service/ServiceRegistrationCoreInfo.h new file mode 100644 index 000000000..db80bb2de --- /dev/null +++ b/framework/src/service/ServiceRegistrationCoreInfo.h @@ -0,0 +1,103 @@ +/*============================================================================= + + Library: CppMicroServices + + Copyright (c) The CppMicroServices developers. See the COPYRIGHT + file at the top-level directory of this distribution and at + https://github.com/CppMicroServices/CppMicroServices/COPYRIGHT . + + 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. + +=============================================================================*/ + +#ifndef CPPMICROSERVICES_SERVICEREGISTRATIONCOREINFO_H +#define CPPMICROSERVICES_SERVICEREGISTRATIONCOREINFO_H + +#include "cppmicroservices/ServiceInterface.h" +#include "cppmicroservices/ServiceReference.h" +#include "cppmicroservices/detail/Threads.h" + +#include "BundlePrivate.h" +#include "Properties.h" + +#include + +namespace cppmicroservices +{ + + /** + * \ingroup MicroServices + */ + class ServiceRegistrationCoreInfo final: public detail::MultiThreaded<> + { + + public: + ServiceRegistrationCoreInfo(BundlePrivate* bundle, InterfaceMapConstPtr service, Properties&& props); + ~ServiceRegistrationCoreInfo() = default; + + ServiceRegistrationCoreInfo(ServiceRegistrationCoreInfo &&) = delete; + ServiceRegistrationCoreInfo& operator=(ServiceRegistrationCoreInfo &&) = delete; + + ServiceRegistrationCoreInfo(ServiceRegistrationCoreInfo const&) = delete; + ServiceRegistrationCoreInfo& operator=(ServiceRegistrationCoreInfo const&) = delete; + + using BundleToRefsMap = std::unordered_map; + using BundleToServiceMap = std::unordered_map; + using BundleToServicesMap = std::unordered_map>; + + /** + * Service or ServiceFactory object. + */ + InterfaceMapConstPtr service; + /** + * Bundles dependent on this service. Integer is used as + * reference counter, counting number of unbalanced getService(). + */ + BundleToRefsMap dependents; + + /** + * Object instances that a prototype factory has produced. + */ + BundleToServicesMap prototypeServiceInstances; + + /** + * Object instance with bundle scope that a factory may have produced. + */ + BundleToServiceMap bundleServiceInstance; + + /** + * Bundle registering this service. + */ + std::weak_ptr bundle_; + + /** + * Service properties. + */ + Properties properties; + + /** + * Is service available. I.e., if true then holders + * of a ServiceReference for the service are allowed to get it. + */ + std::atomic available; + + /** + * Avoid recursive unregistrations. I.e., if true then + * unregistration of this service has started but is not yet + * finished. + */ + std::atomic unregistering; + }; +} // namespace cppmicroservices + +#endif // CPPMICROSERVICES_SERVICEREGISTRATIONCOREINFO_H \ No newline at end of file diff --git a/framework/src/service/ServiceRegistry.cpp b/framework/src/service/ServiceRegistry.cpp index 317c983d2..a6a498679 100644 --- a/framework/src/service/ServiceRegistry.cpp +++ b/framework/src/service/ServiceRegistry.cpp @@ -263,7 +263,7 @@ namespace cppmicroservices for (; s != send; ++s) { - if (filter.empty() || ldap.Evaluate(PropertiesHandle(s->d->properties, true), false)) + if (filter.empty() || ldap.Evaluate(PropertiesHandle((s->d->coreInfo->properties), true), false)) { res.emplace_back(s->GetReference(clazz)); } @@ -296,12 +296,12 @@ namespace cppmicroservices { std::vector classes; { - auto l2 = sr.d->properties.Lock(); + auto l2 = sr.d->coreInfo->properties.Lock(); US_UNUSED(l2); - assert(sr.d->properties.Value_unlocked(Constants::OBJECTCLASS).first.Type() + assert(sr.d->coreInfo->properties.Value_unlocked(Constants::OBJECTCLASS).first.Type() == typeid(std::vector)); - classes - = ref_any_cast>(sr.d->properties.Value_unlocked(Constants::OBJECTCLASS).first); + classes = ref_any_cast>( + sr.d->coreInfo->properties.Value_unlocked(Constants::OBJECTCLASS).first); } services.erase(sr); serviceRegistrations.erase(std::remove(serviceRegistrations.begin(), serviceRegistrations.end(), sr), @@ -328,7 +328,7 @@ namespace cppmicroservices for (auto& sr : serviceRegistrations) { - if (auto bundle_ = sr.d->bundle.lock()) + if (auto bundle_ = sr.d->coreInfo->bundle_.lock()) { if (bundle_.get() == p) { diff --git a/framework/src/util/AnyMap.cpp b/framework/src/util/AnyMap.cpp index 5925e62f5..9eba38b5f 100644 --- a/framework/src/util/AnyMap.cpp +++ b/framework/src/util/AnyMap.cpp @@ -23,6 +23,7 @@ #include "cppmicroservices/AnyMap.h" #include +#include #include namespace cppmicroservices @@ -602,18 +603,18 @@ namespace cppmicroservices // ---------------------------------------------------------- // ------------------------ any_map ----------------------- - any_map::any_map(map_type type) : type(type) + any_map::any_map(map_type type, std::initializer_list l) : type(type) { switch (type) { case map_type::ORDERED_MAP: - map.o = new ordered_any_map(); + map.o = new ordered_any_map(l); break; case map_type::UNORDERED_MAP: - map.uo = new unordered_any_map(); + map.uo = new unordered_any_map(l); break; case map_type::UNORDERED_MAP_CASEINSENSITIVE_KEYS: - map.uoci = new unordered_any_cimap(); + map.uoci = new unordered_any_cimap(l); break; default: throw std::logic_error("invalid map type"); @@ -647,7 +648,9 @@ namespace cppmicroservices any_map::operator=(any_map const& m) { if (this == &m) + { return *this; + } destroy(); type = m.type; @@ -1105,7 +1108,11 @@ namespace cppmicroservices // ---------------------------------------------------------- // ------------------------ AnyMap ------------------------ - AnyMap::AnyMap(map_type type) : any_map(type) {} + AnyMap::AnyMap(std::initializer_list l) + : any_map(any_map::UNORDERED_MAP_CASEINSENSITIVE_KEYS, l) + { + } + AnyMap::AnyMap(map_type type, std::initializer_list l) : any_map(type, l) {} AnyMap::AnyMap(ordered_any_map const& m) : any_map(m) {} @@ -1149,9 +1156,13 @@ namespace cppmicroservices for (; i1 != end; ++i1) { if (i1 == begin) + { os << i1->first << " : " << i1->second.ToString(); + } else + { os << ", " << i1->first << " : " << i1->second.ToString(); + } } os << "}"; return os; diff --git a/framework/src/util/FrameworkPrivate.cpp b/framework/src/util/FrameworkPrivate.cpp index 67781a356..e4f0bf804 100644 --- a/framework/src/util/FrameworkPrivate.cpp +++ b/framework/src/util/FrameworkPrivate.cpp @@ -153,7 +153,7 @@ namespace cppmicroservices break; case Bundle::STATE_ACTIVE: wasActive = true; - // Fall through + [[fallthrough]]; case Bundle::STATE_STARTING: { bool const wa = wasActive; @@ -187,7 +187,7 @@ namespace cppmicroservices case Bundle::STATE_INSTALLED: case Bundle::STATE_RESOLVED: DoInit(); - // Fall through + [[fallthrough]]; case Bundle::STATE_STARTING: operation = BundlePrivate::OP_ACTIVATING; break; diff --git a/framework/src/util/LDAPExpr.cpp b/framework/src/util/LDAPExpr.cpp index 5dc234341..42912ff0b 100644 --- a/framework/src/util/LDAPExpr.cpp +++ b/framework/src/util/LDAPExpr.cpp @@ -25,9 +25,8 @@ #include "cppmicroservices/Any.h" #include "cppmicroservices/Constants.h" -#include "absl/strings/str_cat.h" - #include "Properties.h" +#include "Utils.h" #include "PropsCheck.h" @@ -347,7 +346,7 @@ namespace cppmicroservices if (!Trim(ps.rest()).empty()) { - ps.error(absl::StrCat(LDAPExprConstants::GARBAGE(), " '", ps.rest(), "'")); + ps.error(StringCatFast(LDAPExprConstants::GARBAGE(), " '", ps.rest(), "'")); } d = expr.d; @@ -376,7 +375,7 @@ namespace cppmicroservices { str.erase(0, str.find_first_not_of(' ')); auto const last_not_space = str.find_last_not_of(' '); - if(last_not_space != std::string::npos) + if (last_not_space != std::string::npos) { str.erase(last_not_space + 1); } @@ -493,7 +492,9 @@ namespace cppmicroservices for (auto const& m_arg : d->m_args) { if (!m_arg.IsSimple(keywords, cache, matchCase)) + { return false; + } } return true; } @@ -621,14 +622,18 @@ namespace cppmicroservices for (auto const& m_arg : d->m_args) { if (!m_arg.Evaluate(p, matchCase)) + { return false; + } } return true; case OR: for (auto const& m_arg : d->m_args) { if (m_arg.Evaluate(p, matchCase)) + { return true; + } } return false; case NOT: @@ -643,9 +648,13 @@ namespace cppmicroservices LDAPExpr::Compare(Any const& obj, int op, std::string const& s) const { if (obj.Empty()) + { return false; + } if (op == EQ && s == LDAPExprConstants::WILDCARD_STRING()) + { return true; + } try { @@ -654,13 +663,19 @@ namespace cppmicroservices { return CompareString(ref_any_cast(obj), op, s); } + else if (objType == typeid(char const*)) + { + return CompareString(std::string(ref_any_cast(obj)), op, s); + } else if (objType == typeid(std::vector)) { auto const& list = ref_any_cast>(obj); for (std::size_t it = 0; it != list.size(); it++) { if (CompareString(list[it], op, s)) + { return true; + } } } else if (objType == typeid(std::list)) @@ -669,7 +684,9 @@ namespace cppmicroservices for (auto const& it : list) { if (CompareString(it, op, s)) + { return true; + } } } else if (objType == typeid(char)) @@ -679,7 +696,9 @@ namespace cppmicroservices else if (objType == typeid(bool)) { if (op == LE || op == GE) + { return false; + } std::string boolVal = any_cast(obj) ? "true" : "false"; return std::equal(s.begin(), s.end(), boolVal.begin(), stricomp); @@ -776,7 +795,9 @@ namespace cppmicroservices for (std::size_t it = 0; it != list.size(); it++) { if (Compare(list[it], op, s)) + { return true; + } } } } @@ -846,7 +867,9 @@ namespace cppmicroservices if (!std::isspace(c)) { if (std::isupper(c)) + { c = std::tolower(c); + } sb.append(1, c); } } @@ -857,16 +880,22 @@ namespace cppmicroservices LDAPExpr::PatSubstr(const std::string_view s, int si, const std::string_view pat, int pi) { if (pat.size() - pi == 0) + { return s.size() - si == 0; + } if (pat[pi] == LDAPExprConstants::WILDCARD()) { pi++; for (;;) { if (PatSubstr(s, si, pat, pi)) + { return true; + } if (s.size() - si == 0) + { return false; + } si++; } } @@ -895,7 +924,9 @@ namespace cppmicroservices { ps.skipWhite(); if (!ps.prefix("(")) + { ps.error(LDAPExprConstants::MALFORMED()); + } int op; ps.skipWhite(); @@ -927,7 +958,9 @@ namespace cppmicroservices std::size_t n = v.size(); if (!ps.prefix(")") || n == 0 || (op == NOT && n > 1)) + { ps.error(LDAPExprConstants::MALFORMED()); + } return LDAPExpr(op, v); } @@ -937,16 +970,26 @@ namespace cppmicroservices { std::string attrName = ps.getAttributeName(); if (attrName.empty()) + { ps.error(LDAPExprConstants::MALFORMED()); + } int op = 0; if (ps.prefix("=")) + { op = EQ; + } else if (ps.prefix("<=")) + { op = LE; + } else if (ps.prefix(">=")) + { op = GE; + } else if (ps.prefix("~=")) + { op = APPROX; + } else { // System.out.println("undef op='" + ps.peek() + "'"); @@ -954,7 +997,9 @@ namespace cppmicroservices } std::string attrValue = ps.getAttributeValue(); if (!ps.prefix(")")) + { ps.error(LDAPExprConstants::MALFORMED()); + } return LDAPExpr(op, attrName, attrValue); } @@ -1033,7 +1078,9 @@ namespace cppmicroservices { std::string::iterator startIter = m_str.begin() + m_pos; if (!std::equal(pre.begin(), pre.end(), startIter)) + { return false; + } m_pos += pre.size(); return true; } @@ -1143,7 +1190,7 @@ namespace cppmicroservices void LDAPExpr::ParseState::error(std::string const& m) const { - std::string errorStr = absl::StrCat(m, ": ", (m_str.empty() ? "" : m_str.substr(m_pos))); + std::string errorStr = StringCatFast(m, ": ", (m_str.empty() ? "" : m_str.substr(m_pos))); throw std::invalid_argument(errorStr); } } // namespace cppmicroservices diff --git a/framework/src/util/LDAPFilter.cpp b/framework/src/util/LDAPFilter.cpp index 951f3f451..34d63071c 100644 --- a/framework/src/util/LDAPFilter.cpp +++ b/framework/src/util/LDAPFilter.cpp @@ -29,6 +29,7 @@ #include "Properties.h" #include "PropsCheck.h" #include "ServiceReferenceBasePrivate.h" +#include "Utils.h" #include @@ -70,7 +71,7 @@ namespace cppmicroservices bool LDAPFilter::Match(ServiceReferenceBase const& reference) const { - return ((d) ? d->ldapExpr.Evaluate(reference.d.load()->GetProperties(), false) : false); + return ((d) ? d->ldapExpr.Evaluate(reference.d.Load()->GetProperties(), false) : false); } // This function has been modified to call the LDAPExpr::Evaluate() function which takes diff --git a/framework/src/util/ServiceRegistrationLocks.cpp b/framework/src/util/ServiceRegistrationLocks.cpp new file mode 100644 index 000000000..ded5358bf --- /dev/null +++ b/framework/src/util/ServiceRegistrationLocks.cpp @@ -0,0 +1,37 @@ +/*============================================================================= + Library: CppMicroServices + Copyright (c) The CppMicroServices developers. See the COPYRIGHT + file at the top-level directory of this distribution and at + https://github.com/CppMicroServices/CppMicroServices/COPYRIGHT . + 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. +=============================================================================*/ + +#include "ServiceRegistrationLocks.h" + +#ifdef _MSC_VER +# pragma warning(push) +# pragma warning(disable : 4355) +#endif + +namespace cppmicroservices +{ + + ServiceRegistrationLocks::ServiceRegistrationLocks(const std::shared_ptr& reg, + const std::shared_ptr& coreInfo) + : regL(reg != nullptr ? reg->Lock() : cppmicroservices::detail::MutexLockingStrategy<>::UniqueLock()) + , coreInfoL(coreInfo->Lock()) + { + } +} // namespace cppmicroservices + +#ifdef _MSC_VER +# pragma warning(pop) +#endif \ No newline at end of file diff --git a/framework/src/util/ServiceRegistrationLocks.h b/framework/src/util/ServiceRegistrationLocks.h new file mode 100644 index 000000000..563d160f9 --- /dev/null +++ b/framework/src/util/ServiceRegistrationLocks.h @@ -0,0 +1,53 @@ +/*============================================================================= + Library: CppMicroServices + Copyright (c) The CppMicroServices developers. See the COPYRIGHT + file at the top-level directory of this distribution and at + https://github.com/CppMicroServices/CppMicroServices/COPYRIGHT . + 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. +=============================================================================*/ + +#ifndef CPPMICROSERVICES_SERVICEREGISTRATIONLOCKS_H +#define CPPMICROSERVICES_SERVICEREGISTRATIONLOCKS_H + +#include "ServiceRegistrationBasePrivate.h" +#include "ServiceRegistrationCoreInfo.h" + +namespace cppmicroservices +{ + + /** + * \ingroup MicroServices + * This class is designed to store locks to the ServiceRegistrationBasePrivate object and the + * ServiceRegistrationCoreInfo objects. This will lock and unlock them in order to avoid deadlocks. It also verifies + * the aliveness of the ServiceRegistrationBasePrivate object as its lifetime is no longer linked to + * ServiceReferenceBasePrivate and can therefore die while ServiceRegistrationCoreInfo is still needed. + * + * This is an RAII object, construct it in scope and when it goes out of scope the locks will be released. + */ + class ServiceRegistrationLocks final + { + public: + ServiceRegistrationLocks(const std::shared_ptr& reg, + const std::shared_ptr& coreInfo); + + // Delete all copy and move to enforce that it is only ever constructed into one object -- avoids deadlocks + ServiceRegistrationLocks(ServiceRegistrationLocks const& lockObj) = delete; + ServiceRegistrationLocks(ServiceRegistrationLocks&& lockObj) = default; + ServiceRegistrationLocks& operator=(ServiceRegistrationLocks const& lockObj) = delete; + ServiceRegistrationLocks& operator=(ServiceRegistrationLocks&& lockObj) = delete; + + private: + cppmicroservices::detail::MutexLockingStrategy<>::UniqueLock regL; + cppmicroservices::detail::MutexLockingStrategy<>::UniqueLock coreInfoL; + }; +} // namespace cppmicroservices + +#endif // CPPMICROSERVICES_SERVICEREGISTRATIONLOCKS_H \ No newline at end of file diff --git a/framework/src/util/Utils.cpp b/framework/src/util/Utils.cpp index 6a98922c7..bd781cb14 100644 --- a/framework/src/util/Utils.cpp +++ b/framework/src/util/Utils.cpp @@ -109,7 +109,7 @@ namespace cppmicroservices // Framework storage //------------------------------------------------------------------- - const std::string FWDIR_DEFAULT = "fwdir"; + std::string const FWDIR_DEFAULT = "fwdir"; std::string GetFrameworkDir(CoreBundleContext* ctx) @@ -126,12 +126,12 @@ namespace cppmicroservices GetPersistentStoragePath(CoreBundleContext* ctx, std::string const& leafDir, bool create) { // See if we have a storage directory - const std::string fwdir(GetFrameworkDir(ctx)); + std::string const fwdir(GetFrameworkDir(ctx)); if (fwdir.empty()) { return fwdir; } - const std::string dir = util::GetAbsolute(fwdir, ctx->workingDir) + util::DIR_SEP + leafDir; + std::string const dir = util::GetAbsolute(fwdir, ctx->workingDir) + util::DIR_SEP + leafDir; if (!dir.empty()) { if (util::Exists(dir)) @@ -160,7 +160,7 @@ namespace cppmicroservices } void - TerminateForDebug(const std::exception_ptr ex) + TerminateForDebug(std::exception_ptr const ex) { #if defined(_MSC_VER) && !defined(NDEBUG) && defined(_DEBUG) && defined(_CRT_ERROR) std::string message = util::GetLastExceptionStr(); @@ -170,9 +170,13 @@ namespace cppmicroservices _CrtSetReportMode(_CRT_ERROR, reportMode); int ret = _CrtDbgReport(_CRT_ERROR, __FILE__, __LINE__, CppMicroServices_VERSION_STR, message.c_str()); if (ret == 0 && reportMode & _CRTDBG_MODE_WNDW) + { return; // ignore + } else if (ret == 1) + { _CrtDbgBreak(); + } #else (void)ex; #endif diff --git a/framework/src/util/Utils.h b/framework/src/util/Utils.h index 0cb5af85b..d3046c359 100644 --- a/framework/src/util/Utils.h +++ b/framework/src/util/Utils.h @@ -31,6 +31,116 @@ namespace cppmicroservices { + namespace stringCatFast + { + + template + struct string_size_impl; + + template + struct string_size_impl + { + static constexpr size_t + size(char const (&)[N]) + { + return N - 1; + } + }; + + template + struct string_size_impl + { + static size_t + size(char (&s)[N]) + { + return N ? strlen(s) : 0; + } + }; + + template <> + struct string_size_impl + { + static size_t + size(char const* s) + { + return s ? strlen(s) : 0; + } + }; + + template <> + struct string_size_impl + { + static size_t + size(char* s) + { + return s ? strlen(s) : 0; + } + }; + + template <> + struct string_size_impl + { + static size_t + size(std::string const& s) + { + return s.size(); + } + }; + + template + size_t + string_size(String&& s) + { + using noref_t = typename std::remove_reference::type; + using string_t = typename std:: + conditional::value, noref_t, typename std::remove_cv::type>::type; + return string_size_impl::size(s); + } + + template + struct concatenate_impl; + + template + struct concatenate_impl + { + static size_t + size(String const& s) + { + return string_size(s); + } + static void + concatenate(std::string& result, String&& s) + { + result += s; + } + }; + + template + struct concatenate_impl + { + static size_t + size(String const& s, Rest... rest) + { + return string_size(s) + concatenate_impl::size((rest)...); + } + static void + concatenate(std::string& result, String&& s, Rest&&... rest) + { + result += s; + concatenate_impl::concatenate(result, std::forward(rest)...); + } + }; + + } // namespace stringCatFast + template + std::string + StringCatFast(Strings&&... strings) + { + std::string result; + result.reserve(stringCatFast::concatenate_impl::size((strings)...)); + stringCatFast::concatenate_impl::concatenate(result, std::forward(strings)...); + return result; + } //------------------------------------------------------------------- // File type checking @@ -56,7 +166,7 @@ namespace cppmicroservices class CoreBundleContext; - extern const std::string FWDIR_DEFAULT; + extern std::string const FWDIR_DEFAULT; std::string GetFrameworkDir(CoreBundleContext* ctx); @@ -79,7 +189,7 @@ namespace cppmicroservices // Generic utility functions //------------------------------------------------------------------- - void TerminateForDebug(const std::exception_ptr ex); + void TerminateForDebug(std::exception_ptr const ex); namespace detail { diff --git a/framework/test/bench/BundleTrackerTest.cpp b/framework/test/bench/BundleTrackerTest.cpp new file mode 100644 index 000000000..b4480189a --- /dev/null +++ b/framework/test/bench/BundleTrackerTest.cpp @@ -0,0 +1,298 @@ +#include +#include +#include +#include +#include +#include + +#include + +#include "TestUtils.h" +#include "benchmark/benchmark.h" + +#ifdef GetObject +# undef GetObject +#endif + +using namespace cppmicroservices; + +class BundleTrackerFixture : public ::benchmark::Fixture +{ +public: + using benchmark::Fixture::SetUp; + using benchmark::Fixture::TearDown; + + void SetUp(const ::benchmark::State&) + { + framework = std::make_shared(FrameworkFactory().NewFramework()); + framework->Start(); + } + + void TearDown(const ::benchmark::State&) + { + using namespace std::chrono; + + framework->Stop(); + framework->WaitForStop(milliseconds::zero()); + } + + ~BundleTrackerFixture() { framework.reset(); }; + + std::shared_ptr framework; + static constexpr BundleTracker<>::BundleStateMaskType all_states = + BundleTracker<>::CreateStateMask(Bundle::State::STATE_ACTIVE, + Bundle::State::STATE_INSTALLED, + Bundle::State::STATE_RESOLVED, + Bundle::State::STATE_STARTING, + Bundle::State::STATE_STOPPING, + Bundle::State::STATE_UNINSTALLED); +}; + +BENCHMARK_DEFINE_F(BundleTrackerFixture, CreateBundleTracker) +(benchmark::State& state) +{ + auto context = framework->GetBundleContext(); + for (auto _ : state) { + for (auto i = 0; i < state.range(0); ++i) { + BundleTracker<> bundleTracker(context, all_states); + } + } +} + +BENCHMARK_DEFINE_F(BundleTrackerFixture, OpenBundleTracker) +(benchmark::State& state) +{ + auto context = framework->GetBundleContext(); + BundleTracker<> bundleTracker(context, all_states); + for (auto _ : state) { + auto start = std::chrono::high_resolution_clock::now(); + bundleTracker.Open(); + auto end = std::chrono::high_resolution_clock::now(); + + auto elapsed_time = + std::chrono::duration_cast>(end - start); + state.SetIterationTime(elapsed_time.count()); + + bundleTracker.Close(); + } +} + +BENCHMARK_DEFINE_F(BundleTrackerFixture, BundleTrackerGetObject) +(benchmark::State& state) +{ + auto context = framework->GetBundleContext(); + auto stateMask = + BundleTracker<>::CreateStateMask(Bundle::State::STATE_INSTALLED); + BundleTracker<> bundleTracker(context, stateMask); + bundleTracker.Open(); + Bundle bundle = testing::InstallLib(context, "TestBundleA"); + + for (auto _ : state) { + auto start = std::chrono::high_resolution_clock::now(); + bundleTracker.GetObject(bundle); + auto end = std::chrono::high_resolution_clock::now(); + + auto elapsed_time = + std::chrono::duration_cast>(end - start); + state.SetIterationTime(elapsed_time.count()); + } + + bundleTracker.Close(); +} + +BENCHMARK_DEFINE_F(BundleTrackerFixture, BundleTrackerRemoveMethod) +(benchmark::State& state) +{ + auto context = framework->GetBundleContext(); + auto stateMask = + BundleTracker<>::CreateStateMask(Bundle::State::STATE_ACTIVE); + BundleTracker<> bundleTracker(context, stateMask); + bundleTracker.Open(); + Bundle bundle = testing::InstallLib(context, "TestBundleA"); + + for (auto _ : state) { + // Make bundle enter tracked state + bundle.Start(); + + // Measure performance of Remove(bundle) + auto start = std::chrono::high_resolution_clock::now(); + bundleTracker.Remove(bundle); + auto end = std::chrono::high_resolution_clock::now(); + + auto elapsed_time = + std::chrono::duration_cast>(end - start); + state.SetIterationTime(elapsed_time.count()); + + // Make bundle leave tracked state + bundle.Stop(); + } + + bundleTracker.Close(); +} + +BENCHMARK_DEFINE_F(BundleTrackerFixture, CloseBundleTracker) +(benchmark::State& state) +{ + auto context = framework->GetBundleContext(); + auto stateMask = + BundleTracker<>::CreateStateMask(Bundle::State::STATE_ACTIVE); + BundleTracker<> bundleTracker(context, stateMask); + + for (auto _ : state) { + bundleTracker.Open(); + + auto start = std::chrono::high_resolution_clock::now(); + bundleTracker.Close(); + auto end = std::chrono::high_resolution_clock::now(); + + auto elapsed_time = + std::chrono::duration_cast>(end - start); + state.SetIterationTime(elapsed_time.count()); + } +} + +BENCHMARK_DEFINE_F(BundleTrackerFixture, CloseBundleTrackerWithListeners) +(benchmark::State& state) +{ + auto context = framework->GetBundleContext(); + auto stateMask = + BundleTracker<>::CreateStateMask(Bundle::State::STATE_ACTIVE); + BundleTracker<> bundleTracker(context, stateMask); + + for (auto i = 0; i < state.range(0); ++i) { + context.AddBundleListener([](const BundleEvent&) {}); + } + + for (auto _ : state) { + bundleTracker.Open(); + + auto start = std::chrono::high_resolution_clock::now(); + bundleTracker.Close(); + auto end = std::chrono::high_resolution_clock::now(); + + auto elapsed_time = + std::chrono::duration_cast>(end - start); + state.SetIterationTime(elapsed_time.count()); + } +} + +BENCHMARK_DEFINE_F(BundleTrackerFixture, StartBundle) +(benchmark::State& state) +{ + auto context = framework->GetBundleContext(); + Bundle bundle = testing::InstallLib(context, "TestBundleA"); + + for (auto _ : state) { + auto start = std::chrono::high_resolution_clock::now(); + bundle.Start(); + auto end = std::chrono::high_resolution_clock::now(); + auto elapsed_time = + std::chrono::duration_cast>(end - start); + state.SetIterationTime(elapsed_time.count()); + + bundle.Stop(); + } +} + +BENCHMARK_DEFINE_F(BundleTrackerFixture, BundleTrackerScalability) +(benchmark::State& state) +{ + auto context = framework->GetBundleContext(); + auto stateMask = + BundleTracker<>::CreateStateMask(Bundle::State::STATE_ACTIVE); + + // Create and open BundleTrackers + auto maxBundleTrackers{ state.range(0) }; + std::vector>> trackers; + for (auto i = 0; i < maxBundleTrackers; ++i) { + auto bundleTracker = std::make_unique>(context, stateMask); + bundleTracker->Open(); + trackers.emplace_back(std::move(bundleTracker)); + } + + Bundle bundle = testing::InstallLib(context, "TestBundleA"); + for (auto _ : state) { + // Measure performance of starting a bundle, + // where each BundleTracker issues an AddingBundle callback + auto start = std::chrono::high_resolution_clock::now(); + bundle.Start(); + auto end = std::chrono::high_resolution_clock::now(); + + auto elapsed_time = + std::chrono::duration_cast>(end - start); + state.SetIterationTime(elapsed_time.count()); + + bundle.Stop(); + } + + for (auto& tracker : trackers) { + tracker->Close(); + } +} + +#ifdef PERFORM_LARGE_BUNDLETRACKER_TEST +BENCHMARK_DEFINE_F(BundleTrackerFixture, BundleTrackerBundleScalability) +(benchmark::State& state) +{ + std::string bundleBasePath = "bundles\\bundle_"; + + // Generate paths to each bundle + uint32_t count = 1; + std::vector bundleStrs(state.range(0), bundleBasePath); + std::transform(bundleStrs.begin(), + bundleStrs.end(), + bundleStrs.begin(), + [&count](std::string& s) -> std::string { + return s.append(std::to_string(count++)); + }); + + // Install all bundles + std::vector bundles; + for (auto bundleStr : bundleStrs) { + bundles.emplace_back(testing::InstallLib(context, bundleStr)); + } + + auto context = framework->GetBundleContext(); + auto stateMask = + BundleTracker<>::CreateStateMask(Bundle::State::STATE_ACTIVE, + Bundle::State::STATE_RESOLVED, + Bundle::State::STATE_STARTING); + auto bundleTracker = BundleTracker<>(context, stateMask); + bundleTracker.Open(); + + // Measure performance of many lifecycle changes with a BundleTracker open + for (auto _ : state) { + for (auto bundle : bundles) { + bundle.Start(); + } + for (auto bundle : bundles) { + bundle.Stop(); + } + } + bundleTracker.Close(); +} +#endif + +BENCHMARK_REGISTER_F(BundleTrackerFixture, CreateBundleTracker) + ->Range(1, 10000); +BENCHMARK_REGISTER_F(BundleTrackerFixture, OpenBundleTracker)->UseManualTime(); +BENCHMARK_REGISTER_F(BundleTrackerFixture, BundleTrackerGetObject) + ->UseManualTime(); +BENCHMARK_REGISTER_F(BundleTrackerFixture, BundleTrackerRemoveMethod) + ->UseManualTime(); +BENCHMARK_REGISTER_F(BundleTrackerFixture, CloseBundleTracker)->UseManualTime(); +BENCHMARK_REGISTER_F(BundleTrackerFixture, CloseBundleTrackerWithListeners) + ->RangeMultiplier(128) + ->Range(1, 500000) + ->UseManualTime(); +BENCHMARK_REGISTER_F(BundleTrackerFixture, StartBundle)->UseManualTime(); +BENCHMARK_REGISTER_F(BundleTrackerFixture, BundleTrackerScalability) + ->RangeMultiplier(4) + ->Range(0, 1000) + ->UseManualTime(); + +#ifdef PERFORM_LARGE_BUNDLETRACKER_TEST +BENCHMARK_REGISTER_F(BundleTrackerFixture, BundleTrackerBundleScalability) + ->RangeMultiplier(2) + ->Range(1, 8000); +#endif diff --git a/framework/test/bench/CMakeLists.txt b/framework/test/bench/CMakeLists.txt index e93590ea9..e04adea18 100755 --- a/framework/test/bench/CMakeLists.txt +++ b/framework/test/bench/CMakeLists.txt @@ -20,6 +20,7 @@ set(_bench_src ldapfilter.cpp ldappropexpr.cpp servicequery.cpp + BundleTrackerTest.cpp ) set(_additional_srcs diff --git a/framework/test/bench/ServiceRegistryTest.cpp b/framework/test/bench/ServiceRegistryTest.cpp index 1c059a622..bbb6b7b1c 100644 --- a/framework/test/bench/ServiceRegistryTest.cpp +++ b/framework/test/bench/ServiceRegistryTest.cpp @@ -86,8 +86,9 @@ BENCHMARK_DEFINE_F(ServiceRegistryFixture, RegisterServices) { InterfaceMapPtr iMapCopy(std::make_shared(*interfaceMap)); auto start = high_resolution_clock::now(); - (void)fc.RegisterService(iMapCopy); // benchmark the call to RegisterService + auto reg = fc.RegisterService(iMapCopy); // benchmark the call to RegisterService auto end = high_resolution_clock::now(); + US_UNUSED(reg); auto elapsed_seconds = duration_cast>(end - start); state.SetIterationTime(elapsed_seconds.count()); } @@ -118,12 +119,13 @@ BENCHMARK_DEFINE_F(ServiceRegistryFixture, RegisterServicesWithRank) { InterfaceMapPtr iMapCopy(std::make_shared(*interfaceMap)); auto start = std::chrono::high_resolution_clock::now(); - (void)fc.RegisterService(iMapCopy, - { - {Constants::SERVICE_RANKING, - Any(static_cast(i))} + auto reg = fc.RegisterService(iMapCopy, + { + {Constants::SERVICE_RANKING, + Any(static_cast(i))} }); // benchmark the call to RegisterService auto end = std::chrono::high_resolution_clock::now(); + US_UNUSED(reg); auto elapsed_seconds = std::chrono::duration_cast>(end - start); state.SetIterationTime(elapsed_seconds.count()); } @@ -147,11 +149,12 @@ BENCHMARK_DEFINE_F(ServiceRegistryFixture, FindServices) auto regCount = state.range(0); auto interfaceCount = state.range(1); auto interfaceMap = MakeInterfaceMapWithNInterfaces(interfaceCount); + std::vector regs; for (auto i = regCount; i > 0; --i) { InterfaceMapPtr iMapCopy(std::make_shared(*interfaceMap)); - fc.RegisterService(iMapCopy); + regs.emplace_back(fc.RegisterService(iMapCopy)); } for (auto _ : state) diff --git a/framework/test/bundles/libC1/TestBundleC1.cpp b/framework/test/bundles/libC1/TestBundleC1.cpp index ef5d4ab08..7892fc37d 100644 --- a/framework/test/bundles/libC1/TestBundleC1.cpp +++ b/framework/test/bundles/libC1/TestBundleC1.cpp @@ -85,7 +85,9 @@ namespace cppmicroservices tracker->Close(); if (count != 0) + { throw std::logic_error("Not unregistered all services"); + } } void @@ -104,7 +106,9 @@ namespace cppmicroservices regs.Lock(), regs.v.emplace_back(std::move(reg)); count++; if (i % 5 == 0) + { regs.NotifyAll(); + } } } @@ -115,12 +119,6 @@ namespace cppmicroservices { std::vector currRegs; regs.Lock(), std::swap(currRegs, regs.v); - if (currRegs.empty()) - { - auto l = regs.Lock(); - regs.WaitFor(l, std::chrono::milliseconds(100), [this] { return !regs.v.empty(); }); - std::swap(currRegs, regs.v); - } for (auto& reg : currRegs) { @@ -138,6 +136,13 @@ namespace cppmicroservices count--; } } + /* + Rather than a condition variable, we are looping until regs is non-empty. + This sleep stops this function from looping too quickly and causing slow downs. + While this is less efficent than a condition variable, we are doing this in order to eliminate a + false-positive TSAN warning coming from the function wait_for. + */ + std::this_thread::sleep_for(std::chrono::milliseconds(100)); } } @@ -164,7 +169,9 @@ namespace cppmicroservices ModifiedService(ServiceReferenceU const& reference, InterfaceMapConstPtr const& /*service*/) { if (reference.GetProperty("i") != 5) + { throw std::logic_error("modified end match: wrong property"); + } context.GetService(reference); } @@ -173,7 +180,9 @@ namespace cppmicroservices { auto l = additionalReg.Lock(); if (additionalReg.v) + { additionalReg.v.Unregister(); + } } private: @@ -200,4 +209,4 @@ namespace cppmicroservices } // namespace cppmicroservices -CPPMICROSERVICES_EXPORT_BUNDLE_ACTIVATOR(cppmicroservices::TestBundleC1Activator) +CPPMICROSERVICES_EXPORT_BUNDLE_ACTIVATOR(cppmicroservices::TestBundleC1Activator) \ No newline at end of file diff --git a/framework/test/gtest/AnyMapTest.cpp b/framework/test/gtest/AnyMapTest.cpp index 55f38b43f..2941b08d9 100644 --- a/framework/test/gtest/AnyMapTest.cpp +++ b/framework/test/gtest/AnyMapTest.cpp @@ -39,31 +39,30 @@ TEST(AnyMapTest, CheckExceptions) TEST(AnyMapTest, AtCompoundKey) { // Testing nested vector compound access - AnyMap uo(AnyMap::UNORDERED_MAP); std::vector child { Any(1), Any(2) }; - std::vector parent { Any(child) }; - uo["hi"] = parent; + AnyMap uo { AnyMap::UNORDERED_MAP, { { "hi", std::vector { Any { std::vector { 1, 2 } } } } } }; ASSERT_EQ(uo.AtCompoundKey("hi.0.0"), 1); } TEST(AnyMapTest, IteratorTest) { - AnyMap o(AnyMap::ORDERED_MAP); - o["a"] = 1; - o["b"] = 2; - o["c"] = 3; + AnyMap o = { + any_map::ORDERED_MAP, + {{ "a", 1 }, { "b", 2 }, { "c", 3 }} + }; AnyMap::const_iter ociter(o.begin()); AnyMap::const_iter ociter1(o.cbegin()); - AnyMap uo(AnyMap::UNORDERED_MAP); - uo["1"] = 1; - uo["2"] = 2; + AnyMap uo = { + AnyMap::UNORDERED_MAP, + {{ "1", 1 }, { "2", 2 }} + }; AnyMap::const_iter uociter(uo.begin()); AnyMap::const_iter uociter1(uo.cbegin()); - AnyMap uoci(AnyMap::UNORDERED_MAP_CASEINSENSITIVE_KEYS); - uoci["do"] = 1; - uoci["re"] = 2; + AnyMap uoci { + {{ "do", 1 }, { "re", 2 }} + }; AnyMap::const_iter uoccciiter(uoci.begin()); AnyMap::const_iter uoccciiter1(uoci.cbegin()); @@ -136,15 +135,16 @@ TEST(AnyMapTest, IteratorTest) TEST(AnyMapTest, AnyMap) { - AnyMap::ordered_any_map o; - o["do"] = Any(1); - o["re"] = Any(2); + AnyMap::ordered_any_map o { + {{ "do", 1 }, { "re", 2 }} + }; AnyMap o_anymap(o); AnyMap o_anymap_copy(o_anymap); - AnyMap uo_anymap(AnyMap::UNORDERED_MAP); - uo_anymap["do"] = 1; - uo_anymap["re"] = 2; + AnyMap uo_anymap = { + AnyMap::UNORDERED_MAP, + {{ "do", 1 }, { "re", 2 }} + }; AnyMap::unordered_any_cimap uco; AnyMap uco_anymap(uco); @@ -200,9 +200,11 @@ TEST(AnyMapTest, AnyMap) TEST(AnyMapTest, MoveConstructor) { testing::FLAGS_gtest_death_test_style = "threadsafe"; - AnyMap o(AnyMap::ORDERED_MAP); - o["do"] = Any(1); - o["re"] = Any(2); + AnyMap o = { + AnyMap::ORDERED_MAP, + {{ "do", 1 }, { "re", 2 }} + }; + AnyMap o_anymap_move_ctor(std::move(o)); ASSERT_EQ(any_cast(o_anymap_move_ctor.at("do")), 1); ASSERT_DEATH({ o.size(); }, ".*") << "This call should result in a crash because " @@ -220,9 +222,11 @@ TEST(AnyMapTest, MoveConstructor) TEST(AnyMapTest, MoveAssignment) { testing::FLAGS_gtest_death_test_style = "threadsafe"; - AnyMap o(AnyMap::ORDERED_MAP); - o["do"] = Any(1); - o["re"] = Any(2); + AnyMap o { + AnyMap::ORDERED_MAP, + {{ "do", 1 }, { "re", 2 }} + }; + AnyMap uo_anymap_move_assign(AnyMap::UNORDERED_MAP); uo_anymap_move_assign = std::move(o); ASSERT_EQ(any_cast(uo_anymap_move_assign.at("re")), 2); @@ -370,19 +374,17 @@ manifest_from_cache(cppmicroservices::any_map::key_type const& key, cppmicroserv TEST(AnyMapTest, ManifestFromCache) { - AnyMap cache(AnyMap::UNORDERED_MAP_CASEINSENSITIVE_KEYS); - AnyMap cache_bundles(AnyMap::UNORDERED_MAP_CASEINSENSITIVE_KEYS); - AnyMap cache_bundle1(AnyMap::UNORDERED_MAP_CASEINSENSITIVE_KEYS); - AnyMap cache_bundle2(AnyMap::UNORDERED_MAP_CASEINSENSITIVE_KEYS); - - cache_bundle1["a"] = std::string("A"); - cache_bundle1["b"] = std::string("B"); - cache_bundle1["c"] = std::string("C"); - auto cache_bundle1_copy = cache_bundle1; + AnyMap cache {}; + AnyMap cache_bundles {}; + AnyMap cache_bundle1 = { + {{ "a", std::string("A") }, { "b", std::string("B") }, { "c", std::string("C") }} + }; - cache_bundle2["d"] = std::string("D"); - cache_bundle2["e"] = std::string("E"); - cache_bundle2["f"] = std::string("F"); + AnyMap cache_bundle2 = { + {{ "d", std::string("D") }, { "e", std::string("E") }, { "f", std::string("F") }} + }; + + auto cache_bundle1_copy = cache_bundle1; auto cache_bundle2_copy = cache_bundle2; cache_bundles.emplace(std::string("bundle1"), std::move(cache_bundle1)); @@ -403,3 +405,41 @@ TEST(AnyMapTest, ManifestFromCache) EXPECT_EQ(cache_bundle2_copy, bundle2); EXPECT_EQ(0, bundles.size()); } + +TEST(AnyMapTest, InitializerList) +{ + AnyMap noType { + {{ "a", 1 }, { "b", 2 }} + }; + EXPECT_EQ(any_map::UNORDERED_MAP_CASEINSENSITIVE_KEYS, noType.GetType()); + EXPECT_EQ(2, noType.size()); + EXPECT_EQ(Any(1), noType.at("a")); + EXPECT_EQ(Any(2), noType.at("b")); + + AnyMap ordered_any_map { + any_map::ORDERED_MAP, + {{ "c", 3 }, { "d", 4 }} + }; + EXPECT_EQ(any_map::ORDERED_MAP, ordered_any_map.GetType()); + EXPECT_EQ(2, ordered_any_map.size()); + EXPECT_EQ(Any(3), ordered_any_map.at("c")); + EXPECT_EQ(Any(4), ordered_any_map.at("d")); + + AnyMap unordered_any_map { + any_map::UNORDERED_MAP, + {{ "e", 5 }, { "f", 6 }} + }; + EXPECT_EQ(any_map::UNORDERED_MAP, unordered_any_map.GetType()); + EXPECT_EQ(2, unordered_any_map.size()); + EXPECT_EQ(Any(5), unordered_any_map.at("e")); + EXPECT_EQ(Any(6), unordered_any_map.at("f")); + + AnyMap unordered_any_cimap { + any_map::UNORDERED_MAP_CASEINSENSITIVE_KEYS, + {{ "g", 7 }, { "h", 8 }} + }; + EXPECT_EQ(any_map::UNORDERED_MAP_CASEINSENSITIVE_KEYS, unordered_any_cimap.GetType()); + EXPECT_EQ(2, unordered_any_cimap.size()); + EXPECT_EQ(Any(7), unordered_any_cimap.at("g")); + EXPECT_EQ(Any(8), unordered_any_cimap.at("h")); +} diff --git a/framework/test/gtest/BundleContextTest.cpp b/framework/test/gtest/BundleContextTest.cpp index 7a827b4de..46c81ce1e 100644 --- a/framework/test/gtest/BundleContextTest.cpp +++ b/framework/test/gtest/BundleContextTest.cpp @@ -61,15 +61,54 @@ namespace cppmicroservices }; } // namespace cppmicroservices -TEST(BundleContextTest, BundleContextThrowWhenInvalid) +class BundleContextTest : public ::testing::Test { - cppmicroservices::Framework framework = cppmicroservices::FrameworkFactory().NewFramework(); - ASSERT_TRUE(framework) << "The framework was not created successfully."; - framework.Start(); + protected: + BundleContextTest() : framework(cppmicroservices::FrameworkFactory().NewFramework()) {} - auto context = framework.GetBundleContext(); - ASSERT_TRUE(context) << "The bundle context is not valid."; + void + SetUp() override + { + ASSERT_TRUE(framework) << "The framework was not created successfully."; + framework.Start(); + context = framework.GetBundleContext(); + ASSERT_TRUE(context) << "The bundle context is not valid."; + } + + public: + cppmicroservices::Framework framework; + cppmicroservices::BundleContext context; +}; + +using BundleContParamType = std::pair>, std::vector>; +class BundleContextTestParam : public ::testing::TestWithParam +{ + public: + BundleContextTestParam() : framework(cppmicroservices::FrameworkFactory().NewFramework()) {} + void + SetUp() override + { + ASSERT_TRUE(framework) << "The framework was not created successfully."; + framework.Start(); + context = framework.GetBundleContext(); + ASSERT_TRUE(context) << "The bundle context is not valid."; + } + + void + TearDown() override + { + framework.Stop(); + framework.WaitForStop(std::chrono::milliseconds::zero()); + } + + public: + cppmicroservices::Framework framework; + cppmicroservices::BundleContext context; +}; + +TEST_F(BundleContextTest, BundleContextThrowWhenInvalid) +{ // Register a service and get a service reference for testing // GetService() later. (void)context.RegisterService(std::make_shared()); @@ -107,7 +146,7 @@ TEST(BundleContextTest, BundleContextThrowWhenInvalid) << "GetServiceReference(string) on invalid BundleContext did not throw."; EXPECT_THROW({ (void)context2.GetServiceReference(); }, std::runtime_error) << "GetServiceReference() on invalid BundleContext did not throw."; - EXPECT_THROW({ (void)context2.GetService(sRef); }, std::runtime_error) + EXPECT_THROW({ (void)context2.GetService(sRef); }, std::invalid_argument) << "GetService() on invalid BundleContext did not throw."; EXPECT_THROW({ (void)context2.GetServiceObjects(sRef); }, std::runtime_error) << "GetServiceObjects() on invalid BundleContext did not throw."; @@ -128,17 +167,10 @@ TEST(BundleContextTest, BundleContextThrowWhenInvalid) } #if defined(US_ENABLE_THREADING_SUPPORT) -TEST(BundleContextTest, NoSegfaultWithRegisterServiceShutdownRace) +TEST_F(BundleContextTest, NoSegfaultWithRegisterServiceShutdownRace) { - cppmicroservices::Framework framework = cppmicroservices::FrameworkFactory().NewFramework(); - ASSERT_TRUE(framework) << "The framework was not created successfully."; - framework.Start(); - - auto context = framework.GetBundleContext(); - ASSERT_TRUE(context) << "The bundle context is not valid."; - std::thread thread( - [&framework]() + [&framework = framework]() { framework.Stop(); framework.WaitForStop(std::chrono::milliseconds::zero()); @@ -158,12 +190,8 @@ TEST(BundleContextTest, NoSegfaultWithRegisterServiceShutdownRace) thread.join(); } -TEST(BundleContextTest, NoSegfaultWithServiceFactory) +TEST_F(BundleContextTest, NoSegfaultWithServiceFactory) { - cppmicroservices::Framework framework = FrameworkFactory().NewFramework(); - framework.Start(); - auto context = framework.GetBundleContext(); - InstallLib(context, "TestBundleH").Start(); std::string getServiceThrowsFilter(LDAPProp("getservice_exception") == true); @@ -173,14 +201,81 @@ TEST(BundleContextTest, NoSegfaultWithServiceFactory) ASSERT_EQ("1", svcGetServiceThrowsRefs[0].GetProperty(std::string("getservice_exception")).ToString()); std::thread thread( - [&framework]() + [&framework = framework]() { framework.Stop(); framework.WaitForStop(std::chrono::milliseconds::zero()); }); - ASSERT_EQ(nullptr, context.GetService(svcGetServiceThrowsRefs[0])); - + // Have to wrap call in a try catch. Without one, if the program throws the main thread will exit without calling + // .join() and when the spawned thread is destructed, it will call terminate because it was not joined. + try + { + // Framework may throw as the registration is no longer valid (depending on thread ordering) but we care that no + // thread segfaults + context.GetService(svcGetServiceThrowsRefs[0]); + } + catch (...) + { + } thread.join(); } + #endif + +template +bool +verifyOrdering(std::vector> const& refs) +{ + if (refs.size() > 1) + { + for (size_t i = 1; i < refs.size(); ++i) + { + if ((any_cast(refs[i].GetProperty(Constants::SERVICE_RANKING)) + >= any_cast(refs[i - 1].GetProperty(Constants::SERVICE_RANKING)))) + { + return false; + } + } + } + return true; +} + +INSTANTIATE_TEST_SUITE_P(BundleContextTestParameterized, + BundleContextTestParam, + ::testing::Values( + BundleContParamType { + { { "", 5 } }, + { { { "service.ranking", 2 } }, + { { "service.ranking", 0 } }, + { { "service.ranking", 4 } }, + { { "service.ranking", 1 } }, + { { "service.ranking", 3 } } } +}, + BundleContParamType { + { { "", 5 }, { "(Key1=Val*)", 3 }, { "(Key2=Val*)", 2 } }, + { { { "service.ranking", 2000 }, { "Key1", std::string("Val1") } }, + { { "service.ranking", 15 }, { "Key1", std::string("Val2") } }, + { { "service.ranking", 0 }, { "Key1", std::string("Val2") } }, + { { "service.ranking", 1506 }, { "Key2", std::string("Val1") } }, + { { "service.ranking", 905 }, { "Key2", std::string("Val1") } } } })); + +TEST_P(BundleContextTestParam, TestGetServiceReferenceOrdering) +{ + auto params = GetParam(); + auto props = params.second; + for (auto const& prop : props) + { + (void)context.RegisterService(std::make_shared(), prop); + } + + auto filterPairs = params.first; + for (auto const& pair : filterPairs) + { + auto filt = pair.first; + auto refs = context.GetServiceReferences(filt); + + ASSERT_TRUE(verifyOrdering(refs)); + ASSERT_TRUE(refs.size() == pair.second); + } +} \ No newline at end of file diff --git a/framework/test/gtest/BundleTrackerConcurrencyTest.cpp b/framework/test/gtest/BundleTrackerConcurrencyTest.cpp new file mode 100644 index 000000000..bc0c820dc --- /dev/null +++ b/framework/test/gtest/BundleTrackerConcurrencyTest.cpp @@ -0,0 +1,211 @@ +/*============================================================================= + + Library: CppMicroServices + + Copyright (c) The CppMicroServices developers. See the COPYRIGHT + file at the top-level directory of this distribution and at + https://github.com/CppMicroServices/CppMicroServices/COPYRIGHT . + + 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. + + =============================================================================*/ +#include "cppmicroservices/Bundle.h" +#include "cppmicroservices/BundleTracker.h" +#include "cppmicroservices/Framework.h" +#include "cppmicroservices/FrameworkEvent.h" +#include "cppmicroservices/FrameworkFactory.h" + +#include + +#include "TestUtils.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +using namespace cppmicroservices; + +class BundleTrackerConcurrencyTest : public ::testing::Test +{ + protected: + Framework framework; + BundleContext context; + static constexpr BundleTracker<>::BundleStateMaskType all_states + = BundleTracker<>::CreateStateMask(Bundle::State::STATE_ACTIVE, + Bundle::State::STATE_INSTALLED, + Bundle::State::STATE_RESOLVED, + Bundle::State::STATE_STARTING, + Bundle::State::STATE_STOPPING, + Bundle::State::STATE_UNINSTALLED); + + public: + BundleTrackerConcurrencyTest() : framework(FrameworkFactory().NewFramework()) {}; + + ~BundleTrackerConcurrencyTest() override = default; + + void + SetUp() override + { + framework.Start(); + context = framework.GetBundleContext(); + } + + void + TearDown() override + { + framework.Stop(); + framework.WaitForStop(std::chrono::milliseconds::zero()); + } +}; + +MATCHER_P(HasName, name, "") { return arg.GetSymbolicName() == name; } + +#ifdef US_ENABLE_THREADING_SUPPORT +class MockCustomizer2 : public BundleTrackerCustomizer<> +{ + public: + MOCK_METHOD(std::optional, AddingBundle, (Bundle const&, BundleEvent const&), (override)); + MOCK_METHOD(void, ModifiedBundle, (Bundle const&, BundleEvent const&, Bundle const&), (override)); + MOCK_METHOD(void, RemovedBundle, (Bundle const&, BundleEvent const&, Bundle const&), (override)); +}; + +TEST_F(BundleTrackerConcurrencyTest, TestConcurrentOpenClose) +{ + auto bundleTracker = std::make_unique>(context, all_states); + + size_t numThreads = std::thread::hardware_concurrency(); + ASSERT_GT(numThreads, 0ull) << "number of threads is 0"; + std::vector> futures; + std::promise gate; + auto gateFuture = gate.get_future().share(); + for (size_t i = 0; i <= numThreads; ++i) + { + futures.push_back(std::async(std::launch::async, + [i, &bundleTracker, &gateFuture]() + { + gateFuture.get(); + for (int n = 0; n < 1000; ++n) + { + if (i % 2 == 0) + { + bundleTracker->Open(); + } + else + { + bundleTracker->Close(); + } + } + })); + } + + gate.set_value(); + + for (auto& asyncFuture : futures) + { + asyncFuture.get(); + } +} + +TEST_F(BundleTrackerConcurrencyTest, TestOpeningTrackerWhileBundlesChange) +{ + auto customizer = std::make_shared(); + auto stateMask = BundleTracker<>::CreateStateMask(Bundle::State::STATE_ACTIVE); + auto bundleTracker = std::make_unique>(context, stateMask, customizer); + + size_t numThreads = std::thread::hardware_concurrency(); + ASSERT_GT(numThreads, 0ull) << "number of threads is 0"; + std::vector> futures; + std::promise gate; + auto gateFuture = gate.get_future().share(); + + auto openTrackerFuture = std::async(std::launch::async, + [&bundleTracker, &gateFuture]() + { + gateFuture.get(); + bundleTracker->Open(); + }); + + auto startBundlesFuture + = std::async(std::launch::async, + [this, &gateFuture]() + { + gateFuture.get(); + cppmicroservices::testing::InstallLib(framework.GetBundleContext(), "TestBundleA").Start(); + cppmicroservices::testing::InstallLib(framework.GetBundleContext(), "TestBundleA2").Start(); + cppmicroservices::testing::InstallLib(framework.GetBundleContext(), "TestBundleB").Start(); + cppmicroservices::testing::InstallLib(framework.GetBundleContext(), "TestBundleH").Start(); + cppmicroservices::testing::InstallLib(framework.GetBundleContext(), "TestBundleM").Start(); + cppmicroservices::testing::InstallLib(framework.GetBundleContext(), "TestBundleR").Start(); + }); + + EXPECT_CALL(*customizer, AddingBundle).Times(7); + gate.set_value(); + + openTrackerFuture.get(); + startBundlesFuture.get(); + + bundleTracker->Close(); +} + +TEST_F(BundleTrackerConcurrencyTest, TestNoRaceConditionForRemovingChangingBundle) +{ + auto customizer = std::make_shared(); + auto bundleTracker = std::make_unique>(context, all_states, customizer); + Bundle bundleA = cppmicroservices::testing::InstallLib(framework.GetBundleContext(), "TestBundleA"); + // make AddingBundle return nullopt for all bundles besides bundleA + EXPECT_CALL(*customizer, AddingBundle).WillRepeatedly(::testing::Return(std::nullopt)); + EXPECT_CALL(*customizer, AddingBundle(HasName("TestBundleA"), ::testing::_)).WillOnce(::testing::ReturnArg<0>()); + bundleTracker->Open(); + + size_t numThreads = std::thread::hardware_concurrency(); + ASSERT_GT(numThreads, 0ull) << "number of threads is 0"; + std::promise gate; + auto gateFuture = gate.get_future().share(); + auto removeBundleFuture = std::async(std::launch::async, + [&gateFuture, &bundleTracker, &bundleA]() + { + gateFuture.get(); + bundleTracker->Remove(bundleA); + }); + auto startBundleFuture = std::async(std::launch::async, + [&gateFuture, &bundleA]() + { + gateFuture.get(); + bundleA.Start(); + }); + + // The Remove can occur while bundleA is in any state while being started, + // so all 4 of the following series of callbacks are possible: + // Remove -> Add -> Modify -> Modify + // Modify -> Remove -> Add -> Modify + // Modify -> Modify -> Remove -> Add + // Modify -> Modify -> Modify -> Remove + + int addModifyCounter = 0; // counts the number of AddingBundle and ModifiedBundle callbacks + auto increment = [&addModifyCounter]() { addModifyCounter++; }; + + EXPECT_CALL(*customizer, AddingBundle) + .Times(::testing::AtMost(1)) + .WillOnce(::testing::DoAll(::testing::Invoke(increment), ::testing::ReturnArg<0>())); + EXPECT_CALL(*customizer, ModifiedBundle).Times(::testing::AtLeast(2)).WillRepeatedly(::testing::Invoke(increment)); + EXPECT_CALL(*customizer, RemovedBundle).Times(1); + gate.set_value(); + + removeBundleFuture.get(); + startBundleFuture.get(); + + EXPECT_EQ(addModifyCounter, 3) << "The wrong number of callbacks were issued"; + + // What happens after is out of scope for this test + EXPECT_CALL(*customizer, RemovedBundle).Times(::testing::AnyNumber()); + bundleTracker->Close(); +} +#endif diff --git a/framework/test/gtest/BundleTrackerCustomCallbackTest.cpp b/framework/test/gtest/BundleTrackerCustomCallbackTest.cpp new file mode 100644 index 000000000..190011c8b --- /dev/null +++ b/framework/test/gtest/BundleTrackerCustomCallbackTest.cpp @@ -0,0 +1,684 @@ +/*============================================================================= + + Library: CppMicroServices + + Copyright (c) The CppMicroServices developers. See the COPYRIGHT + file at the top-level directory of this distribution and at + https://github.com/CppMicroServices/CppMicroServices/COPYRIGHT . + + 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. + + =============================================================================*/ +#include "cppmicroservices/Bundle.h" +#include "cppmicroservices/BundleTracker.h" +#include "cppmicroservices/Framework.h" +#include "cppmicroservices/FrameworkEvent.h" +#include "cppmicroservices/FrameworkFactory.h" + +#include "TestUtils.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +#ifdef GetObject +# undef GetObject +#endif + +using namespace cppmicroservices; + +class BundleTrackerCustomCallbackTest : public ::testing::Test +{ +protected: + Framework framework; + BundleContext context; + static constexpr BundleTracker<>::BundleStateMaskType all_states = + BundleTracker<>::CreateStateMask(Bundle::State::STATE_ACTIVE, + Bundle::State::STATE_INSTALLED, + Bundle::State::STATE_RESOLVED, + Bundle::State::STATE_STARTING, + Bundle::State::STATE_STOPPING, + Bundle::State::STATE_UNINSTALLED); + +public: + BundleTrackerCustomCallbackTest() + : framework(FrameworkFactory().NewFramework()){}; + + ~BundleTrackerCustomCallbackTest() override = default; + + void SetUp() override + { + framework.Start(); + context = framework.GetBundleContext(); + } + + void TearDown() override + { + framework.Stop(); + framework.WaitForStop(std::chrono::milliseconds::zero()); + } +}; + +MATCHER_P(HasName, name, "") +{ + return arg.GetSymbolicName() == name; +} + +// +// Test custom implementation through a +// BundleTrackerCustomizer subclass tracking bundles +// +class MockCustomizer : public BundleTrackerCustomizer<> +{ +public: + MOCK_METHOD(std::optional, + AddingBundle, + (const Bundle&, const BundleEvent&), + (override)); + MOCK_METHOD(void, + ModifiedBundle, + (const Bundle&, const BundleEvent&, const Bundle&), + (override)); + MOCK_METHOD(void, + RemovedBundle, + (const Bundle&, const BundleEvent&, const Bundle&), + (override)); +}; + +TEST_F(BundleTrackerCustomCallbackTest, + BundleIsTrackedWhenReturnedByAddingBundleFromCustomizer) +{ + // Given an open BundleTracker + auto stateMask = + BundleTracker<>::CreateStateMask(Bundle::State::STATE_ACTIVE); + auto customizer = std::make_shared(); + auto bundleTracker = BundleTracker<>(context, stateMask, customizer); + // where AddingBundle returns null for bundles we aren't testing (main, system_bundle) + EXPECT_CALL(*customizer, AddingBundle) + .WillRepeatedly(::testing::Return(std::nullopt)); + ASSERT_NO_THROW(bundleTracker.Open()) << "BundleTracker failed to start"; + + // When bundleA makes the following state transitions: + // ---> installed ---> resolved ---> starting ---> ACTIVE (Add) + // Then there should be 1 AddingBundle callback, and + // When it returns the bundle + EXPECT_CALL(*customizer, AddingBundle) + .Times(1) + .WillOnce(::testing::ReturnArg<0>()); // returns the bundle + EXPECT_CALL(*customizer, ModifiedBundle).Times(0); + EXPECT_CALL(*customizer, RemovedBundle).Times(0); + Bundle bundleA = cppmicroservices::testing::InstallLib( + framework.GetBundleContext(), "TestBundleA"); + bundleA.Start(); + + // Then the bundle should be the tracked + EXPECT_EQ(bundleA, bundleTracker.GetObject(bundleA)) + << "The bundle returned by a customizer's AddingBundle should be tracked"; + + // At this point, the callbacks after the bundle is added have been verified. + // We test a different expected behavior below, that RemovedBundle is called on + // the tracked bundle when the BundleTracker is closed. + EXPECT_CALL(*customizer, RemovedBundle).Times(1); + bundleTracker.Close(); +} + +TEST_F(BundleTrackerCustomCallbackTest, + BundleUntrackedIfAddingBundleFromCustomizerReturnsNull) +{ + auto customizer = std::make_shared(); + auto bundleTracker = BundleTracker<>(context, all_states, customizer); + + // When AddingBundle callbacks are issued and return null + EXPECT_CALL(*customizer, AddingBundle) + .Times(::testing::AtLeast(1)) + .WillRepeatedly(::testing::Return(std::nullopt)); + ASSERT_NO_THROW(bundleTracker.Open()) << "BundleTracker failed to start"; + Bundle ignoredBundle = cppmicroservices::testing::InstallLib( + framework.GetBundleContext(), "TestBundleM"); + + // Then no bundles should be tracked + EXPECT_EQ(0, bundleTracker.Size()) + << "No bundles should be tracked if AddingBundle always returns null"; + + bundleTracker.Close(); +} + +TEST_F(BundleTrackerCustomCallbackTest, + CallbacksWorkForCustomizerTrackingBundlesAfterBundleModified) +{ + auto customizer = std::make_shared(); + auto bundleTracker = BundleTracker<>(context, all_states, customizer); + EXPECT_CALL(*customizer, AddingBundle) + .WillRepeatedly(::testing::ReturnArg<0>()); + ASSERT_NO_THROW(bundleTracker.Open()) << "BundleTracker failed to start"; + Bundle bundle = cppmicroservices::testing::InstallLib( + framework.GetBundleContext(), "TestBundleA"); + + // bundle: INSTALLED ---> RESOLVED (Modify) + // ---> STARTING (Modify) + // ---> ACTIVE (Modify) + EXPECT_CALL(*customizer, AddingBundle).Times(0); + EXPECT_CALL(*customizer, ModifiedBundle).Times(3); + EXPECT_CALL(*customizer, RemovedBundle).Times(0); + bundle.Start(); + + // At this point, the callbacks after the bundle is modified have been verified. + // The BundleTracker must be closed, after which RemovedBundle is called for + // all tracked bundles. This behavior is verified in another test, so we do not + // test it here. Instead, we add an EXPECT_CALL with AnyNumber to retire the + // previous EXPECT_CALL with Times(0). + EXPECT_CALL(*customizer, RemovedBundle).Times(::testing::AnyNumber()); + bundleTracker.Close(); +} + +TEST_F(BundleTrackerCustomCallbackTest, + CallbacksWorkForCustomizerTrackingBundlesAfterBundleRemoved) +{ + auto customizer = std::make_shared(); + auto stateMask = + BundleTracker<>::CreateStateMask(Bundle::State::STATE_INSTALLED); + auto bundleTracker = BundleTracker<>(context, stateMask, customizer); + EXPECT_CALL(*customizer, AddingBundle) + .WillRepeatedly(::testing::ReturnArg<0>()); + ASSERT_NO_THROW(bundleTracker.Open()) << "BundleTracker failed to start"; + + Bundle bundle = cppmicroservices::testing::InstallLib( + framework.GetBundleContext(), "TestBundleA"); + + // bundle: INSTALLED ---> uninstalled (Remove) + EXPECT_CALL(*customizer, AddingBundle).Times(0); + EXPECT_CALL(*customizer, ModifiedBundle).Times(0); + EXPECT_CALL(*customizer, RemovedBundle).Times(1); + bundle.Uninstall(); + + // At this point, the callbacks after the bundle is removed have been verified. + // The BundleTracker must be closed, after which RemovedBundle is called for + // all tracked bundles. This behavior is verified in another test, so we do not + // test it here. Instead, we add an EXPECT_CALL with AnyNumber to retire the + // previous EXPECT_CALL with Times(1). + EXPECT_CALL(*customizer, RemovedBundle).Times(::testing::AnyNumber()); + bundleTracker.Close(); +} + +// +// Test custom implementation through a BundleTrackerCustomizer subclass tracking custom objects +// +class MockCustomizerWithObject : public BundleTrackerCustomizer +{ +public: + MOCK_METHOD(std::optional, + AddingBundle, + (const Bundle&, const BundleEvent&), + (override)); + MOCK_METHOD(void, + ModifiedBundle, + (const Bundle&, const BundleEvent&, const int&), + (override)); + MOCK_METHOD(void, + RemovedBundle, + (const Bundle&, const BundleEvent&, const int&), + (override)); +}; + +TEST_F(BundleTrackerCustomCallbackTest, + ObjectIsTrackedWhenReturnedByAddingBundleFromCustomizer) +{ + auto stateMask = + BundleTracker<>::CreateStateMask(Bundle::State::STATE_ACTIVE); + auto customizer = std::make_shared(); + auto bundleTracker = BundleTracker(context, stateMask, customizer); + + // Make AddingBundle return null for bundles we aren't testing (main, system_bundle) + EXPECT_CALL(*customizer, AddingBundle) + .WillRepeatedly(::testing::Return(std::nullopt)); + ASSERT_NO_THROW(bundleTracker.Open()) << "BundleTracker failed to start"; + + // When bundle makes the following state transitions: + // ---> installed ---> resolved ---> starting ---> ACTIVE (Add) + // Then there should be 1 AddingBundle callback, and + // When it returns the object 5 + EXPECT_CALL(*customizer, AddingBundle) + .Times(1) + .WillOnce(::testing::Return(5)); // returns the object 1 + EXPECT_CALL(*customizer, ModifiedBundle).Times(0); + EXPECT_CALL(*customizer, RemovedBundle).Times(0); + Bundle bundle = cppmicroservices::testing::InstallLib( + framework.GetBundleContext(), "TestBundleA"); + bundle.Start(); + + // Then the object 5 should be the tracked + EXPECT_EQ(5, bundleTracker.GetObject(bundle)) + << "The object returned by a customizer's AddingBundle should be tracked"; + + // At this point, the callbacks after the bundle is added have been verified. + // The BundleTracker must be closed, after which RemovedBundle is called for + // all tracked bundles. This behavior is verified in another test, so we do not + // test it here. Instead, we add an EXPECT_CALL with AnyNumber to retire the + // previous EXPECT_CALL with Times(0). + EXPECT_CALL(*customizer, RemovedBundle).Times(::testing::AnyNumber()); + bundleTracker.Close(); +} + +// +// Test custom implementation through +// method override in a BundleTracker subclass tracking bundles +// +class MockBundleTracker : public BundleTracker<> +{ +public: + MockBundleTracker(const BundleContext& context, BundleStateMaskType stateMask) + : BundleTracker(context, stateMask) + {} + + MOCK_METHOD(std::optional, + AddingBundle, + (const Bundle& bundle, const BundleEvent&), + (override)); + MOCK_METHOD(void, + ModifiedBundle, + (const Bundle&, const BundleEvent&, const Bundle&), + (override)); + MOCK_METHOD(void, + RemovedBundle, + (const Bundle&, const BundleEvent&, const Bundle&), + (override)); +}; + +TEST_F(BundleTrackerCustomCallbackTest, + BundleIsTrackedWhenReturnedByAddingBundleFromOverride) +{ + // Given an open BundleTracker + auto stateMask = + BundleTracker<>::CreateStateMask(Bundle::State::STATE_ACTIVE); + auto bundleTracker = MockBundleTracker(context, stateMask); + // where AddingBundle returns null for bundles we aren't testing (main, system_bundle) + EXPECT_CALL(bundleTracker, AddingBundle) + .WillRepeatedly(::testing::Return(std::nullopt)); + ASSERT_NO_THROW(bundleTracker.Open()) << "BundleTracker failed to start"; + + // When bundle makes the following state transitions: + // ---> installed ---> resolved ---> starting ---> ACTIVE (Add) + // Then there should be 1 AddingBundle callback, and + // When it returns the bundle + EXPECT_CALL(bundleTracker, AddingBundle) + .Times(1) + .WillOnce(::testing::ReturnArg<0>()); // returns the bundle + EXPECT_CALL(bundleTracker, ModifiedBundle).Times(0); + EXPECT_CALL(bundleTracker, RemovedBundle).Times(0); + Bundle bundle = cppmicroservices::testing::InstallLib( + framework.GetBundleContext(), "TestBundleA"); + bundle.Start(); + + // Then the bundle should be the tracked + EXPECT_EQ(bundle, bundleTracker.GetObject(bundle)) + << "The bundle returned by AddingBundle from a BundleTracker subclass " + "should be tracked"; + + // At this point, the callbacks after the bundle is added have been verified. + // The BundleTracker must be closed, after which RemovedBundle is called for + // all tracked bundles. This behavior is verified in another test, so we do not + // test it here. Instead, we add an EXPECT_CALL with AnyNumber to retire the + // previous EXPECT_CALL with Times(0). + EXPECT_CALL(bundleTracker, RemovedBundle).Times(::testing::AnyNumber()); + bundleTracker.Close(); +} + +TEST_F(BundleTrackerCustomCallbackTest, + BundleUntrackedIfAddingBundleFromOverrideReturnsNull) +{ + auto bundleTracker = MockBundleTracker(context, all_states); + + // When AddingBundle callbacks are issued and return null + EXPECT_CALL(bundleTracker, AddingBundle) + .WillRepeatedly(::testing::Return(std::nullopt)); + ASSERT_NO_THROW(bundleTracker.Open()) << "BundleTracker failed to start"; + Bundle ignoredBundle = cppmicroservices::testing::InstallLib( + framework.GetBundleContext(), "TestBundleM"); + + // Then no bundles should be tracked + EXPECT_EQ(0, bundleTracker.Size()) + << "No bundles should be tracked if AddingBundle always returns null"; + + bundleTracker.Close(); +} + +TEST_F(BundleTrackerCustomCallbackTest, + CallbacksWorkForOverridenTrackerTrackingBundlesAfterBundleModified) +{ + auto bundleTracker = MockBundleTracker(context, all_states); + EXPECT_CALL(bundleTracker, AddingBundle) + .WillRepeatedly(::testing::ReturnArg<0>()); + ASSERT_NO_THROW(bundleTracker.Open()) << "BundleTracker failed to start"; + Bundle bundle = cppmicroservices::testing::InstallLib( + framework.GetBundleContext(), "TestBundleA"); + + // bundle: INSTALLED ---> RESOLVED (Modify) + // ---> STARTING (Modify) + // ---> ACTIVE (Modify) + EXPECT_CALL(bundleTracker, AddingBundle).Times(0); + EXPECT_CALL(bundleTracker, ModifiedBundle).Times(3); + EXPECT_CALL(bundleTracker, RemovedBundle).Times(0); + bundle.Start(); + + // At this point, the callbacks after the bundle is modified have been verified. + // The BundleTracker must be closed, after which RemovedBundle is called for + // all tracked bundles. This behavior is verified in another test, so we do not + // test it here. Instead, we add an EXPECT_CALL with AnyNumber to retire the + // previous EXPECT_CALL with Times(0). + EXPECT_CALL(bundleTracker, RemovedBundle).Times(::testing::AnyNumber()); + bundleTracker.Close(); +} + +TEST_F(BundleTrackerCustomCallbackTest, + CallbacksWorkForOverridenTrackerTrackingBundlesAfterBundleRemoved) +{ + auto stateMask = + BundleTracker<>::CreateStateMask(Bundle::State::STATE_INSTALLED); + auto bundleTracker = MockBundleTracker(context, stateMask); + EXPECT_CALL(bundleTracker, AddingBundle) + .WillRepeatedly(::testing::ReturnArg<0>()); + ASSERT_NO_THROW(bundleTracker.Open()) << "BundleTracker failed to start"; + + Bundle bundle = cppmicroservices::testing::InstallLib( + framework.GetBundleContext(), "TestBundleA"); + + // bundle: INSTALLED ---> uninstalled (Remove) + EXPECT_CALL(bundleTracker, AddingBundle).Times(0); + EXPECT_CALL(bundleTracker, ModifiedBundle).Times(0); + EXPECT_CALL(bundleTracker, RemovedBundle).Times(1); + bundle.Uninstall(); + + // At this point, the callbacks after the bundle is removed have been verified. + // The BundleTracker must be closed, after which RemovedBundle is called for + // all tracked bundles. This behavior is verified in another test, so we do not + // test it here. Instead, we add an EXPECT_CALL with AnyNumber to retire the + // previous EXPECT_CALL with Times(1). + EXPECT_CALL(bundleTracker, RemovedBundle).Times(::testing::AnyNumber()); + bundleTracker.Close(); +} + +// +// Test custom implementation through +// method override in a BundleTracker subclass tracking custom objects +// +class MockBundleTrackerWithObject : public BundleTracker +{ +public: + MockBundleTrackerWithObject(const BundleContext& context, + BundleStateMaskType stateMask) + : BundleTracker(context, stateMask) + {} + + MOCK_METHOD(std::optional, + AddingBundle, + (const Bundle& bundle, const BundleEvent&), + (override)); + MOCK_METHOD(void, + ModifiedBundle, + (const Bundle&, const BundleEvent&, const int&), + (override)); + MOCK_METHOD(void, + RemovedBundle, + (const Bundle&, const BundleEvent&, const int&), + (override)); +}; + +TEST_F(BundleTrackerCustomCallbackTest, + ObjectIsTrackedWhenReturnedByAddingBundleFromOverride) +{ + // Given an open BundleTracker + auto stateMask = + BundleTracker<>::CreateStateMask(Bundle::State::STATE_ACTIVE); + auto bundleTracker = MockBundleTrackerWithObject(context, stateMask); + // where AddingBundle returns null for bundles we aren't testing (main, system_bundle) + EXPECT_CALL(bundleTracker, AddingBundle) + .WillRepeatedly(::testing::Return(std::nullopt)); + ASSERT_NO_THROW(bundleTracker.Open()) << "BundleTracker failed to start"; + + // When bundle makes the following state transitions: + // ---> installed ---> resolved ---> starting ---> ACTIVE (Add) + // Then there should be 1 AddingBundle callback, and + // When it returns the object 5 + EXPECT_CALL(bundleTracker, AddingBundle) + .Times(1) + .WillOnce(::testing::Return(5)); // returns the object 5 + EXPECT_CALL(bundleTracker, ModifiedBundle).Times(0); + EXPECT_CALL(bundleTracker, RemovedBundle).Times(0); + Bundle bundle = cppmicroservices::testing::InstallLib( + framework.GetBundleContext(), "TestBundleA"); + bundle.Start(); + + // Then the object 5 should be tracked + EXPECT_EQ(5, bundleTracker.GetObject(bundle)) + << "The object returned by AddingBundle from a BundleTracker subclass " + "should be tracked"; + + // At this point, the callbacks after the bundle is added have been verified. + // The BundleTracker must be closed, after which RemovedBundle is called for + // all tracked bundles. This behavior is verified in another test, so we do not + // test it here. Instead, we add an EXPECT_CALL with AnyNumber to retire the + // previous EXPECT_CALL with Times(0). + EXPECT_CALL(bundleTracker, RemovedBundle).Times(::testing::AnyNumber()); + bundleTracker.Close(); +} + +// +// Test functionality non-specific to customization method +// + +TEST_F(BundleTrackerCustomCallbackTest, NoCallbacksWhenClosed) +{ + auto customizer = std::make_shared(); + auto bundleTracker = BundleTracker<>(context, all_states, customizer); + + // When bundles change states before the BundleTracker is open, there should be no callbacks + EXPECT_CALL(*customizer, AddingBundle).Times(0); + EXPECT_CALL(*customizer, ModifiedBundle).Times(0); + EXPECT_CALL(*customizer, RemovedBundle).Times(0); + Bundle testBundle1 = cppmicroservices::testing::InstallLib( + framework.GetBundleContext(), "TestBundleM"); + testBundle1.Start(); + + // Given a series of callbacks while the BundleTracker is open + EXPECT_CALL(*customizer, AddingBundle) + .WillRepeatedly(::testing::ReturnArg<0>()); + EXPECT_CALL(*customizer, ModifiedBundle).Times(::testing::AnyNumber()); + EXPECT_CALL(*customizer, RemovedBundle).Times(::testing::AnyNumber()); + bundleTracker.Open(); + Bundle testBundle2 = cppmicroservices::testing::InstallLib( + framework.GetBundleContext(), "TestBundleA"); + bundleTracker.Close(); + + // When bundles change states after the BundleTracker is closed, there should be no callbacks + EXPECT_CALL(*customizer, AddingBundle).Times(0); + EXPECT_CALL(*customizer, ModifiedBundle).Times(0); + EXPECT_CALL(*customizer, RemovedBundle).Times(0); + testBundle1.Uninstall(); + testBundle2.Start(); + testBundle2.Uninstall(); + cppmicroservices::testing::InstallLib(framework.GetBundleContext(), + "TestBundleB"); +} + +TEST_F(BundleTrackerCustomCallbackTest, + CallbacksWorkForBundleFromBeforeTrackerOpened) +{ + // Given a bundle which enters a tracked state before the BundleTracker is opened + Bundle testBundle = cppmicroservices::testing::InstallLib( + framework.GetBundleContext(), "TestBundleA"); + testBundle.Start(); + auto customizer = std::make_shared(); + auto stateMask = + BundleTracker<>::CreateStateMask(Bundle::State::STATE_RESOLVED, + Bundle::State::STATE_STARTING, + Bundle::State::STATE_ACTIVE); + auto bundleTracker = BundleTracker<>(context, stateMask, customizer); + // and that we ignore callbacks to non-tested bundles + EXPECT_CALL(*customizer, AddingBundle).Times(::testing::AnyNumber()); + + // When the BundleTracker is opened, + // the only callback for the test bundle should be an AddingBundle + EXPECT_CALL(*customizer, AddingBundle(HasName("TestBundleA"), ::testing::_)) + .Times(1) + .WillOnce(::testing::ReturnArg<0>()); + EXPECT_CALL( + *customizer, + ModifiedBundle(HasName("TestBundleA"), ::testing::_, ::testing::_)) + .Times(0); + EXPECT_CALL(*customizer, + RemovedBundle(HasName("TestBundleA"), ::testing::_, ::testing::_)) + .Times(0); + ASSERT_NO_THROW(bundleTracker.Open()) << "BundleTracker failed to start"; + + // When the test bundle is stopped, we should observe + // testBundle : ACTIVE ---> stopping (Remove) ---> RESOLVED (Add) + EXPECT_CALL( + *customizer, + RemovedBundle(HasName("TestBundleA"), ::testing::_, ::testing::_)); + EXPECT_CALL(*customizer, AddingBundle(HasName("TestBundleA"), ::testing::_)) + .WillOnce(::testing::ReturnArg<0>()); + testBundle.Stop(); + + // When the BundleTracker is closed, there should be a RemovedBundle callback + EXPECT_CALL(*customizer, + RemovedBundle(HasName("TestBundleA"), ::testing::_, ::testing::_)) + .Times(1); + bundleTracker.Close(); +} + +void CreateOpenTracker(Framework framework, + BundleContext context, + std::shared_ptr customizer) +{ + auto stateMask = + BundleTracker<>::CreateStateMask(Bundle::State::STATE_ACTIVE); + auto bundleTracker = BundleTracker<>(context, stateMask, customizer); + EXPECT_CALL(*customizer, AddingBundle) + .WillRepeatedly(::testing::ReturnArg<0>()); + bundleTracker.Open(); + Bundle testBundle = cppmicroservices::testing::InstallLib( + framework.GetBundleContext(), "TestBundleA"); + testBundle.Start(); + // bundleTracker goes out of scope +} + +TEST_F(BundleTrackerCustomCallbackTest, + CloseIsCalledWhenOpenTrackerGoesOutOfScope) +{ + auto customizer = std::make_shared(); + + // When an open BundleTracker goes out of scope, + // Close() should be called, and therefore + // RemovedBundle should be called on tracked bundles + EXPECT_CALL(*customizer, RemovedBundle).Times(::testing::AtLeast(1)); + CreateOpenTracker(framework, context, customizer); +} + +TEST_F(BundleTrackerCustomCallbackTest, ErrorInAddingBundlePropagates) +{ + auto customizer = std::make_shared(); + auto stateMask = + BundleTracker<>::CreateStateMask(Bundle::State::STATE_ACTIVE); + auto bundleTracker = BundleTracker<>(context, stateMask, customizer); + EXPECT_CALL(*customizer, AddingBundle) + .WillRepeatedly(::testing::Return(std::nullopt)); + bundleTracker.Open(); + bool receivedErrorEvent{ false }; + auto token = framework.GetBundleContext().AddFrameworkListener( + [&receivedErrorEvent](const FrameworkEvent& evt) { + if (evt.GetType() == FrameworkEvent::Type::FRAMEWORK_ERROR) { + receivedErrorEvent = true; + } + }); + + // When AddingBundle is called and throws an error + EXPECT_CALL(*customizer, AddingBundle) + .WillOnce(::testing::Throw(std::runtime_error("foo"))); + cppmicroservices::testing::InstallLib(framework.GetBundleContext(), + "TestBundleA") + .Start(); + + // Then the framework receives it + EXPECT_TRUE(receivedErrorEvent) + << "Framework did not receive error after AddingBundle throw"; + + EXPECT_CALL(*customizer, RemovedBundle).Times(::testing::AnyNumber()); + bundleTracker.Close(); + framework.GetBundleContext().RemoveListener(std::move(token)); +} + +TEST_F(BundleTrackerCustomCallbackTest, ErrorInModifiedBundlePropagates) +{ + auto customizer = std::make_shared(); + auto stateMask = BundleTracker<>::CreateStateMask( + Bundle::State::STATE_STARTING, Bundle::State::STATE_ACTIVE); + auto bundleTracker = BundleTracker<>(context, stateMask, customizer); + EXPECT_CALL(*customizer, AddingBundle) + .WillRepeatedly(::testing::Return(std::nullopt)); + bundleTracker.Open(); + bool receivedErrorEvent{ false }; + auto token = framework.GetBundleContext().AddFrameworkListener( + [&receivedErrorEvent](const FrameworkEvent& evt) { + if (evt.GetType() == FrameworkEvent::Type::FRAMEWORK_ERROR) { + receivedErrorEvent = true; + } + }); + + // When ModifiedBundle is called and throws an error + EXPECT_CALL(*customizer, AddingBundle).WillOnce(::testing::ReturnArg<0>()); + EXPECT_CALL(*customizer, ModifiedBundle) + .WillOnce(::testing::Throw(std::runtime_error("foo"))); + cppmicroservices::testing::InstallLib(framework.GetBundleContext(), + "TestBundleA") + .Start(); + + // Then the framework receives it + EXPECT_TRUE(receivedErrorEvent) + << "Framework did not receive error after ModifiedBundle throw"; + + EXPECT_CALL(*customizer, RemovedBundle).Times(::testing::AnyNumber()); + bundleTracker.Close(); + framework.GetBundleContext().RemoveListener(std::move(token)); +} + +TEST_F(BundleTrackerCustomCallbackTest, ErrorInRemovedBundlePropagates) +{ + auto customizer = std::make_shared(); + auto stateMask = + BundleTracker<>::CreateStateMask(Bundle::State::STATE_STARTING); + auto bundleTracker = BundleTracker<>(context, stateMask, customizer); + EXPECT_CALL(*customizer, AddingBundle) + .WillRepeatedly(::testing::Return(std::nullopt)); + bundleTracker.Open(); + bool receivedErrorEvent{ false }; + auto token = framework.GetBundleContext().AddFrameworkListener( + [&receivedErrorEvent](const FrameworkEvent& evt) { + if (evt.GetType() == FrameworkEvent::Type::FRAMEWORK_ERROR) { + receivedErrorEvent = true; + } + }); + + // When RemovedBundle is called and throws an error + EXPECT_CALL(*customizer, AddingBundle).WillOnce(::testing::ReturnArg<0>()); + EXPECT_CALL(*customizer, RemovedBundle) + .WillOnce(::testing::Throw(std::runtime_error("foo"))); + cppmicroservices::testing::InstallLib(framework.GetBundleContext(), + "TestBundleA") + .Start(); + + // Then the framework receives it + EXPECT_TRUE(receivedErrorEvent) + << "Framework did not receive error after RemovedBundle throw"; + + EXPECT_CALL(*customizer, RemovedBundle).Times(::testing::AnyNumber()); + bundleTracker.Close(); + framework.GetBundleContext().RemoveListener(std::move(token)); +} diff --git a/framework/test/gtest/BundleTrackerMethodTest.cpp b/framework/test/gtest/BundleTrackerMethodTest.cpp new file mode 100644 index 000000000..68b1e3763 --- /dev/null +++ b/framework/test/gtest/BundleTrackerMethodTest.cpp @@ -0,0 +1,407 @@ +/*============================================================================= + + Library: CppMicroServices + + Copyright (c) The CppMicroServices developers. See the COPYRIGHT + file at the top-level directory of this distribution and at + https://github.com/CppMicroServices/CppMicroServices/COPYRIGHT . + + 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. + + =============================================================================*/ +#include "cppmicroservices/Bundle.h" +#include "cppmicroservices/BundleTracker.h" +#include "cppmicroservices/Framework.h" +#include "cppmicroservices/FrameworkEvent.h" +#include "cppmicroservices/FrameworkFactory.h" + +#include "TestUtils.h" +#include "gtest/gtest.h" + +#ifdef GetObject +# undef GetObject +#endif + +using namespace cppmicroservices; + +class BundleTrackerMethodTest : public ::testing::Test +{ + protected: + Framework framework; + BundleContext context; + BundleTracker<>::BundleStateMaskType all_states + = BundleTracker<>::CreateStateMask(Bundle::State::STATE_ACTIVE, + Bundle::State::STATE_INSTALLED, + Bundle::State::STATE_RESOLVED, + Bundle::State::STATE_STARTING, + Bundle::State::STATE_STOPPING, + Bundle::State::STATE_UNINSTALLED); + + public: + BundleTrackerMethodTest() : framework(FrameworkFactory().NewFramework()) {}; + + ~BundleTrackerMethodTest() override = default; + + void + SetUp() override + { + framework.Start(); + context = framework.GetBundleContext(); + } + + void + TearDown() override + { + framework.Stop(); + framework.WaitForStop(std::chrono::milliseconds::zero()); + } +}; + +TEST_F(BundleTrackerMethodTest, TestCanCreateTracker) +{ + ASSERT_NO_THROW(BundleTracker bundleTracker(context, all_states)) << "Creation of BundleTracker failed"; +} + +TEST_F(BundleTrackerMethodTest, TestGetBundles) +{ + BundleTracker<> bundleTracker(context, all_states); + std::vector bundles = bundleTracker.GetBundles(); + EXPECT_EQ(0, bundles.size()) << "GetBundles() should return an empty vector before Open()"; + + ASSERT_NO_THROW(bundleTracker.Open()) << "BundleTracker failed to start"; + Bundle bundleMain = cppmicroservices::testing::GetBundle("main", context); + Bundle bundleSys = cppmicroservices::testing::GetBundle("system_bundle", context); + Bundle bundleA = cppmicroservices::testing::InstallLib(framework.GetBundleContext(), "TestBundleA"); + bundles = bundleTracker.GetBundles(); + + bool mainTracked = std::find(bundles.begin(), bundles.end(), bundleMain) != bundles.end(); + bool sysTracked = std::find(bundles.begin(), bundles.end(), bundleSys) != bundles.end(); + bool bundleATracked = std::find(bundles.begin(), bundles.end(), bundleA) != bundles.end(); + + EXPECT_TRUE(mainTracked) << "GetBundles() should include the main bundle"; + EXPECT_TRUE(sysTracked) << "GetBundles() should include the system bundle"; + EXPECT_TRUE(bundleATracked) << "GetBundles() should include the test bundle"; + + bundleTracker.Close(); + bundles = bundleTracker.GetBundles(); + EXPECT_EQ(0, bundles.size()) << "GetBundles() should return an empty vector after Close()"; +} + +TEST_F(BundleTrackerMethodTest, TestGetObject) +{ + // Given an open BundleTracker not tracking custom objects + BundleTracker<> bundleTracker(context, all_states); + ASSERT_NO_THROW(bundleTracker.Open()) << "BundleTracker failed to start"; + + // When a bundle is tracked + Bundle bundleA = cppmicroservices::testing::InstallLib(framework.GetBundleContext(), "TestBundleA"); + auto bundles = bundleTracker.GetBundles(); + bool bundleATracked = std::find(bundles.begin(), bundles.end(), bundleA) != bundles.end(); + ASSERT_TRUE(bundleATracked) << "A bundle that should be tracked is untracked"; + + // Then GetObject should return the bundle + EXPECT_EQ(bundleA, bundleTracker.GetObject(bundleA)) + << "GetObject for a tracked bundle should return the bundle when no custom " + "objects were used"; + + // When the bundle is untracked, then GetObject should return nullopt + bundleTracker.Remove(bundleA); + EXPECT_EQ(std::nullopt, bundleTracker.GetObject(bundleA)) + << "GetObject for an untracked, valid bundle should return nullopt"; + + // When the bundle is invalid, then GetObject should return nullopt + Bundle invalidBundle = Bundle(); + ASSERT_NO_THROW(bundleTracker.GetObject(invalidBundle)) << "GetObject threw an error for an invalid bundle"; + EXPECT_EQ(std::nullopt, bundleTracker.GetObject(invalidBundle)) + << "GetObject for an untracked, invalid bundle should return nullopt"; + + bundleTracker.Close(); +} + +TEST_F(BundleTrackerMethodTest, TestGetTracked) +{ + BundleTracker<> bundleTracker(context, all_states); + auto tracked = bundleTracker.GetTracked(); + EXPECT_EQ(0, tracked.size()) << "GetTracked() should return an empty map before Open()"; + + ASSERT_NO_THROW(bundleTracker.Open()) << "BundleTracker failed to start"; + Bundle bundleMain = cppmicroservices::testing::GetBundle("main", context); + Bundle bundleSys = cppmicroservices::testing::GetBundle("system_bundle", context); + Bundle bundleA = cppmicroservices::testing::InstallLib(framework.GetBundleContext(), "TestBundleA"); + tracked = bundleTracker.GetTracked(); + + bool mainTracked = tracked.find(bundleMain) != tracked.end(); + bool sysTracked = tracked.find(bundleSys) != tracked.end(); + bool bundleATracked = tracked.find(bundleA) != tracked.end(); + + EXPECT_TRUE(mainTracked) << "GetTracked() should include the main bundle"; + EXPECT_TRUE(sysTracked) << "GetTracked() should include the system bundle"; + EXPECT_TRUE(bundleATracked) << "GetTracked() should include the test bundle"; + + bundleTracker.Close(); + tracked = bundleTracker.GetTracked(); + EXPECT_EQ(0, tracked.size()) << "GetTracked() should return an empty map after Close()"; +} + +TEST_F(BundleTrackerMethodTest, TestIsEmpty) +{ + BundleTracker<> bundleTracker(context, all_states); + EXPECT_TRUE(bundleTracker.IsEmpty()) << "Unopened BundleTracker should be empty"; + + ASSERT_NO_THROW(bundleTracker.Open()) << "BundleTracker failed to start"; + Bundle bundle = cppmicroservices::testing::InstallLib(framework.GetBundleContext(), "TestBundleA"); + ASSERT_EQ(bundle, bundleTracker.GetObject(bundle)) << "BundleTracker failed to track a bundle"; + EXPECT_FALSE(bundleTracker.IsEmpty()) << "BundleTracker should not be empty when a bundle is tracked"; + + bundleTracker.Close(); + EXPECT_TRUE(bundleTracker.IsEmpty()) << "Closed BundleTracker should be empty"; +} + +TEST_F(BundleTrackerMethodTest, TestSize) +{ + auto stateMask = BundleTracker<>::CreateStateMask(Bundle::State::STATE_UNINSTALLED); + BundleTracker<> bundleTracker(context, stateMask); + EXPECT_EQ(0, bundleTracker.Size()) << "Size of unopened BundleTracker was not 0"; + + ASSERT_NO_THROW(bundleTracker.Open()) << "BundleTracker failed to start"; + EXPECT_EQ(0, bundleTracker.Size()) << "Size should be 0 when no bundles has entered a tracked state"; + + Bundle bundle = cppmicroservices::testing::InstallLib(framework.GetBundleContext(), "TestBundleA"); + bundle.Uninstall(); + EXPECT_EQ(1, bundleTracker.Size()) << "Size should be 1 when 1 bundle entered a tracked state"; + + bundleTracker.Close(); + EXPECT_EQ(0, bundleTracker.Size()) << "Size of closed BundleTracker was not 0"; +} + +TEST_F(BundleTrackerMethodTest, TestGetTrackingCountWorksAfterBundle) +{ + auto stateMask = BundleTracker<>::CreateStateMask(Bundle::State::STATE_ACTIVE); + BundleTracker<> bundleTracker(context, stateMask); + ASSERT_NO_THROW(bundleTracker.Open()) << "BundleTracker failed to start"; + + int preCount = bundleTracker.GetTrackingCount(); + Bundle bundle = cppmicroservices::testing::InstallLib(framework.GetBundleContext(), "TestBundleA"); + bundle.Start(); // bundle: installed->resolved->starting->ACTIVE + int postCount = bundleTracker.GetTrackingCount(); + + EXPECT_EQ(1, postCount - preCount) << "Tracking count didn't increment by 1 after a bundle was added"; + bundleTracker.Close(); +} + +TEST_F(BundleTrackerMethodTest, TestGetTrackingCountWorksAfterBundleModified) +{ + BundleTracker<> bundleTracker(context, all_states); + ASSERT_NO_THROW(bundleTracker.Open()) << "BundleTracker failed to start"; + Bundle bundle = cppmicroservices::testing::InstallLib(framework.GetBundleContext(), "TestBundleA"); + ASSERT_EQ(Bundle::State::STATE_INSTALLED, bundle.GetState()) << "Test bundle failed to install"; + + int preCount = bundleTracker.GetTrackingCount(); + bundle.Start(); // bundle: INSTALLED->RESOLVED->STARTING->ACTIVE + ASSERT_EQ(Bundle::State::STATE_ACTIVE, bundle.GetState()) << "Test bundle failed to start"; + int postCount = bundleTracker.GetTrackingCount(); + + EXPECT_EQ(3, postCount - preCount) << "Tracking count didn't increment by 3 after a bundle was modified 3 " + "times"; + bundleTracker.Close(); +} + +TEST_F(BundleTrackerMethodTest, TestGetTrackingCountWorksAfterBundleRemovedByStateChange) +{ + auto stateMask = BundleTracker<>::CreateStateMask(Bundle::State::STATE_INSTALLED); + BundleTracker<> bundleTracker(context, stateMask); + ASSERT_NO_THROW(bundleTracker.Open()) << "BundleTracker failed to start"; + Bundle bundle = cppmicroservices::testing::InstallLib(framework.GetBundleContext(), "TestBundleA"); + ASSERT_EQ(Bundle::State::STATE_INSTALLED, bundle.GetState()) << "Test bundle failed to install"; + + int preCount = bundleTracker.GetTrackingCount(); + bundle.Start(); // bundle: INSTALLED->resolved->starting->active + ASSERT_EQ(Bundle::State::STATE_ACTIVE, bundle.GetState()) << "Test bundle failed to start"; + int postCount = bundleTracker.GetTrackingCount(); + + EXPECT_EQ(1, postCount - preCount) << "Tracking count didn't increment by 1 after a bundle was removed"; + bundleTracker.Close(); +} + +TEST_F(BundleTrackerMethodTest, TestGetTrackingCountWorksAfterRemoveMethod) +{ + BundleTracker<> bundleTracker(context, all_states); + ASSERT_NO_THROW(bundleTracker.Open()) << "BundleTracker failed to start"; + Bundle bundle = cppmicroservices::testing::InstallLib(framework.GetBundleContext(), "TestBundleA"); + ASSERT_EQ(Bundle::State::STATE_INSTALLED, bundle.GetState()) << "Test bundle failed to install"; + + // If Remove removes a bundle from being tracked, tracking count increments by 1 + int preCount = bundleTracker.GetTrackingCount(); + bundleTracker.Remove(bundle); + ASSERT_EQ(Bundle::State::STATE_INSTALLED, bundle.GetState()) << "Remove() altered the bundle being removed"; + int postCount = bundleTracker.GetTrackingCount(); + + EXPECT_EQ(1, postCount - preCount) << "Tracking count didn't increment by 1 after Remove() removed a bundle"; + + // If Remove doesn't remove a bundle from being tracked, tracking count stays the same + preCount = postCount; + bundleTracker.Remove(bundle); + postCount = bundleTracker.GetTrackingCount(); + + EXPECT_EQ(0, postCount - preCount) << "Tracking count changed after Remove() didn't remove a bundle"; + bundleTracker.Close(); +} + +TEST_F(BundleTrackerMethodTest, TestGetTrackingCountWorksWhenClosed) +{ + BundleTracker<> bundleTracker(context, all_states); + EXPECT_EQ(-1, bundleTracker.GetTrackingCount()) << "Tracking count of unopened BundleTracker was not -1"; + + bundleTracker.Open(); + bundleTracker.Close(); + + EXPECT_EQ(-1, bundleTracker.GetTrackingCount()) << "Tracking count of unopened BundleTracker was not -1"; +} + +TEST_F(BundleTrackerMethodTest, TestRemove) +{ + // Given a BundleTracker tracking a bundle + BundleTracker<> bundleTracker(context, all_states); + ASSERT_NO_THROW(bundleTracker.Open()) << "BundleTracker failed to start"; + Bundle bundle = cppmicroservices::testing::InstallLib(framework.GetBundleContext(), "TestBundleA"); + auto bundlesTracked = bundleTracker.GetBundles(); + bool bundleTracked = std::find(bundlesTracked.begin(), bundlesTracked.end(), bundle) != bundlesTracked.end(); + ASSERT_TRUE(bundleTracked) << "BundleTracker failed to track a bundle"; + + // When Remove is called + bundleTracker.Remove(bundle); + + // Then the bundle should not be tracked + bundlesTracked = bundleTracker.GetBundles(); + bool bundleStillTracked = std::find(bundlesTracked.begin(), bundlesTracked.end(), bundle) != bundlesTracked.end(); + EXPECT_FALSE(bundleStillTracked) << "Calling Remove() didn't removed tracked bundle from being tracked"; + + bundleTracker.Close(); +} + +TEST_F(BundleTrackerMethodTest, TestBundleRemovedByRemoveTrackable) +{ + BundleTracker<> bundleTracker(context, all_states); + ASSERT_NO_THROW(bundleTracker.Open()) << "BundleTracker failed to start"; + Bundle testBundle = cppmicroservices::testing::InstallLib(framework.GetBundleContext(), "TestBundleA"); + + bundleTracker.Remove(testBundle); + testBundle.Start(); + + bool testBundleTracked = bundleTracker.GetObject(testBundle) == testBundle; + EXPECT_TRUE(testBundleTracked) << "Remove() was called on a tracked bundle, then later entered a tracked " + "state. The bundle should be tracked but wasn't"; +} + +TEST_F(BundleTrackerMethodTest, TestDefaultAddingBundleReturnsCorrectValue) +{ + BundleTracker<> bundleTracker(context, all_states); + Bundle bundle = Bundle(); + EXPECT_EQ(bundle, bundleTracker.AddingBundle(bundle, BundleEvent())) + << "Default AddingBundle should return the inputted bundle"; +} + +TEST_F(BundleTrackerMethodTest, TestRemoveUntrackedBundleDoesNothing) +{ + auto stateMask = BundleTracker<>::CreateStateMask(Bundle::State::STATE_ACTIVE); + BundleTracker<> bundleTracker(context, stateMask); + ASSERT_NO_THROW(bundleTracker.Open()) << "BundleTracker failed to start"; + Bundle bundle = cppmicroservices::testing::InstallLib(framework.GetBundleContext(), "TestBundleA"); + + int preCount = bundleTracker.GetTrackingCount(); + ASSERT_NO_THROW(bundleTracker.Remove(bundle)) << "Manually removing untracked Bundle should not throw"; + int postCount = bundleTracker.GetTrackingCount(); + EXPECT_EQ(preCount, postCount) << "BundleTracker should not change the tracking count when untracked " + "Bundle is manually removed"; + + bundleTracker.Close(); +} + +TEST_F(BundleTrackerMethodTest, TestReopeningTracker) +{ + auto stateMask = BundleTracker<>::CreateStateMask(Bundle::State::STATE_ACTIVE); + BundleTracker<> bundleTracker(context, stateMask); + ASSERT_NO_THROW(bundleTracker.Open()) << "BundleTracker failed to start"; + Bundle bundle = cppmicroservices::testing::InstallLib(framework.GetBundleContext(), "TestBundleA"); + bundle.Start(); + ASSERT_EQ(2, bundleTracker.Size()) << "Bundles tracked incorrectly"; + bundleTracker.Close(); + + ASSERT_NO_THROW(bundleTracker.Open()) << "BundleTracker failed to start after it was previously opened and " + "closed"; + EXPECT_EQ(2, bundleTracker.Size()) << "BundleTracker is tracking bundles incorrectly after being reopened"; + + bundleTracker.Close(); +} + +TEST_F(BundleTrackerMethodTest, TestBundlesInUntrackedStatesUntracked) +{ + auto stateMask = BundleTracker<>::CreateStateMask(Bundle::State::STATE_STARTING, Bundle::State::STATE_STOPPING); + BundleTracker<> bundleTracker(context, stateMask); + ASSERT_NO_THROW(bundleTracker.Open()) << "BundleTracker failed to start"; + + Bundle bundle = cppmicroservices::testing::InstallLib(framework.GetBundleContext(), "TestBundleA"); + bundle.Start(); + + EXPECT_EQ(0, bundleTracker.Size()) << "No bundles should be tracked when none are in a state covered by the " + "state mask"; + bundleTracker.Close(); +} + +TEST_F(BundleTrackerMethodTest, TestOpenWithInvalidContextThrowsError) +{ + auto invalidContext = BundleContext(); + BundleTracker<> bundleTracker(invalidContext, all_states); + + EXPECT_THROW(bundleTracker.Open(), std::runtime_error) << "Opening BundleTracker with invalid context should throw"; +} + +TEST_F(BundleTrackerMethodTest, TestOpeningOpenTrackerDoesNothing) +{ + BundleTracker<> bundleTracker(context, all_states); + ASSERT_NO_THROW(bundleTracker.Open()) << "BundleTracker failed to start"; + Bundle bundle = cppmicroservices::testing::InstallLib(framework.GetBundleContext(), "TestBundleA"); + bundle.Start(); + + int preCount = bundleTracker.GetTrackingCount(); + int preSize = bundleTracker.Size(); + ASSERT_NO_THROW(bundleTracker.Open()) << "Open() on opened BundleTracker threw an error"; + int postCount = bundleTracker.GetTrackingCount(); + int postSize = bundleTracker.Size(); + + EXPECT_EQ(0, postCount - preCount) << "Open() on opened BundleTracker increased the tracking count"; + EXPECT_EQ(0, postSize - preSize) << "Open() on opened BundleTracker changed the size"; + bundleTracker.Close(); +} + +TEST_F(BundleTrackerMethodTest, TestClosingClosedTrackerDoesNothing) +{ + BundleTracker<> bundleTracker(context, all_states); + ASSERT_NO_THROW(bundleTracker.Open()) << "BundleTracker failed to start"; + ASSERT_NO_THROW(bundleTracker.Close()) << "BundleTracker failed to close"; + int preCount = bundleTracker.GetTrackingCount(); + + ASSERT_NO_THROW(bundleTracker.Close()) << "Closing closed BundleTracker should no-op"; + int postCount = bundleTracker.GetTrackingCount(); + EXPECT_EQ(preCount, postCount) << "Closing closed BundleTracker should not change the tracking count"; +} + +TEST_F(BundleTrackerMethodTest, TestClosingUnopenTrackerDoesNothing) +{ + BundleTracker<> bundleTracker(context, all_states); + int preCount = bundleTracker.GetTrackingCount(); + + ASSERT_NO_THROW(bundleTracker.Close()) << "Closing unopened BundleTracker should no-op"; + int postCount = bundleTracker.GetTrackingCount(); + EXPECT_EQ(preCount, postCount) << "Closing unopened BundleTracker should not change the tracking count"; +} diff --git a/framework/test/gtest/CMakeLists.txt b/framework/test/gtest/CMakeLists.txt index 152c40222..0381dc521 100644 --- a/framework/test/gtest/CMakeLists.txt +++ b/framework/test/gtest/CMakeLists.txt @@ -78,6 +78,9 @@ set(_gtest_tests ResourceCompilerTest.cpp MultipleListenersTest.cpp BundleTest.cpp + BundleTrackerMethodTest.cpp + BundleTrackerCustomCallbackTest.cpp + BundleTrackerConcurrencyTest.cpp main.cpp ) diff --git a/framework/test/gtest/MultipleListenersTest.cpp b/framework/test/gtest/MultipleListenersTest.cpp index f099129f0..d0471aab8 100644 --- a/framework/test/gtest/MultipleListenersTest.cpp +++ b/framework/test/gtest/MultipleListenersTest.cpp @@ -246,7 +246,12 @@ namespace public: MultipleListenersTest() : framework(FrameworkFactory().NewFramework()) {} - ~MultipleListenersTest() override = default; + ~MultipleListenersTest() override + { + // when using gtest_repeat flag, this clears count within the + // namespace to avoid failures on repetition. + count = 0; + } void SetUp() override diff --git a/framework/test/gtest/ServiceHooksTest.cpp b/framework/test/gtest/ServiceHooksTest.cpp index 43ba63136..ced46b70a 100644 --- a/framework/test/gtest/ServiceHooksTest.cpp +++ b/framework/test/gtest/ServiceHooksTest.cpp @@ -33,10 +33,11 @@ #include "cppmicroservices/ServiceEventListenerHook.h" #include "cppmicroservices/ServiceFindHook.h" #include "cppmicroservices/ServiceListenerHook.h" +#include "gtest/gtest.h" #include "TestUtils.h" #include "TestingConfig.h" -#include "gtest/gtest.h" +#include "gmock/gmock.h" #include @@ -82,7 +83,6 @@ namespace public: TestServiceEventListenerHook(int id, BundleContext const& context) : id(id), bundleCtx(context) {} - using MapType = ShrinkableMap>; void @@ -149,13 +149,9 @@ namespace // One listener with LDAP filter should remain ASSERT_GE(static_cast(listenerInfos.size()), 1); #endif - - ordering.push_back(id); } ServiceListenerHook::ListenerInfo listenerInfo; - - static std::vector ordering; }; class TestServiceEventListenerHookFailure : public ServiceEventListenerHook @@ -170,16 +166,13 @@ namespace } }; - std::vector TestServiceEventListenerHook::ordering; - class TestServiceFindHook : public ServiceFindHook { private: - int id; BundleContext bundleCtx; public: - TestServiceFindHook(int id, BundleContext const& context) : id(id), bundleCtx(context) {} + TestServiceFindHook(BundleContext const& context) : bundleCtx(context) {} void Find(BundleContext const& context, @@ -190,14 +183,9 @@ namespace ASSERT_EQ(context, bundleCtx); references.clear(); - ordering.push_back(id); } - - static std::vector ordering; }; - std::vector TestServiceFindHook::ordering; - // Test failure modes for FindHook class TestServiceFindHookFailure : public ServiceFindHook { @@ -212,11 +200,10 @@ namespace class TestServiceListenerHook : public ServiceListenerHook { private: - int id; BundleContext bundleCtx; public: - TestServiceListenerHook(int id, BundleContext const& context) : id(id), bundleCtx(context) {} + TestServiceListenerHook(BundleContext const& context) : bundleCtx(context) {} void Added(std::vector const& listeners) @@ -224,10 +211,11 @@ namespace for (std::vector::const_iterator iter = listeners.begin(); iter != listeners.end(); ++iter) { if (iter->IsRemoved() || iter->GetBundleContext().GetBundle() != bundleCtx.GetBundle()) + { continue; + } listenerInfos.insert(*iter); lastAdded = listeners.back(); - ordering.push_back(id); } } @@ -237,20 +225,15 @@ namespace for (std::vector::const_iterator iter = listeners.begin(); iter != listeners.end(); ++iter) { listenerInfos.erase(*iter); - ordering.push_back(id * 10); } lastRemoved = listeners.back(); } - static std::vector ordering; - std::unordered_set listenerInfos; ListenerInfo lastAdded; ListenerInfo lastRemoved; }; - std::vector TestServiceListenerHook::ordering; - class TestServiceListenerHookFailure : public ServiceListenerHook { public: @@ -305,6 +288,39 @@ namespace } // end unnamed namespace +class MockServiceEventListenerHook : public ServiceEventListenerHook +{ + public: + MockServiceEventListenerHook() = default; + + using MapType = ShrinkableMap>; + + MOCK_METHOD(void, Event, (ServiceEvent const& event, MapType& listeners), (override)); +}; + +class MockServiceFindHook : public ServiceFindHook +{ + public: + MockServiceFindHook() = default; + + MOCK_METHOD(void, + Find, + (BundleContext const& context, + std::string const& name, + std::string const& filter, + ShrinkableVector& references), + (override)); +}; + +class MockServiceListenerHook : public ServiceListenerHook +{ + public: + MockServiceListenerHook() = default; + + MOCK_METHOD(void, Added, (std::vector const& listeners)); + MOCK_METHOD(void, Removed, (std::vector const& listeners)); +}; + TEST_F(ServiceHooksTest, TestListenerHook) { TestServiceListener serviceListener1; @@ -314,13 +330,13 @@ TEST_F(ServiceHooksTest, TestListenerHook) &TestServiceListener::ServiceChanged, LDAPProp(Constants::OBJECTCLASS) == "bla"); - auto serviceListenerHook1 = std::make_shared(1, context); + auto serviceListenerHook1 = std::make_shared(context); ServiceProperties hookProps1; hookProps1[Constants::SERVICE_RANKING] = 0; ServiceRegistration listenerHookReg1 = context.RegisterService(serviceListenerHook1, hookProps1); - auto serviceListenerHook2 = std::make_shared(2, context); + auto serviceListenerHook2 = std::make_shared(context); ServiceProperties hookProps2; hookProps2[Constants::SERVICE_RANKING] = 10; ServiceRegistration listenerHookReg2 @@ -330,25 +346,12 @@ TEST_F(ServiceHooksTest, TestListenerHook) // check if hooks got notified about the existing listeners ASSERT_EQ(serviceListenerHook1->listenerInfos.size(), 2); #endif + const std::size_t listenerInfoSizeOld = serviceListenerHook1->listenerInfos.size() - 2; context.AddServiceListener(&serviceListener1, &TestServiceListener::ServiceChanged); auto lastAdded = serviceListenerHook1->lastAdded; -#ifdef US_BUILD_SHARED_LIBS - std::vector expectedOrdering; - expectedOrdering.push_back(1); - expectedOrdering.push_back(1); - expectedOrdering.push_back(2); - expectedOrdering.push_back(2); - expectedOrdering.push_back(20); - expectedOrdering.push_back(10); - expectedOrdering.push_back(2); - expectedOrdering.push_back(1); - // Check Listener hook call order - ASSERT_EQ(serviceListenerHook1->ordering, expectedOrdering); -#endif - context.AddServiceListener(&serviceListener1, &TestServiceListener::ServiceChanged, LDAPProp(Constants::OBJECTCLASS) == "blub"); @@ -356,26 +359,9 @@ TEST_F(ServiceHooksTest, TestListenerHook) ASSERT_EQ(lastAdded, serviceListenerHook1->lastRemoved); ASSERT_FALSE(lastAdded == serviceListenerHook1->lastAdded); -#ifdef US_BUILD_SHARED_LIBS - expectedOrdering.push_back(20); - expectedOrdering.push_back(10); - expectedOrdering.push_back(2); - expectedOrdering.push_back(1); - // Check Listener hook call order - ASSERT_EQ(serviceListenerHook1->ordering, expectedOrdering); -#endif - context.RemoveServiceListener(&serviceListener1, &TestServiceListener::ServiceChanged); context.RemoveServiceListener(&serviceListener2, &TestServiceListener::ServiceChanged); -#ifdef US_BUILD_SHARED_LIBS - expectedOrdering.push_back(20); - expectedOrdering.push_back(10); - expectedOrdering.push_back(20); - expectedOrdering.push_back(10); - ASSERT_EQ(serviceListenerHook1->ordering, expectedOrdering); -#endif - // Removed listener infos ASSERT_EQ(serviceListenerHook1->listenerInfos.size(), listenerInfoSizeOld); @@ -385,22 +371,18 @@ TEST_F(ServiceHooksTest, TestListenerHook) TEST_F(ServiceHooksTest, TestFindHook) { - auto serviceFindHook1 = std::make_shared(1, context); + auto serviceFindHook1 = std::make_shared(context); ServiceProperties hookProps1; hookProps1[Constants::SERVICE_RANKING] = 0; ServiceRegistration findHookReg1 = context.RegisterService(serviceFindHook1, hookProps1); - auto serviceFindHook2 = std::make_shared(2, context); + auto serviceFindHook2 = std::make_shared(context); ServiceProperties hookProps2; hookProps2[Constants::SERVICE_RANKING] = 10; ServiceRegistration findHookReg2 = context.RegisterService(serviceFindHook2, hookProps2); - std::vector expectedOrdering; - // Find hook call order - ASSERT_EQ(serviceFindHook1->ordering, expectedOrdering); - TestServiceListener serviceListener; context.AddServiceListener(&serviceListener, &TestServiceListener::ServiceChanged); @@ -413,24 +395,18 @@ TEST_F(ServiceHooksTest, TestFindHook) std::vector refs = context.GetServiceReferences("cppmicroservices::TestBundleAService"); ASSERT_TRUE(refs.empty()); + ServiceReferenceU ref = context.GetServiceReference("cppmicroservices::TestBundleAService"); // Invalid reference (filtered out) ASSERT_FALSE(ref); - expectedOrdering.push_back(2); - expectedOrdering.push_back(1); - expectedOrdering.push_back(2); - expectedOrdering.push_back(1); - - // Find hook call order - ASSERT_EQ(serviceFindHook1->ordering, expectedOrdering); - findHookReg2.Unregister(); findHookReg1.Unregister(); refs = context.GetServiceReferences("cppmicroservices::TestBundleAService"); // Non-empty references ASSERT_FALSE(refs.empty()); + ref = context.GetServiceReference("cppmicroservices::TestBundleAService"); ASSERT_TRUE(ref); @@ -460,13 +436,6 @@ TEST_F(ServiceHooksTest, TestEventListenerHook) ServiceRegistration eventListenerHookReg2 = context.RegisterService(serviceEventListenerHook2, hookProps2); - std::vector expectedOrdering; - expectedOrdering.push_back(1); - expectedOrdering.push_back(1); - expectedOrdering.push_back(2); - // Event listener hook call order - ASSERT_EQ(serviceEventListenerHook1->ordering, expectedOrdering); - // service event of service event listener hook ASSERT_TRUE(serviceListener1.events.empty()); // no service event for filtered listener @@ -474,12 +443,8 @@ TEST_F(ServiceHooksTest, TestEventListenerHook) auto bundle = cppmicroservices::testing::InstallLib(context, "TestBundleA"); ASSERT_TRUE(bundle); - bundle.Start(); - expectedOrdering.push_back(1); - expectedOrdering.push_back(2); - // Test Event listener hook call order - ASSERT_EQ(serviceEventListenerHook1->ordering, expectedOrdering); + bundle.Start(); bundle.Stop(); @@ -602,4 +567,142 @@ TEST_F(ServiceHooksTest, TestListenerHookFailure) framework.GetBundleContext().RemoveListener(std::move(fwkListenerToken)); } +TEST_F(ServiceHooksTest, TestListenerHookCallbackOrdering) +{ + TestServiceListener serviceListener1; + TestServiceListener serviceListener2; + context.AddServiceListener(&serviceListener1, &TestServiceListener::ServiceChanged); + context.AddServiceListener(&serviceListener2, + &TestServiceListener::ServiceChanged, + LDAPProp(Constants::OBJECTCLASS) == "bla"); + + auto serviceListenerHook1 = std::make_shared(); + auto serviceListenerHook2 = std::make_shared(); + +#ifdef US_BUILD_SHARED_LIBS + ::testing::InSequence s; + EXPECT_CALL(*serviceListenerHook1, Added(::testing::_)); + EXPECT_CALL(*serviceListenerHook2, Added(::testing::_)); + EXPECT_CALL(*serviceListenerHook2, Removed(::testing::_)); + EXPECT_CALL(*serviceListenerHook1, Removed(::testing::_)); + EXPECT_CALL(*serviceListenerHook2, Added(::testing::_)); + EXPECT_CALL(*serviceListenerHook1, Added(::testing::_)); + + EXPECT_CALL(*serviceListenerHook2, Removed(::testing::_)); + EXPECT_CALL(*serviceListenerHook1, Removed(::testing::_)); + EXPECT_CALL(*serviceListenerHook2, Added(::testing::_)); + EXPECT_CALL(*serviceListenerHook1, Added(::testing::_)); + + EXPECT_CALL(*serviceListenerHook2, Removed(::testing::_)); + EXPECT_CALL(*serviceListenerHook1, Removed(::testing::_)); + EXPECT_CALL(*serviceListenerHook2, Removed(::testing::_)); + EXPECT_CALL(*serviceListenerHook1, Removed(::testing::_)); +#endif + + ServiceProperties hookProps1; + hookProps1[Constants::SERVICE_RANKING] = 0; + ServiceRegistration listenerHookReg1 + = context.RegisterService(serviceListenerHook1, hookProps1); + + ServiceProperties hookProps2; + hookProps2[Constants::SERVICE_RANKING] = 10; + ServiceRegistration listenerHookReg2 + = context.RegisterService(serviceListenerHook2, hookProps2); + + context.AddServiceListener(&serviceListener1, &TestServiceListener::ServiceChanged); + + context.AddServiceListener(&serviceListener1, + &TestServiceListener::ServiceChanged, + LDAPProp(Constants::OBJECTCLASS) == "blub"); + + context.RemoveServiceListener(&serviceListener1, &TestServiceListener::ServiceChanged); + context.RemoveServiceListener(&serviceListener2, &TestServiceListener::ServiceChanged); + + listenerHookReg2.Unregister(); + listenerHookReg1.Unregister(); +} + +TEST_F(ServiceHooksTest, TestFindHookCallbackOrdering) +{ + auto serviceFindHook1 = std::make_shared(); + auto serviceFindHook2 = std::make_shared(); + + ::testing::InSequence s; + EXPECT_CALL(*serviceFindHook2, Find(::testing::_, ::testing::_, ::testing::_, ::testing::_)); + EXPECT_CALL(*serviceFindHook1, Find(::testing::_, ::testing::_, ::testing::_, ::testing::_)); + EXPECT_CALL(*serviceFindHook2, Find(::testing::_, ::testing::_, ::testing::_, ::testing::_)); + EXPECT_CALL(*serviceFindHook1, Find(::testing::_, ::testing::_, ::testing::_, ::testing::_)); + + ServiceProperties hookProps1; + hookProps1[Constants::SERVICE_RANKING] = 0; + ServiceRegistration findHookReg1 + = context.RegisterService(serviceFindHook1, hookProps1); + + ServiceProperties hookProps2; + hookProps2[Constants::SERVICE_RANKING] = 10; + ServiceRegistration findHookReg2 + = context.RegisterService(serviceFindHook2, hookProps2); + + TestServiceListener serviceListener; + context.AddServiceListener(&serviceListener, &TestServiceListener::ServiceChanged); + + auto bundle = cppmicroservices::testing::InstallLib(context, "TestBundleA"); + + bundle.Start(); + + std::vector refs = context.GetServiceReferences("cppmicroservices::TestBundleAService"); + ServiceReferenceU ref = context.GetServiceReference("cppmicroservices::TestBundleAService"); + + findHookReg2.Unregister(); + findHookReg1.Unregister(); + + bundle.Stop(); + + context.RemoveServiceListener(&serviceListener, &TestServiceListener::ServiceChanged); +} + +TEST_F(ServiceHooksTest, TestEventListenerHookCallbackOrdering) +{ + TestServiceListener serviceListener1; + TestServiceListener serviceListener2; + context.AddServiceListener(&serviceListener1, &TestServiceListener::ServiceChanged); + context.AddServiceListener(&serviceListener2, + &TestServiceListener::ServiceChanged, + LDAPProp(Constants::OBJECTCLASS) == "bla"); + + auto serviceEventListenerHook1 = std::make_shared(); + auto serviceEventListenerHook2 = std::make_shared(); + + ::testing::InSequence s; + EXPECT_CALL(*serviceEventListenerHook1, Event(::testing::_, ::testing::_)).Times(2); + EXPECT_CALL(*serviceEventListenerHook2, Event(::testing::_, ::testing::_)); + EXPECT_CALL(*serviceEventListenerHook1, Event(::testing::_, ::testing::_)); + EXPECT_CALL(*serviceEventListenerHook2, Event(::testing::_, ::testing::_)); + EXPECT_CALL(*serviceEventListenerHook1, Event(::testing::_, ::testing::_)); + EXPECT_CALL(*serviceEventListenerHook2, Event(::testing::_, ::testing::_)); + EXPECT_CALL(*serviceEventListenerHook1, Event(::testing::_, ::testing::_)); + + ServiceProperties hookProps1; + hookProps1[Constants::SERVICE_RANKING] = 10; + ServiceRegistration eventListenerHookReg1 + = context.RegisterService(serviceEventListenerHook1, hookProps1); + + ServiceProperties hookProps2; + hookProps2[Constants::SERVICE_RANKING] = 0; + ServiceRegistration eventListenerHookReg2 + = context.RegisterService(serviceEventListenerHook2, hookProps2); + + auto bundle = cppmicroservices::testing::InstallLib(context, "TestBundleA"); + + bundle.Start(); + + bundle.Stop(); + + eventListenerHookReg2.Unregister(); + eventListenerHookReg1.Unregister(); + + context.RemoveServiceListener(&serviceListener1, &TestServiceListener::ServiceChanged); + context.RemoveServiceListener(&serviceListener2, &TestServiceListener::ServiceChanged); +} + US_MSVC_POP_WARNING diff --git a/framework/test/gtest/ServiceReferenceTest.cpp b/framework/test/gtest/ServiceReferenceTest.cpp index 0321b5f72..036f49f85 100644 --- a/framework/test/gtest/ServiceReferenceTest.cpp +++ b/framework/test/gtest/ServiceReferenceTest.cpp @@ -29,6 +29,7 @@ limitations under the License. #include "cppmicroservices/ServiceRegistration.h" #include "gtest/gtest.h" #include +#include using namespace cppmicroservices; @@ -118,7 +119,8 @@ TEST_F(ServiceReferenceTest, TestRegisterAndGetServiceReferenceTest) InterfaceMap im; im["ServiceNS::ITestServiceB"] = std::make_shared(); - (void)context.RegisterService(std::make_shared(im)); + auto reg = context.RegisterService(std::make_shared(im)); + US_UNUSED(reg); auto sr3 = context.GetServiceReference("ServiceNS::ITestServiceB"); ASSERT_EQ(sr3.GetInterfaceId(), "ServiceNS::ITestServiceB"); @@ -245,3 +247,26 @@ TEST_F(ServiceReferenceTest, TestGetServiceReferenceWithModifiedProperties) }); ASSERT_EQ(context.GetServiceReference(), regArr[1].GetReference()); } + +TEST_F(ServiceReferenceTest, TestRegisterUnregisterCompareReference) +{ + auto context = framework.GetBundleContext(); + std::unordered_set> set; + cppmicroservices::ServiceReference sr1; + cppmicroservices::ServiceReference sr2; + + auto impl = std::make_shared(); + { + auto reg = context.RegisterService(impl); + + sr1 = context.GetServiceReference(); + sr2 = context.GetServiceReference(); + + set.insert(sr1); + ASSERT_NE(set.find(sr2), set.end()); + + reg.Unregister(); + } + + ASSERT_NE(set.find(sr2), set.end()); +} \ No newline at end of file diff --git a/framework/test/gtest/ServiceTrackerTest.cpp b/framework/test/gtest/ServiceTrackerTest.cpp index 0dcdfc814..b59aed993 100644 --- a/framework/test/gtest/ServiceTrackerTest.cpp +++ b/framework/test/gtest/ServiceTrackerTest.cpp @@ -651,3 +651,99 @@ TEST_F(ServiceTrackerTestFixture, TestReOpenServiceTrackerWithCustomizer) }); tracker->Close(); } + +namespace +{ + namespace foo + { + class Bar + { + public: + virtual ~Bar() {} + }; + } // namespace foo + + class MockFooBar : public foo::Bar + { + }; +} // namespace + +TEST_F(ServiceTrackerTestFixture, TestFilterPropertiesTypes) +{ + LDAPFilter filter("(tag=foo::bar::Baz)"); + ServiceTracker tracker(framework.GetBundleContext(), filter); + tracker.Open(); + + auto fooService = std::make_shared(); + cppmicroservices::ServiceProperties props + = std::initializer_list { std::make_pair("tag", + ("foo::bar::Baz")) }; + + auto svc = framework.GetBundleContext().RegisterService(fooService, std::move(props)); + + ASSERT_EQ(tracker.GetTrackingCount(), 1); + + tracker.Close(); +} + +#ifdef US_ENABLE_THREADING_SUPPORT + +TEST(ServiceTrackerTests, TestServiceTrackerDeadlock) +{ + Framework framework = FrameworkFactory().NewFramework(); + framework.Start(); + + ServiceTracker tracker(framework.GetBundleContext()); + tracker.Open(); + auto f = std::async( + [&framework] + { + // technically there's a race here, so the async should ensure WaitForService is started prior to + // terminating the framework. + std::this_thread::sleep_for(std::chrono::seconds(1)); + + framework.Stop(); + framework.WaitForStop(std::chrono::milliseconds::zero()); + }); + + ASSERT_THROW(tracker.WaitForService(), std::logic_error); +} + +TEST(ServiceTrackerTests, TestServiceTrackerInvalidBundle) +{ + Framework framework = FrameworkFactory().NewFramework(); + framework.Start(); + + ServiceTracker tracker(framework.GetBundleContext()); + tracker.Open(); + + framework.Stop(); + framework.WaitForStop(std::chrono::milliseconds::zero()); + + ASSERT_THROW(tracker.WaitForService(), std::logic_error); +} + +// If the test doesn't throw, it is successful. +// Intended to be run with many repititions to test for sporadic failures. +TEST(ServiceTrackerTests, FrameworkTrackerCloseRace) +{ + auto framework = cppmicroservices::FrameworkFactory().NewFramework(); + framework.Start(); + + ServiceTracker tracker(framework.GetBundleContext()); + tracker.Open(); + + auto interfaceOneService = std::make_shared(); + + auto svc = framework.GetBundleContext().RegisterService(interfaceOneService); + + auto fut = std::async(std::launch::async, + [&framework]() + { + framework.Stop(); + framework.WaitForStop(std::chrono::milliseconds::zero()); + }); + tracker.Close(); +} + +#endif \ No newline at end of file diff --git a/third_party/README b/third_party/README index 9392236f2..f9df1473c 100644 --- a/third_party/README +++ b/third_party/README @@ -31,6 +31,9 @@ https://github.com/open-source-parsers/jsoncpp (formerly http://jsoncpp.sourceforge.net) 1.9.5 +Patches + +* Fixed some compiler warnings miniz ----- @@ -39,11 +42,10 @@ MIT License (GitHub) or Public Domain https://github.com/richgel999/miniz (formerly https://code.google.com/p/miniz/) -76b3a872855388c735c564905da030f26334f3b3 +v3.0.2 Note: miniz.c and miniz.h created by downloading the source code - and building it using CMake. This was necessary as a v3.0 - release of miniz has not occurred yet. + and running amalgamate.sh. Patches @@ -134,7 +136,7 @@ Google Test and Google Mock See https://github.com/google/googletest/blob/master/LICENSE for license information. https://github.com/google/googletest -aefb45469ee7e6bde0cd1d2c18412046c30e7bb6 +https://github.com/google/googletest/tree/v1.14.0 RapidJSON --------- @@ -158,7 +160,7 @@ Abseil (absl) See https://github.com/abseil/abseil-cpp/blob/master/LICENSE for license information https://github.com/abseil/abseil-cpp -6ec136281086b71da32b5fb068bd6e46b78a5c79 +https://github.com/abseil/abseil-cpp/releases/tag/20230125.3 spdlog ------ @@ -166,17 +168,21 @@ spdlog See https://github.com/gabime/spdlog/blob/v1.x/LICENSE for license information https://github.com/gabime/spdlog -83b40b8cdac0253a147fbda6d6a7dbc2c836c380 +v1.12.0 * We only use the include folder since we aren't statically linking to the library -* Commit reflects the commit when we copied the include folder +* version reflects the version from which we copied the include folder -Patches: - - * Replaced old-style-cast to long int with static_cast Boost ------ See https://www.boost.org/users/license.html for license information 1.73.0 + +Boost nowide +------------ + +See https://www.boost.org/users/license.html for license information +https://github.com/boostorg/nowide/tree/standalone +https://github.com/boostorg/nowide/commit/2a0543f8279f3f416b5c84fcf7e40db46c3a659a diff --git a/third_party/absl/CMakeLists.txt b/third_party/absl/CMakeLists.txt index f217c59dc..f04f5fd8d 100644 --- a/third_party/absl/CMakeLists.txt +++ b/third_party/absl/CMakeLists.txt @@ -17,7 +17,10 @@ set(_ABSL_SRCS "absl/strings/string_view.cc" "absl/strings/internal/charconv_bigint.cc" "absl/strings/internal/charconv_parse.cc" + "absl/strings/internal/memutil.cc" + + "absl/types/bad_optional_access.cc" ) set(_ABSL_HDRS @@ -27,28 +30,49 @@ set(_ABSL_HDRS "absl/base/log_severity.h" "absl/base/macros.h" "absl/base/optimization.h" + "absl/base/options.h" "absl/base/policy_checks.h" "absl/base/port.h" "absl/base/internal/atomic_hook.h" - "absl/base/internal/bits.h" + "absl/base/internal/endian.h" + "absl/base/internal/errno_saver.h" "absl/base/internal/identity.h" + "absl/base/internal/inline_variable.h" + "absl/base/internal/invoke.h" "absl/base/internal/raw_logging.h" "absl/base/internal/throw_delegate.h" + "absl/base/internal/unaligned_access.h" "absl/meta/type_traits.h" + "absl/memory/memory.h" + + "absl/numeric/bits.h" "absl/numeric/int128.h" + "absl/numeric/internal/bits.h" "absl/strings/ascii.h" "absl/strings/charconv.h" + "absl/strings/escaping.h" "absl/strings/match.h" "absl/strings/numbers.h" "absl/strings/str_cat.h" + "absl/strings/str_join.h" "absl/strings/string_view.h" "absl/strings/internal/charconv_bigint.h" "absl/strings/internal/charconv_parse.h" + "absl/strings/internal/has_absl_stringify.h" "absl/strings/internal/memutil.h" + "absl/strings/internal/ostringstream.h" "absl/strings/internal/resize_uninitialized.h" + "absl/strings/internal/str_join_internal.h" + "absl/strings/internal/stringify_sink.h" + + "absl/types/bad_optional_access.h" + "absl/types/internal/optional.h" + "absl/types/optional.h" + + "absl/utility/utility.h" ) add_library(absl OBJECT ${_ABSL_SRCS} ${_ABSL_HDRS}) diff --git a/third_party/absl/absl/base/attributes.h b/third_party/absl/absl/base/attributes.h index a7da62af7..b7826e776 100644 --- a/third_party/absl/absl/base/attributes.h +++ b/third_party/absl/absl/base/attributes.h @@ -18,8 +18,6 @@ // These macros are used within Abseil and allow the compiler to optimize, where // applicable, certain function calls. // -// This file is used for both C and C++! -// // Most macros here are exposing GCC or Clang features, and are stubbed out for // other compilers. // @@ -32,34 +30,12 @@ // of them are not supported in older version of Clang. Thus, we check // `__has_attribute()` first. If the check fails, we check if we are on GCC and // assume the attribute exists on GCC (which is verified on GCC 4.7). -// -// ----------------------------------------------------------------------------- -// Sanitizer Attributes -// ----------------------------------------------------------------------------- -// -// Sanitizer-related attributes are not "defined" in this file (and indeed -// are not defined as such in any file). To utilize the following -// sanitizer-related attributes within your builds, define the following macros -// within your build using a `-D` flag, along with the given value for -// `-fsanitize`: -// -// * `ADDRESS_SANITIZER` + `-fsanitize=address` (Clang, GCC 4.8) -// * `MEMORY_SANITIZER` + `-fsanitize=memory` (Clang-only) -// * `THREAD_SANITIZER + `-fsanitize=thread` (Clang, GCC 4.8+) -// * `UNDEFINED_BEHAVIOR_SANITIZER` + `-fsanitize=undefined` (Clang, GCC 4.9+) -// * `CONTROL_FLOW_INTEGRITY` + -fsanitize=cfi (Clang-only) -// -// Example: -// -// // Enable branches in the Abseil code that are tagged for ASan: -// $ bazel build --copt=-DADDRESS_SANITIZER --copt=-fsanitize=address -// --linkopt=-fsanitize=address *target* -// -// Since these macro names are only supported by GCC and Clang, we only check -// for `__GNUC__` (GCC or Clang) and the above macros. + #ifndef ABSL_BASE_ATTRIBUTES_H_ #define ABSL_BASE_ATTRIBUTES_H_ +#include "absl/base/config.h" + // ABSL_HAVE_ATTRIBUTE // // A function-like feature checking macro that is a wrapper around @@ -143,7 +119,7 @@ #if ABSL_HAVE_ATTRIBUTE(disable_tail_calls) #define ABSL_HAVE_ATTRIBUTE_NO_TAIL_CALL 1 #define ABSL_ATTRIBUTE_NO_TAIL_CALL __attribute__((disable_tail_calls)) -#elif defined(__GNUC__) && !defined(__clang__) +#elif defined(__GNUC__) && !defined(__clang__) && !defined(__e2k__) #define ABSL_HAVE_ATTRIBUTE_NO_TAIL_CALL 1 #define ABSL_ATTRIBUTE_NO_TAIL_CALL \ __attribute__((optimize("no-optimize-sibling-calls"))) @@ -155,12 +131,15 @@ // ABSL_ATTRIBUTE_WEAK // // Tags a function as weak for the purposes of compilation and linking. -// Weak attributes currently do not work properly in LLVM's Windows backend, -// so disable them there. See https://bugs.llvm.org/show_bug.cgi?id=37598 -// for futher information. -#if (ABSL_HAVE_ATTRIBUTE(weak) || \ - (defined(__GNUC__) && !defined(__clang__))) && \ - !(defined(__llvm__) && defined(_WIN32)) +// Weak attributes did not work properly in LLVM's Windows backend before +// 9.0.0, so disable them there. See https://bugs.llvm.org/show_bug.cgi?id=37598 +// for further information. +// The MinGW compiler doesn't complain about the weak attribute until the link +// step, presumably because Windows doesn't use ELF binaries. +#if (ABSL_HAVE_ATTRIBUTE(weak) || \ + (defined(__GNUC__) && !defined(__clang__))) && \ + (!defined(_WIN32) || (defined(__clang__) && __clang_major__ >= 9)) && \ + !defined(__MINGW32__) #undef ABSL_ATTRIBUTE_WEAK #define ABSL_ATTRIBUTE_WEAK __attribute__((weak)) #define ABSL_HAVE_ATTRIBUTE_WEAK 1 @@ -232,21 +211,24 @@ // out of bounds or does other scary things with memory. // NOTE: GCC supports AddressSanitizer(asan) since 4.8. // https://gcc.gnu.org/gcc-4.8/changes.html -#if defined(__GNUC__) +#if ABSL_HAVE_ATTRIBUTE(no_sanitize_address) #define ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS __attribute__((no_sanitize_address)) +#elif defined(_MSC_VER) && _MSC_VER >= 1928 +// https://docs.microsoft.com/en-us/cpp/cpp/no-sanitize-address +#define ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS __declspec(no_sanitize_address) #else #define ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS #endif // ABSL_ATTRIBUTE_NO_SANITIZE_MEMORY // -// Tells the MemorySanitizer to relax the handling of a given function. All -// "Use of uninitialized value" warnings from such functions will be suppressed, -// and all values loaded from memory will be considered fully initialized. -// This attribute is similar to the ADDRESS_SANITIZER attribute above, but deals -// with initialized-ness rather than addressability issues. +// Tells the MemorySanitizer to relax the handling of a given function. All "Use +// of uninitialized value" warnings from such functions will be suppressed, and +// all values loaded from memory will be considered fully initialized. This +// attribute is similar to the ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS attribute +// above, but deals with initialized-ness rather than addressability issues. // NOTE: MemorySanitizer(msan) is supported by Clang but not GCC. -#if defined(__clang__) +#if ABSL_HAVE_ATTRIBUTE(no_sanitize_memory) #define ABSL_ATTRIBUTE_NO_SANITIZE_MEMORY __attribute__((no_sanitize_memory)) #else #define ABSL_ATTRIBUTE_NO_SANITIZE_MEMORY @@ -257,7 +239,7 @@ // Tells the ThreadSanitizer to not instrument a given function. // NOTE: GCC supports ThreadSanitizer(tsan) since 4.8. // https://gcc.gnu.org/gcc-4.8/changes.html -#if defined(__GNUC__) +#if ABSL_HAVE_ATTRIBUTE(no_sanitize_thread) #define ABSL_ATTRIBUTE_NO_SANITIZE_THREAD __attribute__((no_sanitize_thread)) #else #define ABSL_ATTRIBUTE_NO_SANITIZE_THREAD @@ -269,8 +251,10 @@ // where certain behavior (eg. division by zero) is being used intentionally. // NOTE: GCC supports UndefinedBehaviorSanitizer(ubsan) since 4.9. // https://gcc.gnu.org/gcc-4.9/changes.html -#if defined(__GNUC__) && \ - (defined(UNDEFINED_BEHAVIOR_SANITIZER) || defined(ADDRESS_SANITIZER)) +#if ABSL_HAVE_ATTRIBUTE(no_sanitize_undefined) +#define ABSL_ATTRIBUTE_NO_SANITIZE_UNDEFINED \ + __attribute__((no_sanitize_undefined)) +#elif ABSL_HAVE_ATTRIBUTE(no_sanitize) #define ABSL_ATTRIBUTE_NO_SANITIZE_UNDEFINED \ __attribute__((no_sanitize("undefined"))) #else @@ -281,7 +265,7 @@ // // Tells the ControlFlowIntegrity sanitizer to not instrument a given function. // See https://clang.llvm.org/docs/ControlFlowIntegrity.html for details. -#if defined(__GNUC__) && defined(CONTROL_FLOW_INTEGRITY) +#if ABSL_HAVE_ATTRIBUTE(no_sanitize) #define ABSL_ATTRIBUTE_NO_SANITIZE_CFI __attribute__((no_sanitize("cfi"))) #else #define ABSL_ATTRIBUTE_NO_SANITIZE_CFI @@ -291,7 +275,7 @@ // // Tells the SafeStack to not instrument a given function. // See https://clang.llvm.org/docs/SafeStack.html for details. -#if defined(__GNUC__) && defined(SAFESTACK_SANITIZER) +#if ABSL_HAVE_ATTRIBUTE(no_sanitize) #define ABSL_ATTRIBUTE_NO_SANITIZE_SAFESTACK \ __attribute__((no_sanitize("safe-stack"))) #else @@ -301,10 +285,7 @@ // ABSL_ATTRIBUTE_RETURNS_NONNULL // // Tells the compiler that a particular function never returns a null pointer. -#if ABSL_HAVE_ATTRIBUTE(returns_nonnull) || \ - (defined(__GNUC__) && \ - (__GNUC__ > 5 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 9)) && \ - !defined(__clang__)) +#if ABSL_HAVE_ATTRIBUTE(returns_nonnull) #define ABSL_ATTRIBUTE_RETURNS_NONNULL __attribute__((returns_nonnull)) #else #define ABSL_ATTRIBUTE_RETURNS_NONNULL @@ -334,15 +315,22 @@ __attribute__((section(#name))) __attribute__((noinline)) #endif - // ABSL_ATTRIBUTE_SECTION_VARIABLE // // Tells the compiler/linker to put a given variable into a section and define // `__start_ ## name` and `__stop_ ## name` symbols to bracket the section. // This functionality is supported by GNU linker. #ifndef ABSL_ATTRIBUTE_SECTION_VARIABLE +#ifdef _AIX +// __attribute__((section(#name))) on AIX is achived by using the `.csect` psudo +// op which includes an additional integer as part of its syntax indcating +// alignment. If data fall under different alignments then you might get a +// compilation error indicating a `Section type conflict`. +#define ABSL_ATTRIBUTE_SECTION_VARIABLE(name) +#else #define ABSL_ATTRIBUTE_SECTION_VARIABLE(name) __attribute__((section(#name))) #endif +#endif // ABSL_DECLARE_ATTRIBUTE_SECTION_VARS // @@ -353,8 +341,8 @@ // a no-op on ELF but not on Mach-O. // #ifndef ABSL_DECLARE_ATTRIBUTE_SECTION_VARS -#define ABSL_DECLARE_ATTRIBUTE_SECTION_VARS(name) \ - extern char __start_##name[] ABSL_ATTRIBUTE_WEAK; \ +#define ABSL_DECLARE_ATTRIBUTE_SECTION_VARS(name) \ + extern char __start_##name[] ABSL_ATTRIBUTE_WEAK; \ extern char __stop_##name[] ABSL_ATTRIBUTE_WEAK #endif #ifndef ABSL_DEFINE_ATTRIBUTE_SECTION_VARS @@ -415,6 +403,9 @@ // // Tells the compiler to warn about unused results. // +// For code or headers that are assured to only build with C++17 and up, prefer +// just using the standard `[[nodiscard]]` directly over this macro. +// // When annotating a function, it must appear as the first part of the // declaration or definition. The compiler will warn if the return value from // such a function is unused: @@ -441,9 +432,10 @@ // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66425 // // Note: past advice was to place the macro after the argument list. -#if ABSL_HAVE_ATTRIBUTE(nodiscard) -#define ABSL_MUST_USE_RESULT [[nodiscard]] -#elif defined(__clang__) && ABSL_HAVE_ATTRIBUTE(warn_unused_result) +// +// TODO(b/176172494): Use ABSL_HAVE_CPP_ATTRIBUTE(nodiscard) when all code is +// compliant with the stricter [[nodiscard]]. +#if defined(__clang__) && ABSL_HAVE_ATTRIBUTE(warn_unused_result) #define ABSL_MUST_USE_RESULT __attribute__((warn_unused_result)) #else #define ABSL_MUST_USE_RESULT @@ -505,13 +497,15 @@ // packages/targets, as this may lead to conflicting definitions of functions at // link-time. // +// XRay isn't currently supported on Android: +// https://github.com/android/ndk/issues/368 #if ABSL_HAVE_CPP_ATTRIBUTE(clang::xray_always_instrument) && \ - !defined(ABSL_NO_XRAY_ATTRIBUTES) + !defined(ABSL_NO_XRAY_ATTRIBUTES) && !defined(__ANDROID__) #define ABSL_XRAY_ALWAYS_INSTRUMENT [[clang::xray_always_instrument]] #define ABSL_XRAY_NEVER_INSTRUMENT [[clang::xray_never_instrument]] #if ABSL_HAVE_CPP_ATTRIBUTE(clang::xray_log_args) #define ABSL_XRAY_LOG_ARGS(N) \ - [[clang::xray_always_instrument, clang::xray_log_args(N)]] + [[clang::xray_always_instrument, clang::xray_log_args(N)]] #else #define ABSL_XRAY_LOG_ARGS(N) [[clang::xray_always_instrument]] #endif @@ -542,6 +536,13 @@ // ABSL_ATTRIBUTE_UNUSED // // Prevents the compiler from complaining about variables that appear unused. +// +// For code or headers that are assured to only build with C++17 and up, prefer +// just using the standard '[[maybe_unused]]' directly over this macro. +// +// Due to differences in positioning requirements between the old, compiler +// specific __attribute__ syntax and the now standard [[maybe_unused]], this +// macro does not attempt to take advantage of '[[maybe_unused]]'. #if ABSL_HAVE_ATTRIBUTE(unused) || (defined(__GNUC__) && !defined(__clang__)) #undef ABSL_ATTRIBUTE_UNUSED #define ABSL_ATTRIBUTE_UNUSED __attribute__((__unused__)) @@ -561,7 +562,25 @@ // ABSL_ATTRIBUTE_PACKED // -// Prevents the compiler from padding a structure to natural alignment +// Instructs the compiler not to use natural alignment for a tagged data +// structure, but instead to reduce its alignment to 1. +// +// Therefore, DO NOT APPLY THIS ATTRIBUTE TO STRUCTS CONTAINING ATOMICS. Doing +// so can cause atomic variables to be mis-aligned and silently violate +// atomicity on x86. +// +// This attribute can either be applied to members of a structure or to a +// structure in its entirety. Applying this attribute (judiciously) to a +// structure in its entirety to optimize the memory footprint of very +// commonly-used structs is fine. Do not apply this attribute to a structure in +// its entirety if the purpose is to control the offsets of the members in the +// structure. Instead, apply this attribute only to structure members that need +// it. +// +// When applying ABSL_ATTRIBUTE_PACKED only to specific structure members the +// natural alignment of structure members not annotated is preserved. Aligned +// member accesses are faster than non-aligned member accesses even if the +// targeted microprocessor supports non-aligned accesses. #if ABSL_HAVE_ATTRIBUTE(packed) || (defined(__GNUC__) && !defined(__clang__)) #define ABSL_ATTRIBUTE_PACKED __attribute__((__packed__)) #else @@ -578,15 +597,103 @@ #define ABSL_ATTRIBUTE_FUNC_ALIGN(bytes) #endif +// ABSL_FALLTHROUGH_INTENDED +// +// Annotates implicit fall-through between switch labels, allowing a case to +// indicate intentional fallthrough and turn off warnings about any lack of a +// `break` statement. The ABSL_FALLTHROUGH_INTENDED macro should be followed by +// a semicolon and can be used in most places where `break` can, provided that +// no statements exist between it and the next switch label. +// +// Example: +// +// switch (x) { +// case 40: +// case 41: +// if (truth_is_out_there) { +// ++x; +// ABSL_FALLTHROUGH_INTENDED; // Use instead of/along with annotations +// // in comments +// } else { +// return x; +// } +// case 42: +// ... +// +// Notes: When supported, GCC and Clang can issue a warning on switch labels +// with unannotated fallthrough using the warning `-Wimplicit-fallthrough`. See +// clang documentation on language extensions for details: +// https://clang.llvm.org/docs/AttributeReference.html#fallthrough-clang-fallthrough +// +// When used with unsupported compilers, the ABSL_FALLTHROUGH_INTENDED macro has +// no effect on diagnostics. In any case this macro has no effect on runtime +// behavior and performance of code. + +#ifdef ABSL_FALLTHROUGH_INTENDED +#error "ABSL_FALLTHROUGH_INTENDED should not be defined." +#elif ABSL_HAVE_CPP_ATTRIBUTE(fallthrough) +#define ABSL_FALLTHROUGH_INTENDED [[fallthrough]] +#elif ABSL_HAVE_CPP_ATTRIBUTE(clang::fallthrough) +#define ABSL_FALLTHROUGH_INTENDED [[clang::fallthrough]] +#elif ABSL_HAVE_CPP_ATTRIBUTE(gnu::fallthrough) +#define ABSL_FALLTHROUGH_INTENDED [[gnu::fallthrough]] +#else +#define ABSL_FALLTHROUGH_INTENDED \ + do { \ + } while (0) +#endif + +// ABSL_DEPRECATED() +// +// Marks a deprecated class, struct, enum, function, method and variable +// declarations. The macro argument is used as a custom diagnostic message (e.g. +// suggestion of a better alternative). +// +// For code or headers that are assured to only build with C++14 and up, prefer +// just using the standard `[[deprecated("message")]]` directly over this macro. +// +// Examples: +// +// class ABSL_DEPRECATED("Use Bar instead") Foo {...}; +// +// ABSL_DEPRECATED("Use Baz() instead") void Bar() {...} +// +// template +// ABSL_DEPRECATED("Use DoThat() instead") +// void DoThis(); +// +// enum FooEnum { +// kBar ABSL_DEPRECATED("Use kBaz instead"), +// }; +// +// Every usage of a deprecated entity will trigger a warning when compiled with +// GCC/Clang's `-Wdeprecated-declarations` option. Google's production toolchain +// turns this warning off by default, instead relying on clang-tidy to report +// new uses of deprecated code. +#if ABSL_HAVE_ATTRIBUTE(deprecated) +#define ABSL_DEPRECATED(message) __attribute__((deprecated(message))) +#else +#define ABSL_DEPRECATED(message) +#endif + // ABSL_CONST_INIT // // A variable declaration annotated with the `ABSL_CONST_INIT` attribute will // not compile (on supported platforms) unless the variable has a constant // initializer. This is useful for variables with static and thread storage // duration, because it guarantees that they will not suffer from the so-called -// "static init order fiasco". Prefer to put this attribute on the most visible -// declaration of the variable, if there's more than one, because code that -// accesses the variable can then use the attribute for optimization. +// "static init order fiasco". +// +// This attribute must be placed on the initializing declaration of the +// variable. Some compilers will give a -Wmissing-constinit warning when this +// attribute is placed on some other declaration but missing from the +// initializing declaration. +// +// In some cases (notably with thread_local variables), `ABSL_CONST_INIT` can +// also be used in a non-initializing declaration to tell the compiler that a +// variable is already initialized, reducing overhead that would otherwise be +// incurred by a hidden guard variable. Thus annotating all declarations with +// this attribute is recommended to potentially enhance optimization. // // Example: // @@ -595,14 +702,81 @@ // ABSL_CONST_INIT static MyType my_var; // }; // -// MyType MyClass::my_var = MakeMyType(...); +// ABSL_CONST_INIT MyType MyClass::my_var = MakeMyType(...); +// +// For code or headers that are assured to only build with C++20 and up, prefer +// just using the standard `constinit` keyword directly over this macro. // // Note that this attribute is redundant if the variable is declared constexpr. -#if ABSL_HAVE_CPP_ATTRIBUTE(clang::require_constant_initialization) -// NOLINTNEXTLINE(whitespace/braces) +#if defined(__cpp_constinit) && __cpp_constinit >= 201907L +#define ABSL_CONST_INIT constinit +#elif ABSL_HAVE_CPP_ATTRIBUTE(clang::require_constant_initialization) #define ABSL_CONST_INIT [[clang::require_constant_initialization]] #else #define ABSL_CONST_INIT -#endif // ABSL_HAVE_CPP_ATTRIBUTE(clang::require_constant_initialization) +#endif + +// These annotations are not available yet due to fear of breaking code. +#define ABSL_ATTRIBUTE_PURE_FUNCTION +#define ABSL_ATTRIBUTE_CONST_FUNCTION + +// ABSL_ATTRIBUTE_LIFETIME_BOUND indicates that a resource owned by a function +// parameter or implicit object parameter is retained by the return value of the +// annotated function (or, for a parameter of a constructor, in the value of the +// constructed object). This attribute causes warnings to be produced if a +// temporary object does not live long enough. +// +// When applied to a reference parameter, the referenced object is assumed to be +// retained by the return value of the function. When applied to a non-reference +// parameter (for example, a pointer or a class type), all temporaries +// referenced by the parameter are assumed to be retained by the return value of +// the function. +// +// See also the upstream documentation: +// https://clang.llvm.org/docs/AttributeReference.html#lifetimebound +#if ABSL_HAVE_CPP_ATTRIBUTE(clang::lifetimebound) +#define ABSL_ATTRIBUTE_LIFETIME_BOUND [[clang::lifetimebound]] +#elif ABSL_HAVE_ATTRIBUTE(lifetimebound) +#define ABSL_ATTRIBUTE_LIFETIME_BOUND __attribute__((lifetimebound)) +#else +#define ABSL_ATTRIBUTE_LIFETIME_BOUND +#endif + +// ABSL_ATTRIBUTE_TRIVIAL_ABI +// Indicates that a type is "trivially relocatable" -- meaning it can be +// relocated without invoking the constructor/destructor, using a form of move +// elision. +// +// From a memory safety point of view, putting aside destructor ordering, it's +// safe to apply ABSL_ATTRIBUTE_TRIVIAL_ABI if an object's location +// can change over the course of its lifetime: if a constructor can be run one +// place, and then the object magically teleports to another place where some +// methods are run, and then the object teleports to yet another place where it +// is destroyed. This is notably not true for self-referential types, where the +// move-constructor must keep the self-reference up to date. If the type changed +// location without invoking the move constructor, it would have a dangling +// self-reference. +// +// The use of this teleporting machinery means that the number of paired +// move/destroy operations can change, and so it is a bad idea to apply this to +// a type meant to count the number of moves. +// +// Warning: applying this can, rarely, break callers. Objects passed by value +// will be destroyed at the end of the call, instead of the end of the +// full-expression containing the call. In addition, it changes the ABI +// of functions accepting this type by value (e.g. to pass in registers). +// +// See also the upstream documentation: +// https://clang.llvm.org/docs/AttributeReference.html#trivial-abi +// +#if ABSL_HAVE_CPP_ATTRIBUTE(clang::trivial_abi) +#define ABSL_ATTRIBUTE_TRIVIAL_ABI [[clang::trivial_abi]] +#define ABSL_HAVE_ATTRIBUTE_TRIVIAL_ABI 1 +#elif ABSL_HAVE_ATTRIBUTE(trivial_abi) +#define ABSL_ATTRIBUTE_TRIVIAL_ABI __attribute__((trivial_abi)) +#define ABSL_HAVE_ATTRIBUTE_TRIVIAL_ABI 1 +#else +#define ABSL_ATTRIBUTE_TRIVIAL_ABI +#endif #endif // ABSL_BASE_ATTRIBUTES_H_ diff --git a/third_party/absl/absl/base/casts.h b/third_party/absl/absl/base/casts.h index aba017821..b99adb069 100644 --- a/third_party/absl/absl/base/casts.h +++ b/third_party/absl/absl/base/casts.h @@ -29,24 +29,16 @@ #include #include +#if defined(__cpp_lib_bit_cast) && __cpp_lib_bit_cast >= 201806L +#include // For std::bit_cast. +#endif // defined(__cpp_lib_bit_cast) && __cpp_lib_bit_cast >= 201806L + #include "absl/base/internal/identity.h" #include "absl/base/macros.h" #include "absl/meta/type_traits.h" namespace absl { - -namespace internal_casts { - -template -struct is_bitcastable - : std::integral_constant< - bool, - sizeof(Dest) == sizeof(Source) && - type_traits_internal::is_trivially_copyable::value && - type_traits_internal::is_trivially_copyable::value && - std::is_default_constructible::value> {}; - -} // namespace internal_casts +ABSL_NAMESPACE_BEGIN // implicit_cast() // @@ -104,79 +96,85 @@ constexpr To implicit_cast(typename absl::internal::identity_t to) { // bit_cast() // -// Performs a bitwise cast on a type without changing the underlying bit -// representation of that type's value. The two types must be of the same size -// and both types must be trivially copyable. As with most casts, use with -// caution. A `bit_cast()` might be needed when you need to temporarily treat a -// type as some other type, such as in the following cases: +// Creates a value of the new type `Dest` whose representation is the same as +// that of the argument, which is of (deduced) type `Source` (a "bitwise cast"; +// every bit in the value representation of the result is equal to the +// corresponding bit in the object representation of the source). Source and +// destination types must be of the same size, and both types must be trivially +// copyable. // -// * Serialization (casting temporarily to `char *` for those purposes is -// always allowed by the C++ standard) -// * Managing the individual bits of a type within mathematical operations -// that are not normally accessible through that type -// * Casting non-pointer types to pointer types (casting the other way is -// allowed by `reinterpret_cast()` but round-trips cannot occur the other -// way). -// -// Example: +// As with most casts, use with caution. A `bit_cast()` might be needed when you +// need to treat a value as the value of some other type, for example, to access +// the individual bits of an object which are not normally accessible through +// the object's type, such as for working with the binary representation of a +// floating point value: // // float f = 3.14159265358979; -// int i = bit_cast(f); +// int i = bit_cast(f); // // i = 0x40490fdb // -// Casting non-pointer types to pointer types and then dereferencing them -// traditionally produces undefined behavior. +// Reinterpreting and accessing a value directly as a different type (as shown +// below) usually results in undefined behavior. // // Example: // // // WRONG -// float f = 3.14159265358979; // WRONG -// int i = * reinterpret_cast(&f); // WRONG +// float f = 3.14159265358979; +// int i = reinterpret_cast(f); // Wrong +// int j = *reinterpret_cast(&f); // Equally wrong +// int k = *bit_cast(&f); // Equally wrong // -// The address-casting method produces undefined behavior according to the ISO -// C++ specification section [basic.lval]. Roughly, this section says: if an -// object in memory has one type, and a program accesses it with a different -// type, the result is undefined behavior for most values of "different type". +// Reinterpret-casting results in undefined behavior according to the ISO C++ +// specification, section [basic.lval]. Roughly, this section says: if an object +// in memory has one type, and a program accesses it with a different type, the +// result is undefined behavior for most "different type". +// +// Using bit_cast on a pointer and then dereferencing it is no better than using +// reinterpret_cast. You should only use bit_cast on the value itself. // // Such casting results in type punning: holding an object in memory of one type // and reading its bits back using a different type. A `bit_cast()` avoids this -// issue by implementing its casts using `memcpy()`, which avoids introducing -// this undefined behavior. -// -// NOTE: The requirements here are more strict than the bit_cast of standard -// proposal p0476 due to the need for workarounds and lack of intrinsics. -// Specifically, this implementation also requires `Dest` to be -// default-constructible. -template < - typename Dest, typename Source, - typename std::enable_if::value, - int>::type = 0> +// issue by copying the object representation to a new value, which avoids +// introducing this undefined behavior (since the original value is never +// accessed in the wrong way). +// +// The requirements of `absl::bit_cast` are more strict than that of +// `std::bit_cast` unless compiler support is available. Specifically, without +// compiler support, this implementation also requires `Dest` to be +// default-constructible. In C++20, `absl::bit_cast` is replaced by +// `std::bit_cast`. +#if defined(__cpp_lib_bit_cast) && __cpp_lib_bit_cast >= 201806L + +using std::bit_cast; + +#else // defined(__cpp_lib_bit_cast) && __cpp_lib_bit_cast >= 201806L + +template ::value && + type_traits_internal::is_trivially_copyable::value +#if !ABSL_HAVE_BUILTIN(__builtin_bit_cast) + && std::is_default_constructible::value +#endif // !ABSL_HAVE_BUILTIN(__builtin_bit_cast) + , + int>::type = 0> +#if ABSL_HAVE_BUILTIN(__builtin_bit_cast) +inline constexpr Dest bit_cast(const Source& source) { + return __builtin_bit_cast(Dest, source); +} +#else // ABSL_HAVE_BUILTIN(__builtin_bit_cast) inline Dest bit_cast(const Source& source) { Dest dest; memcpy(static_cast(std::addressof(dest)), static_cast(std::addressof(source)), sizeof(dest)); return dest; } +#endif // ABSL_HAVE_BUILTIN(__builtin_bit_cast) -// NOTE: This overload is only picked if the requirements of bit_cast are not -// met. It is therefore UB, but is provided temporarily as previous versions of -// this function template were unchecked. Do not use this in new code. -template < - typename Dest, typename Source, - typename std::enable_if< - !internal_casts::is_bitcastable::value, int>::type = 0> -ABSL_DEPRECATED( - "absl::bit_cast type requirements were violated. Update the types being " - "used such that they are the same size and are both TriviallyCopyable.") -inline Dest bit_cast(const Source& source) { - static_assert(sizeof(Dest) == sizeof(Source), - "Source and destination types should have equal sizes."); - - Dest dest; - memcpy(&dest, &source, sizeof(dest)); - return dest; -} +#endif // defined(__cpp_lib_bit_cast) && __cpp_lib_bit_cast >= 201806L +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_BASE_CASTS_H_ diff --git a/third_party/absl/absl/base/config.h b/third_party/absl/absl/base/config.h index 1cb69b0f7..05d960b63 100644 --- a/third_party/absl/absl/base/config.h +++ b/third_party/absl/absl/base/config.h @@ -56,6 +56,25 @@ #include #endif // __cplusplus +// ABSL_INTERNAL_CPLUSPLUS_LANG +// +// MSVC does not set the value of __cplusplus correctly, but instead uses +// _MSVC_LANG as a stand-in. +// https://docs.microsoft.com/en-us/cpp/preprocessor/predefined-macros +// +// However, there are reports that MSVC even sets _MSVC_LANG incorrectly at +// times, for example: +// https://github.com/microsoft/vscode-cpptools/issues/1770 +// https://reviews.llvm.org/D70996 +// +// For this reason, this symbol is considered INTERNAL and code outside of +// Abseil must not use it. +#if defined(_MSVC_LANG) +#define ABSL_INTERNAL_CPLUSPLUS_LANG _MSVC_LANG +#elif defined(__cplusplus) +#define ABSL_INTERNAL_CPLUSPLUS_LANG __cplusplus +#endif + #if defined(__APPLE__) // Included for TARGET_OS_IPHONE, __IPHONE_OS_VERSION_MIN_REQUIRED, // __IPHONE_8_0. @@ -63,8 +82,107 @@ #include #endif +#include "absl/base/options.h" #include "absl/base/policy_checks.h" +// Abseil long-term support (LTS) releases will define +// `ABSL_LTS_RELEASE_VERSION` to the integer representing the date string of the +// LTS release version, and will define `ABSL_LTS_RELEASE_PATCH_LEVEL` to the +// integer representing the patch-level for that release. +// +// For example, for LTS release version "20300401.2", this would give us +// ABSL_LTS_RELEASE_VERSION == 20300401 && ABSL_LTS_RELEASE_PATCH_LEVEL == 2 +// +// These symbols will not be defined in non-LTS code. +// +// Abseil recommends that clients live-at-head. Therefore, if you are using +// these symbols to assert a minimum version requirement, we recommend you do it +// as +// +// #if defined(ABSL_LTS_RELEASE_VERSION) && ABSL_LTS_RELEASE_VERSION < 20300401 +// #error Project foo requires Abseil LTS version >= 20300401 +// #endif +// +// The `defined(ABSL_LTS_RELEASE_VERSION)` part of the check excludes +// live-at-head clients from the minimum version assertion. +// +// See https://abseil.io/about/releases for more information on Abseil release +// management. +// +// LTS releases can be obtained from +// https://github.com/abseil/abseil-cpp/releases. +#define ABSL_LTS_RELEASE_VERSION 20230125 +#define ABSL_LTS_RELEASE_PATCH_LEVEL 3 + +// Helper macro to convert a CPP variable to a string literal. +#define ABSL_INTERNAL_DO_TOKEN_STR(x) #x +#define ABSL_INTERNAL_TOKEN_STR(x) ABSL_INTERNAL_DO_TOKEN_STR(x) + +// ----------------------------------------------------------------------------- +// Abseil namespace annotations +// ----------------------------------------------------------------------------- + +// ABSL_NAMESPACE_BEGIN/ABSL_NAMESPACE_END +// +// An annotation placed at the beginning/end of each `namespace absl` scope. +// This is used to inject an inline namespace. +// +// The proper way to write Abseil code in the `absl` namespace is: +// +// namespace absl { +// ABSL_NAMESPACE_BEGIN +// +// void Foo(); // absl::Foo(). +// +// ABSL_NAMESPACE_END +// } // namespace absl +// +// Users of Abseil should not use these macros, because users of Abseil should +// not write `namespace absl {` in their own code for any reason. (Abseil does +// not support forward declarations of its own types, nor does it support +// user-provided specialization of Abseil templates. Code that violates these +// rules may be broken without warning.) +#if !defined(ABSL_OPTION_USE_INLINE_NAMESPACE) || \ + !defined(ABSL_OPTION_INLINE_NAMESPACE_NAME) +#error options.h is misconfigured. +#endif + +// Check that ABSL_OPTION_INLINE_NAMESPACE_NAME is neither "head" nor "" +#if defined(__cplusplus) && ABSL_OPTION_USE_INLINE_NAMESPACE == 1 + +#define ABSL_INTERNAL_INLINE_NAMESPACE_STR \ + ABSL_INTERNAL_TOKEN_STR(ABSL_OPTION_INLINE_NAMESPACE_NAME) + +static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != '\0', + "options.h misconfigured: ABSL_OPTION_INLINE_NAMESPACE_NAME must " + "not be empty."); +static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' || + ABSL_INTERNAL_INLINE_NAMESPACE_STR[1] != 'e' || + ABSL_INTERNAL_INLINE_NAMESPACE_STR[2] != 'a' || + ABSL_INTERNAL_INLINE_NAMESPACE_STR[3] != 'd' || + ABSL_INTERNAL_INLINE_NAMESPACE_STR[4] != '\0', + "options.h misconfigured: ABSL_OPTION_INLINE_NAMESPACE_NAME must " + "be changed to a new, unique identifier name."); + +#endif + +#if ABSL_OPTION_USE_INLINE_NAMESPACE == 0 +#define ABSL_NAMESPACE_BEGIN +#define ABSL_NAMESPACE_END +#define ABSL_INTERNAL_C_SYMBOL(x) x +#elif ABSL_OPTION_USE_INLINE_NAMESPACE == 1 +#define ABSL_NAMESPACE_BEGIN \ + inline namespace ABSL_OPTION_INLINE_NAMESPACE_NAME { +#define ABSL_NAMESPACE_END } +#define ABSL_INTERNAL_C_SYMBOL_HELPER_2(x, v) x##_##v +#define ABSL_INTERNAL_C_SYMBOL_HELPER_1(x, v) \ + ABSL_INTERNAL_C_SYMBOL_HELPER_2(x, v) +#define ABSL_INTERNAL_C_SYMBOL(x) \ + ABSL_INTERNAL_C_SYMBOL_HELPER_1(x, ABSL_OPTION_INLINE_NAMESPACE_NAME) +#else +#error options.h is misconfigured. +#endif + // ----------------------------------------------------------------------------- // Compiler Feature Checks // ----------------------------------------------------------------------------- @@ -84,12 +202,35 @@ #define ABSL_HAVE_BUILTIN(x) 0 #endif +#ifdef __has_feature +#define ABSL_HAVE_FEATURE(f) __has_feature(f) +#else +#define ABSL_HAVE_FEATURE(f) 0 +#endif + +// Portable check for GCC minimum version: +// https://gcc.gnu.org/onlinedocs/cpp/Common-Predefined-Macros.html +#if defined(__GNUC__) && defined(__GNUC_MINOR__) +#define ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(x, y) \ + (__GNUC__ > (x) || __GNUC__ == (x) && __GNUC_MINOR__ >= (y)) +#else +#define ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(x, y) 0 +#endif + +#if defined(__clang__) && defined(__clang_major__) && defined(__clang_minor__) +#define ABSL_INTERNAL_HAVE_MIN_CLANG_VERSION(x, y) \ + (__clang_major__ > (x) || __clang_major__ == (x) && __clang_minor__ >= (y)) +#else +#define ABSL_INTERNAL_HAVE_MIN_CLANG_VERSION(x, y) 0 +#endif + // ABSL_HAVE_TLS is defined to 1 when __thread should be supported. -// We assume __thread is supported on Linux when compiled with Clang or compiled -// against libstdc++ with _GLIBCXX_HAVE_TLS defined. +// We assume __thread is supported on Linux or Asylo when compiled with Clang or +// compiled against libstdc++ with _GLIBCXX_HAVE_TLS defined. #ifdef ABSL_HAVE_TLS #error ABSL_HAVE_TLS cannot be directly set -#elif defined(__linux__) && (defined(__clang__) || defined(_GLIBCXX_HAVE_TLS)) +#elif (defined(__linux__) || defined(__ASYLO__)) && \ + (defined(__clang__) || defined(_GLIBCXX_HAVE_TLS)) #define ABSL_HAVE_TLS 1 #endif @@ -101,10 +242,10 @@ // gcc >= 4.8.1 using libstdc++, and Visual Studio. #ifdef ABSL_HAVE_STD_IS_TRIVIALLY_DESTRUCTIBLE #error ABSL_HAVE_STD_IS_TRIVIALLY_DESTRUCTIBLE cannot be directly set -#elif defined(_LIBCPP_VERSION) || \ - (!defined(__clang__) && defined(__GNUC__) && defined(__GLIBCXX__) && \ - (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))) || \ - defined(_MSC_VER) +#elif defined(_LIBCPP_VERSION) || defined(_MSC_VER) || \ + (defined(__clang__) && __clang_major__ >= 15) || \ + (!defined(__clang__) && defined(__GLIBCXX__) && \ + ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(4, 8)) #define ABSL_HAVE_STD_IS_TRIVIALLY_DESTRUCTIBLE 1 #endif @@ -117,21 +258,34 @@ // // Checks whether `std::is_trivially_copy_assignable` is supported. -// Notes: Clang with libc++ supports these features, as does gcc >= 5.1 with -// either libc++ or libstdc++, and Visual Studio (but not NVCC). +// Notes: Clang with libc++ supports these features, as does gcc >= 7.4 with +// libstdc++, or gcc >= 8.2 with libc++, and Visual Studio (but not NVCC). #if defined(ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE) #error ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE cannot be directly set #elif defined(ABSL_HAVE_STD_IS_TRIVIALLY_ASSIGNABLE) #error ABSL_HAVE_STD_IS_TRIVIALLY_ASSIGNABLE cannot directly set -#elif (defined(__clang__) && defined(_LIBCPP_VERSION)) || \ - (!defined(__clang__) && defined(__GNUC__) && \ - (__GNUC__ > 7 || (__GNUC__ == 7 && __GNUC_MINOR__ >= 4)) && \ - (defined(_LIBCPP_VERSION) || defined(__GLIBCXX__))) || \ - (defined(_MSC_VER) && !defined(__NVCC__)) +#elif (defined(__clang__) && defined(_LIBCPP_VERSION)) || \ + (defined(__clang__) && __clang_major__ >= 15) || \ + (!defined(__clang__) && \ + ((ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(7, 4) && defined(__GLIBCXX__)) || \ + (ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(8, 2) && \ + defined(_LIBCPP_VERSION)))) || \ + (defined(_MSC_VER) && !defined(__NVCC__) && !defined(__clang__)) #define ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE 1 #define ABSL_HAVE_STD_IS_TRIVIALLY_ASSIGNABLE 1 #endif +// ABSL_HAVE_STD_IS_TRIVIALLY_COPYABLE +// +// Checks whether `std::is_trivially_copyable` is supported. +// +// Notes: Clang 15+ with libc++ supports these features, GCC hasn't been tested. +#if defined(ABSL_HAVE_STD_IS_TRIVIALLY_COPYABLE) +#error ABSL_HAVE_STD_IS_TRIVIALLY_COPYABLE cannot be directly set +#elif defined(__clang__) && (__clang_major__ >= 15) +#define ABSL_HAVE_STD_IS_TRIVIALLY_COPYABLE 1 +#endif + // ABSL_HAVE_THREAD_LOCAL // // Checks whether C++11's `thread_local` storage duration specifier is @@ -145,11 +299,9 @@ // * Xcode 9.3 started disallowing `thread_local` for 32-bit iOS simulator // targeting iOS 9.x. // * Xcode 10 moves the deployment target check for iOS < 9.0 to link time -// making __has_feature unreliable there. +// making ABSL_HAVE_FEATURE unreliable there. // -// Otherwise, `__has_feature` is only supported by Clang so it has be inside -// `defined(__APPLE__)` check. -#if __has_feature(cxx_thread_local) && \ +#if ABSL_HAVE_FEATURE(cxx_thread_local) && \ !(TARGET_OS_IPHONE && __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_9_0) #define ABSL_HAVE_THREAD_LOCAL 1 #endif @@ -181,13 +333,6 @@ #endif #endif // defined(__ANDROID__) && defined(__clang__) -// Emscripten doesn't yet support `thread_local` or `__thread`. -// https://github.com/emscripten-core/emscripten/issues/3502 -#if defined(__EMSCRIPTEN__) -#undef ABSL_HAVE_TLS -#undef ABSL_HAVE_THREAD_LOCAL -#endif // defined(__EMSCRIPTEN__) - // ABSL_HAVE_INTRINSIC_INT128 // // Checks whether the __int128 compiler extension for a 128-bit integral type is @@ -233,19 +378,21 @@ // For further details, consult the compiler's documentation. #ifdef ABSL_HAVE_EXCEPTIONS #error ABSL_HAVE_EXCEPTIONS cannot be directly set. - +#elif ABSL_INTERNAL_HAVE_MIN_CLANG_VERSION(3, 6) +// Clang >= 3.6 +#if ABSL_HAVE_FEATURE(cxx_exceptions) +#define ABSL_HAVE_EXCEPTIONS 1 +#endif // ABSL_HAVE_FEATURE(cxx_exceptions) #elif defined(__clang__) -// TODO(calabrese) -// Switch to using __cpp_exceptions when we no longer support versions < 3.6. -// For details on this check, see: -// http://releases.llvm.org/3.6.0/tools/clang/docs/ReleaseNotes.html#the-exceptions-macro -#if defined(__EXCEPTIONS) && __has_feature(cxx_exceptions) +// Clang < 3.6 +// http://releases.llvm.org/3.6.0/tools/clang/docs/ReleaseNotes.html#the-exceptions-macro +#if defined(__EXCEPTIONS) && ABSL_HAVE_FEATURE(cxx_exceptions) #define ABSL_HAVE_EXCEPTIONS 1 -#endif // defined(__EXCEPTIONS) && __has_feature(cxx_exceptions) - +#endif // defined(__EXCEPTIONS) && ABSL_HAVE_FEATURE(cxx_exceptions) // Handle remaining special cases and default to exceptions being supported. -#elif !(defined(__GNUC__) && (__GNUC__ < 5) && !defined(__EXCEPTIONS)) && \ - !(defined(__GNUC__) && (__GNUC__ >= 5) && !defined(__cpp_exceptions)) && \ +#elif !(defined(__GNUC__) && (__GNUC__ < 5) && !defined(__EXCEPTIONS)) && \ + !(ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(5, 0) && \ + !defined(__cpp_exceptions)) && \ !(defined(_MSC_VER) && !defined(_CPPUNWIND)) #define ABSL_HAVE_EXCEPTIONS 1 #endif @@ -277,10 +424,12 @@ // POSIX.1-2001. #ifdef ABSL_HAVE_MMAP #error ABSL_HAVE_MMAP cannot be directly set -#elif defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || \ - defined(__ros__) || defined(__native_client__) || defined(__asmjs__) || \ - defined(__wasm__) || defined(__Fuchsia__) || defined(__sun) || \ - defined(__ASYLO__) +#elif defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || \ + defined(_AIX) || defined(__ros__) || defined(__native_client__) || \ + defined(__asmjs__) || defined(__wasm__) || defined(__Fuchsia__) || \ + defined(__sun) || defined(__ASYLO__) || defined(__myriad2__) || \ + defined(__HAIKU__) || defined(__OpenBSD__) || defined(__NetBSD__) || \ + defined(__QNX__) #define ABSL_HAVE_MMAP 1 #endif @@ -291,10 +440,20 @@ #ifdef ABSL_HAVE_PTHREAD_GETSCHEDPARAM #error ABSL_HAVE_PTHREAD_GETSCHEDPARAM cannot be directly set #elif defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || \ - defined(__ros__) + defined(_AIX) || defined(__ros__) || defined(__OpenBSD__) || \ + defined(__NetBSD__) #define ABSL_HAVE_PTHREAD_GETSCHEDPARAM 1 #endif +// ABSL_HAVE_SCHED_GETCPU +// +// Checks whether sched_getcpu is available. +#ifdef ABSL_HAVE_SCHED_GETCPU +#error ABSL_HAVE_SCHED_GETCPU cannot be directly set +#elif defined(__linux__) +#define ABSL_HAVE_SCHED_GETCPU 1 +#endif + // ABSL_HAVE_SCHED_YIELD // // Checks whether the platform implements sched_yield(2) as defined in @@ -307,7 +466,7 @@ // ABSL_HAVE_SEMAPHORE_H // -// Checks whether the platform supports the header and sem_open(3) +// Checks whether the platform supports the header and sem_init(3) // family of functions as standardized in POSIX.1-2001. // // Note: While Apple provides for both iOS and macOS, it is @@ -334,6 +493,11 @@ #define ABSL_HAVE_ALARM 1 #elif defined(_MSC_VER) // feature tests for Microsoft's library +#elif defined(__MINGW32__) +// mingw32 doesn't provide alarm(2): +// https://osdn.net/projects/mingw/scm/git/mingw-org-wsl/blobs/5.2-trunk/mingwrt/include/unistd.h +// mingw-w64 provides a no-op implementation: +// https://sourceforge.net/p/mingw-w64/mingw-w64/ci/master/tree/mingw-w64-crt/misc/alarm.c #elif defined(__EMSCRIPTEN__) // emscripten doesn't support signals #elif defined(__Fuchsia__) @@ -372,22 +536,41 @@ #error "absl endian detection needs to be set up for your compiler" #endif -// macOS 10.13 and iOS 10.11 don't let you use , , or -// even though the headers exist and are publicly noted to work. See -// https://github.com/abseil/abseil-cpp/issues/207 and +// macOS < 10.13 and iOS < 11 don't let you use , , or +// even though the headers exist and are publicly noted to work, because the +// libc++ shared library shipped on the system doesn't have the requisite +// exported symbols. See https://github.com/abseil/abseil-cpp/issues/207 and // https://developer.apple.com/documentation/xcode_release_notes/xcode_10_release_notes +// // libc++ spells out the availability requirements in the file // llvm-project/libcxx/include/__config via the #define // _LIBCPP_AVAILABILITY_BAD_OPTIONAL_ACCESS. -#if defined(__APPLE__) && defined(_LIBCPP_VERSION) && \ - ((defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && \ - __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ < 101400) || \ - (defined(__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__) && \ - __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ < 120000) || \ - (defined(__ENVIRONMENT_WATCH_OS_VERSION_MIN_REQUIRED__) && \ - __ENVIRONMENT_WATCH_OS_VERSION_MIN_REQUIRED__ < 120000) || \ - (defined(__ENVIRONMENT_TV_OS_VERSION_MIN_REQUIRED__) && \ - __ENVIRONMENT_TV_OS_VERSION_MIN_REQUIRED__ < 50000)) +// +// Unfortunately, Apple initially mis-stated the requirements as macOS < 10.14 +// and iOS < 12 in the libc++ headers. This was corrected by +// https://github.com/llvm/llvm-project/commit/7fb40e1569dd66292b647f4501b85517e9247953 +// which subsequently made it into the XCode 12.5 release. We need to match the +// old (incorrect) conditions when built with old XCode, but can use the +// corrected earlier versions with new XCode. +#if defined(__APPLE__) && defined(_LIBCPP_VERSION) && \ + ((_LIBCPP_VERSION >= 11000 && /* XCode 12.5 or later: */ \ + ((defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && \ + __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ < 101300) || \ + (defined(__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__) && \ + __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ < 110000) || \ + (defined(__ENVIRONMENT_WATCH_OS_VERSION_MIN_REQUIRED__) && \ + __ENVIRONMENT_WATCH_OS_VERSION_MIN_REQUIRED__ < 40000) || \ + (defined(__ENVIRONMENT_TV_OS_VERSION_MIN_REQUIRED__) && \ + __ENVIRONMENT_TV_OS_VERSION_MIN_REQUIRED__ < 110000))) || \ + (_LIBCPP_VERSION < 11000 && /* Pre-XCode 12.5: */ \ + ((defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && \ + __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ < 101400) || \ + (defined(__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__) && \ + __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ < 120000) || \ + (defined(__ENVIRONMENT_WATCH_OS_VERSION_MIN_REQUIRED__) && \ + __ENVIRONMENT_WATCH_OS_VERSION_MIN_REQUIRED__ < 50000) || \ + (defined(__ENVIRONMENT_TV_OS_VERSION_MIN_REQUIRED__) && \ + __ENVIRONMENT_TV_OS_VERSION_MIN_REQUIRED__ < 120000)))) #define ABSL_INTERNAL_APPLE_CXX17_TYPES_UNAVAILABLE 1 #else #define ABSL_INTERNAL_APPLE_CXX17_TYPES_UNAVAILABLE 0 @@ -401,7 +584,7 @@ #endif #ifdef __has_include -#if __has_include() && __cplusplus >= 201703L && \ +#if __has_include() && defined(__cplusplus) && __cplusplus >= 201703L && \ !ABSL_INTERNAL_APPLE_CXX17_TYPES_UNAVAILABLE #define ABSL_HAVE_STD_ANY 1 #endif @@ -415,8 +598,8 @@ #endif #ifdef __has_include -#if __has_include() && __cplusplus >= 201703L && \ - !ABSL_INTERNAL_APPLE_CXX17_TYPES_UNAVAILABLE +#if __has_include() && defined(__cplusplus) && \ + __cplusplus >= 201703L && !ABSL_INTERNAL_APPLE_CXX17_TYPES_UNAVAILABLE #define ABSL_HAVE_STD_OPTIONAL 1 #endif #endif @@ -429,8 +612,8 @@ #endif #ifdef __has_include -#if __has_include() && __cplusplus >= 201703L && \ - !ABSL_INTERNAL_APPLE_CXX17_TYPES_UNAVAILABLE +#if __has_include() && defined(__cplusplus) && \ + __cplusplus >= 201703L && !ABSL_INTERNAL_APPLE_CXX17_TYPES_UNAVAILABLE #define ABSL_HAVE_STD_VARIANT 1 #endif #endif @@ -443,7 +626,8 @@ #endif #ifdef __has_include -#if __has_include() && __cplusplus >= 201703L +#if __has_include() && defined(__cplusplus) && \ + __cplusplus >= 201703L #define ABSL_HAVE_STD_STRING_VIEW 1 #endif #endif @@ -455,14 +639,77 @@ // not correctly set by MSVC, so we use `_MSVC_LANG` to check the language // version. // TODO(zhangxy): fix tests before enabling aliasing for `std::any`. -#if defined(_MSC_VER) && _MSC_VER >= 1910 && \ - ((defined(_MSVC_LANG) && _MSVC_LANG > 201402) || __cplusplus > 201402) +#if defined(_MSC_VER) && _MSC_VER >= 1910 && \ + ((defined(_MSVC_LANG) && _MSVC_LANG > 201402) || \ + (defined(__cplusplus) && __cplusplus > 201402)) // #define ABSL_HAVE_STD_ANY 1 #define ABSL_HAVE_STD_OPTIONAL 1 #define ABSL_HAVE_STD_VARIANT 1 #define ABSL_HAVE_STD_STRING_VIEW 1 #endif +// ABSL_USES_STD_ANY +// +// Indicates whether absl::any is an alias for std::any. +#if !defined(ABSL_OPTION_USE_STD_ANY) +#error options.h is misconfigured. +#elif ABSL_OPTION_USE_STD_ANY == 0 || \ + (ABSL_OPTION_USE_STD_ANY == 2 && !defined(ABSL_HAVE_STD_ANY)) +#undef ABSL_USES_STD_ANY +#elif ABSL_OPTION_USE_STD_ANY == 1 || \ + (ABSL_OPTION_USE_STD_ANY == 2 && defined(ABSL_HAVE_STD_ANY)) +#define ABSL_USES_STD_ANY 1 +#else +#error options.h is misconfigured. +#endif + +// ABSL_USES_STD_OPTIONAL +// +// Indicates whether absl::optional is an alias for std::optional. +#if !defined(ABSL_OPTION_USE_STD_OPTIONAL) +#error options.h is misconfigured. +#elif ABSL_OPTION_USE_STD_OPTIONAL == 0 || \ + (ABSL_OPTION_USE_STD_OPTIONAL == 2 && !defined(ABSL_HAVE_STD_OPTIONAL)) +#undef ABSL_USES_STD_OPTIONAL +#elif ABSL_OPTION_USE_STD_OPTIONAL == 1 || \ + (ABSL_OPTION_USE_STD_OPTIONAL == 2 && defined(ABSL_HAVE_STD_OPTIONAL)) +#define ABSL_USES_STD_OPTIONAL 1 +#else +#error options.h is misconfigured. +#endif + +// ABSL_USES_STD_VARIANT +// +// Indicates whether absl::variant is an alias for std::variant. +#if !defined(ABSL_OPTION_USE_STD_VARIANT) +#error options.h is misconfigured. +#elif ABSL_OPTION_USE_STD_VARIANT == 0 || \ + (ABSL_OPTION_USE_STD_VARIANT == 2 && !defined(ABSL_HAVE_STD_VARIANT)) +#undef ABSL_USES_STD_VARIANT +#elif ABSL_OPTION_USE_STD_VARIANT == 1 || \ + (ABSL_OPTION_USE_STD_VARIANT == 2 && defined(ABSL_HAVE_STD_VARIANT)) +#define ABSL_USES_STD_VARIANT 1 +#else +#error options.h is misconfigured. +#endif + +// ABSL_USES_STD_STRING_VIEW +// +// Indicates whether absl::string_view is an alias for std::string_view. +#if !defined(ABSL_OPTION_USE_STD_STRING_VIEW) +#error options.h is misconfigured. +#elif ABSL_OPTION_USE_STD_STRING_VIEW == 0 || \ + (ABSL_OPTION_USE_STD_STRING_VIEW == 2 && \ + !defined(ABSL_HAVE_STD_STRING_VIEW)) +#undef ABSL_USES_STD_STRING_VIEW +#elif ABSL_OPTION_USE_STD_STRING_VIEW == 1 || \ + (ABSL_OPTION_USE_STD_STRING_VIEW == 2 && \ + defined(ABSL_HAVE_STD_STRING_VIEW)) +#define ABSL_USES_STD_STRING_VIEW 1 +#else +#error options.h is misconfigured. +#endif + // In debug mode, MSVC 2017's std::variant throws a EXCEPTION_ACCESS_VIOLATION // SEH exception from emplace for variant when constructing the // struct can throw. This defeats some of variant_test and @@ -471,4 +718,237 @@ #define ABSL_INTERNAL_MSVC_2017_DBG_MODE #endif +// ABSL_INTERNAL_MANGLED_NS +// ABSL_INTERNAL_MANGLED_BACKREFERENCE +// +// Internal macros for building up mangled names in our internal fork of CCTZ. +// This implementation detail is only needed and provided for the MSVC build. +// +// These macros both expand to string literals. ABSL_INTERNAL_MANGLED_NS is +// the mangled spelling of the `absl` namespace, and +// ABSL_INTERNAL_MANGLED_BACKREFERENCE is a back-reference integer representing +// the proper count to skip past the CCTZ fork namespace names. (This number +// is one larger when there is an inline namespace name to skip.) +#if defined(_MSC_VER) +#if ABSL_OPTION_USE_INLINE_NAMESPACE == 0 +#define ABSL_INTERNAL_MANGLED_NS "absl" +#define ABSL_INTERNAL_MANGLED_BACKREFERENCE "5" +#else +#define ABSL_INTERNAL_MANGLED_NS \ + ABSL_INTERNAL_TOKEN_STR(ABSL_OPTION_INLINE_NAMESPACE_NAME) "@absl" +#define ABSL_INTERNAL_MANGLED_BACKREFERENCE "6" +#endif +#endif + +// ABSL_DLL +// +// When building Abseil as a DLL, this macro expands to `__declspec(dllexport)` +// so we can annotate symbols appropriately as being exported. When used in +// headers consuming a DLL, this macro expands to `__declspec(dllimport)` so +// that consumers know the symbol is defined inside the DLL. In all other cases, +// the macro expands to nothing. +#if defined(_MSC_VER) +#if defined(ABSL_BUILD_DLL) +#define ABSL_DLL __declspec(dllexport) +#elif defined(ABSL_CONSUME_DLL) +#define ABSL_DLL __declspec(dllimport) +#else +#define ABSL_DLL +#endif +#else +#define ABSL_DLL +#endif // defined(_MSC_VER) + +#if defined(_MSC_VER) +#if defined(ABSL_BUILD_TEST_DLL) +#define ABSL_TEST_DLL __declspec(dllexport) +#elif defined(ABSL_CONSUME_TEST_DLL) +#define ABSL_TEST_DLL __declspec(dllimport) +#else +#define ABSL_TEST_DLL +#endif +#else +#define ABSL_TEST_DLL +#endif // defined(_MSC_VER) + +// ABSL_HAVE_MEMORY_SANITIZER +// +// MemorySanitizer (MSan) is a detector of uninitialized reads. It consists of +// a compiler instrumentation module and a run-time library. +#ifdef ABSL_HAVE_MEMORY_SANITIZER +#error "ABSL_HAVE_MEMORY_SANITIZER cannot be directly set." +#elif !defined(__native_client__) && ABSL_HAVE_FEATURE(memory_sanitizer) +#define ABSL_HAVE_MEMORY_SANITIZER 1 +#endif + +// ABSL_HAVE_THREAD_SANITIZER +// +// ThreadSanitizer (TSan) is a fast data race detector. +#ifdef ABSL_HAVE_THREAD_SANITIZER +#error "ABSL_HAVE_THREAD_SANITIZER cannot be directly set." +#elif defined(__SANITIZE_THREAD__) +#define ABSL_HAVE_THREAD_SANITIZER 1 +#elif ABSL_HAVE_FEATURE(thread_sanitizer) +#define ABSL_HAVE_THREAD_SANITIZER 1 +#endif + +// ABSL_HAVE_ADDRESS_SANITIZER +// +// AddressSanitizer (ASan) is a fast memory error detector. +#ifdef ABSL_HAVE_ADDRESS_SANITIZER +#error "ABSL_HAVE_ADDRESS_SANITIZER cannot be directly set." +#elif defined(__SANITIZE_ADDRESS__) +#define ABSL_HAVE_ADDRESS_SANITIZER 1 +#elif ABSL_HAVE_FEATURE(address_sanitizer) +#define ABSL_HAVE_ADDRESS_SANITIZER 1 +#endif + +// ABSL_HAVE_HWADDRESS_SANITIZER +// +// Hardware-Assisted AddressSanitizer (or HWASAN) is even faster than asan +// memory error detector which can use CPU features like ARM TBI, Intel LAM or +// AMD UAI. +#ifdef ABSL_HAVE_HWADDRESS_SANITIZER +#error "ABSL_HAVE_HWADDRESS_SANITIZER cannot be directly set." +#elif defined(__SANITIZE_HWADDRESS__) +#define ABSL_HAVE_HWADDRESS_SANITIZER 1 +#elif ABSL_HAVE_FEATURE(hwaddress_sanitizer) +#define ABSL_HAVE_HWADDRESS_SANITIZER 1 +#endif + +// ABSL_HAVE_LEAK_SANITIZER +// +// LeakSanitizer (or lsan) is a detector of memory leaks. +// https://clang.llvm.org/docs/LeakSanitizer.html +// https://github.com/google/sanitizers/wiki/AddressSanitizerLeakSanitizer +// +// The macro ABSL_HAVE_LEAK_SANITIZER can be used to detect at compile-time +// whether the LeakSanitizer is potentially available. However, just because the +// LeakSanitizer is available does not mean it is active. Use the +// always-available run-time interface in //absl/debugging/leak_check.h for +// interacting with LeakSanitizer. +#ifdef ABSL_HAVE_LEAK_SANITIZER +#error "ABSL_HAVE_LEAK_SANITIZER cannot be directly set." +#elif defined(LEAK_SANITIZER) +// GCC provides no method for detecting the presense of the standalone +// LeakSanitizer (-fsanitize=leak), so GCC users of -fsanitize=leak should also +// use -DLEAK_SANITIZER. +#define ABSL_HAVE_LEAK_SANITIZER 1 +// Clang standalone LeakSanitizer (-fsanitize=leak) +#elif ABSL_HAVE_FEATURE(leak_sanitizer) +#define ABSL_HAVE_LEAK_SANITIZER 1 +#elif defined(ABSL_HAVE_ADDRESS_SANITIZER) +// GCC or Clang using the LeakSanitizer integrated into AddressSanitizer. +#define ABSL_HAVE_LEAK_SANITIZER 1 +#endif + +// ABSL_HAVE_CLASS_TEMPLATE_ARGUMENT_DEDUCTION +// +// Class template argument deduction is a language feature added in C++17. +#ifdef ABSL_HAVE_CLASS_TEMPLATE_ARGUMENT_DEDUCTION +#error "ABSL_HAVE_CLASS_TEMPLATE_ARGUMENT_DEDUCTION cannot be directly set." +#elif defined(__cpp_deduction_guides) +#define ABSL_HAVE_CLASS_TEMPLATE_ARGUMENT_DEDUCTION 1 +#endif + +// ABSL_INTERNAL_NEED_REDUNDANT_CONSTEXPR_DECL +// +// Prior to C++17, static constexpr variables defined in classes required a +// separate definition outside of the class body, for example: +// +// class Foo { +// static constexpr int kBar = 0; +// }; +// constexpr int Foo::kBar; +// +// In C++17, these variables defined in classes are considered inline variables, +// and the extra declaration is redundant. Since some compilers warn on the +// extra declarations, ABSL_INTERNAL_NEED_REDUNDANT_CONSTEXPR_DECL can be used +// conditionally ignore them: +// +// #ifdef ABSL_INTERNAL_NEED_REDUNDANT_CONSTEXPR_DECL +// constexpr int Foo::kBar; +// #endif +#if defined(ABSL_INTERNAL_CPLUSPLUS_LANG) && \ + ABSL_INTERNAL_CPLUSPLUS_LANG < 201703L +#define ABSL_INTERNAL_NEED_REDUNDANT_CONSTEXPR_DECL 1 +#endif + +// `ABSL_INTERNAL_HAS_RTTI` determines whether abseil is being compiled with +// RTTI support. +#ifdef ABSL_INTERNAL_HAS_RTTI +#error ABSL_INTERNAL_HAS_RTTI cannot be directly set +#elif !defined(__GNUC__) || defined(__GXX_RTTI) +#define ABSL_INTERNAL_HAS_RTTI 1 +#endif // !defined(__GNUC__) || defined(__GXX_RTTI) + +// ABSL_INTERNAL_HAVE_SSE is used for compile-time detection of SSE support. +// See https://gcc.gnu.org/onlinedocs/gcc/x86-Options.html for an overview of +// which architectures support the various x86 instruction sets. +#ifdef ABSL_INTERNAL_HAVE_SSE +#error ABSL_INTERNAL_HAVE_SSE cannot be directly set +#elif defined(__SSE__) +#define ABSL_INTERNAL_HAVE_SSE 1 +#elif defined(_M_X64) || (defined(_M_IX86_FP) && _M_IX86_FP >= 1) +// MSVC only defines _M_IX86_FP for x86 32-bit code, and _M_IX86_FP >= 1 +// indicates that at least SSE was targeted with the /arch:SSE option. +// All x86-64 processors support SSE, so support can be assumed. +// https://docs.microsoft.com/en-us/cpp/preprocessor/predefined-macros +#define ABSL_INTERNAL_HAVE_SSE 1 +#endif + +// ABSL_INTERNAL_HAVE_SSE2 is used for compile-time detection of SSE2 support. +// See https://gcc.gnu.org/onlinedocs/gcc/x86-Options.html for an overview of +// which architectures support the various x86 instruction sets. +#ifdef ABSL_INTERNAL_HAVE_SSE2 +#error ABSL_INTERNAL_HAVE_SSE2 cannot be directly set +#elif defined(__SSE2__) +#define ABSL_INTERNAL_HAVE_SSE2 1 +#elif defined(_M_X64) || (defined(_M_IX86_FP) && _M_IX86_FP >= 2) +// MSVC only defines _M_IX86_FP for x86 32-bit code, and _M_IX86_FP >= 2 +// indicates that at least SSE2 was targeted with the /arch:SSE2 option. +// All x86-64 processors support SSE2, so support can be assumed. +// https://docs.microsoft.com/en-us/cpp/preprocessor/predefined-macros +#define ABSL_INTERNAL_HAVE_SSE2 1 +#endif + +// ABSL_INTERNAL_HAVE_SSSE3 is used for compile-time detection of SSSE3 support. +// See https://gcc.gnu.org/onlinedocs/gcc/x86-Options.html for an overview of +// which architectures support the various x86 instruction sets. +// +// MSVC does not have a mode that targets SSSE3 at compile-time. To use SSSE3 +// with MSVC requires either assuming that the code will only every run on CPUs +// that support SSSE3, otherwise __cpuid() can be used to detect support at +// runtime and fallback to a non-SSSE3 implementation when SSSE3 is unsupported +// by the CPU. +#ifdef ABSL_INTERNAL_HAVE_SSSE3 +#error ABSL_INTERNAL_HAVE_SSSE3 cannot be directly set +#elif defined(__SSSE3__) +#define ABSL_INTERNAL_HAVE_SSSE3 1 +#endif + +// ABSL_INTERNAL_HAVE_ARM_NEON is used for compile-time detection of NEON (ARM +// SIMD). +// +// If __CUDA_ARCH__ is defined, then we are compiling CUDA code in device mode. +// In device mode, NEON intrinsics are not available, regardless of host +// platform. +// https://llvm.org/docs/CompileCudaWithLLVM.html#detecting-clang-vs-nvcc-from-code +#ifdef ABSL_INTERNAL_HAVE_ARM_NEON +#error ABSL_INTERNAL_HAVE_ARM_NEON cannot be directly set +#elif defined(__ARM_NEON) && !defined(__CUDA_ARCH__) +#define ABSL_INTERNAL_HAVE_ARM_NEON 1 +#endif + +// ABSL_HAVE_CONSTANT_EVALUATED is used for compile-time detection of +// constant evaluation support through `absl::is_constant_evaluated`. +#ifdef ABSL_HAVE_CONSTANT_EVALUATED +#error ABSL_HAVE_CONSTANT_EVALUATED cannot be directly set +#endif +#ifdef __cpp_lib_is_constant_evaluated +#define ABSL_HAVE_CONSTANT_EVALUATED 1 +#elif ABSL_HAVE_BUILTIN(__builtin_is_constant_evaluated) +#define ABSL_HAVE_CONSTANT_EVALUATED 1 +#endif + #endif // ABSL_BASE_CONFIG_H_ diff --git a/third_party/absl/absl/base/internal/atomic_hook.h b/third_party/absl/absl/base/internal/atomic_hook.h index 803e90597..ae21cd7fe 100644 --- a/third_party/absl/absl/base/internal/atomic_hook.h +++ b/third_party/absl/absl/base/internal/atomic_hook.h @@ -11,7 +11,6 @@ // 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. -// #ifndef ABSL_BASE_INTERNAL_ATOMIC_HOOK_H_ #define ABSL_BASE_INTERNAL_ATOMIC_HOOK_H_ @@ -21,28 +20,51 @@ #include #include -#ifdef _MSC_FULL_VER +#include "absl/base/attributes.h" +#include "absl/base/config.h" + +#if defined(_MSC_VER) && !defined(__clang__) +#define ABSL_HAVE_WORKING_CONSTEXPR_STATIC_INIT 0 +#else +#define ABSL_HAVE_WORKING_CONSTEXPR_STATIC_INIT 1 +#endif + +#if defined(_MSC_VER) #define ABSL_HAVE_WORKING_ATOMIC_POINTER 0 #else #define ABSL_HAVE_WORKING_ATOMIC_POINTER 1 #endif namespace absl { +ABSL_NAMESPACE_BEGIN namespace base_internal { template class AtomicHook; -// AtomicHook is a helper class, templatized on a raw function pointer type, for -// implementing Abseil customization hooks. It is a callable object that -// dispatches to the registered hook. +// To workaround AtomicHook not being constant-initializable on some platforms, +// prefer to annotate instances with `ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES` +// instead of `ABSL_CONST_INIT`. +#if ABSL_HAVE_WORKING_CONSTEXPR_STATIC_INIT +#define ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES ABSL_CONST_INIT +#else +#define ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES +#endif + +// `AtomicHook` is a helper class, templatized on a raw function pointer type, +// for implementing Abseil customization hooks. It is a callable object that +// dispatches to the registered hook. Objects of type `AtomicHook` must have +// static or thread storage duration. // // A default constructed object performs a no-op (and returns a default // constructed object) if no hook has been registered. // -// Hooks can be pre-registered via constant initialization, for example, -// ABSL_CONST_INIT static AtomicHook my_hook(DefaultAction); -// and then changed at runtime via a call to Store(). +// Hooks can be pre-registered via constant initialization, for example: +// +// ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES static AtomicHook +// my_hook(DefaultAction); +// +// and then changed at runtime via a call to `Store()`. // // Reads and writes guarantee memory_order_acquire/memory_order_release // semantics. @@ -57,12 +79,23 @@ class AtomicHook { // Constructs an object that by default dispatches to/returns the // pre-registered default_fn when no hook has been registered at runtime. -#if ABSL_HAVE_WORKING_ATOMIC_POINTER +#if ABSL_HAVE_WORKING_ATOMIC_POINTER && ABSL_HAVE_WORKING_CONSTEXPR_STATIC_INIT explicit constexpr AtomicHook(FnPtr default_fn) : hook_(default_fn), default_fn_(default_fn) {} -#else +#elif ABSL_HAVE_WORKING_CONSTEXPR_STATIC_INIT explicit constexpr AtomicHook(FnPtr default_fn) : hook_(kUninitialized), default_fn_(default_fn) {} +#else + // As of January 2020, on all known versions of MSVC this constructor runs in + // the global constructor sequence. If `Store()` is called by a dynamic + // initializer, we want to preserve the value, even if this constructor runs + // after the call to `Store()`. If not, `hook_` will be + // zero-initialized by the linker and we have no need to set it. + // https://developercommunity.visualstudio.com/content/problem/336946/class-with-constexpr-constructor-not-using-static.html + explicit constexpr AtomicHook(FnPtr default_fn) + : /* hook_(deliberately omitted), */ default_fn_(default_fn) { + static_assert(kUninitialized == 0, "here we rely on zero-initialization"); + } #endif // Stores the provided function pointer as the value for this hook. @@ -158,8 +191,10 @@ class AtomicHook { }; #undef ABSL_HAVE_WORKING_ATOMIC_POINTER +#undef ABSL_HAVE_WORKING_CONSTEXPR_STATIC_INIT } // namespace base_internal +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_BASE_INTERNAL_ATOMIC_HOOK_H_ diff --git a/third_party/absl/absl/base/internal/endian.h b/third_party/absl/absl/base/internal/endian.h new file mode 100644 index 000000000..50747d75e --- /dev/null +++ b/third_party/absl/absl/base/internal/endian.h @@ -0,0 +1,282 @@ +// Copyright 2017 The Abseil Authors. +// +// 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 +// +// https://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. +// + +#ifndef ABSL_BASE_INTERNAL_ENDIAN_H_ +#define ABSL_BASE_INTERNAL_ENDIAN_H_ + +#include +#include + +#include "absl/base/casts.h" +#include "absl/base/config.h" +#include "absl/base/internal/unaligned_access.h" +#include "absl/base/port.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN + +inline uint64_t gbswap_64(uint64_t host_int) { +#if ABSL_HAVE_BUILTIN(__builtin_bswap64) || defined(__GNUC__) + return __builtin_bswap64(host_int); +#elif defined(_MSC_VER) + return _byteswap_uint64(host_int); +#else + return (((host_int & uint64_t{0xFF}) << 56) | + ((host_int & uint64_t{0xFF00}) << 40) | + ((host_int & uint64_t{0xFF0000}) << 24) | + ((host_int & uint64_t{0xFF000000}) << 8) | + ((host_int & uint64_t{0xFF00000000}) >> 8) | + ((host_int & uint64_t{0xFF0000000000}) >> 24) | + ((host_int & uint64_t{0xFF000000000000}) >> 40) | + ((host_int & uint64_t{0xFF00000000000000}) >> 56)); +#endif +} + +inline uint32_t gbswap_32(uint32_t host_int) { +#if ABSL_HAVE_BUILTIN(__builtin_bswap32) || defined(__GNUC__) + return __builtin_bswap32(host_int); +#elif defined(_MSC_VER) + return _byteswap_ulong(host_int); +#else + return (((host_int & uint32_t{0xFF}) << 24) | + ((host_int & uint32_t{0xFF00}) << 8) | + ((host_int & uint32_t{0xFF0000}) >> 8) | + ((host_int & uint32_t{0xFF000000}) >> 24)); +#endif +} + +inline uint16_t gbswap_16(uint16_t host_int) { +#if ABSL_HAVE_BUILTIN(__builtin_bswap16) || defined(__GNUC__) + return __builtin_bswap16(host_int); +#elif defined(_MSC_VER) + return _byteswap_ushort(host_int); +#else + return (((host_int & uint16_t{0xFF}) << 8) | + ((host_int & uint16_t{0xFF00}) >> 8)); +#endif +} + +#ifdef ABSL_IS_LITTLE_ENDIAN + +// Portable definitions for htonl (host-to-network) and friends on little-endian +// architectures. +inline uint16_t ghtons(uint16_t x) { return gbswap_16(x); } +inline uint32_t ghtonl(uint32_t x) { return gbswap_32(x); } +inline uint64_t ghtonll(uint64_t x) { return gbswap_64(x); } + +#elif defined ABSL_IS_BIG_ENDIAN + +// Portable definitions for htonl (host-to-network) etc on big-endian +// architectures. These definitions are simpler since the host byte order is the +// same as network byte order. +inline uint16_t ghtons(uint16_t x) { return x; } +inline uint32_t ghtonl(uint32_t x) { return x; } +inline uint64_t ghtonll(uint64_t x) { return x; } + +#else +#error \ + "Unsupported byte order: Either ABSL_IS_BIG_ENDIAN or " \ + "ABSL_IS_LITTLE_ENDIAN must be defined" +#endif // byte order + +inline uint16_t gntohs(uint16_t x) { return ghtons(x); } +inline uint32_t gntohl(uint32_t x) { return ghtonl(x); } +inline uint64_t gntohll(uint64_t x) { return ghtonll(x); } + +// Utilities to convert numbers between the current hosts's native byte +// order and little-endian byte order +// +// Load/Store methods are alignment safe +namespace little_endian { +// Conversion functions. +#ifdef ABSL_IS_LITTLE_ENDIAN + +inline uint16_t FromHost16(uint16_t x) { return x; } +inline uint16_t ToHost16(uint16_t x) { return x; } + +inline uint32_t FromHost32(uint32_t x) { return x; } +inline uint32_t ToHost32(uint32_t x) { return x; } + +inline uint64_t FromHost64(uint64_t x) { return x; } +inline uint64_t ToHost64(uint64_t x) { return x; } + +inline constexpr bool IsLittleEndian() { return true; } + +#elif defined ABSL_IS_BIG_ENDIAN + +inline uint16_t FromHost16(uint16_t x) { return gbswap_16(x); } +inline uint16_t ToHost16(uint16_t x) { return gbswap_16(x); } + +inline uint32_t FromHost32(uint32_t x) { return gbswap_32(x); } +inline uint32_t ToHost32(uint32_t x) { return gbswap_32(x); } + +inline uint64_t FromHost64(uint64_t x) { return gbswap_64(x); } +inline uint64_t ToHost64(uint64_t x) { return gbswap_64(x); } + +inline constexpr bool IsLittleEndian() { return false; } + +#endif /* ENDIAN */ + +inline uint8_t FromHost(uint8_t x) { return x; } +inline uint16_t FromHost(uint16_t x) { return FromHost16(x); } +inline uint32_t FromHost(uint32_t x) { return FromHost32(x); } +inline uint64_t FromHost(uint64_t x) { return FromHost64(x); } +inline uint8_t ToHost(uint8_t x) { return x; } +inline uint16_t ToHost(uint16_t x) { return ToHost16(x); } +inline uint32_t ToHost(uint32_t x) { return ToHost32(x); } +inline uint64_t ToHost(uint64_t x) { return ToHost64(x); } + +inline int8_t FromHost(int8_t x) { return x; } +inline int16_t FromHost(int16_t x) { + return bit_cast(FromHost16(bit_cast(x))); +} +inline int32_t FromHost(int32_t x) { + return bit_cast(FromHost32(bit_cast(x))); +} +inline int64_t FromHost(int64_t x) { + return bit_cast(FromHost64(bit_cast(x))); +} +inline int8_t ToHost(int8_t x) { return x; } +inline int16_t ToHost(int16_t x) { + return bit_cast(ToHost16(bit_cast(x))); +} +inline int32_t ToHost(int32_t x) { + return bit_cast(ToHost32(bit_cast(x))); +} +inline int64_t ToHost(int64_t x) { + return bit_cast(ToHost64(bit_cast(x))); +} + +// Functions to do unaligned loads and stores in little-endian order. +inline uint16_t Load16(const void *p) { + return ToHost16(ABSL_INTERNAL_UNALIGNED_LOAD16(p)); +} + +inline void Store16(void *p, uint16_t v) { + ABSL_INTERNAL_UNALIGNED_STORE16(p, FromHost16(v)); +} + +inline uint32_t Load32(const void *p) { + return ToHost32(ABSL_INTERNAL_UNALIGNED_LOAD32(p)); +} + +inline void Store32(void *p, uint32_t v) { + ABSL_INTERNAL_UNALIGNED_STORE32(p, FromHost32(v)); +} + +inline uint64_t Load64(const void *p) { + return ToHost64(ABSL_INTERNAL_UNALIGNED_LOAD64(p)); +} + +inline void Store64(void *p, uint64_t v) { + ABSL_INTERNAL_UNALIGNED_STORE64(p, FromHost64(v)); +} + +} // namespace little_endian + +// Utilities to convert numbers between the current hosts's native byte +// order and big-endian byte order (same as network byte order) +// +// Load/Store methods are alignment safe +namespace big_endian { +#ifdef ABSL_IS_LITTLE_ENDIAN + +inline uint16_t FromHost16(uint16_t x) { return gbswap_16(x); } +inline uint16_t ToHost16(uint16_t x) { return gbswap_16(x); } + +inline uint32_t FromHost32(uint32_t x) { return gbswap_32(x); } +inline uint32_t ToHost32(uint32_t x) { return gbswap_32(x); } + +inline uint64_t FromHost64(uint64_t x) { return gbswap_64(x); } +inline uint64_t ToHost64(uint64_t x) { return gbswap_64(x); } + +inline constexpr bool IsLittleEndian() { return true; } + +#elif defined ABSL_IS_BIG_ENDIAN + +inline uint16_t FromHost16(uint16_t x) { return x; } +inline uint16_t ToHost16(uint16_t x) { return x; } + +inline uint32_t FromHost32(uint32_t x) { return x; } +inline uint32_t ToHost32(uint32_t x) { return x; } + +inline uint64_t FromHost64(uint64_t x) { return x; } +inline uint64_t ToHost64(uint64_t x) { return x; } + +inline constexpr bool IsLittleEndian() { return false; } + +#endif /* ENDIAN */ + +inline uint8_t FromHost(uint8_t x) { return x; } +inline uint16_t FromHost(uint16_t x) { return FromHost16(x); } +inline uint32_t FromHost(uint32_t x) { return FromHost32(x); } +inline uint64_t FromHost(uint64_t x) { return FromHost64(x); } +inline uint8_t ToHost(uint8_t x) { return x; } +inline uint16_t ToHost(uint16_t x) { return ToHost16(x); } +inline uint32_t ToHost(uint32_t x) { return ToHost32(x); } +inline uint64_t ToHost(uint64_t x) { return ToHost64(x); } + +inline int8_t FromHost(int8_t x) { return x; } +inline int16_t FromHost(int16_t x) { + return bit_cast(FromHost16(bit_cast(x))); +} +inline int32_t FromHost(int32_t x) { + return bit_cast(FromHost32(bit_cast(x))); +} +inline int64_t FromHost(int64_t x) { + return bit_cast(FromHost64(bit_cast(x))); +} +inline int8_t ToHost(int8_t x) { return x; } +inline int16_t ToHost(int16_t x) { + return bit_cast(ToHost16(bit_cast(x))); +} +inline int32_t ToHost(int32_t x) { + return bit_cast(ToHost32(bit_cast(x))); +} +inline int64_t ToHost(int64_t x) { + return bit_cast(ToHost64(bit_cast(x))); +} + +// Functions to do unaligned loads and stores in big-endian order. +inline uint16_t Load16(const void *p) { + return ToHost16(ABSL_INTERNAL_UNALIGNED_LOAD16(p)); +} + +inline void Store16(void *p, uint16_t v) { + ABSL_INTERNAL_UNALIGNED_STORE16(p, FromHost16(v)); +} + +inline uint32_t Load32(const void *p) { + return ToHost32(ABSL_INTERNAL_UNALIGNED_LOAD32(p)); +} + +inline void Store32(void *p, uint32_t v) { + ABSL_INTERNAL_UNALIGNED_STORE32(p, FromHost32(v)); +} + +inline uint64_t Load64(const void *p) { + return ToHost64(ABSL_INTERNAL_UNALIGNED_LOAD64(p)); +} + +inline void Store64(void *p, uint64_t v) { + ABSL_INTERNAL_UNALIGNED_STORE64(p, FromHost64(v)); +} + +} // namespace big_endian + +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_BASE_INTERNAL_ENDIAN_H_ diff --git a/third_party/absl/absl/base/internal/errno_saver.h b/third_party/absl/absl/base/internal/errno_saver.h new file mode 100644 index 000000000..251de510f --- /dev/null +++ b/third_party/absl/absl/base/internal/errno_saver.h @@ -0,0 +1,43 @@ +// Copyright 2017 The Abseil Authors. +// +// 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 +// +// https://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. + +#ifndef ABSL_BASE_INTERNAL_ERRNO_SAVER_H_ +#define ABSL_BASE_INTERNAL_ERRNO_SAVER_H_ + +#include + +#include "absl/base/config.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace base_internal { + +// `ErrnoSaver` captures the value of `errno` upon construction and restores it +// upon deletion. It is used in low-level code and must be super fast. Do not +// add instrumentation, even in debug modes. +class ErrnoSaver { + public: + ErrnoSaver() : saved_errno_(errno) {} + ~ErrnoSaver() { errno = saved_errno_; } + int operator()() const { return saved_errno_; } + + private: + const int saved_errno_; +}; + +} // namespace base_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_BASE_INTERNAL_ERRNO_SAVER_H_ diff --git a/third_party/absl/absl/base/internal/identity.h b/third_party/absl/absl/base/internal/identity.h index 086447c60..a3154ed7b 100644 --- a/third_party/absl/absl/base/internal/identity.h +++ b/third_party/absl/absl/base/internal/identity.h @@ -16,7 +16,10 @@ #ifndef ABSL_BASE_INTERNAL_IDENTITY_H_ #define ABSL_BASE_INTERNAL_IDENTITY_H_ +#include "absl/base/config.h" + namespace absl { +ABSL_NAMESPACE_BEGIN namespace internal { template @@ -28,6 +31,7 @@ template using identity_t = typename identity::type; } // namespace internal +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_BASE_INTERNAL_IDENTITY_H_ diff --git a/third_party/absl/absl/base/internal/inline_variable.h b/third_party/absl/absl/base/internal/inline_variable.h new file mode 100644 index 000000000..df933faff --- /dev/null +++ b/third_party/absl/absl/base/internal/inline_variable.h @@ -0,0 +1,107 @@ +// Copyright 2017 The Abseil Authors. +// +// 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 +// +// https://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. + +#ifndef ABSL_BASE_INTERNAL_INLINE_VARIABLE_H_ +#define ABSL_BASE_INTERNAL_INLINE_VARIABLE_H_ + +#include + +#include "absl/base/internal/identity.h" + +// File: +// This file define a macro that allows the creation of or emulation of C++17 +// inline variables based on whether or not the feature is supported. + +//////////////////////////////////////////////////////////////////////////////// +// Macro: ABSL_INTERNAL_INLINE_CONSTEXPR(type, name, init) +// +// Description: +// Expands to the equivalent of an inline constexpr instance of the specified +// `type` and `name`, initialized to the value `init`. If the compiler being +// used is detected as supporting actual inline variables as a language +// feature, then the macro expands to an actual inline variable definition. +// +// Requires: +// `type` is a type that is usable in an extern variable declaration. +// +// Requires: `name` is a valid identifier +// +// Requires: +// `init` is an expression that can be used in the following definition: +// constexpr type name = init; +// +// Usage: +// +// // Equivalent to: `inline constexpr size_t variant_npos = -1;` +// ABSL_INTERNAL_INLINE_CONSTEXPR(size_t, variant_npos, -1); +// +// Differences in implementation: +// For a direct, language-level inline variable, decltype(name) will be the +// type that was specified along with const qualification, whereas for +// emulated inline variables, decltype(name) may be different (in practice +// it will likely be a reference type). +//////////////////////////////////////////////////////////////////////////////// + +#ifdef __cpp_inline_variables + +// Clang's -Wmissing-variable-declarations option erroneously warned that +// inline constexpr objects need to be pre-declared. This has now been fixed, +// but we will need to support this workaround for people building with older +// versions of clang. +// +// Bug: https://bugs.llvm.org/show_bug.cgi?id=35862 +// +// Note: +// identity_t is used here so that the const and name are in the +// appropriate place for pointer types, reference types, function pointer +// types, etc.. +#if defined(__clang__) +#define ABSL_INTERNAL_EXTERN_DECL(type, name) \ + extern const ::absl::internal::identity_t name; +#else // Otherwise, just define the macro to do nothing. +#define ABSL_INTERNAL_EXTERN_DECL(type, name) +#endif // defined(__clang__) + +// See above comment at top of file for details. +#define ABSL_INTERNAL_INLINE_CONSTEXPR(type, name, init) \ + ABSL_INTERNAL_EXTERN_DECL(type, name) \ + inline constexpr ::absl::internal::identity_t name = init + +#else + +// See above comment at top of file for details. +// +// Note: +// identity_t is used here so that the const and name are in the +// appropriate place for pointer types, reference types, function pointer +// types, etc.. +#define ABSL_INTERNAL_INLINE_CONSTEXPR(var_type, name, init) \ + template \ + struct AbslInternalInlineVariableHolder##name { \ + static constexpr ::absl::internal::identity_t kInstance = init; \ + }; \ + \ + template \ + constexpr ::absl::internal::identity_t \ + AbslInternalInlineVariableHolder##name::kInstance; \ + \ + static constexpr const ::absl::internal::identity_t& \ + name = /* NOLINT */ \ + AbslInternalInlineVariableHolder##name<>::kInstance; \ + static_assert(sizeof(void (*)(decltype(name))) != 0, \ + "Silence unused variable warnings.") + +#endif // __cpp_inline_variables + +#endif // ABSL_BASE_INTERNAL_INLINE_VARIABLE_H_ diff --git a/third_party/absl/absl/base/internal/invoke.h b/third_party/absl/absl/base/internal/invoke.h new file mode 100644 index 000000000..643c2a42f --- /dev/null +++ b/third_party/absl/absl/base/internal/invoke.h @@ -0,0 +1,241 @@ +// Copyright 2017 The Abseil Authors. +// +// 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 +// +// https://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. +// +// absl::base_internal::invoke(f, args...) is an implementation of +// INVOKE(f, args...) from section [func.require] of the C++ standard. +// When compiled as C++17 and later versions, it is implemented as an alias of +// std::invoke. +// +// [func.require] +// Define INVOKE (f, t1, t2, ..., tN) as follows: +// 1. (t1.*f)(t2, ..., tN) when f is a pointer to a member function of a class T +// and t1 is an object of type T or a reference to an object of type T or a +// reference to an object of a type derived from T; +// 2. ((*t1).*f)(t2, ..., tN) when f is a pointer to a member function of a +// class T and t1 is not one of the types described in the previous item; +// 3. t1.*f when N == 1 and f is a pointer to member data of a class T and t1 is +// an object of type T or a reference to an object of type T or a reference +// to an object of a type derived from T; +// 4. (*t1).*f when N == 1 and f is a pointer to member data of a class T and t1 +// is not one of the types described in the previous item; +// 5. f(t1, t2, ..., tN) in all other cases. +// +// The implementation is SFINAE-friendly: substitution failure within invoke() +// isn't an error. + +#ifndef ABSL_BASE_INTERNAL_INVOKE_H_ +#define ABSL_BASE_INTERNAL_INVOKE_H_ + +#include "absl/base/config.h" + +#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 201703L + +#include + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace base_internal { + +using std::invoke; +using std::invoke_result_t; +using std::is_invocable_r; + +} // namespace base_internal +ABSL_NAMESPACE_END +} // namespace absl + +#else // ABSL_INTERNAL_CPLUSPLUS_LANG >= 201703L + +#include +#include +#include + +#include "absl/meta/type_traits.h" + +// The following code is internal implementation detail. See the comment at the +// top of this file for the API documentation. + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace base_internal { + +// The five classes below each implement one of the clauses from the definition +// of INVOKE. The inner class template Accept checks whether the +// clause is applicable; static function template Invoke(f, args...) does the +// invocation. +// +// By separating the clause selection logic from invocation we make sure that +// Invoke() does exactly what the standard says. + +template +struct StrippedAccept { + template + struct Accept : Derived::template AcceptImpl::type>::type...> {}; +}; + +// (t1.*f)(t2, ..., tN) when f is a pointer to a member function of a class T +// and t1 is an object of type T or a reference to an object of type T or a +// reference to an object of a type derived from T. +struct MemFunAndRef : StrippedAccept { + template + struct AcceptImpl : std::false_type {}; + + template + struct AcceptImpl + : std::integral_constant::value && + absl::is_function::value> { + }; + + template + static decltype((std::declval().* + std::declval())(std::declval()...)) + Invoke(MemFun&& mem_fun, Obj&& obj, Args&&... args) { +// Ignore bogus GCC warnings on this line. +// See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=101436 for similar example. +#if ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(11, 0) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Warray-bounds" +#pragma GCC diagnostic ignored "-Wmaybe-uninitialized" +#endif + return (std::forward(obj).* + std::forward(mem_fun))(std::forward(args)...); +#if ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(11, 0) +#pragma GCC diagnostic pop +#endif + } +}; + +// ((*t1).*f)(t2, ..., tN) when f is a pointer to a member function of a +// class T and t1 is not one of the types described in the previous item. +struct MemFunAndPtr : StrippedAccept { + template + struct AcceptImpl : std::false_type {}; + + template + struct AcceptImpl + : std::integral_constant::value && + absl::is_function::value> { + }; + + template + static decltype(((*std::declval()).* + std::declval())(std::declval()...)) + Invoke(MemFun&& mem_fun, Ptr&& ptr, Args&&... args) { + return ((*std::forward(ptr)).* + std::forward(mem_fun))(std::forward(args)...); + } +}; + +// t1.*f when N == 1 and f is a pointer to member data of a class T and t1 is +// an object of type T or a reference to an object of type T or a reference +// to an object of a type derived from T. +struct DataMemAndRef : StrippedAccept { + template + struct AcceptImpl : std::false_type {}; + + template + struct AcceptImpl + : std::integral_constant::value && + !absl::is_function::value> {}; + + template + static decltype(std::declval().*std::declval()) Invoke( + DataMem&& data_mem, Ref&& ref) { + return std::forward(ref).*std::forward(data_mem); + } +}; + +// (*t1).*f when N == 1 and f is a pointer to member data of a class T and t1 +// is not one of the types described in the previous item. +struct DataMemAndPtr : StrippedAccept { + template + struct AcceptImpl : std::false_type {}; + + template + struct AcceptImpl + : std::integral_constant::value && + !absl::is_function::value> {}; + + template + static decltype((*std::declval()).*std::declval()) Invoke( + DataMem&& data_mem, Ptr&& ptr) { + return (*std::forward(ptr)).*std::forward(data_mem); + } +}; + +// f(t1, t2, ..., tN) in all other cases. +struct Callable { + // Callable doesn't have Accept because it's the last clause that gets picked + // when none of the previous clauses are applicable. + template + static decltype(std::declval()(std::declval()...)) Invoke( + F&& f, Args&&... args) { + return std::forward(f)(std::forward(args)...); + } +}; + +// Resolves to the first matching clause. +template +struct Invoker { + typedef typename std::conditional< + MemFunAndRef::Accept::value, MemFunAndRef, + typename std::conditional< + MemFunAndPtr::Accept::value, MemFunAndPtr, + typename std::conditional< + DataMemAndRef::Accept::value, DataMemAndRef, + typename std::conditional::value, + DataMemAndPtr, Callable>::type>::type>:: + type>::type type; +}; + +// The result type of Invoke. +template +using invoke_result_t = decltype(Invoker::type::Invoke( + std::declval(), std::declval()...)); + +// Invoke(f, args...) is an implementation of INVOKE(f, args...) from section +// [func.require] of the C++ standard. +template +invoke_result_t invoke(F&& f, Args&&... args) { + return Invoker::type::Invoke(std::forward(f), + std::forward(args)...); +} + +template +struct IsInvocableRImpl : std::false_type {}; + +template +struct IsInvocableRImpl< + absl::void_t >, R, F, + Args...> + : std::integral_constant< + bool, + std::is_convertible, + R>::value || + std::is_void::value> {}; + +// Type trait whose member `value` is true if invoking `F` with `Args` is valid, +// and either the return type is convertible to `R`, or `R` is void. +// C++11-compatible version of `std::is_invocable_r`. +template +using is_invocable_r = IsInvocableRImpl; + +} // namespace base_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_INTERNAL_CPLUSPLUS_LANG >= 201703L + +#endif // ABSL_BASE_INTERNAL_INVOKE_H_ diff --git a/third_party/absl/absl/base/internal/raw_logging.cc b/third_party/absl/absl/base/internal/raw_logging.cc index dcecaff2e..6273e8471 100644 --- a/third_party/absl/absl/base/internal/raw_logging.cc +++ b/third_party/absl/absl/base/internal/raw_logging.cc @@ -14,15 +14,17 @@ #include "absl/base/internal/raw_logging.h" -#include #include +#include #include #include #include +#include #include "absl/base/attributes.h" #include "absl/base/config.h" #include "absl/base/internal/atomic_hook.h" +#include "absl/base/internal/errno_saver.h" #include "absl/base/log_severity.h" // We know how to perform low-level writes to stderr in POSIX and Windows. For @@ -36,10 +38,10 @@ // This preprocessor token is also defined in raw_io.cc. If you need to copy // this, consider moving both to config.h instead. #if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || \ - defined(__Fuchsia__) || defined(__native_client__) || \ - defined(__EMSCRIPTEN__) -#include + defined(__Fuchsia__) || defined(__native_client__) || \ + defined(__OpenBSD__) || defined(__EMSCRIPTEN__) || defined(__ASYLO__) +#include #define ABSL_HAVE_POSIX_WRITE 1 #define ABSL_LOW_LEVEL_WRITE_SUPPORTED 1 @@ -50,7 +52,8 @@ // ABSL_HAVE_SYSCALL_WRITE is defined when the platform provides the syscall // syscall(SYS_write, /*int*/ fd, /*char* */ buf, /*size_t*/ len); // for low level operations that want to avoid libc. -#if (defined(__linux__) || defined(__FreeBSD__)) && !defined(__ANDROID__) +#if (defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__)) && \ + !defined(__ANDROID__) #include #define ABSL_HAVE_SYSCALL_WRITE 1 #define ABSL_LOW_LEVEL_WRITE_SUPPORTED 1 @@ -67,34 +70,35 @@ #undef ABSL_HAVE_RAW_IO #endif -// TODO(gfalcon): We want raw-logging to work on as many platforms as possible. -// Explicitly #error out when not ABSL_LOW_LEVEL_WRITE_SUPPORTED, except for a -// whitelisted set of platforms for which we expect not to be able to raw log. +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace raw_log_internal { +namespace { -ABSL_CONST_INIT static absl::base_internal::AtomicHook< - absl::raw_logging_internal::LogPrefixHook> log_prefix_hook; -ABSL_CONST_INIT static absl::base_internal::AtomicHook< - absl::raw_logging_internal::AbortHook> abort_hook; +// TODO(gfalcon): We want raw-logging to work on as many platforms as possible. +// Explicitly `#error` out when not `ABSL_LOW_LEVEL_WRITE_SUPPORTED`, except for +// a selected set of platforms for which we expect not to be able to raw log. #ifdef ABSL_LOW_LEVEL_WRITE_SUPPORTED -static const char kTruncated[] = " ... (message truncated)\n"; +constexpr char kTruncated[] = " ... (message truncated)\n"; // sprintf the format to the buffer, adjusting *buf and *size to reflect the // consumed bytes, and return whether the message fit without truncation. If // truncation occurred, if possible leave room in the buffer for the message // kTruncated[]. -inline static bool VADoRawLog(char** buf, int* size, const char* format, - va_list ap) ABSL_PRINTF_ATTRIBUTE(3, 0); -inline static bool VADoRawLog(char** buf, int* size, - const char* format, va_list ap) { - int n = vsnprintf(*buf, *size, format, ap); +bool VADoRawLog(char** buf, int* size, const char* format, va_list ap) + ABSL_PRINTF_ATTRIBUTE(3, 0); +bool VADoRawLog(char** buf, int* size, const char* format, va_list ap) { + if (*size < 0) + return false; + int n = vsnprintf(*buf, static_cast(*size), format, ap); bool result = true; if (n < 0 || n > *size) { result = false; if (static_cast(*size) > sizeof(kTruncated)) { - n = *size - sizeof(kTruncated); // room for truncation message + n = *size - static_cast(sizeof(kTruncated)); } else { - n = 0; // no room for truncation message + n = 0; // no room for truncation message } } *size -= n; @@ -103,9 +107,7 @@ inline static bool VADoRawLog(char** buf, int* size, } #endif // ABSL_LOW_LEVEL_WRITE_SUPPORTED -static constexpr int kLogBufSize = 3000; - -namespace { +constexpr int kLogBufSize = 3000; // CAVEAT: vsnprintf called from *DoRawLog below has some (exotic) code paths // that invoke malloc() and getenv() that might acquire some locks. @@ -116,9 +118,11 @@ namespace { bool DoRawLog(char** buf, int* size, const char* format, ...) ABSL_PRINTF_ATTRIBUTE(3, 4); bool DoRawLog(char** buf, int* size, const char* format, ...) { + if (*size < 0) + return false; va_list ap; va_start(ap, format); - int n = vsnprintf(*buf, *size, format, ap); + int n = vsnprintf(*buf, static_cast(*size), format, ap); va_end(ap); if (n < 0 || n > *size) return false; *size -= n; @@ -126,10 +130,22 @@ bool DoRawLog(char** buf, int* size, const char* format, ...) { return true; } +bool DefaultLogFilterAndPrefix(absl::LogSeverity, const char* file, int line, + char** buf, int* buf_size) { + DoRawLog(buf, buf_size, "[%s : %d] RAW: ", file, line); + return true; +} + +ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES +absl::base_internal::AtomicHook + log_filter_and_prefix_hook(DefaultLogFilterAndPrefix); +ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES +absl::base_internal::AtomicHook abort_hook; + void RawLogVA(absl::LogSeverity severity, const char* file, int line, - const char* format, va_list ap) ABSL_PRINTF_ATTRIBUTE(4, 0); + const char* format, va_list ap) ABSL_PRINTF_ATTRIBUTE(4, 0); void RawLogVA(absl::LogSeverity severity, const char* file, int line, - const char* format, va_list ap) { + const char* format, va_list ap) { char buffer[kLogBufSize]; char* buf = buffer; int size = sizeof(buffer); @@ -146,14 +162,7 @@ void RawLogVA(absl::LogSeverity severity, const char* file, int line, } #endif - auto log_prefix_hook_ptr = log_prefix_hook.Load(); - if (log_prefix_hook_ptr) { - enabled = log_prefix_hook_ptr(severity, file, line, &buf, &size); - } else { - if (enabled) { - DoRawLog(&buf, &size, "[%s : %d] RAW: ", file, line); - } - } + enabled = log_filter_and_prefix_hook(severity, file, line, &buf, &size); const char* const prefix_end = buf; #ifdef ABSL_LOW_LEVEL_WRITE_SUPPORTED @@ -164,11 +173,12 @@ void RawLogVA(absl::LogSeverity severity, const char* file, int line, } else { DoRawLog(&buf, &size, "%s", kTruncated); } - absl::raw_logging_internal::SafeWriteToStderr(buffer, strlen(buffer)); + AsyncSignalSafeWriteToStderr(buffer, strlen(buffer)); } #else static_cast(format); static_cast(ap); + static_cast(enabled); #endif // Abort the process after logging a FATAL message, even if the output itself @@ -179,17 +189,28 @@ void RawLogVA(absl::LogSeverity severity, const char* file, int line, } } +// Non-formatting version of RawLog(). +// +// TODO(gfalcon): When string_view no longer depends on base, change this +// interface to take its message as a string_view instead. +void DefaultInternalLog(absl::LogSeverity severity, const char* file, int line, + const std::string& message) { + RawLog(severity, file, line, "%.*s", static_cast(message.size()), + message.data()); +} + } // namespace -namespace absl { -namespace raw_logging_internal { -void SafeWriteToStderr(const char *s, size_t len) { +void AsyncSignalSafeWriteToStderr(const char* s, size_t len) { + absl::base_internal::ErrnoSaver errno_saver; #if defined(ABSL_HAVE_SYSCALL_WRITE) + // We prefer calling write via `syscall` to minimize the risk of libc doing + // something "helpful". syscall(SYS_write, STDERR_FILENO, s, len); #elif defined(ABSL_HAVE_POSIX_WRITE) write(STDERR_FILENO, s, len); #elif defined(ABSL_HAVE_RAW_IO) - _write(/* stderr */ 2, s, len); + _write(/* stderr */ 2, s, static_cast(len)); #else // stderr logging unsupported on this platform (void) s; @@ -198,24 +219,13 @@ void SafeWriteToStderr(const char *s, size_t len) { } void RawLog(absl::LogSeverity severity, const char* file, int line, - const char* format, ...) ABSL_PRINTF_ATTRIBUTE(4, 5); -void RawLog(absl::LogSeverity severity, const char* file, int line, - const char* format, ...) { + const char* format, ...) { va_list ap; va_start(ap, format); RawLogVA(severity, file, line, format, ap); va_end(ap); } -// Non-formatting version of RawLog(). -// -// TODO(gfalcon): When string_view no longer depends on base, change this -// interface to take its message as a string_view instead. -static void DefaultInternalLog(absl::LogSeverity severity, const char* file, - int line, const std::string& message) { - RawLog(severity, file, line, "%s", message.c_str()); -} - bool RawLoggingFullySupported() { #ifdef ABSL_LOW_LEVEL_WRITE_SUPPORTED return true; @@ -224,12 +234,20 @@ bool RawLoggingFullySupported() { #endif // !ABSL_LOW_LEVEL_WRITE_SUPPORTED } -ABSL_CONST_INIT absl::base_internal::AtomicHook - internal_log_function(DefaultInternalLog); +ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES ABSL_DLL + absl::base_internal::AtomicHook + internal_log_function(DefaultInternalLog); + +void RegisterLogFilterAndPrefixHook(LogFilterAndPrefixHook func) { + log_filter_and_prefix_hook.Store(func); +} + +void RegisterAbortHook(AbortHook func) { abort_hook.Store(func); } void RegisterInternalLogFunction(InternalLogFunction func) { internal_log_function.Store(func); } -} // namespace raw_logging_internal +} // namespace raw_log_internal +ABSL_NAMESPACE_END } // namespace absl diff --git a/third_party/absl/absl/base/internal/raw_logging.h b/third_party/absl/absl/base/internal/raw_logging.h index 6a4c09360..c7b889cd8 100644 --- a/third_party/absl/absl/base/internal/raw_logging.h +++ b/third_party/absl/absl/base/internal/raw_logging.h @@ -22,9 +22,11 @@ #include #include "absl/base/attributes.h" +#include "absl/base/config.h" #include "absl/base/internal/atomic_hook.h" #include "absl/base/log_severity.h" #include "absl/base/macros.h" +#include "absl/base/optimization.h" #include "absl/base/port.h" // This is similar to LOG(severity) << format..., but @@ -41,12 +43,11 @@ #define ABSL_RAW_LOG(severity, ...) \ do { \ - constexpr const char* absl_raw_logging_internal_basename = \ - ::absl::raw_logging_internal::Basename(__FILE__, \ - sizeof(__FILE__) - 1); \ - ::absl::raw_logging_internal::RawLog(ABSL_RAW_LOGGING_INTERNAL_##severity, \ - absl_raw_logging_internal_basename, \ - __LINE__, __VA_ARGS__); \ + constexpr const char* absl_raw_log_internal_basename = \ + ::absl::raw_log_internal::Basename(__FILE__, sizeof(__FILE__) - 1); \ + ::absl::raw_log_internal::RawLog(ABSL_RAW_LOG_INTERNAL_##severity, \ + absl_raw_log_internal_basename, __LINE__, \ + __VA_ARGS__); \ } while (0) // Similar to CHECK(condition) << message, but for low-level modules: @@ -70,14 +71,14 @@ // // The API is a subset of the above: each macro only takes two arguments. Use // StrCat if you need to build a richer message. -#define ABSL_INTERNAL_LOG(severity, message) \ - do { \ - constexpr const char* absl_raw_logging_internal_basename = \ - ::absl::raw_logging_internal::Basename(__FILE__, \ - sizeof(__FILE__) - 1); \ - ::absl::raw_logging_internal::internal_log_function( \ - ABSL_RAW_LOGGING_INTERNAL_##severity, \ - absl_raw_logging_internal_basename, __LINE__, message); \ +#define ABSL_INTERNAL_LOG(severity, message) \ + do { \ + constexpr const char* absl_raw_log_internal_filename = __FILE__; \ + ::absl::raw_log_internal::internal_log_function( \ + ABSL_RAW_LOG_INTERNAL_##severity, absl_raw_log_internal_filename, \ + __LINE__, message); \ + if (ABSL_RAW_LOG_INTERNAL_##severity == ::absl::LogSeverity::kFatal) \ + ABSL_UNREACHABLE(); \ } while (0) #define ABSL_INTERNAL_CHECK(condition, message) \ @@ -89,15 +90,16 @@ } \ } while (0) -#define ABSL_RAW_LOGGING_INTERNAL_INFO ::absl::LogSeverity::kInfo -#define ABSL_RAW_LOGGING_INTERNAL_WARNING ::absl::LogSeverity::kWarning -#define ABSL_RAW_LOGGING_INTERNAL_ERROR ::absl::LogSeverity::kError -#define ABSL_RAW_LOGGING_INTERNAL_FATAL ::absl::LogSeverity::kFatal -#define ABSL_RAW_LOGGING_INTERNAL_LEVEL(severity) \ +#define ABSL_RAW_LOG_INTERNAL_INFO ::absl::LogSeverity::kInfo +#define ABSL_RAW_LOG_INTERNAL_WARNING ::absl::LogSeverity::kWarning +#define ABSL_RAW_LOG_INTERNAL_ERROR ::absl::LogSeverity::kError +#define ABSL_RAW_LOG_INTERNAL_FATAL ::absl::LogSeverity::kFatal +#define ABSL_RAW_LOG_INTERNAL_LEVEL(severity) \ ::absl::NormalizeLogSeverity(severity) namespace absl { -namespace raw_logging_internal { +ABSL_NAMESPACE_BEGIN +namespace raw_log_internal { // Helper function to implement ABSL_RAW_LOG // Logs format... at "severity" level, reporting it @@ -106,12 +108,9 @@ namespace raw_logging_internal { void RawLog(absl::LogSeverity severity, const char* file, int line, const char* format, ...) ABSL_PRINTF_ATTRIBUTE(4, 5); -// Writes the provided buffer directly to stderr, in a safe, low-level manner. -// -// In POSIX this means calling write(), which is async-signal safe and does -// not malloc. If the platform supports the SYS_write syscall, we invoke that -// directly to side-step any libc interception. -void SafeWriteToStderr(const char *s, size_t len); +// Writes the provided buffer directly to stderr, in a signal-safe, low-level +// manner. +void AsyncSignalSafeWriteToStderr(const char* s, size_t len); // compile-time function to get the "base" filename, that is, the part of // a filename after the last "/" or "\" path separator. The search starts at @@ -130,7 +129,7 @@ constexpr const char* Basename(const char* fname, int offset) { // TODO(gfalcon): Come up with a better name for this method. bool RawLoggingFullySupported(); -// Function type for a raw_logging customization hook for suppressing messages +// Function type for a raw_log customization hook for suppressing messages // by severity, and for writing custom prefixes on non-suppressed messages. // // The installed hook is called for every raw log invocation. The message will @@ -139,27 +138,31 @@ bool RawLoggingFullySupported(); // also provided with an output buffer, where it can write a custom log message // prefix. // -// The raw_logging system does not allocate memory or grab locks. User-provided +// The raw_log system does not allocate memory or grab locks. User-provided // hooks must avoid these operations, and must not throw exceptions. // // 'severity' is the severity level of the message being written. // 'file' and 'line' are the file and line number where the ABSL_RAW_LOG macro // was located. -// 'buffer' and 'buf_size' are pointers to the buffer and buffer size. If the -// hook writes a prefix, it must increment *buffer and decrement *buf_size +// 'buf' and 'buf_size' are pointers to the buffer and buffer size. If the +// hook writes a prefix, it must increment *buf and decrement *buf_size // accordingly. -using LogPrefixHook = bool (*)(absl::LogSeverity severity, const char* file, - int line, char** buffer, int* buf_size); +using LogFilterAndPrefixHook = bool (*)(absl::LogSeverity severity, + const char* file, int line, char** buf, + int* buf_size); -// Function type for a raw_logging customization hook called to abort a process +// Function type for a raw_log customization hook called to abort a process // when a FATAL message is logged. If the provided AbortHook() returns, the // logging system will call abort(). // // 'file' and 'line' are the file and line number where the ABSL_RAW_LOG macro // was located. -// The null-terminated logged message lives in the buffer between 'buf_start' +// The NUL-terminated logged message lives in the buffer between 'buf_start' // and 'buf_end'. 'prefix_end' points to the first non-prefix character of the -// buffer (as written by the LogPrefixHook.) +// buffer (as written by the LogFilterAndPrefixHook.) +// +// The lifetime of the filename and message buffers will not end while the +// process remains alive. using AbortHook = void (*)(const char* file, int line, const char* buf_start, const char* prefix_end, const char* buf_end); @@ -171,11 +174,22 @@ using InternalLogFunction = void (*)(absl::LogSeverity severity, const char* file, int line, const std::string& message); -extern base_internal::AtomicHook internal_log_function; +ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES ABSL_DLL extern base_internal::AtomicHook< + InternalLogFunction> + internal_log_function; +// Registers hooks of the above types. Only a single hook of each type may be +// registered. It is an error to call these functions multiple times with +// different input arguments. +// +// These functions are safe to call at any point during initialization; they do +// not block or malloc, and are async-signal safe. +void RegisterLogFilterAndPrefixHook(LogFilterAndPrefixHook func); +void RegisterAbortHook(AbortHook func); void RegisterInternalLogFunction(InternalLogFunction func); -} // namespace raw_logging_internal +} // namespace raw_log_internal +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_BASE_INTERNAL_RAW_LOGGING_H_ diff --git a/third_party/absl/absl/base/internal/throw_delegate.cc b/third_party/absl/absl/base/internal/throw_delegate.cc index 8e928b8a2..c260ff1ee 100644 --- a/third_party/absl/absl/base/internal/throw_delegate.cc +++ b/third_party/absl/absl/base/internal/throw_delegate.cc @@ -18,89 +18,195 @@ #include #include #include + #include "absl/base/config.h" #include "absl/base/internal/raw_logging.h" namespace absl { +ABSL_NAMESPACE_BEGIN namespace base_internal { +// NOTE: The various STL exception throwing functions are placed within the +// #ifdef blocks so the symbols aren't exposed on platforms that don't support +// them, such as the Android NDK. For example, ANGLE fails to link when building +// within AOSP without them, since the STL functions don't exist. namespace { +#ifdef ABSL_HAVE_EXCEPTIONS template [[noreturn]] void Throw(const T& error) { -#ifdef ABSL_HAVE_EXCEPTIONS throw error; -#else - ABSL_RAW_LOG(FATAL, "%s", error.what()); - std::abort(); -#endif } +#endif } // namespace void ThrowStdLogicError(const std::string& what_arg) { +#ifdef ABSL_HAVE_EXCEPTIONS Throw(std::logic_error(what_arg)); +#else + ABSL_RAW_LOG(FATAL, "%s", what_arg.c_str()); + std::abort(); +#endif } void ThrowStdLogicError(const char* what_arg) { +#ifdef ABSL_HAVE_EXCEPTIONS Throw(std::logic_error(what_arg)); +#else + ABSL_RAW_LOG(FATAL, "%s", what_arg); + std::abort(); +#endif } void ThrowStdInvalidArgument(const std::string& what_arg) { +#ifdef ABSL_HAVE_EXCEPTIONS Throw(std::invalid_argument(what_arg)); +#else + ABSL_RAW_LOG(FATAL, "%s", what_arg.c_str()); + std::abort(); +#endif } void ThrowStdInvalidArgument(const char* what_arg) { +#ifdef ABSL_HAVE_EXCEPTIONS Throw(std::invalid_argument(what_arg)); +#else + ABSL_RAW_LOG(FATAL, "%s", what_arg); + std::abort(); +#endif } void ThrowStdDomainError(const std::string& what_arg) { +#ifdef ABSL_HAVE_EXCEPTIONS Throw(std::domain_error(what_arg)); +#else + ABSL_RAW_LOG(FATAL, "%s", what_arg.c_str()); + std::abort(); +#endif } void ThrowStdDomainError(const char* what_arg) { +#ifdef ABSL_HAVE_EXCEPTIONS Throw(std::domain_error(what_arg)); +#else + ABSL_RAW_LOG(FATAL, "%s", what_arg); + std::abort(); +#endif } void ThrowStdLengthError(const std::string& what_arg) { +#ifdef ABSL_HAVE_EXCEPTIONS Throw(std::length_error(what_arg)); +#else + ABSL_RAW_LOG(FATAL, "%s", what_arg.c_str()); + std::abort(); +#endif } void ThrowStdLengthError(const char* what_arg) { +#ifdef ABSL_HAVE_EXCEPTIONS Throw(std::length_error(what_arg)); +#else + ABSL_RAW_LOG(FATAL, "%s", what_arg); + std::abort(); +#endif } void ThrowStdOutOfRange(const std::string& what_arg) { +#ifdef ABSL_HAVE_EXCEPTIONS Throw(std::out_of_range(what_arg)); +#else + ABSL_RAW_LOG(FATAL, "%s", what_arg.c_str()); + std::abort(); +#endif } void ThrowStdOutOfRange(const char* what_arg) { +#ifdef ABSL_HAVE_EXCEPTIONS Throw(std::out_of_range(what_arg)); +#else + ABSL_RAW_LOG(FATAL, "%s", what_arg); + std::abort(); +#endif } void ThrowStdRuntimeError(const std::string& what_arg) { +#ifdef ABSL_HAVE_EXCEPTIONS Throw(std::runtime_error(what_arg)); +#else + ABSL_RAW_LOG(FATAL, "%s", what_arg.c_str()); + std::abort(); +#endif } void ThrowStdRuntimeError(const char* what_arg) { +#ifdef ABSL_HAVE_EXCEPTIONS Throw(std::runtime_error(what_arg)); +#else + ABSL_RAW_LOG(FATAL, "%s", what_arg); + std::abort(); +#endif } void ThrowStdRangeError(const std::string& what_arg) { +#ifdef ABSL_HAVE_EXCEPTIONS Throw(std::range_error(what_arg)); +#else + ABSL_RAW_LOG(FATAL, "%s", what_arg.c_str()); + std::abort(); +#endif } void ThrowStdRangeError(const char* what_arg) { +#ifdef ABSL_HAVE_EXCEPTIONS Throw(std::range_error(what_arg)); +#else + ABSL_RAW_LOG(FATAL, "%s", what_arg); + std::abort(); +#endif } void ThrowStdOverflowError(const std::string& what_arg) { +#ifdef ABSL_HAVE_EXCEPTIONS Throw(std::overflow_error(what_arg)); +#else + ABSL_RAW_LOG(FATAL, "%s", what_arg.c_str()); + std::abort(); +#endif } void ThrowStdOverflowError(const char* what_arg) { +#ifdef ABSL_HAVE_EXCEPTIONS Throw(std::overflow_error(what_arg)); +#else + ABSL_RAW_LOG(FATAL, "%s", what_arg); + std::abort(); +#endif } void ThrowStdUnderflowError(const std::string& what_arg) { +#ifdef ABSL_HAVE_EXCEPTIONS Throw(std::underflow_error(what_arg)); +#else + ABSL_RAW_LOG(FATAL, "%s", what_arg.c_str()); + std::abort(); +#endif } void ThrowStdUnderflowError(const char* what_arg) { +#ifdef ABSL_HAVE_EXCEPTIONS Throw(std::underflow_error(what_arg)); +#else + ABSL_RAW_LOG(FATAL, "%s", what_arg); + std::abort(); +#endif } -void ThrowStdBadFunctionCall() { Throw(std::bad_function_call()); } +void ThrowStdBadFunctionCall() { +#ifdef ABSL_HAVE_EXCEPTIONS + Throw(std::bad_function_call()); +#else + std::abort(); +#endif +} -void ThrowStdBadAlloc() { Throw(std::bad_alloc()); } +void ThrowStdBadAlloc() { +#ifdef ABSL_HAVE_EXCEPTIONS + Throw(std::bad_alloc()); +#else + std::abort(); +#endif +} } // namespace base_internal +ABSL_NAMESPACE_END } // namespace absl diff --git a/third_party/absl/absl/base/internal/throw_delegate.h b/third_party/absl/absl/base/internal/throw_delegate.h index 03c700b5e..075f52725 100644 --- a/third_party/absl/absl/base/internal/throw_delegate.h +++ b/third_party/absl/absl/base/internal/throw_delegate.h @@ -19,7 +19,10 @@ #include +#include "absl/base/config.h" + namespace absl { +ABSL_NAMESPACE_BEGIN namespace base_internal { // Helper functions that allow throwing exceptions consistently from anywhere. @@ -66,6 +69,7 @@ namespace base_internal { // [[noreturn]] void ThrowStdBadArrayNewLength(); } // namespace base_internal +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_BASE_INTERNAL_THROW_DELEGATE_H_ diff --git a/third_party/absl/absl/base/internal/unaligned_access.h b/third_party/absl/absl/base/internal/unaligned_access.h new file mode 100644 index 000000000..093dd9b49 --- /dev/null +++ b/third_party/absl/absl/base/internal/unaligned_access.h @@ -0,0 +1,82 @@ +// +// Copyright 2017 The Abseil Authors. +// +// 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 +// +// https://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. +// + +#ifndef ABSL_BASE_INTERNAL_UNALIGNED_ACCESS_H_ +#define ABSL_BASE_INTERNAL_UNALIGNED_ACCESS_H_ + +#include + +#include + +#include "absl/base/attributes.h" +#include "absl/base/config.h" + +// unaligned APIs + +// Portable handling of unaligned loads, stores, and copies. + +// The unaligned API is C++ only. The declarations use C++ features +// (namespaces, inline) which are absent or incompatible in C. +#if defined(__cplusplus) +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace base_internal { + +inline uint16_t UnalignedLoad16(const void *p) { + uint16_t t; + memcpy(&t, p, sizeof t); + return t; +} + +inline uint32_t UnalignedLoad32(const void *p) { + uint32_t t; + memcpy(&t, p, sizeof t); + return t; +} + +inline uint64_t UnalignedLoad64(const void *p) { + uint64_t t; + memcpy(&t, p, sizeof t); + return t; +} + +inline void UnalignedStore16(void *p, uint16_t v) { memcpy(p, &v, sizeof v); } + +inline void UnalignedStore32(void *p, uint32_t v) { memcpy(p, &v, sizeof v); } + +inline void UnalignedStore64(void *p, uint64_t v) { memcpy(p, &v, sizeof v); } + +} // namespace base_internal +ABSL_NAMESPACE_END +} // namespace absl + +#define ABSL_INTERNAL_UNALIGNED_LOAD16(_p) \ + (absl::base_internal::UnalignedLoad16(_p)) +#define ABSL_INTERNAL_UNALIGNED_LOAD32(_p) \ + (absl::base_internal::UnalignedLoad32(_p)) +#define ABSL_INTERNAL_UNALIGNED_LOAD64(_p) \ + (absl::base_internal::UnalignedLoad64(_p)) + +#define ABSL_INTERNAL_UNALIGNED_STORE16(_p, _val) \ + (absl::base_internal::UnalignedStore16(_p, _val)) +#define ABSL_INTERNAL_UNALIGNED_STORE32(_p, _val) \ + (absl::base_internal::UnalignedStore32(_p, _val)) +#define ABSL_INTERNAL_UNALIGNED_STORE64(_p, _val) \ + (absl::base_internal::UnalignedStore64(_p, _val)) + +#endif // defined(__cplusplus), end of unaligned API + +#endif // ABSL_BASE_INTERNAL_UNALIGNED_ACCESS_H_ diff --git a/third_party/absl/absl/base/log_severity.cc b/third_party/absl/absl/base/log_severity.cc index 02a2a4852..60a8fc1f8 100644 --- a/third_party/absl/absl/base/log_severity.cc +++ b/third_party/absl/absl/base/log_severity.cc @@ -16,10 +16,40 @@ #include +#include "absl/base/attributes.h" + namespace absl { +ABSL_NAMESPACE_BEGIN std::ostream& operator<<(std::ostream& os, absl::LogSeverity s) { if (s == absl::NormalizeLogSeverity(s)) return os << absl::LogSeverityName(s); return os << "absl::LogSeverity(" << static_cast(s) << ")"; } + +std::ostream& operator<<(std::ostream& os, absl::LogSeverityAtLeast s) { + switch (s) { + case absl::LogSeverityAtLeast::kInfo: + case absl::LogSeverityAtLeast::kWarning: + case absl::LogSeverityAtLeast::kError: + case absl::LogSeverityAtLeast::kFatal: + return os << ">=" << static_cast(s); + case absl::LogSeverityAtLeast::kInfinity: + return os << "INFINITY"; + } + return os; +} + +std::ostream& operator<<(std::ostream& os, absl::LogSeverityAtMost s) { + switch (s) { + case absl::LogSeverityAtMost::kInfo: + case absl::LogSeverityAtMost::kWarning: + case absl::LogSeverityAtMost::kError: + case absl::LogSeverityAtMost::kFatal: + return os << "<=" << static_cast(s); + case absl::LogSeverityAtMost::kNegativeInfinity: + return os << "NEGATIVE_INFINITY"; + } + return os; +} +ABSL_NAMESPACE_END } // namespace absl diff --git a/third_party/absl/absl/base/log_severity.h b/third_party/absl/absl/base/log_severity.h index 5a1d5576c..8bdca38b5 100644 --- a/third_party/absl/absl/base/log_severity.h +++ b/third_party/absl/absl/base/log_severity.h @@ -12,19 +12,60 @@ // See the License for the specific language governing permissions and // limitations under the License. -#ifndef ABSL_BASE_INTERNAL_LOG_SEVERITY_H_ -#define ABSL_BASE_INTERNAL_LOG_SEVERITY_H_ +#ifndef ABSL_BASE_LOG_SEVERITY_H_ +#define ABSL_BASE_LOG_SEVERITY_H_ #include #include #include "absl/base/attributes.h" +#include "absl/base/config.h" namespace absl { +ABSL_NAMESPACE_BEGIN -// Four severity levels are defined. Logging APIs should terminate the program +// absl::LogSeverity +// +// Four severity levels are defined. Logging APIs should terminate the program // when a message is logged at severity `kFatal`; the other levels have no // special semantics. +// +// Values other than the four defined levels (e.g. produced by `static_cast`) +// are valid, but their semantics when passed to a function, macro, or flag +// depend on the function, macro, or flag. The usual behavior is to normalize +// such values to a defined severity level, however in some cases values other +// than the defined levels are useful for comparison. +// +// Example: +// +// // Effectively disables all logging: +// SetMinLogLevel(static_cast(100)); +// +// Abseil flags may be defined with type `LogSeverity`. Dependency layering +// constraints require that the `AbslParseFlag()` overload be declared and +// defined in the flags library itself rather than here. The `AbslUnparseFlag()` +// overload is defined there as well for consistency. +// +// absl::LogSeverity Flag String Representation +// +// An `absl::LogSeverity` has a string representation used for parsing +// command-line flags based on the enumerator name (e.g. `kFatal`) or +// its unprefixed name (without the `k`) in any case-insensitive form. (E.g. +// "FATAL", "fatal" or "Fatal" are all valid.) Unparsing such flags produces an +// unprefixed string representation in all caps (e.g. "FATAL") or an integer. +// +// Additionally, the parser accepts arbitrary integers (as if the type were +// `int`). +// +// Examples: +// +// --my_log_level=kInfo +// --my_log_level=INFO +// --my_log_level=info +// --my_log_level=0 +// +// Unparsing a flag produces the same result as `absl::LogSeverityName()` for +// the standard levels and a base-ten integer otherwise. enum class LogSeverity : int { kInfo = 0, kWarning = 1, @@ -32,6 +73,8 @@ enum class LogSeverity : int { kFatal = 3, }; +// LogSeverities() +// // Returns an iterable of all standard `absl::LogSeverity` values, ordered from // least to most severe. constexpr std::array LogSeverities() { @@ -39,8 +82,10 @@ constexpr std::array LogSeverities() { absl::LogSeverity::kError, absl::LogSeverity::kFatal}}; } +// LogSeverityName() +// // Returns the all-caps string representation (e.g. "INFO") of the specified -// severity level if it is one of the normal levels and "UNKNOWN" otherwise. +// severity level if it is one of the standard levels and "UNKNOWN" otherwise. constexpr const char* LogSeverityName(absl::LogSeverity s) { return s == absl::LogSeverity::kInfo ? "INFO" @@ -51,6 +96,8 @@ constexpr const char* LogSeverityName(absl::LogSeverity s) { : s == absl::LogSeverity::kFatal ? "FATAL" : "UNKNOWN"; } +// NormalizeLogSeverity() +// // Values less than `kInfo` normalize to `kInfo`; values greater than `kFatal` // normalize to `kError` (**NOT** `kFatal`). constexpr absl::LogSeverity NormalizeLogSeverity(absl::LogSeverity s) { @@ -59,13 +106,67 @@ constexpr absl::LogSeverity NormalizeLogSeverity(absl::LogSeverity s) { : s > absl::LogSeverity::kFatal ? absl::LogSeverity::kError : s; } constexpr absl::LogSeverity NormalizeLogSeverity(int s) { - return NormalizeLogSeverity(static_cast(s)); + return absl::NormalizeLogSeverity(static_cast(s)); } +// operator<< +// // The exact representation of a streamed `absl::LogSeverity` is deliberately // unspecified; do not rely on it. std::ostream& operator<<(std::ostream& os, absl::LogSeverity s); +// Enums representing a lower bound for LogSeverity. APIs that only operate on +// messages of at least a certain level (for example, `SetMinLogLevel()`) use +// this type to specify that level. absl::LogSeverityAtLeast::kInfinity is +// a level above all threshold levels and therefore no log message will +// ever meet this threshold. +enum class LogSeverityAtLeast : int { + kInfo = static_cast(absl::LogSeverity::kInfo), + kWarning = static_cast(absl::LogSeverity::kWarning), + kError = static_cast(absl::LogSeverity::kError), + kFatal = static_cast(absl::LogSeverity::kFatal), + kInfinity = 1000, +}; + +std::ostream& operator<<(std::ostream& os, absl::LogSeverityAtLeast s); + +// Enums representing an upper bound for LogSeverity. APIs that only operate on +// messages of at most a certain level (for example, buffer all messages at or +// below a certain level) use this type to specify that level. +// absl::LogSeverityAtMost::kNegativeInfinity is a level below all threshold +// levels and therefore will exclude all log messages. +enum class LogSeverityAtMost : int { + kNegativeInfinity = -1000, + kInfo = static_cast(absl::LogSeverity::kInfo), + kWarning = static_cast(absl::LogSeverity::kWarning), + kError = static_cast(absl::LogSeverity::kError), + kFatal = static_cast(absl::LogSeverity::kFatal), +}; + +std::ostream& operator<<(std::ostream& os, absl::LogSeverityAtMost s); + +#define COMPOP(op1, op2, T) \ + constexpr bool operator op1(absl::T lhs, absl::LogSeverity rhs) { \ + return static_cast(lhs) op1 rhs; \ + } \ + constexpr bool operator op2(absl::LogSeverity lhs, absl::T rhs) { \ + return lhs op2 static_cast(rhs); \ + } + +// Comparisons between `LogSeverity` and `LogSeverityAtLeast`/ +// `LogSeverityAtMost` are only supported in one direction. +// Valid checks are: +// LogSeverity >= LogSeverityAtLeast +// LogSeverity < LogSeverityAtLeast +// LogSeverity <= LogSeverityAtMost +// LogSeverity > LogSeverityAtMost +COMPOP(>, <, LogSeverityAtLeast) +COMPOP(<=, >=, LogSeverityAtLeast) +COMPOP(<, >, LogSeverityAtMost) +COMPOP(>=, <=, LogSeverityAtMost) +#undef COMPOP + +ABSL_NAMESPACE_END } // namespace absl -#endif // ABSL_BASE_INTERNAL_LOG_SEVERITY_H_ +#endif // ABSL_BASE_LOG_SEVERITY_H_ diff --git a/third_party/absl/absl/base/macros.h b/third_party/absl/absl/base/macros.h index 606a90ac0..f33cd1927 100644 --- a/third_party/absl/absl/base/macros.h +++ b/third_party/absl/absl/base/macros.h @@ -31,6 +31,8 @@ #include #include +#include "absl/base/attributes.h" +#include "absl/base/config.h" #include "absl/base/optimization.h" #include "absl/base/port.h" @@ -43,122 +45,22 @@ (sizeof(::absl::macros_internal::ArraySizeHelper(array))) namespace absl { +ABSL_NAMESPACE_BEGIN namespace macros_internal { // Note: this internal template function declaration is used by ABSL_ARRAYSIZE. // The function doesn't need a definition, as we only use its type. template auto ArraySizeHelper(const T (&array)[N]) -> char (&)[N]; } // namespace macros_internal +ABSL_NAMESPACE_END } // namespace absl -// kLinkerInitialized -// -// An enum used only as a constructor argument to indicate that a variable has -// static storage duration, and that the constructor should do nothing to its -// state. Use of this macro indicates to the reader that it is legal to -// declare a static instance of the class, provided the constructor is given -// the absl::base_internal::kLinkerInitialized argument. -// -// Normally, it is unsafe to declare a static variable that has a constructor or -// a destructor because invocation order is undefined. However, if the type can -// be zero-initialized (which the loader does for static variables) into a valid -// state and the type's destructor does not affect storage, then a constructor -// for static initialization can be declared. -// -// Example: -// // Declaration -// explicit MyClass(absl::base_internal:LinkerInitialized x) {} -// -// // Invocation -// static MyClass my_global(absl::base_internal::kLinkerInitialized); -namespace absl { -namespace base_internal { -enum LinkerInitialized { - kLinkerInitialized = 0, -}; -} // namespace base_internal -} // namespace absl - -// ABSL_FALLTHROUGH_INTENDED -// -// Annotates implicit fall-through between switch labels, allowing a case to -// indicate intentional fallthrough and turn off warnings about any lack of a -// `break` statement. The ABSL_FALLTHROUGH_INTENDED macro should be followed by -// a semicolon and can be used in most places where `break` can, provided that -// no statements exist between it and the next switch label. -// -// Example: -// -// switch (x) { -// case 40: -// case 41: -// if (truth_is_out_there) { -// ++x; -// ABSL_FALLTHROUGH_INTENDED; // Use instead of/along with annotations -// // in comments -// } else { -// return x; -// } -// case 42: -// ... -// -// Notes: when compiled with clang in C++11 mode, the ABSL_FALLTHROUGH_INTENDED -// macro is expanded to the [[clang::fallthrough]] attribute, which is analysed -// when performing switch labels fall-through diagnostic -// (`-Wimplicit-fallthrough`). See clang documentation on language extensions -// for details: -// http://clang.llvm.org/docs/AttributeReference.html#fallthrough-clang-fallthrough -// -// When used with unsupported compilers, the ABSL_FALLTHROUGH_INTENDED macro -// has no effect on diagnostics. In any case this macro has no effect on runtime -// behavior and performance of code. -#ifdef ABSL_FALLTHROUGH_INTENDED -#error "ABSL_FALLTHROUGH_INTENDED should not be defined." -#endif - -// TODO(zhangxy): Use c++17 standard [[fallthrough]] macro, when supported. -#if defined(__clang__) && defined(__has_warning) -#if __has_feature(cxx_attributes) && __has_warning("-Wimplicit-fallthrough") -#define ABSL_FALLTHROUGH_INTENDED [[clang::fallthrough]] -#endif -#elif defined(__GNUC__) && __GNUC__ >= 7 -#define ABSL_FALLTHROUGH_INTENDED [[gnu::fallthrough]] -#endif - -#ifndef ABSL_FALLTHROUGH_INTENDED -#define ABSL_FALLTHROUGH_INTENDED \ - do { \ - } while (0) -#endif - -// ABSL_DEPRECATED() -// -// Marks a deprecated class, struct, enum, function, method and variable -// declarations. The macro argument is used as a custom diagnostic message (e.g. -// suggestion of a better alternative). -// -// Example: -// -// class ABSL_DEPRECATED("Use Bar instead") Foo {...}; -// ABSL_DEPRECATED("Use Baz instead") void Bar() {...} -// -// Every usage of a deprecated entity will trigger a warning when compiled with -// clang's `-Wdeprecated-declarations` option. This option is turned off by -// default, but the warnings will be reported by clang-tidy. -#if defined(__clang__) && __cplusplus >= 201103L -#define ABSL_DEPRECATED(message) __attribute__((deprecated(message))) -#endif - -#ifndef ABSL_DEPRECATED -#define ABSL_DEPRECATED(message) -#endif - // ABSL_BAD_CALL_IF() // // Used on a function overload to trap bad calls: any call that matches the // overload will cause a compile-time error. This macro uses a clang-specific // "enable_if" attribute, as described at -// http://clang.llvm.org/docs/AttributeReference.html#enable-if +// https://clang.llvm.org/docs/AttributeReference.html#enable-if // // Overloads which use this macro should be bracketed by // `#ifdef ABSL_BAD_CALL_IF`. @@ -171,12 +73,9 @@ enum LinkerInitialized { // ABSL_BAD_CALL_IF(c <= -1 || c > 255, // "'c' must have the value of an unsigned char or EOF"); // #endif // ABSL_BAD_CALL_IF - -#if defined(__clang__) -# if __has_attribute(enable_if) -# define ABSL_BAD_CALL_IF(expr, msg) \ - __attribute__((enable_if(expr, "Bad call trap"), unavailable(msg))) -# endif +#if ABSL_HAVE_ATTRIBUTE(enable_if) +#define ABSL_BAD_CALL_IF(expr, msg) \ + __attribute__((enable_if(expr, "Bad call trap"), unavailable(msg))) #endif // ABSL_ASSERT() @@ -200,6 +99,35 @@ enum LinkerInitialized { : [] { assert(false && #expr); }()) // NOLINT #endif +// `ABSL_INTERNAL_HARDENING_ABORT()` controls how `ABSL_HARDENING_ASSERT()` +// aborts the program in release mode (when NDEBUG is defined). The +// implementation should abort the program as quickly as possible and ideally it +// should not be possible to ignore the abort request. +#define ABSL_INTERNAL_HARDENING_ABORT() \ + do { \ + ABSL_INTERNAL_IMMEDIATE_ABORT_IMPL(); \ + ABSL_INTERNAL_UNREACHABLE_IMPL(); \ + } while (false) + +// ABSL_HARDENING_ASSERT() +// +// `ABSL_HARDENING_ASSERT()` is like `ABSL_ASSERT()`, but used to implement +// runtime assertions that should be enabled in hardened builds even when +// `NDEBUG` is defined. +// +// When `NDEBUG` is not defined, `ABSL_HARDENING_ASSERT()` is identical to +// `ABSL_ASSERT()`. +// +// See `ABSL_OPTION_HARDENED` in `absl/base/options.h` for more information on +// hardened mode. +#if ABSL_OPTION_HARDENED == 1 && defined(NDEBUG) +#define ABSL_HARDENING_ASSERT(expr) \ + (ABSL_PREDICT_TRUE((expr)) ? static_cast(0) \ + : [] { ABSL_INTERNAL_HARDENING_ABORT(); }()) +#else +#define ABSL_HARDENING_ASSERT(expr) ABSL_ASSERT(expr) +#endif + #ifdef ABSL_HAVE_EXCEPTIONS #define ABSL_INTERNAL_TRY try #define ABSL_INTERNAL_CATCH_ANY catch (...) diff --git a/third_party/absl/absl/base/optimization.h b/third_party/absl/absl/base/optimization.h index 0dcbef32a..ad0121ad3 100644 --- a/third_party/absl/absl/base/optimization.h +++ b/third_party/absl/absl/base/optimization.h @@ -22,13 +22,15 @@ #ifndef ABSL_BASE_OPTIMIZATION_H_ #define ABSL_BASE_OPTIMIZATION_H_ +#include + #include "absl/base/config.h" // ABSL_BLOCK_TAIL_CALL_OPTIMIZATION // -// Instructs the compiler to avoid optimizing tail-call recursion. Use of this -// macro is useful when you wish to preserve the existing function order within -// a stack trace for logging, debugging, or profiling purposes. +// Instructs the compiler to avoid optimizing tail-call recursion. This macro is +// useful when you wish to preserve the existing function order within a stack +// trace for logging, debugging, or profiling purposes. // // Example: // @@ -89,6 +91,7 @@ #define ABSL_CACHELINE_SIZE 64 #endif #endif +#endif #ifndef ABSL_CACHELINE_SIZE // A reasonable default guess. Note that overestimates tend to waste more @@ -104,9 +107,10 @@ // Cacheline aligning objects properly allows constructive memory sharing and // prevents destructive (or "false") memory sharing. // -// NOTE: this macro should be replaced with usage of `alignas()` using +// NOTE: callers should replace uses of this macro with `alignas()` using // `std::hardware_constructive_interference_size` and/or -// `std::hardware_destructive_interference_size` when available within C++17. +// `std::hardware_destructive_interference_size` when C++17 becomes available to +// them. // // See http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0154r1.html // for more information. @@ -138,12 +142,11 @@ // the generated machine code. // 3) Prefer applying this attribute to individual variables. Avoid // applying it to types. This tends to localize the effect. +#if defined(__clang__) || defined(__GNUC__) #define ABSL_CACHELINE_ALIGNED __attribute__((aligned(ABSL_CACHELINE_SIZE))) #elif defined(_MSC_VER) -#define ABSL_CACHELINE_SIZE 64 #define ABSL_CACHELINE_ALIGNED __declspec(align(ABSL_CACHELINE_SIZE)) #else -#define ABSL_CACHELINE_SIZE 64 #define ABSL_CACHELINE_ALIGNED #endif @@ -171,11 +174,131 @@ // to yield performance improvements. #if ABSL_HAVE_BUILTIN(__builtin_expect) || \ (defined(__GNUC__) && !defined(__clang__)) -#define ABSL_PREDICT_FALSE(x) (__builtin_expect(x, 0)) -#define ABSL_PREDICT_TRUE(x) (__builtin_expect(!!(x), 1)) +#define ABSL_PREDICT_FALSE(x) (__builtin_expect(false || (x), false)) +#define ABSL_PREDICT_TRUE(x) (__builtin_expect(false || (x), true)) #else #define ABSL_PREDICT_FALSE(x) (x) #define ABSL_PREDICT_TRUE(x) (x) #endif +// `ABSL_INTERNAL_IMMEDIATE_ABORT_IMPL()` aborts the program in the fastest +// possible way, with no attempt at logging. One use is to implement hardening +// aborts with ABSL_OPTION_HARDENED. Since this is an internal symbol, it +// should not be used directly outside of Abseil. +#if ABSL_HAVE_BUILTIN(__builtin_trap) || \ + (defined(__GNUC__) && !defined(__clang__)) +#define ABSL_INTERNAL_IMMEDIATE_ABORT_IMPL() __builtin_trap() +#else +#define ABSL_INTERNAL_IMMEDIATE_ABORT_IMPL() abort() +#endif + +// `ABSL_INTERNAL_UNREACHABLE_IMPL()` is the platform specific directive to +// indicate that a statement is unreachable, and to allow the compiler to +// optimize accordingly. Clients should use `ABSL_UNREACHABLE()`, which is +// defined below. +#if defined(__cpp_lib_unreachable) && __cpp_lib_unreachable >= 202202L +#define ABSL_INTERNAL_UNREACHABLE_IMPL() std::unreachable() +#elif defined(__GNUC__) || ABSL_HAVE_BUILTIN(__builtin_unreachable) +#define ABSL_INTERNAL_UNREACHABLE_IMPL() __builtin_unreachable() +#elif ABSL_HAVE_BUILTIN(__builtin_assume) +#define ABSL_INTERNAL_UNREACHABLE_IMPL() __builtin_assume(false) +#elif defined(_MSC_VER) +#define ABSL_INTERNAL_UNREACHABLE_IMPL() __assume(false) +#else +#define ABSL_INTERNAL_UNREACHABLE_IMPL() +#endif + +// `ABSL_UNREACHABLE()` is an unreachable statement. A program which reaches +// one has undefined behavior, and the compiler may optimize accordingly. +#if ABSL_OPTION_HARDENED == 1 && defined(NDEBUG) +// Abort in hardened mode to avoid dangerous undefined behavior. +#define ABSL_UNREACHABLE() \ + do { \ + ABSL_INTERNAL_IMMEDIATE_ABORT_IMPL(); \ + ABSL_INTERNAL_UNREACHABLE_IMPL(); \ + } while (false) +#else +// The assert only fires in debug mode to aid in debugging. +// When NDEBUG is defined, reaching ABSL_UNREACHABLE() is undefined behavior. +#define ABSL_UNREACHABLE() \ + do { \ + /* NOLINTNEXTLINE: misc-static-assert */ \ + assert(false && "ABSL_UNREACHABLE reached"); \ + ABSL_INTERNAL_UNREACHABLE_IMPL(); \ + } while (false) +#endif + +// ABSL_ASSUME(cond) +// +// Informs the compiler that a condition is always true and that it can assume +// it to be true for optimization purposes. +// +// WARNING: If the condition is false, the program can produce undefined and +// potentially dangerous behavior. +// +// In !NDEBUG mode, the condition is checked with an assert(). +// +// NOTE: The expression must not have side effects, as it may only be evaluated +// in some compilation modes and not others. Some compilers may issue a warning +// if the compiler cannot prove the expression has no side effects. For example, +// the expression should not use a function call since the compiler cannot prove +// that a function call does not have side effects. +// +// Example: +// +// int x = ...; +// ABSL_ASSUME(x >= 0); +// // The compiler can optimize the division to a simple right shift using the +// // assumption specified above. +// int y = x / 16; +// +#if !defined(NDEBUG) +#define ABSL_ASSUME(cond) assert(cond) +#elif ABSL_HAVE_BUILTIN(__builtin_assume) +#define ABSL_ASSUME(cond) __builtin_assume(cond) +#elif defined(_MSC_VER) +#define ABSL_ASSUME(cond) __assume(cond) +#elif defined(__cpp_lib_unreachable) && __cpp_lib_unreachable >= 202202L +#define ABSL_ASSUME(cond) \ + do { \ + if (!(cond)) std::unreachable(); \ + } while (false) +#elif defined(__GNUC__) || ABSL_HAVE_BUILTIN(__builtin_unreachable) +#define ABSL_ASSUME(cond) \ + do { \ + if (!(cond)) __builtin_unreachable(); \ + } while (false) +#else +#define ABSL_ASSUME(cond) \ + do { \ + static_cast(false && (cond)); \ + } while (false) +#endif + +// ABSL_INTERNAL_UNIQUE_SMALL_NAME(cond) +// This macro forces small unique name on a static file level symbols like +// static local variables or static functions. This is intended to be used in +// macro definitions to optimize the cost of generated code. Do NOT use it on +// symbols exported from translation unit since it may cause a link time +// conflict. +// +// Example: +// +// #define MY_MACRO(txt) +// namespace { +// char VeryVeryLongVarName[] ABSL_INTERNAL_UNIQUE_SMALL_NAME() = txt; +// const char* VeryVeryLongFuncName() ABSL_INTERNAL_UNIQUE_SMALL_NAME(); +// const char* VeryVeryLongFuncName() { return txt; } +// } +// + +#if defined(__GNUC__) +#define ABSL_INTERNAL_UNIQUE_SMALL_NAME2(x) #x +#define ABSL_INTERNAL_UNIQUE_SMALL_NAME1(x) ABSL_INTERNAL_UNIQUE_SMALL_NAME2(x) +#define ABSL_INTERNAL_UNIQUE_SMALL_NAME() \ + asm(ABSL_INTERNAL_UNIQUE_SMALL_NAME1(.absl.__COUNTER__)) +#else +#define ABSL_INTERNAL_UNIQUE_SMALL_NAME() +#endif + #endif // ABSL_BASE_OPTIMIZATION_H_ diff --git a/third_party/absl/absl/base/options.h b/third_party/absl/absl/base/options.h new file mode 100644 index 000000000..b6de636d9 --- /dev/null +++ b/third_party/absl/absl/base/options.h @@ -0,0 +1,232 @@ +// Copyright 2019 The Abseil Authors. +// +// 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 +// +// https://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. +// +// ----------------------------------------------------------------------------- +// File: options.h +// ----------------------------------------------------------------------------- +// +// This file contains Abseil configuration options for setting specific +// implementations instead of letting Abseil determine which implementation to +// use at compile-time. Setting these options may be useful for package or build +// managers who wish to guarantee ABI stability within binary builds (which are +// otherwise difficult to enforce). +// +// *** IMPORTANT NOTICE FOR PACKAGE MANAGERS: It is important that +// maintainers of package managers who wish to package Abseil read and +// understand this file! *** +// +// Abseil contains a number of possible configuration endpoints, based on +// parameters such as the detected platform, language version, or command-line +// flags used to invoke the underlying binary. As is the case with all +// libraries, binaries which contain Abseil code must ensure that separate +// packages use the same compiled copy of Abseil to avoid a diamond dependency +// problem, which can occur if two packages built with different Abseil +// configuration settings are linked together. Diamond dependency problems in +// C++ may manifest as violations to the One Definition Rule (ODR) (resulting in +// linker errors), or undefined behavior (resulting in crashes). +// +// Diamond dependency problems can be avoided if all packages utilize the same +// exact version of Abseil. Building from source code with the same compilation +// parameters is the easiest way to avoid such dependency problems. However, for +// package managers who cannot control such compilation parameters, we are +// providing the file to allow you to inject ABI (Application Binary Interface) +// stability across builds. Settings options in this file will neither change +// API nor ABI, providing a stable copy of Abseil between packages. +// +// Care must be taken to keep options within these configurations isolated +// from any other dynamic settings, such as command-line flags which could alter +// these options. This file is provided specifically to help build and package +// managers provide a stable copy of Abseil within their libraries and binaries; +// other developers should not have need to alter the contents of this file. +// +// ----------------------------------------------------------------------------- +// Usage +// ----------------------------------------------------------------------------- +// +// For any particular package release, set the appropriate definitions within +// this file to whatever value makes the most sense for your package(s). Note +// that, by default, most of these options, at the moment, affect the +// implementation of types; future options may affect other implementation +// details. +// +// NOTE: the defaults within this file all assume that Abseil can select the +// proper Abseil implementation at compile-time, which will not be sufficient +// to guarantee ABI stability to package managers. + +#ifndef ABSL_BASE_OPTIONS_H_ +#define ABSL_BASE_OPTIONS_H_ + +// ----------------------------------------------------------------------------- +// Type Compatibility Options +// ----------------------------------------------------------------------------- +// +// ABSL_OPTION_USE_STD_ANY +// +// This option controls whether absl::any is implemented as an alias to +// std::any, or as an independent implementation. +// +// A value of 0 means to use Abseil's implementation. This requires only C++11 +// support, and is expected to work on every toolchain we support. +// +// A value of 1 means to use an alias to std::any. This requires that all code +// using Abseil is built in C++17 mode or later. +// +// A value of 2 means to detect the C++ version being used to compile Abseil, +// and use an alias only if a working std::any is available. This option is +// useful when you are building your entire program, including all of its +// dependencies, from source. It should not be used otherwise -- for example, +// if you are distributing Abseil in a binary package manager -- since in +// mode 2, absl::any will name a different type, with a different mangled name +// and binary layout, depending on the compiler flags passed by the end user. +// For more info, see https://abseil.io/about/design/dropin-types. +// +// User code should not inspect this macro. To check in the preprocessor if +// absl::any is a typedef of std::any, use the feature macro ABSL_USES_STD_ANY. + +#define ABSL_OPTION_USE_STD_ANY 2 + + +// ABSL_OPTION_USE_STD_OPTIONAL +// +// This option controls whether absl::optional is implemented as an alias to +// std::optional, or as an independent implementation. +// +// A value of 0 means to use Abseil's implementation. This requires only C++11 +// support, and is expected to work on every toolchain we support. +// +// A value of 1 means to use an alias to std::optional. This requires that all +// code using Abseil is built in C++17 mode or later. +// +// A value of 2 means to detect the C++ version being used to compile Abseil, +// and use an alias only if a working std::optional is available. This option +// is useful when you are building your program from source. It should not be +// used otherwise -- for example, if you are distributing Abseil in a binary +// package manager -- since in mode 2, absl::optional will name a different +// type, with a different mangled name and binary layout, depending on the +// compiler flags passed by the end user. For more info, see +// https://abseil.io/about/design/dropin-types. + +// User code should not inspect this macro. To check in the preprocessor if +// absl::optional is a typedef of std::optional, use the feature macro +// ABSL_USES_STD_OPTIONAL. + +#define ABSL_OPTION_USE_STD_OPTIONAL 2 + + +// ABSL_OPTION_USE_STD_STRING_VIEW +// +// This option controls whether absl::string_view is implemented as an alias to +// std::string_view, or as an independent implementation. +// +// A value of 0 means to use Abseil's implementation. This requires only C++11 +// support, and is expected to work on every toolchain we support. +// +// A value of 1 means to use an alias to std::string_view. This requires that +// all code using Abseil is built in C++17 mode or later. +// +// A value of 2 means to detect the C++ version being used to compile Abseil, +// and use an alias only if a working std::string_view is available. This +// option is useful when you are building your program from source. It should +// not be used otherwise -- for example, if you are distributing Abseil in a +// binary package manager -- since in mode 2, absl::string_view will name a +// different type, with a different mangled name and binary layout, depending on +// the compiler flags passed by the end user. For more info, see +// https://abseil.io/about/design/dropin-types. +// +// User code should not inspect this macro. To check in the preprocessor if +// absl::string_view is a typedef of std::string_view, use the feature macro +// ABSL_USES_STD_STRING_VIEW. + +#define ABSL_OPTION_USE_STD_STRING_VIEW 2 + +// ABSL_OPTION_USE_STD_VARIANT +// +// This option controls whether absl::variant is implemented as an alias to +// std::variant, or as an independent implementation. +// +// A value of 0 means to use Abseil's implementation. This requires only C++11 +// support, and is expected to work on every toolchain we support. +// +// A value of 1 means to use an alias to std::variant. This requires that all +// code using Abseil is built in C++17 mode or later. +// +// A value of 2 means to detect the C++ version being used to compile Abseil, +// and use an alias only if a working std::variant is available. This option +// is useful when you are building your program from source. It should not be +// used otherwise -- for example, if you are distributing Abseil in a binary +// package manager -- since in mode 2, absl::variant will name a different +// type, with a different mangled name and binary layout, depending on the +// compiler flags passed by the end user. For more info, see +// https://abseil.io/about/design/dropin-types. +// +// User code should not inspect this macro. To check in the preprocessor if +// absl::variant is a typedef of std::variant, use the feature macro +// ABSL_USES_STD_VARIANT. + +#define ABSL_OPTION_USE_STD_VARIANT 2 + + +// ABSL_OPTION_USE_INLINE_NAMESPACE +// ABSL_OPTION_INLINE_NAMESPACE_NAME +// +// These options controls whether all entities in the absl namespace are +// contained within an inner inline namespace. This does not affect the +// user-visible API of Abseil, but it changes the mangled names of all symbols. +// +// This can be useful as a version tag if you are distributing Abseil in +// precompiled form. This will prevent a binary library build of Abseil with +// one inline namespace being used with headers configured with a different +// inline namespace name. Binary packagers are reminded that Abseil does not +// guarantee any ABI stability in Abseil, so any update of Abseil or +// configuration change in such a binary package should be combined with a +// new, unique value for the inline namespace name. +// +// A value of 0 means not to use inline namespaces. +// +// A value of 1 means to use an inline namespace with the given name inside +// namespace absl. If this is set, ABSL_OPTION_INLINE_NAMESPACE_NAME must also +// be changed to a new, unique identifier name. In particular "head" is not +// allowed. + +#define ABSL_OPTION_USE_INLINE_NAMESPACE 1 +#define ABSL_OPTION_INLINE_NAMESPACE_NAME lts_20230125 + +// ABSL_OPTION_HARDENED +// +// This option enables a "hardened" build in release mode (in this context, +// release mode is defined as a build where the `NDEBUG` macro is defined). +// +// A value of 0 means that "hardened" mode is not enabled. +// +// A value of 1 means that "hardened" mode is enabled. +// +// Hardened builds have additional security checks enabled when `NDEBUG` is +// defined. Defining `NDEBUG` is normally used to turn `assert()` macro into a +// no-op, as well as disabling other bespoke program consistency checks. By +// defining ABSL_OPTION_HARDENED to 1, a select set of checks remain enabled in +// release mode. These checks guard against programming errors that may lead to +// security vulnerabilities. In release mode, when one of these programming +// errors is encountered, the program will immediately abort, possibly without +// any attempt at logging. +// +// The checks enabled by this option are not free; they do incur runtime cost. +// +// The checks enabled by this option are always active when `NDEBUG` is not +// defined, even in the case when ABSL_OPTION_HARDENED is defined to 0. The +// checks enabled by this option may abort the program in a different way and +// log additional information when `NDEBUG` is not defined. + +#define ABSL_OPTION_HARDENED 0 + +#endif // ABSL_BASE_OPTIONS_H_ diff --git a/third_party/absl/absl/base/policy_checks.h b/third_party/absl/absl/base/policy_checks.h index 699fb1a2e..b8cd4c944 100644 --- a/third_party/absl/absl/base/policy_checks.h +++ b/third_party/absl/absl/base/policy_checks.h @@ -41,20 +41,20 @@ #endif // ----------------------------------------------------------------------------- -// Compiler Check +// Toolchain Check // ----------------------------------------------------------------------------- -// We support MSVC++ 14.0 update 2 and later. +// We support Visual Studio 2017 (MSVC++ 15.0) and later. // This minimum will go up. -#if defined(_MSC_FULL_VER) && _MSC_FULL_VER < 190023918 && !defined(__clang__) -#error "This package requires Visual Studio 2015 Update 2 or higher." +#if defined(_MSC_VER) && _MSC_VER < 1910 && !defined(__clang__) +#error "This package requires Visual Studio 2017 (MSVC++ 15.0) or higher." #endif -// We support gcc 4.7 and later. +// We support GCC 7 and later. // This minimum will go up. #if defined(__GNUC__) && !defined(__clang__) -#if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 7) -#error "This package requires gcc 4.7 or higher." +#if __GNUC__ < 7 +#error "This package requires GCC 7 or higher." #endif #endif @@ -69,29 +69,21 @@ // C++ Version Check // ----------------------------------------------------------------------------- -// Enforce C++11 as the minimum. Note that Visual Studio has not -// advanced __cplusplus despite being good enough for our purposes, so -// so we exempt it from the check. -#if defined(__cplusplus) && !defined(_MSC_VER) -#if __cplusplus < 201103L -#error "C++ versions less than C++11 are not supported." -#endif +// Enforce C++14 as the minimum. +#if defined(_MSVC_LANG) +#if _MSVC_LANG < 201402L +#error "C++ versions less than C++14 are not supported." +#endif // _MSVC_LANG < 201402L +#elif defined(__cplusplus) +#if __cplusplus < 201402L +#error "C++ versions less than C++14 are not supported." +#endif // __cplusplus < 201402L #endif // ----------------------------------------------------------------------------- // Standard Library Check // ----------------------------------------------------------------------------- -// We have chosen glibc 2.12 as the minimum as it was tagged for release -// in May, 2010 and includes some functionality used in Google software -// (for instance pthread_setname_np): -// https://sourceware.org/ml/libc-alpha/2010-05/msg00000.html -#if defined(__GLIBC__) && defined(__GLIBC_PREREQ) -#if !__GLIBC_PREREQ(2, 12) -#error "Minimum required version of glibc is 2.12." -#endif -#endif - #if defined(_STLPORT_VERSION) #error "STLPort is not supported." #endif diff --git a/third_party/absl/absl/base/port.h b/third_party/absl/absl/base/port.h index 6c28068d4..5bc4d6cd9 100644 --- a/third_party/absl/absl/base/port.h +++ b/third_party/absl/absl/base/port.h @@ -14,7 +14,6 @@ // // This files is a forwarding header for other headers containing various // portability macros and functions. -// This file is used for both C and C++! #ifndef ABSL_BASE_PORT_H_ #define ABSL_BASE_PORT_H_ diff --git a/third_party/absl/absl/memory/memory.h b/third_party/absl/absl/memory/memory.h new file mode 100644 index 000000000..3508135c3 --- /dev/null +++ b/third_party/absl/absl/memory/memory.h @@ -0,0 +1,278 @@ +// Copyright 2017 The Abseil Authors. +// +// 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 +// +// https://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. +// +// ----------------------------------------------------------------------------- +// File: memory.h +// ----------------------------------------------------------------------------- +// +// This header file contains utility functions for managing the creation and +// conversion of smart pointers. This file is an extension to the C++ +// standard library header file. + +#ifndef ABSL_MEMORY_MEMORY_H_ +#define ABSL_MEMORY_MEMORY_H_ + +#include +#include +#include +#include +#include +#include + +#include "absl/base/macros.h" +#include "absl/meta/type_traits.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN + +// ----------------------------------------------------------------------------- +// Function Template: WrapUnique() +// ----------------------------------------------------------------------------- +// +// Adopts ownership from a raw pointer and transfers it to the returned +// `std::unique_ptr`, whose type is deduced. Because of this deduction, *do not* +// specify the template type `T` when calling `WrapUnique`. +// +// Example: +// X* NewX(int, int); +// auto x = WrapUnique(NewX(1, 2)); // 'x' is std::unique_ptr. +// +// Do not call WrapUnique with an explicit type, as in +// `WrapUnique(NewX(1, 2))`. The purpose of WrapUnique is to automatically +// deduce the pointer type. If you wish to make the type explicit, just use +// `std::unique_ptr` directly. +// +// auto x = std::unique_ptr(NewX(1, 2)); +// - or - +// std::unique_ptr x(NewX(1, 2)); +// +// While `absl::WrapUnique` is useful for capturing the output of a raw +// pointer factory, prefer 'absl::make_unique(args...)' over +// 'absl::WrapUnique(new T(args...))'. +// +// auto x = WrapUnique(new X(1, 2)); // works, but nonideal. +// auto x = make_unique(1, 2); // safer, standard, avoids raw 'new'. +// +// Note that `absl::WrapUnique(p)` is valid only if `delete p` is a valid +// expression. In particular, `absl::WrapUnique()` cannot wrap pointers to +// arrays, functions or void, and it must not be used to capture pointers +// obtained from array-new expressions (even though that would compile!). +template +std::unique_ptr WrapUnique(T* ptr) { + static_assert(!std::is_array::value, "array types are unsupported"); + static_assert(std::is_object::value, "non-object types are unsupported"); + return std::unique_ptr(ptr); +} + +// ----------------------------------------------------------------------------- +// Function Template: make_unique() +// ----------------------------------------------------------------------------- +// +// Creates a `std::unique_ptr<>`, while avoiding issues creating temporaries +// during the construction process. `absl::make_unique<>` also avoids redundant +// type declarations, by avoiding the need to explicitly use the `new` operator. +// +// https://en.cppreference.com/w/cpp/memory/unique_ptr/make_unique +// +// For more background on why `std::unique_ptr(new T(a,b))` is problematic, +// see Herb Sutter's explanation on +// (Exception-Safe Function Calls)[https://herbsutter.com/gotw/_102/]. +// (In general, reviewers should treat `new T(a,b)` with scrutiny.) +// +// Historical note: Abseil once provided a C++11 compatible implementation of +// the C++14's `std::make_unique`. Now that C++11 support has been sunsetted, +// `absl::make_unique` simply uses the STL-provided implementation. New code +// should use `std::make_unique`. +using std::make_unique; + +// ----------------------------------------------------------------------------- +// Function Template: RawPtr() +// ----------------------------------------------------------------------------- +// +// Extracts the raw pointer from a pointer-like value `ptr`. `absl::RawPtr` is +// useful within templates that need to handle a complement of raw pointers, +// `std::nullptr_t`, and smart pointers. +template +auto RawPtr(T&& ptr) -> decltype(std::addressof(*ptr)) { + // ptr is a forwarding reference to support Ts with non-const operators. + return (ptr != nullptr) ? std::addressof(*ptr) : nullptr; +} +inline std::nullptr_t RawPtr(std::nullptr_t) { return nullptr; } + +// ----------------------------------------------------------------------------- +// Function Template: ShareUniquePtr() +// ----------------------------------------------------------------------------- +// +// Adopts a `std::unique_ptr` rvalue and returns a `std::shared_ptr` of deduced +// type. Ownership (if any) of the held value is transferred to the returned +// shared pointer. +// +// Example: +// +// auto up = absl::make_unique(10); +// auto sp = absl::ShareUniquePtr(std::move(up)); // shared_ptr +// CHECK_EQ(*sp, 10); +// CHECK(up == nullptr); +// +// Note that this conversion is correct even when T is an array type, and more +// generally it works for *any* deleter of the `unique_ptr` (single-object +// deleter, array deleter, or any custom deleter), since the deleter is adopted +// by the shared pointer as well. The deleter is copied (unless it is a +// reference). +// +// Implements the resolution of [LWG 2415](http://wg21.link/lwg2415), by which a +// null shared pointer does not attempt to call the deleter. +template +std::shared_ptr ShareUniquePtr(std::unique_ptr&& ptr) { + return ptr ? std::shared_ptr(std::move(ptr)) : std::shared_ptr(); +} + +// ----------------------------------------------------------------------------- +// Function Template: WeakenPtr() +// ----------------------------------------------------------------------------- +// +// Creates a weak pointer associated with a given shared pointer. The returned +// value is a `std::weak_ptr` of deduced type. +// +// Example: +// +// auto sp = std::make_shared(10); +// auto wp = absl::WeakenPtr(sp); +// CHECK_EQ(sp.get(), wp.lock().get()); +// sp.reset(); +// CHECK(wp.lock() == nullptr); +// +template +std::weak_ptr WeakenPtr(const std::shared_ptr& ptr) { + return std::weak_ptr(ptr); +} + +// ----------------------------------------------------------------------------- +// Class Template: pointer_traits +// ----------------------------------------------------------------------------- +// +// Historical note: Abseil once provided an implementation of +// `std::pointer_traits` for platforms that had not yet provided it. Those +// platforms are no longer supported. New code should simply use +// `std::pointer_traits`. +using std::pointer_traits; + +// ----------------------------------------------------------------------------- +// Class Template: allocator_traits +// ----------------------------------------------------------------------------- +// +// Historical note: Abseil once provided an implementation of +// `std::allocator_traits` for platforms that had not yet provided it. Those +// platforms are no longer supported. New code should simply use +// `std::allocator_traits`. +using std::allocator_traits; + +namespace memory_internal { + +// ExtractOr::type evaluates to E if possible. Otherwise, D. +template