File: win32InputHandler.cpp

package info (click to toggle)
norm 1.5.9%2Bdfsg-4
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 9,680 kB
  • sloc: cpp: 123,494; xml: 7,536; tcl: 5,460; makefile: 3,442; python: 1,898; java: 1,750; ansic: 642; sh: 21; csh: 8
file content (243 lines) | stat: -rw-r--r-- 6,987 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
 

#include <windows.h>
#include <stdio.h>	 // for perror()
#include <string.h>  // for memset()
#include <process.h>  // for _beginthreadex(), _endthreadex()

// This class creates a child thread to read STDIN into a buffer.
// An event is set that can be monitored by the parent to know
// when input is available.  Mutexes are used to regulate the
// thread's reading (i.e., flow control).
class Win32InputHandler
{
	public:
		Win32InputHandler();
		~Win32InputHandler();

		bool Open(int buflen = 4096);
		void Close();

		// The event handle returned here can be used in
		// a call like WaitForSignalObject() to get notified
		// when there is data to be read.
		HANDLE GetEventHandle() const
			{return input_event;}

		// This does not block and the input_event
		// handle is a cue for when to call it
		// Returns number of bytes copied into "buffer"
		// (-1 is returned upon input error (input closed))
		int ReadData(char* buffer, int numBytes);

	private:
		static unsigned int __stdcall DoThreadOpen(void* param);
		unsigned int Run();

		HANDLE				input_handle;
		HANDLE				input_event;
		HANDLE				thread_handle;
		bool				input_ready;
		char*				input_buffer;
		int					input_buflen;
		int					input_index;
		int					input_outdex;
		CONDITION_VARIABLE	buffer_vacancy;
		CRITICAL_SECTION	buffer_lock;	
		bool				is_running;
		int					input_length;
		bool				input_error;
		
};  // end class Win32InputHandler

Win32InputHandler::Win32InputHandler()
	: input_handle(NULL), input_event(NULL), thread_handle(NULL),
	  input_ready(false), input_buffer(NULL), input_buflen(0),
	  input_index(0), input_outdex(0), is_running(false),
	  input_length(0), input_error(false)
{

}

Win32InputHandler::~Win32InputHandler()
{
	if (is_running) Close();
}

bool Win32InputHandler::Open(int buflen)
{
	if (is_running) Close();
	if (NULL != input_buffer) 
    {
        input_buffer = NULL;
        delete[] input_buffer;
    }
	input_buflen = input_index = input_outdex = input_length = 0;
	input_error = false;
	if (NULL != input_event) CloseHandle(input_event);

	// Get the STDIN handle whatever it may be
	input_handle = GetStdHandle(STD_INPUT_HANDLE);
	/*if (FILE_TYPE_CHAR == GetFileType(input_handle))
	{
		// The STDIN is a console
		DWORD consoleMode;
		bool result = GetConsoleMode(input_handle, &consoleMode);
		consoleMode &= ~(ENABLE_LINE_INPUT | ENABLE_MOUSE_INPUT | ENABLE_WINDOW_INPUT);
		SetConsoleMode(input_handle, consoleMode);
	}*/

	// Create an event handle, non-signaled initially
	input_event = CreateEvent(NULL, TRUE, FALSE, NULL);
	if (NULL == input_event)
	{
		perror("Win32InputHandler::Open() CreateEvent() error");
		return false;
	}
	if (NULL == (input_buffer = new char[buflen]))
	{
		perror("Win32InputHandler::Open() new input_buffer error");
		CloseHandle(input_event);
		input_event = NULL;
		return false;
	}
	input_buflen = buflen;
	InitializeConditionVariable(&buffer_vacancy);
	InitializeCriticalSection(&buffer_lock);
	is_running = true;
	if (0 == (thread_handle = (HANDLE)_beginthreadex(NULL, 0, DoThreadOpen, this, 0, NULL)))
	{
		perror("Win32InputHandler::Open() _beginthreadex() error");
		DeleteCriticalSection(&buffer_lock);
		delete[] input_buffer;
		input_buffer = NULL;
		CloseHandle(input_event);
		input_event = NULL;
		return false;
	}
	return true;
}  // end Win32InputHandler::Open()

void  Win32InputHandler::Close()
{
	EnterCriticalSection(&buffer_lock);
	is_running = false;
	WakeConditionVariable(&buffer_vacancy);  // in case it was full
	LeaveCriticalSection(&buffer_lock);
	WaitForSingleObject(thread_handle, INFINITE);
	DeleteCriticalSection(&buffer_lock);
	delete[] input_buffer;
	input_buffer = NULL;
	CloseHandle(input_event);
	input_event = NULL;
}  // end Win32InputHandler::Close()

unsigned int __stdcall Win32InputHandler::DoThreadOpen(void* param)
{
	Win32InputHandler* inputHandler = reinterpret_cast<Win32InputHandler*>(param);
	unsigned int exitStatus = inputHandler->Run();
	_endthreadex(exitStatus);
	return exitStatus;
}  // end  Win32InputHandler::DoThreadOpen()

unsigned int Win32InputHandler::Run()
{
	// This loop is for the child thread to read input one byte at a time
	// (Since consoles/anonymous pipes don't support async i/o)
	// The input bytes are copied to a circularly-managed "input_buffer"
	// that can be accessed via the "ReadData()" method by the parent
	while (is_running)
	{
		// Read input one byte at a time and buffer
		DWORD dwRead = 0;
		BOOL result = ReadFile(input_handle, input_buffer+input_index, 1, &dwRead, NULL);
		EnterCriticalSection(&buffer_lock);
		if (result)
		{
			if (0 != dwRead)
			{
				input_length++;
				input_index++;
				if (input_index >= input_buflen)
					input_index = 0;
			}
			else
			{
				LeaveCriticalSection(&buffer_lock);
				continue;
			}
		}
		else
		{
			// What do we do on error?!
			input_error = true;
			is_running = false;
		}
		// Signals the parent of data ready (or error)
		if (!input_ready)
		{
			SetEvent(input_event);
			input_ready = true;
		}
		// If the buffer has been filled, this blocks this child
		// loop until buffer space is available or it is stopped.
		while ((input_index == input_outdex) && (0 != input_length) && is_running)
			SleepConditionVariableCS(&buffer_vacancy, &buffer_lock, INFINITE);
		LeaveCriticalSection(&buffer_lock);
	}  // end while(is_running)
	return (input_error ? 1 : 0);
}  // end Win32InputHandler::Run()

// Called by parent to gobble up data (non-blocking read)
// Returns number of bytes read or -1 on error (e.g., STDIN pipe closed)
int Win32InputHandler::ReadData(char* buffer, int numBytes)
{
	EnterCriticalSection(&buffer_lock); 
	if ((0 != input_length) && (0 != numBytes))
	{
		if (numBytes > input_length)
			numBytes = input_length;
		input_length -= numBytes;
		if (0 == input_length)
		{
			// On error condition, the event is left triggered
			// after the input_buffer is emptied so parent gets
			// all data buffered before the error (e.g., eof) 
			// occurred.
			if (input_ready && !input_error)
			{
				input_ready = false;
				ResetEvent(input_event);
			}
		}
		// Copy "numBytes" of available data to caller's "buffer"
		// (two-step process when input_buffer wraps)
		unsigned int offset = input_outdex;
		input_outdex += numBytes;
		if (input_outdex >= input_buflen)
		{
			input_outdex -= input_buflen;
			unsigned int count = numBytes - input_outdex;
			memcpy(buffer, input_buffer+offset, count);
			if (0 != input_outdex)
				memcpy(buffer+count, input_buffer, input_outdex);
		}
		else
		{
			memcpy(buffer, input_buffer + offset, numBytes);
		}
	}
	else
	{
		// Doing the error check
		if (input_error && (0 != numBytes))
			numBytes = -1;  // to indicate an error has occurred and child is dead
		else
			numBytes = 0;
	}
	// This wakes up the child if it is sleeping because the buffer was full
	if (numBytes > 0)
		WakeConditionVariable(&buffer_vacancy);
	LeaveCriticalSection(&buffer_lock);
	return numBytes;
}  // end Win32InputHandler::ReadData()