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
* 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.
{
if (g_observeHandle != nullptr)
{
- IPCACloseHandle(g_observeHandle);
+ IPCACloseHandle(g_observeHandle, nullptr, 0);
g_observeHandle = nullptr;
}
g_deviceDiscoveredCV.wait_for(lock, std::chrono::milliseconds{ timeOutMs });
// Stop discovery.
- IPCACloseHandle(g_discoverDeviceHandle);
+ IPCACloseHandle(g_discoverDeviceHandle, nullptr, 0);
}
return g_targetElevatorDiscovered;
g_OCFDeviceList.clear();
- IPCACloseHandle(discoverDeviceHandle);
+ IPCACloseHandle(discoverDeviceHandle, nullptr, 0);
IPCAClose(g_ipcaAppHandle);
}
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;
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)
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;
}
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);
}
}
- DeleteAndUnregisterCallbackInfo(mapKey);
+ return DeleteAndUnregisterCallbackInfo(mapKey, closeHandleComplete, context);
}
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);
}
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)
{
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(
return nullptr;
}
- cbInfo->app = m_app;
+ CommonInitializeCallbackInfo(cbInfo);
cbInfo->type = cbType;
cbInfo->callbackContext = context;
- cbInfo->callbackInProgressCount = 0;
- cbInfo->markedToBeRemoved = false;
switch (cbType)
{
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)
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 : "");
}
}
+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);
// 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)
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);
const_cast<void*>(cbInfo->callbackContext));
ClearCallbackInProgress(cbInfo->mapKey);
+ RemoveCallbackInfo(cbInfo->mapKey);
}
void Callback::PasswordInputCallback(std::string deviceId,
}
ClearCallbackInProgress(cbInfo->mapKey);
+ RemoveCallbackInfo(cbInfo->mapKey);
}
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;
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);
// Password callback registration
InputPinCallbackHandle m_passwordInputCallbackHandle;
+ CallbackInfo::Ptr m_passwordInputCallbackInfo;
DisplayPinCallbackHandle m_passwordDisplayCallbackHandle;
+ CallbackInfo::Ptr m_passwordDisplayCallbackInfo;
};
#endif // APP_H_
// 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.
// 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.
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;
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
#include "iotivity_config.h"
#include "ocrandom.h"
#include "octypes.h"
+#include "oic_time.h"
#include "ipca.h"
#include "ipcatestdata.h"
#include "ipcaelevatorclient.h"
m_getDataCompleteCbCV.wait_for(
lock,
std::chrono::seconds(10),
- [this ] { return m_getPropertiesCallbackCalled; });
+ [this] { return m_getPropertiesCallbackCalled; });
return m_getPropertiesCallbackCalled;
}
{
m_deleteResourceCallbackCalled = false;
- // Set properties.
+ // Delete resource.
EXPECT_EQ(IPCA_OK, IPCADeleteResource(
m_deviceHandle,
&C_DeleteResourceCb,
{
if (m_observeHandle)
{
- IPCACloseHandle(m_observeHandle);
+ IPCACloseHandle(m_observeHandle, nullptr, 0);
}
}
{
if (m_observeHandle != nullptr)
{
- IPCACloseHandle(m_observeHandle);
+ IPCACloseHandle(m_observeHandle, nullptr, 0);
m_observeHandle = nullptr;
}
if (m_deviceDiscoveryHandle != nullptr)
{
- IPCACloseHandle(m_deviceDiscoveryHandle);
+ IPCACloseHandle(m_deviceDiscoveryHandle, nullptr, 0);
m_deviceDiscoveryHandle = nullptr;
}
m_discoveredElevator1DeviceId.c_str(), &m_deviceHandle));
}
-void IPCAElevatorClient::SetPropertiesCallback(IPCAStatus result,
+void IPCAElevatorClient::SetPropertiesCallback(
+ IPCAStatus result,
void* context,
IPCAPropertyBagHandle propertyBagHandle)
{
m_setPropertiesCompleteCV.notify_all();
}
-void IPCAElevatorClient::GetPropertiesCallback(IPCAStatus result,
+void IPCAElevatorClient::GetPropertiesCallback(
+ IPCAStatus result,
void* context,
IPCAPropertyBagHandle propertyBagHandle)
{
m_getDataCompleteCbCV.notify_all();
}
-void IPCAElevatorClient::CreateResourceCallback(IPCAStatus result,
+void IPCAElevatorClient::CreateResourceCallback(
+ IPCAStatus result,
const char* newResourcePath,
IPCAPropertyBagHandle propertyBagHandle)
{
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;
+}
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();
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();
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)
{
{\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
\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
std::shared_ptr<OCResourceRequest> request)
{
OCEntityHandlerResult ehResult = OC_EH_ERROR;
-
if (request->getResourceUri() == ELEVATOR_CO_RESOURCE_PATH)
{
return OC_EH_FORBIDDEN;