From 9b486f5e139d9ec73f8e6f6b729b2985dc6ae3a9 Mon Sep 17 00:00:00 2001 From: yash-ni <131851960+yash-ni@users.noreply.github.com> Date: Tue, 5 Mar 2024 17:31:37 +0530 Subject: [PATCH] Fixed gRPC Client Hang issue (#349) * Improved cmake tests configuration * Added option to remove occurrence in unary call * Added option for occurrence in unary call * Added without occurrence option for streaming calls * Restructured the code * Made occurrence changes to streaming vi templates * Updated route_guide example * Updated route_guide example * Fixed route_guide clients * Updated helloworld example * Changed activeclientcalls to unordered_map * Removed wait from cleanUpProc * Added feature toggle for occurrence * Improved occurrence wrapper * Added feature toggle docs * Bumped vipb version to v1.2.1.1 --- docs/FeatureToggles.md | 21 ++ docs/QuickStart.md | 5 + .../gRPC Server and Client Template [2].vipb | 8 +- .../build spec/LabVIEW gRPC Servicer.vipb | 34 +- .../Client Complete Client Streaming Call.vim | Bin 21790 -> 22002 bytes .../Client API/Client Read From Stream.vim | Bin 13420 -> 13648 bytes .../Client API/Client Unary Call.vim | Bin 19762 -> 19678 bytes .../Server API/Server/Get Server DLL Path.vi | Bin 16423 -> 17107 bytes .../Shared/Wait On Occurrence Wrapper.vi | Bin 0 -> 30474 bytes .../build spec/LabVIEW gRPC Library.vipb | 14 +- .../gRPC lv Support/grpc-lvsupport.lvlib | 1 + src/feature_toggles.cc | 4 + src/feature_toggles.h | 8 +- src/grpc_client.cc | 330 ++++++++++-------- src/grpc_client.h | 31 +- src/grpc_interop.cc | 8 + src/lv_interop.cc | 2 +- 17 files changed, 281 insertions(+), 185 deletions(-) create mode 100644 docs/FeatureToggles.md create mode 100644 labview source/gRPC lv Support/Shared/Wait On Occurrence Wrapper.vi diff --git a/docs/FeatureToggles.md b/docs/FeatureToggles.md new file mode 100644 index 00000000..5f1187cf --- /dev/null +++ b/docs/FeatureToggles.md @@ -0,0 +1,21 @@ +# Introducing Feature Toggles + +Starting from release v1.2.0.1 we have added support for feature toggles. This allows you to enable or disable certain features in the application without the need to redeploy the application. + +## How to use feature toggles + +Feature toggles are managed through the `feature_config.ini` file (located next to _labview_grpc_server_ library). You can find the `data` section in the file. Here is an example of how to use feature toggles: + +```ini +[data] +EfficientMessageCopy = TRUE +useOccurrence = TRUE +``` + +In the example above, the `EfficientMessageCopy` and `useOccurrence` features are enabled by default. If you want to disable a feature, you can set the value to `FALSE`. + +### More about the flags + +1. `EfficientMessageCopy` - This feature is used to enable or disable the efficient message copy feature. When enabled, the client will use efficient message copy to have throughput. When disabled, the client will use the default message copy. + +2. `useOccurrence` - This feature is used to enable or disable the occurrence feature. When enabled, the client will use occurrence to manage synchroniation between LabVIEW execution threads. When disabled, the client will use not use LabVIEW occurrences. \ No newline at end of file diff --git a/docs/QuickStart.md b/docs/QuickStart.md index 2aeb1c6f..34464966 100644 --- a/docs/QuickStart.md +++ b/docs/QuickStart.md @@ -169,6 +169,11 @@ From the same project, ![Say Hello Again Client Code implementation](images/HelloAgain-Client-Code.png "Say Hello Again Client Code implementation") + +### [Optional] Configure feature toggles + +[Feature Toggle documentation](./FeatureToggles.md) + ### Run! Just like we did before, from the `examples/helloworld` directory: diff --git a/labview source/Client Server Support New/build spec/gRPC Server and Client Template [2].vipb b/labview source/Client Server Support New/build spec/gRPC Server and Client Template [2].vipb index 9ead8c35..dce42dc4 100644 --- a/labview source/Client Server Support New/build spec/gRPC Server and Client Template [2].vipb +++ b/labview source/Client Server Support New/build spec/gRPC Server and Client Template [2].vipb @@ -1,7 +1,7 @@ - + NI_lib_gRPC_Server_and_Client_Template[2] - 1.2.0.1 + 1.2.1.1 false .. ..\..\Builds @@ -17,8 +17,8 @@ - ni_lib_labview_grpc_library >=1.0.0.5 - ni_lib_labview_grpc_servicer >=1.0.0.5 + ni_lib_labview_grpc_library >=1.2.1.1 + ni_lib_labview_grpc_servicer >=1.2.1.1 diff --git a/labview source/gRPC lv Servicer/build spec/LabVIEW gRPC Servicer.vipb b/labview source/gRPC lv Servicer/build spec/LabVIEW gRPC Servicer.vipb index 2d932439..7a072dc6 100644 --- a/labview source/gRPC lv Servicer/build spec/LabVIEW gRPC Servicer.vipb +++ b/labview source/gRPC lv Servicer/build spec/LabVIEW gRPC Servicer.vipb @@ -1,7 +1,7 @@ - + NI_lib_LabVIEW_gRPC_Servicer - 1.2.0.1 + 1.2.1.1 false .. ..\..\Builds @@ -17,7 +17,7 @@ - ni_lib_labview_grpc_library >=1.0.0.5 + ni_lib_labview_grpc_library >=1.2.1.1 @@ -241,7 +241,7 @@ ..\Servicer - EF4D1AC5CAC7F902A7CF8FF5793A28D2 + 2853EF4A6A207E56900A7E4A3B89B820 0 @@ -290,7 +290,7 @@ ..\iService\Server API - 4D9DD7F1C4F365611E7BC9C68C3D0746 + 56DEEC61A2B33CDA6209D66F1823D416 0 @@ -353,7 +353,7 @@ ..\Servicer\Run Servicer.vi - 01211C985534A0A64931AEB80B781E5B + 37CAC1E328AC86A39F584A6C543A8B47 1 @@ -402,7 +402,7 @@ ..\ServiceBase\Accessors\Read Server Stop.vi - 0DAEFFEC54821C1C8A04748AE22F62E7 + 0B31FB6D6BADFFD57F13DDC174FFF2DC 1 @@ -507,7 +507,7 @@ ..\ServiceBase\Server API\Start.vi - 868D92BE0C2CC5C8CEC7B97978B5B57B + A61CAED75DFB09FF807B585DB6A2E329 2 @@ -598,7 +598,7 @@ ..\Servicer\Accessors\Server State - EB0CABE3504FCDCD16AB2CCE0426666E + AB736E3BFE2F16DC39CAD22AED3B9A42 2 @@ -675,7 +675,7 @@ ..\Servicer\Server API\Stop Server.vi - D190312A2A9E027E772830E427BDC409 + 792C56BDAF2F7B6C8C400BA54A35FF4C 5 @@ -724,7 +724,7 @@ ..\Servicer\Accessors\gRPC ID\Set gRPC ID.vi - 9D246540CD96A150DF328248B0FBB69D + 7930AED5671BE270A8A44DD524E1FF6B 5 @@ -773,7 +773,7 @@ ..\Servicer\Accessors\Server Address\Set Server Address.vi - A9D1C08BDED898EA77A18DF0558BFF54 + EBF3BC054A4624C356F6D016BCC226AB 5 @@ -822,7 +822,7 @@ ..\Servicer\Accessors\Server Certificate File\Set Server Certificate File.vi - ABBDD30E4FA250571B687C540BBEF65F + 80EC661840B290568D6B374E3ED4929B 5 @@ -871,7 +871,7 @@ ..\Servicer\Accessors\Server Key File\Set Server Key File.vi - D009C94F8A0CE4085DB46DB08D81EB34 + 91B118288976929E18942FC5B9EC14ED 5 @@ -948,7 +948,7 @@ ..\Servicer\Accessors\Server State\Set Server State.vi - 0339BCF065B73EB2CFA62B59E69A63B4 + 8B469662DF05DD5A2C74E2EB7AF68AD4 11 @@ -997,7 +997,7 @@ ..\Servicer\Accessors\Server State\State Ref\Set Server State Ref.vi - 4989E0323BB114A6D1EB7502BBA40157 + 89F1B5F7BDA2A86F19F601292A18CD1F 11 @@ -1046,7 +1046,7 @@ ..\Servicer\Accessors\Server State\State UE\Set Server State UE.vi - F9328007F4D8CCC6D9AF239EE1D0C1BF + CF4FCC3F6AB2C9C175844260ABFCED8F \ No newline at end of file diff --git a/labview source/gRPC lv Support/Client API/Client Complete Client Streaming Call.vim b/labview source/gRPC lv Support/Client API/Client Complete Client Streaming Call.vim index a4435df466f2273633dfa866c9d86aeabf3be8bc..9b40c3bfb46901d82e0ca8fbb0b4ed65fe28cff3 100644 GIT binary patch delta 5331 zcmaKwcT`hb(}zQ^q9BA0p=m%uuLeW{p-K-Oq(-Dyks^d9NEZko2q@A!p(DK)={*!d zKIOhzHj*CHH{wvM*kHBtcH13hK+2QWQ- z06^OX0Kf|Z005w?y=EN90O9EFT}sZBua@6q>Ya?mCAm5yzp@)w26%_v4mVzZAQwa+ z@y;Fmlu0OzlTBY_U5HzAu=K@cDI3*bBwv=kK*78ijC zSV#&&L;{r^c;fDXbOR~4IXnJ?oUM_3p0)r@gwn4pWUdUKNXpaJ!PfE~k*>$p>fud-{1wgT z;9&Vr(EqLTSGfp(r+}BRhf4!$G$4DEkEz&b@pnzOTPyde&y{XiZw|7a!o+qGdVMmi z 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 AkN^Mx delta 3945 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 delta 5712 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 GNd5=-Ru;wp diff --git a/labview source/gRPC lv Support/Server API/Server/Get Server DLL Path.vi b/labview source/gRPC lv Support/Server API/Server/Get Server DLL Path.vi index adc89bf52def2f7187d2ba4bc97158bd534714dc..729124a1febadfe3fc526d9a52510b0b3b26d173 100644 GIT binary patch delta 6459 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

QLo`eSJy3fS$y95`;$a4`&GnrVpXHX~#H@$SR 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 + NI_lib_LabVIEW_gRPC_Library - 1.2.0.1 + 1.2.1.1 false .. ..\..\Builds @@ -252,7 +252,7 @@ https://github.com/ni/grpc-labview ..\Server API - F627CB60D44A5C4716C7A488480A4D48 + BEFED5DB5E2108370610EE39BBEEFB14 0 @@ -371,7 +371,7 @@ https://github.com/ni/grpc-labview ..\Client API\Client Cancel Call.vi - 64CFAF9B687AE3AFE8B3616E673536CC + DB38FE0CA2B16A33371A9E33EC3FB024 0 @@ -490,7 +490,7 @@ https://github.com/ni/grpc-labview ..\Server API\UnpackFromBuffer.vim - 73B249E2631224E8EA80A91855771A7A + A301642648F8716CD2A20F4C93FC59CB 2 @@ -721,7 +721,7 @@ https://github.com/ni/grpc-labview ..\Server API\Message Requests\Write Call Response.vim - C6F6FDFB6E222C0392DBF68D544AD686 + 09E45FBB1C71CA79F5165A0D0744C58A 2 @@ -840,7 +840,7 @@ https://github.com/ni/grpc-labview ..\Server API\Server\Stop Server.vi - A93825BB3476291241D9E34DA3CF83DA + FDA001872E863E8C254CF53A4876F2C6 \ No newline at end of file diff --git a/labview source/gRPC lv Support/grpc-lvsupport.lvlib b/labview source/gRPC lv Support/grpc-lvsupport.lvlib index a2db545b..36b0c1f0 100644 --- a/labview source/gRPC lv Support/grpc-lvsupport.lvlib +++ b/labview source/gRPC lv Support/grpc-lvsupport.lvlib @@ -108,5 +108,6 @@ + diff --git a/src/feature_toggles.cc b/src/feature_toggles.cc index 1bb8f85c..4dd58c03 100644 --- a/src/feature_toggles.cc +++ b/src/feature_toggles.cc @@ -3,6 +3,7 @@ #include #include #include +#include namespace grpc_labview { // Function to read feature configurations from an INI file @@ -35,7 +36,10 @@ namespace grpc_labview { std::string key, value; if (std::getline(iss, key, '=') && std::getline(iss, value)) { // Append section name to key for uniqueness + key.erase(std::remove_if(key.begin(), key.end(), ::isspace),key.end()); + value.erase(std::remove_if(value.begin(), value.end(), ::isspace),value.end()); std::string fullKey = currentSection.empty() ? key : currentSection + "_" + key; + std::transform(value.begin(), value.end(), value.begin(), ::tolower); featureFlags[fullKey] = (value == "true"); } } diff --git a/src/feature_toggles.h b/src/feature_toggles.h index e23ad679..62a20068 100644 --- a/src/feature_toggles.h +++ b/src/feature_toggles.h @@ -1,17 +1,18 @@ #include #include #include -#include +#include namespace grpc_labview { class FeatureConfig { private: - std::map featureFlags; + std::unordered_map featureFlags; // Constructor to initialize with default values FeatureConfig() { featureFlags["gRPC"] = true; // Enable gRPC by default as an example, this will never be overridden by config file - featureFlags["EfficientMessageCopy"] = true; + featureFlags["data_EfficientMessageCopy"] = true; + featureFlags["data_useOccurrence"] = true; } public: @@ -27,4 +28,5 @@ namespace grpc_labview { // Function to check if a feature is enabled bool isFeatureEnabled(const std::string& featureName) const; }; + } \ No newline at end of file diff --git a/src/grpc_client.cc b/src/grpc_client.cc index 28bd5ee6..90c4ee56 100644 --- a/src/grpc_client.cc +++ b/src/grpc_client.cc @@ -18,19 +18,19 @@ namespace grpc_labview //--------------------------------------------------------------------- //--------------------------------------------------------------------- LabVIEWgRPCClient::LabVIEWgRPCClient() - { + { } //--------------------------------------------------------------------- //--------------------------------------------------------------------- - void LabVIEWgRPCClient::Connect(const char* address, const std::string& certificatePath) - { + void LabVIEWgRPCClient::Connect(const char *address, const std::string &certificatePath) + { std::shared_ptr creds; if (!certificatePath.empty()) { std::string cacert = read_keycert(certificatePath); grpc::SslCredentialsOptions ssl_opts; - ssl_opts.pem_root_certs=cacert; + ssl_opts.pem_root_certs = cacert; creds = grpc::SslCredentials(ssl_opts); } else @@ -46,13 +46,13 @@ namespace grpc_labview //--------------------------------------------------------------------- //--------------------------------------------------------------------- ClientCall::~ClientCall() - { + { } //--------------------------------------------------------------------- //--------------------------------------------------------------------- void ClientCall::Finish() - { + { } //--------------------------------------------------------------------- @@ -77,7 +77,7 @@ namespace grpc_labview //--------------------------------------------------------------------- //--------------------------------------------------------------------- - bool ServerStreamingClientCall::Read(LVMessage* message) + bool ServerStreamingClientCall::Read(LVMessage *message) { bool result = _reader->Read(message); return result; @@ -104,15 +104,15 @@ namespace grpc_labview if (!_writesComplete) { _writesComplete = true; - _writer->WritesDone(); + _writer->WritesDone(); } } - + //--------------------------------------------------------------------- //--------------------------------------------------------------------- - bool ClientStreamingClientCall::Write(LVMessage* message) + bool ClientStreamingClientCall::Write(LVMessage *message) { - return _writer->Write(*message); + return _writer->Write(*message); } //--------------------------------------------------------------------- @@ -136,20 +136,20 @@ namespace grpc_labview if (!_writesComplete) { _writesComplete = true; - _readerWriter->WritesDone(); + _readerWriter->WritesDone(); } } - + //--------------------------------------------------------------------- //--------------------------------------------------------------------- - bool BidiStreamingClientCall::Read(LVMessage* message) + bool BidiStreamingClientCall::Read(LVMessage *message) { return _readerWriter->Read(message); } //--------------------------------------------------------------------- //--------------------------------------------------------------------- - bool BidiStreamingClientCall::Write(LVMessage* message) + bool BidiStreamingClientCall::Write(LVMessage *message) { return _readerWriter->Write(*message); } @@ -166,12 +166,12 @@ namespace grpc_labview } } -int32_t ClientCleanUpProc(grpc_labview::gRPCid* clientId); -void CheckActiveAndSignalOccurenceForClientCall(grpc_labview::ClientCall* clientCall); +int32_t ClientCleanUpProc(grpc_labview::gRPCid *clientId); +void CheckActiveAndSignalOccurenceForClientCall(grpc_labview::ClientCall *clientCall); //--------------------------------------------------------------------- //--------------------------------------------------------------------- -LIBRARY_EXPORT int32_t CreateClient(const char* address, const char* certificatePath, grpc_labview::gRPCid** clientId) +LIBRARY_EXPORT int32_t CreateClient(const char *address, const char *certificatePath, grpc_labview::gRPCid **clientId) { grpc_labview::InitCallbacks(); @@ -184,7 +184,7 @@ LIBRARY_EXPORT int32_t CreateClient(const char* address, const char* certificate //--------------------------------------------------------------------- //--------------------------------------------------------------------- -int32_t CloseClient(grpc_labview::LabVIEWgRPCClient* client) +int32_t CloseClient(grpc_labview::LabVIEWgRPCClient *client) { if (!client) { @@ -196,22 +196,20 @@ int32_t CloseClient(grpc_labview::LabVIEWgRPCClient* client) } // Signal a lv occurence for an active client call from async c++ thread -void CheckActiveAndSignalOccurenceForClientCall(grpc_labview::ClientCall* clientCall) +void CheckActiveAndSignalOccurenceForClientCall(grpc_labview::ClientCall *clientCall) { if (clientCall == nullptr) { return; } - std::lock_guard lock(clientCall->_client->clientLock); - std::list::iterator it; - it = std::find(clientCall->_client->ActiveClientCalls.begin(), clientCall->_client->ActiveClientCalls.end(), clientCall); - if (it != clientCall->_client->ActiveClientCalls.end()) + std::unique_lock lock(clientCall->_client->clientLock); + if (clientCall->_client->ActiveClientCalls.find(clientCall) != clientCall->_client->ActiveClientCalls.end()) { grpc_labview::SignalOccurrence(clientCall->_occurrence); } } -LIBRARY_EXPORT int32_t CloseClient(grpc_labview::gRPCid* clientId) +LIBRARY_EXPORT int32_t CloseClient(grpc_labview::gRPCid *clientId) { auto client = clientId->CastTo(); if (!client) @@ -224,28 +222,26 @@ LIBRARY_EXPORT int32_t CloseClient(grpc_labview::gRPCid* clientId) return 0; } -int32_t ClientCleanUpProc(grpc_labview::gRPCid* clientId) +int32_t ClientCleanUpProc(grpc_labview::gRPCid *clientId) { auto client = clientId->CastTo(); if (!client) { return -1; } + std::unique_lock lock(client->clientLock); + for (auto activeClientCall = client->ActiveClientCalls.begin(); activeClientCall != client->ActiveClientCalls.end(); activeClientCall++) { - std::lock_guard lock(client->clientLock); - for (auto activeClientCall = client->ActiveClientCalls.begin(); activeClientCall != client->ActiveClientCalls.end(); activeClientCall++) - { - (*activeClientCall)->Cancel(); - } - - client->ActiveClientCalls.clear(); + activeClientCall->first->Cancel(); } + client->ActiveClientCalls.clear(); + lock.unlock(); return CloseClient(client.get()); } //--------------------------------------------------------------------- //--------------------------------------------------------------------- -LIBRARY_EXPORT int32_t CreateClientContext(grpc_labview::gRPCid** contextId) +LIBRARY_EXPORT int32_t CreateClientContext(grpc_labview::gRPCid **contextId) { auto clientContext = std::make_shared(); *contextId = grpc_labview::gPointerManager.RegisterPointer(clientContext); @@ -254,7 +250,7 @@ LIBRARY_EXPORT int32_t CreateClientContext(grpc_labview::gRPCid** contextId) //--------------------------------------------------------------------- //--------------------------------------------------------------------- -LIBRARY_EXPORT int32_t CloseClientContext(grpc_labview::gRPCid* contextId) +LIBRARY_EXPORT int32_t CloseClientContext(grpc_labview::gRPCid *contextId) { auto context = contextId->CastTo(); if (!context) @@ -265,20 +261,19 @@ LIBRARY_EXPORT int32_t CloseClientContext(grpc_labview::gRPCid* contextId) return 0; } - //--------------------------------------------------------------------- //--------------------------------------------------------------------- LIBRARY_EXPORT int32_t ClientUnaryCall2( - grpc_labview::gRPCid* clientId, - grpc_labview::MagicCookie* occurrence, - const char* methodName, - const char* requestMessageName, - const char* responseMessageName, - int8_t* requestCluster, - grpc_labview::gRPCid** callId, + grpc_labview::gRPCid *clientId, + grpc_labview::MagicCookie *occurrence, + const char *methodName, + const char *requestMessageName, + const char *responseMessageName, + int8_t *requestCluster, + grpc_labview::gRPCid **callId, int32_t timeoutMs, - grpc_labview::gRPCid* contextId, - int8_t* responseCluster) + grpc_labview::gRPCid *contextId, + int8_t *responseCluster) { auto client = clientId->CastTo(); if (!client) @@ -306,66 +301,79 @@ LIBRARY_EXPORT int32_t ClientUnaryCall2( clientContext->set_deadline(timeoutMs); } + auto featureConfig = grpc_labview::FeatureConfig::getInstance(); + auto clientCall = new grpc_labview::ClientCall(); + std::unique_lock lock(client->clientLock); + client->ActiveClientCalls[clientCall] = true; + lock.unlock(); *callId = grpc_labview::gPointerManager.RegisterPointer(clientCall); clientCall->_client = client; clientCall->_methodName = methodName; - clientCall->_occurrence = *occurrence; + + if(featureConfig.isFeatureEnabled("data_useOccurrence")) + { + clientCall->_occurrence = *occurrence; + } + else{ + clientCall->_occurrence = 0; + } clientCall->_context = clientContext; - auto featureConfig = grpc_labview::FeatureConfig::getInstance(); - if (featureConfig.isFeatureEnabled("EfficientMessageCopy") && responseCluster != nullptr){ + if (featureConfig.isFeatureEnabled("data_EfficientMessageCopy") && responseCluster != nullptr) + { clientCall->_useLVEfficientMessage = true; } - if (clientCall->_useLVEfficientMessage){ + if (clientCall->_useLVEfficientMessage) + { clientCall->_request = std::make_shared(requestMetadata); clientCall->_response = std::make_shared(responseMetadata); clientCall->_request->SetLVClusterHandle(reinterpret_cast(requestCluster)); clientCall->_response->SetLVClusterHandle(reinterpret_cast(responseCluster)); } - else { + else + { clientCall->_request = std::make_shared(requestMetadata); - clientCall->_response = std::make_shared(responseMetadata); + clientCall->_response = std::make_shared(responseMetadata); } try { grpc_labview::ClusterDataCopier::CopyFromCluster(*clientCall->_request.get(), requestCluster); } - catch (grpc_labview::InvalidEnumValueException& e) + catch (grpc_labview::InvalidEnumValueException &e) { return e.code; } clientCall->_runFuture = std::async( - std::launch::async, - [clientCall]() + std::launch::async, + [clientCall]() { grpc::internal::RpcMethod method(clientCall->_methodName.c_str(), grpc::internal::RpcMethod::NORMAL_RPC); - clientCall->_status = grpc::internal::BlockingUnaryCall(clientCall->_client->Channel.get(), method, &(clientCall->_context.get()->gRPCClientContext) , *clientCall->_request.get(), clientCall->_response.get()); - CheckActiveAndSignalOccurenceForClientCall(clientCall); + clientCall->_status = grpc::internal::BlockingUnaryCall(clientCall->_client->Channel.get(), method, &(clientCall->_context.get()->gRPCClientContext), *clientCall->_request.get(), clientCall->_response.get()); + if(clientCall->_occurrence != 0) + { + CheckActiveAndSignalOccurenceForClientCall(clientCall); + } return 0; }); - - std::lock_guard lock(client->clientLock); - client->ActiveClientCalls.push_back(clientCall); return 0; } - //--------------------------------------------------------------------- //--------------------------------------------------------------------- LIBRARY_EXPORT int32_t ClientUnaryCall( - grpc_labview::gRPCid* clientId, - grpc_labview::MagicCookie* occurrence, - const char* methodName, - const char* requestMessageName, - const char* responseMessageName, - int8_t* requestCluster, - grpc_labview::gRPCid** callId, + grpc_labview::gRPCid *clientId, + grpc_labview::MagicCookie *occurrence, + const char *methodName, + const char *requestMessageName, + const char *responseMessageName, + int8_t *requestCluster, + grpc_labview::gRPCid **callId, int32_t timeoutMs, - grpc_labview::gRPCid* contextId) + grpc_labview::gRPCid *contextId) { return ClientUnaryCall2(clientId, occurrence, methodName, requestMessageName, responseMessageName, requestCluster, callId, timeoutMs, contextId, nullptr); } @@ -374,10 +382,10 @@ LIBRARY_EXPORT int32_t ClientUnaryCall( //--------------------------------------------------------------------- //--------------------------------------------------------------------- LIBRARY_EXPORT int32_t CompleteClientUnaryCall2( - grpc_labview::gRPCid* callId, - int8_t* responseCluster, - grpc_labview::LStrHandle* errorMessage, - grpc_labview::AnyCluster* errorDetailsCluster) + grpc_labview::gRPCid *callId, + int8_t *responseCluster, + grpc_labview::LStrHandle *errorMessage, + grpc_labview::AnyCluster *errorDetailsCluster) { auto clientCall = callId->CastTo(); if (!clientCall) @@ -385,17 +393,20 @@ LIBRARY_EXPORT int32_t CompleteClientUnaryCall2( return -1; } + grpc_labview::gPointerManager.UnregisterPointer(callId); int32_t result = 0; if (clientCall->_status.ok()) { - if (!clientCall->_useLVEfficientMessage) { + clientCall->_runFuture.wait(); + if (!clientCall->_useLVEfficientMessage) + { try { grpc_labview::ClusterDataCopier::CopyToCluster(*clientCall->_response.get(), responseCluster); } - catch (grpc_labview::InvalidEnumValueException& e) + catch (grpc_labview::InvalidEnumValueException &e) { if (errorMessage != nullptr) { @@ -416,14 +427,19 @@ LIBRARY_EXPORT int32_t CompleteClientUnaryCall2( { } } - std::lock_guard lock(clientCall->_client->clientLock); - clientCall->_client->ActiveClientCalls.remove(clientCall.get()); + std::unique_lock lock(clientCall->_client->clientLock); + auto call = clientCall->_client->ActiveClientCalls.find(clientCall.get()); + if (call != clientCall->_client->ActiveClientCalls.end()) + { + clientCall->_client->ActiveClientCalls.erase(call); + } + lock.unlock(); return result; } //--------------------------------------------------------------------- //--------------------------------------------------------------------- -LIBRARY_EXPORT int32_t CompleteClientUnaryCall(grpc_labview::gRPCid* callId, int8_t* responseCluster) +LIBRARY_EXPORT int32_t CompleteClientUnaryCall(grpc_labview::gRPCid *callId, int8_t *responseCluster) { return CompleteClientUnaryCall2(callId, responseCluster, nullptr, nullptr); } @@ -431,13 +447,13 @@ LIBRARY_EXPORT int32_t CompleteClientUnaryCall(grpc_labview::gRPCid* callId, int //--------------------------------------------------------------------- //--------------------------------------------------------------------- LIBRARY_EXPORT int32_t ClientBeginClientStreamingCall( - grpc_labview::gRPCid* clientId, - const char* methodName, - const char* requestMessageName, - const char* responseMessageName, - grpc_labview::gRPCid** callId, + grpc_labview::gRPCid *clientId, + const char *methodName, + const char *requestMessageName, + const char *responseMessageName, + grpc_labview::gRPCid **callId, int32_t timeoutMs, - grpc_labview::gRPCid* contextId) + grpc_labview::gRPCid *contextId) { auto client = clientId->CastTo(); if (!client) @@ -466,6 +482,9 @@ LIBRARY_EXPORT int32_t ClientBeginClientStreamingCall( } auto clientCall = new grpc_labview::ClientStreamingClientCall(); + std::unique_lock lock(client->clientLock); + client->ActiveClientCalls[clientCall] = true; + lock.unlock(); *callId = grpc_labview::gPointerManager.RegisterPointer(clientCall); clientCall->_client = client; clientCall->_request = std::make_shared(requestMetadata); @@ -476,23 +495,21 @@ LIBRARY_EXPORT int32_t ClientBeginClientStreamingCall( auto writer = grpc::internal::ClientWriterFactory::Create(client->Channel.get(), method, &(clientCall->_context.get()->gRPCClientContext), clientCall->_response.get()); clientCall->_writer = std::shared_ptr>(writer); - std::lock_guard lock(client->clientLock); - client->ActiveClientCalls.push_back(clientCall); - return 0; + return 0; } //--------------------------------------------------------------------- //--------------------------------------------------------------------- LIBRARY_EXPORT int32_t ClientBeginServerStreamingCall( - grpc_labview::gRPCid* clientId, - const char* methodName, - const char* requestMessageName, - const char* responseMessageName, - int8_t* requestCluster, - grpc_labview::gRPCid** callId, + grpc_labview::gRPCid *clientId, + const char *methodName, + const char *requestMessageName, + const char *responseMessageName, + int8_t *requestCluster, + grpc_labview::gRPCid **callId, int32_t timeoutMs, - grpc_labview::gRPCid* contextId) -{ + grpc_labview::gRPCid *contextId) +{ auto client = clientId->CastTo(); if (!client) { @@ -520,6 +537,9 @@ LIBRARY_EXPORT int32_t ClientBeginServerStreamingCall( } auto clientCall = new grpc_labview::ServerStreamingClientCall(); + std::unique_lock lock(client->clientLock); + client->ActiveClientCalls[clientCall] = true; + lock.unlock(); *callId = grpc_labview::gPointerManager.RegisterPointer(clientCall); clientCall->_client = client; clientCall->_request = std::make_shared(requestMetadata); @@ -530,7 +550,7 @@ LIBRARY_EXPORT int32_t ClientBeginServerStreamingCall( { grpc_labview::ClusterDataCopier::CopyFromCluster(*clientCall->_request.get(), requestCluster); } - catch (grpc_labview::InvalidEnumValueException& e) + catch (grpc_labview::InvalidEnumValueException &e) { return e.code; } @@ -539,21 +559,19 @@ LIBRARY_EXPORT int32_t ClientBeginServerStreamingCall( auto reader = grpc::internal::ClientReaderFactory::Create(client->Channel.get(), method, &(clientCall->_context.get()->gRPCClientContext), *clientCall->_request.get()); clientCall->_reader = std::shared_ptr>(reader); - std::lock_guard lock(client->clientLock); - client->ActiveClientCalls.push_back(clientCall); return 0; } //--------------------------------------------------------------------- //--------------------------------------------------------------------- LIBRARY_EXPORT int32_t ClientBeginBidiStreamingCall( - grpc_labview::gRPCid* clientId, - const char* methodName, - const char* requestMessageName, - const char* responseMessageName, - grpc_labview::gRPCid** callId, + grpc_labview::gRPCid *clientId, + const char *methodName, + const char *requestMessageName, + const char *responseMessageName, + grpc_labview::gRPCid **callId, int32_t timeoutMs, - grpc_labview::gRPCid* contextId) + grpc_labview::gRPCid *contextId) { auto client = clientId->CastTo(); if (!client) @@ -582,6 +600,9 @@ LIBRARY_EXPORT int32_t ClientBeginBidiStreamingCall( } auto clientCall = new grpc_labview::BidiStreamingClientCall(); + std::unique_lock lock(client->clientLock); + client->ActiveClientCalls[clientCall] = true; + lock.unlock(); *callId = grpc_labview::gPointerManager.RegisterPointer(clientCall); clientCall->_client = client; clientCall->_request = std::make_shared(requestMetadata); @@ -592,34 +613,42 @@ LIBRARY_EXPORT int32_t ClientBeginBidiStreamingCall( auto readerWriter = grpc::internal::ClientReaderWriterFactory::Create(client->Channel.get(), method, &(clientCall->_context.get()->gRPCClientContext)); clientCall->_readerWriter = std::shared_ptr>(readerWriter); - std::lock_guard lock(client->clientLock); - client->ActiveClientCalls.push_back(clientCall); return 0; } //--------------------------------------------------------------------- //--------------------------------------------------------------------- -LIBRARY_EXPORT int32_t ClientBeginReadFromStream(grpc_labview::gRPCid* callId, grpc_labview::MagicCookie* occurrencePtr) +LIBRARY_EXPORT int32_t ClientBeginReadFromStream(grpc_labview::gRPCid *callId, grpc_labview::MagicCookie *occurrencePtr) { auto reader = callId->CastTo(); auto call = callId->CastTo(); - auto occurrence = *occurrencePtr; + auto featureConfig = grpc_labview::FeatureConfig::getInstance(); + + grpc_labview::MagicCookie occurrence = 0; + if (featureConfig.isFeatureEnabled("data_useOccurrence")) + { + occurrence = *occurrencePtr; + } if (!reader || !call) { - grpc_labview::SignalOccurrence(occurrence); + if (occurrence != 0){ + grpc_labview::SignalOccurrence(occurrence); + } return -1; } call->_occurrence = occurrence; reader->_readFuture = std::async( - std::launch::async, - [call, reader]() + std::launch::async, + [call, reader]() { call->_response->Clear(); auto result = reader->Read(call->_response.get()); - CheckActiveAndSignalOccurenceForClientCall(call.get()); + if (call->_occurrence != 0){ + CheckActiveAndSignalOccurenceForClientCall(call.get()); + } return result; }); @@ -628,7 +657,7 @@ LIBRARY_EXPORT int32_t ClientBeginReadFromStream(grpc_labview::gRPCid* callId, g //--------------------------------------------------------------------- //--------------------------------------------------------------------- -LIBRARY_EXPORT int32_t ClientCompleteReadFromStream(grpc_labview::gRPCid* callId, int* success, int8_t* responseCluster) +LIBRARY_EXPORT int32_t ClientCompleteReadFromStream(grpc_labview::gRPCid *callId, int *success, int8_t *responseCluster) { auto reader = callId->CastTo(); auto call = callId->CastTo(); @@ -644,7 +673,7 @@ LIBRARY_EXPORT int32_t ClientCompleteReadFromStream(grpc_labview::gRPCid* callId { grpc_labview::ClusterDataCopier::CopyToCluster(*call->_response.get(), responseCluster); } - catch (grpc_labview::InvalidEnumValueException& e) + catch (grpc_labview::InvalidEnumValueException &e) { return e.code; } @@ -654,7 +683,7 @@ LIBRARY_EXPORT int32_t ClientCompleteReadFromStream(grpc_labview::gRPCid* callId //--------------------------------------------------------------------- //--------------------------------------------------------------------- -LIBRARY_EXPORT int32_t ClientWriteToStream(grpc_labview::gRPCid* callId, int8_t* requestCluster, int* success) +LIBRARY_EXPORT int32_t ClientWriteToStream(grpc_labview::gRPCid *callId, int8_t *requestCluster, int *success) { auto writer = callId->CastTo(); if (!writer) @@ -670,7 +699,7 @@ LIBRARY_EXPORT int32_t ClientWriteToStream(grpc_labview::gRPCid* callId, int8_t* { grpc_labview::ClusterDataCopier::CopyFromCluster(*clientCall->_request.get(), requestCluster); } - catch (grpc_labview::InvalidEnumValueException& e) + catch (grpc_labview::InvalidEnumValueException &e) { return e.code; } @@ -680,7 +709,7 @@ LIBRARY_EXPORT int32_t ClientWriteToStream(grpc_labview::gRPCid* callId, int8_t* //--------------------------------------------------------------------- //--------------------------------------------------------------------- -LIBRARY_EXPORT int32_t ClientWritesComplete(grpc_labview::gRPCid* callId) +LIBRARY_EXPORT int32_t ClientWritesComplete(grpc_labview::gRPCid *callId) { auto writer = callId->CastTo(); if (!writer) @@ -694,11 +723,11 @@ LIBRARY_EXPORT int32_t ClientWritesComplete(grpc_labview::gRPCid* callId) //--------------------------------------------------------------------- //--------------------------------------------------------------------- LIBRARY_EXPORT int32_t FinishClientCompleteClientStreamingCall( - grpc_labview::gRPCid* callId, - int8_t* responseCluster, - grpc_labview::LStrHandle* errorMessage, - grpc_labview::AnyCluster* errorDetailsCluster) -{ + grpc_labview::gRPCid *callId, + int8_t *responseCluster, + grpc_labview::LStrHandle *errorMessage, + grpc_labview::AnyCluster *errorDetailsCluster) +{ auto call = callId->CastTo(); if (!call) { @@ -707,11 +736,12 @@ LIBRARY_EXPORT int32_t FinishClientCompleteClientStreamingCall( int32_t result = 0; if (call->_status.ok()) { + call->_runFuture.wait(); try { grpc_labview::ClusterDataCopier::CopyToCluster(*call->_response.get(), responseCluster); } - catch (grpc_labview::InvalidEnumValueException& e) + catch (grpc_labview::InvalidEnumValueException &e) { result = e.code; if (errorMessage != nullptr) @@ -731,30 +761,42 @@ LIBRARY_EXPORT int32_t FinishClientCompleteClientStreamingCall( { } } + std::unique_lock lock(call->_client->clientLock); + auto client_call = call->_client->ActiveClientCalls.find(call.get()); + if (client_call != call->_client->ActiveClientCalls.end()) { - std::lock_guard lock(call->_client->clientLock); - call->_client->ActiveClientCalls.remove(call.get()); + call->_client->ActiveClientCalls.erase(client_call); } + lock.unlock(); grpc_labview::gPointerManager.UnregisterPointer(callId); return result; } //--------------------------------------------------------------------- //--------------------------------------------------------------------- -LIBRARY_EXPORT int32_t ClientCompleteClientStreamingCall(grpc_labview::gRPCid* callId, grpc_labview::MagicCookie* occurrencePtr) +LIBRARY_EXPORT int32_t ClientCompleteClientStreamingCall(grpc_labview::gRPCid *callId, grpc_labview::MagicCookie *occurrencePtr) { auto call = callId->CastTo(); if (!call) { return -1; } - call->_occurrence = *occurrencePtr; + auto featureConfig = grpc_labview::FeatureConfig::getInstance(); + + if(featureConfig.isFeatureEnabled("data_useOccurrence")){ + call->_occurrence = *occurrencePtr; + } + else{ + call->_occurrence = 0; + } call->_runFuture = std::async( std::launch::async, [call]() { call->Finish(); - CheckActiveAndSignalOccurenceForClientCall(call.get()); + if(call->_occurrence != 0){ + CheckActiveAndSignalOccurenceForClientCall(call.get()); + } return 0; }); return 0; @@ -763,9 +805,9 @@ LIBRARY_EXPORT int32_t ClientCompleteClientStreamingCall(grpc_labview::gRPCid* c //--------------------------------------------------------------------- //--------------------------------------------------------------------- LIBRARY_EXPORT int32_t ClientCompleteStreamingCall( - grpc_labview::gRPCid* callId, - grpc_labview::LStrHandle* errorMessage, - grpc_labview::AnyCluster* errorDetailsCluster) + grpc_labview::gRPCid *callId, + grpc_labview::LStrHandle *errorMessage, + grpc_labview::AnyCluster *errorDetailsCluster) { auto call = callId->CastTo(); if (!call) @@ -778,7 +820,7 @@ LIBRARY_EXPORT int32_t ClientCompleteStreamingCall( grpc_labview::gPointerManager.UnregisterPointer(callId); call->Finish(); - int32_t result = 0; + int32_t result = 0; if (!call->_status.ok()) { result = -(1000 + call->_status.error_code()); @@ -790,15 +832,20 @@ LIBRARY_EXPORT int32_t ClientCompleteStreamingCall( { } } - std::lock_guard lock(call->_client->clientLock); - call->_client->ActiveClientCalls.remove(call.get()); + std::unique_lock lock(call->_client->clientLock); + auto client_call = call->_client->ActiveClientCalls.find(call.get()); + if (client_call != call->_client->ActiveClientCalls.end()) + { + call->_client->ActiveClientCalls.erase(client_call); + } + lock.unlock(); return result; } //--------------------------------------------------------------------- //--------------------------------------------------------------------- LIBRARY_EXPORT int32_t ClientCancelCallContext( - grpc_labview::gRPCid* contextId) + grpc_labview::gRPCid *contextId) { auto context = contextId->CastTo(); if (!context) @@ -813,9 +860,9 @@ LIBRARY_EXPORT int32_t ClientCancelCallContext( //--------------------------------------------------------------------- //--------------------------------------------------------------------- LIBRARY_EXPORT int32_t ClientCancelCall( - grpc_labview::gRPCid* callId, - grpc_labview::LStrHandle* errorMessage, - grpc_labview::AnyCluster* errorDetailsCluster) + grpc_labview::gRPCid *callId, + grpc_labview::LStrHandle *errorMessage, + grpc_labview::AnyCluster *errorDetailsCluster) { auto call = callId->CastTo(); if (!call) @@ -840,7 +887,12 @@ LIBRARY_EXPORT int32_t ClientCancelCall( { } } - std::lock_guard lock(call->_client->clientLock); - call->_client->ActiveClientCalls.remove(call.get()); + std::unique_lock lock(call->_client->clientLock); + auto client_call = call->_client->ActiveClientCalls.find(call.get()); + if (client_call != call->_client->ActiveClientCalls.end()) + { + call->_client->ActiveClientCalls.erase(client_call); + } + lock.unlock(); return result; } \ No newline at end of file diff --git a/src/grpc_client.h b/src/grpc_client.h index c0a62384..b1f953a5 100644 --- a/src/grpc_client.h +++ b/src/grpc_client.h @@ -10,9 +10,9 @@ #include #include #include -#include +#include -namespace grpc_labview +namespace grpc_labview { //--------------------------------------------------------------------- //--------------------------------------------------------------------- @@ -25,11 +25,11 @@ namespace grpc_labview { public: LabVIEWgRPCClient(); - void Connect(const char* address, const std::string& certificatePath); + void Connect(const char *address, const std::string &certificatePath); public: std::shared_ptr Channel; - std::list ActiveClientCalls; + std::unordered_map ActiveClientCalls; std::mutex clientLock; }; @@ -51,7 +51,7 @@ namespace grpc_labview virtual ~ClientCall(); virtual void Finish(); void Cancel(); - + public: std::shared_ptr _client; std::string _methodName; @@ -69,7 +69,7 @@ namespace grpc_labview class StreamWriter { public: - virtual bool Write(LVMessage* message) = 0; + virtual bool Write(LVMessage *message) = 0; virtual void WritesComplete() = 0; }; @@ -81,17 +81,18 @@ namespace grpc_labview std::future _readFuture; public: - virtual bool Read(LVMessage* message) = 0; + virtual bool Read(LVMessage *message) = 0; }; //--------------------------------------------------------------------- //--------------------------------------------------------------------- class ServerStreamingClientCall : public ClientCall, public StreamReader - { + { public: ~ServerStreamingClientCall() override; - bool Read(LVMessage* message) override; + bool Read(LVMessage *message) override; void Finish() override; + public: std::shared_ptr> _reader; }; @@ -99,16 +100,17 @@ namespace grpc_labview //--------------------------------------------------------------------- //--------------------------------------------------------------------- class ClientStreamingClientCall : public ClientCall, public StreamWriter - { + { public: ClientStreamingClientCall() { _writesComplete = false; } ~ClientStreamingClientCall(); void Finish() override; - bool Write(LVMessage* message) override; + bool Write(LVMessage *message) override; void WritesComplete() override; public: std::shared_ptr> _writer; + private: bool _writesComplete; }; @@ -116,17 +118,18 @@ namespace grpc_labview //--------------------------------------------------------------------- //--------------------------------------------------------------------- class BidiStreamingClientCall : public ClientCall, public StreamReader, public StreamWriter - { + { public: BidiStreamingClientCall() { _writesComplete = false; } ~BidiStreamingClientCall(); void Finish() override; void WritesComplete() override; - bool Read(LVMessage* message) override; - bool Write(LVMessage* message) override; + bool Read(LVMessage *message) override; + bool Write(LVMessage *message) override; public: std::shared_ptr> _readerWriter; + private: bool _writesComplete; }; diff --git a/src/grpc_interop.cc b/src/grpc_interop.cc index 50bde0ad..24e4bfcf 100644 --- a/src/grpc_interop.cc +++ b/src/grpc_interop.cc @@ -10,6 +10,7 @@ #include #include #include +#include namespace grpc_labview { @@ -187,6 +188,13 @@ namespace grpc_labview int32_t ServerCleanupProc(grpc_labview::gRPCid* serverId); +//--------------------------------------------------------------------- +//--------------------------------------------------------------------- +LIBRARY_EXPORT void readIniFile(const char* filePath) +{ + grpc_labview::FeatureConfig::getInstance().readConfigFromFile(filePath); +} + //--------------------------------------------------------------------- //--------------------------------------------------------------------- LIBRARY_EXPORT int32_t LVCreateServer(grpc_labview::gRPCid** id) diff --git a/src/lv_interop.cc b/src/lv_interop.cc index 51f9371b..5c362441 100644 --- a/src/lv_interop.cc +++ b/src/lv_interop.cc @@ -60,7 +60,7 @@ namespace grpc_labview void InitCallbacks() { // Instantiating the feature toggles singleton that will read the feature configuration file - FeatureConfig::getInstance().readConfigFromFile("feature_config.ini"); + // FeatureConfig::getInstance().readConfigFromFile("feature_config.ini"); if (NumericArrayResizeImp != nullptr) {