/* 
 * Copyright (c) 2004-2005 The Trustees of Indiana University and Indiana
 *                         University Research and Technology
 *                         Corporation.  All rights reserved.
 * Copyright (c) 2004-2005 The University of Tennessee and The University
 *                         of Tennessee Research Foundation.  All rights
 *                         reserved.
 * Copyright (c) 2004-2005 High Performance Computing Center Stuttgart, 
 *                         University of Stuttgart.  All rights reserved.
 * Copyright (c) 2004-2005 The Regents of the University of California.
 *                         All rights reserved.
 * $COPYRIGHT$
 * 
 * Additional copyrights may follow
 * 
 * $HEADER$
 */

#ifndef OMPI_WIN_H
#define OMPI_WIN_H

#include "ompi_config.h"
#include "mpi.h"

#include "opal/class/opal_object.h"
#include "opal/class/opal_hash_table.h"
#include "ompi/errhandler/errhandler.h"
#include "ompi/info/info.h"
#include "ompi/communicator/communicator.h"
#include "ompi/group/group.h"
#include "ompi/mca/osc/osc.h"

#if defined(c_plusplus) || defined(__cplusplus)
extern "C" {
#endif

/* flags */
#define OMPI_WIN_FREED        0x00000001
#define OMPI_WIN_INVALID      0x00000002
#define OMPI_WIN_NO_LOCKS     0x00000004

/* mode */
#define OMPI_WIN_ACCESS_EPOCH 0x00000001
#define OMPI_WIN_EXPOSE_EPOCH 0x00000002
#define OMPI_WIN_FENCE        0x00000010
#define OMPI_WIN_POSTED       0x00000020
#define OMPI_WIN_STARTED      0x00000040
#define OMPI_WIN_LOCK_ACCESS  0x00000080

OMPI_DECLSPEC extern ompi_pointer_array_t ompi_mpi_windows;

struct ompi_win_t {
    opal_object_t w_base;

    opal_mutex_t  w_lock;

    char w_name[MPI_MAX_OBJECT_NAME];

    /* Group associated with this window. */
    ompi_group_t *w_group;

    /* Information about the state of the window.  */
    uint16_t w_flags;

    /* Attributes */
    opal_hash_table_t *w_keyhash;

    /* index in Fortran <-> C translation array */
    int w_f_to_c_index;

    /* Error handling.  This field does not have the "w_" prefix so
       that the OMPI_ERRHDL_* macros can find it, regardless of
       whether it's a comm, window, or file. */
    ompi_errhandler_t                    *error_handler;
    ompi_errhandler_type_t               errhandler_type;

    /* displacement factor */
    int w_disp_unit;

    void *w_baseptr;
    long w_size;

    /** Current epoch / mode (access, expose, lock, etc.).  Checked by
        the argument checking code in the MPI layer, set by the OSC
        component.  Modified without locking w_lock. */
    volatile uint16_t w_mode;

    /* one sided interface */
    ompi_osc_base_module_t *w_osc_module;
};
typedef struct ompi_win_t ompi_win_t;
OMPI_DECLSPEC OBJ_CLASS_DECLARATION(ompi_win_t);

OMPI_DECLSPEC extern ompi_win_t ompi_mpi_win_null;

int ompi_win_init(void);
int ompi_win_finalize(void);

int ompi_win_create(void *base, long size, int disp_unit, 
                    ompi_communicator_t *comm, ompi_info_t *info,
                    ompi_win_t **newwin);

int ompi_win_free(ompi_win_t *win);

int ompi_win_set_name(ompi_win_t *win, char *win_name);
int ompi_win_get_name(ompi_win_t *win, char *win_name, int *length);

int ompi_win_group(ompi_win_t *win, ompi_group_t **group);

static inline int ompi_win_invalid(ompi_win_t *win) {
    if (NULL == win || 
        MPI_WIN_NULL == win ||
        (OMPI_WIN_INVALID & win->w_flags) ||
        (OMPI_WIN_FREED & win->w_flags)) {
        return true;
    } else {
        return false;
    }
}

static inline int ompi_win_peer_invalid(ompi_win_t *win, int peer) {
    if (win->w_group->grp_proc_count <= peer) return true;
    return false;
}

static inline int ompi_win_rank(ompi_win_t *win) {
    return win->w_group->grp_my_rank;
}

static inline bool ompi_win_allow_locks(ompi_win_t *win) {
    return (0 == (win->w_flags & OMPI_WIN_NO_LOCKS));
}

static inline int16_t ompi_win_get_mode(ompi_win_t *win) {
    int16_t mode = win->w_mode;
    opal_atomic_rmb();
    return mode;
}

static inline void ompi_win_set_mode(ompi_win_t *win, int16_t mode) {
    win->w_mode = mode;
    opal_atomic_wmb();
}

/* already in an access epoch */
static inline bool ompi_win_access_epoch(ompi_win_t *win) {
    int16_t mode = ompi_win_get_mode(win);
    return (OMPI_WIN_ACCESS_EPOCH & mode);
}

/* we're either already in an access epoch or can easily start one
   (stupid fence rule). Either way, it's ok to be the origin of a
   communication call. */
static inline bool ompi_win_comm_allowed(ompi_win_t *win) {
    int16_t mode = ompi_win_get_mode(win);
    return (OMPI_WIN_ACCESS_EPOCH & mode || OMPI_WIN_FENCE & mode);
}

#if defined(c_plusplus) || defined(__cplusplus)
}
#endif
#endif