[Win32utils-devel] rb_w32_select function patch

Heesob Park phasis at gmail.com
Mon Apr 28 06:53:26 EDT 2008


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>


More information about the win32utils-devel mailing list