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? Regards, Park Heesob /*========================================================================================*/ 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) { long r; fd_set file_rd; fd_set file_wr; #ifdef USE_INTERRUPT_WINSOCK fd_set trap; #endif /* USE_INTERRUPT_WINSOCK */ int file_nfds; DWORD val; int fd; int i; if (!NtSocketsInitialized) { StartSockets(); } r = 0; if (rd && rd->fd_count > r) r = rd->fd_count; if (wr && wr->fd_count > r) r = wr->fd_count; if (ex && ex->fd_count > r) r = ex->fd_count; if (nfds > r) nfds = r; if (nfds == 0 && timeout) { Sleep(timeout->tv_sec * 1000 + timeout->tv_usec / 1000); return 0; } file_nfds = extract_file_fd(rd, &file_rd); 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) { printf("CreateEvent failed (%d)\n", GetLastError()); return -1; } select_thread_list = ALLOCA_N(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; r = WaitForMultipleObjects(file_nfds+1,select_thread_list,FALSE, timeout->tv_sec * 1000 + timeout->tv_usec / 1000); if (!SetEvent(main_select_event) ) { printf("SetEvent failed (%d)\n", GetLastError()); return -1; } CloseHandle(main_select_event); 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; if (wr) *wr = file_wr; return file_nfds; } #if USE_INTERRUPT_WINSOCK if (ex) trap = *ex; else trap.fd_count = 0; if (trap.fd_count < FD_SETSIZE) trap.fd_array[trap.fd_count++] = (SOCKET)interrupted_event; // else unable to catch interrupt. ex = &trap; #endif /* USE_INTERRUPT_WINSOCK */ RUBY_CRITICAL({ r = select(nfds, rd, wr, ex, timeout); if (r == SOCKET_ERROR) { errno = map_errno(WSAGetLastError()); } }); return r; } -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://rubyforge.org/pipermail/win32utils-devel/attachments/20080428/a9adc2e0/attachment-0001.html>
On Mon, Apr 28, 2008 at 7:53 AM, Heesob Park <phasis at gmail.com> 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? >Park, This replacement is compatible with ruby_1_8 branch or just for Sapphire? I''ll love get this included in 1.8 to solve issues regarding One-Click Installer for the time being.> > Regards, > > Park Heesob >Thank you for your work, tried to apply your older patch about this and failed due changes introduced by years in ruby code. Regards, -- Luis Lavena Multimedia systems - Human beings, who are almost unique in having the ability to learn from the experience of others, are also remarkable for their apparent disinclination to do so. Douglas Adams
Hi, ----- Original Message ----- From: "Luis Lavena" <luislavena at gmail.com> To: "Development and ideas for win32utils projects" <win32utils-devel at rubyforge.org> Sent: Monday, April 28, 2008 7:56 PM Subject: Re: [Win32utils-devel] rb_w32_select function patch> On Mon, Apr 28, 2008 at 7:53 AM, Heesob Park <phasis at gmail.com> 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? >> > > Park, > > This replacement is compatible with ruby_1_8 branch or just for > Sapphire? I''ll love get this included in 1.8 to solve issues regarding > One-Click Installer for the time being. >I tested code with ruby 1.8.6 p-114 compiled with VC++6.0 and VC++9.0. Regards, Park Heesob
On Mon, Apr 28, 2008 at 9:45 AM, Park Heesob <phasis at gmail.com> wrote:> Hi, > > > ----- Original Message ----- > From: "Luis Lavena" <luislavena at gmail.com> > To: "Development and ideas for win32utils projects" <win32utils-devel at rubyforge.org> > Sent: Monday, April 28, 2008 7:56 PM > Subject: Re: [Win32utils-devel] rb_w32_select function patch > > > > > Park, > > > > This replacement is compatible with ruby_1_8 branch or just for > > Sapphire? I''ll love get this included in 1.8 to solve issues regarding > > One-Click Installer for the time being. > > > I tested code with ruby 1.8.6 p-114 compiled with VC++6.0 and VC++9.0. >Good thing to know. I''ll check with GCC build of One-Click.> Regards, >Thank you again, -- Luis Lavena Multimedia systems - Human beings, who are almost unique in having the ability to learn from the experience of others, are also remarkable for their apparent disinclination to do so. Douglas Adams
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. Where "test_thread.rb" is just: # test_thread.rb $LOAD_PATH.unshift File.join(Dir.pwd, ''lib'') Dir[''test/core/thread/**/*.rb''].each{ |f| begin require f rescue LoadError next end } Or you can just run ''rake test_thread'' if you have Rake installed. You can grab "ruby_test" out of the "shards" CVS repo. cvs -d :pserver:anonymous at rubyforge.org:/var/cvs/shards login cvs -d :pserver:anonymous at rubyforge.org:/var/cvs/shards checkout ruby_test Is it possible I applied the patch wrong? Do you happen to have a unified diff, i.e. "diff -u" output? Regards, Dan
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
On Tue, Apr 29, 2008 at 2:53 AM, Heesob Park <phasis at gmail.com> wrote:> Hi, > > [snip]> I added some safe check code. > Here is diff -u output: >Hello Park, I just tested your patch for rb_w32_select and found, even gets() is no longer blocking other threads from being executed, everything stops when you start typing something... Compared to other other platforms, the behavior differs, so maybe is not the best solution... I thought your previous patch (2005, 2006 maybe?) workaround that issue. But don''t remember quite right. In any case, this looks good, one step closer :-) (btw, platform i386-mingw32) -- Luis Lavena Multimedia systems - Human beings, who are almost unique in having the ability to learn from the experience of others, are also remarkable for their apparent disinclination to do so. Douglas Adams
Hi, 2008/4/29 Luis Lavena <luislavena at gmail.com>:> On Tue, Apr 29, 2008 at 2:53 AM, Heesob Park <phasis at gmail.com> wrote: > > Hi, > > > > [snip] > > > I added some safe check code. > > Here is diff -u output: > > > > Hello Park, > > I just tested your patch for rb_w32_select and found, even gets() is > no longer blocking other threads from being executed, everything stops > when you start typing something... > > Compared to other other platforms, the behavior differs, so maybe is > not the best solution... > > I thought your previous patch (2005, 2006 maybe?) workaround that > issue. But don''t remember quite right. >That patch is not working well as you expected.> In any case, this looks good, one step closer :-) > > (btw, platform i386-mingw32) >Here is a modified version for two step closer :) --- win32.c.org 2008-04-29 14:44:41.000000000 +0900 +++ win32.c 2008-04-29 18:25:26.000000000 +0900 @@ -2046,6 +2046,65 @@ return fileset->fd_count; } +static HANDLE main_select_event=NULL; +static HANDLE *select_thread_list=NULL; + +static DWORD WINAPI +select_read_thread(PVOID argp) +{ + HANDLE fd_handle = (HANDLE)argp; + DWORD ret = 1; + DWORD mode = 0; + + GetConsoleMode(fd_handle,&mode); + if(mode!=0) { + // RealConsole Input + DWORD num_events,num_events_read,pre_num=0; + INPUT_RECORD *input_record; + int i; + + while(1) { + if(WaitForSingleObject(main_select_event,0)!=WAIT_TIMEOUT) break; + GetNumberOfConsoleInputEvents(fd_handle, &num_events); + if(pre_num != num_events) { + input_record = (INPUT_RECORD *)malloc(sizeof(INPUT_RECORD)*num_events); + PeekConsoleInput(fd_handle, input_record, num_events, &num_events_read); + for(i=0;i<num_events;i++) { + if ((input_record[i].EventType == KEY_EVENT) && input_record[i].Event.KeyEvent.bKeyDown) { + if(input_record[i].Event.KeyEvent.uChar.AsciiChar==13) { /* carriage return */ + free(input_record); + return ret; + } + else if(input_record[i].Event.KeyEvent.uChar.AsciiChar==26) { /* ^Z - end of file */ + free(input_record); + return ret; + } + } + } + free(input_record); + pre_num = num_events; + } + } + 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 +2116,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 +2137,57 @@ 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++) { + while(1) { + exit_code = 0; + GetExitCodeThread(select_thread_list[i],&exit_code); + if(exit_code!=STILL_ACTIVE) { + CloseHandle(select_thread_list[i]); + break; + } + } + } + CloseHandle(main_select_event); + free(select_thread_list); + if(r==WAIT_TIMEOUT||r==file_nfds) + 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
Heesob Park wrote:> Hi, > > 2008/4/29 Luis Lavena <luislavena at gmail.com>: >> On Tue, Apr 29, 2008 at 2:53 AM, Heesob Park <phasis at gmail.com> wrote: >>> Hi, >>> >>> [snip] >>> I added some safe check code. >>> Here is diff -u output: >>> >> Hello Park, >> >> I just tested your patch for rb_w32_select and found, even gets() is >> no longer blocking other threads from being executed, everything stops >> when you start typing something... >> >> Compared to other other platforms, the behavior differs, so maybe is >> not the best solution... >> >> I thought your previous patch (2005, 2006 maybe?) workaround that >> issue. But don''t remember quite right. >> > That patch is not working well as you expected. > >> In any case, this looks good, one step closer :-) >> >> (btw, platform i386-mingw32) >> > Here is a modified version for two step closer :)<snip> That works. Passes my (limited) thread test suite, too. :) Awesome. Many thanks. I''ll commit this in the CVS repo for Sapphire. Dan
Hi, 2008/4/29 Daniel Berger <djberg96 at gmail.com>:> > <snip> > > That works. Passes my (limited) thread test suite, too. :) > > Awesome. Many thanks. I''ll commit this in the CVS repo for Sapphire. > > Dan >Now, I have made rb_w32_select function to work with pipe . Here is a test code: readPipe, writePipe = IO.pipe t = Thread.new { while true; sleep 1; puts "got #{readPipe.readline}"; end } i = 1 while true i += 1 sleep 1 puts "hello from main" if i>3 writePipe.puts "test #{i}\n" end end t.join Here is patch diff output: --- win32.c.org 2008-04-29 14:44:41.000000000 +0900 +++ win32.c 2008-04-30 16:46:32.000000000 +0900 @@ -2046,6 +2050,91 @@ return fileset->fd_count; } +static HANDLE main_select_event=NULL; +static HANDLE *select_thread_list=NULL; + +static DWORD WINAPI +select_read_thread(PVOID argp) +{ + HANDLE fd_handle = (HANDLE)argp; + DWORD ret = 1; + DWORD mode = 0; + + GetConsoleMode(fd_handle,&mode); + if(mode) { + /* Console Input */ + DWORD num_events,num_events_read,pre_num=0; + INPUT_RECORD *input_record; + int i; + + while(1) { + if(WaitForSingleObject(main_select_event,0)!=WAIT_TIMEOUT) break; + GetNumberOfConsoleInputEvents(fd_handle, &num_events); + if(pre_num != num_events) { + input_record = (INPUT_RECORD *)malloc(sizeof(INPUT_RECORD)*num_events); + PeekConsoleInput(fd_handle, input_record, num_events, &num_events_read); + for(i=0;i<num_events;i++) { + if ((input_record[i].EventType == KEY_EVENT) && input_record[i].Event.KeyEvent.bKeyDown) { + if(input_record[i].Event.KeyEvent.uChar.AsciiChar==13) { /* carriage return */ + free(input_record); + return ret; + } + else if(input_record[i].Event.KeyEvent.uChar.AsciiChar==26) { /* ^Z - end of file */ + free(input_record); + return ret; + } + } + } + free(input_record); + pre_num = num_events; + } + } + return ret; + } else { + DWORD state; + ret = GetNamedPipeHandleState(fd_handle,&state,NULL,NULL,NULL,NULL,0); + if(ret) { + /* Pipe Input */ + int bytes_avail=0; + while(1) { + if(WaitForSingleObject(main_select_event,0)!=WAIT_TIMEOUT) break; + ret = PeekNamedPipe(fd_handle,NULL,0,NULL,&bytes_avail,NULL); + if(bytes_avail>0) { + return bytes_avail; + } + } + } + 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) { + /* Console Output */ + return ret; + } else { + DWORD state; + ret = GetNamedPipeHandleState(fd_handle,&state,NULL,NULL,NULL,NULL,0); + if(ret) { + /* Pipe output */ + int bytes_written=0; + ret = WriteFile(fd_handle,NULL,0,&bytes_written,NULL); + if(ret) { + return ret; + } + } + } + return ret; +} + long rb_w32_select (int nfds, fd_set *rd, fd_set *wr, fd_set *ex, struct timeval *timeout) @@ -2074,6 +2163,80 @@ file_nfds += extract_file_fd(wr, &file_wr); if (file_nfds) { + DWORD val,exit_code; + int i; + main_select_event = CreateEvent(NULL,TRUE,FALSE,NULL); + if(main_select_event == NULL) + { + printf("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; + } + /* thread cleanup */ + for(i=0;i<file_nfds;i++) { + while(1) { + exit_code = 0; + GetExitCodeThread(select_thread_list[i],&exit_code); + if(exit_code!=STILL_ACTIVE) { + CloseHandle(select_thread_list[i]); + break; + } + } + } + free(select_thread_list); + CloseHandle(main_select_event); + + if(r==WAIT_TIMEOUT||r==file_nfds) { /* timeout or interrupt */ + FD_ZERO(&file_rd); + FD_ZERO(&file_wr); + *rd = file_rd; + *wr = file_wr; + return 0; + } + if(r<file_rd.fd_count) { /* read ready */ + FD_ZERO(&file_wr); + *rd = file_rd; + *wr = file_wr; + return 1; + } + else { /* write ready */ + FD_ZERO(&file_rd); + *rd = file_rd; + *wr = file_wr; + return 1; + } + // assume normal files are always readable/writable // fake read/write fd_set and return value if (rd) *rd = file_rd; Regards, Park Heesob
> -----Original Message----- > From: win32utils-devel-bounces at rubyforge.org > [mailto:win32utils-devel-bounces at rubyforge.org] On Behalf Of > Heesob Park > Sent: Wednesday, April 30, 2008 2:09 AM > To: Development and ideas for win32utils projects > Subject: Re: [Win32utils-devel] rb_w32_select function patch > > Hi, > > 2008/4/29 Daniel Berger <djberg96 at gmail.com>: > > > > <snip> > > > > That works. Passes my (limited) thread test suite, too. :) > > > > Awesome. Many thanks. I''ll commit this in the CVS repo for Sapphire. > > > > Dan > > > Now, I have made rb_w32_select function to work with pipe . > Here is a test code: > > readPipe, writePipe = IO.pipe > t = Thread.new { while true; sleep 1; puts "got > #{readPipe.readline}"; end } > > i = 1 > while true > i += 1 > sleep 1 > puts "hello from main" > if i>3 > writePipe.puts "test #{i}\n" > end > end > > t.join<snip> Excellent, thanks! With this in place I''m wondering how open3 could be implemented to work on Windows without fork and without resorting to a C extension. Or if IO.popen needs to be reworked. Any thoughts? Thanks, Dan This communication is the property of Qwest and may contain confidential or privileged information. Unauthorized use of this communication is strictly prohibited and may be unlawful. If you have received this communication in error, please immediately notify the sender by reply e-mail and destroy all copies of the communication and any attachments.
> -----Original Message----- > From: win32utils-devel-bounces at rubyforge.org > [mailto:win32utils-devel-bounces at rubyforge.org] On Behalf Of > Heesob Park > Sent: Tuesday, April 29, 2008 3:33 AM > To: Development and ideas for win32utils projects > Subject: Re: [Win32utils-devel] rb_w32_select function patch > > Hi, > > 2008/4/29 Luis Lavena <luislavena at gmail.com>: > > On Tue, Apr 29, 2008 at 2:53 AM, Heesob Park > <phasis at gmail.com> wrote: > > > Hi, > > > > > > [snip] > > > > > I added some safe check code. > > > Here is diff -u output: > > > > > > > Hello Park, > > > > I just tested your patch for rb_w32_select and found, even > gets() is > > no longer blocking other threads from being executed, > everything stops > > when you start typing something... > > > > Compared to other other platforms, the behavior differs, so > maybe is > > not the best solution... > > > > I thought your previous patch (2005, 2006 maybe?) workaround that > > issue. But don''t remember quite right. > > > That patch is not working well as you expected. > > > In any case, this looks good, one step closer :-) > > > > (btw, platform i386-mingw32) > > > Here is a modified version for two step closer :)<snip> The only difference I see between this and a Unix version is that anything I type on the command line doesn''t appear until I hit the Enter key. On Unix (or with Jruby on Windows), I see whatever I type on the console right away. I''m guessing it''s a cmd.exe or native thread issue. It''s nothing big, but I thought I''d mention it. Regards, Dan This communication is the property of Qwest and may contain confidential or privileged information. Unauthorized use of this communication is strictly prohibited and may be unlawful. If you have received this communication in error, please immediately notify the sender by reply e-mail and destroy all copies of the communication and any attachments.
Hi, 2008/4/30 Berger, Daniel <Daniel.Berger at qwest.com>:> > Excellent, thanks! > > With this in place I''m wondering how open3 could be implemented to work > on Windows without fork and without resorting to a C extension. Or if > IO.popen needs to be reworked. Any thoughts? >Before considering open3, there is a remain issue of writing on pipe blocking problem. Blocking on write pipe will block whole application. Here is a test code: readPipe, writePipe = IO.pipe t = Thread.new { sleep 5; while true; sleep 0.1; puts "got #{readPipe.readline.length} bytes"; end } i = 1 while true i += 1 sleep 1 puts "hello from main" if i>3 writePipe.puts "a"*2048 end end t.join I have modified rb_w32_select using NtQueryInformationFile,FilePipeLocalInformation to detect or prevent blocking of pipe write. Here is the latest patch: --- win32.c.org 2008-04-29 14:44:41.000000000 +0900 +++ win32.c 2008-05-02 14:08:10.000000000 +0900 @@ -2046,6 +2050,114 @@ return fileset->fd_count; } +static HANDLE main_select_event=NULL; +static HANDLE *select_thread_list=NULL; + +static DWORD WINAPI +select_read_thread(PVOID argp) +{ + HANDLE fd_handle = (HANDLE)argp; + DWORD ret = 1; + DWORD mode = 0; + + GetConsoleMode(fd_handle,&mode); + if(mode) { + /* Console Input */ + DWORD num_events,num_events_read,pre_num=0; + INPUT_RECORD *input_record; + int i; + + while(1) { + if(WaitForSingleObject(main_select_event,0)!=WAIT_TIMEOUT) break; + GetNumberOfConsoleInputEvents(fd_handle, &num_events); + if(pre_num != num_events) { + input_record = (INPUT_RECORD *)malloc(sizeof(INPUT_RECORD)*num_events); + PeekConsoleInput(fd_handle, input_record, num_events, &num_events_read); + for(i=0;i<num_events;i++) { + if(input_record[i].Event.KeyEvent.uChar.AsciiChar) return ret; + if ((input_record[i].EventType == KEY_EVENT) && input_record[i].Event.KeyEvent.bKeyDown) { + if(input_record[i].Event.KeyEvent.uChar.AsciiChar==13) { /* carriage return */ + free(input_record); + return ret; + } + else if(input_record[i].Event.KeyEvent.uChar.AsciiChar==26) { /* ^Z - end of file */ + free(input_record); + return ret; + } + } + } + free(input_record); + pre_num = num_events; + } + } + return ret; + } else { + DWORD state; + ret = GetNamedPipeHandleState(fd_handle,&state,NULL,NULL,NULL,NULL,0); + if(ret) { + /* Pipe Input */ + int bytes_avail=0; + while(1) { + if(WaitForSingleObject(main_select_event,0)!=WAIT_TIMEOUT) break; + ret = PeekNamedPipe(fd_handle,NULL,0,NULL,&bytes_avail,NULL); + if(bytes_avail>0) { + return bytes_avail; + } + } + } + return ret; + } + return ret; +} + +typedef DWORD (WINAPI *PNtQueryInformationFile)(HANDLE, PVOID, PVOID, DWORD, DWORD ); +#define FilePipeLocalInformation 24 +typedef struct _FILE_PIPE_LOCAL_INFORMATION { + ULONG NamedPipeType; + ULONG NamedPipeConfiguration; + ULONG MaximumInstances; + ULONG CurrentInstances; + ULONG InboundQuota; + ULONG ReadDataAvailable; + ULONG OutboundQuota; + ULONG WriteQuotaAvailable; + ULONG NamedPipeState; + ULONG NamedPipeEnd; +} FILE_PIPE_LOCAL_INFORMATION, *PFILE_PIPE_LOCAL_INFORMATION; +static PNtQueryInformationFile NtQueryInformationFile=NULL; + +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) { + /* Console Output */ + return ret; + } else { + DWORD state; + ret = GetNamedPipeHandleState(fd_handle,&state,NULL,NULL,NULL,NULL,0); + if(ret) { + /* Pipe output */ + int bytes_written=0; + int bytes_avail=0; + DWORD iob[2]; + FILE_PIPE_LOCAL_INFORMATION fpli = {0}; + if(NtQueryInformationFile) { + while(1) { + if(WaitForSingleObject(main_select_event,0)!=WAIT_TIMEOUT) break; + ret = NtQueryInformationFile(fd_handle,iob,&fpli,sizeof(fpli),FilePipeLocalInformation); + if(fpli.OutboundQuota==fpli.WriteQuotaAvailable) return ret; + } + } + } + } + return ret; +} + long rb_w32_select (int nfds, fd_set *rd, fd_set *wr, fd_set *ex, struct timeval *timeout) @@ -2074,6 +2187,86 @@ file_nfds += extract_file_fd(wr, &file_wr); if (file_nfds) { + DWORD val,exit_code; + int i; + if(!NtQueryInformationFile) + NtQueryInformationFile = (PNtQueryInformationFile) + GetProcAddress(GetModuleHandle("ntdll.dll"),"NtQueryInformationFile" ); + main_select_event = CreateEvent(NULL,TRUE,FALSE,NULL); + if(main_select_event == NULL) + { + printf("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; + } + /* thread cleanup */ + for(i=0;i<file_nfds;i++) { + while(1) { + exit_code = 0; + GetExitCodeThread(select_thread_list[i],&exit_code); + if(exit_code!=STILL_ACTIVE) { + CloseHandle(select_thread_list[i]); + break; + } + } + } + free(select_thread_list); + CloseHandle(main_select_event); + + if(r==WAIT_TIMEOUT) { /* timeout */ + FD_ZERO(&file_rd); + FD_ZERO(&file_wr); + if(rd) *rd = file_rd; + if(wr) *wr = file_wr; + return 0; + } else if((r -= WAIT_OBJECT_0) == file_nfds) { /* interrupt */ + FD_ZERO(&file_rd); + FD_ZERO(&file_wr); + if(rd) *rd = file_rd; + if(wr) *wr = file_wr; + return -1; + } else if(r<file_rd.fd_count) { /* read ready */ + FD_ZERO(&file_wr); + if(rd) *rd = file_rd; + if(wr) *wr = file_wr; + return 1; + } else { /* write ready */ + FD_ZERO(&file_rd); + if(rd) *rd = file_rd; + if(wr) *wr = file_wr; + return 1; + } + // assume normal files are always readable/writable // fake read/write fd_set and return value if (rd) *rd = file_rd; Regards, Park Heesob