/* --- Copyright University of Sussex 1996. All rights reserved. ----------
 * File:			C.win32/extern/src/ast.c
 * Purpose:			Asynchronous traps
 * Author:			Robert John Duncan, Jun 14 1994 (see revisions)
 * Documentation:
 * Related Files:
 */

#include "popcore.h"


#define AST_QUEUE_LEN 64

static POP_AST ast_queue[AST_QUEUE_LEN];
	/* Queue of pending ASTs */

static unsigned ast_queue_add, ast_queue_rem;
	/* Cursors for add/remove */

HANDLE pop_ast_queue_non_empty;
	/* Event indicating the presence of ASTs in the queue */

static CRITICAL_SECTION ast_queue_section;
	/* Gives exclusive access to the AST queue */

#ifdef __NUTC__
	/* Allow polling for X events */

static LONG ast_poll_flag;
	/* Poll flag */

static void (*ast_poll)(void);
	/* Poll function */

void pop_poll_trap(void (*poll)(void))
{
	EnterCriticalSection(&ast_queue_section);
	ast_poll_flag = 1;
	ast_poll = poll;
	/* set pop _trap */
	(void)InterlockedExchange(&_pop_signals_pending, 1);
	SetEvent(pop_ast_queue_non_empty);
	LeaveCriticalSection(&ast_queue_section);
}
#endif

void _pop_add_ast(DWORD type, DWORD data)
	/*	Add an AST to the queue
	*/
{
	unsigned i;

	EnterCriticalSection(&ast_queue_section);
	i = ast_queue_add;
	ast_queue[i].type = type;
	ast_queue[i].data = data;
	ast_queue_add = i = (i+1) % AST_QUEUE_LEN;
	if (i == ast_queue_rem)
		/* overflow -- lose oldest one */
		ast_queue_rem = (i+1) % AST_QUEUE_LEN;
	/* set pop _trap */
	(void)InterlockedExchange(&_pop_signals_pending, 1);
	SetEvent(pop_ast_queue_non_empty);
	LeaveCriticalSection(&ast_queue_section);
}

BOOL _pop_rem_ast(DWORD *type, DWORD *data)
	/*	Remove next AST from queue
	*/
{
	unsigned i;
	BOOL status;

#ifdef __NUTC__
	while (InterlockedExchange(&ast_poll_flag, 0))
		/* do this outside the C/S because it may do any amount of work */
		ast_poll();
#endif
	EnterCriticalSection(&ast_queue_section);
	i = ast_queue_rem;
	if (i == ast_queue_add)
	{
		/* queue empty -- clear pop _trap and return nothing */
		(void)InterlockedExchange(&_pop_signals_pending, 0);
		ResetEvent(pop_ast_queue_non_empty);
		status = FALSE;
	}
	else
	{
		*type = ast_queue[i].type;
		*data = ast_queue[i].data;
		ast_queue_rem = (i+1) % AST_QUEUE_LEN;
		status = TRUE;
	}
	LeaveCriticalSection(&ast_queue_section);
	return status;
}

void _pop_timer_trap(DWORD ident)
{
	_pop_add_ast(POP_AST_TIMER, ident);
}

void pop_signal(DWORD sig)
	/*	Raise a signal
	*/
{
	_pop_add_ast(POP_AST_SIGNAL, sig);
}

void pause_popintr(void)
	/*	Sleep until something arrives in the AST queue
	*/
{
	WaitForSingleObject(pop_ast_queue_non_empty, INFINITE);
}

void pop_init_ast(void)
	/*	Initialize AST handling
	*/
{
	static BOOL init_done = FALSE;

	if (init_done) return;
	InitializeCriticalSection(&ast_queue_section);
	pop_ast_queue_non_empty = CreateEvent(NULL, TRUE, FALSE, NULL);
	init_done = TRUE;
}


/* --- Revision History ---------------------------------------------------
--- Robert Duncan, May 29 1996
		Added crude polling support for compilation in the NuTCRACKER
		environment
--- Robert Duncan, Mar 29 1996
		Handling of console control events moved to "console.c"
--- Robert John Duncan, Sep 13 1995
		Change to type of _pop_signals_pending
 */
