/* ==========================================================================
 * libevnet/src/bufio-drain.c - Network server library for libevent.
 * --------------------------------------------------------------------------
 * Copyright (c) 2006  William Ahern
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to permit
 * persons to whom the Software is furnished to do so, subject to the
 * following conditions:
 *
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
 * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
 * USE OR OTHER DEALINGS IN THE SOFTWARE.
 * ==========================================================================
 */
#include <stddef.h>		/* size_t */

#include <errno.h>		/* EAGAIN */

#include <assert.h>

#include <sys/queue.h>		/* SLIST */

#include <sys/time.h>		/* struct timeval */

#include "bufio.h"		/* struct bufio_sink struct bufio_source */
#include "drain.h"

#ifndef NULL
#define NULL	0
#endif


#define BUFIO_DRAIN_FRAME_PUSH(d, f) do {				\
	(f)->dp	= &(d);							\
	SLIST_INSERT_HEAD(&(d)->stack, (f), sle);			\
} while(0)

#define BUFIO_DRAIN_FRAME_POP(d, f) do {				\
	if ((d) != 0) {							\
		assert((f) == SLIST_FIRST(&(d)->stack));		\
		SLIST_REMOVE_HEAD(&(d)->stack, sle);			\
	}								\
} while(0)

#define BUFIO_DRAIN_FRAME_OKAY(f)	((f)->dp != 0)

#define BUFIO_DRAIN_FRAME_KILL(f)	(*(f)->dp = 0)

#define BUFIO_DRAIN_FRAME_KILL_ALL(d) {						\
	struct bufio_drain_frame *f;						\
										\
	while (SLIST_END(&d->stack) != (f = SLIST_FIRST(&(d)->stack))) {	\
		SLIST_REMOVE_HEAD(&(d)->stack, sle);				\
										\
		BUFIO_DRAIN_FRAME_KILL(f);					\
	}									\
} while(0)


const struct bufio_drain_options bufio_drain_defaults;


static void bufio_drain_try(struct bufio_drain *);

static void bufio_drain_catch_source(struct bufio_source *src, enum bufio_errno ioe, void *arg) {
	bufio_drain_try(arg);

	return /* void */;
} /* bufio_drain_catch_source() */


static void bufio_drain_catch_sink(struct bufio_sink *snk, enum bufio_errno ioe, void *arg) {
	bufio_drain_try(arg);

	return /* void */;
} /* bufio_drain_catch_sink() */


static void bufio_drain_try(struct bufio_drain *d) {
	struct bufio_drain_frame f;
	size_t nbytes;
	enum bufio_errno ioe;

	BUFIO_DRAIN_FRAME_PUSH(d, &f);

	d->src->clearerr(d->src);
	d->snk->clearerr(d->snk);
	ioe	= 0;

	do {
		nbytes	= d->src->copyto(d->src, d->snk, d->opts.flags, &ioe);
	} while (nbytes > 0 && BUFIO_DRAIN_FRAME_OKAY(&f));

	if (!BUFIO_DRAIN_FRAME_OKAY(&f))
		return /* void */;

	BUFIO_DRAIN_FRAME_POP(d, &f);

	if (!BUFIO_WOULDBLOCK(ioe)) {
		if (d->cb.fn != 0)
			d->cb.fn(d, ioe, d->cb.arg);

		return /* void */;
	}

	if (ioe == d->src->lasterr(d->src)) {
		d->src->poll(d->src, &bufio_drain_catch_source, d, 0);
	} else {
		d->snk->poll(d->snk, &bufio_drain_catch_sink, d, 0);
	}

	return /* void */;
} /* bufio_drain_try() */


struct bufio_drain *bufio_drain_init(struct bufio_drain *d, const struct bufio_drain_options *opts, struct bufio_sink *snk, struct bufio_source *src) {
	static const struct bufio_drain drain_initializer;

	*d	= drain_initializer;

	d->opts	= *((opts != 0)? opts : &bufio_drain_defaults);

	d->src	= src;
	d->snk	= snk;

	return d;
} /* bufio_drain_init() */


void bufio_drain_destroy(struct bufio_drain *d) {
	BUFIO_DRAIN_FRAME_KILL_ALL(d);

	bufio_drain_stop(d);

	return /* void */;
} /* bufio_drain_destroy() */


void bufio_drain_start(struct bufio_drain *d, void (*cb)(struct bufio_drain *, enum bufio_errno, void *)) {
	bufio_drain_try(d);

	return /* void */;
} /* bufio_drain_start() */


void bufio_drain_stop(struct bufio_drain *d) {
	d->src->cancel(d->src, 0);
	d->snk->cancel(d->snk, 0);

	return /* void */;
} /* bufio_drain_stop() */




syntax highlighted by Code2HTML, v. 0.9.1