Add vsm_get_event_fd and vsm_enter_eventloop 35/40435/7
authorMateusz Malicki <m.malicki2@samsung.com>
Wed, 3 Jun 2015 13:58:18 +0000 (15:58 +0200)
committerJan Olszak <j.olszak@samsung.com>
Tue, 16 Jun 2015 12:55:23 +0000 (05:55 -0700)
[Feature]       Provide possibility to implement custom event loop for
                user of vasum client library
[Cause]         Integration with other services
[Solution]      Possibility to fetch event fd and run event loop
[Verification]  run ClientSuite tests

Change-Id: Ic47e5de8bfd09889330d97d35d4c9ef47f238953

client/host-ipc-connection.cpp
client/host-ipc-connection.hpp
client/vasum-client-impl.cpp
client/vasum-client-impl.hpp
client/vasum-client.cpp
client/vasum-client.h
tests/unit_tests/client/ut-client.cpp
wrapper/wrapper.cpp

index 7444f19..a58e0d7 100644 (file)
@@ -35,22 +35,23 @@ namespace {
     const int TIMEOUT_INFINITE = -1;
 } //namespace
 
-void HostIPCConnection::createSystem()
+void HostIPCConnection::connect(const std::string& address, ipc::epoll::EventPoll& eventPoll)
 {
-    mClient.reset(new ipc::Client(mDispatcher.getPoll(), HOST_IPC_SOCKET));
+    mClient.reset(new ipc::Client(eventPoll, address));
     mClient->start();
 }
-ipc::epoll::ThreadDispatcher& HostIPCConnection::getDispatcher()
+
+void HostIPCConnection::disconnect()
 {
-    return mDispatcher;
+    mClient.reset();
 }
 
-void HostIPCConnection::create(const std::string& address)
+bool HostIPCConnection::isConnected()
 {
-    mClient.reset(new ipc::Client(mDispatcher.getPoll(), address));
-    mClient->start();
+    return mClient && mClient->isStarted();
 }
 
+
 void HostIPCConnection::callGetZoneIds(api::ZoneIds& argOut)
 {
     argOut = *mClient->callSync<api::Void, api::ZoneIds>(
index 52b90b7..16a2a9e 100644 (file)
@@ -27,8 +27,8 @@
 #define VASUM_CLIENT_HOST_IPC_CONNECTION_HPP
 
 #include <api/messages.hpp>
-#include <ipc/epoll/thread-dispatcher.hpp>
 #include <ipc/client.hpp>
+#include <ipc/epoll/event-poll.hpp>
 
 #include <functional>
 
@@ -42,9 +42,9 @@ class HostIPCConnection {
 public:
     typedef unsigned int SubscriptionId;
     typedef std::function<void(const vasum::api::Notification&)> NotificationCallback;
-    void createSystem();
-    void create(const std::string& address);
-    ipc::epoll::ThreadDispatcher& getDispatcher();
+    void connect(const std::string& address, ipc::epoll::EventPoll& eventPoll);
+    void disconnect();
+    bool isConnected();
 
     void callGetZoneIds(vasum::api::ZoneIds& argOut);
     void callGetActiveZoneId(vasum::api::ZoneId& argOut);
@@ -80,7 +80,6 @@ public:
     void unsubscribe(const SubscriptionId& id);
 
 private:
-    ipc::epoll::ThreadDispatcher mDispatcher;
     std::unique_ptr<ipc::Client> mClient;
 };
 
index f87e9a0..97cd004 100644 (file)
  * @brief   This file contains vasum-server's client implementation
  */
 
+//TODO: Make dispatcher related function thread-safe.
+//For now vsm_connect, vsm_get_dispatcher_type, vsm_set_dispatcher_type,
+//vsm_get_poll_fd, vsm_enter_eventloop can't be used at the same time.
+//TODO: Make vsm_get_status_message thread-safe version (vsm_get_status_message_r)
+
 #include <config.hpp>
 #include "vasum-client-impl.hpp"
 #include "utils.hpp"
@@ -34,7 +39,6 @@
 #include <algorithm>
 #include <vector>
 #include <cstring>
-#include <cassert>
 #include <fstream>
 #include <arpa/inet.h>
 #include <linux/if.h>
@@ -127,6 +131,11 @@ bool readFirstLineOfFile(const string& path, string& ret)
 
 } //namespace
 
+#define IS_SET(param)                                            \
+    if (!param) {                                                \
+        throw InvalidArgumentException(#param " is not set");    \
+    }
+
 Client::Status::Status()
     : mVsmStatus(VSMCLIENT_SUCCESS), mMsg()
 {
@@ -145,20 +154,39 @@ Client::~Client() noexcept
 {
 }
 
+bool Client::isInternalDispatcherEnabled() const
+{
+    return static_cast<bool>(mInternalDispatcher);
+}
+
+ipc::epoll::EventPoll& Client::getEventPoll() const
+{
+    if ((mInternalDispatcher && mEventPoll) || (!mInternalDispatcher && !mEventPoll)) {
+        throw OperationFailedException("Can't determine dispatcher method");
+    }
+
+    if (isInternalDispatcherEnabled()) {
+        return mInternalDispatcher->getPoll();
+    } else {
+        return *mEventPoll;
+    }
+}
+
 VsmStatus Client::coverException(const function<void(void)>& worker) noexcept
 {
     try {
         worker();
+        mStatusMutex.lock();
         mStatus = Status(VSMCLIENT_SUCCESS);
-    } catch (const vasum::IOException& ex) {
+    } catch (const IOException& ex) {
         mStatus = Status(VSMCLIENT_IO_ERROR, ex.what());
-    } catch (const vasum::OperationFailedException& ex) {
+    } catch (const OperationFailedException& ex) {
         mStatus = Status(VSMCLIENT_OPERATION_FAILED, ex.what());
-    } catch (const vasum::InvalidArgumentException& ex) {
+    } catch (const InvalidArgumentException& ex) {
         mStatus = Status(VSMCLIENT_INVALID_ARGUMENT, ex.what());
-    } catch (const vasum::InvalidResponseException& ex) {
+    } catch (const InvalidResponseException& ex) {
         mStatus = Status(VSMCLIENT_OTHER_ERROR, ex.what());
-    } catch (const vasum::ClientException& ex) {
+    } catch (const ClientException& ex) {
         mStatus = Status(VSMCLIENT_CUSTOM_ERROR, ex.what());
     } catch (const ipc::IPCUserException& ex) {
         mStatus = Status(VSMCLIENT_CUSTOM_ERROR, ex.what());
@@ -167,26 +195,86 @@ VsmStatus Client::coverException(const function<void(void)>& worker) noexcept
     } catch (const exception& ex) {
         mStatus = Status(VSMCLIENT_CUSTOM_ERROR, ex.what());
     }
-    return mStatus.mVsmStatus;
+    VsmStatus ret = mStatus.mVsmStatus;
+    mStatusMutex.unlock();
+    return ret;
+}
+
+VsmStatus Client::connectSystem() noexcept
+{
+    return connect(HOST_IPC_SOCKET);
+}
+
+VsmStatus Client::connect(const std::string& address) noexcept
+{
+    return coverException([&] {
+        if (!mInternalDispatcher && !mEventPoll) {
+            vsm_set_dispatcher_type(VSMDISPATCHER_INTERNAL);
+        }
+        mHostClient.connect(address, getEventPoll());
+    });
+}
+
+VsmStatus Client::disconnect() noexcept
+{
+    return coverException([&] {
+        mHostClient.disconnect();
+    });
 }
 
-VsmStatus Client::createSystem() noexcept
+VsmStatus Client::vsm_get_poll_fd(int* fd) noexcept
 {
     return coverException([&] {
-        mHostClient.createSystem();
+        IS_SET(fd);
+        if (isInternalDispatcherEnabled()) {
+            throw OperationFailedException("Can't get event fd from internal dispatcher");
+        }
+        *fd =  mEventPoll->getPollFD();
     });
 }
 
-VsmStatus Client::create(const string& address) noexcept
+VsmStatus Client::vsm_enter_eventloop(int /* flags */, int timeout) noexcept
 {
     return coverException([&] {
-        mHostClient.create(address);
+        if (isInternalDispatcherEnabled()) {
+            throw OperationFailedException("Can't enter to event loop of internal dispatcher");
+        }
+        mEventPoll->dispatchIteration(timeout);
     });
 }
 
-ipc::epoll::EventPoll& Client::getEventPoll() noexcept
+VsmStatus Client::vsm_set_dispatcher_type(VsmDispacherType dispacher) noexcept
 {
-    return mHostClient.getDispatcher().getPoll();
+    return coverException([&] {
+        if (mHostClient.isConnected()) {
+            throw OperationFailedException("Can't change dispacher");
+        }
+        switch (dispacher) {
+            case VSMDISPATCHER_INTERNAL:
+                mInternalDispatcher.reset(new ipc::epoll::ThreadDispatcher());
+                mEventPoll.reset();
+                break;
+            case VSMDISPATCHER_EXTERNAL:
+                mEventPoll.reset(new ipc::epoll::EventPoll());
+                mInternalDispatcher.reset();
+                break;
+            default:
+                throw OperationFailedException("Unsupported EventDispacher type");
+        }
+    });
+}
+
+VsmStatus Client::vsm_get_dispatcher_type(VsmDispacherType* dispacher) noexcept
+{
+    return coverException([&] {
+        IS_SET(dispacher);
+
+        if (isInternalDispatcherEnabled()) {
+            *dispacher = VSMDISPATCHER_INTERNAL;
+        } else {
+            *dispacher = VSMDISPATCHER_EXTERNAL;
+        }
+    });
 }
 
 const char* Client::vsm_get_status_message() const noexcept
@@ -196,6 +284,7 @@ const char* Client::vsm_get_status_message() const noexcept
 
 VsmStatus Client::vsm_get_status() const noexcept
 {
+    lock_guard<mutex> lock(mStatusMutex);
     return mStatus.mVsmStatus;
 }
 
@@ -209,9 +298,9 @@ VsmStatus Client::vsm_get_zone_dbuses(VsmArrayString* /*keys*/, VsmArrayString*
 
 VsmStatus Client::vsm_get_zone_ids(VsmArrayString* array) noexcept
 {
-    assert(array);
-
     return coverException([&] {
+        IS_SET(array);
+
         api::ZoneIds zoneIds;
         mHostClient.callGetZoneIds(zoneIds);
         convert(zoneIds, *array);
@@ -220,9 +309,9 @@ VsmStatus Client::vsm_get_zone_ids(VsmArrayString* array) noexcept
 
 VsmStatus Client::vsm_get_active_zone_id(VsmString* id) noexcept
 {
-    assert(id);
-
     return coverException([&] {
+        IS_SET(id);
+
         api::ZoneId zoneId;
         mHostClient.callGetActiveZoneId(zoneId);
         *id = ::strdup(zoneId.value.c_str());
@@ -231,10 +320,10 @@ VsmStatus Client::vsm_get_active_zone_id(VsmString* id) noexcept
 
 VsmStatus Client::vsm_get_zone_rootpath(const char* id, VsmString* rootpath) noexcept
 {
-    assert(id);
-    assert(rootpath);
-
     return coverException([&] {
+        IS_SET(id);
+        IS_SET(rootpath);
+
         api::ZoneInfoOut info;
         mHostClient.callGetZoneInfo({ id }, info);
         *rootpath = ::strdup(info.rootPath.c_str());
@@ -243,9 +332,9 @@ VsmStatus Client::vsm_get_zone_rootpath(const char* id, VsmString* rootpath) noe
 
 VsmStatus Client::vsm_lookup_zone_by_pid(int pid, VsmString* id) noexcept
 {
-    assert(id);
-
     return coverException([&] {
+        IS_SET(id);
+
         const string path = "/proc/" + to_string(pid) + "/cpuset";
 
         string cpuset;
@@ -264,10 +353,10 @@ VsmStatus Client::vsm_lookup_zone_by_pid(int pid, VsmString* id) noexcept
 
 VsmStatus Client::vsm_lookup_zone_by_id(const char* id, VsmZone* zone) noexcept
 {
-    assert(id);
-    assert(zone);
-
     return coverException([&] {
+        IS_SET(id);
+        IS_SET(zone);
+
         api::ZoneInfoOut info;
         mHostClient.callGetZoneInfo({ id }, info);
         convert(info, *zone);
@@ -284,18 +373,18 @@ VsmStatus Client::vsm_lookup_zone_by_terminal_id(int, VsmString*) noexcept
 
 VsmStatus Client::vsm_set_active_zone(const char* id) noexcept
 {
-    assert(id);
-
     return coverException([&] {
+        IS_SET(id);
+
         mHostClient.callSetActiveZone({ id });
     });
 }
 
 VsmStatus Client::vsm_create_zone(const char* id, const char* tname) noexcept
 {
-    assert(id);
-
     return coverException([&] {
+        IS_SET(id);
+
         string template_name = tname ? tname : "default";
         mHostClient.callCreateZone({ id, template_name });
     });
@@ -303,45 +392,40 @@ VsmStatus Client::vsm_create_zone(const char* id, const char* tname) noexcept
 
 VsmStatus Client::vsm_destroy_zone(const char* id) noexcept
 {
-    assert(id);
-
     return coverException([&] {
+        IS_SET(id);
         mHostClient.callDestroyZone({ id });
     });
 }
 
 VsmStatus Client::vsm_shutdown_zone(const char* id) noexcept
 {
-    assert(id);
-
     return coverException([&] {
+        IS_SET(id);
         mHostClient.callShutdownZone({ id });
     });
 }
 
 VsmStatus Client::vsm_start_zone(const char* id) noexcept
 {
-    assert(id);
-
     return coverException([&] {
+        IS_SET(id);
         mHostClient.callStartZone({ id });
     });
 }
 
 VsmStatus Client::vsm_lock_zone(const char* id) noexcept
 {
-    assert(id);
-
     return coverException([&] {
+        IS_SET(id);
         mHostClient.callLockZone({ id });
     });
 }
 
 VsmStatus Client::vsm_unlock_zone(const char* id) noexcept
 {
-    assert(id);
-
     return coverException([&] {
+        IS_SET(id);
         mHostClient.callUnlockZone({ id });
     });
 }
@@ -365,30 +449,30 @@ VsmStatus Client::vsm_del_state_callback(VsmSubscriptionId subscriptionId) noexc
 
 VsmStatus Client::vsm_grant_device(const char* id, const char* device, uint32_t flags) noexcept
 {
-    assert(id);
-    assert(device);
-
     return coverException([&] {
+        IS_SET(id);
+        IS_SET(device);
+
         mHostClient.callGrantDevice({ id, device, flags });
     });
 }
 
 VsmStatus Client::vsm_revoke_device(const char* id, const char* device) noexcept
 {
-    assert(id);
-    assert(device);
-
     return coverException([&] {
+        IS_SET(id);
+        IS_SET(device);
+
         mHostClient.callRevokeDevice({ id, device });
     });
 }
 
 VsmStatus Client::vsm_zone_get_netdevs(const char* id, VsmArrayString* netdevIds) noexcept
 {
-    assert(id);
-    assert(netdevIds);
-
     return coverException([&] {
+        IS_SET(id);
+        IS_SET(netdevIds);
+
         api::NetDevList netdevs;
         mHostClient.callGetNetdevList({ id }, netdevs);
         convert(netdevs, *netdevIds);
@@ -402,11 +486,11 @@ VsmStatus Client::vsm_netdev_get_ip_addr(const char* id,
 {
     using namespace boost::algorithm;
 
-    assert(id);
-    assert(netdevId);
-    assert(addr);
-
     return coverException([&] {
+        IS_SET(id);
+        IS_SET(netdevId);
+        IS_SET(addr);
+
         api::GetNetDevAttrs attrs;
         mHostClient.callGetNetdevAttrs({ id, netdevId }, attrs);
 
@@ -454,11 +538,11 @@ VsmStatus Client::vsm_netdev_set_ipv4_addr(const char* id,
                                       struct in_addr* addr,
                                       int prefix) noexcept
 {
-    assert(id);
-    assert(netdevId);
-    assert(addr);
-
     return coverException([&] {
+        IS_SET(id);
+        IS_SET(netdevId);
+        IS_SET(addr);
+
         string value = "ip:" + toString(addr) + ",""prefixlen:" + to_string(prefix);
         mHostClient.callSetNetdevAttrs({ id, netdevId, { { "ipv4", value } }  } );
     });
@@ -469,11 +553,11 @@ VsmStatus Client::vsm_netdev_set_ipv6_addr(const char* id,
                                       struct in6_addr* addr,
                                       int prefix) noexcept
 {
-    assert(id);
-    assert(netdevId);
-    assert(addr);
-
     return coverException([&] {
+        IS_SET(id);
+        IS_SET(netdevId);
+        IS_SET(addr);
+
         string value = "ip:" + toString(addr) + ",""prefixlen:" + to_string(prefix);
         mHostClient.callSetNetdevAttrs({ id, netdevId, { { "ipv6", value } }  } );
     });
@@ -484,11 +568,11 @@ VsmStatus Client::vsm_netdev_del_ipv4_addr(const char* id,
                                       struct in_addr* addr,
                                       int prefix) noexcept
 {
-    assert(id);
-    assert(netdevId);
-    assert(addr);
-
     return coverException([&] {
+        IS_SET(id);
+        IS_SET(netdevId);
+        IS_SET(addr);
+
         //CIDR notation
         string ip = toString(addr) + "/" + to_string(prefix);
         mHostClient.callDeleteNetdevIpAddress({ id, netdevId, ip });
@@ -500,11 +584,11 @@ VsmStatus Client::vsm_netdev_del_ipv6_addr(const char* id,
                                       struct in6_addr* addr,
                                       int prefix) noexcept
 {
-    assert(id);
-    assert(netdevId);
-    assert(addr);
-
     return coverException([&] {
+        IS_SET(id);
+        IS_SET(netdevId);
+        IS_SET(addr);
+
         //CIDR notation
         string ip = toString(addr) + "/" + to_string(prefix);
         mHostClient.callDeleteNetdevIpAddress({ id, netdevId, ip });
@@ -514,10 +598,10 @@ VsmStatus Client::vsm_netdev_del_ipv6_addr(const char* id,
 
 VsmStatus Client::vsm_netdev_up(const char* id, const char* netdevId) noexcept
 {
-    assert(id);
-    assert(netdevId);
-
     return coverException([&] {
+        IS_SET(id);
+        IS_SET(netdevId);
+
         mHostClient.callSetNetdevAttrs({ id, netdevId, { { "flags", to_string(IFF_UP) },
                                                           { "change", to_string(IFF_UP) }  }  } );
     });
@@ -525,10 +609,10 @@ VsmStatus Client::vsm_netdev_up(const char* id, const char* netdevId) noexcept
 
 VsmStatus Client::vsm_netdev_down(const char* id, const char* netdevId) noexcept
 {
-    assert(id);
-    assert(netdevId);
-
     return coverException([&] {
+        IS_SET(id);
+        IS_SET(netdevId);
+
         mHostClient.callSetNetdevAttrs({ id, netdevId, { { "flags", to_string(~IFF_UP) },
                                                           { "change", to_string(IFF_UP) }  }  } );
     });
@@ -538,11 +622,11 @@ VsmStatus Client::vsm_create_netdev_veth(const char* id,
                                     const char* zoneDev,
                                     const char* hostDev) noexcept
 {
-    assert(id);
-    assert(zoneDev);
-    assert(hostDev);
-
     return coverException([&] {
+        IS_SET(id);
+        IS_SET(zoneDev);
+        IS_SET(hostDev);
+
         mHostClient.callCreateNetdevVeth({ id, zoneDev, hostDev });
     });
 }
@@ -552,21 +636,21 @@ VsmStatus Client::vsm_create_netdev_macvlan(const char* id,
                                        const char* hostDev,
                                        enum macvlan_mode mode) noexcept
 {
-    assert(id);
-    assert(zoneDev);
-    assert(hostDev);
-
     return coverException([&] {
+        IS_SET(id);
+        IS_SET(zoneDev);
+        IS_SET(hostDev);
+
         mHostClient.callCreateNetdevMacvlan({ id, zoneDev, hostDev, mode });
     });
 }
 
 VsmStatus Client::vsm_create_netdev_phys(const char* id, const char* devId) noexcept
 {
-    assert(id);
-    assert(devId);
-
     return coverException([&] {
+        IS_SET(id);
+        IS_SET(devId);
+
         mHostClient.callCreateNetdevPhys({ id, devId });
     });
 }
@@ -577,11 +661,11 @@ VsmStatus Client::vsm_lookup_netdev_by_name(const char* id,
 {
     using namespace boost::algorithm;
 
-    assert(id);
-    assert(netdevId);
-    assert(netdev);
-
     return coverException([&] {
+        IS_SET(id);
+        IS_SET(netdevId);
+        IS_SET(netdev);
+
         api::GetNetDevAttrs attrs;
         mHostClient.callGetNetdevAttrs({ id, netdevId }, attrs);
         auto it = find_if(attrs.values.begin(),
@@ -610,10 +694,10 @@ VsmStatus Client::vsm_lookup_netdev_by_name(const char* id,
 
 VsmStatus Client::vsm_destroy_netdev(const char* id, const char* devId) noexcept
 {
-    assert(id);
-    assert(devId);
-
     return coverException([&] {
+        IS_SET(id);
+        IS_SET(devId);
+
         mHostClient.callDestroyNetdev({ id, devId });
     });
 }
@@ -625,10 +709,10 @@ VsmStatus Client::vsm_declare_file(const char* id,
                               mode_t mode,
                               VsmString* declarationId) noexcept
 {
-    assert(id);
-    assert(path);
-
     return coverException([&] {
+        IS_SET(id);
+        IS_SET(path);
+
         api::Declaration declaration;
         mHostClient.callDeclareFile({ id, type, path, flags, (int)mode }, declaration);
         if (declarationId != NULL) {
@@ -645,15 +729,15 @@ VsmStatus Client::vsm_declare_mount(const char *source,
                                const char *data,
                                VsmString* declarationId) noexcept
 {
-    assert(source);
-    assert(id);
-    assert(target);
-    assert(type);
-    if (!data) {
-        data = "";
-    }
-
     return coverException([&] {
+        IS_SET(source);
+        IS_SET(id);
+        IS_SET(target);
+        IS_SET(type);
+        if (!data) {
+            data = "";
+        }
+
         api::Declaration declaration;
         mHostClient.callDeclareMount({ source, id, target, type, flags, data }, declaration);
         if (declarationId != NULL) {
@@ -667,11 +751,11 @@ VsmStatus Client::vsm_declare_link(const char* source,
                               const char* target,
                               VsmString* declarationId) noexcept
 {
-    assert(source);
-    assert(id);
-    assert(target);
-
     return coverException([&] {
+        IS_SET(source);
+        IS_SET(id);
+        IS_SET(target);
+
         api::Declaration declaration;
         mHostClient.callDeclareLink({ source, id, target }, declaration);
         if (declarationId != NULL) {
@@ -682,10 +766,10 @@ VsmStatus Client::vsm_declare_link(const char* source,
 
 VsmStatus Client::vsm_list_declarations(const char* id, VsmArrayString* declarations) noexcept
 {
-    assert(id);
-    assert(declarations);
-
     return coverException([&] {
+        IS_SET(id);
+        IS_SET(declarations);
+
         api::Declarations declarationsOut;
         mHostClient.callGetDeclarations({ id }, declarationsOut);
         convert(declarationsOut, *declarations);
@@ -694,10 +778,10 @@ VsmStatus Client::vsm_list_declarations(const char* id, VsmArrayString* declarat
 
 VsmStatus Client::vsm_remove_declaration(const char* id, VsmString declaration) noexcept
 {
-    assert(id);
-    assert(declaration);
-
     return coverException([&] {
+        IS_SET(id);
+        IS_SET(declaration);
+
         mHostClient.callRemoveDeclaration({ id, declaration });
     });
 }
index 5a1d2fd..0ec3634 100644 (file)
 #include "vasum-client.h"
 #include "host-ipc-connection.hpp"
 
+#include <ipc/epoll/thread-dispatcher.hpp>
+#include <ipc/epoll/event-poll.hpp>
+
+#include <mutex>
+#include <memory>
 #include <functional>
 #include <linux/if_link.h>
 
@@ -63,21 +68,44 @@ public:
     ~Client() noexcept;
 
     /**
-     * Create client with system dbus address.
+     * Connect client with system ipc address.
      *
      * @return status of this function call
      */
-    VsmStatus createSystem() noexcept;
-
-    ipc::epoll::EventPoll& getEventPoll() noexcept;
+    VsmStatus connectSystem() noexcept;
 
     /**
-     * Create client.
+     * Connect client.
      *
-     * @param address Dbus socket address
+     * @param address ipc socket address
      * @return status of this function call
      */
-    VsmStatus create(const std::string& address) noexcept;
+    VsmStatus connect(const std::string& address) noexcept;
+
+    /**
+     * Disconnect client
+     */
+    VsmStatus disconnect() noexcept;
+
+    /**
+     *  @see ::vsm_get_poll_fd
+     */
+    VsmStatus vsm_get_poll_fd(int* fd) noexcept;
+
+    /**
+     *  @see ::vsm_enter_eventloop
+     */
+    VsmStatus vsm_enter_eventloop(int flags, int timeout) noexcept;
+
+    /**
+     *  @see ::vsm_set_dispatcher_type
+     */
+    VsmStatus vsm_set_dispatcher_type(VsmDispacherType dispacher) noexcept;
+
+    /**
+     *  @see ::vsm_get_dispatcher_type
+     */
+    VsmStatus vsm_get_dispatcher_type(VsmDispacherType* dispacher) noexcept;
 
     /**
      *  @see ::vsm_get_status_message
@@ -344,11 +372,15 @@ private:
         VsmStatus mVsmStatus;
         std::string mMsg;
     };
-
     Status mStatus;
 
+    mutable std::mutex mStatusMutex;
+    std::unique_ptr<ipc::epoll::ThreadDispatcher> mInternalDispatcher;
+    std::unique_ptr<ipc::epoll::EventPoll> mEventPoll;
     HostConnection mHostClient;
 
+    bool isInternalDispatcherEnabled() const;
+    ipc::epoll::EventPoll& getEventPoll() const;
     VsmStatus coverException(const std::function<void(void)>& worker) noexcept;
     VsmStatus vsm_netdev_get_ip_addr(const char* zone,
                                      const char* netdevId,
index 1d5664c..f36c1ed 100644 (file)
@@ -45,6 +45,26 @@ Client& getClient(VsmClient client)
 
 } // namespace
 
+API VsmStatus vsm_get_poll_fd(VsmClient client, int* fd)
+{
+    return getClient(client).vsm_get_poll_fd(fd);
+}
+
+API VsmStatus vsm_enter_eventloop(VsmClient client, int flags, int timeout)
+{
+    return getClient(client).vsm_enter_eventloop(flags, timeout);
+}
+
+API VsmStatus vsm_set_dispatcher_type(VsmClient client, VsmDispacherType dispacher)
+{
+    return getClient(client).vsm_set_dispatcher_type(dispacher);
+}
+
+API VsmStatus vsm_get_dispatcher_type(VsmClient client, VsmDispacherType* dispacher)
+{
+    return getClient(client).vsm_get_dispatcher_type(dispacher);
+}
+
 API VsmClient vsm_client_create()
 {
     Client* clientPtr = new(nothrow) Client();
@@ -53,12 +73,17 @@ API VsmClient vsm_client_create()
 
 API VsmStatus vsm_connect(VsmClient client)
 {
-    return getClient(client).createSystem();
+    return getClient(client).connectSystem();
 }
 
 API VsmStatus vsm_connect_custom(VsmClient client, const char* address)
 {
-    return getClient(client).create(address);
+    return getClient(client).connect(address);
+}
+
+API VsmStatus vsm_disconnect(VsmClient client)
+{
+    return getClient(client).disconnect();
 }
 
 API void vsm_array_string_free(VsmArrayString astring)
index 7a98fa7..3832d23 100644 (file)
@@ -180,9 +180,128 @@ typedef enum {
     VSMFILE_REGULAR
 } VsmFileType;
 
+/**
+ * Event dispacher types.
+ *
+ * @par Example usage:
+ * @code
+#include <pthread.h>
+#include <assert.h>
+#include <stdio.h>
+#include <signal.h>
+#include <sys/epoll.h>
+#include <vasum-client.h>
+
+volatile static sig_atomic_t running;
+static int LOOP_INTERVAL = 1000; // ms
+
+void* main_loop(void* client)
+{
+    int fd = -1;
+    VsmStatus status = vsm_get_poll_fd(client, &fd);
+    assert(VSMCLIENT_SUCCESS == status);
+
+    while (running) {
+        struct epoll_event event;
+        int num = epoll_wait(fd, &event, 1, LOOP_INTERVAL);
+        if (num > 0) {
+            status = vsm_enter_eventloop(client, 0 , 0);
+            assert(VSMCLIENT_SUCCESS == status);
+        }
+    }
+    return NULL;
+}
+
+int main(int argc, char** argv)
+{
+    pthread_t loop;
+    VsmStatus status;
+    VsmClient client;
+    int ret = 0;
+
+    client = vsm_client_create();
+    assert(client);
+
+    status = vsm_set_dispatcher_type(client, VSMDISPATCHER_EXTERNAL);
+    assert(VSMCLIENT_SUCCESS == status);
+
+    status = vsm_connect(client);
+    assert(VSMCLIENT_SUCCESS == status);
+
+    // start event loop
+    running = 1;
+    ret = pthread_create(&loop, NULL, main_loop, client);
+    assert(ret == 0);
+
+    // make vsm_* calls on client
+    // ...
+
+    status = vsm_disconnect(client);
+    assert(VSMCLIENT_SUCCESS == status);
+
+    //stop event loop
+    running = 0;
+    ret = pthread_join(loop, NULL);
+    assert(ret == 0);
+
+    vsm_client_free(client); // destroy client handle
+    return ret;
+}
+ @endcode
+ */
+typedef enum {
+    VSMDISPATCHER_EXTERNAL,         /**< User must handle dispatching messages */
+    VSMDISPATCHER_INTERNAL          /**< Library will take care of dispatching messages */
+} VsmDispacherType;
+
 #ifndef __VASUM_WRAPPER_SOURCE__
 
 /**
+ * Get file descriptor associated with event dispatcher of zone client
+ *
+ * The function vsm_get_poll_fd() returns the file descriptor associated with the event dispatcher of vsm client.
+ * The file descriptor can be bound to another I/O multiplexing facilities like epoll, select, and poll.
+ *
+ * @param[in] client vsm client
+ * @param[out] fd epoll file descriptor
+ * @return status of this function call
+*/
+VsmStatus vsm_get_poll_fd(VsmClient client, int* fd);
+
+/**
+ * Wait for an I/O event on a vsm client
+ *
+ * vsm_enter_eventloop() waits for event on the vsm client
+ *
+ * The call waits for a maximum time of timout milliseconds. Specifying a timeout of -1 makes vsm_enter_eventloop() wait indefinitely,
+ * while specifying a timeout equal to zero makes vsm_enter_eventloop() to return immediately even if no events are available.
+ *
+ * @param[in] client vasum-server's client
+ * @param[in] flags Reserved
+ * @param[in] timeout Timeout time (milisecond), -1 is infinite
+ * @return status of this function call
+*/
+VsmStatus vsm_enter_eventloop(VsmClient client, int flags, int timeout);
+
+/**
+ * Set dispatching method
+ *
+ * @param[in] client vasum-server's client
+ * @param[in] dispacher dispatching method
+ * @return status of this function call
+ */
+VsmStatus vsm_set_dispatcher_type(VsmClient client, VsmDispacherType dispacher);
+
+/**
+ * Get dispatching method
+ *
+ * @param[in] client vasum-server's client
+ * @param[out] dispacher dispatching method
+ * @return status of this function call
+ */
+VsmStatus vsm_get_dispatcher_type(VsmClient client, VsmDispacherType* dispacher);
+
+/**
  * Create a new vasum-server's client.
  *
  * @return Created client pointer or NULL on failure.
@@ -230,6 +349,14 @@ VsmStatus vsm_connect(VsmClient client);
 VsmStatus vsm_connect_custom(VsmClient client, const char* address);
 
 /**
+ * Disconnect client from vasum-server.
+ *
+ * @param[in] client vasum-server's client
+ * @return status of this function call
+ */
+VsmStatus vsm_disconnect(VsmClient client);
+
+/**
  * Release VsmArrayString.
  *
  * @param[in] astring VsmArrayString
index ce02a33..65d6e49 100644 (file)
@@ -46,6 +46,8 @@
 #include <tuple>
 #include <utility>
 
+#include <sys/epoll.h>
+
 using namespace vasum;
 using namespace utils;
 
@@ -55,6 +57,7 @@ const std::string TEST_CONFIG_PATH =
     VSM_TEST_CONFIG_INSTALL_DIR "/test-daemon.conf";
 const std::string ZONES_PATH = "/tmp/ut-zones"; // the same as in daemon.conf
 const std::string TEMPLATE_NAME = "console-ipc";
+const int EVENT_TIMEOUT = 500; // ms
 
 struct Fixture {
     utils::ScopedDir mZonesPathGuard;
@@ -83,6 +86,73 @@ struct Fixture {
     }
 };
 
+class SimpleEventLoop {
+public:
+    SimpleEventLoop(VsmClient client)
+        : mClient(client)
+        , mIsProcessing(true)
+        , mThread([this] { loop(); })
+    {
+    }
+    ~SimpleEventLoop()
+    {
+        mIsProcessing = false;
+        mThread.join();
+    }
+
+private:
+    VsmClient mClient;
+    std::atomic_bool mIsProcessing;
+    std::thread mThread;
+    void loop() {
+        while (mIsProcessing) {
+            vsm_enter_eventloop(mClient, 0, EVENT_TIMEOUT);
+        }
+    }
+};
+
+class AggragatedEventLoop {
+public:
+    AggragatedEventLoop()
+        : mIsProcessing(true)
+        , mThread([this] { loop(); })
+    {
+    }
+
+    ~AggragatedEventLoop()
+    {
+        mIsProcessing = false;
+        mThread.join();
+    }
+
+    VsmStatus addEventSource(VsmClient client) {
+        int fd = -1;
+        VsmStatus ret = vsm_get_poll_fd(client, &fd);
+        if (VSMCLIENT_SUCCESS != ret) {
+            return ret;
+        }
+        mEventPoll.addFD(fd, EPOLLIN | EPOLLHUP | EPOLLRDHUP, [client] (int, ipc::epoll::Events) {
+            vsm_enter_eventloop(client, 0, 0);
+        });
+        mFds.push_back(fd);
+        return VSMCLIENT_SUCCESS;
+    }
+private:
+    std::atomic_bool mIsProcessing;
+    std::thread mThread;
+    ipc::epoll::EventPoll mEventPoll;
+    std::vector<int> mFds;
+
+    void loop() {
+        while (mIsProcessing) {
+            mEventPoll.dispatchIteration(EVENT_TIMEOUT);
+        }
+        for (int fd : mFds) {
+            mEventPoll.removeFD(fd);
+        }
+    }
+};
+
 const std::set<std::string> EXPECTED_ZONES = { "zone1", "zone2", "zone3" };
 
 void convertArrayToSet(VsmArrayString values, std::set<std::string>& ret)
@@ -103,6 +173,18 @@ int getArrayStringLength(VsmArrayString astring, int max_len = -1)
     return i;
 }
 
+VsmStatus makeSimpleRequest(VsmClient client)
+{
+    BOOST_REQUIRE_EQUAL(VSMCLIENT_SUCCESS, vsm_connect(client));
+    //make a simple call
+    VsmString zone = NULL;
+    VsmStatus status = vsm_get_active_zone_id(client, &zone);
+    vsm_string_free(zone);
+    // disconnect but not destroy
+    vsm_disconnect(client);
+    return status;
+}
+
 } // namespace
 
 // make nice BOOST_*_EQUAL output
@@ -426,6 +508,75 @@ BOOST_AUTO_TEST_CASE(ZoneGetNetdevs)
     vsm_client_free(client);
 }
 
+BOOST_AUTO_TEST_CASE(DefaultDispatcher)
+{
+    VsmClient client = vsm_client_create();
+
+    BOOST_CHECK_EQUAL(VSMCLIENT_SUCCESS, makeSimpleRequest(client));
+
+    vsm_client_free(client);
+}
+
+BOOST_AUTO_TEST_CASE(SetDispatcher)
+{
+    VsmClient client = vsm_client_create();
+
+    BOOST_REQUIRE_EQUAL(VSMCLIENT_SUCCESS, vsm_set_dispatcher_type(client, VSMDISPATCHER_INTERNAL));
+    BOOST_CHECK_EQUAL(VSMCLIENT_SUCCESS, makeSimpleRequest(client));
+
+    BOOST_REQUIRE_EQUAL(VSMCLIENT_SUCCESS, vsm_set_dispatcher_type(client, VSMDISPATCHER_EXTERNAL));
+    {
+        SimpleEventLoop loop(client);
+        BOOST_CHECK_EQUAL(VSMCLIENT_SUCCESS, makeSimpleRequest(client));
+    }
+
+    BOOST_REQUIRE_EQUAL(VSMCLIENT_SUCCESS, vsm_set_dispatcher_type(client, VSMDISPATCHER_INTERNAL));
+    BOOST_CHECK_EQUAL(VSMCLIENT_SUCCESS, makeSimpleRequest(client));
+
+    BOOST_REQUIRE_EQUAL(VSMCLIENT_SUCCESS, vsm_set_dispatcher_type(client, VSMDISPATCHER_EXTERNAL));
+    {
+        SimpleEventLoop loop(client);
+        BOOST_CHECK_EQUAL(VSMCLIENT_SUCCESS, makeSimpleRequest(client));
+    }
+
+    BOOST_REQUIRE_EQUAL(VSMCLIENT_SUCCESS, vsm_set_dispatcher_type(client, VSMDISPATCHER_INTERNAL));
+    BOOST_CHECK_EQUAL(VSMCLIENT_SUCCESS, makeSimpleRequest(client));
+
+    vsm_client_free(client);
+}
+
+BOOST_AUTO_TEST_CASE(GetPollFd)
+{
+    VsmClient client1 = vsm_client_create();
+    VsmClient client2 = vsm_client_create();
+
+    BOOST_REQUIRE_EQUAL(VSMCLIENT_SUCCESS, vsm_set_dispatcher_type(client1, VSMDISPATCHER_EXTERNAL));
+    BOOST_REQUIRE_EQUAL(VSMCLIENT_SUCCESS, vsm_set_dispatcher_type(client2, VSMDISPATCHER_EXTERNAL));
+    {
+        AggragatedEventLoop loop;
+        BOOST_CHECK_EQUAL(VSMCLIENT_SUCCESS, loop.addEventSource(client1));
+        BOOST_CHECK_EQUAL(VSMCLIENT_SUCCESS, loop.addEventSource(client2));
+
+        BOOST_REQUIRE_EQUAL(VSMCLIENT_SUCCESS, vsm_connect(client1));
+        BOOST_REQUIRE_EQUAL(VSMCLIENT_SUCCESS, vsm_connect(client2));
+
+        VsmString zone;
+        //make a simple call
+        zone = NULL;
+        BOOST_CHECK_EQUAL(VSMCLIENT_SUCCESS, vsm_get_active_zone_id(client1, &zone));
+        vsm_string_free(zone);
+        zone = NULL;
+        BOOST_CHECK_EQUAL(VSMCLIENT_SUCCESS, vsm_get_active_zone_id(client2, &zone));
+        vsm_string_free(zone);
+
+        // disconnect but not destroy
+        vsm_disconnect(client1);
+        vsm_disconnect(client2);
+    }
+    vsm_client_free(client1);
+    vsm_client_free(client2);
+}
+
 //TODO: We need createBridge from vasum::netdev
 //BOOST_AUTO_TEST_CASE(CreateDestroyNetdev)
 //BOOST_AUTO_TEST_CASE(LookupNetdev)
index 8b890cf..a975087 100644 (file)
@@ -151,7 +151,7 @@ static int wrap_error(VsmStatus st, const Client *c)
 static void init_context_wrap(WrappedContext *w)
 {
     w->client = new Client();
-    VsmStatus st = w->client->createSystem();
+    VsmStatus st = w->client->connectSystem();
     wrap_error(st, w->client);
 
     memset(&w->hq_ctx, 0, sizeof(w->hq_ctx));