os.path.join(Dir('.').abspath, 'oic_malloc', 'include'),
os.path.join(Dir('.').abspath, 'oic_string', 'include'),
os.path.join(Dir('.').abspath, 'oic_time', 'include'),
+ os.path.join(Dir('.').abspath, 'ocatomic', 'include'),
os.path.join(Dir('.').abspath, 'ocrandom', 'include'),
os.path.join(Dir('.').abspath, 'octhread', 'include')
])
else:
common_src.append('octhread/src/noop/octhread.c')
+if target_os in ['windows', 'msys_nt']:
+ common_src.append('ocatomic/src/windows/ocatomic.c')
+elif target_os in ['arduino']:
+ common_src.append('ocatomic/src/arduino/ocatomic.c')
+else:
+ common_src.append('ocatomic/src/others/ocatomic.c')
+
common_env.AppendUnique(LIBS = ['logger'])
common_env.AppendUnique(CPPPATH = ['#resource/csdk/logger/include'])
commonlib = common_env.StaticLibrary('c_common', common_src)
common_env.InstallTarget(commonlib, 'c_common')
common_env.UserInstallTargetLib(commonlib, 'c_common')
+common_env.UserInstallTargetHeader('iotivity_debug.h', 'c_common', 'iotivity_debug.h')
common_env.UserInstallTargetHeader('platform_features.h', 'c_common', 'platform_features.h')
env.PrependUnique(LIBS = ['c_common'])
--- /dev/null
+/* *****************************************************************\r
+ *\r
+ * Copyright 2017 Microsoft\r
+ *\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ *\r
+ ******************************************************************/\r
+#ifndef OC_ATOMIC_H\r
+#define OC_ATOMIC_H\r
+\r
+#include <stdint.h>\r
+\r
+#ifdef __cplusplus\r
+extern "C"\r
+{\r
+#endif /* __cplusplus */\r
+\r
+/**\r
+ * Increments (increases by one) the value of the specified int32_t variable atomically.\r
+ *\r
+ * @param[in] addend Pointer to the variable to be incremented.\r
+ * @return int32_t The resulting incremented value.\r
+ */\r
+int32_t oc_atomic_increment(volatile int32_t *addend);\r
+\r
+/**\r
+ * Decrements (decreases by one) the value of the specified int32_t variable atomically.\r
+ *\r
+ * @param[in] addend Pointer to the variable to be decremented.\r
+ * @return int32_t The resulting decremented value.\r
+ */\r
+int32_t oc_atomic_decrement(volatile int32_t *addend);\r
+\r
+#ifdef __cplusplus\r
+} /* extern "C" */\r
+#endif /* __cplusplus */\r
+\r
+#endif /* OC_ATOMIC_H */\r
--- /dev/null
+/* *****************************************************************\r
+ *\r
+ * Copyright 2017 Microsoft\r
+ *\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ *\r
+ ******************************************************************/\r
+\r
+/**\r
+ * @file\r
+ * This file implements stubs for atomic functions. These stubs are not designed\r
+ * to be used by multi-threaded OS.\r
+ */\r
+\r
+#include "ocatomic.h"\r
+\r
+int32_t oc_atomic_increment(volatile int32_t *addend)\r
+{\r
+ (*addend)++;\r
+ return *addend;\r
+}\r
+\r
+int32_t oc_atomic_decrement(volatile int32_t *addend)\r
+{\r
+ (*addend)--;\r
+ return *addend;\r
+}\r
--- /dev/null
+/* *****************************************************************\r
+ *\r
+ * Copyright 2017 Microsoft\r
+ *\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ *\r
+ ******************************************************************/\r
+\r
+/**\r
+ * @file\r
+ * This file implements APIs related to atomic operations compatible with GCC compilers.\r
+ */\r
+\r
+#include "ocatomic.h"\r
+\r
+int32_t oc_atomic_increment(volatile int32_t *addend)\r
+{\r
+ return __sync_add_and_fetch(addend, 1);\r
+}\r
+\r
+int32_t oc_atomic_decrement(volatile int32_t *addend)\r
+{\r
+ return __sync_sub_and_fetch(addend, 1);\r
+}\r
--- /dev/null
+/* *****************************************************************\r
+ *\r
+ * Copyright 2017 Microsoft\r
+ *\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ *\r
+ ******************************************************************/\r
+\r
+/**\r
+ * @file\r
+ * This file implements APIs related to atomic operations for Windows.\r
+ */\r
+\r
+#include "ocatomic.h"\r
+#include <windows.h>\r
+\r
+int32_t oc_atomic_increment(volatile int32_t *addend)\r
+{\r
+ return InterlockedIncrement((volatile long*)addend);\r
+}\r
+\r
+int32_t oc_atomic_decrement(volatile int32_t *addend)\r
+{\r
+ return InterlockedDecrement((volatile long*)addend);\r
+}\r
liboctbstack_env.PrependUnique(CPPPATH = [
'#/extlibs/timer/',
+ '#resource/c_common/ocatomic/include',
'#resource/csdk/logger/include',
'#resource/csdk/include',
'include',
#define __STDC_LIMIT_MACROS
#endif
#include "iotivity_config.h"
+#include "iotivity_debug.h"
#include <stdlib.h>
#include <inttypes.h>
#include <string.h>
#include <ctype.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
#include "ocstack.h"
#include "ocstackinternal.h"
#include "cainterface.h"
#include "oicgroup.h"
#include "ocendpoint.h"
+#include "ocatomic.h"
+#include "platform_features.h"
#if defined(TCP_ADAPTER) && defined(WITH_CLOUD)
#include "occonnectionmanager.h"
typedef enum
{
OC_STACK_UNINITIALIZED = 0,
- OC_STACK_INITIALIZED,
- OC_STACK_UNINIT_IN_PROGRESS
+ OC_STACK_INITIALIZED
} OCStackState;
#ifdef WITH_PRESENCE
CAConnectionStateChangedCB g_connectionHandler = NULL;
// Persistent Storage callback handler for open/read/write/close/unlink
static OCPersistentStorage *g_PersistentStorageHandler = NULL;
+// Number of users of OCStack, based on the successful calls to OCInit2 prior to OCStop
+// The variable must not be declared static because it is also referenced by the unit test
+uint32_t g_ocStackStartCount = 0;
+// Number of threads currently executing OCInit2 or OCStop
+volatile int32_t g_ocStackStartStopThreadCount = 0;
//-----------------------------------------------------------------------------
// Macros
static OCStackResult OCMapZoneIdToLinkLocalEndpoint(OCDiscoveryPayload *payload, uint32_t ifindex);
#endif
+/**
+ * Initialize the stack.
+ * Caller of this function must serialize calls to this function and the stop counterpart.
+ * @param mode Mode of operation.
+ * @param serverFlags The server flag used when the mode of operation is a server mode.
+ * @param clientFlags The client flag used when the mode of operation is a client mode.
+ * @param transportType The transport type.
+ *
+ * @return ::OC_STACK_OK on success, some other value upon failure.
+ */
+static OCStackResult OCInitializeInternal(OCMode mode, OCTransportFlags serverFlags,
+ OCTransportFlags clientFlags, OCTransportAdapter transportType);
+
+/**
+ * DeInitialize the stack.
+ * Caller of this function must serialize calls to this function and the init counterpart.
+ *
+ * @return ::OC_STACK_OK on success, some other value upon failure.
+ */
+static OCStackResult OCDeInitializeInternal();
+
//-----------------------------------------------------------------------------
// Internal functions
//-----------------------------------------------------------------------------
+static void OCEnterInitializer()
+{
+ for (;;)
+ {
+ int32_t initCount = oc_atomic_increment(&g_ocStackStartStopThreadCount);
+ assert(initCount > 0);
+ if (initCount == 1)
+ {
+ break;
+ }
+ OC_VERIFY(oc_atomic_decrement(&g_ocStackStartStopThreadCount) >= 0);
+#if !defined(ARDUINO)
+ // Yield execution to the thread that is holding the lock.
+ sleep(0);
+#else // ARDUINO
+ assert(!"Not expecting initCount to go above 1 on Arduino");
+ break;
+#endif // ARDUINO
+ }
+}
+
+static void OCLeaveInitializer()
+{
+ OC_VERIFY(oc_atomic_decrement(&g_ocStackStartStopThreadCount) >= 0);
+}
bool checkProxyUri(OCHeaderOption *options, uint8_t numOptions)
{
return OCInit2(mode, OC_DEFAULT_FLAGS, OC_DEFAULT_FLAGS, OC_DEFAULT_ADAPTER);
}
-OCStackResult OCInit2(OCMode mode, OCTransportFlags serverFlags, OCTransportFlags clientFlags,
- OCTransportAdapter transportType)
+OCStackResult OCInit2(OCMode mode, OCTransportFlags serverFlags,
+ OCTransportFlags clientFlags, OCTransportAdapter transportType)
+{
+ OIC_LOG(INFO, TAG, "Entering OCInit2");
+
+ // Serialize calls to start and stop the stack.
+ OCEnterInitializer();
+
+ OCStackResult result = OC_STACK_OK;
+
+ if (g_ocStackStartCount == 0)
+ {
+ // This is the first call to initialize the stack so it gets to do the real work.
+ result = OCInitializeInternal(mode, serverFlags, clientFlags, transportType);
+ }
+
+ if (result == OC_STACK_OK)
+ {
+ // Increment the start count since we're about to return success.
+ assert(g_ocStackStartCount != UINT_MAX);
+ assert(stackState == OC_STACK_INITIALIZED);
+ g_ocStackStartCount++;
+ }
+
+ OCLeaveInitializer();
+ return result;
+}
+
+OCStackResult OCInitializeInternal(OCMode mode, OCTransportFlags serverFlags,
+ OCTransportFlags clientFlags, OCTransportAdapter transportType)
{
- if(stackState == OC_STACK_INITIALIZED)
+ if (stackState == OC_STACK_INITIALIZED)
{
OIC_LOG(INFO, TAG, "Subsequent calls to OCInit() without calling \
OCStop() between them are ignored.");
#endif
OCStackResult result = OC_STACK_ERROR;
- OIC_LOG(INFO, TAG, "Entering OCInit");
// Validate mode
if (!((mode == OC_CLIENT) || (mode == OC_SERVER) || (mode == OC_CLIENT_SERVER)
{
OIC_LOG(INFO, TAG, "Entering OCStop");
- if (stackState == OC_STACK_UNINIT_IN_PROGRESS)
+ // Serialize calls to start and stop the stack.
+ OCEnterInitializer();
+
+ OCStackResult result = OC_STACK_OK;
+
+ if (g_ocStackStartCount == 1)
{
- OIC_LOG(DEBUG, TAG, "Stack already stopping, exiting");
- return OC_STACK_OK;
+ // This is the last call to stop the stack, do the real work.
+ result = OCDeInitializeInternal();
}
- else if (stackState != OC_STACK_INITIALIZED)
+ else if (g_ocStackStartCount == 0)
{
- OIC_LOG(INFO, TAG, "Stack not initialized");
- return OC_STACK_ERROR;
+ OIC_LOG(ERROR, TAG, "Too many calls to OCStop");
+ assert(!"Too many calls to OCStop");
+ result = OC_STACK_ERROR;
}
- // unset cautil config
- CAUtilConfig_t configs = {(CATransportBTFlags_t)CA_DEFAULT_BT_FLAGS};
- CAUtilSetBTConfigure(configs);
+ if (result == OC_STACK_OK)
+ {
+ g_ocStackStartCount--;
+ }
- stackState = OC_STACK_UNINIT_IN_PROGRESS;
+ OCLeaveInitializer();
+ return result;
+}
+
+OCStackResult OCDeInitializeInternal()
+{
+ assert(stackState == OC_STACK_INITIALIZED);
#ifdef WITH_PRESENCE
// Ensure that the TTL associated with ANY and ALL presence notifications originating from
OCCMTerminate();
#endif
+ // Unset cautil config
+ CAUtilConfig_t configs = {(CATransportBTFlags_t)CA_DEFAULT_BT_FLAGS};
+ CAUtilSetBTConfigure(configs);
+
stackState = OC_STACK_UNINITIALIZED;
return OC_STACK_OK;
}
#endif
}
+extern "C" uint32_t g_ocStackStartCount;
+
class OCDiscoverTests : public testing::Test
{
protected:
{
itst::DeadmanTimer killSwitch(SHORT_TEST_TIMEOUT);
EXPECT_EQ(OC_STACK_OK, OCInit(0, 5683, OC_SERVER));
+ EXPECT_EQ(1, g_ocStackStartCount);
EXPECT_EQ(OC_STACK_OK, OCStop());
+ EXPECT_EQ(0, g_ocStackStartCount);
}
TEST(StackInit, StackInitNullPort)
{
itst::DeadmanTimer killSwitch(SHORT_TEST_TIMEOUT);
EXPECT_EQ(OC_STACK_ERROR, OCInit(0, 0, (OCMode)10));
- EXPECT_EQ(OC_STACK_ERROR, OCStop());
+ EXPECT_EQ(0, g_ocStackStartCount);
}
TEST(StackStart, StackStartSuccessClient)
TEST(StackStart, StackStartSuccessiveInits)
{
itst::DeadmanTimer killSwitch(SHORT_TEST_TIMEOUT);
+ EXPECT_EQ(0, g_ocStackStartCount);
EXPECT_EQ(OC_STACK_OK, OCInit("127.0.0.1", 5683, OC_SERVER));
+ EXPECT_EQ(1, g_ocStackStartCount);
EXPECT_EQ(OC_STACK_OK, OCInit("127.0.0.2", 5683, OC_SERVER));
+ EXPECT_EQ(2, g_ocStackStartCount);
+ EXPECT_EQ(OC_STACK_OK, OCStop());
+ EXPECT_EQ(1, g_ocStackStartCount);
EXPECT_EQ(OC_STACK_OK, OCStop());
+ EXPECT_EQ(0, g_ocStackStartCount);
}
TEST(StackStart, SetPlatformInfoValid)
EXPECT_EQ(OC_STACK_OK, OCStop());
}
-TEST(StackStop, StackStopWithoutInit)
-{
- itst::DeadmanTimer killSwitch(SHORT_TEST_TIMEOUT);
- EXPECT_EQ(OC_STACK_ERROR, OCStop());
-}
-
-TEST(StackStop, StackStopRepeated)
-{
- itst::DeadmanTimer killSwitch(SHORT_TEST_TIMEOUT);
- EXPECT_EQ(OC_STACK_OK, OCInit("127.0.0.1", 5683, OC_CLIENT));
- EXPECT_EQ(OC_STACK_OK, OCStop());
- EXPECT_EQ(OC_STACK_ERROR, OCStop());
-}
-
TEST(StackResource, DISABLED_UpdateResourceNullURI)
{
itst::DeadmanTimer killSwitch(SHORT_TEST_TIMEOUT);