Skip to content

Commit

Permalink
Merge pull request #222 from CppMicroServices/213-check-recursive-ser…
Browse files Browse the repository at this point in the history
…vice-factory-calls

Handle recursive service factory calls.
  • Loading branch information
saschazelzer committed Aug 17, 2017
2 parents 05b605c + 592a368 commit 49ebdc2
Show file tree
Hide file tree
Showing 11 changed files with 388 additions and 176 deletions.
117 changes: 82 additions & 35 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -36,22 +36,23 @@ set(CMAKE_CXX_STANDARD 11)

# We make use of the following C++11 language and library features:
#
# Language feature GCC Clang AppleClang VisualStudio
# ------------------------------------------------------------------------
# Rvalue references 4.3 2.9 ?? 2012
# Variadic templates 4.3 2.9 ?? 2013*
# Initializer lists 4.4 3.1 ?? 2013
# Static assertions 4.3 2.9 ?? 2010
# auto 4.4 2.9 ?? 2010
# Lambdas 4.5 3.1 ?? 2012
# decltype 4.3 2.9 ?? 2012
# Right angle brackets 4.3 2.9 ?? 2010
# Null pointer constant 4.6 3.0 ?? 2010
# Explicit conversion operators 4.5 3.0 ?? 2013
# Defaulted functions 4.4 3.0 ?? 2013*
# Deleted functions 4.4 2.9 ?? 2013*
# Range-based for 4.6 3.0 ?? 2012
# Atomic operations 4.4 3.1 ?? 2012
# Language feature GCC Clang Xcode VisualStudio
# -------------------------------------------------------------------
# Rvalue references 4.3 2.9 6 2012
# Variadic templates 4.3 2.9 6 2013*
# Initializer lists 4.4 3.1 6 2013
# Static assertions 4.3 2.9 6 2010
# auto 4.4 2.9 6 2010
# Lambdas 4.5 3.1 6 2012
# decltype 4.3 2.9 6 2012
# Right angle brackets 4.3 2.9 6 2010
# Null pointer constant 4.6 3.0 6 2010
# Explicit conversion operators 4.5 3.0 6 2013
# Defaulted functions 4.4 3.0 6 2013*
# Deleted functions 4.4 2.9 6 2013*
# Range-based for 4.6 3.0 6 2012
# Atomic operations 4.4 3.1 6 2012
# thread_local (optional) 4.8 3.3 8 2015
#
# Library features used
# ---------------------
Expand All @@ -65,37 +66,42 @@ set(CMAKE_CXX_STANDARD 11)
# wait_condition
# exception_ptr

set(US_COMPILER_GNU_MINIMUM_VERSION 4.6 )
set(US_COMPILER_CLANG_MINIMUM_VERSION 3.1 )
set(US_COMPILER_APPLE_CLANG_MINIMUM_VERSION 5.0 ) # ??
set(US_COMPILER_MSVC_MINIMUM_VERSION 18.00.31101 )
set(US_COMPILER_GNU_MINIMUM_VERSION 4.6 )
set(US_COMPILER_Clang_MINIMUM_VERSION 3.1 )
set(US_COMPILER_AppleClang_MINIMUM_VERSION 6.0 )
set(US_COMPILER_MSVC_MINIMUM_VERSION 18.00.31101 )

set(US_COMPILER_MSVC_MINIMUM_VERSION_PRODUCT "Visual Studio 2013 Update 4")
set(US_COMPILER_GNU_MINIMUM_VERSION_PRODUCT "GCC ${US_COMPILER_GNU_MINIMUM_VERSION}")
set(US_COMPILER_Clang_MINIMUM_VERSION_PRODUCT "Clang ${US_COMPILER_CLANG_MINIMUM_VERSION}")
set(US_COMPILER_AppleClang_MINIMUM_VERSION_PRODUCT "Xcode 6.0 (Clang ${US_COMPILER_AppleClang_MINIMUM_VERSION})")
set(US_COMPILER_MSVC_MINIMUM_VERSION_PRODUCT "Visual Studio 2013 Update 4 (MSVC ${US_COMPILER_MSVC_MINIMUM_VERSION})")

set(US_COMPILER_GNU_MINIMUM_RECOMMENDED_VERSION 4.8 )
set(US_COMPILER_Clang_MINIMUM_RECOMMENDED_VERSION 3.3 )
set(US_COMPILER_AppleClang_MINIMUM_RECOMMENDED_VERSION 8.0 )
set(US_COMPILER_MSVC_MINIMUM_RECOMMENDED_VERSION 19.00 )

set(US_COMPILER_GNU_MINIMUM_RECOMMENDED_VERSION_PRODUCT "GCC ${US_COMPILER_GNU_MINIMUM_RECOMMENDED_VERSION}")
set(US_COMPILER_Clang_MINIMUM_RECOMMENDED_VERSION_PRODUCT "Clang ${US_COMPILER_CLANG_MINIMUM_RECOMMENDED_VERSION}")
set(US_COMPILER_AppleClang_MINIMUM_RECOMMENDED_VERSION_PRODUCT "Xcode 8.0 (Clang ${US_COMPILER_AppleClang_MINIMUM_RECOMMENDED_VERSION})")
set(US_COMPILER_MSVC_MINIMUM_RECOMMENDED_VERSION_PRODUCT "Visual Studio 2015 (MSVC ${US_COMPILER_MSVC_MINIMUM_RECOMMENDED_VERSION})")

if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
if (CMAKE_CXX_COMPILER_VERSION AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS ${US_COMPILER_GNU_MINIMUM_VERSION})
message(FATAL_ERROR "GCC version ${US_COMPILER_GNU_MINIMUM_VERSION} or higher required (you are using version ${CMAKE_CXX_COMPILER_VERSION}).")
endif()
set(US_COMPILER_GNU 1)
elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
if (CMAKE_CXX_COMPILER_VERSION AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS ${US_COMPILER_CLANG_MINIMUM_VERSION})
message(FATAL_ERROR "Clang version ${US_COMPILER_CLANG_MINIMUM_VERSION} or higher required (you are using version ${CMAKE_CXX_COMPILER_VERSION}).")
endif()
set(US_COMPILER_CLANG 1)
elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "AppleClang")
if (CMAKE_CXX_COMPILER_VERSION AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS ${US_COMPILER_APPLE_CLANG_MINIMUM_VERSION})
message(FATAL_ERROR "Apple Clang version ${US_COMPILER_APPLE_CLANG_MINIMUM_VERSION} or higher required (you are using version ${CMAKE_CXX_COMPILER_VERSION}).")
endif()
set(US_COMPILER_APPLE_CLANG 1)
elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
if (CMAKE_CXX_COMPILER_VERSION AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS ${US_COMPILER_MSVC_MINIMUM_VERSION})
message(FATAL_ERROR "${US_COMPILER_MSVC_MINIMUM_VERSION_PRODUCT} or newer required (MSVC ${US_COMPILER_MSVC_MINIMUM_VERSION} or higher, you are using version ${CMAKE_CXX_COMPILER_VERSION})")
endif()
set(US_COMPILER_MSVC 1)
else()
message(WARNING "You are using an unsupported compiler! Compilation has only been tested with Clang (Linux or Apple), GCC and MSVC.")
endif()

if (CMAKE_CXX_COMPILER_VERSION AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS ${US_COMPILER_${CMAKE_CXX_COMPILER_ID}_MINIMUM_VERSION})
message(FATAL_ERROR "${US_COMPILER_${CMAKE_CXX_COMPILER_ID}_MINIMUM_VERSION_PRODUCT} or higher required (you are using ${CMAKE_CXX_COMPILER_ID} ${CMAKE_CXX_COMPILER_VERSION}).")
endif()

#-----------------------------------------------------------------------------
# Update CMake module path
#------------------------------------------------------------------------------
Expand Down Expand Up @@ -304,7 +310,7 @@ if(US_BUILD_TESTING)

set(GTEST_BOTH_LIBRARIES gtest gtest_main)

# This project also sets BUILD_SHARED_LIBS. We want to guarantee that GTest is always built as a
# This project also sets BUILD_SHARED_LIBS. We want to guarantee that GTest is always built as a
# static library even if CppMicroServices is a shared library.
set(BUILD_SHARED_LIBS OFF CACHE BOOL "" FORCE)
add_subdirectory(third_party/googletest)
Expand Down Expand Up @@ -379,7 +385,7 @@ else()
if(NOT US_CXX_TSAN_FLAGS AND US_ENABLE_TSAN)
message(WARNING "TSAN requested, but compiler does not recognize it")
endif()

set(CMAKE_REQUIRED_LIBRARIES "-fprofile-arcs -ftest-coverage")
usFunctionCheckCompilerFlags("-fprofile-arcs -ftest-coverage" US_CXX_COVERAGE_FLAGS)
set(CMAKE_REQUIRED_LIBRARIES "${_orig_req_libs}")
Expand Down Expand Up @@ -458,6 +464,38 @@ include(CheckIncludeFileCXX)

CHECK_INCLUDE_FILE_CXX(cxxabi.h US_HAVE_CXXABI_H)

#-----------------------------------------------------------------------------
# C++ language support
#-----------------------------------------------------------------------------

set(disabled_func )

CHECK_CXX_SOURCE_COMPILES(
"
#include <string>
static thread_local std::string dummy;
int main() {
return 0;
}
" US_HAVE_THREAD_LOCAL)

if(MINGW)
#
# The MinGW winpthread implementation does not properly destruct
# non-POD thread-local objects, leading to segmentation faults on
# thread exit. See
#
# https://github.com/Alexpux/MINGW-packages/issues/2519
# https://sourceforge.net/p/mingw-w64/bugs/527/
#
set(US_HAVE_THREAD_LOCAL 0)
set(disabled_func "${disabled_func}MinGW winpthread: ")
endif()

if(NOT US_HAVE_THREAD_LOCAL AND US_ENABLE_THREADING_SUPPORT)
set(disabled_func "${disabled_func}thread_local support unavailable. Recursive service factory calls will not be detected. See also https://github.com/CppMicroServices/CppMicroServices/issues/213\n")
endif()

#-----------------------------------------------------------------------------
# C++ library support
#-----------------------------------------------------------------------------
Expand All @@ -479,6 +517,15 @@ return 0;
}
" US_HAVE_REGEX)

if(NOT US_HAVE_REGEX AND US_BUILD_TESTING)
set(disabled_func "${disabled_func}regular expression support unavailable. Some tests will be disabled.\n")
endif()

# Print a warning summary
if (disabled_func)
message(WARNING "${US_COMPILER_${CMAKE_CXX_COMPILER_ID}_MINIMUM_RECOMMENDED_VERSION_PRODUCT} or higher recommended (you are using ${CMAKE_CXX_COMPILER_ID} ${CMAKE_CXX_COMPILER_VERSION}). The following functionality will not be available:\n${disabled_func}")
endif()

#-----------------------------------------------------------------------------
# System Information
#-----------------------------------------------------------------------------
Expand Down
23 changes: 16 additions & 7 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ modular and dynamic service-oriented applications. It is based on
`OSGi <http://osgi.org>`_, but tailored to support native cross-platform solutions.

Proper usage of C++ Micro Services patterns and concepts leads to systems
with one or more of the following properties:
with one or more of the following properties:

- Re-use of software components
- Loose coupling between service providers and consumers
Expand All @@ -55,7 +55,16 @@ Supported Platforms
The library makes use of some C++11 features and compiles on many
different platforms.

Minimum required compiler versions:
Recommended minimum required compiler versions:

- GCC 4.8
- Clang 3.3
- Clang from Xcode 8.0
- Visual Studio 2015

You may use older compilers, but certain functionality may not be
available. Check the warnings printed during configuration of
your build tree. The following are the absolute minimum requirements:

- GCC 4.6
- Clang 3.1
Expand Down Expand Up @@ -108,7 +117,7 @@ Quick Start

Essentially, the C++ Micro Services library provides you with a powerful
dynamic service registry on top of a managed lifecycle. The framework manages,
among other things, logical units of modularity called *bundles* that
among other things, logical units of modularity called *bundles* that
are contained in shared or static libraries. Each bundle
within a library has an associated :any:`cppmicroservices::BundleContext`
object, through which the service registry is accessed.
Expand Down Expand Up @@ -189,7 +198,7 @@ file for details about the contribution process.
.. |RTD Build Status (development)| image:: https://readthedocs.org/projects/cppmicroservices/badge/?version=latest&style=flat-square
:target: http://docs.cppmicroservices.org/en/latest/?badge=development
:alt: Documentation Status (development)
.. |Code Coverage Status| image:: https://codecov.io/gh/cppmicroservices/CppMicroServices/branch/master/graph/badge.svg
:target: https://codecov.io/gh/cppmicroservices/CppMicroServices
.. |Code Coverage Status (development)| image:: https://codecov.io/gh/cppmicroservices/CppMicroServices/branch/development/graph/badge.svg
:target: https://codecov.io/gh/cppmicroservices/CppMicroServices
.. |Code Coverage Status| image:: https://img.shields.io/codecov/c/github/CppMicroServices/CppMicroServices/master.svg?style=flat-square
:target: https://codecov.io/gh/cppmicroservices/CppMicroServices/branch/master
.. |Code Coverage Status (development)| image:: https://img.shields.io/codecov/c/github/CppMicroServices/CppMicroServices/development.svg?style=flat-square
:target: https://codecov.io/gh/cppmicroservices/CppMicroServices/branch/development
5 changes: 5 additions & 0 deletions cmake/GlobalConfig.h.in
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,11 @@ US_MSVC_DISABLE_WARNING(4996)
// Mark a variable or expression result as unused
#define US_UNUSED(x) (void)(x)

//-------------------------------------------------------------------
// C++ Language features
//-------------------------------------------------------------------

#cmakedefine US_HAVE_THREAD_LOCAL

//-------------------------------------------------------------------
// C++ Library features
Expand Down
8 changes: 4 additions & 4 deletions framework/include/cppmicroservices/Bundle.h
Original file line number Diff line number Diff line change
Expand Up @@ -329,11 +329,11 @@ class US_Framework_EXPORT Bundle
* <code>BundleContext</code> can be used by the caller to act on behalf
* of this bundle.
*
* If this bundle is not in the <code>STATE_STARTED</code> state, then this
* bundle has no valid <code>BundleContext</code> and this method will
* return a default constructed \c BundleContext object.
* If this bundle is not in the \c STATE_STARTING, \c STATE_ACTIVE, or
* \c STATE_STOPPING states, then this bundle has no valid \c BundleContext
* and this method will return an invalid \c BundleContext object.
*
* @return A valid or invalid <code>BundleContext</code> for this bundle or.
* @return A valid or invalid <code>BundleContext</code> for this bundle.
*/
BundleContext GetBundleContext() const;

Expand Down
89 changes: 48 additions & 41 deletions framework/include/cppmicroservices/ServiceFactory.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,29 +34,24 @@ namespace cppmicroservices {
* A factory for \link Constants::SCOPE_BUNDLE bundle scope\endlink services.
* The factory can provide service objects unique to each bundle.
*
* <p>
* When registering a service, a <code>ServiceFactory</code> object can be
* used instead of a service object, so that the bundle developer can gain
* control of the specific service object granted to a bundle that is using the
* service.
* When registering a service, a \c ServiceFactory object can be
* used instead of a service object, so that the bundle developer can create
* a customized service object for each bundle that is using the service.
*
* <p>
* When this happens, the
* <code>BundleContext::GetService(const ServiceReference&)</code> method calls the
* <code>ServiceFactory::GetService</code> method to create a service object
* specifically for the requesting bundle. The service object returned by the
* <code>ServiceFactory</code> is cached by the framework until the bundle
* releases its use of the service.
* When a bundle requests the service object, the framework calls the
* \c ServiceFactory::GetService method to return a service object
* customized for the requesting bundle. The returned service object is
* cached by the framework for subsequent calls to
* <code>BundleContext::GetService(const ServiceReference&)</code> until
* the bundle releases its use of the service.
*
* <p>
* When the bundle's use count for the service equals zero (including the bundle
* stopping or the service being unregistered), the
* <code>ServiceFactory::UngetService</code> method is called.
* When the bundle's use count for the service is decremented to zero
* (including the bundle stopping or the service being unregistered), the
* framework will call the \c ServiceFactory::UngetService method.
*
* <p>
* <code>ServiceFactory</code> objects are only used by the framework and are
* not made available to other bundles in the bundle environment. The framework
* may concurrently call a <code>ServiceFactory</code>.
* \c ServiceFactory objects are only used by the framework and are not
* made available to other bundles in the bundle environment. The framework
* may concurrently call a \c ServiceFactory.
*
* @see BundleContext#GetService
* @see PrototypeServiceFactory
Expand All @@ -70,25 +65,35 @@ class ServiceFactory
virtual ~ServiceFactory() {}

/**
* Creates a new service object.
* Returns a service object for a bundle.
*
* <p>
* The Framework invokes this method the first time the specified
* <code>bundle</code> requests a service object using the
* The framework invokes this method the first time the specified
* \c bundle requests a service object using the
* <code>BundleContext::GetService(const ServiceReferenceBase&)</code> method. The
* service factory can then return a specific service object for each
* factory can then return a customized service object for each bundle.
*
* The framework checks that the returned service object is valid. If the
* returned service object is null or does not contain entries for all the
* classes named when the service was registered, a framework event of type
* \c FrameworkEvent::FRAMEWORK_ERROR is fired containing a service exception
* of type \c ServiceException::FACTORY_ERROR and null is returned to the
* bundle. If this method throws an exception, a framework event of type
* \c FrameworkEvent::FRAMEWORK_ERROR is fired containing a service exception
* of type \c ServiceException::FACTORY_EXCEPTION with the thrown exception
* as a nested exception and null is returned to the bundle. If this method is
* recursively called for the specified bundle, a framework event of type
* \c FrameworkEvent::FRAMEWORK_ERROR is fired containing a service exception
* of type \c ServiceException::FACTORY_RECURSION and null is returned to the
* bundle.
*
* <p>
* The framework caches the value returned (unless the InterfaceMap is empty),
* and will return the same service object on any future call to
* <code>BundleContext::GetService</code> for the same bundles. This means the
* framework does not allow this method to be concurrently called for the
* same bundle.
* The framework caches the valid service object, and will return the same
* service object on any future call to \c BundleContext::GetService for the
* specified bundle. This means the framework does not allow this method to
* be concurrently called for the specified bundle.
*
* @param bundle The bundle using the service.
* @param registration The <code>ServiceRegistrationBase</code> object for the
* service.
* @param bundle The bundle requesting the service.
* @param registration The \c ServiceRegistrationBase object for the
* requested service.
* @return A service object that <strong>must</strong> contain entries for all
* the interfaces named when the service was registered.
* @see BundleContext#GetService
Expand All @@ -98,17 +103,19 @@ class ServiceFactory
const ServiceRegistrationBase& registration) = 0;

/**
* Releases a service object.
* Releases a service object customized for a bundle.
*
* <p>
* The framework invokes this method when a service has been released by a
* bundle.
* The Framework invokes this method when a service has been released by a
* bundle. If this method throws an exception, a framework event of type
* \c FrameworkEvent::FRAMEWORK_ERROR is fired containing a service
* exception of type \c ServiceException::FACTORY_EXCEPTION with the thrown
* exception as a nested exception.
*
* @param bundle The Bundle releasing the service.
* @param registration The <code>ServiceRegistration</code> object for the
* service.
* @param registration The \c ServiceRegistration object for the
* service being released.
* @param service The service object returned by a previous call to the
* <code>ServiceFactory::GetService</code> method.
* \c ServiceFactory::GetService method.
* @see InterfaceMapConstPtr
*/
virtual void UngetService(const Bundle& bundle,
Expand Down
4 changes: 2 additions & 2 deletions framework/src/bundle/BundlePrivate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -366,8 +366,8 @@ void BundlePrivate::FinalizeActivation(LockType& l)
// finalization already in progress.
return;
}
// Lazy activation; fall through to STATE_RESOLVED.
}
// INTENTIONALLY FALLS THROUGH - in case of lazy activation.
case Bundle::STATE_RESOLVED:
{
// 6:
Expand Down Expand Up @@ -439,8 +439,8 @@ void BundlePrivate::Uninstall()
coreCtx->listeners.SendFrameworkEvent(FrameworkEvent(FrameworkEvent::Type::FRAMEWORK_WARNING, MakeBundle(shared_from_this()), std::string(), std::current_exception()));
}
}
// Fall through
}
// INTENTIONALLY FALLS THROUGH
case Bundle::STATE_RESOLVED:
case Bundle::STATE_INSTALLED:
{
Expand Down

0 comments on commit 49ebdc2

Please sign in to comment.