3 Copyright (c) Scott Gasch
11 This module implements an input thread dedicated to watching for
12 user input on stdin and maintaining a queue of input events for
13 consumption by the main engine thread. Currently only the main
14 engine thread (and not helper threads) may consume user input
15 because: 1. I see no performance reason to make all threads able
16 to consume input and 2. it makes the locking assumptions easier.
24 $Id: input.c 345 2007-12-02 22:56:42Z scott $
30 DLIST_ENTRY g_InputEventList;
31 volatile static ULONG g_uInputLock;
32 extern CHAR g_szInitialCommand[SMALL_STRING_LEN_CHAR];
34 #define INPUT_IS_LOCKED (g_uInputLock != 0)
36 AcquireSpinLock(&g_uInputLock); \
37 ASSERT(INPUT_IS_LOCKED);
38 #define UNLOCK_INPUT \
39 ASSERT(INPUT_IS_LOCKED); \
40 ReleaseSpinLock(&g_uInputLock);
41 volatile FLAG g_fExitProgram = FALSE;
42 volatile ULONG g_uNumInputEvents;
44 typedef struct _INPUT_EVENT
51 ULONG g_uBlockingInputLock = 0;
54 _WaitUntilTheresInputToRead(void)
56 if (g_uBlockingInputLock != (ULONG)-1)
58 SystemObtainSemaphoreResource(g_uBlockingInputLock);
59 SystemReleaseSemaphoreResource(g_uBlockingInputLock);
63 SystemDeferExecution(100);
69 _UnblockBlockedReaders(void)
71 if (g_uBlockingInputLock != (ULONG)-1)
73 SystemReleaseSemaphoreResource(g_uBlockingInputLock);
79 _BlockBlockingReaders(void)
81 if (g_uBlockingInputLock != (ULONG)-1)
83 SystemObtainSemaphoreResource(g_uBlockingInputLock);
89 PushNewInput(CHAR *buf)
94 Allocate a new input event for some user input and push it onto
109 FLAG fInQuote, fSemi;
115 pEvent = (INPUT_EVENT *)SystemAllocateMemory(sizeof(INPUT_EVENT));
118 // Note: you can specify more than one command per line using
119 // a semi-colon to separate them. If the semi-colon is quoted,
120 // though, ignore it.
130 if (FALSE == fInQuote)
141 fInQuote = FLIP(fInQuote);
152 while((FALSE == fDone) && (FALSE == fSemi));
153 pEvent->szInput = STRDUP(buf);
156 // Push it onto the queue
159 InsertTailList(&g_InputEventList, &(pEvent->links));
161 if (g_uNumInputEvents == 1)
163 _UnblockBlockedReaders();
167 while(*q && isspace(*q)) q++;
169 Trace("INPUT THREAD SAW (event %u): %s",
173 if (!STRNCMPI("quit", q, 4))
175 g_fExitProgram = TRUE;
183 while(FALSE == fDone);
188 _InitInputSystemCommonCode(void)
193 Common code for initializing the input system (called whether we
194 are spawing an input thread or running in batch mode with no input
207 setbuf(stdout, NULL);
209 setbuf(stderr, NULL);
211 g_uBlockingInputLock = SystemCreateSemaphore(1);
212 if (g_uBlockingInputLock == (ULONG)-1)
214 Bug("_InitInputSystemCommonCode: Failed to create input semaphore.\n");
216 g_uNumInputEvents = 0;
217 _BlockBlockingReaders();
218 InitializeListHead(&g_InputEventList);
220 if (g_szInitialCommand[0] != '\0')
222 Trace("INPUT SYSTEM INIT: Pushing \"%s\"\n", g_szInitialCommand);
223 PushNewInput(g_szInitialCommand);
224 g_szInitialCommand[0] = '\0';
231 InitInputSystemInBatchMode(void)
236 User specified --batch on the cmdline... so don't spawn an input
237 thread. Just process the initial command.
249 if (g_szInitialCommand[0] == '\0')
251 UtilPanic(INCONSISTENT_STATE,
252 NULL, "Batch mode specified with no initial command",
256 _InitInputSystemCommonCode();
261 char *readline(const char *prompt);
262 void add_history(const char *line);
266 InputThreadEntry(ULONG uUnused)
271 The entry point of the input thread.
283 static CHAR buf[SMALL_STRING_LEN_CHAR];
288 CHAR *pReadline = NULL;
291 while(TRUE != g_fExitProgram)
298 memset(buf, 0, sizeof(buf));
299 pReadline = readline(NULL);
300 if (pReadline != NULL)
302 strncpy(buf, pReadline, sizeof(buf) - 1);
303 add_history(pReadline);
311 if (NULL == fgets(buf, sizeof(buf), stdin))
316 if (fFailure == TRUE)
318 Trace("INPUT THREAD: got end-of-file on stdin, exiting.\n");
323 // And push it onto the queue
327 Trace("INPUT THREAD: thread terminating.\n");
332 while(!IsListEmpty(&g_InputEventList))
334 p = RemoveHeadList(&g_InputEventList);
335 pEvent = CONTAINING_STRUCT(p, INPUT_EVENT, links);
336 SystemFreeMemory(pEvent->szInput);
337 SystemFreeMemory(pEvent);
339 g_uNumInputEvents = (ULONG)-1;
345 InitInputSystemWithDedicatedThread(void)
350 Initialize the input system with a dedicated input thread.
364 _InitInputSystemCommonCode();
365 if (FALSE == SystemCreateThread(InputThreadEntry,
369 Bug("Failed to start input thread!\n");
382 Peek at what the next input event will be without consuming it.
383 Because only one thread can be consuming events from the queue at
384 a time (presumably the same thread that called this Peek function)
385 it should be safe to do this without a lock. But the overhead is
386 low so I lock here anyway.
402 if (!IsListEmpty(&g_InputEventList))
404 p = CONTAINING_STRUCT(g_InputEventList.pFlink, INPUT_EVENT, links);
418 Read the next input (and dequeue it).
435 if (!IsListEmpty(&g_InputEventList))
437 q = RemoveHeadList(&g_InputEventList);
438 p = CONTAINING_STRUCT(q, INPUT_EVENT, links);
440 if (g_uNumInputEvents == 0)
442 _BlockBlockingReaders();
453 BlockingReadInput(void)
458 Block waiting on the next input read.
474 _WaitUntilTheresInputToRead();
475 if (TRUE == g_fExitProgram)
477 if (-1 != g_uBlockingInputLock)
479 SystemDeleteSemaphore(g_uBlockingInputLock);
483 pCh = ReadNextInput();
486 if (strlen(pCh) > 0) break;
487 SystemFreeMemory(pCh);
495 NumberOfPendingInputEvents(void)
500 How many input events are pending on the queue. Note: the value
501 returned is obviously potentially out of date the instant it gets
502 sent back to the caller...
514 return(g_uNumInputEvents);