/***********************************************************************
*                                                                      *
*               This software is part of the ast package               *
*           Copyright (c) 1999-2006 AT&T Knowledge Ventures            *
*                      and is licensed under the                       *
*                  Common Public License, Version 1.0                  *
*                      by AT&T Knowledge Ventures                      *
*                                                                      *
*                A copy of the License is available at                 *
*            http://www.opensource.org/licenses/cpl1.0.txt             *
*         (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9)         *
*                                                                      *
*              Information and Software Systems Research               *
*                            AT&T Research                             *
*                           Florham Park NJ                            *
*                                                                      *
*                 Glenn Fowler <gsf@research.att.com>                  *
*                                                                      *
***********************************************************************/
#include	"sftest.h"
#include	<vthread.h>
#include	<signal.h>

#define N_STR	1000

static Sfio_t*	Sf;
static char	Bigz[10*N_STR];
static char*	Str[26] =
	{	"aaaaaaaaa",
	 	"bbbbbbbbb",
	 	"ccccccccc",
	 	"ddddddddd",
	 	"eeeeeeeee",
	 	"fffffffff",
	 	"ggggggggg",
	 	"hhhhhhhhh",
	 	"iiiiiiiii",
	 	"jjjjjjjjj",
	 	"kkkkkkkkk",
	 	"lllllllll",
	 	"mmmmmmmmm",
	 	"nnnnnnnnn",
	 	"ooooooooo",
	 	"ppppppppp",
	 	"qqqqqqqqq",
	 	"rrrrrrrrr",
	 	"sssssssss",
	 	"ttttttttt",
	 	"uuuuuuuuu",
	 	"vvvvvvvvv",
	 	"wwwwwwwww",
	 	"xxxxxxxxx",
	 	"yyyyyyyyy",
	 	"zzzzzzzzz"
	};

static int	Inverted;	/* failure means success */

#if _STD_C
void* writesmall(void* arg)
#else
void* writesmall(arg)
void*	arg;
#endif
{
	char*	s;
	int	n;

	s = Str[(int)arg];
	for(n = 0; n < N_STR; ++n)
	{	if(sfputr(Sf, s, '\n') != 10)
		{	if(Inverted)
				tsuccess("sfputr failed as expected");
			else	terror("sfputr failed");
		}
	}

	return arg;
}

#if _STD_C
void* writebig(void* arg)
#else
void* writebig(arg)
void*	arg;
#endif
{
	int	r = (rand()%3) + 1;	sleep(r);

	if(sfwrite(Sf,Bigz,sizeof(Bigz)) != sizeof(Bigz))
		terror("Writing bigz");

	return arg;
}

#if __STD_C
void sighandler(int sig)
#else
void sighandler(sig)
int     sig;
#endif
{
	tmesg("\tSignal %d.\n", sig);
	TSTEXIT(0);
}

MAIN()
{
#if vt_threaded
	int		count[26];
	char*		s;
	int		i, k, n;
	Vthread_t*	thread[26];

	/* make the big z string */
	for(i = 0, s = Bigz; i < N_STR; ++i, s += 10)
		strcpy(s, "zzzzzzzzz\n");

	signal(SIGQUIT,sighandler);
	signal(SIGINT,sighandler);

do_inverted: /* get back to here when trying to make things fail */

	if(!Inverted)
		tmesg("\tTesting thread-safe streams.\n");
	else	tmesg("\tTesting unsafe streams: if hung, send INTR or QUIT.\n");

	/* spin threads writing small chunks */
	Sf = sfopen(NIL(Sfio_t*),tstfile(0), Inverted ? "w+" : "mw+");

	for(i = 0; i < 26; ++i)
	{	if(!(thread[i] = vtopen(0, 0)) )
			terror("Creating thread handle[%d]", i);
 		if(vtrun(thread[i], writesmall, (Void_t*)i) < 0)
			terror("Running thread [%d]", i);
	}

	for(i = 0; i < 26; ++i)
	{	count[i] = 0;
		vtwait(thread[i]);
	}

	if(sfseek(Sf,(Sfoff_t)0,SEEK_SET) != (Sfoff_t)0)
	{	if(Inverted)
			tsuccess("Rewinding failed as expected");
		else	terror("Rewinding");
	}

	for(n = 0;; ++n)
	{	if(!(s = sfgetr(Sf,'\n',1)) )
			break;

		i = s[0] - 'a';
		if(i < 0 || i >= 26 || sfvalue(Sf) != 10)
		{	if(Inverted)
				tsuccess("Bad data as expected");
			else	terror("Bad data s='%s' n=%d", s, n);
		}
		if(strcmp(s, Str[i]) != 0)
		{	if(Inverted)
				tsuccess("Bad str as expected");
			else	terror("Bad str s='%s' i=%d Str[i]='%s' n=%d",
					s, i, Str[i], n);
		}

		count[i] += 1;
	}

	for(i = 0; i < 26; ++i)
	{	if(count[i] != N_STR)
		{	if(Inverted)
				tsuccess("Bad count as expected");
			else	terror("Bad count[%d] = %d", i, count[i]);
		}
	}

	/* spin threads with one writing a big chunk */
	Sf = sfopen(Sf,tstfile(0), Inverted ? "w+" : "mw+");

	for(i = 0; i < 25; ++i)
	{	if(!(thread[i] = vtopen(0, 0)) )
			terror("Creating thread %d", i);
		if(vtrun(thread[i], writesmall, (void*)i) < 0)
			terror("Running thread %d", i);
	}
	sleep(1);
	if(!(thread[i] = vtopen(0,0)))
		terror("Creating big thread z");
	if(vtrun(thread[i],writebig,(void*)i) < 0)
		terror("Running big thread z");

	for(i = 0; i < 26; ++i)
	{	count[i] = 0;
		vtwait(thread[i]);
	}

	if(sfseek(Sf,(Sfoff_t)0,SEEK_SET) != (Sfoff_t)0)
	{	if(Inverted)
			tsuccess("Rewinding failed as expected");
		else	terror("Rewinding");
	}

	for(n = 0; ; ++n)
	{	if(!(s = sfgetr(Sf,'\n',1)) )
			break;

		i = s[0] - 'a';
		if(i < 0 || i >= 26 || sfvalue(Sf) != 10)
		{	if(Inverted)
				tsuccess("Bad data as expected");
			else	terror("Bad data s='%s' n=%d", s, n);
		}
		if(strcmp(s, Str[i]) != 0)
		{	if(Inverted)
				tsuccess("Bad str as expected");
			else	terror("Bad str s='%s' i=%d Str[i]='%s' n=%d",
					s, i, Str[i], n);
		}
		count[i] += 1;

		if(i == 25) /* the 'z' */
		{	for(k = 1; k < N_STR; ++k, ++n)
			{	if(!(s = sfgetr(Sf,'\n',1)) )
				{	if(Inverted)
						tsuccess("Premature eof as expected");
					else	terror("Premature eof n=%d", n);
				}
				if(strcmp(s, Str[25]) != 0)
				{	if(Inverted)
						tsuccess("Bad str as expected");
					else	terror("Bad str s='%s' n=%d", s, n);
				}
				count[i] += 1;
			}
		}
	}

	for(i = 0; i < 26; ++i)
	{	if(count[i] != N_STR)
		{	if(Inverted)
				tsuccess("Bad count as expected");
			else	terror("Bad count[%d] = %d", i, count[i]);
		}
	}

	if(!Inverted)
	{	Inverted = 1;
		goto do_inverted;
	}
	else	tmesg("\tUnsafe streams work ok on this platform!\n");

#endif

	TSTEXIT(0);
}
