[Win32utils-devel] rb_w32_select function patch
Heesob Park
phasis at gmail.com
Tue Apr 29 01:53:07 EDT 2008
Hi,
2008/4/29 Daniel Berger <djberg96 at gmail.com>:
> Heesob Park wrote:
> > Hi, all
> > As you know, rb_w32_select don't work well with standard input.
> > Here is test code:
> >
> > t = Thread.new {
> > while true
> > puts "printing a line"
> > sleep 2
> > end
> > }
> >
> > gets
> > t.exit
> > puts "exiting"
> > Following is the first version of patch code of rb_w32_select function
> in win32.c
> > It is inspired by cygwin select implementation code and adopted some code
> from it.
> > The basic idea is creating thread for each file descriptor and wait
> events.
> > What's your thought about it?
> >
>
> I applied the patch and then tried to run the thread tests in my ruby_test
> test suite against Ruby 1.8.6 p114 on Windows XP, built with VC++ 8.
>
> C:\Documents and Settings\djberge\workspace\ruby_test>ruby test_thread.rb
> Loaded suite test_thread
> Started
> ......................................................C:/rubyVC8/lib/ruby/1.8/test/unit/ui/console/testrunner.
> rb:113: [BUG] Segmentation fault
> ruby 1.8.6 (2008-03-03) [i386-mswin32_80]
>
>
> This application has requested the Runtime to terminate it in an unusual
> way.
> Please contact the application's support team for more information.
>
I missed timeval NULL check for sleep without argument.
>
> Is it possible I applied the patch wrong? Do you happen to have a unified
> diff, i.e. "diff -u" output?
>
I added some safe check code.
Here is diff -u output:
--- win32.c.org 2008-04-29 14:44:41.000000000 +0900
+++ win32.c 2008-04-29 14:32:59.000000000 +0900
@@ -2046,6 +2046,117 @@
return fileset->fd_count;
}
+static HANDLE main_select_event=NULL;
+static HANDLE *select_thread_list=NULL;
+
+static struct {
+ int vk;
+ int val[4];
+} keytable[] = {
+ /* N S C A */
+ {VK_LEFT, {1,1,1,1}},
+ {VK_RIGHT, {1,1,1,1}},
+ {VK_UP, {1,1,1,1}},
+ {VK_DOWN, {1,1,1,1}},
+ {VK_PRIOR, {1,1,1,1}},
+ {VK_NEXT, {1,1,1,1}},
+ {VK_HOME, {1,1,1,1}},
+ {VK_END, {1,1,1,1}},
+ {VK_INSERT, {1,1,1,1}},
+ {VK_DELETE, {1,1,1,1}},
+ {VK_F1, {1,1,0,0}},
+ {VK_F2, {1,1,0,0}},
+ {VK_F3, {1,1,0,0}},
+ {VK_F4, {1,1,0,0}},
+ {VK_F5, {1,1,0,0}},
+ {VK_F6, {1,1,1,0}},
+ {VK_F7, {1,1,0,0}},
+ {VK_F8, {1,1,0,0}},
+ {VK_F9, {1,1,0,0}},
+ {VK_F10, {1,1,0,0}},
+ {VK_F11, {1,0,0,0}},
+ {VK_F12, {1,0,0,0}},
+ {VK_NUMPAD5, {1,0,0,0}},
+ {VK_CLEAR, {1,0,0,0}},
+ {'6', {0,0,1,0}},
+ {0, {0,0,0,0}}
+};
+
+static int
+is_non_ascii_key(INPUT_RECORD* irec)
+{
+#define NORMAL 0
+#define SHIFT 1
+#define CONTROL 2
+#define ALT 3
+ int modifier_index = NORMAL;
+ int i;
+
+ if (irec->Event.KeyEvent.dwControlKeyState & SHIFT_PRESSED)
+ modifier_index = SHIFT;
+ else if (irec->Event.KeyEvent.dwControlKeyState &
+ (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED))
+ modifier_index = CONTROL;
+ else if (irec->Event.KeyEvent.dwControlKeyState &
+ (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED))
+ modifier_index = ALT;
+
+ for (i = 0; keytable[i].vk; i++)
+ if (irec->Event.KeyEvent.wVirtualKeyCode == keytable[i].vk)
+ return keytable[i].val[modifier_index];
+
+ if (irec->Event.KeyEvent.uChar.AsciiChar) {
+ return 1;
+ }
+ return 0;
+}
+
+static DWORD WINAPI
+select_read_thread(PVOID argp)
+{
+ HANDLE fd_handle = (HANDLE)argp;
+ DWORD ret = 1;
+ DWORD mode = 0;
+
+ mode = 0;
+ GetConsoleMode(fd_handle,&mode);
+ if(mode!=0) {
+ // RealConsole Input
+ INPUT_RECORD irec;
+ DWORD events_read=0;
+
+ for(;;) {
+ ret = WaitForSingleObject(main_select_event,0);
+ if(ret!=WAIT_TIMEOUT) break;
+ if(!PeekConsoleInput(fd_handle,&irec,1,&events_read))
+ break;
+ if (irec.EventType == KEY_EVENT && irec.Event.KeyEvent.bKeyDown &&
+ (irec.Event.KeyEvent.uChar.AsciiChar || is_non_ascii_key(&irec))) {
+ return ret;
+ }
+ if(events_read>0)
+ ReadConsoleInput(fd_handle, &irec, 1, &events_read);
+ }
+ return ret;
+ }
+ return ret;
+}
+
+static DWORD WINAPI
+select_write_thread(PVOID argp)
+{
+ HANDLE fd_handle = (HANDLE)argp;
+ DWORD ret = 1;
+ DWORD mode = 0;
+
+ GetConsoleMode(fd_handle,&mode);
+ if(mode!=0) {
+ // RealConsole Output
+ return ret;
+ }
+ return ret;
+}
+
long
rb_w32_select (int nfds, fd_set *rd, fd_set *wr, fd_set *ex,
struct timeval *timeout)
@@ -2057,6 +2168,10 @@
fd_set trap;
#endif /* USE_INTERRUPT_WINSOCK */
int file_nfds;
+ DWORD val;
+ int fd;
+ int i;
+ DWORD exit_code;
if (!NtSocketsInitialized) {
StartSockets();
@@ -2074,6 +2189,55 @@
file_nfds += extract_file_fd(wr, &file_wr);
if (file_nfds)
{
+ main_select_event = CreateEvent(NULL,TRUE,FALSE,"main_select_event");
+ if(main_select_event == NULL)
+ {
+ fprintf(stderr,"CreateEvent failed (%d)\n", GetLastError());
+ return -1;
+ }
+ select_thread_list = malloc(sizeof(HANDLE)*(file_nfds+1));
+
+ for(i=0; i<file_nfds; i++)
+ {
+ if(i<file_rd.fd_count) {
+ select_thread_list[i] = CreateThread(NULL,0,select_read_thread,
+ (PVOID)file_rd.fd_array[i],0,&val);
+ }
+ else {
+ select_thread_list[i] = CreateThread(NULL,0,select_write_thread,
+ (PVOID)file_wr.fd_array[i-file_rd.fd_count],0,&val);
+ }
+ if (select_thread_list[i] == NULL)
+ {
+ printf("CreateThread failed (%d)\n", GetLastError());
+ return -1;
+ }
+ }
+
+ select_thread_list[file_nfds] = interrupted_event;
+ if(timeout)
+ r = WaitForMultipleObjects(file_nfds+1,select_thread_list,FALSE,
+ timeout->tv_sec * 1000 + timeout->tv_usec / 1000);
+ else
+ r =
WaitForMultipleObjects(file_nfds+1,select_thread_list,FALSE,INFINITE);
+ if (!SetEvent(main_select_event) )
+ {
+ printf("SetEvent failed (%d)\n", GetLastError());
+ return -1;
+ }
+ for(i=0;i<file_nfds;i++) {
+ exit_code = 0;
+ GetExitCodeThread(select_thread_list[i],&exit_code);
+ if(exit_code==STILL_ACTIVE) {
+ TerminateThread(select_thread_list[i],1);
+ }
+ CloseHandle(select_thread_list[i]);
+ }
+ CloseHandle(main_select_event);
+ free(select_thread_list);
+ if(r==WAIT_TIMEOUT)
+ return 0;
+
// assume normal files are always readable/writable
// fake read/write fd_set and return value
if (rd) *rd = file_rd;
Regards,
Park Heesob
More information about the win32utils-devel
mailing list