/*
 *
 * Copyright 1998-1999, University of Notre Dame.
 * Authors: Jeffrey M. Squyres, Kinis L. Meyer with M. D. McNally 
 *          and Andrew Lumsdaine
 *
 * This file is part of the Notre Dame LAM implementation of MPI.
 *
 * You should have received a copy of the License Agreement for the
 * Notre Dame LAM implementation of MPI along with the software; see
 * the file LICENSE.  If not, contact Office of Research, University
 * of Notre Dame, Notre Dame, IN 46556.
 *
 * Permission to modify the code and to distribute modified code is
 * granted, provided the text of this NOTICE is retained, a notice that
 * the code was modified is included with the above COPYRIGHT NOTICE and
 * with the COPYRIGHT NOTICE in the LICENSE file, and that the LICENSE
 * file is distributed with the modified code.
 *
 * LICENSOR MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED.
 * By way of example, but not limitation, Licensor MAKES NO
 * REPRESENTATIONS OR WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY
 * PARTICULAR PURPOSE OR THAT THE USE OF THE LICENSED SOFTWARE COMPONENTS
 * OR DOCUMENTATION WILL NOT INFRINGE ANY PATENTS, COPYRIGHTS, TRADEMARKS
 * OR OTHER RIGHTS.  
 *
 * Additional copyrights may follow.
 *
 *
 *	Ohio Trollius
 *	Copyright 1997 The Ohio State University
 *	RBD/NJN
 *
 *	$Id: finalize.c,v 6.11 1999/11/12 05:19:29 jsquyres Exp $
 *
 *	Function:	- terminate the MPI session
 *	Returns:	- MPI_SUCCESS or error code
 */

#include <lam_config.h>

#include <stdlib.h>

#include <blktype.h>
#include <mpi.h>
#include <MPISYSF.h>
#include <mpisys.h>
#include <rpisys.h>
#include <terror.h>
#include <trreq.h>
#include <typical.h>
#include <t_types.h>
#if LAM_WANT_IMPI
#include <impi.h>
#endif

/*
 * external functions
 */
extern void		_cio_cleanup();
extern void		lam_kexit();

/*
 * local functions
 */
static int		waitall();
static void		free_comm();
static void		free_dtype();
static void		free_env();
static void		free_errhdl();
static void		free_f77();
static void		free_op();
static void		free_rdtype();

int
MPI_Finalize()

{
	int		err;			/* error code */
	
	lam_initerr();
	lam_setfunc(BLKMPIFINAL);

	LAM_TRACE(lam_tr_cffstart(BLKMPIFINAL));
/*
 * Block until all pending requests are done.
 */
	err = waitall();
	if (err != MPI_SUCCESS) {
		return(lam_errfunc(MPI_COMM_WORLD, BLKMPIFINAL, err));
	}
/*
 * IMPI finalization
 */
#if LAM_WANT_IMPI
	if (_kio.ki_rtf & RTF_IMPI) {
	  if (IMPI_Finalize() != 0)
	    return(lam_errfunc(MPI_COMM_WORLD, BLKMPIFINAL, err));
	}
#endif
/*
 * RPI specific finalization.
 */
	if (RPI_SPLIT(_rpi_lamd_finalize, _rpi_c2c_finalize, (0))) {
		return(lam_errfunc(MPI_COMM_WORLD, BLKMPIFINAL,
					lam_mkerr(MPI_ERR_INTERN, errno)));
	}
/*
 * Cleanup.
 */
	lam_nukepublished();
	lam_ports_free();
	lam_free_onesided();

	free_env();
	free_rdtype();
	free_comm();
	free_op();
	free_dtype();
	free_errhdl();
	free_f77();

	LAM_TRACE(lam_tr_cffend(BLKMPIFINAL, -1, 0, 0, 0));

	lam_tr_off();
	lam_nuketrace();
 	lam_nukeprocs();

	if (_kio.ki_rtf & RTF_MPISIGS) {
		lam_mpi_reset_sighandlers();
	}

	lam_flinit = 0;
	lam_flfinal = 1;
	lam_kexit(0);

	lam_resetfunc(BLKMPIFINAL);
	lam_nukefunc();

	return(MPI_SUCCESS);
}


/*
 *	waitall
 *
 *	Function:	- wait till all pending requests are done
 *			- window handler requests are ingored
 *	Returns:	- MPI_SUCCESS or error code
 */
static int
waitall()

{
	int		err;			/* error code */
	int		nreqs;			/* # pending requests */
	MPI_Request	*reqs;			/* request array */
	int		i;

	nreqs = _mpi_req_getn();
	if (nreqs == 0) return(MPI_SUCCESS);

	reqs = (MPI_Request *) malloc((unsigned) nreqs * sizeof(MPI_Request));
	if (reqs == 0) {
		return(lam_mkerr(MPI_ERR_OTHER, errno));
	}

	_mpi_req_get(nreqs, reqs);
/*
 * Ignore window handler requests.
 */
	for (i = 0; i < nreqs; i++) {
	    if (reqs[i]->rq_hdlr == lam_osd_handler) {
		reqs[i] = MPI_REQUEST_NULL;
	    }
	}

	err = MPI_Waitall(nreqs, reqs, MPI_STATUSES_IGNORE);

	free((char *) reqs);
	return(err);
}

/*
 *	free_errhdl
 *
 *	Function:	- deallocate pre-defined error handles
 */
static void
free_errhdl()

{
/*
 * Nothing to free.
 */
}

/*
 *	free_comm
 *
 *	Function:	- deallocate pre-defined communicators
 */
static void
free_comm()

{
        MPI_Errhandler err;
/*
 * Free the "parent" communicator if any.
 */
	if (lam_comm_parent != MPI_COMM_NULL) {
		lam_comm_free(lam_comm_parent);
	}
/*
 * Free the "world" communicator.
 * Free the error handler, if a user error handler was defined on it
 * (otherwise, we have a memory leak)
 */
	err = MPI_COMM_WORLD->c_errhdl;
	if (err != 0 && err != MPI_ERRHANDLER_NULL && 
	    err != MPI_ERRORS_ARE_FATAL && err != MPI_ERRORS_RETURN)
	  MPI_Errhandler_free(&MPI_COMM_WORLD->c_errhdl);
	lam_rmcid(MPI_COMM_WORLD->c_contextid);
	free((char *) MPI_COMM_WORLD->c_group);
	ah_free(MPI_COMM_WORLD->c_keys);

/*
 * Free the "self" communicator.
 * Free the error handler, if a user error handler was defined on it
 * (otherwise, we have a memory leak)
 */
	err = MPI_COMM_SELF->c_errhdl;
	if (err != 0 && err != MPI_ERRHANDLER_NULL && 
	    err != MPI_ERRORS_ARE_FATAL && err != MPI_ERRORS_RETURN)
	  MPI_Errhandler_free(&MPI_COMM_WORLD->c_errhdl);
	lam_rmcid(MPI_COMM_SELF->c_contextid);
	free((char *) MPI_COMM_SELF->c_group);
	if (MPI_COMM_SELF->c_keys) {
		ah_free(MPI_COMM_SELF->c_keys);
	}

	al_free(lam_comms);

	lam_nukecids();
}

/*
 *	free_dtype
 *
 *	Function:	- deallocate basic (intrinsic) datatypes
 */
static void
free_dtype()

{
/*
 * Nothing to free.
 */
}

/*
 *	free_rdtype
 *
 *	Function:	- deallocate reduction datatypes
 */
static void
free_rdtype()

{
	lam_type_free(MPI_2INT);
	lam_type_free(MPI_FLOAT_INT);
	lam_type_free(MPI_DOUBLE_INT);
	lam_type_free(MPI_LONG_DOUBLE_INT);
	lam_type_free(MPI_LONG_INT);
	lam_type_free(MPI_SHORT_INT);
	lam_type_free(MPI_F_2INTEGER);
	lam_type_free(MPI_F_2REAL);
	lam_type_free(MPI_F_2DOUBLE_PRECISION);
}

/*
 *	free_op
 *
 *	Function:	- deallocate intrinsic reduction operations
 */
static void
free_op()

{
/*
 * Nothing to free.
 */
}

/*
 *	free_env
 *
 *	Function:	- deallocate environment attributes
 */
static void
free_env()

{
	MPI_Attr_delete(MPI_COMM_WORLD, MPI_TAG_UB);
	MPI_Attr_delete(MPI_COMM_WORLD, MPI_HOST);
	MPI_Attr_delete(MPI_COMM_WORLD, MPI_IO);
	MPI_Attr_delete(MPI_COMM_WORLD, MPI_WTIME_IS_GLOBAL);
	MPI_Attr_delete(MPI_COMM_WORLD, MPI_UNIVERSE_SIZE);
	if (lam_appnum >= 0) {
	    MPI_Attr_delete(MPI_COMM_WORLD, MPI_APPNUM);
	}

	lam_nukekeys();
}

/*
 *	free_f77
 *
 *	Function:	- deallocate f77 support structures
 */
static void
free_f77()

{
	free((char *) lam_F_handles);
}
