#include "dbus/connection.hpp"
#include "dbus/exception.hpp"
+#include "utils/callback-wrapper.hpp"
#include "log/logger.hpp"
const int CALL_METHOD_TIMEOUT_MS = 1000;
-template<class Callback>
-void deleteCallback(gpointer data)
-{
- delete reinterpret_cast<Callback*>(data);
-}
-
class ScopedError {
public:
ScopedError() : mError(NULL) {}
if (mNameId) {
g_bus_unown_name(mNameId);
}
- //TODO should we unregister, flush, close etc?
- //if (!g_dbus_connection_close_sync(mConnection, NULL, NULL)) {
- // LOGE("Could not close connection");
- //}
g_object_unref(mConnection);
LOGT("Connection deleted");
}
G_BUS_NAME_OWNER_FLAGS_NONE,
&DbusConnection::onNameAcquired,
&DbusConnection::onNameLost,
- new NameCallbacks(onNameAcquired, onNameLost),
- &deleteCallback<NameCallbacks>);
+ utils::createCallbackWrapper(
+ NameCallbacks(onNameAcquired, onNameLost),
+ mGuard.spawn()),
+ &utils::deleteCallbackWrapper<NameCallbacks>);
}
void DbusConnection::onNameAcquired(GDBusConnection*, const gchar* name, gpointer userData)
{
LOGD("Name acquired " << name);
- const NameCallbacks& callbacks = *reinterpret_cast<const NameCallbacks*>(userData);
+ const NameCallbacks& callbacks = utils::getCallbackFromPointer<NameCallbacks>(userData);
if (callbacks.nameAcquired) {
callbacks.nameAcquired();
}
void DbusConnection::onNameLost(GDBusConnection*, const gchar* name, gpointer userData)
{
LOGE("Name lost " << name);
- const NameCallbacks& callbacks = *reinterpret_cast<const NameCallbacks*>(userData);
+ const NameCallbacks& callbacks = utils::getCallbackFromPointer<NameCallbacks>(userData);
if (callbacks.nameLost) {
callbacks.nameLost();
}
NULL,
G_DBUS_SIGNAL_FLAGS_NONE,
&DbusConnection::onSignal,
- new SignalCallback(callback),
- &deleteCallback<SignalCallback>);
+ utils::createCallbackWrapper(callback, mGuard.spawn()),
+ &utils::deleteCallbackWrapper<SignalCallback>);
}
void DbusConnection::onSignal(GDBusConnection*,
GVariant* parameters,
gpointer userData)
{
- const SignalCallback& callback = *static_cast<const SignalCallback*>(userData);
+ const SignalCallback& callback = utils::getCallbackFromPointer<SignalCallback>(userData);
LOGD("Signal: " << sender << "; " << object << "; " << interface << "; " << name);
objectPath.c_str(),
interfaceInfo,
&vtable,
- new MethodCallCallback(callback),
- &deleteCallback<MethodCallCallback>,
+ utils::createCallbackWrapper(callback, mGuard.spawn()),
+ &utils::deleteCallbackWrapper<MethodCallCallback>,
&error);
g_dbus_node_info_unref(nodeInfo);
if (error) {
GDBusMethodInvocation* invocation,
gpointer userData)
{
- const MethodCallCallback& callback = *static_cast<const MethodCallCallback*>(userData);
+ const MethodCallCallback& callback = utils::getCallbackFromPointer<MethodCallCallback>(userData);
LOGD("MethodCall; " << objectPath << "; " << interface << "; " << method);
#ifndef COMMON_DBUS_CONNECTION_HPP
#define COMMON_DBUS_CONNECTION_HPP
+#include "utils/callback-guard.hpp"
+
#include <memory>
#include <string>
#include <functional>
: nameAcquired(acquired), nameLost(lost) {}
};
+ utils::CallbackGuard mGuard;
GDBusConnection* mConnection;
guint mNameId;
--- /dev/null
+/*
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Contact: Piotr Bartosiewicz <p.bartosiewi@partner.samsung.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+/**
+ * @file
+ * @author Piotr Bartosiewicz (p.bartosiewi@partner.samsung.com)
+ * @brief Callback guard
+ */
+
+#include "callback-guard.hpp"
+#include "log/logger.hpp"
+
+#include <mutex>
+#include <condition_variable>
+#include <cassert>
+
+
+namespace security_containers {
+namespace utils {
+
+// Reference counting class like shared_ptr but with the ability to wait for it.
+class CallbackGuard::SharedState {
+public:
+ void inc()
+ {
+ std::unique_lock<std::mutex> lock(mMutex);
+ ++mCounter;
+ }
+
+ void dec()
+ {
+ std::unique_lock<std::mutex> lock(mMutex);
+ --mCounter;
+ mEmptyCondition.notify_all();
+ }
+
+ long size()
+ {
+ std::unique_lock<std::mutex> lock(mMutex);
+ return mCounter;
+ }
+
+ bool wait(const unsigned int timeoutMs)
+ {
+ std::unique_lock<std::mutex> lock(mMutex);
+ return mEmptyCondition.wait_for(lock,
+ std::chrono::milliseconds(timeoutMs),
+ [this] {return mCounter == 0;});
+ }
+private:
+ std::mutex mMutex;
+ std::condition_variable mEmptyCondition;
+ long mCounter = 0;
+};
+
+namespace {
+
+template<class SharedState>
+class TrackerImpl {
+public:
+ TrackerImpl(const std::shared_ptr<SharedState>& sharedState)
+ : mSharedState(sharedState)
+ {
+ mSharedState->inc();
+ }
+
+ ~TrackerImpl()
+ {
+ mSharedState->dec();
+ }
+private:
+ std::shared_ptr<SharedState> mSharedState;
+};
+
+// Relatively big timeout in case of deadlock.
+// This timeout should never be exceeded with
+// a properly written code.
+const unsigned int TIMEOUT = 5000;
+
+} // namespace
+
+
+CallbackGuard::CallbackGuard()
+ : mSharedState(new SharedState())
+{
+}
+
+CallbackGuard::~CallbackGuard()
+{
+ if (!waitForTrackers(TIMEOUT)) {
+ LOGE("==== DETECTED INVALID CALLBACK USE ====");
+ assert(0 && "Invalid callback use");
+ }
+}
+
+CallbackGuard::Tracker CallbackGuard::spawn() const
+{
+ return Tracker(new TrackerImpl<SharedState>(mSharedState));
+}
+
+long CallbackGuard::getTrackersCount() const
+{
+ return mSharedState->size();
+}
+
+bool CallbackGuard::waitForTrackers(const unsigned int timeoutMs)
+{
+ return mSharedState->wait(timeoutMs);
+}
+
+} // namespace utils
+} // namespace security_containers
--- /dev/null
+/*
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Contact: Piotr Bartosiewicz <p.bartosiewi@partner.samsung.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+/**
+ * @file
+ * @author Piotr Bartosiewicz (p.bartosiewi@partner.samsung.com)
+ * @brief Callback guard
+ */
+
+#ifndef COMMON_UTILS_CALLBACK_GUARD_HPP
+#define COMMON_UTILS_CALLBACK_GUARD_HPP
+
+
+#include <memory>
+
+
+namespace security_containers {
+namespace utils {
+
+/**
+ * Callback guard.
+ * An utility class to control and/or monitor callback lifecycle.
+ */
+class CallbackGuard {
+public:
+ typedef std::shared_ptr<void> Tracker;
+
+ /**
+ * Creates a guard.
+ */
+ CallbackGuard();
+
+ /**
+ * Waits for all trackers.
+ */
+ ~CallbackGuard();
+
+ /**
+ * Creates a tracker.
+ */
+ Tracker spawn() const;
+
+ /**
+ * Gets trackers count
+ */
+ long getTrackersCount() const;
+
+ /**
+ * Wait for all trackers.
+ */
+ bool waitForTrackers(const unsigned int timeoutMs);
+private:
+ class SharedState;
+ std::shared_ptr<SharedState> mSharedState;
+
+ CallbackGuard(const CallbackGuard&) = delete;
+ CallbackGuard& operator=(const CallbackGuard&) = delete;
+};
+
+} // namespace utils
+} // namespace security_containers
+
+
+#endif // COMMON_UTILS_CALLBACK_GUARD_HPP
--- /dev/null
+/*
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Contact: Piotr Bartosiewicz <p.bartosiewi@partner.samsung.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+/**
+ * @file
+ * @author Piotr Bartosiewicz (p.bartosiewi@partner.samsung.com)
+ * @brief Callback wrapper
+ */
+
+#ifndef COMMON_UTILS_CALLBACK_WRAPPER_HPP
+#define COMMON_UTILS_CALLBACK_WRAPPER_HPP
+
+#include "callback-guard.hpp"
+
+
+namespace security_containers {
+namespace utils {
+
+
+/**
+ * Wraps callback and callback tracker into single object
+ */
+template<class Callback>
+class CallbackWrapper {
+public:
+ CallbackWrapper(const Callback& callback, const CallbackGuard::Tracker& tracker)
+ : mCallback(callback)
+ , mTracker(tracker)
+ {
+ }
+
+ /**
+ * @return Wrapped callback
+ */
+ const Callback& get() const
+ {
+ return mCallback;
+ }
+private:
+ Callback mCallback;
+ CallbackGuard::Tracker mTracker;
+};
+
+/**
+ * Creates callback wrapper. Usefull for C callback api.
+ */
+template<class Callback>
+CallbackWrapper<Callback>* createCallbackWrapper(const Callback& callback,
+ const CallbackGuard::Tracker& tracker)
+{
+ return new CallbackWrapper<Callback>(callback, tracker);
+}
+
+/**
+ * Deletes callback wrapper. Usefull for C callback api.
+ */
+template<class Callback>
+void deleteCallbackWrapper(void* pointer)
+{
+ delete reinterpret_cast<CallbackWrapper<Callback>*>(pointer);
+}
+
+/**
+ * Recovers callback from wrapper pointer. Usefull for C callback api.
+ */
+template<class Callback>
+const Callback& getCallbackFromPointer(const void* pointer)
+{
+ return reinterpret_cast<const CallbackWrapper<Callback>*>(pointer)->get();
+}
+
+
+} // namespace utils
+} // namespace security_containers
+
+
+#endif // COMMON_UTILS_CALLBACK_WRAPPER_HPP
--- /dev/null
+/*
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Contact: Piotr Bartosiewicz <p.bartosiewi@partner.samsung.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+
+/**
+ * @file
+ * @author Piotr Bartosiewicz (p.bartosiewi@partner.samsung.com)
+ * @brief Unit tests of callback guard
+ */
+
+#include "ut.hpp"
+
+#include "utils/callback-guard.hpp"
+#include "utils/latch.hpp"
+
+#include <future>
+#include <thread>
+
+
+BOOST_AUTO_TEST_SUITE(CallbackGuardSuite)
+
+using namespace security_containers::utils;
+
+const int unsigned TIMEOUT = 1000;
+
+BOOST_AUTO_TEST_CASE(EmptyTest)
+{
+ CallbackGuard guard;
+ BOOST_CHECK_EQUAL(0, guard.getTrackersCount());
+ BOOST_CHECK(guard.waitForTrackers(TIMEOUT));
+}
+
+BOOST_AUTO_TEST_CASE(SimpleTest)
+{
+ CallbackGuard guard;
+ guard.spawn();
+ guard.spawn();
+ BOOST_CHECK_EQUAL(0, guard.getTrackersCount());
+ CallbackGuard::Tracker tracker1 = guard.spawn();
+ CallbackGuard::Tracker tracker2 = guard.spawn();
+ BOOST_CHECK_EQUAL(2, guard.getTrackersCount());
+ CallbackGuard::Tracker tracker2Copy = tracker2;
+ BOOST_CHECK_EQUAL(2, guard.getTrackersCount());
+ tracker2.reset();
+ BOOST_CHECK_EQUAL(2, guard.getTrackersCount());
+ tracker2Copy.reset();
+ BOOST_CHECK_EQUAL(1, guard.getTrackersCount());
+ tracker1.reset();
+ BOOST_CHECK_EQUAL(0, guard.getTrackersCount());
+ BOOST_CHECK(guard.waitForTrackers(TIMEOUT));
+}
+
+BOOST_AUTO_TEST_CASE(ThreadTest)
+{
+ CallbackGuard guard;
+ Latch trackerCreated;
+ Latch trackerCanBeDestroyed;
+
+ std::future<bool> future = std::async(std::launch::async, [&] {
+ CallbackGuard::Tracker tracker = guard.spawn();
+ trackerCreated.set();
+ if (!trackerCanBeDestroyed.wait(TIMEOUT)) {
+ return false;
+ }
+ std::this_thread::sleep_for(std::chrono::milliseconds(200));
+ return true;
+ });
+
+ BOOST_CHECK(trackerCreated.wait(TIMEOUT));
+ BOOST_CHECK_EQUAL(1, guard.getTrackersCount());
+
+ trackerCanBeDestroyed.set();
+ BOOST_CHECK(guard.waitForTrackers(TIMEOUT));
+ BOOST_CHECK_EQUAL(0, guard.getTrackersCount());
+
+ future.wait();
+ BOOST_CHECK(future.get());
+}
+
+BOOST_AUTO_TEST_SUITE_END()