diff --git a/libretroshare/src/gxs/gxstokenqueue.cc b/libretroshare/src/gxs/gxstokenqueue.cc index c5ebf87fd..c9eb8ef92 100644 --- a/libretroshare/src/gxs/gxstokenqueue.cc +++ b/libretroshare/src/gxs/gxstokenqueue.cc @@ -36,20 +36,17 @@ bool GxsTokenQueue::queueRequest(uint32_t token, uint32_t req_type) void GxsTokenQueue::checkRequests() { { - RsStackMutex stack(mQueueMtx); /********** STACK LOCKED MTX ******/ - if (mQueue.empty()) - { - return; - } + RS_STACK_MUTEX(mQueueMtx); + if (mQueue.empty()) return; } // Must check all, and move to a different list - for reentrant / good mutex behaviour. std::list toload; std::list::iterator it; - bool stuffToLoad = false; + { - RsStackMutex stack(mQueueMtx); /********** STACK LOCKED MTX ******/ + RS_STACK_MUTEX(mQueueMtx); for(it = mQueue.begin(); it != mQueue.end();) { uint32_t token = it->mToken; @@ -62,29 +59,29 @@ void GxsTokenQueue::checkRequests() stuffToLoad = true; #ifdef GXS_DEBUG - std::cerr << "GxsTokenQueue::checkRequests() token: " << token << " Complete"; - std::cerr << std::endl; + std::cerr << "GxsTokenQueue::checkRequests() token: " << token + << " Complete" << std::endl; #endif ++it; } else if (status == RsTokenService::FAILED) { // maybe we should do alternative callback? - std::cerr << "GxsTokenQueue::checkRequests() ERROR Request Failed: " << token; - std::cerr << std::endl; + std::cerr << __PRETTY_FUNCTION__ << " ERROR Request Failed! " + << " token: " << token << std::endl; it = mQueue.erase(it); } else { #ifdef GXS_DEBUG - std::cerr << "GxsTokenQueue::checkRequests() token: " << token << " is unfinished, status: " << status; - std::cerr << std::endl; + std::cerr << "GxsTokenQueue::checkRequests() token: " << token + << " is unfinished, status: " << status << std::endl; #endif ++it; } } - } + } // RS_STACK_MUTEX(mQueueMtx) END if (stuffToLoad) { @@ -95,11 +92,3 @@ void GxsTokenQueue::checkRequests() } } - // This must be overloaded to complete the functionality. -void GxsTokenQueue::handleResponse(uint32_t token, uint32_t req_type) -{ - std::cerr << "GxsTokenQueue::handleResponse(" << token << "," << req_type << ") ERROR: NOT HANDLED"; - std::cerr << std::endl; -} - - diff --git a/libretroshare/src/gxs/gxstokenqueue.h b/libretroshare/src/gxs/gxstokenqueue.h index 0b58f7c3d..c6c32262e 100644 --- a/libretroshare/src/gxs/gxstokenqueue.h +++ b/libretroshare/src/gxs/gxstokenqueue.h @@ -54,7 +54,7 @@ public: protected: /// This must be overloaded to complete the functionality. - virtual void handleResponse(uint32_t token, uint32_t req_type); + virtual void handleResponse(uint32_t token, uint32_t req_type) = 0; private: RsGenExchange *mGenExchange; diff --git a/libretroshare/src/gxs/rsdataservice.cc b/libretroshare/src/gxs/rsdataservice.cc index ca83a2b19..b10c4aec9 100644 --- a/libretroshare/src/gxs/rsdataservice.cc +++ b/libretroshare/src/gxs/rsdataservice.cc @@ -1198,16 +1198,16 @@ void RsDataService::locked_retrieveGroups(RetroCursor* c, std::vector } } -int RsDataService::retrieveNxsMsgs(const GxsMsgReq &reqIds, GxsMsgResult &msg, bool /* cache */, bool withMeta) +int RsDataService::retrieveNxsMsgs( + const GxsMsgReq &reqIds, GxsMsgResult &msg, bool /* cache */, + bool withMeta ) { #ifdef RS_DATA_SERVICE_DEBUG_TIME rstime::RsScopeTimer timer(""); int resultCount = 0; #endif - GxsMsgReq::const_iterator mit = reqIds.begin(); - - for(; mit != reqIds.end(); ++mit) + for(auto mit = reqIds.begin(); mit != reqIds.end(); ++mit) { const RsGxsGroupId& grpId = mit->first; @@ -1216,9 +1216,9 @@ int RsDataService::retrieveNxsMsgs(const GxsMsgReq &reqIds, GxsMsgResult &msg, b const std::set& msgIdV = mit->second; std::vector msgSet; - if(msgIdV.empty()){ - - RsStackMutex stack(mDbMutex); + if(msgIdV.empty()) + { + RS_STACK_MUTEX(mDbMutex); RetroCursor* c = mDb->sqlQuery(MSG_TABLE_NAME, withMeta ? mMsgColumnsWithMeta : mMsgColumns, KEY_GRP_ID+ "='" + grpId.toStdString() + "'", ""); @@ -1228,16 +1228,17 @@ int RsDataService::retrieveNxsMsgs(const GxsMsgReq &reqIds, GxsMsgResult &msg, b } delete c; - }else{ + } + else + { + RS_STACK_MUTEX(mDbMutex); // request each grp - std::set::const_iterator sit = msgIdV.begin(); - - for(; sit!=msgIdV.end();++sit){ + for( std::set::const_iterator sit = msgIdV.begin(); + sit!=msgIdV.end();++sit ) + { const RsGxsMessageId& msgId = *sit; - RsStackMutex stack(mDbMutex); - RetroCursor* c = mDb->sqlQuery(MSG_TABLE_NAME, withMeta ? mMsgColumnsWithMeta : mMsgColumns, KEY_GRP_ID+ "='" + grpId.toStdString() + "' AND " + KEY_MSG_ID + "='" + msgId.toStdString() + "'", ""); diff --git a/libretroshare/src/gxs/rsdataservice.h b/libretroshare/src/gxs/rsdataservice.h index 572558cd8..8b4e2aac9 100644 --- a/libretroshare/src/gxs/rsdataservice.h +++ b/libretroshare/src/gxs/rsdataservice.h @@ -47,10 +47,14 @@ public: * Retrieves all msgs * @param reqIds requested msg ids (grpId,msgId), leave msg list empty to get all msgs for the grp * @param msg result of msg retrieval - * @param cache whether to store results of this retrieval in memory for faster later retrieval + * @param cache IGNORED whether to store results of this retrieval in memory + * for faster later retrieval + * @param strictFilter if true do not request any message if reqIds is empty * @return error code - */ - int retrieveNxsMsgs(const GxsMsgReq& reqIds, GxsMsgResult& msg, bool cache, bool withMeta = false); + */ + int retrieveNxsMsgs( + const GxsMsgReq& reqIds, GxsMsgResult& msg, bool cache, + bool withMeta = false ); /*! * Retrieves groups, if empty, retrieves all grps, if map is not empty diff --git a/libretroshare/src/gxs/rsgds.h b/libretroshare/src/gxs/rsgds.h index 3c63efb2c..2235249a7 100644 --- a/libretroshare/src/gxs/rsgds.h +++ b/libretroshare/src/gxs/rsgds.h @@ -137,16 +137,19 @@ public: typedef std::map MsgStoreMap; RsGeneralDataService(){} - virtual ~RsGeneralDataService(){return;} + virtual ~RsGeneralDataService(){} /*! * Retrieves all msgs * @param reqIds requested msg ids (grpId,msgId), leave msg list empty to get all msgs for the grp * @param msg result of msg retrieval * @param cache whether to store results of this retrieval in memory for faster later retrieval + * @param strictFilter if true do not request any message if reqIds is empty * @return error code - */ - virtual int retrieveNxsMsgs(const GxsMsgReq& reqIds, GxsMsgResult& msg, bool cache, bool withMeta=false) = 0; + */ + virtual int retrieveNxsMsgs( + const GxsMsgReq& reqIds, GxsMsgResult& msg, bool cache, + bool withMeta = false ) = 0; /*! * Retrieves all groups stored diff --git a/libretroshare/src/gxs/rsgenexchange.cc b/libretroshare/src/gxs/rsgenexchange.cc index 1ffaf793a..183751356 100644 --- a/libretroshare/src/gxs/rsgenexchange.cc +++ b/libretroshare/src/gxs/rsgenexchange.cc @@ -1925,7 +1925,7 @@ void RsGenExchange::setGroupStatusFlags(uint32_t& token, const RsGxsGroupId& grp void RsGenExchange::setGroupServiceString(uint32_t& token, const RsGxsGroupId& grpId, const std::string& servString) { - RS_STACK_MUTEX(mGenMtx) ; + RS_STACK_MUTEX(mGenMtx); token = mDataAccess->generatePublicToken(); GrpLocMetaData g; diff --git a/libretroshare/src/gxs/rsgxsdataaccess.cc b/libretroshare/src/gxs/rsgxsdataaccess.cc index a72123f92..f9049d9f0 100644 --- a/libretroshare/src/gxs/rsgxsdataaccess.cc +++ b/libretroshare/src/gxs/rsgxsdataaccess.cc @@ -39,14 +39,16 @@ RsGxsDataAccess::~RsGxsDataAccess() for(std::map::const_iterator it(mRequests.begin());it!=mRequests.end();++it) delete it->second ; } -bool RsGxsDataAccess::requestGroupInfo(uint32_t &token, uint32_t ansType, const RsTokReqOptions &opts, - const std::list &groupIds) +bool RsGxsDataAccess::requestGroupInfo( + uint32_t &token, uint32_t ansType, const RsTokReqOptions &opts, + const std::list &groupIds ) { - if(groupIds.empty()) - { - std::cerr << "(WW) Group Id list is empty" << std::endl; - return false; - } + if(groupIds.empty()) + { + std::cerr << __PRETTY_FUNCTION__ << " (WW) Group Id list is empty!" + << std::endl; + return false; + } GxsRequest* req = NULL; uint32_t reqType = opts.mReqType; @@ -76,19 +78,19 @@ bool RsGxsDataAccess::requestGroupInfo(uint32_t &token, uint32_t ansType, const req = gir; } - if(req == NULL) - { - std::cerr << "RsGxsDataAccess::requestGroupInfo() request type not recognised, type " - << reqType << std::endl; - return false; - }else - { - generateToken(token); + if(!req) + { + std::cerr << __PRETTY_FUNCTION__ << " request type not recognised, " + << "reqType: " << reqType << std::endl; + return false; + } + + generateToken(token); #ifdef DATA_DEBUG - std::cerr << "RsGxsDataAccess::requestGroupInfo() gets Token: " << token << std::endl; + std::cerr << "RsGxsDataAccess::requestGroupInfo() gets token: " << token + << std::endl; #endif - } setReq(req, token, ansType, opts); storeRequest(req); @@ -130,11 +132,8 @@ bool RsGxsDataAccess::requestGroupInfo(uint32_t &token, uint32_t ansType, const void RsGxsDataAccess::generateToken(uint32_t &token) { - RsStackMutex stack(mDataMutex); /****** LOCKED *****/ - + RS_STACK_MUTEX(mDataMutex); token = mNextToken++; - - return; } @@ -301,15 +300,12 @@ void RsGxsDataAccess::setReq(GxsRequest* req, uint32_t token, uint32_t ansType, req->Options = opts; return; } -void RsGxsDataAccess::storeRequest(GxsRequest* req) +void RsGxsDataAccess::storeRequest(GxsRequest* req) { - RsStackMutex stack(mDataMutex); /****** LOCKED *****/ - - req->status = PENDING; - req->reqTime = time(NULL); + RS_STACK_MUTEX(mDataMutex); + req->status = PENDING; + req->reqTime = time(NULL); mRequests[req->token] = req; - - return; } RsTokenService::GxsRequestStatus RsGxsDataAccess::requestStatus(uint32_t token) @@ -1040,29 +1036,42 @@ bool RsGxsDataAccess::getMsgData(MsgDataReq* req) { GxsMsgReq msgIdOut; + const RsTokReqOptions& opts(req->Options); + // filter based on options - getMsgList(req->mMsgIds, req->Options, msgIdOut); + getMsgList(req->mMsgIds, opts, msgIdOut); + + // If the list is empty because of filtering do not retrieve from DB + if((opts.mMsgFlagMask || opts.mStatusMask) && msgIdOut.empty()) + return true; mDataStore->retrieveNxsMsgs(msgIdOut, req->mMsgData, true, true); - return true; } bool RsGxsDataAccess::getMsgSummary(MsgMetaReq* req) { - GxsMsgReq msgIdOut; + GxsMsgReq msgIdOut; - // filter based on options - getMsgList(req->mMsgIds, req->Options, msgIdOut); + const RsTokReqOptions& opts(req->Options); - mDataStore->retrieveGxsMsgMetaData(msgIdOut, req->mMsgMetaData); + // filter based on options + getMsgList(req->mMsgIds, opts, msgIdOut); + + // If the list is empty because of filtering do not retrieve from DB + if((opts.mMsgFlagMask || opts.mStatusMask) && msgIdOut.empty()) + return true; + + mDataStore->retrieveGxsMsgMetaData(msgIdOut, req->mMsgMetaData); return true; } -bool RsGxsDataAccess::getMsgList(const GxsMsgReq& msgIds, const RsTokReqOptions& opts, GxsMsgReq& msgIdsOut) +bool RsGxsDataAccess::getMsgList( + const GxsMsgReq& msgIds, const RsTokReqOptions& opts, + GxsMsgReq& msgIdsOut ) { GxsMsgMetaResult result; @@ -1693,41 +1702,45 @@ void RsGxsDataAccess::cleanseMsgMetaMap(GxsMsgMetaResult& result) return; } -void RsGxsDataAccess::filterMsgList(GxsMsgIdResult& msgIds, const RsTokReqOptions& opts, - const MsgMetaFilter& msgMetas) const +void RsGxsDataAccess::filterMsgList( + GxsMsgIdResult& resultsMap, const RsTokReqOptions& opts, + const MsgMetaFilter& msgMetas ) const { - - GxsMsgIdResult::iterator mit = msgIds.begin(); - for(;mit != msgIds.end(); ++mit) + for( GxsMsgIdResult::iterator grpIt = resultsMap.begin(); + grpIt != resultsMap.end(); ++grpIt ) { + const RsGxsGroupId& groupId(grpIt->first); + std::set& msgsIdSet(grpIt->second); - MsgMetaFilter::const_iterator cit = msgMetas.find(mit->first); + MsgMetaFilter::const_iterator cit = msgMetas.find(groupId); + if(cit == msgMetas.end()) continue; - if(cit == msgMetas.end()) - continue; + std::cerr << __PRETTY_FUNCTION__ << " " << msgsIdSet.size() + << " for group: " << groupId << " before filtering" + << std::endl; - std::set& msgs = mit->second; - std::set::iterator vit = msgs.begin(); - const std::map& meta = cit->second; - std::map::const_iterator cit2; - - for(; vit != msgs.end();) + for( std::set::iterator msgIdIt = msgsIdSet.begin(); + msgIdIt != msgsIdSet.end(); ) { + const RsGxsMessageId& msgId(*msgIdIt); + const std::map& msgsMetaMap = + cit->second; bool keep = false; - if( (cit2 = meta.find(*vit)) != meta.end() ) + std::map::const_iterator msgsMetaMapIt; + + if( (msgsMetaMapIt = msgsMetaMap.find(msgId)) != msgsMetaMap.end() ) { - keep = checkMsgFilter(opts, cit2->second); + keep = checkMsgFilter(opts, msgsMetaMapIt->second); } - if(keep) - { - ++vit; - }else - { - vit = msgs.erase(vit); - } + if(keep) ++msgIdIt; + else msgIdIt = msgsIdSet.erase(msgIdIt); } + + std::cerr << __PRETTY_FUNCTION__ << " " << msgsIdSet.size() + << " for group: " << groupId << " after filtering" + << std::endl; } } @@ -1905,62 +1918,87 @@ bool RsGxsDataAccess::checkGrpFilter(const RsTokReqOptions &opts, const RsGxsGrp return subscribeMatch; } -bool RsGxsDataAccess::checkMsgFilter(const RsTokReqOptions& opts, const RsGxsMsgMetaData* meta) const +bool RsGxsDataAccess::checkMsgFilter( + const RsTokReqOptions& opts, const RsGxsMsgMetaData* meta ) const { - bool statusMatch = false; - if (opts.mStatusMask) + if (opts.mStatusMask) { // Exact Flags match required. - if ((opts.mStatusMask & opts.mStatusFilter) == (opts.mStatusMask & meta->mMsgStatus)) + if ( (opts.mStatusMask & opts.mStatusFilter) == + (opts.mStatusMask & meta->mMsgStatus) ) { - std::cerr << "checkMsgFilter() Accepting Msg as StatusMatches: "; - std::cerr << " Mask: " << opts.mStatusMask << " StatusFilter: " << opts.mStatusFilter; - std::cerr << " MsgStatus: " << meta->mMsgStatus << " MsgId: " << meta->mMsgId; - std::cerr << std::endl; - - statusMatch = true; +#ifdef DATA_DEBUG + std::cerr << __PRETTY_FUNCTION__ + << " Continue checking Msg as StatusMatches: " + << " Mask: " << opts.mStatusMask + << " StatusFilter: " << opts.mStatusFilter + << " MsgStatus: " << meta->mMsgStatus + << " MsgId: " << meta->mMsgId << std::endl; +#endif } else { - std::cerr << "checkMsgFilter() Dropping Msg due to !StatusMatch "; - std::cerr << " Mask: " << opts.mStatusMask << " StatusFilter: " << opts.mStatusFilter; - std::cerr << " MsgStatus: " << meta->mMsgStatus << " MsgId: " << meta->mMsgId; - std::cerr << std::endl; +#ifdef DATA_DEBUG + std::cerr << __PRETTY_FUNCTION__ + << " Dropping Msg due to !StatusMatch " + << " Mask: " << opts.mStatusMask + << " StatusFilter: " << opts.mStatusFilter + << " MsgStatus: " << meta->mMsgStatus + << " MsgId: " << meta->mMsgId << std::endl; +#endif + + return false; } } else { - // no status comparision, - statusMatch = true; +#ifdef DATA_DEBUG + std::cerr << __PRETTY_FUNCTION__ + << " Status check not requested" + << " mStatusMask: " << opts.mStatusMask + << " MsgId: " << meta->mMsgId << std::endl; +#endif } - bool flagMatch = false; + if(opts.mMsgFlagMask) + { + // Exact Flags match required. + if ( (opts.mMsgFlagMask & opts.mMsgFlagFilter) == + (opts.mMsgFlagMask & meta->mMsgFlags) ) + { +#ifdef DATA_DEBUG + std::cerr << __PRETTY_FUNCTION__ + << " Accepting Msg as FlagMatches: " + << " Mask: " << opts.mMsgFlagMask + << " FlagFilter: " << opts.mMsgFlagFilter + << " MsgFlag: " << meta->mMsgFlags + << " MsgId: " << meta->mMsgId << std::endl; +#endif + } + else + { +#ifdef DATA_DEBUG + std::cerr << __PRETTY_FUNCTION__ + << " Dropping Msg due to !FlagMatch " + << " Mask: " << opts.mMsgFlagMask + << " FlagFilter: " << opts.mMsgFlagFilter + << " MsgFlag: " << meta->mMsgFlags + << " MsgId: " << meta->mMsgId << std::endl; +#endif - if(opts.mMsgFlagMask) - { - // Exact Flags match required. - if ((opts.mMsgFlagMask & opts.mMsgFlagFilter) == (opts.mMsgFlagMask & meta->mMsgFlags)) - { - std::cerr << "checkMsgFilter() Accepting Msg as FlagMatches: "; - std::cerr << " Mask: " << opts.mMsgFlagMask << " FlagFilter: " << opts.mMsgFlagFilter; - std::cerr << " MsgFlag: " << meta->mMsgFlags << " MsgId: " << meta->mMsgId; - std::cerr << std::endl; + return false; + } + } + else + { +#ifdef DATA_DEBUG + std::cerr << __PRETTY_FUNCTION__ + << " Flags check not requested" + << " mMsgFlagMask: " << opts.mMsgFlagMask + << " MsgId: " << meta->mMsgId << std::endl; +#endif + } - flagMatch = true; - } - else - { - std::cerr << "checkMsgFilter() Dropping Msg due to !FlagMatch "; - std::cerr << " Mask: " << opts.mMsgFlagMask << " FlagFilter: " << opts.mMsgFlagFilter; - std::cerr << " MsgFlag: " << meta->mMsgFlags << " MsgId: " << meta->mMsgId; - std::cerr << std::endl; - - flagMatch = false; - } - }else{ - flagMatch = true; - } - - return statusMatch && flagMatch; + return true; } diff --git a/libretroshare/src/gxs/rsgxsnetservice.cc b/libretroshare/src/gxs/rsgxsnetservice.cc index 8e1aca41a..a7b558e2b 100644 --- a/libretroshare/src/gxs/rsgxsnetservice.cc +++ b/libretroshare/src/gxs/rsgxsnetservice.cc @@ -5290,7 +5290,8 @@ void RsGxsNetService::receiveTurtleSearchResults(TurtleRequestId req,const unsig #ifdef NXS_NET_DEBUG_8 GXSNETDEBUG___ << " passing the grp data to observer." << std::endl; #endif - mObserver->receiveNewGroups(new_grps); + mObserver->receiveNewGroups(new_grps); + mObserver->receiveDistantSearchResults(req, grpId); } bool RsGxsNetService::search( const std::string& substring, diff --git a/libretroshare/src/retroshare/rsgxschannels.h b/libretroshare/src/retroshare/rsgxschannels.h index cc95cf0bd..37edf7b05 100644 --- a/libretroshare/src/retroshare/rsgxschannels.h +++ b/libretroshare/src/retroshare/rsgxschannels.h @@ -22,7 +22,7 @@ * * *******************************************************************************/ -#include +#include #include #include #include @@ -32,6 +32,7 @@ #include "retroshare/rsgxscommon.h" #include "serialiser/rsserializable.h" #include "retroshare/rsturtle.h" +#include "util/rsdeprecate.h" class RsGxsChannels; @@ -100,10 +101,70 @@ std::ostream &operator<<(std::ostream& out, const RsGxsChannelPost& post); class RsGxsChannels: public RsGxsIfaceHelper, public RsGxsCommentService { public: - explicit RsGxsChannels(RsGxsIface& gxs) : RsGxsIfaceHelper(gxs) {} virtual ~RsGxsChannels() {} + /** + * @brief Create channel. Blocking API. + * @jsonapi{development} + * @param[inout] channel Channel data (name, description...) + * @return false on error, true otherwise + */ + virtual bool createChannel(RsGxsChannelGroup& channel) = 0; + + /** + * @brief Create channel post. Blocking API. + * @jsonapi{development} + * @param[inout] post + * @return false on error, true otherwise + */ + virtual bool createPost(RsGxsChannelPost& post) = 0; + + /** + * @brief Edit channel details. + * @jsonapi{development} + * @param[in] channel Channel data (name, description...) with modifications + * @return false on error, true otherwise + */ + virtual bool editChannel(RsGxsChannelGroup& channel) = 0; + + /** + * @brief Share extra file + * Can be used to share extra file attached to a channel post + * @jsonapi{development} + * @param[in] path file path + * @return false on error, true otherwise + */ + virtual bool ExtraFileHash(const std::string& path) = 0; + + /** + * @brief Remove extra file from shared files + * @jsonapi{development} + * @param[in] hash hash of the file to remove + * @return false on error, true otherwise + */ + virtual bool ExtraFileRemove(const RsFileHash& hash) = 0; + + /** + * @brief Get auto-download option value for given channel + * @jsonapi{development} + * @param[in] channelId channel id + * @param[out] enabled storage for the auto-download option value + * @return false if something failed, true otherwhise + */ + virtual bool getChannelAutoDownload( + const RsGxsGroupId& channelId, bool& enabled ) = 0; + + /** + * @brief Get download directory for the given channel + * @jsonapi{development} + * @param[in] channelId id of the channel + * @param[out] directory reference to string where to store the path + * @return false on error, true otherwise + */ + virtual bool getChannelDownloadDirectory( const RsGxsGroupId& channelId, + std::string& directory ) = 0; + /** * @brief Get channels summaries list. Blocking API. * @jsonapi{development} @@ -138,63 +199,37 @@ public: std::vector& comments ) = 0; /** - * @brief Create channel. Blocking API. + * @brief Toggle post read status. Blocking API. * @jsonapi{development} - * @param[inout] channel Channel data (name, description...) + * @param[in] postId post identifier + * @param[in] read true to mark as read, false to mark as unread * @return false on error, true otherwise */ - virtual bool createChannel(RsGxsChannelGroup& channel) = 0; + virtual bool markRead(const RsGxsGrpMsgIdPair& postId, bool read) = 0; /** - * @brief Create channel post. Blocking API. + * @brief Enable or disable auto-download for given channel. Blocking API * @jsonapi{development} - * @param[inout] post - * @return false on error, true otherwise - */ - virtual bool createPost(RsGxsChannelPost& post) = 0; - - - /* Specific Service Data - * TODO: change the orrible const uint32_t &token to uint32_t token - * TODO: create a new typedef for token so code is easier to read - */ - - virtual bool getGroupData(const uint32_t &token, std::vector &groups) = 0; - virtual bool getPostData(const uint32_t &token, std::vector &posts, std::vector &cmts) = 0; - virtual bool getPostData(const uint32_t &token, std::vector &posts) = 0; - - /** - * @brief toggle message read status - * @jsonapi{development} - * @param[out] token GXS token queue token - * @param[in] msgId - * @param[in] read - */ - virtual void setMessageReadStatus( - uint32_t& token, const RsGxsGrpMsgIdPair& msgId, bool read) = 0; - - /** - * @brief Enable or disable auto-download for given channel - * @jsonapi{development} - * @param[in] groupId channel id + * @param[in] channelId channel id * @param[in] enable true to enable, false to disable * @return false if something failed, true otherwhise */ virtual bool setChannelAutoDownload( - const RsGxsGroupId &groupId, bool enable) = 0; + const RsGxsGroupId& channelId, bool enable ) = 0; /** - * @brief Get auto-download option value for given channel + * @brief Share channel publishing key + * This can be used to authorize other peers to post on the channel * @jsonapi{development} - * @param[in] groupId channel id - * @param[in] enabled storage for the auto-download option value - * @return false if something failed, true otherwhise + * @param[in] channelId id of the channel + * @param[in] peers peers to share the key with + * @return false on error, true otherwise */ - virtual bool getChannelAutoDownload( - const RsGxsGroupId &groupId, bool& enabled) = 0; + virtual bool shareChannelKeys( + const RsGxsGroupId& channelId, const std::set& peers ) = 0; /** - * @brief Set download directory for the given channel + * @brief Set download directory for the given channel. Blocking API. * @jsonapi{development} * @param[in] channelId id of the channel * @param[in] directory path @@ -204,90 +239,14 @@ public: const RsGxsGroupId& channelId, const std::string& directory) = 0; /** - * @brief Get download directory for the given channel + * @brief Subscrbe to a channel. Blocking API * @jsonapi{development} - * @param[in] channelId id of the channel - * @param[out] directory reference to string where to store the path + * @param[in] channelId Channel id + * @param[in] subscribe true to subscribe, false to unsubscribe * @return false on error, true otherwise */ - virtual bool getChannelDownloadDirectory( const RsGxsGroupId& channelId, - std::string& directory ) = 0; - - /** - * @brief Share channel publishing key - * This can be used to authorize other peers to post on the channel - * @jsonapi{development} - * @param[in] groupId Channel id - * @param[in] peers peers to which share the key - * @return false on error, true otherwise - */ - virtual bool groupShareKeys( - const RsGxsGroupId& groupId, const std::set& peers ) = 0; - - /** - * @brief Request subscription to a group. - * The action is performed asyncronously, so it could fail in a subsequent - * phase even after returning true. - * @jsonapi{development} - * @param[out] token Storage for RsTokenService token to track request - * status. - * @param[in] groupId Channel id - * @param[in] subscribe - * @return false on error, true otherwise - */ - virtual bool subscribeToGroup( uint32_t& token, const RsGxsGroupId &groupId, - bool subscribe ) = 0; - - /** - * @brief Request channel creation. - * The action is performed asyncronously, so it could fail in a subsequent - * phase even after returning true. - * @param[out] token Storage for RsTokenService token to track request - * status. - * @param[in] group Channel data (name, description...) - * @return false on error, true otherwise - */ - virtual bool createGroup(uint32_t& token, RsGxsChannelGroup& group) = 0; - - /** - * @brief Request post creation. - * The action is performed asyncronously, so it could fail in a subsequent - * phase even after returning true. - * @param[out] token Storage for RsTokenService token to track request - * status. - * @param[in] post - * @return false on error, true otherwise - */ - virtual bool createPost(uint32_t& token, RsGxsChannelPost& post) = 0; - - /** - * @brief Request channel change. - * The action is performed asyncronously, so it could fail in a subsequent - * phase even after returning true. - * @jsonapi{development} - * @param[out] token Storage for RsTokenService token to track request - * status. - * @param[in] group Channel data (name, description...) with modifications - * @return false on error, true otherwise - */ - virtual bool updateGroup(uint32_t& token, RsGxsChannelGroup& group) = 0; - - /** - * @brief Share extra file - * Can be used to share extra file attached to a channel post - * @jsonapi{development} - * @param[in] path file path - * @return false on error, true otherwise - */ - virtual bool ExtraFileHash(const std::string& path) = 0; - - /** - * @brief Remove extra file from shared files - * @jsonapi{development} - * @param[in] hash hash of the file to remove - * @return false on error, true otherwise - */ - virtual bool ExtraFileRemove(const RsFileHash& hash) = 0; + virtual bool subscribeToChannel( const RsGxsGroupId &channelId, + bool subscribe ) = 0; /** * @brief Request remote channels search @@ -303,15 +262,123 @@ public: const std::function& multiCallback, rstime_t maxWait = 300 ) = 0; + /** + * @brief Request remote channel + * @jsonapi{development} + * @param[in] channelId id of the channel to request to distants peers + * @param multiCallback function that will be called each time a result is + * received + * @param[in] maxWait maximum wait time in seconds for search results + * @return false on error, true otherwise + */ + virtual bool turtleChannelRequest( + const RsGxsGroupId& channelId, + const std::function& multiCallback, + rstime_t maxWait = 300 ) = 0; + + + /* Following functions are deprecated as they expose internal functioning + * semantic, instead of a safe to use API */ + + RS_DEPRECATED_FOR(getChannelsInfo) + virtual bool getGroupData(const uint32_t &token, std::vector &groups) = 0; + + RS_DEPRECATED_FOR(getChannelsContent) + virtual bool getPostData(const uint32_t &token, std::vector &posts, std::vector &cmts) = 0; + + RS_DEPRECATED_FOR(getChannelsContent) + virtual bool getPostData(const uint32_t &token, std::vector &posts) = 0; + + /** + * @brief toggle message read status + * @deprecated + * @param[out] token GXS token queue token + * @param[in] msgId + * @param[in] read + */ + RS_DEPRECATED_FOR(markRead) + virtual void setMessageReadStatus( + uint32_t& token, const RsGxsGrpMsgIdPair& msgId, bool read) = 0; + + /** + * @brief Share channel publishing key + * This can be used to authorize other peers to post on the channel + * @deprecated + * @param[in] groupId Channel id + * @param[in] peers peers to which share the key + * @return false on error, true otherwise + */ + RS_DEPRECATED_FOR(shareChannelKeys) + virtual bool groupShareKeys( + const RsGxsGroupId& groupId, const std::set& peers ) = 0; + + /** + * @brief Request subscription to a group. + * The action is performed asyncronously, so it could fail in a subsequent + * phase even after returning true. + * @deprecated + * @param[out] token Storage for RsTokenService token to track request + * status. + * @param[in] groupId Channel id + * @param[in] subscribe + * @return false on error, true otherwise + */ + RS_DEPRECATED_FOR(subscribeToChannel) + virtual bool subscribeToGroup( uint32_t& token, const RsGxsGroupId &groupId, + bool subscribe ) = 0; + + /** + * @brief Request channel creation. + * The action is performed asyncronously, so it could fail in a subsequent + * phase even after returning true. + * @deprecated + * @param[out] token Storage for RsTokenService token to track request + * status. + * @param[in] group Channel data (name, description...) + * @return false on error, true otherwise + */ + RS_DEPRECATED_FOR(createChannel) + virtual bool createGroup(uint32_t& token, RsGxsChannelGroup& group) = 0; + + /** + * @brief Request post creation. + * The action is performed asyncronously, so it could fail in a subsequent + * phase even after returning true. + * @deprecated + * @param[out] token Storage for RsTokenService token to track request + * status. + * @param[in] post + * @return false on error, true otherwise + */ + RS_DEPRECATED + virtual bool createPost(uint32_t& token, RsGxsChannelPost& post) = 0; + + /** + * @brief Request channel change. + * The action is performed asyncronously, so it could fail in a subsequent + * phase even after returning true. + * @deprecated + * @param[out] token Storage for RsTokenService token to track request + * status. + * @param[in] group Channel data (name, description...) with modifications + * @return false on error, true otherwise + */ + RS_DEPRECATED_FOR(editChannel) + virtual bool updateGroup(uint32_t& token, RsGxsChannelGroup& group) = 0; + ////////////////////////////////////////////////////////////////////////////// /// Distant synchronisation methods /// ////////////////////////////////////////////////////////////////////////////// /// + RS_DEPRECATED_FOR(turtleChannelRequest) virtual TurtleRequestId turtleGroupRequest(const RsGxsGroupId& group_id)=0; + RS_DEPRECATED virtual TurtleRequestId turtleSearchRequest(const std::string& match_string)=0; + RS_DEPRECATED_FOR(turtleSearchRequest) virtual bool retrieveDistantSearchResults(TurtleRequestId req, std::map &results) =0; + RS_DEPRECATED virtual bool clearDistantSearchResults(TurtleRequestId req)=0; + RS_DEPRECATED_FOR(turtleChannelRequest) virtual bool retrieveDistantGroup(const RsGxsGroupId& group_id,RsGxsChannelGroup& distant_group)=0; - ////////////////////////////////////////////////////////////////////////////// }; diff --git a/libretroshare/src/services/p3gxschannels.cc b/libretroshare/src/services/p3gxschannels.cc index 3897cbb2a..0b4c05dca 100644 --- a/libretroshare/src/services/p3gxschannels.cc +++ b/libretroshare/src/services/p3gxschannels.cc @@ -76,7 +76,10 @@ p3GxsChannels::p3GxsChannels( RsGenExchange( gds, nes, new RsGxsChannelSerialiser(), RS_SERVICE_GXS_TYPE_CHANNELS, gixs, channelsAuthenPolicy() ), RsGxsChannels(static_cast(*this)), GxsTokenQueue(this), - mSearchCallbacksMapMutex("GXS channels search") + mSubscribedGroupsMutex("GXS channels subscribed groups cache"), + mKnownChannelsMutex("GXS channels known channels timestamp cache"), + mSearchCallbacksMapMutex("GXS channels search callbacks map"), + mDistantChannelsCallbacksMapMutex("GXS channels distant channels callbacks map") { // For Dummy Msgs. mGenActive = false; @@ -174,7 +177,10 @@ bool p3GxsChannels::saveList(bool &cleanup, std::list&saveList) RsGxsForumNotifyRecordsItem *item = new RsGxsForumNotifyRecordsItem ; - item->records = mKnownChannels ; + { + RS_STACK_MUTEX(mKnownChannelsMutex); + item->records = mKnownChannels; + } saveList.push_back(item) ; return true; @@ -191,8 +197,9 @@ bool p3GxsChannels::loadList(std::list& loadList) RsGxsForumNotifyRecordsItem *fnr = dynamic_cast(item) ; - if(fnr != NULL) + if(fnr) { + RS_STACK_MUTEX(mKnownChannelsMutex); mKnownChannels.clear(); for(auto it(fnr->records.begin());it!=fnr->records.end();++it) @@ -228,7 +235,7 @@ void p3GxsChannels::notifyChanges(std::vector &changes) std::cerr << "p3GxsChannels::notifyChanges() : " << changes.size() << "changes to notify" << std::endl; #endif - p3Notify *notify = NULL; + p3Notify* notify = nullptr; if (!changes.empty()) { notify = RsServer::notify(); @@ -271,9 +278,8 @@ void p3GxsChannels::notifyChanges(std::vector &changes) std::cerr << "p3GxsChannels::notifyChanges() Msgs for Group: " << mit->first; std::cerr << std::endl; #endif - bool enabled = false ; - - if (autoDownloadEnabled(mit->first, enabled) && enabled) + bool enabled = false; + if (autoDownloadEnabled(mit->first, enabled) && enabled) { #ifdef GXSCHANNELS_DEBUG std::cerr << "p3GxsChannels::notifyChanges() AutoDownload for Group: " << mit->first; @@ -306,6 +312,7 @@ void p3GxsChannels::notifyChanges(std::vector &changes) /* group received */ std::list &grpList = grpChange->mGrpIdList; std::list::iterator git; + RS_STACK_MUTEX(mKnownChannelsMutex); for (git = grpList.begin(); git != grpList.end(); ++git) { if(mKnownChannels.find(*git) == mKnownChannels.end()) @@ -338,15 +345,15 @@ void p3GxsChannels::notifyChanges(std::vector &changes) /* shouldn't need to worry about groups - as they need to be subscribed to */ } - request_SpecificSubscribedGroups(unprocessedGroups); + if(!unprocessedGroups.empty()) + request_SpecificSubscribedGroups(unprocessedGroups); RsGxsIfaceHelper::receiveChanges(changes); } void p3GxsChannels::service_tick() { - -static rstime_t last_dummy_tick = 0; + static rstime_t last_dummy_tick = 0; if (time(NULL) > last_dummy_tick + 5) { @@ -414,75 +421,84 @@ bool p3GxsChannels::groupShareKeys( * at the moment - fix it up later */ -bool p3GxsChannels::getPostData(const uint32_t &token, std::vector &msgs, std::vector &cmts) +bool p3GxsChannels::getPostData( + const uint32_t &token, std::vector &msgs, + std::vector &cmts ) { #ifdef GXSCHANNELS_DEBUG - std::cerr << "p3GxsChannels::getPostData()"; - std::cerr << std::endl; + std::cerr << __PRETTY_FUNCTION__ << std::cerr << std::endl; #endif GxsMsgDataMap msgData; - bool ok = RsGenExchange::getMsgData(token, msgData); - - if(ok) + if(!RsGenExchange::getMsgData(token, msgData)) { - GxsMsgDataMap::iterator mit = msgData.begin(); - - for(; mit != msgData.end(); ++mit) + std::cerr << __PRETTY_FUNCTION__ <<" ERROR in request" << std::endl; + return false; + } + + GxsMsgDataMap::iterator mit = msgData.begin(); + + for(; mit != msgData.end(); ++mit) + { + std::vector& msgItems = mit->second; + std::vector::iterator vit = msgItems.begin(); + + for(; vit != msgItems.end(); ++vit) { - std::vector& msgItems = mit->second; - std::vector::iterator vit = msgItems.begin(); + RsGxsChannelPostItem* postItem = + dynamic_cast(*vit); - for(; vit != msgItems.end(); ++vit) + if(postItem) { - RsGxsChannelPostItem* postItem = dynamic_cast(*vit); - - if(postItem) + RsGxsChannelPost msg; + postItem->toChannelPost(msg, true); + msgs.push_back(msg); + delete postItem; + } + else + { + RsGxsCommentItem* cmtItem = + dynamic_cast(*vit); + if(cmtItem) { - RsGxsChannelPost msg; - postItem->toChannelPost(msg, true); - msgs.push_back(msg); - delete postItem; + RsGxsComment cmt; + RsGxsMsgItem *mi = (*vit); + cmt = cmtItem->mMsg; + cmt.mMeta = mi->meta; +#ifdef GXSCOMMENT_DEBUG + std::cerr << "p3GxsChannels::getPostData Found Comment:" << std::endl; + cmt.print(std::cerr," ", "cmt"); +#endif + cmts.push_back(cmt); + delete cmtItem; } else { - RsGxsCommentItem* cmtItem = dynamic_cast(*vit); - if(cmtItem) - { - RsGxsComment cmt; - RsGxsMsgItem *mi = (*vit); - cmt = cmtItem->mMsg; - cmt.mMeta = mi->meta; -#ifdef GXSCOMMENT_DEBUG - std::cerr << "p3GxsChannels::getPostData Found Comment:" << std::endl; - cmt.print(std::cerr," ", "cmt"); -#endif - cmts.push_back(cmt); - delete cmtItem; - } - else - { - RsGxsMsgItem* msg = (*vit); - //const uint16_t RS_SERVICE_GXS_TYPE_CHANNELS = 0x0217; - //const uint8_t RS_PKT_SUBTYPE_GXSCHANNEL_POST_ITEM = 0x03; - //const uint8_t RS_PKT_SUBTYPE_GXSCOMMENT_COMMENT_ITEM = 0xf1; - std::cerr << "Not a GxsChannelPostItem neither a RsGxsCommentItem" - << " PacketService=" << std::hex << (int)msg->PacketService() << std::dec - << " PacketSubType=" << std::hex << (int)msg->PacketSubType() << std::dec - << " , deleting!" << std::endl; - delete *vit; - } + RsGxsMsgItem* msg = (*vit); + //const uint16_t RS_SERVICE_GXS_TYPE_CHANNELS = 0x0217; + //const uint8_t RS_PKT_SUBTYPE_GXSCHANNEL_POST_ITEM = 0x03; + //const uint8_t RS_PKT_SUBTYPE_GXSCOMMENT_COMMENT_ITEM = 0xf1; + std::cerr << __PRETTY_FUNCTION__ + << " Not a GxsChannelPostItem neither a " + << "RsGxsCommentItem PacketService=" << std::hex + << (int)msg->PacketService() << std::dec + << " PacketSubType=" << std::hex + << (int)msg->PacketSubType() << std::dec + << " , deleting!" << std::endl; + delete *vit; } } } } - else - { - std::cerr << "p3GxsChannels::getPostData() ERROR in request"; - std::cerr << std::endl; - } - return ok; + return true; +} + +bool p3GxsChannels::getPostData( + const uint32_t& token, std::vector& posts ) +{ + std::vector cmts; + return getPostData(token, posts, cmts); } //Not currently used @@ -548,21 +564,6 @@ bool p3GxsChannels::getPostData(const uint32_t &token, std::vector::iterator it; - - it = mSubscribedGroups.find(groupId); - if (it == mSubscribedGroups.end()) - { -#ifdef GXSCHANNELS_DEBUG - std::cerr << "p3GxsChannels::setAutoDownload() Missing Group" << std::endl; -#endif - return false; - } + it = mSubscribedGroups.find(groupId); + if (it == mSubscribedGroups.end()) + { + std::cerr << __PRETTY_FUNCTION__ << " Error! Unknown groupId: " + << groupId.toStdString() << std::endl; + return false; + } /* extract from ServiceString */ SSGxsChannelGroup ss; ss.load(it->second.mServiceString); - if (directory == ss.mDownloadDirectory) - { -#ifdef GXSCHANNELS_DEBUG - std::cerr << "p3GxsChannels::setDownloadDirectory() WARNING setting looks okay already" << std::endl; -#endif + if (directory == ss.mDownloadDirectory) + { + std::cerr << __PRETTY_FUNCTION__ << " Warning! groupId: " << groupId + << " Was already configured to download into: " << directory + << std::endl; + return false; + } - } - - /* we are just going to set it anyway. */ ss.mDownloadDirectory = directory; std::string serviceString = ss.save(); uint32_t token; @@ -611,6 +613,13 @@ bool p3GxsChannels::setChannelDownloadDirectory(const RsGxsGroupId &groupId, con it->second.mServiceString = serviceString; // update Local Cache. RsGenExchange::setGroupServiceString(token, groupId, serviceString); // update dbase. + if(waitToken(token) != RsTokenService::COMPLETE) + { + std::cerr << __PRETTY_FUNCTION__ << " Error! Feiled setting group " + << " service string" << std::endl; + return false; + } + /* now reload it */ std::list groups; groups.push_back(groupId); @@ -626,25 +635,24 @@ bool p3GxsChannels::getChannelDownloadDirectory(const RsGxsGroupId & groupId,std std::cerr << "p3GxsChannels::getChannelDownloadDirectory(" << id << ")" << std::endl; #endif + RS_STACK_MUTEX(mSubscribedGroupsMutex); + std::map::iterator it; - it = mSubscribedGroups.find(groupId); - - if (it == mSubscribedGroups.end()) - { -#ifdef GXSCHANNELS_DEBUG - std::cerr << "p3GxsChannels::getChannelDownloadDirectory() No Entry" << std::endl; -#endif - - return false; - } + it = mSubscribedGroups.find(groupId); + if (it == mSubscribedGroups.end()) + { + std::cerr << __PRETTY_FUNCTION__ << " Error! Unknown groupId: " + << groupId.toStdString() << std::endl; + return false; + } /* extract from ServiceString */ SSGxsChannelGroup ss; ss.load(it->second.mServiceString); directory = ss.mDownloadDirectory; - return true ; + return true; } void p3GxsChannels::request_AllSubscribedGroups() @@ -668,7 +676,8 @@ void p3GxsChannels::request_AllSubscribedGroups() } -void p3GxsChannels::request_SpecificSubscribedGroups(const std::list &groups) +void p3GxsChannels::request_SpecificSubscribedGroups( + const std::list &groups ) { #ifdef GXSCHANNELS_DEBUG std::cerr << "p3GxsChannels::request_SpecificSubscribedGroups()"; @@ -681,8 +690,19 @@ void p3GxsChannels::request_SpecificSubscribedGroups(const std::listrequestGroupInfo(token, ansType, opts, groups); - GxsTokenQueue::queueRequest(token, GXSCHANNELS_SUBSCRIBED_META); + if(!RsGenExchange::getTokenService()-> + requestGroupInfo(token, ansType, opts, groups)) + { + std::cerr << __PRETTY_FUNCTION__ << " Failed requesting groups info!" + << std::endl; + return; + } + + if(!GxsTokenQueue::queueRequest(token, GXSCHANNELS_SUBSCRIBED_META)) + { + std::cerr << __PRETTY_FUNCTION__ << " Failed queuing request!" + << std::endl; + } } @@ -746,6 +766,7 @@ void p3GxsChannels::updateSubscribedGroup(const RsGroupMetaData &group) std::cerr << std::endl; #endif + RS_STACK_MUTEX(mSubscribedGroupsMutex); mSubscribedGroups[group.mGroupId] = group; } @@ -757,9 +778,8 @@ void p3GxsChannels::clearUnsubscribedGroup(const RsGxsGroupId &id) std::cerr << std::endl; #endif - //std::map mSubscribedGroups; + RS_STACK_MUTEX(mSubscribedGroupsMutex); std::map::iterator it; - it = mSubscribedGroups.find(id); if (it != mSubscribedGroups.end()) { @@ -838,24 +858,20 @@ void p3GxsChannels::request_GroupUnprocessedPosts(const std::list } -void p3GxsChannels::load_SpecificUnprocessedPosts(const uint32_t &token) +void p3GxsChannels::load_unprocessedPosts(uint32_t token) { #ifdef GXSCHANNELS_DEBUG - std::cerr << "p3GxsChannels::load_SpecificUnprocessedPosts"; - std::cerr << std::endl; + std::cerr << "p3GxsChannels::load_SpecificUnprocessedPosts" << std::endl; #endif std::vector posts; if (!getPostData(token, posts)) { -#ifdef GXSCHANNELS_DEBUG - std::cerr << "p3GxsChannels::load_SpecificUnprocessedPosts ERROR"; - std::cerr << std::endl; -#endif + std::cerr << __PRETTY_FUNCTION__ << " ERROR getting post data!" + << std::endl; return; } - std::vector::iterator it; for(it = posts.begin(); it != posts.end(); ++it) { @@ -864,58 +880,27 @@ void p3GxsChannels::load_SpecificUnprocessedPosts(const uint32_t &token) } } - -void p3GxsChannels::load_GroupUnprocessedPosts(const uint32_t &token) -{ -#ifdef GXSCHANNELS_DEBUG - std::cerr << "p3GxsChannels::load_GroupUnprocessedPosts"; - std::cerr << std::endl; -#endif - - std::vector posts; - if (!getPostData(token, posts)) - { -#ifdef GXSCHANNELS_DEBUG - std::cerr << "p3GxsChannels::load_GroupUnprocessedPosts ERROR"; - std::cerr << std::endl; -#endif - return; - } - - - std::vector::iterator it; - for(it = posts.begin(); it != posts.end(); ++it) - { - handleUnprocessedPost(*it); - } -} - void p3GxsChannels::handleUnprocessedPost(const RsGxsChannelPost &msg) { #ifdef GXSCHANNELS_DEBUG - std::cerr << "p3GxsChannels::handleUnprocessedPost() GroupId: " << msg.mMeta.mGroupId << " MsgId: " << msg.mMeta.mMsgId; - std::cerr << std::endl; + std::cerr << __PRETTY_FUNCTION__ << " GroupId: " << msg.mMeta.mGroupId + << " MsgId: " << msg.mMeta.mMsgId << std::endl; #endif if (!IS_MSG_UNPROCESSED(msg.mMeta.mMsgStatus)) { - std::cerr << "p3GxsChannels::handleUnprocessedPost() Msg already Processed"; - std::cerr << std::endl; - std::cerr << "p3GxsChannels::handleUnprocessedPost() ERROR - this should not happen"; - std::cerr << std::endl; + std::cerr << __PRETTY_FUNCTION__ << " ERROR Msg already Processed! " + << "mMsgId: " << msg.mMeta.mMsgId << std::endl; return; } - bool enabled = false ; - /* check that autodownload is set */ - if (autoDownloadEnabled(msg.mMeta.mGroupId,enabled) && enabled ) + bool enabled = false; + if (autoDownloadEnabled(msg.mMeta.mGroupId, enabled) && enabled) { - - #ifdef GXSCHANNELS_DEBUG - std::cerr << "p3GxsChannels::handleUnprocessedPost() AutoDownload Enabled ... handling"; - std::cerr << std::endl; + std::cerr << __PRETTY_FUNCTION__ << " AutoDownload Enabled... handling" + << std::endl; #endif /* check the date is not too old */ @@ -928,8 +913,7 @@ void p3GxsChannels::handleUnprocessedPost(const RsGxsChannelPost &msg) // MORE THOUGHT HAS TO GO INTO THAT STUFF. #ifdef GXSCHANNELS_DEBUG - std::cerr << "p3GxsChannels::handleUnprocessedPost() START DOWNLOAD"; - std::cerr << std::endl; + std::cerr << __PRETTY_FUNCTION__ << " START DOWNLOAD" << std::endl; #endif std::list::const_iterator fit; @@ -951,8 +935,11 @@ void p3GxsChannels::handleUnprocessedPost(const RsGxsChannelPost &msg) rsFiles->FileRequest(fname, hash, size, localpath, flags, srcIds); } - else - std::cerr << "WARNING: Channel file is not auto-downloaded because its size exceeds the threshold of " << CHANNEL_MAX_AUTO_DL << " bytes." << std::endl; + else + std::cerr << __PRETTY_FUNCTION__ << "Channel file is not auto-" + << "downloaded because its size exceeds the threshold" + << " of " << CHANNEL_MAX_AUTO_DL << " bytes." + << std::endl; } } @@ -986,19 +973,18 @@ void p3GxsChannels::handleResponse(uint32_t token, uint32_t req_type) load_SubscribedGroups(token); break; - case GXSCHANNELS_UNPROCESSED_SPECIFIC: - load_SpecificUnprocessedPosts(token); - break; + case GXSCHANNELS_UNPROCESSED_SPECIFIC: + load_unprocessedPosts(token); + break; - case GXSCHANNELS_UNPROCESSED_GENERIC: - load_SpecificUnprocessedPosts(token); - break; + case GXSCHANNELS_UNPROCESSED_GENERIC: + load_unprocessedPosts(token); + break; - default: - /* error */ - std::cerr << "p3GxsService::handleResponse() Unknown Request Type: " << req_type; - std::cerr << std::endl; - break; + default: + std::cerr << __PRETTY_FUNCTION__ << "ERROR Unknown Request Type: " + << req_type << std::endl; + break; } } @@ -1045,20 +1031,63 @@ bool p3GxsChannels::getChannelsContent( bool p3GxsChannels::createChannel(RsGxsChannelGroup& channel) { uint32_t token; - if( !createGroup(token, channel) - || waitToken(token) != RsTokenService::COMPLETE ) - return false; - - if(RsGenExchange::getPublishedGroupMeta(token, channel.mMeta)) + if(!createGroup(token, channel)) { -#ifdef RS_DEEP_SEARCH - DeepSearch::indexChannelGroup(channel); -#endif // RS_DEEP_SEARCH - - return true; + std::cerr << __PRETTY_FUNCTION__ << "Error! Failed updating group." + << std::endl; + return false; } - return false; + if(waitToken(token) != RsTokenService::COMPLETE) + { + std::cerr << __PRETTY_FUNCTION__ << "Error! GXS operation failed." + << std::endl; + return false; + } + + if(!RsGenExchange::getPublishedGroupMeta(token, channel.mMeta)) + { + std::cerr << __PRETTY_FUNCTION__ << "Error! Failure getting updated " + << " group data." << std::endl; + return false; + } + +#ifdef RS_DEEP_SEARCH + DeepSearch::indexChannelGroup(channel); +#endif // RS_DEEP_SEARCH + + return true; +} + +bool p3GxsChannels::editChannel(RsGxsChannelGroup& channel) +{ + uint32_t token; + if(!updateGroup(token, channel)) + { + std::cerr << __PRETTY_FUNCTION__ << "Error! Failed updating group." + << std::endl; + return false; + } + + if(waitToken(token) != RsTokenService::COMPLETE) + { + std::cerr << __PRETTY_FUNCTION__ << "Error! GXS operation failed." + << std::endl; + return false; + } + + if(!RsGenExchange::getPublishedGroupMeta(token, channel.mMeta)) + { + std::cerr << __PRETTY_FUNCTION__ << "Error! Failure getting updated " + << " group data." << std::endl; + return false; + } + +#ifdef RS_DEEP_SEARCH + DeepSearch::indexChannelGroup(channel); +#endif // RS_DEEP_SEARCH + + return true; } bool p3GxsChannels::createPost(RsGxsChannelPost& post) @@ -1079,6 +1108,29 @@ bool p3GxsChannels::createPost(RsGxsChannelPost& post) return false; } +bool p3GxsChannels::subscribeToChannel( + const RsGxsGroupId& groupId, bool subscribe ) +{ + uint32_t token; + if( !subscribeToGroup(token, groupId, subscribe) + || waitToken(token) != RsTokenService::COMPLETE ) return false; + return true; +} + +bool p3GxsChannels::markRead(const RsGxsGrpMsgIdPair& msgId, bool read) +{ + uint32_t token; + setMessageReadStatus(token, msgId, read); + if(waitToken(token) != RsTokenService::COMPLETE ) return false; + return true; +} + +bool p3GxsChannels::shareChannelKeys( + const RsGxsGroupId& channelId, const std::set& peers) +{ + return groupShareKeys(channelId, peers); +} + //////////////////////////////////////////////////////////////////////////////// /// Blocking API implementation end @@ -1092,20 +1144,17 @@ bool p3GxsChannels::createPost(RsGxsChannelPost& post) bool p3GxsChannels::autoDownloadEnabled(const RsGxsGroupId &groupId,bool& enabled) { #ifdef GXSCHANNELS_DEBUG - std::cerr << "p3GxsChannels::autoDownloadEnabled(" << id << ")"; + std::cerr << "p3GxsChannels::autoDownloadEnabled(" << groupId << ")"; std::cerr << std::endl; #endif + RS_STACK_MUTEX(mSubscribedGroupsMutex); std::map::iterator it; - it = mSubscribedGroups.find(groupId); if (it == mSubscribedGroups.end()) { -#ifdef GXSCHANNELS_DEBUG - std::cerr << "p3GxsChannels::autoDownloadEnabled() No Entry"; - std::cerr << std::endl; -#endif - + std::cerr << __PRETTY_FUNCTION__ << " WARNING requested channel: " + << groupId << " is not subscribed" << std::endl; return false; } @@ -1190,23 +1239,20 @@ std::string SSGxsChannelGroup::save() const return output; } -bool p3GxsChannels::setAutoDownload(const RsGxsGroupId &groupId, bool enabled) +bool p3GxsChannels::setAutoDownload(const RsGxsGroupId& groupId, bool enabled) { #ifdef GXSCHANNELS_DEBUG - std::cerr << "p3GxsChannels::setAutoDownload() id: " << groupId << " enabled: " << enabled; - std::cerr << std::endl; + std::cerr << __PRETTY_FUNCTION__ << " id: " << groupId + << " enabled: " << enabled << std::endl; #endif + RS_STACK_MUTEX(mSubscribedGroupsMutex); std::map::iterator it; - it = mSubscribedGroups.find(groupId); if (it == mSubscribedGroups.end()) { -#ifdef GXSCHANNELS_DEBUG - std::cerr << "p3GxsChannels::setAutoDownload() Missing Group"; - std::cerr << std::endl; -#endif - + std::cerr << __PRETTY_FUNCTION__ << " ERROR requested channel: " + << groupId.toStdString() << " is not subscribed!" << std::endl; return false; } @@ -1215,27 +1261,21 @@ bool p3GxsChannels::setAutoDownload(const RsGxsGroupId &groupId, bool enabled) ss.load(it->second.mServiceString); if (enabled == ss.mAutoDownload) { - /* it should be okay! */ -#ifdef GXSCHANNELS_DEBUG - std::cerr << "p3GxsChannels::setAutoDownload() WARNING setting looks okay already"; - std::cerr << std::endl; -#endif - + std::cerr << __PRETTY_FUNCTION__ << " WARNING mAutoDownload was already" + << " properly set to: " << enabled << " for channel:" + << groupId.toStdString() << std::endl; + return false; } - /* we are just going to set it anyway. */ ss.mAutoDownload = enabled; std::string serviceString = ss.save(); + uint32_t token; + RsGenExchange::setGroupServiceString(token, groupId, serviceString); + + if(waitToken(token) != RsTokenService::COMPLETE) return false; it->second.mServiceString = serviceString; // update Local Cache. - RsGenExchange::setGroupServiceString(token, groupId, serviceString); // update dbase. - - /* now reload it */ - std::list groups; - groups.push_back(groupId); - - request_SpecificSubscribedGroups(groups); return true; } @@ -1604,7 +1644,7 @@ void p3GxsChannels::dummy_tick() } - cleanTimedOutSearches(); + cleanTimedOutCallbacks(); } @@ -1776,7 +1816,7 @@ TurtleRequestId p3GxsChannels::turtleGroupRequest(const RsGxsGroupId& group_id) } TurtleRequestId p3GxsChannels::turtleSearchRequest(const std::string& match_string) { - return netService()->turtleSearchRequest(match_string) ; + return netService()->turtleSearchRequest(match_string); } bool p3GxsChannels::clearDistantSearchResults(TurtleRequestId req) @@ -1834,13 +1874,43 @@ bool p3GxsChannels::turtleSearchRequest( TurtleRequestId sId = turtleSearchRequest(matchString); + { RS_STACK_MUTEX(mSearchCallbacksMapMutex); mSearchCallbacksMap.emplace( sId, std::make_pair( multiCallback, std::chrono::system_clock::now() + - std::chrono::seconds(maxWait) ) ); + std::chrono::seconds(maxWait) ) ); + } + + return true; +} + +/// @see RsGxsChannels::turtleChannelRequest +bool p3GxsChannels::turtleChannelRequest( + const RsGxsGroupId& channelId, + const std::function& multiCallback, + rstime_t maxWait) +{ + if(channelId.isNull()) + { + std::cerr << __PRETTY_FUNCTION__ << "Error! channelId can't be null!" + << std::endl; + return false; + } + + TurtleRequestId sId = turtleGroupRequest(channelId); + + { + RS_STACK_MUTEX(mDistantChannelsCallbacksMapMutex); + mDistantChannelsCallbacksMap.emplace( + sId, + std::make_pair( + multiCallback, + std::chrono::system_clock::now() + + std::chrono::seconds(maxWait) ) ); + } return true; } @@ -1851,29 +1921,77 @@ void p3GxsChannels::receiveDistantSearchResults( std::cerr << __PRETTY_FUNCTION__ << "(" << id << ", " << grpId << ")" << std::endl; - RsGenExchange::receiveDistantSearchResults(id, grpId); - RsGxsGroupSummary gs; - gs.mGroupId = grpId; - netService()->retrieveDistantGroupSummary(grpId, gs); + { + RsGenExchange::receiveDistantSearchResults(id, grpId); + RsGxsGroupSummary gs; + gs.mGroupId = grpId; + netService()->retrieveDistantGroupSummary(grpId, gs); + + { + RS_STACK_MUTEX(mSearchCallbacksMapMutex); + auto cbpt = mSearchCallbacksMap.find(id); + if(cbpt != mSearchCallbacksMap.end()) + { + cbpt->second.first(gs); + return; + } + } // end RS_STACK_MUTEX(mSearchCallbacksMapMutex); + } + + { + RS_STACK_MUTEX(mDistantChannelsCallbacksMapMutex); + auto cbpt = mDistantChannelsCallbacksMap.find(id); + if(cbpt != mDistantChannelsCallbacksMap.end()) + { + std::function callback = + cbpt->second.first; + RsThread::async([this, callback, grpId]() + { + std::list chanIds({grpId}); + std::vector channelsInfo; + if(!getChannelsInfo(chanIds, channelsInfo)) + { + std::cerr << __PRETTY_FUNCTION__ << " Error! Received " + << "distant channel result grpId: " << grpId + << " but failed getting channel info" + << std::endl; + return; + } + + for(const RsGxsChannelGroup& chan : channelsInfo) + callback(chan); + } ); + + return; + } + } // RS_STACK_MUTEX(mDistantChannelsCallbacksMapMutex); +} + +void p3GxsChannels::cleanTimedOutCallbacks() +{ + auto now = std::chrono::system_clock::now(); { RS_STACK_MUTEX(mSearchCallbacksMapMutex); - auto cbpt = mSearchCallbacksMap.find(id); - if(cbpt != mSearchCallbacksMap.end()) - cbpt->second.first(gs); - } // end RS_STACK_MUTEX(mSearchCallbacksMapMutex); -} + for( auto cbpt = mSearchCallbacksMap.begin(); + cbpt != mSearchCallbacksMap.end(); ) + if(cbpt->second.second <= now) + { + clearDistantSearchResults(cbpt->first); + cbpt = mSearchCallbacksMap.erase(cbpt); + } + else ++cbpt; + } // RS_STACK_MUTEX(mSearchCallbacksMapMutex); -void p3GxsChannels::cleanTimedOutSearches() -{ - RS_STACK_MUTEX(mSearchCallbacksMapMutex); - auto now = std::chrono::system_clock::now(); - for( auto cbpt = mSearchCallbacksMap.begin(); - cbpt != mSearchCallbacksMap.end(); ) - if(cbpt->second.second <= now) - { - clearDistantSearchResults(cbpt->first); - cbpt = mSearchCallbacksMap.erase(cbpt); - } - else ++cbpt; + { + RS_STACK_MUTEX(mDistantChannelsCallbacksMapMutex); + for( auto cbpt = mDistantChannelsCallbacksMap.begin(); + cbpt != mDistantChannelsCallbacksMap.end(); ) + if(cbpt->second.second <= now) + { + clearDistantSearchResults(cbpt->first); + cbpt = mDistantChannelsCallbacksMap.erase(cbpt); + } + else ++cbpt; + } // RS_STACK_MUTEX(mDistantChannelsCallbacksMapMutex) } diff --git a/libretroshare/src/services/p3gxschannels.h b/libretroshare/src/services/p3gxschannels.h index 327444509..3caf31c59 100644 --- a/libretroshare/src/services/p3gxschannels.h +++ b/libretroshare/src/services/p3gxschannels.h @@ -83,7 +83,7 @@ public: virtual bool getGroupData(const uint32_t &token, std::vector &groups); virtual bool getPostData(const uint32_t &token, std::vector &posts, std::vector &cmts); -virtual bool getPostData(const uint32_t &token, std::vector &posts) { std::vector cmts; return getPostData( token, posts, cmts);} +virtual bool getPostData(const uint32_t &token, std::vector &posts); //Not currently used //virtual bool getRelatedPosts(const uint32_t &token, std::vector &posts); @@ -112,6 +112,12 @@ virtual bool getChannelDownloadDirectory(const RsGxsGroupId &groupId, std::strin const std::function& multiCallback, rstime_t maxWait = 300 ); + /// @see RsGxsChannels::turtleChannelRequest + virtual bool turtleChannelRequest( + const RsGxsGroupId& channelId, + const std::function& multiCallback, + rstime_t maxWait = 300 ); + /** * Receive results from turtle search @see RsGenExchange @see RsNxsObserver * @see RsGxsNetService::receiveTurtleSearchResults @@ -183,9 +189,22 @@ virtual bool ExtraFileRemove(const RsFileHash &hash); /// Implementation of @see RsGxsChannels::createChannel virtual bool createChannel(RsGxsChannelGroup& channel); + /// Implementation of @see RsGxsChannels::editChannel + virtual bool editChannel(RsGxsChannelGroup& channel); + /// Implementation of @see RsGxsChannels::createPost virtual bool createPost(RsGxsChannelPost& post); + /// Implementation of @see RsGxsChannels::subscribeToChannel + virtual bool subscribeToChannel( const RsGxsGroupId &groupId, + bool subscribe ); + + /// Implementation of @see RsGxsChannels::setPostRead + virtual bool markRead(const RsGxsGrpMsgIdPair& msgId, bool read); + + virtual bool shareChannelKeys( + const RsGxsGroupId& channelId, const std::set& peers ); + protected: // Overloaded from GxsTokenQueue for Request callbacks. virtual void handleResponse(uint32_t token, uint32_t req_type); @@ -201,10 +220,8 @@ static uint32_t channelsAuthenPolicy(); void load_SubscribedGroups(const uint32_t &token); void request_SpecificUnprocessedPosts(std::list > &ids); - void load_SpecificUnprocessedPosts(const uint32_t &token); - void request_GroupUnprocessedPosts(const std::list &grouplist); - void load_GroupUnprocessedPosts(const uint32_t &token); + void load_unprocessedPosts(uint32_t token); void handleUnprocessedPost(const RsGxsChannelPost &msg); @@ -214,11 +231,6 @@ static uint32_t channelsAuthenPolicy(); bool setAutoDownload(const RsGxsGroupId &groupId, bool enabled); bool autoDownloadEnabled(const RsGxsGroupId &groupId, bool &enabled); - - - std::map mSubscribedGroups; - - // DUMMY DATA, virtual bool generateDummyData(); @@ -246,14 +258,21 @@ bool generateGroup(uint32_t &token, std::string groupName); RsGxsMessageId mMsgId; }; + std::map mSubscribedGroups; + RsMutex mSubscribedGroupsMutex; + + /** G10h4ck: Is this stuff really used? And for what? BEGIN */ uint32_t mGenToken; bool mGenActive; int mGenCount; std::vector mGenRefs; RsGxsMessageId mGenThreadId; + /** G10h4ck: Is this stuff really used? And for what? END */ - p3GxsCommentService *mCommentService; - std::map mKnownChannels; + p3GxsCommentService* mCommentService; + + std::map mKnownChannels; + RsMutex mKnownChannelsMutex; /** Store search callbacks with timeout*/ std::map< @@ -264,8 +283,17 @@ bool generateGroup(uint32_t &token, std::string groupName); > mSearchCallbacksMap; RsMutex mSearchCallbacksMapMutex; - /// Cleanup mSearchCallbacksMap - void cleanTimedOutSearches(); + /** Store distant channels requests callbacks with timeout*/ + std::map< + TurtleRequestId, + std::pair< + std::function, + std::chrono::system_clock::time_point > + > mDistantChannelsCallbacksMap; + RsMutex mDistantChannelsCallbacksMapMutex; + + /// Cleanup mSearchCallbacksMap and mDistantChannelsCallbacksMap + void cleanTimedOutCallbacks(); }; #endif diff --git a/libretroshare/src/util/rsthreads.h b/libretroshare/src/util/rsthreads.h index 630c136a8..6a61a9b4e 100644 --- a/libretroshare/src/util/rsthreads.h +++ b/libretroshare/src/util/rsthreads.h @@ -27,6 +27,8 @@ #include #include #include +#include +#include #include #include "util/rstime.h" @@ -239,7 +241,7 @@ pthread_t createThread(RsThread &thread); class RsThread { - public: +public: RsThread(); virtual ~RsThread() {} @@ -259,6 +261,17 @@ class RsThread void ask_for_stop(); + /** + * Execute given function on another thread without blocking the caller + * execution. + * This can be generalized with variadic template, ATM it is enough to wrap + * any kind of function call or job into a lambda which get no paramethers + * and return nothing but can capture + * This can be easly optimized later by using a thread pool + */ + static void async(const std::function& fn) + { std::thread(fn).detach(); } + protected: virtual void runloop() =0; /* called once the thread is started. Should be overloaded by subclasses. */ void go() ; // this one calls runloop and also sets the flags correctly when the thread is finished running. diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelDialog.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelDialog.cpp index 9e6bc6f88..28d147cfa 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelDialog.cpp +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelDialog.cpp @@ -20,6 +20,7 @@ #include #include +#include #include @@ -33,6 +34,7 @@ #include "gui/settings/rsharesettings.h" #include "gui/notifyqt.h" #include "gui/common/GroupTreeWidget.h" +#include "util/qtthreadsutils.h" class GxsChannelGroupInfoData : public RsUserdata { @@ -275,17 +277,37 @@ QWidget *GxsChannelDialog::createCommentHeaderWidget(const RsGxsGroupId &grpId, void GxsChannelDialog::toggleAutoDownload() { RsGxsGroupId grpId = groupId(); - if (grpId.isNull()) { + if (grpId.isNull()) return; + + bool autoDownload; + if(!rsGxsChannels->getChannelAutoDownload(grpId, autoDownload)) + { + std::cerr << __PRETTY_FUNCTION__ << " failed to get autodownload value " + << "for channel: " << grpId.toStdString() << std::endl; return; } - bool autoDownload ; - - if(!rsGxsChannels->getChannelAutoDownload(grpId,autoDownload) || !rsGxsChannels->setChannelAutoDownload(grpId, !autoDownload)) + RsThread::async([this, grpId, autoDownload]() { - std::cerr << "GxsChannelDialog::toggleAutoDownload() Auto Download failed to set"; - std::cerr << std::endl; - } + if(!rsGxsChannels->setChannelAutoDownload(grpId, !autoDownload)) + { + std::cerr << __PRETTY_FUNCTION__ << " failed to set autodownload " + << "for channel: " << grpId << std::endl; + return; + } + + RsQThreadUtils::postToObject( [=]() + { + /* Here it goes any code you want to be executed on the Qt Gui + * thread, for example to update the data model with new information + * after a blocking call to RetroShare API complete, note that + * Qt::QueuedConnection is important! + */ + + std::cerr << __PRETTY_FUNCTION__ << " Has been executed on GUI " + << "thread but was scheduled by async thread" << std::endl; + }, this ); + }); } void GxsChannelDialog::loadGroupSummaryToken(const uint32_t &token, std::list &groupInfo, RsUserdata *&userdata) diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidget.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidget.cpp index f51d1cc5f..92fe5e85e 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidget.cpp +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidget.cpp @@ -31,8 +31,10 @@ #include "gui/settings/rsharesettings.h" #include "gui/feeds/SubFileItem.h" #include "gui/notifyqt.h" -#include #include "util/DateTime.h" +#include "util/qtthreadsutils.h" + +#include #define CHAN_DEFAULT_IMAGE ":/images/channels.png" @@ -621,13 +623,13 @@ bool GxsChannelPostsWidget::navigatePostItem(const RsGxsMessageId &msgId) void GxsChannelPostsWidget::subscribeGroup(bool subscribe) { - if (groupId().isNull()) { - return; - } + RsGxsGroupId grpId(groupId()); + if (grpId.isNull()) return; - uint32_t token; - rsGxsChannels->subscribeToGroup(token, groupId(), subscribe); -// mChannelQueue->queueRequest(token, 0, RS_TOKREQ_ANSTYPE_ACK, TOKEN_TYPE_SUBSCRIBE_CHANGE); + RsThread::async([=]() + { + rsGxsChannels->subscribeToChannel(grpId, subscribe); + } ); } void GxsChannelPostsWidget::setAutoDownload(bool autoDl) @@ -643,12 +645,35 @@ void GxsChannelPostsWidget::toggleAutoDownload() return; } - bool autoDownload ; - if(!rsGxsChannels->getChannelAutoDownload(grpId,autoDownload) || !rsGxsChannels->setChannelAutoDownload(grpId, !autoDownload)) + bool autoDownload; + if(!rsGxsChannels->getChannelAutoDownload(grpId, autoDownload)) { - std::cerr << "GxsChannelDialog::toggleAutoDownload() Auto Download failed to set"; - std::cerr << std::endl; + std::cerr << __PRETTY_FUNCTION__ << " failed to get autodownload value " + << "for channel: " << grpId.toStdString() << std::endl; + return; } + + RsThread::async([this, grpId, autoDownload]() + { + if(!rsGxsChannels->setChannelAutoDownload(grpId, !autoDownload)) + { + std::cerr << __PRETTY_FUNCTION__ << " failed to set autodownload " + << "for channel: " << grpId.toStdString() << std::endl; + return; + } + + RsQThreadUtils::postToObject( [=]() + { + /* Here it goes any code you want to be executed on the Qt Gui + * thread, for example to update the data model with new information + * after a blocking call to RetroShare API complete, note that + * Qt::QueuedConnection is important! + */ + + std::cerr << __PRETTY_FUNCTION__ << " Has been executed on GUI " + << "thread but was scheduled by async thread" << std::endl; + }, this ); + }); } bool GxsChannelPostsWidget::insertGroupData(const uint32_t &token, RsGroupMetaData &metaData) diff --git a/retroshare-gui/src/retroshare-gui.pro b/retroshare-gui/src/retroshare-gui.pro index 6501b1caa..61baf713e 100644 --- a/retroshare-gui/src/retroshare-gui.pro +++ b/retroshare-gui/src/retroshare-gui.pro @@ -424,6 +424,7 @@ HEADERS += rshare.h \ util/ObjectPainter.h \ util/QtVersion.h \ util/RsFile.h \ + util/qtthreadsutils.h \ gui/profile/ProfileWidget.h \ gui/profile/ProfileManager.h \ gui/profile/StatusMessage.h \ diff --git a/retroshare-gui/src/util/qtthreadsutils.h b/retroshare-gui/src/util/qtthreadsutils.h new file mode 100644 index 000000000..f7dce65dc --- /dev/null +++ b/retroshare-gui/src/util/qtthreadsutils.h @@ -0,0 +1,117 @@ +/* + * RetroShare + * Copyright (C) 2018 Gioacchino Mazzurco + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +#pragma once + +/* Thanks to KubaO which realeased original C++14 versions of this functions + * under public domain license + * https://github.com/KubaO/stackoverflown/blob/master/questions/metacall-21646467/main.cpp + * https://github.com/KubaO/stackoverflown/blob/master/LICENSE + */ + +#include +#include +#include + +namespace RsQThreadUtils { + +#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) + +/** + * @brief execute given function in the QThread where given QObject belongs + */ +template +void postToObject(F &&fun, QObject *obj = qApp) +{ + if (qobject_cast(obj)) + qWarning() << "posting a call to a thread object - consider using postToThread"; + QObject src; + auto type = obj->metaObject(); + QObject::connect( &src, &QObject::destroyed, obj, + [fun, type, obj] + { + // ensure that the object is not being destructed + if (obj->metaObject()->inherits(type)) fun(); + }, Qt::QueuedConnection ); +} + +/** + * @brief execute given function in the given QThread + */ +template +void postToThread(F &&fun, QThread *thread = qApp->thread()) +{ + QObject * obj = QAbstractEventDispatcher::instance(thread); + Q_ASSERT(obj); + QObject src; + auto type = obj->metaObject(); + QObject::connect( &src, &QObject::destroyed, obj, + [fun, type, obj] + { + // ensure that the object is not being destructed + if (obj->metaObject()->inherits(type)) fun(); + }, Qt::QueuedConnection ); +} + +#else // QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) + +template +struct FEvent : QEvent +{ + using Fun = typename std::decay::type; + const QObject *const obj; + const QMetaObject *const type = obj->metaObject(); + Fun fun; + template + FEvent(const QObject *obj, Fun &&fun) : + QEvent(QEvent::None), obj(obj), fun(std::forward(fun)) {} + ~FEvent() + { +#if QT_VERSION >= QT_VERSION_CHECK(5, 7, 0) + // ensure that the object is not being destructed + if (obj->metaObject()->inherits(type)) fun(); +#else // QT_VERSION >= QT_VERSION_CHECK(5, 7, 0) + fun(); +#endif // QT_VERSION >= QT_VERSION_CHECK(5, 7, 0) + } +}; + +/** + * @brief execute given function in the QThread where given QObject belongs + */ +template +static void postToObject(F &&fun, QObject *obj = qApp) +{ + if (qobject_cast(obj)) + qWarning() << "posting a call to a thread object - consider using postToThread"; + QCoreApplication::postEvent(obj, new FEvent(obj, std::forward(fun))); +} + +/** + * @brief execute given function in the given QThread + */ +template +static void postToThread(F &&fun, QThread *thread = qApp->thread()) +{ + QObject * obj = QAbstractEventDispatcher::instance(thread); + Q_ASSERT(obj); + QCoreApplication::postEvent(obj, new FEvent(obj, std::forward(fun))); +} +#endif // QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) + +} +