/*
 * Copyright (c) 2003-2005 Sendmail, Inc. and its suppliers.
 *	All rights reserved.
 *
 * By using this file, you agree to the terms and conditions set
 * forth in the LICENSE file which can be found at the top level of
 * the sendmail distribution.
 */

#include "sm/generic.h"
SM_RCSID("@(#)$Id: t-evthr-4.c,v 1.10 2006/03/13 19:01:27 ca Exp $")

#include "sm/assert.h"
#include "sm/error.h"
#include "sm/memops.h"
#include "sm/heap.h"
#include "sm/test.h"
#include "sm/evthr.h"
#include "sm/io.h"
#include "sm/unixsock.h"

#include <stdio.h>

/*
**  HACK
**  Needs to be cleaned up.
**  Just for testing whether a "non-sleep" task can be activated via
**  evthr_new_sl();
*/

#define WHAT_TERM	0
#define WHAT_CONT	1
#define IOBUFSIZE	32

#define REPS 5

static int Verbose = 0;
static sm_evthr_task_P	task, task2;

struct t_ctx_S
{
	sm_evthr_ctx_P	 ctx;
	char		*str;
	int		 fd;
	int		 what;
	int		 status;
	int		 called;
	int		 buflen;
	char		 buf[IOBUFSIZE];
};
typedef struct t_ctx_S t_ctx_T, *t_ctx_P;

/*
**  FCT1 -- read/write/sleep
*/

static sm_ret_T
fct1(sm_evthr_task_P tsk)
{
	t_ctx_P fctx;
	int fd, r, l;
	char *str;

	SM_ASSERT(tsk != NULL);
	fctx = (t_ctx_P) tsk->evthr_t_actx;
	fd = fctx->fd;
	l = fctx->status--;
	str = (fctx->str == NULL) ? "<NiL>" : fctx->str;
	fctx->called++;
	if (Verbose > 1)
		fprintf(stderr, "fct1: called %p '%s', fd=%d, status=%d, ev=%x\n",
			tsk, str, fd, l, evthr_rqevents(tsk));
	r = fctx->what;
	if (r > 1)
		sleep(r - 1);
	if (evthr_got_slp(tsk))
	{
		if (Verbose > 2)
			fprintf(stderr, "%ld: fct1: got sleep\n",
				(long) time(NULLT));
		return EVTHR_TERM|EVTHR_DEL;
	}
	if (fd >= 0)
	{
		if (evthr_got_rd(tsk))
		{
			sm_memzero(fctx->buf, sizeof(fctx->buf));
			r = read(fd, fctx->buf, sizeof(fctx->buf));
			fctx->buf[sizeof(fctx->buf) - 1] = '\0';
			if (Verbose > 2)
				fprintf(stderr, "fct1: got r=%d, buf=%s\n",
					r, fctx->buf);
			if (r > 0)
			{
				fctx->buflen = r;
				for (l = 0; l < r - 1; l++)
				{
					if (fctx->buf[l] == 'Q')
						return EVTHR_TERM|EVTHR_DEL;
					fctx->buf[l]++;


				}
				return EVTHR_WAITQ|evthr_r_yes(EVTHR_EV_WR)|evthr_r_no(EVTHR_EV_RD);
			}
		}
		if (evthr_got_wr(tsk))
		{
			r = write(fd, fctx->buf, fctx->buflen);
			if (Verbose > 2)
				fprintf(stderr, "fct1: wrote r=%d\n", r);
			if (r > 0)
			{
				return EVTHR_WAITQ|evthr_r_no(EVTHR_EV_WR)|evthr_r_yes(EVTHR_EV_RD);
			}
		}
	}
	if (l <= 0)
		return EVTHR_DEL;
	switch (fctx->what)
	{
	  case WHAT_TERM:
		return EVTHR_TERM|EVTHR_DEL;
	  case WHAT_CONT:
	  default:
		return EVTHR_WAITQ;
	}
	/* NOTREACHED */
	return EVTHR_TERM|EVTHR_DEL;
}

/*
**  FCT2 -- sleep
*/

static sm_ret_T
fct2(sm_evthr_task_P tsk)
{
	sm_ret_T ret;
	int i;
	t_ctx_P fctx;
	timeval_T sleept, delay, wakeup;

	SM_ASSERT(tsk != NULL);
	SM_ASSERT(evthr_got_slp(tsk));
	fctx = (t_ctx_P) tsk->evthr_t_actx;
	i = gettimeofday(&sleept, NULL);
	delay.tv_sec = 1;
	delay.tv_usec = 0;
	timeradd(&sleept, &delay, &wakeup);

	i = fctx->called++;
	if (i == 0)
	{
		if (Verbose > 2)
			fprintf(stderr, "%ld: fct2: wakeup tsk1\n",
				(long) time(NULLT));
		ret = evthr_timeval(tsk->evthr_t_ctx, &wakeup);
		SM_TEST(sm_is_success(ret));
		ret = evthr_new_sl(task, wakeup, false);
		SM_TEST(sm_is_success(ret));
	}
	if (fctx->called >= REPS)
		return EVTHR_TERM|EVTHR_DEL;
	timeradd(&sleept, &delay, &tsk->evthr_t_sleep);
	return EVTHR_SLPQ;
}

static void
testev(int what, int loops, int reps)
{
	int lfd, r;
	sm_ret_T ret;
	sm_evthr_ctx_P evthr_ctx;
	t_ctx_T tctx, tctx2;
	timeval_T sleept;
	char dat[16];
	char *sockname;

	ret = thr_init();
	SM_TEST(sm_is_success(ret));
	sm_memzero(&sleept, sizeof(sleept));
	ret = evthr_init(&evthr_ctx, 1, 6, 10);
	SM_TEST(sm_is_success(ret));
	SM_TEST(evthr_ctx != NULL);

	lfd = -1;
	strlcpy(dat, "Accept\n", sizeof(dat));

	tctx.called = 0;
	sockname = "t-evthr-4.sock";
	(void) unlink(sockname);
	lfd = unix_server_listen(sockname, 10);
	SM_TEST(lfd >= 0);
	if (lfd >= 0)
	{
		tctx.ctx = evthr_ctx;
		tctx.str = dat;
		tctx.fd = lfd;
		tctx.what = what;
		tctx.status = loops;
		ret = evthr_task_new(evthr_ctx, &task, EVTHR_EV_LI, lfd,
			&sleept, fct1, (void *) &tctx);
		SM_TEST(sm_is_success(ret));
		SM_TEST(task != NULL);

		r = gettimeofday(&sleept, NULL);
		SM_TEST(r == 0);
		sleept.tv_sec += 1;
		tctx2.called = 0;
		ret = evthr_task_new(evthr_ctx, &task2, EVTHR_EV_SL, -1,
				&sleept, fct2, (void *) &tctx2);
		SM_TEST(sm_is_success(ret));
		SM_TEST(task2 != NULL);

		ret = evthr_loop(evthr_ctx);
		SM_TEST(sm_is_success(ret));
		if (!sm_is_success(ret))
			fprintf(stderr, "evthr_loop()=%x\n", ret);

		/*
		**  we should "hold" the system before deleting tasks?
		**  deleting the tasks while they are still in use
		**  will break things.
		*/

		if (lfd >= 0)
			close(lfd);
		SM_TEST(tctx.called > 0);
		if (reps > 0)
			SM_TEST(tctx.called == reps);
		if (Verbose > 0)
		{
			fprintf(stderr, "fcts=%d\n", tctx.called);
		}
		(void) unlink(sockname);
	}
	else
	{
		fprintf(stderr, "unix_server_listen()=%d, errno=%d\n",
			lfd, errno);
	}
	ret = evthr_stop(evthr_ctx);
	SM_TEST(sm_is_success(ret));
	if (!sm_is_success(ret))
		fprintf(stderr, "evthr_stop()=%x\n", ret);
	ret = thr_stop();
	SM_TEST(sm_is_success(ret));
}

int
main(int argc, char *argv[])
{
	int c, what, loops, reps;

	what = 1;
	loops = 16;
	reps = -1;
	while ((c = getopt(argc, argv, "l:r:w:V")) != -1)
	{
		switch (c)
		{
		  case 'l':
			loops = atoi(optarg);
			break;
		  case 'r':
			reps = atoi(optarg);
			break;
		  case 'w':
			what = atoi(optarg);
			break;
		  case 'V':
			Verbose++;
			break;
#if 0
		  default:
			usage(argv[0]);
			return(1);
#endif /* 0 */
		}
	}
	sm_test_begin(argc, argv, "test evthr");
	testev(what, loops, reps);
	return sm_test_end();
}


syntax highlighted by Code2HTML, v. 0.9.1