/*
   Copyright (c) 2015 Red Hat, Inc. <http://www.redhat.com>
   This file is part of GlusterFS.

   This file is licensed to you under your choice of the GNU Lesser
   General Public License, version 3 or any later version (LGPLv3 or
   later), or the GNU General Public License, version 2 (GPLv2), in all
   cases as published by the Free Software Foundation.
*/

#ifndef __CHANGELOG_EV_HANDLE_H
#define __CHANGELOG_EV_HANDLE_H

#include <glusterfs/list.h>
#include <glusterfs/xlator.h>
#include "rpc-clnt.h"

#include <glusterfs/rot-buffs.h>

struct changelog_clnt;

typedef struct changelog_rpc_clnt {
    xlator_t *this;

    gf_lock_t lock;

    gf_atomic_t ref;
    gf_boolean_t disconnected;

    unsigned int filter;
    char sock[UNIX_PATH_MAX];

    struct changelog_clnt *c_clnt; /* back pointer to list holder */

    struct rpc_clnt *rpc; /* RPC client endpoint */

    struct list_head list; /* ->pending, ->waitq, ->active */

    void (*cleanup)(struct changelog_rpc_clnt *); /* cleanup handler */
} changelog_rpc_clnt_t;

static inline void
changelog_rpc_clnt_ref(changelog_rpc_clnt_t *crpc)
{
    GF_ATOMIC_INC(crpc->ref);
}

static inline void
changelog_set_disconnect_flag(changelog_rpc_clnt_t *crpc, gf_boolean_t flag)
{
    crpc->disconnected = flag;
}

static inline int
changelog_rpc_clnt_is_disconnected(changelog_rpc_clnt_t *crpc)
{
    return (crpc->disconnected == _gf_true);
}

static inline void
changelog_rpc_clnt_unref(changelog_rpc_clnt_t *crpc)
{
    gf_boolean_t gone = _gf_false;
    uint64_t ref = 0;

    ref = GF_ATOMIC_DEC(crpc->ref);

    if (!ref && changelog_rpc_clnt_is_disconnected(crpc)) {
        list_del(&crpc->list);
        gone = _gf_true;
    }

    if (gone)
        crpc->cleanup(crpc);
}

/**
 * This structure holds pending and active clients. On probe RPC all
 * an instance of the above structure (@changelog_rpc_clnt) is placed
 * in ->pending and gets moved to ->active on a successful connect.
 *
 * locking rules:
 *
 * Manipulating ->pending
 * ->pending_lock
 *    ->pending
 *
 * Manipulating ->active
 * ->active_lock
 *    ->active
 *
 * Moving object from ->pending to ->active
 * ->pending_lock
 *   ->active_lock
 *
 * Objects are _never_ moved from ->active to ->pending, i.e., during
 * disconnection, the object is destroyed. Well, we could have tried
 * to reconnect, but that's pure waste.. let the other end reconnect.
 */

typedef struct changelog_clnt {
    xlator_t *this;

    /* pending connections */
    pthread_mutex_t pending_lock;
    pthread_cond_t pending_cond;
    struct list_head pending;

    /* current active connections */
    gf_lock_t active_lock;
    struct list_head active;

    gf_lock_t wait_lock;
    struct list_head waitq;

    /* consumer part of rot-buffs */
    rbuf_t *rbuf;
    unsigned long sequence;
} changelog_clnt_t;

void *
changelog_ev_connector(void *);

void *
changelog_ev_dispatch(void *);

/* APIs */
void
changelog_ev_queue_connection(changelog_clnt_t *, changelog_rpc_clnt_t *);

void
changelog_ev_cleanup_connections(xlator_t *, changelog_clnt_t *);

void
changelog_process_cleanup_event(xlator_t *);
#endif
