Commit 42c04eb1 authored by Marc Hoersken's avatar Marc Hoersken
Browse files

sockfilt.c: clean up threaded approach and add documentation

parent 6fd97fca
Loading
Loading
Loading
Loading
+137 −48
Original line number Diff line number Diff line
@@ -506,102 +506,170 @@ static void lograw(unsigned char *buffer, ssize_t len)
 * to re-create a select() function with support for other handle types.
 *
 * select() function with support for WINSOCK2 sockets and all
 * other handle types supported by WaitForMultipleObjectsEx().
 *
 * TODO: Differentiate between read/write/except for non-SOCKET handles.
 * other handle types supported by WaitForMultipleObjectsEx() as
 * well as disk files, anonymous and names pipes, and character input.
 *
 * http://msdn.microsoft.com/en-us/library/windows/desktop/ms687028.aspx
 * http://msdn.microsoft.com/en-us/library/windows/desktop/ms741572.aspx
 */
static DWORD WINAPI select_ws_stdin_wait_thread(LPVOID lpParameter)
struct select_ws_wait_data {
  HANDLE handle; /* actual handle to wait for during select */
  HANDLE event;  /* internal event to abort waiting thread */
};
static DWORD WINAPI select_ws_wait_thread(LPVOID lpParameter)
{
  struct select_ws_wait_data *data;
  HANDLE handle, handles[2];
  INPUT_RECORD inputrecord;
  LARGE_INTEGER size, pos;
  DWORD type, length;

  handle = GetStdHandle(STD_INPUT_HANDLE);
  handles[0] = (HANDLE) lpParameter;
  /* retrieve handles from internal structure */
  data = (struct select_ws_wait_data *) lpParameter;
  if(data) {
    handle = data->handle;
    handles[0] = data->event;
    handles[1] = handle;
  type = GetFileType(handle);
    free(data);
  }
  else
    return -1;

  /* retrieve the type of file to wait on */
  type = GetFileType(handle);
  switch(type) {
    case FILE_TYPE_DISK:
       /* The handle represents a file on disk, this means:
        * - WaitForMultipleObjectsEx will always be signalled for it.
        * - comparison of current position in file and total size of
        *   the file can be used to check if we reached the end yet.
        *
        * Approach: Loop till either the internal event is signalled
        *           or if the end of the file has already been reached.
        */
      while(WaitForMultipleObjectsEx(2, handles, FALSE, INFINITE, FALSE)
            == WAIT_OBJECT_0 + 1) {
        /* get total size of file */
        size.QuadPart = 0;
        if(GetFileSizeEx(handle, &size)) {
          /* get the current position within the file */
          pos.QuadPart = 0;
          if(SetFilePointerEx(handle, pos, &pos, FILE_CURRENT)) {
            if(size.QuadPart == pos.QuadPart)
            /* compare position with size, abort if not equal */
            if(size.QuadPart == pos.QuadPart) {
              /* sleep and continue waiting */
              SleepEx(100, FALSE);
            else
              break;
              continue;
            }
          else
            break;
          }
        else
        }
        /* there is some data available, stop waiting */
        break;
      }
      break;

    case FILE_TYPE_CHAR:
       /* The handle represents a character input, this means:
        * - WaitForMultipleObjectsEx will be signalled on any kind of input,
        *   including mouse and window size events we do not care about.
        *
        * Approach: Loop till either the internal event is signalled
        *           or we get signalled for an actual key-event.
        */
      while(WaitForMultipleObjectsEx(2, handles, FALSE, INFINITE, FALSE)
            == WAIT_OBJECT_0 + 1) {
        /* check if this is an actual console handle */
        if(GetConsoleMode(handle, &length)) {
          /* retrieve an event from the console buffer */
          length = 0;
          if(PeekConsoleInput(handle, &inputrecord, 1, &length)) {
            if(length == 1 && inputrecord.EventType != KEY_EVENT)
            /* check if the event is not an actual key-event */
            if(length == 1 && inputrecord.EventType != KEY_EVENT) {
              /* purge the non-key-event and continue waiting */
              ReadConsoleInput(handle, &inputrecord, 1, &length);
            else
              break;
              continue;
            }
          else
            break;
          }
        else
        }
        /* there is some data available, stop waiting */
        break;
      }
      break;

    case FILE_TYPE_PIPE:
       /* The handle represents an anonymous or named pipe, this means:
        * - WaitForMultipleObjectsEx will always be signalled for it.
        * - peek into the pipe and retrieve the amount of data available.
        *
        * Approach: Loop till either the internal event is signalled
        *           or there is data in the pipe available for reading.
        */
      while(WaitForMultipleObjectsEx(2, handles, FALSE, INFINITE, FALSE)
            == WAIT_OBJECT_0 + 1) {
        /* peek into the pipe and retrieve the amount of data available */
        if(PeekNamedPipe(handle, NULL, 0, NULL, &length, NULL)) {
          if(length == 0)
          /* if there is no data available, sleep and continue waiting */
          if(length == 0) {
            SleepEx(100, FALSE);
          else
            break;
            continue;
          }
        }
        else {
          if(GetLastError() == ERROR_BROKEN_PIPE)
          /* if the pipe has been closed, sleep and continue waiting */
          if(GetLastError() == ERROR_BROKEN_PIPE) {
            SleepEx(100, FALSE);
          else
            break;
            continue;
          }
        }
        /* there is some data available, stop waiting */
        break;
      }
      break;

    default:
      /* The handle has an unknown type, try to wait on it */
      WaitForMultipleObjectsEx(2, handles, FALSE, INFINITE, FALSE);
      break;
  }

  return 0;
}
static HANDLE select_ws_wait(HANDLE handle, HANDLE event)
{
  struct select_ws_wait_data *data;
  HANDLE thread = NULL;

  /* allocate internal waiting data structure */
  data = malloc(sizeof(struct select_ws_wait_data));
  if(data) {
    data->handle = handle;
    data->event = event;

    /* launch waiting thread */
    thread = CreateThread(NULL, 0,
                          &select_ws_wait_thread,
                          data, 0, NULL);

    /* free data if thread failed to launch */
    if(!thread) {
      free(data);
    }
  }

  return thread;
}
static int select_ws(int nfds, fd_set *readfds, fd_set *writefds,
                     fd_set *exceptfds, struct timeval *timeout)
{
  DWORD milliseconds, wait, idx;
  WSAEVENT wsaevent, *wsaevents;
  WSANETWORKEVENTS wsanetevents;
  HANDLE handle, *handles;
  HANDLE handle, *handles, *threads;
  curl_socket_t sock, *fdarr, *wsasocks;
  long networkevents;
  int error, fds;
  HANDLE threadevent = NULL, threadhandle = NULL;
  DWORD nfd = 0, wsa = 0;
  HANDLE waitevent = NULL;
  DWORD nfd = 0, thd = 0, wsa = 0;
  int ret = 0;

  /* check if the input value is valid */
@@ -616,6 +684,13 @@ static int select_ws(int nfds, fd_set *readfds, fd_set *writefds,
    return 0;
  }

  /* create internal event to signal waiting threads */
  waitevent = CreateEvent(NULL, TRUE, FALSE, NULL);
  if(!waitevent) {
    errno = ENOMEM;
    return -1;
  }

  /* allocate internal array for the original input handles */
  fdarr = malloc(nfds * sizeof(curl_socket_t));
  if(fdarr == NULL) {
@@ -631,9 +706,19 @@ static int select_ws(int nfds, fd_set *readfds, fd_set *writefds,
    return -1;
  }

  /* allocate internal array for the internal threads handles */
  threads = malloc(nfds * sizeof(HANDLE));
  if(threads == NULL) {
    free(handles);
    free(fdarr);
    errno = ENOMEM;
    return -1;
  }

  /* allocate internal array for the internal socket handles */
  wsasocks = malloc(nfds * sizeof(curl_socket_t));
  if(wsasocks == NULL) {
    free(threads);
    free(handles);
    free(fdarr);
    errno = ENOMEM;
@@ -643,6 +728,7 @@ static int select_ws(int nfds, fd_set *readfds, fd_set *writefds,
  /* allocate internal array for the internal WINSOCK2 events */
  wsaevents = malloc(nfds * sizeof(WSAEVENT));
  if(wsaevents == NULL) {
    free(threads);
    free(wsasocks);
    free(handles);
    free(fdarr);
@@ -668,11 +754,11 @@ static int select_ws(int nfds, fd_set *readfds, fd_set *writefds,
    if(networkevents) {
      fdarr[nfd] = curlx_sitosk(fds);
      if(fds == fileno(stdin)) {
        threadevent = CreateEvent(NULL, TRUE, FALSE, NULL);
        threadhandle = CreateThread(NULL, 0,
                                    &select_ws_stdin_wait_thread,
                                    threadevent, 0, NULL);
        handles[nfd] = threadhandle;
        handle = GetStdHandle(STD_INPUT_HANDLE);
        handle = select_ws_wait(handle, waitevent);
        handles[nfd] = handle;
        threads[thd] = handle;
        thd++;
      }
      else if(fds == fileno(stdout)) {
        handles[nfd] = GetStdHandle(STD_OUTPUT_HANDLE);
@@ -685,14 +771,19 @@ static int select_ws(int nfds, fd_set *readfds, fd_set *writefds,
        if(wsaevent != WSA_INVALID_EVENT) {
          error = WSAEventSelect(fds, wsaevent, networkevents);
          if(error != SOCKET_ERROR) {
            handles[nfd] = wsaevent;
            handle = (HANDLE) wsaevent;
            handles[nfd] = handle;
            wsasocks[wsa] = curlx_sitosk(fds);
            wsaevents[wsa] = wsaevent;
            wsa++;
          }
          else {
            handles[nfd] = (HANDLE) curlx_sitosk(fds);
            WSACloseEvent(wsaevent);
            handle = (HANDLE) curlx_sitosk(fds);
            handle = select_ws_wait(handle, waitevent);
            handles[nfd] = handle;
            threads[thd] = handle;
            thd++;
          }
        }
      }
@@ -711,10 +802,8 @@ static int select_ws(int nfds, fd_set *readfds, fd_set *writefds,
  /* wait for one of the internal handles to trigger */
  wait = WaitForMultipleObjectsEx(nfd, handles, FALSE, milliseconds, FALSE);

  /* signal the event handle for the waiting thread */
  if(threadevent) {
    SetEvent(threadevent);
  }
  /* signal the event handle for the waiting threads */
  SetEvent(waitevent);

  /* loop over the internal handles returned in the descriptors */
  for(idx = 0; idx < nfd; idx++) {
@@ -783,16 +872,16 @@ static int select_ws(int nfds, fd_set *readfds, fd_set *writefds,
    WSACloseEvent(wsaevents[idx]);
  }

  if(threadhandle) {
    WaitForSingleObject(threadhandle, INFINITE);
    CloseHandle(threadhandle);
  }
  if(threadevent) {
    CloseHandle(threadevent);
  for(idx = 0; idx < thd; idx++) {
    WaitForSingleObject(threads[thd], INFINITE);
    CloseHandle(threads[thd]);
  }

  CloseHandle(waitevent);

  free(wsaevents);
  free(wsasocks);
  free(threads);
  free(handles);
  free(fdarr);