From 3fd43cd5fcb22bb65bf2a92a25d95d801b11c9e0 Mon Sep 17 00:00:00 2001 From: Scott Gasch Date: Wed, 1 Jun 2016 18:58:58 -0700 Subject: Initial checkin for typhoon chess engine. --- src/input.c | 515 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 515 insertions(+) create mode 100755 src/input.c (limited to 'src/input.c') diff --git a/src/input.c b/src/input.c new file mode 100755 index 0000000..9f829f7 --- /dev/null +++ b/src/input.c @@ -0,0 +1,515 @@ +/** + +Copyright (c) Scott Gasch + +Module Name: + + input.c + +Abstract: + + This module implements an input thread dedicated to watching for + user input on stdin and maintaining a queue of input events for + consumption by the main engine thread. Currently only the main + engine thread (and not helper threads) may consume user input + because: 1. I see no performance reason to make all threads able + to consume input and 2. it makes the locking assumptions easier. + +Author: + + Scott Gasch (scott.gasch@gmail.com) 14 May 2004 + +Revision History: + + $Id: input.c 345 2007-12-02 22:56:42Z scott $ + +**/ + +#include "chess.h" + +DLIST_ENTRY g_InputEventList; +volatile static ULONG g_uInputLock; +extern CHAR g_szInitialCommand[SMALL_STRING_LEN_CHAR]; + +#define INPUT_IS_LOCKED (g_uInputLock != 0) +#define LOCK_INPUT \ + AcquireSpinLock(&g_uInputLock); \ + ASSERT(INPUT_IS_LOCKED); +#define UNLOCK_INPUT \ + ASSERT(INPUT_IS_LOCKED); \ + ReleaseSpinLock(&g_uInputLock); +volatile FLAG g_fExitProgram = FALSE; +volatile ULONG g_uNumInputEvents; + +typedef struct _INPUT_EVENT +{ + DLIST_ENTRY links; + CHAR *szInput; +} +INPUT_EVENT; + +ULONG g_uBlockingInputLock = 0; + +static void +_WaitUntilTheresInputToRead(void) +{ + if (g_uBlockingInputLock != (ULONG)-1) + { + SystemObtainSemaphoreResource(g_uBlockingInputLock); + SystemReleaseSemaphoreResource(g_uBlockingInputLock); + } + else + { + SystemDeferExecution(100); + } +} + + +static void +_UnblockBlockedReaders(void) +{ + if (g_uBlockingInputLock != (ULONG)-1) + { + SystemReleaseSemaphoreResource(g_uBlockingInputLock); + } +} + + +static void +_BlockBlockingReaders(void) +{ + if (g_uBlockingInputLock != (ULONG)-1) + { + SystemObtainSemaphoreResource(g_uBlockingInputLock); + } +} + + +void +PushNewInput(CHAR *buf) +/** + +Routine description: + + Allocate a new input event for some user input and push it onto + the queue. + +Parameters: + + CHAR *buf + +Return value: + + void + +**/ +{ + INPUT_EVENT *pEvent; + FLAG fDone = FALSE; + FLAG fInQuote, fSemi; + CHAR *p; + CHAR *q; + + do + { + pEvent = (INPUT_EVENT *)SystemAllocateMemory(sizeof(INPUT_EVENT)); + + // + // Note: you can specify more than one command per line using + // a semi-colon to separate them. If the semi-colon is quoted, + // though, ignore it. + // + fInQuote = FALSE; + fSemi = FALSE; + p = buf; + do + { + switch(*p) + { + case ';': + if (FALSE == fInQuote) + { + *p = '\0'; + fSemi = TRUE; + } + else + { + p++; + } + break; + case '"': + fInQuote = FLIP(fInQuote); + p++; + break; + case '\0': + fDone = TRUE; + break; + default: + p++; + break; + } + } + while((FALSE == fDone) && (FALSE == fSemi)); + pEvent->szInput = STRDUP(buf); + + // + // Push it onto the queue + // + LOCK_INPUT; + InsertTailList(&g_InputEventList, &(pEvent->links)); + g_uNumInputEvents++; + if (g_uNumInputEvents == 1) + { + _UnblockBlockedReaders(); + } + q = pEvent->szInput; + ASSERT(q); + while(*q && isspace(*q)) q++; +#ifdef DEBUG + Trace("INPUT THREAD SAW (event %u): %s", + g_uNumInputEvents, + pEvent->szInput); +#endif + if (!STRNCMPI("quit", q, 4)) + { + g_fExitProgram = TRUE; + UNLOCK_INPUT; + return; + } + UNLOCK_INPUT; + + buf = (p + 1); + } + while(FALSE == fDone); +} + + +static void +_InitInputSystemCommonCode(void) +/** + +Routine description: + + Common code for initializing the input system (called whether we + are spawing an input thread or running in batch mode with no input + thread). + +Parameters: + + void + +Return value: + + void + +**/ +{ + setbuf(stdout, NULL); + setbuf(stdin, NULL); + setbuf(stderr, NULL); + + g_uBlockingInputLock = SystemCreateSemaphore(1); + if (g_uBlockingInputLock == (ULONG)-1) + { + Bug("_InitInputSystemCommonCode: Failed to create input semaphore.\n"); + } + g_uNumInputEvents = 0; + _BlockBlockingReaders(); + InitializeListHead(&g_InputEventList); + g_uInputLock = 0; + if (g_szInitialCommand[0] != '\0') + { + Trace("INPUT SYSTEM INIT: Pushing \"%s\"\n", g_szInitialCommand); + PushNewInput(g_szInitialCommand); + g_szInitialCommand[0] = '\0'; + } +} + + + +void +InitInputSystemInBatchMode(void) +/** + +Routine description: + + User specified --batch on the cmdline... so don't spawn an input + thread. Just process the initial command. + +Parameters: + + void + +Return value: + + void + +**/ +{ + if (g_szInitialCommand[0] == '\0') + { + UtilPanic(INCONSISTENT_STATE, + NULL, "Batch mode specified with no initial command", + NULL, NULL, + __FILE__, __LINE__); + } + _InitInputSystemCommonCode(); +} + + +#ifdef USE_READLINE +char *readline(const char *prompt); +void add_history(const char *line); +#endif + +ULONG +InputThreadEntry(ULONG uUnused) +/** + +Routine description: + + The entry point of the input thread. + +Parameters: + + ULONG uUnused + +Return value: + + ULONG + +**/ +{ + static CHAR buf[SMALL_STRING_LEN_CHAR]; + INPUT_EVENT *pEvent; + DLIST_ENTRY *p; + FLAG fFailure; +#ifdef USE_READLINE + CHAR *pReadline = NULL; +#endif + + while(TRUE != g_fExitProgram) + { + // + // Get another input + // + fFailure = FALSE; +#ifdef USE_READLINE + memset(buf, 0, sizeof(buf)); + pReadline = readline(NULL); + if (pReadline != NULL) + { + strncpy(buf, pReadline, sizeof(buf) - 1); + add_history(pReadline); + free(pReadline); + } + else + { + fFailure = TRUE; + } +#else + if (NULL == fgets(buf, sizeof(buf), stdin)) + { + fFailure = TRUE; + } +#endif + if (fFailure == TRUE) + { + Trace("INPUT THREAD: got end-of-file on stdin, exiting.\n"); + break; + } + + // + // And push it onto the queue + // + PushNewInput(buf); + } + Trace("INPUT THREAD: thread terminating.\n"); + + // + // Free the queue + // + while(!IsListEmpty(&g_InputEventList)) + { + p = RemoveHeadList(&g_InputEventList); + pEvent = CONTAINING_STRUCT(p, INPUT_EVENT, links); + SystemFreeMemory(pEvent->szInput); + SystemFreeMemory(pEvent); + } + g_uNumInputEvents = (ULONG)-1; + return(0); +} + + +ULONG +InitInputSystemWithDedicatedThread(void) +/** + +Routine description: + + Initialize the input system with a dedicated input thread. + +Parameters: + + void + +Return value: + + ULONG + +**/ +{ + ULONG uHandle = -1; + + _InitInputSystemCommonCode(); + if (FALSE == SystemCreateThread(InputThreadEntry, + 0, + &uHandle)) + { + Bug("Failed to start input thread!\n"); + uHandle = -1; + } + return(uHandle); +} + + +CHAR * +PeekNextInput(void) +/** + +Routine description: + + Peek at what the next input event will be without consuming it. + Because only one thread can be consuming events from the queue at + a time (presumably the same thread that called this Peek function) + it should be safe to do this without a lock. But the overhead is + low so I lock here anyway. + +Parameters: + + void + +Return value: + + CHAR * + +**/ +{ + INPUT_EVENT *p; + char *q = NULL; + + LOCK_INPUT; + if (!IsListEmpty(&g_InputEventList)) + { + p = CONTAINING_STRUCT(g_InputEventList.pFlink, INPUT_EVENT, links); + q = p->szInput; + } + UNLOCK_INPUT; + return(q); +} + + +CHAR * +ReadNextInput(void) +/** + +Routine description: + + Read the next input (and dequeue it). + +Parameters: + + void + +Return value: + + CHAR * + +**/ +{ + CHAR *pRet = NULL; + INPUT_EVENT *p; + DLIST_ENTRY *q; + + LOCK_INPUT; + if (!IsListEmpty(&g_InputEventList)) + { + q = RemoveHeadList(&g_InputEventList); + p = CONTAINING_STRUCT(q, INPUT_EVENT, links); + g_uNumInputEvents--; + if (g_uNumInputEvents == 0) + { + _BlockBlockingReaders(); + } + pRet = p->szInput; + SystemFreeMemory(p); + } + UNLOCK_INPUT; + return(pRet); +} + + +CHAR * +BlockingReadInput(void) +/** + +Routine description: + + Block waiting on the next input read. + +Parameters: + + void + +Return value: + + CHAR + +**/ +{ + CHAR *pCh = NULL; + + do + { + _WaitUntilTheresInputToRead(); + if (TRUE == g_fExitProgram) + { + if (-1 != g_uBlockingInputLock) + { + SystemDeleteSemaphore(g_uBlockingInputLock); + } + return(NULL); + } + pCh = ReadNextInput(); + if (NULL != pCh) + { + if (strlen(pCh) > 0) break; + SystemFreeMemory(pCh); + } + } + while(1); + return(pCh); +} + +ULONG +NumberOfPendingInputEvents(void) +/** + +Routine description: + + How many input events are pending on the queue. Note: the value + returned is obviously potentially out of date the instant it gets + sent back to the caller... + +Parameters: + + void + +Return value: + + ULONG + +**/ +{ + return(g_uNumInputEvents); +} -- cgit v1.3