Add completion callback to IPCACloseHandle().
authorSoemin Tjong <stjong@microsoft.com>
Wed, 1 Mar 2017 05:39:06 +0000 (21:39 -0800)
committerDan Mihai <Daniel.Mihai@microsoft.com>
Sat, 15 Apr 2017 17:44:51 +0000 (17:44 +0000)
This addresses the scenario where the app needs to be certain when all
callbacks related to the handle it is closing are completed.

Change-Id: I75b211d477405be27c6c804bc89e529088aa1a90
Signed-off-by: Soemin Tjong <stjong@microsoft.com>
Reviewed-on: https://gerrit.iotivity.org/gerrit/18217
Tested-by: jenkins-iotivity <jenkins@iotivity.org>
Reviewed-by: Dan Mihai <Daniel.Mihai@microsoft.com>
13 files changed:
resource/IPCA/inc/ipca.h
resource/IPCA/samples/ElevatorClient/ElevatorClient.cpp
resource/IPCA/samples/ipcaapp/ipcaapp.cpp
resource/IPCA/src/app.cpp
resource/IPCA/src/callback.cpp
resource/IPCA/src/inc/app.h
resource/IPCA/src/inc/callback.h
resource/IPCA/src/ipca.cpp
resource/IPCA/unittests/IPCAElevatorClient.cpp
resource/IPCA/unittests/IPCAElevatorClient.h
resource/IPCA/unittests/ipcaunittests.cpp
resource/IPCA/unittests/mockOC.cpp
resource/IPCA/unittests/testelevatorserver.cpp

index f0d8385..6be0da0 100644 (file)
@@ -366,6 +366,15 @@ typedef void (IPCA_CALL *IPCACreateResourceComplete)(
 typedef void (IPCA_CALL *IPCADeleteResourceComplete)(IPCAStatus result, void* context);
 
 /**
+ * Callback from IPCA when a handle is completely closed.
+ *
+ * @param[in] context   Caller's context set in IPCACloseHandle().
+ *
+ */
+typedef void (IPCA_CALL *IPCACloseHandleComplete)(void* context);
+
+
+/**
  * Discovery status in IPCADiscoverDeviceCallback.
  */
 typedef enum
@@ -748,14 +757,22 @@ IPCAStatus IPCA_CALL IPCAObserveResource(
  * For handle returned by IPCAObserveResource(), IPCACloseHandle unsubscribes server's resource
  * change notification.
  *
- * @param[in] handle  Handle returned in the following methods:
- *                    IPCAGetProperties()
- *                    IPCASetProperties()
- *                    IPCAObserveResource()
- *                    IPCADiscoverDevices()
+ * @param[in] handle                Handle returned in the following methods:
+ *                                  IPCAGetProperties(), IPCASetProperties(), IPCAObserveResource()
+ *                                  IPCADiscoverDevices(), IPCACreateResource() and
+ *                                  IPCARequestAccess().
+ * @param[in] closeHandleComplete   Optional callback when handle is completely closed.
+ *                                  Upon receiving this callback, an application can safely perform
+ *                                  any needed cleanup for resources related to the handle closed.
+ * @param[in] context               Application's context that is passed back as argument in the
+ *                                  closeHandleComplete callback.
  *
+ * @return IPCA_OK if successful.
+ *         IPCA_FAIL if handle is already closed, closeHandleComplete will not be called.
  */
-void IPCA_CALL IPCACloseHandle(IPCAHandle handle);
+IPCAStatus IPCA_CALL IPCACloseHandle(IPCAHandle handle,
+                                     IPCACloseHandleComplete closeHandleComplete,
+                                     void* context);
 
 /**
  * Perform factory reset.
index 8d91f83..2ee8830 100644 (file)
@@ -74,7 +74,7 @@ IPCAStatus RequestObserve()
 {
     if (g_observeHandle != nullptr)
     {
-        IPCACloseHandle(g_observeHandle);
+        IPCACloseHandle(g_observeHandle, nullptr, 0);
         g_observeHandle = nullptr;
     }
 
@@ -539,7 +539,7 @@ bool DiscoverElevator(bool freeRun, size_t timeOutMs)
         g_deviceDiscoveredCV.wait_for(lock, std::chrono::milliseconds{ timeOutMs });
 
         // Stop discovery.
-        IPCACloseHandle(g_discoverDeviceHandle);
+        IPCACloseHandle(g_discoverDeviceHandle, nullptr, 0);
     }
 
     return g_targetElevatorDiscovered;
index 69c8908..adc3fff 100644 (file)
@@ -650,6 +650,6 @@ int main()
 
     g_OCFDeviceList.clear();
 
-    IPCACloseHandle(discoverDeviceHandle);
+    IPCACloseHandle(discoverDeviceHandle, nullptr, 0);
     IPCAClose(g_ipcaAppHandle);
 }
index 2ea0096..016816d 100644 (file)
@@ -31,7 +31,9 @@ App::App(const IPCAAppInfo* ipcaAppInfo, IPCAVersion ipcaVersion) :
     m_isStopped(false),
     m_passwordInputCallbackHandle(nullptr),
     m_passwordDisplayCallbackHandle(nullptr),
-    m_ipcaVersion(ipcaVersion)
+    m_ipcaVersion(ipcaVersion),
+    m_passwordInputCallbackInfo(nullptr),
+    m_passwordDisplayCallbackInfo(nullptr)
 {
     m_ipcaAppInfo.appId = ipcaAppInfo->appId;
     m_ipcaAppInfo.appName = ipcaAppInfo->appName;
@@ -106,6 +108,19 @@ void App::Stop()
     ocfFramework.Stop(m_passwordInputCallbackHandle, m_passwordDisplayCallbackHandle);
     m_passwordInputCallbackHandle = nullptr;
     m_passwordDisplayCallbackHandle = nullptr;
+
+    // Deregister the callback info.
+    if (m_passwordInputCallbackInfo != nullptr)
+    {
+        DeleteAndUnregisterCallbackInfo(m_passwordInputCallbackInfo->mapKey);
+        m_passwordInputCallbackInfo = nullptr;
+    }
+
+    if (m_passwordDisplayCallbackInfo != nullptr)
+    {
+        DeleteAndUnregisterCallbackInfo(m_passwordDisplayCallbackInfo->mapKey);
+        m_passwordDisplayCallbackInfo = nullptr;
+    }
 }
 
 void App::AppWorkerThread(App* app)
@@ -366,6 +381,10 @@ IPCAStatus App::SetPasswordCallbacks(
     ocfFramework.SetInputPasswordCallback(inputCallbackInfo, &m_passwordInputCallbackHandle);
     ocfFramework.SetDisplayPasswordCallback(displayCallbackInfo, &m_passwordDisplayCallbackHandle);
 
+    // The CallbackInfo to be deregistered in Stop().
+    m_passwordInputCallbackInfo = inputCallbackInfo;
+    m_passwordDisplayCallbackInfo = displayCallbackInfo;
+
     return IPCA_OK;
 }
 
@@ -661,7 +680,9 @@ IPCAStatus App::DeleteResource(
     return status;
 }
 
-void App::CloseIPCAHandle(IPCAHandle handle)
+IPCAStatus App::CloseIPCAHandle(IPCAHandle handle,
+                    IPCACloseHandleComplete closeHandleComplete,
+                    const void* context)
 {
     size_t mapKey = reinterpret_cast<size_t>(handle);
 
@@ -682,7 +703,7 @@ void App::CloseIPCAHandle(IPCAHandle handle)
         }
     }
 
-    DeleteAndUnregisterCallbackInfo(mapKey);
+    return DeleteAndUnregisterCallbackInfo(mapKey, closeHandleComplete, context);
 }
 
 IPCAStatus App::CreateAndRegisterNewCallbackInfo(
@@ -734,7 +755,10 @@ IPCAStatus App::CreateAndRegisterNewCallbackInfo(
     return status;
 }
 
-void App::DeleteAndUnregisterCallbackInfo(size_t mapKey)
+IPCAStatus App::DeleteAndUnregisterCallbackInfo(
+                                size_t mapKey,
+                                IPCACloseHandleComplete closeHandleComplete,
+                                const void* context)
 {
-    m_callback->RemoveCallbackInfo(mapKey);
+    return m_callback->RemoveCallbackInfo(mapKey, closeHandleComplete, context);
 }
index e321364..8cb9cfe 100644 (file)
@@ -57,7 +57,7 @@ void Callback::Stop()
     m_stopCalled = true;
 
     // Wait some amount of time for all callbacks in progress to complete.
-    const int WaitTimeSeconds = 3;
+    const int WaitTimeSeconds = 30;
     int i = 0;
     while (i < WaitTimeSeconds)
     {
@@ -93,10 +93,25 @@ void Callback::Stop()
 
     if (allStopped == false)
     {
-        OIC_LOG_V(WARNING, TAG, "Stop() time out waiting for pending callbacks to complete.");
+        std::cout << "Stop() timed out: m_callbackInfoList count = " << m_callbackInfoList.size();
+        std::cout << " m_expiredCallbacksInprogress = " << m_expiredCallbacksInprogress;
+        OIC_LOG_V(
+            ERROR,
+            TAG,
+            "Stop() timed out: m_callbackInfoList count = [%d] m_expiredCallbacksInprogress = [%d]",
+            m_callbackInfoList.size(),
+            m_expiredCallbacksInprogress);
         throw timeoutException;
     }
+}
 
+void Callback::CommonInitializeCallbackInfo(CallbackInfo::Ptr cbInfo)
+{
+    cbInfo->app = m_app;
+    cbInfo->callbackInProgressCount = 0;
+    cbInfo->markedToBeRemoved = false;
+    cbInfo->requestSentTimestamp = 0;
+    cbInfo->closeHandleCompleteCallback = nullptr;
 }
 
 CallbackInfo::Ptr Callback::CreatePasswordCallbackInfo(
@@ -119,11 +134,9 @@ CallbackInfo::Ptr Callback::CreatePasswordCallbackInfo(
         return nullptr;
     }
 
-    cbInfo->app = m_app;
+    CommonInitializeCallbackInfo(cbInfo);
     cbInfo->type = cbType;
     cbInfo->callbackContext = context;
-    cbInfo->callbackInProgressCount = 0;
-    cbInfo->markedToBeRemoved = false;
 
     switch (cbType)
     {
@@ -152,12 +165,10 @@ CallbackInfo::Ptr Callback::CreateRequestAccessCompletionCallbackInfo(
         return nullptr;
     }
 
-    cbInfo->app = m_app;
+    CommonInitializeCallbackInfo(cbInfo);
     cbInfo->device = device;
     cbInfo->type = Callbacktype_RequestAccessCompletionCallback;
     cbInfo->callbackContext = context;
-    cbInfo->callbackInProgressCount = 0;
-    cbInfo->markedToBeRemoved = false;
     cbInfo->requestAccessCompletionCallback = completionCallback;
 
     if (resourcePath != nullptr)
@@ -187,13 +198,10 @@ CallbackInfo::Ptr Callback::CreateCallbackInfo(
         return nullptr;
     }
 
-    cbInfo->app = m_app;
+    CommonInitializeCallbackInfo(cbInfo);
     cbInfo->device = device;
     cbInfo->type = cbType;
     cbInfo->callbackContext = context;
-    cbInfo->callbackInProgressCount = 0;
-    cbInfo->markedToBeRemoved = false;
-    cbInfo->requestSentTimestamp = 0;
 
     cbInfo->resourcePath = std::string(resourcePath ? resourcePath : "");
     cbInfo->resourceInterface = std::string(resourceInterface ? resourceInterface : "");
@@ -356,6 +364,17 @@ bool Callback::SetCallbackInProgress(size_t mapKey)
     }
 }
 
+void Callback::CallCloseHandleComplete(IPCACloseHandleComplete closeHandleComplete,
+                                       const void* context)
+{
+    if (closeHandleComplete != nullptr)
+    {
+        std::thread callbackThread = std::thread(closeHandleComplete,
+                                                 const_cast<void*>(context));
+        callbackThread.detach();
+    }
+}
+
 bool Callback::ClearCallbackInProgress(size_t mapKey)
 {
     std::lock_guard<std::mutex> lock(m_callbackMutex);
@@ -364,30 +383,72 @@ bool Callback::ClearCallbackInProgress(size_t mapKey)
     // must complete.
     if (m_callbackInfoList.find(mapKey) !=  m_callbackInfoList.end())
     {
-        m_callbackInfoList[mapKey]->callbackInProgressCount--;
+        CallbackInfo::Ptr cbInfo = m_callbackInfoList[mapKey];
+
+        // There should be one ClearCallbackInProgress() for each SetCallbackInProgress().
+        assert(cbInfo->callbackInProgressCount > 0);
+
+        // One less in progress callback.
+        cbInfo->callbackInProgressCount--;
+
+        // closeHandleCompleteCallback is set if IPCACloseHandle() is called when callback
+        // for the handle that the app is closing is already in progress.
+        // Call it if this is the last callback that has completed.
+        if ((cbInfo->closeHandleCompleteCallback != nullptr) &&
+            (cbInfo->callbackInProgressCount == 0))
+        {
+            CallCloseHandleComplete(
+                cbInfo->closeHandleCompleteCallback,
+                cbInfo->closeHandleCompletecontext);
+
+            cbInfo->closeHandleCompleteCallback = nullptr;
+        }
         return true;
     }
 
-    OIC_LOG_V(INFO, TAG, "ClearCallbackInProgress() mapKey [%d] is not found", mapKey);
+    OIC_LOG_V(WARNING, TAG, "ClearCallbackInProgress() mapKey [%d] is not found", mapKey);
     assert(false); // In progress callback is not expected to be removed from the list.
     return false;
 }
 
-void Callback::RemoveCallbackInfo(size_t mapKey)
+IPCAStatus Callback::RemoveCallbackInfo(size_t mapKey,
+                        IPCACloseHandleComplete closeHandleComplete,
+                        const void* context)
 {
+    CallbackInfo::Ptr callbackInfo = nullptr;
+
     std::lock_guard<std::mutex> lock(m_callbackMutex);
 
     if (m_callbackInfoList.find(mapKey) !=  m_callbackInfoList.end())
     {
-        if (m_callbackInfoList[mapKey]->callbackInProgressCount == 0)
-        {
-            m_callbackInfoList.erase(mapKey);
-        }
-        else
-        {
-            m_callbackInfoList[mapKey]->markedToBeRemoved = true;
-        }
+        callbackInfo = m_callbackInfoList[mapKey];
     }
+
+    if ((callbackInfo == nullptr) || (callbackInfo->markedToBeRemoved == true))
+    {
+        // Handle is already closed (no longer in the list) or closing (marked to be removed).
+        return IPCA_FAIL;
+    }
+
+    // callbackInProgress count is 0 when there's no in progress callback to app's code.
+    if (callbackInfo->callbackInProgressCount == 0)
+    {
+        // Remove the reference to the CallbackInfo object and call closeHandleComplete
+        // to app if requested.
+        m_callbackInfoList.erase(mapKey);
+        CallCloseHandleComplete(closeHandleComplete, context);
+    }
+    else
+    {
+        // There's at least one in progress callback to app's code.
+        callbackInfo->markedToBeRemoved = true;
+
+        // Call to closeHandleComplete will happen when all the callbacks are completed.
+        callbackInfo->closeHandleCompleteCallback = closeHandleComplete;
+        callbackInfo->closeHandleCompletecontext = context;
+    }
+
+    return IPCA_OK;
 }
 
 void Callback::CompleteAndRemoveExpiredCallbackInfo(std::vector<CallbackInfo::Ptr>& cbInfoList)
@@ -408,8 +469,9 @@ void Callback::CompleteAndRemoveExpiredCallbackInfo(std::vector<CallbackInfo::Pt
         for(auto const& entry : m_callbackInfoList)
         {
             // Opportunistic removal of callback that couldn't be removed during
-            // RemoveCallbackInfo().
-            if (entry.second->markedToBeRemoved == true)
+            // RemoveCallbackInfo() and if by now it has completed the callback.
+            if ((entry.second->markedToBeRemoved == true) &&
+                (entry.second->callbackInProgressCount == 0))
             {
                 m_expiredCallbacksInprogress++;
                 cbInfoList.push_back(entry.second);
@@ -751,6 +813,7 @@ void Callback::DeleteResourceCallback(IPCAStatus status, CallbackInfo::Ptr cbInf
                 const_cast<void*>(cbInfo->callbackContext));
 
     ClearCallbackInProgress(cbInfo->mapKey);
+    RemoveCallbackInfo(cbInfo->mapKey);
 }
 
 void Callback::PasswordInputCallback(std::string deviceId,
@@ -846,4 +909,5 @@ void Callback::RequestAccessCompletionCallback(IPCAStatus status, CallbackInfo::
     }
 
     ClearCallbackInProgress(cbInfo->mapKey);
+    RemoveCallbackInfo(cbInfo->mapKey);
 }
index 59deed0..6222e0d 100644 (file)
@@ -127,10 +127,12 @@ class App
                             void* context,
                             IPCAHandle* handle);
 
-
-        // Close handle returned in DiscoverDevices(), GetProperties(), SetProperties(), or
-        // ObserveResource().
-        void CloseIPCAHandle(IPCAHandle handle);
+        // Close handle returned in DiscoverDevices(), GetProperties(), SetProperties(),
+        // ObserveResource(), CreateResource() and RequestAccess().
+        IPCAStatus CloseIPCAHandle(
+                        IPCAHandle handle,
+                        IPCACloseHandleComplete closeHandleComplete,
+                        const void* context);
 
     private:
         std::mutex m_appMutex;
@@ -166,7 +168,10 @@ class App
                                     const char* resourceType);
 
         // Delete the CallbackInfo and unregister from Callback object list.
-        void DeleteAndUnregisterCallbackInfo(size_t mapKey);
+        IPCAStatus DeleteAndUnregisterCallbackInfo(
+                                    size_t mapKey,
+                                    IPCACloseHandleComplete closeHandleComplete = nullptr,
+                                    const void* context = nullptr);
 
         // Thread performing periodic discovery.
         static void AppWorkerThread(App* app);
@@ -177,6 +182,8 @@ class App
 
         // Password callback registration
         InputPinCallbackHandle m_passwordInputCallbackHandle;
+        CallbackInfo::Ptr m_passwordInputCallbackInfo;
         DisplayPinCallbackHandle m_passwordDisplayCallbackHandle;
+        CallbackInfo::Ptr m_passwordDisplayCallbackInfo;
 };
 #endif // APP_H_
index ee7998d..b9350e5 100644 (file)
@@ -42,40 +42,47 @@ typedef std::shared_ptr<Device> DevicePtr;
 // Structure contains information to make callbacks to app.
 struct CallbackInfo
 {
-    public:
-        CallbackInfo() { /* std::cout << "+ CallbackInfo " << this << std::endl; */ }
-        ~CallbackInfo() { /* std::cout << "- CallbackInfo " << this << " mapKey: ";
-                             std::cout << mapKey << std::endl; */ }
-        typedef std::shared_ptr<CallbackInfo> Ptr;
-        size_t mapKey;          // key to m_callbackInfoList map.  Initialized in AddCallbackInfo().
-        App* app;               // the app that creates this callback.
-        DevicePtr device;     // the device expected to respond to the callback.
-        CallbackType type;
-        union
-        {
-            IPCADiscoverDeviceCallback discoveryCallback;
-            IPCAResourceChangeCallback resourceChangeCallback;
-            IPCAGetPropertiesComplete getCallback;
-            IPCASetPropertiesComplete setCallback;
-            IPCACreateResourceComplete createResourceCallback;
-            IPCADeleteResourceComplete deleteResourceCallback;
-            IPCAProvidePasswordCallback passwordInputCallback;
-            IPCADisplayPasswordCallback passwordDisplayCallback;
-            IPCARequestAccessCompletionCallback requestAccessCompletionCallback;
-        };
-        const void* callbackContext;    // app's callback context.
-        std::vector<std::string> resourceTypeList;  // Parameter for Discovery.
-        std::string resourcePath;       // Parameters for for Get, Set, Observe request.
-        std::string resourceInterface;
-        std::string resourceType;
-        size_t callbackInProgressCount; // Non zero if callback is in progress.
-        bool markedToBeRemoved; // Set to true when this object can't be removed in
-                                // RemoveCallbackInfo(). It'll be removed opportunistically.
-        std::vector<std::string> discoveredDevicesList; // List of device ids that were indicated to
-                                                        // app with IPCA_DEVICE_DISCOVERED.
-        std::shared_ptr<OC::OCResource> ocResource; // The OCResource this callback works with.
-
-        uint64_t requestSentTimestamp; // when the request was sent to the server.
+    CallbackInfo() { /* std::cout << "+ CallbackInfo " << this << std::endl; */ }
+    ~CallbackInfo() { /* std::cout << "- CallbackInfo " << this << " mapKey: ";
+                         std::cout << mapKey << std::endl; */ }
+    typedef std::shared_ptr<CallbackInfo> Ptr;
+    size_t mapKey;          // key to m_callbackInfoList map.  Initialized in AddCallbackInfo().
+    App* app;               // the app that creates this callback.
+    DevicePtr device;     // the device expected to respond to the callback.
+    CallbackType type;
+    union
+    {
+        IPCADiscoverDeviceCallback discoveryCallback;
+        IPCAResourceChangeCallback resourceChangeCallback;
+        IPCAGetPropertiesComplete getCallback;
+        IPCASetPropertiesComplete setCallback;
+        IPCACreateResourceComplete createResourceCallback;
+        IPCADeleteResourceComplete deleteResourceCallback;
+        IPCAProvidePasswordCallback passwordInputCallback;
+        IPCADisplayPasswordCallback passwordDisplayCallback;
+        IPCARequestAccessCompletionCallback requestAccessCompletionCallback;
+    };
+    const void* callbackContext;    // app's callback context.
+    std::vector<std::string> resourceTypeList;  // Parameter for Discovery.
+    std::string resourcePath;       // Parameters for for Get, Set, Observe request.
+    std::string resourceInterface;
+    std::string resourceType;
+
+    size_t callbackInProgressCount; // Non zero if callback is in progress.
+    bool markedToBeRemoved; // Set to true when this object can't be removed in
+                            // RemoveCallbackInfo(). It'll be removed opportunistically.
+
+    // closeHandleCompleteCallback is from argument in IPCACloseHandle().
+    // It will be set in this structure if IPCACloseHandle() is called when the callback
+    // for this CallbackInfo is in progress.
+    IPCACloseHandleComplete closeHandleCompleteCallback;
+    const void* closeHandleCompletecontext;
+
+    std::vector<std::string> discoveredDevicesList; // List of device ids that were indicated to
+                                                    // app with IPCA_DEVICE_DISCOVERED.
+    std::shared_ptr<OC::OCResource> ocResource; // The OCResource this callback works with.
+
+    uint64_t requestSentTimestamp; // when the request was sent to the server.
 };
 
 // Represent IPCAResourceChangeCallback, IPCAGetPropertiesComplete, IPCASetPropertiesComplete.
@@ -136,7 +143,10 @@ class Callback
 
         // Remove the CallbackInfo matching mapKey. This function sets markedToBeRemoved if the
         // callback is in the middle of calling back.
-        void RemoveCallbackInfo(size_t mapKey);
+        // Function returns IPCA_FAIL if callback is already closed.
+        IPCAStatus RemoveCallbackInfo(size_t mapKey,
+                        IPCACloseHandleComplete closeHandleComplete = nullptr,
+                        const void* context = nullptr);
 
         // Complete the callback for expired CallbackInfo and remove them from the
         // m_callbackInfoList. Caller receives a list of them.
@@ -189,6 +199,13 @@ class Callback
         bool MatchAllRequiredResourceTypes(const std::vector<std::string>& requiredResourceTypes,
                                            const std::vector<std::string>& deviceResourceTypes);
 
+        // Callback to app's closeHandleComplete() that is set in IPCACloseHandle().
+        void CallCloseHandleComplete(IPCACloseHandleComplete closeHandleComplete,
+                                     const void* context);
+
+        // Common initialization for new CallbackInfo object.
+        void CommonInitializeCallbackInfo(CallbackInfo::Ptr callbackInfo);
+
     private:
         // Mutex for synchronization use.
         std::mutex m_callbackMutex;
index c462092..28d3d0c 100644 (file)
@@ -290,13 +290,17 @@ IPCAStatus IPCA_CALL IPCADeleteResource(
                     handle);
 }
 
-void IPCA_CALL IPCACloseHandle(IPCAHandle handle)
+IPCAStatus IPCA_CALL IPCACloseHandle(IPCAHandle handle,
+                        IPCACloseHandleComplete closeHandleComplete,
+                        void* context)
 {
     std::lock_guard<std::mutex> lock(g_ipcaAppMutex);
     if (g_app != nullptr)
     {
-        g_app->CloseIPCAHandle(handle);
+        return (g_app->CloseIPCAHandle(handle, closeHandleComplete, context));
     }
+
+    return IPCA_FAIL;
 }
 
 typedef struct
index d77131f..2fd4337 100644 (file)
@@ -26,6 +26,7 @@
 #include "iotivity_config.h"
 #include "ocrandom.h"
 #include "octypes.h"
+#include "oic_time.h"
 #include "ipca.h"
 #include "ipcatestdata.h"
 #include "ipcaelevatorclient.h"
@@ -90,7 +91,7 @@ bool IPCAElevatorClient::GetElevatorProperties()
     m_getDataCompleteCbCV.wait_for(
         lock,
         std::chrono::seconds(10),
-        [this ] { return m_getPropertiesCallbackCalled; });
+        [this] { return m_getPropertiesCallbackCalled; });
 
     return m_getPropertiesCallbackCalled;
 }
@@ -178,7 +179,7 @@ bool IPCAElevatorClient::DeleteElevatorResource()
 {
     m_deleteResourceCallbackCalled = false;
 
-    // Set properties.
+    // Delete resource.
     EXPECT_EQ(IPCA_OK, IPCADeleteResource(
                             m_deviceHandle,
                             &C_DeleteResourceCb,
@@ -286,7 +287,7 @@ void IPCAElevatorClient::StopObserve()
 {
     if (m_observeHandle)
     {
-        IPCACloseHandle(m_observeHandle);
+        IPCACloseHandle(m_observeHandle, nullptr, 0);
     }
 }
 
@@ -382,7 +383,7 @@ void IPCAElevatorClient::TearDown()
 {
     if (m_observeHandle != nullptr)
     {
-        IPCACloseHandle(m_observeHandle);
+        IPCACloseHandle(m_observeHandle, nullptr, 0);
         m_observeHandle = nullptr;
     }
 
@@ -394,7 +395,7 @@ void IPCAElevatorClient::TearDown()
 
     if (m_deviceDiscoveryHandle != nullptr)
     {
-        IPCACloseHandle(m_deviceDiscoveryHandle);
+        IPCACloseHandle(m_deviceDiscoveryHandle, nullptr, 0);
         m_deviceDiscoveryHandle = nullptr;
     }
 
@@ -646,7 +647,8 @@ void IPCAElevatorClient::DiscoverElevator1()
                             m_discoveredElevator1DeviceId.c_str(), &m_deviceHandle));
 }
 
-void IPCAElevatorClient::SetPropertiesCallback(IPCAStatus result,
+void IPCAElevatorClient::SetPropertiesCallback(
+                                IPCAStatus result,
                                 void* context,
                                 IPCAPropertyBagHandle propertyBagHandle)
 {
@@ -658,7 +660,8 @@ void IPCAElevatorClient::SetPropertiesCallback(IPCAStatus result,
     m_setPropertiesCompleteCV.notify_all();
 }
 
-void IPCAElevatorClient::GetPropertiesCallback(IPCAStatus result,
+void IPCAElevatorClient::GetPropertiesCallback(
+                                IPCAStatus result,
                                 void* context,
                                 IPCAPropertyBagHandle propertyBagHandle)
 {
@@ -680,7 +683,8 @@ void IPCAElevatorClient::GetPropertiesCallback(IPCAStatus result,
     m_getDataCompleteCbCV.notify_all();
 }
 
-void IPCAElevatorClient::CreateResourceCallback(IPCAStatus result,
+void IPCAElevatorClient::CreateResourceCallback(
+                                IPCAStatus result,
                                 const char* newResourcePath,
                                 IPCAPropertyBagHandle propertyBagHandle)
 {
@@ -715,3 +719,375 @@ void IPCAElevatorClient::ResourceChangeNotificationCallback(
 
     m_resourceChangeCbCV.notify_all();
 }
+
+// This data structure is used to coordinate between C_ControlledRequestCompleteCallback() and
+// RunCloseHandleTest().
+typedef struct
+{
+    bool isInCallback;  // Set to true when callback for request is called.
+    bool isTimeToCompleteCallback;  // Set to true when callback should complete.
+    uint64_t completeCallbackTimestamp;  // Timestamp when isTimeToCompleteCallback is set.
+    uint64_t closeHandleCompleteTimestamp;  // When IPCACloseHandleComplete() is called.
+} ContextForCloseHandleTest;
+
+// This callback coordinates with RunCloseHandleTest() on when to complete the callback.
+void IPCA_CALL C_ControlledRequestCompleteCallback(
+                            IPCAStatus result,
+                            void* context,
+                            IPCAPropertyBagHandle propertyBagHandle)
+{
+    OC_UNUSED(propertyBagHandle);
+
+    ContextForCloseHandleTest* testContext = reinterpret_cast<ContextForCloseHandleTest*>(context);
+    testContext->isInCallback = true;
+
+    if (result != IPCA_OK)
+    {
+        std::cout << "C_ControlledRequestCompleteCallback(): unsuccessful. result = " << result;
+        std::cout << std::endl;
+        return;
+    }
+
+    // Hang on here until the RunCloseHandleTest() indicates that it's time to complete
+    // this callback.
+    while (testContext->isTimeToCompleteCallback == false)
+    {
+        std::this_thread::sleep_for(std::chrono::milliseconds(100));
+    }
+}
+
+void IPCA_CALL C_ControlledCreateResourceCallback(
+                        IPCAStatus result,
+                        void* context,
+                        const char* newResourcePath,
+                        IPCAPropertyBagHandle propertyBagHandle)
+{
+    OC_UNUSED(newResourcePath);
+    C_ControlledRequestCompleteCallback(result, context, propertyBagHandle);
+}
+
+void IPCA_CALL C_ControlledDeleteResourceCallback(IPCAStatus result, void* context)
+{
+    C_ControlledRequestCompleteCallback(result, context, NULL);
+}
+
+void IPCA_CALL C_ControlledDiscoverCallback(
+                            void* context,
+                            IPCADeviceStatus deviceStatus,
+                            const IPCADiscoveredDeviceInfo* discoveredDeviceInfo)
+{
+    OC_UNUSED(deviceStatus);
+    OC_UNUSED(discoveredDeviceInfo);
+    C_ControlledRequestCompleteCallback(IPCA_OK, context, NULL);
+}
+
+void IPCA_CALL C_CloseHandleCallback(void* context)
+{
+    ContextForCloseHandleTest* testContext = reinterpret_cast<ContextForCloseHandleTest*>(context);
+    testContext->closeHandleCompleteTimestamp = OICGetCurrentTime(TIME_IN_MS);
+}
+
+// This function coordinates with C_ControlledRequestCompleteCallback() to test behavior
+// of IPCACloseHandle() when callback is in progress.
+void IPCAElevatorClient::RunCloseHandleTest(IPCAHandle ipcaHandle, void* context)
+{
+    ContextForCloseHandleTest* testContext = reinterpret_cast<ContextForCloseHandleTest*>(context);
+
+    // Wait for the callback. If the server did not respond, rely on IPCA to time out the pending
+    // request.
+    while (testContext->isInCallback == false)
+    {
+        std::this_thread::sleep_for(std::chrono::milliseconds(100));
+    }
+
+    // Close the handle.  At this time C_ControlledRequestCompleteCallback() is looping
+    // waiting for isTimeToCompleteCallback to turn to true.
+    if (IPCA_OK != IPCACloseHandle(
+                            ipcaHandle,
+                            &C_CloseHandleCallback,
+                            static_cast<void*>(testContext)))
+    {
+        std::cout << "RunCloseHandleTest(): IPCACloseHandle() failed. Request may have timed out.";
+        std::cout << std::endl;
+        return;
+    }
+
+    // Another call to IPCAClose() on closing handle should not succeed.
+    ASSERT_NE(IPCA_OK, IPCACloseHandle(
+                            ipcaHandle,
+                            &C_CloseHandleCallback,
+                            static_cast<void*>(testContext)));
+
+    // Sleep an extra 100 milliseconds to make sure C_CloseHandleCallback is not called.
+    std::this_thread::sleep_for(std::chrono::milliseconds(100));
+    testContext->completeCallbackTimestamp = OICGetCurrentTime(TIME_IN_MS);
+    testContext->isTimeToCompleteCallback = true;
+
+    // Now wait for C_CloseHandleCallback() to complete.
+    while (testContext->closeHandleCompleteTimestamp == 0)
+    {
+        std::this_thread::sleep_for(std::chrono::milliseconds(100));
+    }
+
+    // The closeHandleCompleteTimestamp value must be equal to or greater than when
+    // isTimeToCompleteCallback is set. It could be equal if C_ControlledRequestCompleteCallback()
+    // processes the isTimeToCompleteCallbackas as soon as it's set.
+    ASSERT_TRUE(testContext->closeHandleCompleteTimestamp >=
+                testContext->completeCallbackTimestamp);
+}
+
+IPCAStatus IPCAElevatorClient::TestCloseHandleForGet()
+{
+    IPCAHandle getHandle;
+    ContextForCloseHandleTest testContext;
+    memset(&testContext, 0, sizeof(ContextForCloseHandleTest));
+
+    // Call Get api.
+    IPCAStatus status = IPCAGetProperties(
+                                m_deviceHandle,
+                                &C_ControlledRequestCompleteCallback,
+                                reinterpret_cast<void*>(&testContext),
+                                ELEVATOR_RESOURCE_PATH,
+                                nullptr,
+                                nullptr,
+                                &getHandle);
+
+    if (status != IPCA_OK)
+    {
+        return status;
+    }
+
+    RunCloseHandleTest(getHandle, &testContext);
+    return IPCA_OK;
+}
+
+IPCAStatus IPCAElevatorClient::TestCloseHandleForSet()
+{
+    IPCAStatus status;
+    IPCAHandle setHandle;
+    ContextForCloseHandleTest testContext;
+    memset(&testContext, 0, sizeof(ContextForCloseHandleTest));
+
+    IPCAPropertyBagHandle propertyBagHandle;
+    status = IPCAPropertyBagCreate(&propertyBagHandle);
+    if (status != IPCA_OK)
+    {
+        return status;
+    }
+
+    status = IPCAPropertyBagSetValueInt(
+                    propertyBagHandle,
+                    ELEVATOR_PROPERTY_TARGET_FLOOR,
+                    3 /* target floor, value is random */);
+
+    if (status != IPCA_OK)
+    {
+        return status;
+    }
+
+    // Set properties.
+    status = IPCASetProperties(
+                    m_deviceHandle,
+                    &C_ControlledRequestCompleteCallback,
+                    reinterpret_cast<void*>(&testContext),
+                    (const char*)ELEVATOR_RESOURCE_PATH,
+                    nullptr,
+                    (const char*)ELEVATOR_RESOURCE_TYPE,
+                    propertyBagHandle,
+                    &setHandle);
+
+    if (status != IPCA_OK)
+    {
+        return status;
+    }
+
+    RunCloseHandleTest(setHandle, &testContext);
+    return IPCA_OK;
+}
+
+IPCAStatus IPCAElevatorClient::TestCloseHandleForObserve()
+{
+    IPCAHandle observeHandle;
+    ContextForCloseHandleTest testContext;
+    memset(&testContext, 0, sizeof(ContextForCloseHandleTest));
+
+    // Call Get api.
+    IPCAStatus status = IPCAObserveResource(
+                                   m_deviceHandle,
+                                   &C_ControlledRequestCompleteCallback,
+                                   reinterpret_cast<void*>(&testContext),
+                                   ELEVATOR_RESOURCE_PATH,
+                                   nullptr,
+                                   &observeHandle);
+    if (status != IPCA_OK)
+    {
+        return status;
+    }
+
+    // Set target floor so the server sends notifications.
+    bool result;
+    SetTargetFloor(10, &result);    // Set to floor 10
+    if (result != true)
+    {
+        return IPCA_FAIL;
+    }
+
+    RunCloseHandleTest(observeHandle, &testContext);
+    return IPCA_OK;
+}
+
+IPCAStatus IPCAElevatorClient::TestCloseHandleForCreate()
+{
+    IPCAStatus status;
+    IPCAHandle createHandle;
+    ContextForCloseHandleTest testContext;
+    memset(&testContext, 0, sizeof(ContextForCloseHandleTest));
+
+    IPCAPropertyBagHandle propertyBagHandle;
+    status = IPCAPropertyBagCreate(&propertyBagHandle);
+    if (status != IPCA_OK)
+    {
+        return status;
+    }
+
+    // Call Create api.
+    status = IPCACreateResource(
+                        m_deviceHandle,
+                        &C_ControlledCreateResourceCallback,
+                        reinterpret_cast<void*>(&testContext),
+                        ELEVATOR_RESOURCE_CREATE_RELATIVE_PATH,
+                        nullptr,
+                        nullptr,
+                        propertyBagHandle,
+                        &createHandle);
+
+    if (status != IPCA_OK)
+    {
+        return status;
+    }
+
+    RunCloseHandleTest(createHandle, &testContext);
+    return IPCA_OK;
+}
+
+
+IPCAStatus IPCAElevatorClient::TestCloseHandleForDelete()
+{
+    IPCAHandle deleteHandle;
+    ContextForCloseHandleTest testContext;
+    memset(&testContext, 0, sizeof(ContextForCloseHandleTest));
+
+    // Call Delete api.
+    IPCAStatus status = IPCADeleteResource(
+                            m_deviceHandle,
+                            &C_ControlledDeleteResourceCallback,
+                            reinterpret_cast<void*>(&testContext),
+                            (const char*)ELEVATOR_RESOURCE_DELETE_PATH,
+                            &deleteHandle);
+
+    if (status != IPCA_OK)
+    {
+        return status;
+    }
+
+    RunCloseHandleTest(deleteHandle, &testContext);
+    return IPCA_OK;
+}
+
+IPCAStatus IPCAElevatorClient::TestCloseHandleForDiscover()
+{
+    IPCAHandle discoverDeviceHandle;
+    ContextForCloseHandleTest testContext;
+    memset(&testContext, 0, sizeof(ContextForCloseHandleTest));
+
+    const char* RequiredResourceTypes[] = {
+        ELEVATOR_RESOURCE_TYPE,
+        ELEVATOR_CO_RESOURCE_TYPE,
+        ELEVATOR_RESOURCE_CREATE_RELATIVE_PATH_TYPE,
+        ELEVATOR_RESOURCE_DELETE_TYPE
+    };
+
+    const int ResourceTypeCount = sizeof(RequiredResourceTypes) / sizeof(char*);
+
+   // Start discovery.
+   IPCAStatus status = IPCADiscoverDevices(
+                               m_ipcaAppHandle,
+                               &C_ControlledDiscoverCallback,
+                               reinterpret_cast<void*>(&testContext),
+                               RequiredResourceTypes,
+                               ResourceTypeCount,
+                               &discoverDeviceHandle);
+
+    if (status != IPCA_OK)
+    {
+        return status;
+    }
+
+    RunCloseHandleTest(discoverDeviceHandle, &testContext);
+    return IPCA_OK;
+}
+
+// Callback from IPCA for IPCAGetProperties() call in TestMultipleCallsToCloseSameHandle().
+void IPCA_CALL C_RequestComplete(
+                    IPCAStatus result,
+                    void* context,
+                    IPCAPropertyBagHandle propertyBagHandle)
+{
+    OC_UNUSED(result);
+    OC_UNUSED(context);
+    OC_UNUSED(propertyBagHandle);
+}
+
+// IPCACloseHandleComplete() in TestMultipleCallsToCloseSameHandle().
+void IPCA_CALL C_CloseHandleComplete(void* context)
+{
+    size_t* testContext = reinterpret_cast<size_t*>(context);
+    (*testContext)++;
+}
+
+// Wait for *value to reach target or timeout. Return *value.
+size_t C_WaitNumber(size_t* value, size_t target)
+{
+    const uint64_t MAX_WAIT_TIME_MS = 100;
+    uint64_t beginTime = OICGetCurrentTime(TIME_IN_MS);
+    while ((OICGetCurrentTime(TIME_IN_MS) - beginTime < MAX_WAIT_TIME_MS) && (*value != target))
+    {
+        std::this_thread::sleep_for(std::chrono::milliseconds(100));
+    }
+
+    return *value;
+}
+
+// Observe the right behavior of IPCACloseHandle() when handle is already closed.
+IPCAStatus IPCAElevatorClient::TestMultipleCallsToCloseSameHandle()
+{
+    IPCAHandle getHandle;
+    IPCAStatus status = IPCAGetProperties(
+                                m_deviceHandle,
+                                &C_RequestComplete,
+                                NULL,
+                                ELEVATOR_RESOURCE_PATH,
+                                nullptr,
+                                nullptr,
+                                &getHandle);
+    if (status != IPCA_OK)
+    {
+        return status;
+    }
+
+    size_t count = 0;
+
+    // First IPCACloseHandle() should be succesful.
+    EXPECT_EQ(IPCA_OK, IPCACloseHandle(getHandle, &C_CloseHandleComplete, static_cast<void*>(&count)));
+    EXPECT_EQ(1, C_WaitNumber(&count, 1));
+
+    // Subsequent IPCACloseHandle() on the same handle is expected to fail.
+    // And the C_CloseHandleMultiple() is not called, i.e. count should not increase.
+    EXPECT_EQ(IPCA_FAIL, IPCACloseHandle(getHandle, &C_CloseHandleComplete, static_cast<void*>(&count)));
+    EXPECT_EQ(1, C_WaitNumber(&count, 2));
+
+    EXPECT_EQ(IPCA_FAIL, IPCACloseHandle(getHandle, &C_CloseHandleComplete, static_cast<void*>(&count)));
+    EXPECT_EQ(1, C_WaitNumber(&count, 3));
+
+    return IPCA_OK;
+}
index 1c771be..dc3a55d 100644 (file)
@@ -85,6 +85,15 @@ public:
         IPCAStatus result,
         IPCAPropertyBagHandle propertyBagHandle);
 
+    // IPCACloseHandle() tests.
+    IPCAStatus TestCloseHandleForGet();
+    IPCAStatus TestCloseHandleForSet();
+    IPCAStatus TestCloseHandleForObserve();
+    IPCAStatus TestCloseHandleForCreate();
+    IPCAStatus TestCloseHandleForDelete();
+    IPCAStatus TestCloseHandleForDiscover();
+    IPCAStatus TestMultipleCallsToCloseSameHandle();
+
     // Failure cases.
     IPCAStatus GetUnknownResource();
     IPCAStatus SetUnknownResource();
@@ -140,6 +149,9 @@ private:
             std::string resourcePath, IPCAPropertyBagHandle propertyBagHandle);
     bool DeleteElevatorResource();
 
+    // Test timing of IPCACloseHandle() for the ipcaHandle.
+    void RunCloseHandleTest(IPCAHandle ipcaHandle, void* testContext);
+
 protected:
     virtual void SetUp();
     virtual void TearDown();
index 4f9fde3..647efd4 100644 (file)
@@ -607,6 +607,40 @@ TEST_F(IPCAElevatorClient, SuccessfulReboot)
     EXPECT_EQ(IPCA_OK, RebootElevator());
 }
 
+TEST_F(IPCAElevatorClient, TestCloseHandleTimingForGet)
+{
+    EXPECT_EQ(IPCA_OK, TestCloseHandleForGet());
+}
+
+TEST_F(IPCAElevatorClient, TestCloseHandleTimingForSet)
+{
+    EXPECT_EQ(IPCA_OK, TestCloseHandleForSet());
+}
+
+TEST_F(IPCAElevatorClient, TestCloseHandleTimingForCreate)
+{
+    EXPECT_EQ(IPCA_OK, TestCloseHandleForCreate());
+}
+
+TEST_F(IPCAElevatorClient, TestCloseHandleTimingForDelete)
+{
+    EXPECT_EQ(IPCA_OK, TestCloseHandleForDelete());
+}
+
+TEST_F(IPCAElevatorClient, TestCloseHandleTimingForObserve)
+{
+    EXPECT_EQ(IPCA_OK, TestCloseHandleForObserve());
+}
+
+TEST_F(IPCAElevatorClient, TestCloseHandleTimingForDiscover)
+{
+    EXPECT_EQ(IPCA_OK, TestCloseHandleForDiscover());
+}
+
+TEST_F(IPCAElevatorClient, TestMultipleCallsToCloseHandle)
+{
+    EXPECT_EQ(IPCA_OK, TestMultipleCallsToCloseSameHandle());
+}
 
 TEST(ElevatorServerStop, Stop)
 {
index e94a6da..a286937 100644 (file)
@@ -445,10 +445,15 @@ OCStackResult OCPlatform::sendResponse(const std::shared_ptr<OCResourceResponse>
     {\r
         case OC_REST_POST:\r
         {\r
+            OCStackResult ocResult = OC_STACK_ERROR;\r
+            if ((result == OC_EH_OK) || (result == OC_EH_RESOURCE_CREATED))\r
+            {\r
+                ocResult = OC_STACK_OK;\r
+            }\r
             std::thread postCallbackThread(pendingRequest->postCallback,\r
                             serverHeaderOptions,\r
                             ocRep,\r
-                            (result == OC_EH_OK) ? OC_STACK_OK : OC_STACK_ERROR);\r
+                            ocResult);\r
             postCallbackThread.detach();\r
             break;\r
         }\r
@@ -465,9 +470,15 @@ OCStackResult OCPlatform::sendResponse(const std::shared_ptr<OCResourceResponse>
 \r
         case OC_REST_DELETE:\r
         {\r
+            OCStackResult ocResult = OC_STACK_ERROR;\r
+            if ((result == OC_EH_OK) || (result == OC_EH_RESOURCE_DELETED))\r
+            {\r
+                ocResult = OC_STACK_OK;\r
+            }\r
+\r
             std::thread deleteCallbackThread(pendingRequest->deleteCallback,\r
                             serverHeaderOptions,\r
-                            (result == OC_EH_OK) ? OC_STACK_OK : OC_STACK_ERROR);\r
+                            ocResult);\r
             deleteCallbackThread.detach();\r
             break;\r
         }\r
index a6eac74..e762060 100644 (file)
@@ -104,7 +104,6 @@ OCEntityHandlerResult ElevatorServer::ElevatorEntityHandler(
                             std::shared_ptr<OCResourceRequest> request)
 {
     OCEntityHandlerResult ehResult = OC_EH_ERROR;
-
     if (request->getResourceUri() == ELEVATOR_CO_RESOURCE_PATH)
     {
         return OC_EH_FORBIDDEN;