[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