provider.hpp 12.6 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
/*
 * (C) 2017 The University of Chicago
 * 
 * See COPYRIGHT in top-level directory.
 */
#ifndef __THALLIUM_PROVIDER_HPP
#define __THALLIUM_PROVIDER_HPP

#include <memory>
#include <iostream>
#include <string>
#include <functional>
#include <unordered_map>
#include <atomic>
#include <margo.h>
#include <thallium/engine.hpp>

namespace thallium {

typedef std::integral_constant<bool,true> ignore_return_value;

/**
 * @brief The provider class represents an object that
 * exposes its member functions as RPCs. It is a template
 * class meant to be used as base class for other objects.
 * For example:
 * class MyProvider : public thallium::provider<MyProvider> { ... };
 *
 * @tparam T
 */
template<typename T>
class provider {

private:

    engine&  m_engine;
    uint16_t m_provider_id;

public:

	provider(engine& e, uint16_t provider_id)
    : m_engine(e), m_provider_id(provider_id) {}

    virtual ~provider() {}

    /**
     * @brief Copy-constructor is deleted.
     */
	provider(const provider& other)            = delete;

    /**
     * @brief Move-constructor is deleted.
     */
	provider(provider&& other)                 = delete;
    
    /**
     * @brief Move-assignment operator is deleted.
     */
	provider& operator=(provider&& other)      = delete;

    /**
     * @brief Copy-assignment operator is deleted.
     */
	provider& operator=(const provider& other) = delete;

66
    protected:
67 68 69 70 71 72 73 74 75 76 77 78 79 80
    /**
     * @brief Waits for the engine to finalize.
     */
    inline void wait_for_finalize() {
        m_engine.wait_for_finalize();
    }

    /**
     * @brief Finalize the engine.
     */
    inline void finalize() {
        m_engine.finalize();
    }

81 82 83 84 85 86 87 88 89 90 91 92 93

private:

    // define_member as RPC for the case return value is NOT void and
    // the first argument is a request. The return value should be ignored,
    // since the user is expected to call req.respond(...).
    template<typename S, typename R, typename A1, typename ... Args>
    remote_procedure define_member(
            S&& name,
            R(T::*func)(A1, Args...),
            const std::integral_constant<bool, false>& r_is_void,
            const std::integral_constant<bool, true>& first_arg_is_request,
            const pool& p) {
94
        T* self = static_cast<T*>(this);
95 96
        std::function<void(const request&, Args...)> fun = [self, func](const request& req, Args... args) {
            (self->*func)(req, args...);
97
        };
98
        return m_engine.define(std::forward<S>(name), fun, m_provider_id, p);
99 100
    }

101 102 103
    // define_member as RPC for the case the return value is NOT void
    // and the request is not passed to the function. The return value
    // should be sent using req.respond(...).
104
    template<typename S, typename R, typename ... Args>
105 106 107 108 109 110
    remote_procedure define_member(
            S&& name,
            R(T::*func)(Args...),
            const std::integral_constant<bool, false>& r_is_void,
            const std::integral_constant<bool, false>& first_arg_is_request,
            const pool& p) {
111
        T* self = static_cast<T*>(this);
112 113 114 115
        std::function<void(const request&, Args...)> fun = [self, func](const request& req, Args... args) {
            R r = (self->*func)(args...);
            req.respond(r);
        };
116
        return m_engine.define(std::forward<S>(name), fun, m_provider_id, p);
117 118
    }

119 120 121 122 123 124 125 126 127 128
    // define_memver as RPC for the case the return value IS void
    // and the first argument is a request. The user is expected to call
    // req.respond(...) himself.
    template<typename S, typename R, typename A1, typename ... Args>
    remote_procedure define_member(
            S&& name,
            R(T::*func)(A1, Args...),
            const std::integral_constant<bool, true>& r_is_void,
            const std::integral_constant<bool, true>& first_arg_is_request,
            const pool& p = pool()) {
129
        T* self = static_cast<T*>(this);
130 131 132 133 134 135 136 137
        std::function<void(const request&, Args...)> fun = [self, func](const request& req, Args... args) {
            (self->*func)(req, args...);
        };
        return m_engine.define(std::forward<S>(name), fun, m_provider_id, p);
    }

    // define_member as RPC for the case the return value IS void
    // and the first argument IS NOT a request. We call disable_response.
138
    template<typename S, typename R, typename ... Args>
139 140 141 142 143 144
    remote_procedure define_member(
            S&& name,
            R(T::*func)(Args...),
            const std::integral_constant<bool, true>& r_is_void,
            const std::integral_constant<bool, false>& first_arg_is_request,
            const pool& p = pool()) {
145
        T* self = static_cast<T*>(this);
146 147 148
        std::function<void(const request&, Args...)> fun = [self, func](const request& req, Args... args) {
            (self->*func)(args...);
        };
149
        return m_engine.define(std::forward<S>(name), fun, m_provider_id, p).disable_response();
150 151
    }

152 153 154 155 156 157 158 159 160 161 162 163 164 165
    // ---
    // the following 4 functions are the same but for "const" member functions
    // ---

    // define_member as RPC for the case return value is NOT void and
    // the first argument is a request. The return value should be ignored,
    // since the user is expected to call req.respond(...).
    template<typename S, typename R, typename A1, typename ... Args>
    remote_procedure define_member(
            S&& name,
            R(T::*func)(A1, Args...) const,
            const std::integral_constant<bool, false>& r_is_void,
            const std::integral_constant<bool, true>& first_arg_is_request,
            const pool& p) {
166
        T* self = static_cast<T*>(this);
167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182
        std::function<void(const request&, Args...)> fun = [self, func](const request& req, Args... args) {
            (self->*func)(req, args...);
        };
        return m_engine.define(std::forward<S>(name), fun, m_provider_id, p);
    }

    // define_member as RPC for the case the return value is NOT void
    // and the request is not passed to the function. The return value
    // should be sent using req.respond(...).
    template<typename S, typename R, typename ... Args>
    remote_procedure define_member(
            S&& name,
            R(T::*func)(Args...) const,
            const std::integral_constant<bool, false>& r_is_void,
            const std::integral_constant<bool, false>& first_arg_is_request,
            const pool& p) {
183
        T* self = static_cast<T*>(this);
184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200
        std::function<void(const request&, Args...)> fun = [self, func](const request& req, Args... args) {
            R r = (self->*func)(args...);
            req.respond(r);
        };
        return m_engine.define(std::forward<S>(name), fun, m_provider_id, p);
    }

    // define_memver as RPC for the case the return value IS void
    // and the first argument is a request. The user is expected to call
    // req.respond(...) himself.
    template<typename S, typename R, typename A1, typename ... Args>
    remote_procedure define_member(
            S&& name,
            R(T::*func)(A1, Args...) const,
            const std::integral_constant<bool, true>& r_is_void,
            const std::integral_constant<bool, true>& first_arg_is_request,
            const pool& p = pool()) {
201
        T* self = static_cast<T*>(this);
202 203 204 205 206 207 208 209 210 211 212 213 214 215 216
        std::function<void(const request&, Args...)> fun = [self, func](const request& req, Args... args) {
            (self->*func)(req, args...);
        };
        return m_engine.define(std::forward<S>(name), fun, m_provider_id, p);
    }

    // define_member as RPC for the case the return value IS void
    // and the first argument IS NOT a request. We call disable_response.
    template<typename S, typename R, typename ... Args>
    remote_procedure define_member(
            S&& name,
            R(T::*func)(Args...) const,
            const std::integral_constant<bool, true>& r_is_void,
            const std::integral_constant<bool, false>& first_arg_is_request,
            const pool& p = pool()) {
217
        T* self = static_cast<T*>(this);
218 219 220 221 222
        std::function<void(const request&, Args...)> fun = [self, func](const request& req, Args... args) {
            (self->*func)(args...);
        };
        return m_engine.define(std::forward<S>(name), fun, m_provider_id, p).disable_response();
    }
223
protected:
224 225

    /**
226
     * @brief Defines an RPC using a member function of the child class.
227
     *
228 229 230 231 232 233 234 235 236
     * @tparam S type of the name (e.g. C-like string or std::string)
     * @tparam R return value of the member function
     * @tparam A1 type of the first argument of the member function
     * @tparam Args type of other arguments of the member function
     * @tparam FIRST_ARG_IS_REQUEST automatically deducing whether the first type is a thallium::request
     * @tparam R_IS_VOID automatically deducing whether the return value is void
     * @param name name of the RPC
     * @param T::*func member function
     * @param p Argobots pool
237
     */
238 239 240 241 242
    template<typename S, typename R, typename A1, typename ... Args, 
             typename FIRST_ARG_IS_REQUEST = typename std::is_same<A1, const request&>::type,
             typename R_IS_VOID = typename std::is_void<R>::type>
    inline remote_procedure define(S&& name, R(T::*func)(A1, Args...), const pool& p, R_IS_VOID r_is_void = R_IS_VOID()) {
        return define_member(std::forward<S>(name), func, r_is_void, FIRST_ARG_IS_REQUEST(), p);
243 244
    }

245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261
    template<typename S, typename R, typename A1, typename ... Args, 
             typename FIRST_ARG_IS_REQUEST = typename std::is_same<A1, const request&>::type,
             typename R_IS_VOID = typename std::is_void<R>::type>
    inline remote_procedure define(S&& name, R(T::*func)(A1, Args...), R_IS_VOID r_is_void = R_IS_VOID()) {
        return define_member(std::forward<S>(name), func, r_is_void, FIRST_ARG_IS_REQUEST(), pool());
    }

    template<typename S, typename R, typename R_IS_VOID = typename std::is_void<R>::type>
    inline remote_procedure define(S&& name, R(T::*func)(), const pool& p, R_IS_VOID r_is_void = R_IS_VOID()) {
        std::integral_constant<bool, false> first_arg_is_request;
        return define_member(std::forward<S>(name), func, r_is_void, first_arg_is_request, p);
    }

    template<typename S, typename R, typename R_IS_VOID = typename std::is_void<R>::type>
    inline remote_procedure define(S&& name, R(T::*func)(), R_IS_VOID r_is_void = R_IS_VOID()) {
        std::integral_constant<bool, false> first_arg_is_request;
        return define_member(std::forward<S>(name), func, r_is_void, first_arg_is_request, pool());
262 263
    }

264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292
    // ---
    // the following 4 functions are the same but for "const" member functions
    // ---

    template<typename S, typename R, typename A1, typename ... Args, 
             typename FIRST_ARG_IS_REQUEST = typename std::is_same<A1, const request&>::type,
             typename R_IS_VOID = typename std::is_void<R>::type>
    inline remote_procedure define(S&& name, R(T::*func)(A1, Args...) const, const pool& p, R_IS_VOID r_is_void = R_IS_VOID()) {
        return define_member(std::forward<S>(name), func, r_is_void, FIRST_ARG_IS_REQUEST(), p);
    }

    template<typename S, typename R, typename A1, typename ... Args, 
             typename FIRST_ARG_IS_REQUEST = typename std::is_same<A1, const request&>::type,
             typename R_IS_VOID = typename std::is_void<R>::type>
    inline remote_procedure define(S&& name, R(T::*func)(A1, Args...) const, R_IS_VOID r_is_void = R_IS_VOID()) {
        return define_member(std::forward<S>(name), func, r_is_void, FIRST_ARG_IS_REQUEST(), pool());
    }

    template<typename S, typename R, typename R_IS_VOID = typename std::is_void<R>::type>
    inline remote_procedure define(S&& name, R(T::*func)() const, const pool& p, R_IS_VOID r_is_void = R_IS_VOID()) {
        std::integral_constant<bool, false> first_arg_is_request;
        return define_member(std::forward<S>(name), func, r_is_void, first_arg_is_request, p);
    }

    template<typename S, typename R, typename R_IS_VOID = typename std::is_void<R>::type>
    inline remote_procedure define(S&& name, R(T::*func)() const, R_IS_VOID r_is_void = R_IS_VOID()) {
        std::integral_constant<bool, false> first_arg_is_request;
        return define_member(std::forward<S>(name), func, r_is_void, first_arg_is_request, pool());
    }
293 294
public:

295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325
    /**
     * @brief Get the engine associated with this provider.
     *
     * @return The engine associated with this provider.
     */
    const engine& get_engine() const {
        return m_engine;
    }

    /**
     * @brief Get the engine associated with this provider.
     *
     * @return The engine associated with this provider.
     */
    engine& get_engine() {
        return m_engine;
    }

    /**
     * @brief Get the provider id.
     *
     * @return The provider id.
     */
    uint16_t get_provider_id() const {
        return m_provider_id;
    }
};

} // namespace thallium

#endif