/**
 * Copyright (c) NVIDIA CORPORATION & AFFILIATES, 2001-2017. ALL RIGHTS RESERVED.
 *
 * See file LICENSE for terms.
 */

#ifndef UCT_WORKER_H_
#define UCT_WORKER_H_

#include <uct/api/uct.h>
#include <ucs/datastruct/callbackq.h>
#include <ucs/datastruct/list.h>


/**
 * Transport-specific data on a worker
 */
typedef struct uct_worker_tl_data {
    ucs_list_link_t        list;
    uint32_t               refcount;
    uint32_t               key;
} uct_worker_tl_data_t;


typedef struct uct_priv_worker {
    uct_worker_t           super;
    ucs_async_context_t    *async;
    ucs_thread_mode_t      thread_mode;
    ucs_list_link_t        tl_data;
} uct_priv_worker_t;


typedef struct uct_worker_progress {
    uct_worker_cb_id_t     id;
    uint32_t               refcount;
} uct_worker_progress_t;


#define uct_worker_tl_data_get(_worker, _key, _type, _cmp_fn, _init_fn, ...) \
    ({ \
        uct_worker_tl_data_t *data; \
        _type *result; \
        ucs_status_t _status; \
        \
        ucs_list_for_each(data, &(_worker)->tl_data, list) { \
            if ((data->key == (_key)) && _cmp_fn(ucs_derived_of(data, _type), \
                                                 ## __VA_ARGS__)) \
            { \
                ++data->refcount; \
                break; \
            } \
        } \
        \
        if (&data->list == &(_worker)->tl_data) { /* not found */ \
            result = ucs_malloc(sizeof(_type), UCS_PP_QUOTE(_type)); \
            if (result == NULL) { \
                result = UCS_STATUS_PTR(UCS_ERR_NO_MEMORY); \
            } else { \
                data = (uct_worker_tl_data_t*)result;\
                data->key      = (_key); \
                data->refcount = 1; \
                _status = _init_fn(ucs_derived_of(data, _type), ## __VA_ARGS__); \
                if (_status != UCS_OK) { \
                    ucs_free(result); \
                    result = UCS_STATUS_PTR(_status); \
                } else { \
                    ucs_list_add_tail(&(_worker)->tl_data, &data->list); \
                } \
            } \
        } else { \
            result = ucs_derived_of(data, _type); \
        } \
        result; \
    })


#define uct_worker_tl_data_put(_data, _cleanup_fn, ...) \
    { \
        uct_worker_tl_data_t *data = (uct_worker_tl_data_t*)(_data); \
        if (--data->refcount == 0) { \
            ucs_list_del(&data->list); \
            _cleanup_fn((_data), ## __VA_ARGS__); \
            ucs_free(data); \
        } \
    }


void uct_worker_progress_init(uct_worker_progress_t *prog);

void uct_worker_progress_add_safe(uct_priv_worker_t *worker, ucs_callback_t cb,
                                  void *arg, uct_worker_progress_t *prog);

void uct_worker_progress_remove_all(uct_priv_worker_t *worker,
                                    uct_worker_progress_t *prog);

void uct_worker_progress_remove(uct_priv_worker_t *worker, uct_worker_progress_t *prog);

#endif
