/*
 * Copyright (c) 2002, 2004, 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.c,v 1.27 2006/03/13 19:13:19 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 <stdio.h>

#define WHAT_TERM	0
#define WHAT_CONT	1

static int Verbose = 0;

struct t_ctx_S
{
	char	*str;
	int	 fd;
	int	 what;
	int	 status;
	int	 called;
};
typedef struct t_ctx_S t_ctx_T, *t_ctx_P;

/*
**  FCT1 -- read from fd
*/

static sm_ret_T
fct1(sm_evthr_task_P tsk)
{
	t_ctx_P ctx;
	int fd, r, l;
	char buf[2];

	SM_ASSERT(tsk != NULL);
	SM_ASSERT(evthr_got_rd(tsk));
	ctx = (t_ctx_P) tsk->evthr_t_actx;
	fd = ctx->fd;
	l = ctx->status--;
	ctx->called++;
	if (Verbose > 1)
		fprintf(stderr, "fct1: called %lx '%s', fd=%d, status=%d\n",
			(long) tsk, ctx->str, fd, l);
	r = ctx->what;
	if (r > 1)
		sleep(r - 1);
	if (fd >= 0)
	{
		r = read(fd, buf, sizeof(buf));
		if (Verbose > 2)
			fprintf(stderr, "fct1: got r=%d, buf=%d\n",
				r, (int) buf[0]);
		if (r > 0)
			return EVTHR_WAITQ;
	}
	if (l <= 0)
		return EVTHR_TERM|EVTHR_DEL;
	switch (ctx->what)
	{
	  case WHAT_TERM:
		return EVTHR_TERM|EVTHR_DEL;
	  case WHAT_CONT:
	  default:
		return EVTHR_WAITQ;
	}
	/* NOTREACHED */
	return EVTHR_TERM;
}

/*
**  FCT2 -- write to fd
*/

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

	SM_ASSERT(tsk != NULL);
	SM_ASSERT(evthr_got_wr(tsk));
	ctx = (t_ctx_P) tsk->evthr_t_actx;
	ctx->called++;
	fd = ctx->fd;
	str = ctx->str;
	l = ctx->status--;
	if (Verbose > 2)
		fprintf(stderr, "fct2: called %lx '%s', fd=%d, status=%d\n",
			(long) tsk, ctx->str, fd, l);
	if (l <= 0)
		return EVTHR_DEL;
	if (fd >= 0)
	{
		r = write(fd, str, strlen(str));
		if (Verbose > 2)
			fprintf(stderr, "fct2: wrote r=%d\n", r);
		if (r > 0)
			return EVTHR_WAITQ;
	}
	switch (ctx->what)
	{
	  case WHAT_CONT:
		return EVTHR_WAITQ;
	  case WHAT_TERM:
		return EVTHR_DEL;
	}
	return EVTHR_DEL;
}

/*
**  FCTS -- deletes itself after first call
*/

static sm_ret_T
fcts(sm_evthr_task_P tsk)
{
	t_ctx_P ctx;

	SM_ASSERT(tsk != NULL);
	ctx = (t_ctx_P) tsk->evthr_t_actx;
	ctx->called++;
	if (Verbose > 1)
		fprintf(stderr, "fcts: called %lx '%s'\n",
			(long) tsk, ctx->str);
	return EVTHR_DEL;
}

static void
testev(int fd, int fdo, int what, int loops)
{
	sm_ret_T ret;
	int r;
	sm_evthr_ctx_P evthr_ctx;
	sm_evthr_task_P	task, task2, task3;
	t_ctx_T tctx, tctx2, tctx3;
	char data[16], dat2[16], dat3[16];
	struct timeval sleept;

	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);

	strlcpy(data, "String", sizeof(data));
	strlcpy(dat2, "Str2\n", sizeof(dat2));
	strlcpy(dat3, "Sleep3\n", sizeof(dat3));
	if (fd >= 0)
	{
		tctx.str = data;
		tctx.fd = fd;
		tctx.what = what;
		tctx.status = loops;
		tctx.called = 0;
		ret = evthr_task_new(evthr_ctx, &task, EVTHR_EV_RD, fd, &sleept,
					fct1, (void *) &tctx);
		SM_TEST(sm_is_success(ret));
		SM_TEST(task != NULL);
	}

	if (fdo >= 0)
	{
		tctx2.str = dat2;
		tctx2.fd = fdo;
		tctx2.what = what;
		tctx2.status = loops;
		tctx2.called = 0;
		ret = evthr_task_new(evthr_ctx, &task2, EVTHR_EV_WR, fdo, &sleept,
					fct2, (void *) &tctx2);
		SM_TEST(sm_is_success(ret));
		SM_TEST(task2 != NULL);
	}

	r = gettimeofday(&sleept, NULL);
	SM_TEST(r == 0);
	sleept.tv_usec += 1000;
/*
*/
	tctx3.str = dat3;
	tctx3.fd = fdo;
	tctx3.what = what;
	tctx3.status = loops;
	tctx3.called = 0;
	ret = evthr_task_new(evthr_ctx, &task3, EVTHR_EV_SL, -1, &sleept,
				fcts, (void *) &tctx3);
	SM_TEST(sm_is_success(ret));
	SM_TEST(task3 != 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 0
	ret = evthr_task_del(evthr_ctx, task, THR_LOCK_UNLOCK);
	SM_TEST(sm_is_success(ret));
	if (!sm_is_success(ret))
		fprintf(stderr, "evthr_task_del()=%x\n", ret);

	ret = evthr_task_del(evthr_ctx, task2, THR_LOCK_UNLOCK);
	SM_TEST(sm_is_success(ret));
	if (!sm_is_success(ret))
		fprintf(stderr, "evthr_task_del()=%x\n", ret);
#endif /* 0 */

	SM_TEST(tctx.called > 0);
	SM_TEST(tctx2.called > 0);
	SM_TEST(tctx3.called == 1);
	if (Verbose > 0)
	{
		fprintf(stderr, "fct1=%d, fct2=%d, fcts=%d\n",
			tctx.called, tctx2.called, tctx3.called);
	}
	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, fdin, fdout, what, loops;

	fdin = STDIN_FILENO;
	fdout = STDOUT_FILENO;
	what = 1;
	loops = 16;
	while ((c = getopt(argc, argv, "i:l:o:w:V")) != -1)
	{
		switch (c)
		{
		  case 'i':
			fdin = atoi(optarg);
			break;
		  case 'l':
			loops = atoi(optarg);
			break;
		  case 'o':
			fdout = 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(fdin, fdout, what, loops);
	return sm_test_end();
}


syntax highlighted by Code2HTML, v. 0.9.1