/*
 * sio_async_sg.c
 *
 * SIO Asynchronous Scatter/Gather I/O API Routines
 *
 * Authors: Khalil Amiri, CMU SCS/ECE, July 18 1997
 *          Sean Levy, CMU SCS, July 1999
 */
/*
 * Copyright (c) of Carnegie Mellon University, 1996,1997,1998,1999.
 *
 * Permission to reproduce, use, and prepare derivative works of
 * this software for internal use is granted provided the copyright
 * and "No Warranty" statements are included with all reproductions
 * and derivative works. This software may also be redistributed
 * without charge provided that the copyright and "No Warranty"
 * statements are included in all redistributions.
 *
 * NO WARRANTY. THIS SOFTWARE IS FURNISHED ON AN "AS IS" BASIS.
 * CARNEGIE MELLON UNIVERSITY MAKES NO WARRANTIES OF ANY KIND, EITHER
 * EXPRESSED OR IMPLIED AS TO THE MATTER INCLUDING, BUT NOT LIMITED
 * TO: WARRANTY OF FITNESS FOR PURPOSE OR MERCHANTABILITY, EXCLUSIVITY
 * OF RESULTS OR RESULTS OBTAINED FROM USE OF THIS SOFTWARE. CARNEGIE
 * MELLON UNIVERSITY DOES NOT MAKE ANY WARRANTY OF ANY KIND WITH RESPECT
 * TO FREEDOM FROM PATENT, TRADEMARK, OR COPYRIGHT INFRINGEMENT.
 */

#include "_sio_internal.h"

sio_return_t
sio_async_sg_read(int extfd, const sio_file_io_list_t *fl, sio_count_t fllen,
                  const sio_mem_io_list_t *ml, sio_count_t mllen,
                  sio_async_handle_t *handlep)
{
	struct _sio_fdesc *intfd;
	struct _sio_iodesc *iodesc;
	sio_return_t rv;
	nasd_thread_t iothread;
  int rc;

	_sio_init();

	/* internalize file desc. structure (refcnt++) */
	intfd = _sio_exttable_internalize(_sio_exttable_fdescs, extfd);
#ifdef EXTTABLE_DEBUG
	printf("%d internalized to %d\n", extfd, intfd);
#endif
	if (intfd == NULL)
		return (SIO_ERR_INVALID_DESCRIPTOR);

	rv = SIO_SUCCESS;
	NASD_LOCK_MUTEX(_sio_async_io_count_mutex);
	if (_sio_async_io_count >= SIO_MAX_ASYNC_OUTSTANDING)
		rv = SIO_ERR_MAX_ASYNC_OUTSTANDING_EXCEEDED;
	else
		_sio_async_io_count++;
	NASD_UNLOCK_MUTEX(_sio_async_io_count_mutex);
	if (rv != SIO_SUCCESS)
		goto bad1;

	/* create an I/O description for the I/O (refcnt = 1) */
	rv = _sio_iodesc_create(intfd, _SIO_IOD_READ, fl, fllen, ml, mllen,
                          &iodesc);
	if (rv != SIO_SUCCESS)
		goto bad2;

	/* register I/O description as external (refcnt++) */
	_sio_exttable_register(_sio_exttable_asynchandles, iodesc);

	/* fork a thread to do the I/O */
	_sio_rcobj_ref(iodesc); /* rc++ */
  rc = nasd_thread_create(&iothread, _sio_iodesc_doio, iodesc);
  if (rc) {
		rv = SIO_ERR_VEND_OSERR_FIRST + errno;
		goto bad3;
	}
	/*pthread_detach(&iothread);*/ /* XXX */

	*handlep = _sio_exttable_externalize(_sio_exttable_asynchandles,
                                       iodesc);				/* refcnt-- */
#ifdef EXTTABLE_DEBUG
	printf("%d (iodesc) externalized to %d\n", iodesc, *handlep);
#endif
	_sio_rcobj_unref(intfd);		/* refcnt-- */
	return (SIO_SUCCESS);

bad3:
	_sio_exttable_unregister(_sio_exttable_asynchandles, iodesc); /*rc--*/
	_sio_iodesc_destroy(iodesc);		/* kill desc (refcnt = 1) */
bad2:
  NASD_LOCK_MUTEX(_sio_async_io_count_mutex);
	_sio_async_io_count--;
	NASD_UNLOCK_MUTEX(_sio_async_io_count_mutex);
bad1:
	_sio_rcobj_unref(intfd);		/* refcnt-- */
	return (rv);
}

sio_return_t
sio_async_sg_write(int extfd, const sio_file_io_list_t *fl, sio_count_t fllen,
                   const sio_mem_io_list_t *ml, sio_count_t mllen,
                   sio_async_handle_t *handlep)
{
	struct _sio_fdesc *intfd;
	struct _sio_iodesc *iodesc;
	sio_return_t rv;
	nasd_thread_t iothread;
  int rc;

	_sio_init();

	/* internalize file desc. structure (refcnt++) */
	intfd = _sio_exttable_internalize(_sio_exttable_fdescs, extfd);
	if (intfd == NULL)
		return (SIO_ERR_INVALID_DESCRIPTOR);

	rv = SIO_SUCCESS;
	NASD_LOCK_MUTEX(_sio_async_io_count_mutex);
	if (_sio_async_io_count >= SIO_MAX_ASYNC_OUTSTANDING)
		rv = SIO_ERR_MAX_ASYNC_OUTSTANDING_EXCEEDED;
	else
		_sio_async_io_count++;
	NASD_UNLOCK_MUTEX(_sio_async_io_count_mutex);
	if (rv != SIO_SUCCESS)
		goto bad1;

	/* create an I/O description for the I/O (refcnt = 1) */
	rv = _sio_iodesc_create(intfd, _SIO_IOD_WRITE, fl, fllen, ml, mllen,
                          &iodesc);
	if (rv != SIO_SUCCESS)
		goto bad2;

	/* register I/O description as external (refcnt++) */
	_sio_exttable_register(_sio_exttable_asynchandles, iodesc);

	/* fork a thread to do the I/O */
	_sio_rcobj_ref(iodesc); /* rc++ */
  rc = nasd_thread_create(&iothread, _sio_iodesc_doio, iodesc);
  if (rc) {
		rv = SIO_ERR_VEND_OSERR_FIRST + errno;
		goto bad3;
	}
	/*pthread_detach(&iothread);*/ /* XXX */

	*handlep = _sio_exttable_externalize(_sio_exttable_asynchandles,
                                       iodesc);				/* refcnt-- */
	_sio_rcobj_unref(intfd);		/* refcnt-- */
	return (SIO_SUCCESS);

bad3:
	_sio_exttable_unregister(_sio_exttable_asynchandles, iodesc); /*rc--*/
	_sio_iodesc_destroy(iodesc);		/* kill desc (refcnt = 1) */
bad2:
  NASD_LOCK_MUTEX(_sio_async_io_count_mutex);
	_sio_async_io_count--;
	NASD_UNLOCK_MUTEX(_sio_async_io_count_mutex);
bad1:
	_sio_rcobj_unref(intfd);		/* refcnt-- */
	return (rv);
}

sio_return_t
sio_async_status_any(sio_async_handle_t *handlel, sio_count_t count,
                     sio_count_t *index, sio_async_status_t *status,
                     sio_async_flags_t flags)
{
	struct _sio_iodesc *iodesc;
	sio_return_t rv;
	sio_count_t i;
	int done;

	_sio_init(); 
	if ((flags & (SIO_ASYNC_BLOCKING | SIO_ASYNC_NONBLOCKING)) == 0 ||
	    (flags & (SIO_ASYNC_BLOCKING | SIO_ASYNC_NONBLOCKING)) ==
      (SIO_ASYNC_BLOCKING | SIO_ASYNC_NONBLOCKING))
		return (SIO_ERR_VEND_INVALID_FLAGS);

	rv = SIO_ERR_IO_IN_PROGRESS;
	if (flags & SIO_ASYNC_NONBLOCKING) {
		for (done = 0, i = 0; !done && i < count; i++) {
			if (handlel[i] == SIO_ASYNC_DUMMY_HANDLE)
				continue;
	
			iodesc = _sio_exttable_internalize(_sio_exttable_asynchandles,
                                         handlel[i]);
			if (iodesc == NULL) {
				rv = SIO_ERR_INVALID_HANDLE;
				break;
			}
	
			NASD_LOCK_MUTEX(iodesc->iod_rcobj.rco_mutex);
			if (iodesc->iod_flags & _SIO_IOD_STATUSWAIT) {
				done = 1;
				rv = SIO_ERR_VEND_ASYNC_ALREADY_WAITING;
				goto loop;
			}
			if (iodesc->iod_flags & _SIO_IOD_GOINGAWAY) {
				done = 1;
				rv = SIO_ERR_INVALID_HANDLE;
				goto loop;
			}
			if (iodesc->iod_flags & _SIO_IOD_DONE) {
				rv = SIO_SUCCESS;
				break;
			}
    loop:
			NASD_UNLOCK_MUTEX(iodesc->iod_rcobj.rco_mutex);
			_sio_rcobj_unref(iodesc);
		}
		*index = i;
		if (rv == SIO_SUCCESS) {
			iodesc->iod_flags |= _SIO_IOD_GOINGAWAY;
			NASD_UNLOCK_MUTEX(iodesc->iod_rcobj.rco_mutex);

			_sio_exttable_unregister(_sio_exttable_asynchandles,
                               iodesc); /*rc--*/
			_sio_rcobj_unref(iodesc); /* rc-- */
			/* collect status and destroy the iodesc */
			_sio_iodesc_collect_status(iodesc, &status->count,
                                 &status->status);
		}
	} else {
		/* XXX */
		rv = SIO_ERR_OP_UNSUPPORTED;
	}

	return (rv);
}

sio_return_t
sio_async_cancel_all(sio_async_handle_t *handlel, sio_count_t count)
{
	struct _sio_iodesc *iodesc;
	sio_count_t i;

	_sio_init();
	for (i = 0; i < count; i++) {
		iodesc = _sio_exttable_internalize(_sio_exttable_asynchandles,
		    handlel[i]);
		if (iodesc == NULL)
			return (SIO_ERR_INVALID_HANDLE);

		NASD_LOCK_MUTEX(iodesc->iod_rcobj.rco_mutex);
		iodesc->iod_flags |= _SIO_IOD_CANCELED;
		NASD_UNLOCK_MUTEX(iodesc->iod_rcobj.rco_mutex);

		_sio_rcobj_unref(iodesc);
	}

	return (SIO_SUCCESS);
}

/* Local Variables:  */
/* indent-tabs-mode: nil */
/* tab-width: 2 */
/* End: */
