/** * SPDX license identifier: MPL-2.0 * * Copyright (C) 2012, BMW AG * * \author Christian Linke, christian.linke@bmw.de BMW 2011,2012 * \author Alesksandar Donchev, aleksander.donchev@partner.bmw.de BMW 2015 * * \copyright * This Source Code Form is subject to the terms of the * Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with * this file, You can obtain one at http://mozilla.org/MPL/2.0/. * * \file CAmSerializer.h * For further information see http://www.genivi.org/. */ #ifndef CAMSERIALIZER_H_ #define CAMSERIALIZER_H_ #include #include #include #include #include #include #include "CAmDltWrapper.h" #include "CAmSocketHandler.h" /** * todo: performance improvement we could implement a memory pool that is more efficient here and avoids * allocation and deallocation times. */ namespace am { /** * magic class that does the serialization of functions calls * The constructor must be called within the main threadcontext, after that using the * overloaded template function call will serialize all calls and call them within the * main thread context.\n * More details can be found here: \ref util * \warning asynchronous calls may be used in the mainthread context, but if you want to use synchronous calls make sure that you use one * instance of this class per thread otherwise you could be lost in never returning calls.\n * Examples of the usage can be found in IAmCommandReceiverShadow of the ControlPlugin or IAmRoutingReceiverShadow of the * PluginRoutingInterfaceAsync. * */ class CAmSerializer { private: /** * Prototype for a delegate */ class CAmDelegate { public: typedef enum:bool { SyncCallType = false, AsyncCallType = true } CallType; virtual ~CAmDelegate() {}; virtual CallType call(int* pipe)=0; }; /** * Prototype for a delegate with variadic template arguments in conjunction with the following class. */ template class CAmDelegateAsyncImpl : public CAmDelegate { Class mInstance; Method mMethod; Tuple mArguments; public: friend class CAmSerializer; static void call(Class instance, Method method, Tuple && arguments) { CAmDelegateAsyncImpl::call(instance, method, std::forward(arguments)); } CAmDelegateAsyncImpl(Class instance, Method method, Tuple && arguments) { mInstance = instance; mMethod = method; mArguments = std::move(arguments); } CallType call(int* pipe) { (void) pipe; call(mInstance, mMethod, std::forward(mArguments)); return (AsyncCallType); }; }; /** * Prototype for a delegate with variadic template arguments. */ template class CAmDelegateAsyncImpl : public CAmDelegate { Class mInstance; Method mMethod; Tuple mArguments; public: friend class CAmSerializer; static void call(Class instance, Method method, Tuple && t) { (*instance.*method)(std::get(std::forward(t))...); } CAmDelegateAsyncImpl(Class instance, Method method, Tuple && arguments) { mInstance = instance; mMethod = method; mArguments = std::move(arguments); } CallType call(int* pipe) { (void) pipe; call(mInstance, mMethod, std::forward(mArguments)); return (AsyncCallType); }; }; /** * Prototype for a delegate with variadic template arguments in conjunction with the following class. */ template class CAmDelegateSyncImpl : public CAmDelegate { Class mInstance; Method mMethod; Tuple mArguments; Return mReturn; public: friend class CAmSerializer; static void call(Class instance,Method method, Return & result, Tuple && arguments) { CAmDelegateSyncImpl::call(instance, method, result, std::forward(arguments)); } CAmDelegateSyncImpl(Class instance, Method method, Tuple && arguments) { mInstance = instance; mMethod = method; mArguments = std::move(arguments); } CallType call(int* pipe) { call(mInstance, mMethod, mReturn, std::forward(mArguments)); ssize_t result(-1); result = write(pipe[1], this, sizeof(this)); if (result == -1) logError("CAmSerializer: Problem writing into pipe! Error No:",errno); return (SyncCallType); }; }; /** * Prototype for a delegate with variadic template arguments. */ template class CAmDelegateSyncImpl : public CAmDelegate { Class mInstance; Method mMethod; Tuple mArguments; Return mReturn; public: friend class CAmSerializer; static void call(Class instance, Method method, Return & result, Tuple && t) { result = (*instance.*method)(std::get(t)...); } CAmDelegateSyncImpl(Class instance, Method method, Tuple && arguments) { mInstance = instance; mMethod = method; mArguments = std::move(arguments); } CallType call(int* pipe) { call(mInstance, mMethod, mReturn, std::forward(mArguments)); ssize_t result(-1); result = write(pipe[1], this, sizeof(this)); if (result == -1) logError("CAmSerializer: Problem writing into pipe! Error No:",errno); return (SyncCallType); }; }; typedef CAmDelegate* CAmDelegagePtr; //!< pointer to a delegate public: /** * instantiates a async delegate with given arguments and sends the delegate pointer over the pipe */ template void doAsyncCall(Class intsance, Method method, Tuple & arguments) { typedef typename std::decay::type ttype; typedef CAmDelegateAsyncImpl::value, std::tuple_size::value> AsyncDelegate; AsyncDelegate *pImp = new AsyncDelegate(intsance, method, std::forward(arguments)); send(pImp); //Do not delete the pointer. It will be deleted automatically later. } /** * instantiates a sync delegate with given arguments and sends the delegate pointer over the pipe */ template void doSyncCall(Class intsance, Method method, Return & result, Tuple & arguments) { typedef typename std::decay::type ttype; typedef CAmDelegateSyncImpl::value, std::tuple_size::value> SyncDelegate; SyncDelegate *pImp = new SyncDelegate(intsance, method, std::forward(arguments)); send(pImp); int numReads; SyncDelegate *p = NULL; if ((numReads = read(mReturnPipe[0], &p, sizeof(p))) == -1) { logError("CAmSerializer::doSyncCall could not read pipe!"); throw std::runtime_error("CAmSerializer Could not read pipe!"); } result = std::move(pImp->mReturn); arguments = std::move(pImp->mArguments); //Delete the pointer. delete pImp; } private: /** * rings the line of the pipe and adds the delegate pointer to the queue * @param p delegate pointer */ inline void send(CAmDelegagePtr p) { if (write(mPipe[1], &p, sizeof(p)) == -1) { throw std::runtime_error("could not write to pipe !"); } } int mPipe[2]; //!< the pipe int mReturnPipe[2]; //!< pipe handling returns std::deque mListDelegatePoiters; //!< intermediate queue to store the pipe results public: /** * calls a function with variadic arguments threadsafe * @param instance the instance of the class that shall be called * @param function the function that shall be called as member function pointer. * @param output variable. * @tparam TClass the type of the Class to be called * @tparam TRet the type of the result * @tparam TArgs argument list * \section ex Example: * @code * class MyGreatClass * { * public: * int AGreatMethod(int x); * } * CAmSerializer serial(&Sockethandler); * MyGreatClass anInstance; * int result; * serial.syncCall(&anInstance,&MyGreatClass::AGreatMethod, result, 100); * @endcode */ template void syncCall(TClass* instance, TRet (TClass::*method)(TArgs ...), TRet & result, TArgs & ... arguments) { auto t = std::make_tuple(arguments...); doSyncCall(instance, method, result, t); std::tie(arguments...) = t; } /** * calls a function with variadic arguments threadsafe * @param instance the instance of the class that shall be called * @param function the function that shall be called as member function pointer. * @tparam TClass the type of the Class to be called * @tparam TRet the type of the result * @tparam TArgs argument list * \section ex Example: * @code * class MyGreatClass * { * public: * int AGreatMethod(int x); * } * CAmSerializer serial(&Sockethandler); * MyGreatClass anInstance; * serial.asyncCall(&anInstance,&MyGreatClass::AGreatMethod, 100); * @endcode */ template void asyncCall(TClass* instance, TRet (TClass::*method)(TArgs ...), TArgs & ... arguments) { auto t = std::make_tuple(arguments...); doAsyncCall(instance, method, t); } /** * calls a function with no arguments threadsafe * @param instance the instance of the class that shall be called * @param function the function that shall be called as memberfunction pointer. * @tparam TClass1 the type of the Class to be called * \section ex Example: * @code * class myClass * { * public: * void myfunction(); * } * CAmSerializer serial(&Sockethandler); * myClass instanceMyClass; * serial(&instanceMyClass,&myClass::myfunction); * @endcode */ template void asyncCall(TClass* instance, void (TClass::*function)()) { auto t = std::make_tuple(); doAsyncCall(instance, function, t); } /** * calls a function with one arguments asynchronously threadsafe * @param instance the instance of the class that shall be called * @param function the function that shall be called as memberfunction pointer. * @param argument the argument * @tparam TClass1 the type of the Class to be called * @tparam Targ the type of the argument to be called * \section ex Example: * @code * class myClass * { * public: * void myfunction(int k); * } * CAmSerializer serial(&Sockethandler); * myClass instanceMyClass; * serial(&instanceMyClass,&myClass::myfunction,k); * @endcode * */ template void asyncCall(TClass1* instance, void (TClass1::*function)(Targ), Targ argument) { auto t = std::make_tuple(argument); doAsyncCall(instance, function, t); } /** * calls a function with one argument called by reference asynchronously threadsafe * @param instance the instance of the class that shall be called * @param function the function that shall be called as memberfunction pointer. * @param argument the argument * @tparam TClass1 the type of the Class to be called * @tparam Targ the type of the argument to be called * \section ex Example: * @code * class myClass * { * public: * void myfunction(int k); * } * CAmSerializer serial(&Sockethandler); * myClass instanceMyClass; * serial(&instanceMyClass,&myClass::myfunction,k); * @endcode * */ template void asyncCall(TClass1* instance, void (TClass1::*function)(Targ&), Targ& argument) { auto t = std::make_tuple(argument); doAsyncCall(instance, function, t); } /** * calls a function with two arguments asynchronously threadsafe. for more see asyncCall with one argument * @param instance pointer to the instance of the class * @param function memberfunction poitner * @param argument the first argument * @param argument1 the second argument * @tparam TClass1 the type of the Class to be called * @tparam Targ the type of the argument to be called * @tparam Targ1 the type of the first argument to be called */ template void asyncCall(TClass1* instance, void (TClass1::*function)(Targ argument, Targ1 argument1), Targ argument, Targ1 argument1) { auto t = std::make_tuple(argument,argument1); doAsyncCall(instance, function, t); } /** * calls a function with two arguments asynchronously threadsafe, first argument is a reference. for more see asyncCall with one argument * @param instance pointer to the instance of the class * @param function memberfunction poitner * @param argument the first argument * @param argument1 the second argument * @tparam TClass1 the type of the Class to be called * @tparam Targ the type of the argument to be called * @tparam Targ1 the type of the first argument to be called */ template void asyncCall(TClass1* instance, void (TClass1::*function)(Targ& argument, Targ1 argument1), Targ& argument, Targ1 argument1) { auto t = std::make_tuple(argument,argument1); doAsyncCall(instance, function, t); } /** * calls a function with two arguments asynchronously threadsafe, second argument is a reference. for more see asyncCall with one argument * @param instance pointer to the instance of the class * @param function memberfunction poitner * @param argument the first argument * @param argument1 the second argument * @tparam TClass1 the type of the Class to be called * @tparam Targ the type of the argument to be called * @tparam Targ1 the type of the first argument to be called */ template void asyncCall(TClass1* instance, void (TClass1::*function)(Targ argument, Targ1& argument1), Targ argument, Targ1& argument1) { auto t = std::make_tuple(argument,argument1); doAsyncCall(instance, function, t); } /** * calls a function with two arguments asynchronously threadsafe, both arguments are references. for more see asyncCall with one argument * @param instance pointer to the instance of the class * @param function memberfunction poitner * @param argument the first argument * @param argument1 the second argument * @tparam TClass1 the type of the Class to be called * @tparam Targ the type of the argument to be called * @tparam Targ1 the type of the first argument to be called */ template void asyncCall(TClass1* instance, void (TClass1::*function)(Targ& argument, Targ1& argument1), Targ& argument, Targ1& argument1) { auto t = std::make_tuple(argument,argument1); doAsyncCall(instance, function, t); } /** * calls a function with three arguments asynchronously threadsafe. for more see other asycCall */ template void asyncCall(TClass1* instance, void (TClass1::*function)(Targ argument, Targ1 argument1, Targ2 argument2), Targ argument, Targ1 argument1, Targ2 argument2) { auto t = std::make_tuple(argument,argument1, argument2); doAsyncCall(instance, function, t); } /** * calls a function with three arguments asynchronously threadsafe. for more see other asycCall */ template void asyncCall(TClass1* instance, void (TClass1::*function)(Targ& argument, Targ1 argument1, Targ2 argument2), Targ& argument, Targ1 argument1, Targ2 argument2) { auto t = std::make_tuple(argument,argument1, argument2); doAsyncCall(instance, function, t); } /** * calls a function with three arguments asynchronously threadsafe. for more see other asycCall */ template void asyncCall(TClass1* instance, void (TClass1::*function)(Targ argument, Targ1& argument1, Targ2 argument2), Targ argument, Targ1& argument1, Targ2 argument2) { auto t = std::make_tuple(argument,argument1, argument2); doAsyncCall(instance, function, t); } /** * calls a function with three arguments asynchronously threadsafe. for more see other asycCall */ template void asyncCall(TClass1* instance, void (TClass1::*function)(Targ argument, Targ1 argument1, Targ2& argument2), Targ argument, Targ1 argument1, Targ2& argument2) { auto t = std::make_tuple(argument,argument1, argument2); doAsyncCall(instance, function, t); } /** * calls a function with three arguments asynchronously threadsafe. for more see other asycCall */ template void asyncCall(TClass1* instance, void (TClass1::*function)(Targ argument, Targ1& argument1, Targ2& argument2), Targ argument, Targ1& argument1, Targ2& argument2) { auto t = std::make_tuple(argument,argument1, argument2); doAsyncCall(instance, function, t); } /** * calls a function with three arguments asynchronously threadsafe. for more see other asycCall */ template void asyncCall(TClass1* instance, void (TClass1::*function)(Targ& argument, Targ1& argument1, Targ2& argument2), Targ& argument, Targ1& argument1, Targ2& argument2) { auto t = std::make_tuple(argument,argument1, argument2); doAsyncCall(instance, function, t); } /** * calls a function with three arguments asynchronously threadsafe. for more see other asycCall */ template void asyncCall(TClass1* instance, void (TClass1::*function)(Targ& argument, Targ1& argument1, Targ2 argument2), Targ& argument, Targ1& argument1, Targ2 argument2) { auto t = std::make_tuple(argument,argument1, argument2); doAsyncCall(instance, function, t); } /** * calls a function with three arguments asynchronously threadsafe. for more see other asycCall */ template void asyncCall(TClass1* instance, void (TClass1::*function)(Targ& argument, Targ1 argument1, Targ2& argument2), Targ& argument, Targ1 argument1, Targ2& argument2) { auto t = std::make_tuple(argument,argument1, argument2); doAsyncCall(instance, function, t); } /** * calls a function with four arguments asynchronously threadsafe. for more see other asycCall */ template void asyncCall(TClass1* instance, void (TClass1::*function)(Targ argument, Targ1 argument1, Targ2 argument2, Targ3 argument3), Targ argument, Targ1 argument1, Targ2 argument2, Targ3 argument3) { auto t = std::make_tuple(argument,argument1, argument2,argument3); doAsyncCall(instance, function, t); } /** * calls a synchronous function with no arguments threadsafe * @param instance the instance of the class that shall be called * @param function the function that shall be called as memberfunction pointer. * @param retVal the return parameter, no const allowed ! * @tparam TClass1 the type of the class to be called * @tparam TretVal the type of the return parameter * \section ex Example: * @code * class myClass * { * public: * am_Error_e myfunction(); * } * CAmSerializer serial(&Sockethandler); * myClass instanceMyClass; * am_Error_e error; * serial(&instanceMyClass,&myClass::myfunction, error); * @endcode * All arguments given to synchronous functions must be non-const since the results of the operations will be written back to the arguments. * */ template void syncCall(TClass1* instance, TretVal (TClass1::*function)(), TretVal& retVal) { auto t = std::make_tuple(); doSyncCall(instance, function, retVal, t); } /** * calls a function with one argument synchronous threadsafe * @param instance the instance of the class that shall be called * @param function the function that shall be called as memberfunction pointer. * @param retVal the return parameter, no const allowed ! * @param argument the argument, no const allowed ! * @tparam TClass1 the type of the class to be called * @tparam TretVal the type of the return parameter * @tparam TargCall the type of the argument like in the function to be called. here all references and const must be * respected! * @tparam Targ the type of the argument, here no const and no references allowed ! * \section ex Example: * @code * class myClass * { * public: * am_Error_e myfunction(int k); * } * CAmSerializer serial(&Sockethandler); * myClass instanceMyClass; * am_Error_e error; * int l; * serial(&instanceMyClass,&myClass::myfunction,error,l); * @endcode * All arguments given to synchronous functions must be non-const since the results of the operations will be written back to the arguments. */ template void syncCall(TClass1* instance, TretVal (TClass1::*function)(TargCall), TretVal& retVal, Targ& argument) { auto t = std::make_tuple(argument); doSyncCall(instance, function, retVal, t); std::tie(argument) = t; } /** * calls a function with one argument synchronous threadsafe for const functions. For more see syncCall with one argument */ template void syncCall(TClass1* instance, TretVal (TClass1::*function)(TargCall) const, TretVal& retVal, Targ& argument) { auto t = std::make_tuple(argument); doSyncCall(instance, function, retVal, t); std::tie(argument) = t; } /** * calls a function with two arguments synchronously threadsafe. For more see syncCall with one argument */ template void syncCall(TClass1* instance, TretVal (TClass1::*function)(TargCall, Targ1Call), TretVal& retVal, Targ& argument, Targ1& argument1) { auto t = std::make_tuple(argument, argument1); doSyncCall(instance, function, retVal, t); std::tie(argument, argument1) = t; } /** * calls a function with two arguments synchronously threadsafe const. For more see syncCall with one argument */ template void syncCall(TClass1* instance, TretVal (TClass1::*function)(TargCall, Targ1Call) const, TretVal& retVal, Targ& argument, Targ1& argument1) { auto t = std::make_tuple(argument, argument1); doSyncCall(instance, function, retVal, t); std::tie(argument, argument1) = t; } /** * calls a function with three arguments synchronously threadsafe. for more see syncCall with one argument */ template void syncCall(TClass1* instance, TretVal (TClass1::*function)(TargCall, TargCall1, TargCall2), TretVal& retVal, Targ& argument, Targ1& argument1, Targ2& argument2) { auto t = std::make_tuple(argument, argument1, argument2); doSyncCall(instance, function, retVal, t); std::tie(argument, argument1,argument2) = t; } /** * calls a const function with three arguments synchronously threadsafe. for more see syncCall with one argument */ template void syncCall(TClass1* instance, TretVal (TClass1::*function)(TargCall, TargCall1, TargCall2) const, TretVal& retVal, Targ& argument, Targ1& argument1, Targ2& argument2) { auto t = std::make_tuple(argument, argument1, argument2); doSyncCall(instance, function, retVal, t); std::tie(argument, argument1,argument2) = t; } /** * calls a function with four arguments synchronously threadsafe. for more see syncCall with one argument */ template void syncCall(TClass1* instance, TretVal (TClass1::*function)(TargCall, TargCall1, TargCall2, TargCall3), TretVal& retVal, Targ& argument, Targ1& argument1, Targ2& argument2, Targ3& argument3) { auto t = std::make_tuple(argument, argument1, argument2, argument3); doSyncCall(instance, function, retVal, t); std::tie(argument, argument1,argument2, argument3) = t; } /** * calls a function with five arguments synchronously threadsafe. for more see syncCall with one argument */ template void syncCall(TClass1* instance, TretVal (TClass1::*function)(TargCall, TargCall1, TargCall2, TargCall3, TargCall4), TretVal& retVal, Targ& argument, Targ1& argument1, Targ2& argument2, Targ3& argument3, Targ4& argument4) { auto t = std::make_tuple(argument, argument1, argument2, argument3, argument4); doSyncCall(instance, function, retVal, t); std::tie(argument, argument1,argument2, argument3, argument4) = t; } /** * calls a function with six arguments synchronously threadsafe. for more see syncCall with one argument */ template void syncCall(TClass1* instance, TretVal (TClass1::*function)(TargCall, TargCall1, TargCall2, TargCall3, TargCall4, TargCall5), TretVal& retVal, Targ& argument, Targ1& argument1, Targ2& argument2, Targ3& argument3, Targ4& argument4, Targ5& argument5) { auto t = std::make_tuple(argument, argument1, argument2, argument3, argument4, argument5); doSyncCall(instance, function, retVal, t); std::tie(argument, argument1,argument2, argument3, argument4, argument5) = t; } /** * receiver callback for sockethandling, for more, see CAmSocketHandler */ void receiverCallback(const pollfd pollfd, const sh_pollHandle_t handle, void* userData) { (void) handle; (void) userData; int numReads; CAmDelegagePtr listPointers[3]; if ((numReads = read(pollfd.fd, &listPointers, sizeof(listPointers))) == -1) { logError("CAmSerializer::receiverCallback could not read pipe!"); throw std::runtime_error("CAmSerializer Could not read pipe!"); } mListDelegatePoiters.assign(listPointers, listPointers + (numReads / sizeof(CAmDelegagePtr))); } /** * checker callback for sockethandling, for more, see CAmSocketHandler */ bool checkerCallback(const sh_pollHandle_t handle, void* userData) { (void) handle; (void) userData; if (mListDelegatePoiters.empty()) return (false); return (true); } /** * dispatcher callback for sockethandling, for more, see CAmSocketHandler */ bool dispatcherCallback(const sh_pollHandle_t handle, void* userData) { (void) handle; (void) userData; CAmDelegagePtr delegatePoiter = mListDelegatePoiters.front(); mListDelegatePoiters.pop_front(); if (delegatePoiter->call(mReturnPipe)) delete delegatePoiter; if (mListDelegatePoiters.empty()) return (false); return (true); } TAmShPollFired receiverCallbackT; TAmShPollDispatch dispatcherCallbackT; TAmShPollCheck checkerCallbackT; /** * The constructor must be called in the mainthread context ! * @param iSocketHandler pointer to the CAmSocketHandler */ CAmSerializer(CAmSocketHandler *iSocketHandler) : mPipe(), // mListDelegatePoiters(), // receiverCallbackT(this, &CAmSerializer::receiverCallback), // dispatcherCallbackT(this, &CAmSerializer::dispatcherCallback), // checkerCallbackT(this, &CAmSerializer::checkerCallback) { assert(NULL!=iSocketHandler); if (pipe(mPipe) == -1) { logError("CAmSerializer could not create pipe!"); throw std::runtime_error("CAmSerializer Could not open pipe!"); } if (pipe(mReturnPipe) == -1) { logError("CAmSerializer could not create mReturnPipe!"); throw std::runtime_error("CAmSerializer Could not open mReturnPipe!"); } short event = 0; sh_pollHandle_t handle; event |= POLLIN; iSocketHandler->addFDPoll(mPipe[0], event, NULL, &receiverCallbackT, &checkerCallbackT, &dispatcherCallbackT, NULL, handle); } ~CAmSerializer() { close(mPipe[0]); close(mPipe[1]); close(mReturnPipe[0]); close(mReturnPipe[1]); } }; } /* namespace am */ #endif /* CAMSERIALIZER_H_ */