<commit_before>/*
 * Windows compat: POSIX compatibility wrapper
 * Copyright (C) 2009 <NAME> <<EMAIL>>
 *
 * Parts of poll implementation from libusb-win32, by <NAME> et al.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 *
 */

/*
 * Posix poll() and pipe() Windows compatibility layer for libusb 1.0
 *
 * The way this layer works is by using OVERLAPPED with async I/O transfers, as
 * OVERLAPPED have an associated event which is flagged for I/O completion.
 * 
 * For USB pollable async I/O, you would typically:
 * - obtain a Windows HANDLE to a file or device that has been opened in
 *   OVERLAPPED mode
 * - call _libusb_create_fd with this handle to obtain a custom fd.
 *   Note that if you need simultaneous R/W access, you need to call create_fd
 *   twice, once in _O_RDONLY and once in _O_WRONLY mode to obtain 2 separate
 *   pollable fds
 * - leave the core functions call the poll routine and flag POLLIN/POLLOUT
 * 
 * For pipe pollable synchronous I/O (read end polling only), you would:
 * - create an anonymous pipe with _libusb_pipe to obtain 2 fds (r & w)
 * - use _libusb_write / _libusb_read to write to either end of the pipe
 * - use poll to check for data to read
 * Note that the _libusb_read/_libusb_write function actually perform 
 * asynchronous I/O internally, and could potentially be modified to support
 * O_NON_BLOCK
 *
 * The way the polling on _libusb_read works is by splitting all read I/O
 * into a dual 1 byte/n-1 bytes asynchronous read operation.
 * The 1 byte data (called the marker), is always armed for asynchronous
 * readout, so that as soon as data becomes available, an OVERLAPPED event
 * will be flagged, which poll can report.
 * Then during the _libusb_read routine itself, this 1 byte marker is copied
 * to the buffer, along with the rest of the data.
 *
 * Note that, since most I/O is buffered, being notified when only the first
 * byte of data is available is unlikely to delay read operations, since the
 * rest of the data should be available in system buffers by the time read
 * is called.
 *
 * Also note that if you don't use _libusb_read to read inbound data, but
 * use the OVERLAPPED directly (which is what we do in the USB async I/O 
 * functions), the marker is not used at all.
 */
#include <windows.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <io.h>

#include "windows_compat.h"

// Uncomment to debug the polling layer
//#define DEBUG_WINDOWS_COMPAT
#if defined(DEBUG_WINDOWS_COMPAT)
#define printb printf
#else
// MSVC6 cannot use a variadic argument and non MSVC
// compilers produce warnings if parenthesis are ommitted.
#if defined(_MSC_VER)
#define printb
#else
#define printb(...)
#endif
#endif

#if defined(_PREFAST_)
#pragma warning(disable:28719)
#endif

#if defined(__CYGWIN__ )
// cygwin produces a warning unless these prototypes are defined
extern int _close(int fd);
extern int _snprintf(char *buffer, size_t count, const char *format, ...);
extern int cygwin_attach_handle_to_fd(char *name, int fd, HANDLE handle, int bin, int access); 
// _open_osfhandle() is not available on cygwin, but we can emulate
// it for our needs with cygwin_attach_handle_to_fd()
static inline int _open_osfhandle(intptr_t osfhandle, int flags)
{
	int access;
	switch (flags) {
	case _O_RDONLY:
		access = GENERIC_READ;
		break;
	case _O_WRONLY:
		access = GENERIC_WRITE;
		break;
	case _O_RDWR:
		access = GENERIC_READ|GENERIC_WRITE;
		break;
	default:
		printb("_open_osfhandle (emulated): unuspported access mode\n");
		return -1;
	}
	return cygwin_attach_handle_to_fd("/dev/null", -1, (HANDLE)osfhandle, -1, access);
}
#endif

#define CHECK_INIT_POLLING do {if(!is_polling_set) init_polling();} while(0)

// public fd data
const struct winfd INVALID_WINFD = {-1, NULL, NULL, RW_NONE, FALSE};
struct winfd poll_fd[MAX_FDS];
// internal fd data
struct {
	CRITICAL_SECTION mutex; // lock for fds
	BYTE marker;            // 1st byte of a _libusb_read operation gets stored here

} _poll_fd[MAX_FDS];

// globals
BOOLEAN is_polling_set = FALSE;
LONG pipe_number = 0;
static volatile LONG compat_spinlock = 0;

// Attempt to use CancelIoEx on platforms and environments supporting it
// NB: this fucntion doesn't care about the validity of the handles
__inline BOOL cancel_io(int index)
{
	if ((index < 0) || (index >= MAX_FDS)) {
		return FALSE;
	}
	if (windows_version < WINDOWS_VISTA_AND_LATER) {
		return CancelIo(poll_fd[index].handle);
	}
	// cygwin and MinGW don't have Ex for now...
#if !defined(_MSC_VER)
	return CancelIo(poll_fd[index].handle);
#else
	return CancelIoEx(poll_fd[index].handle, poll_fd[index].overlapped);
#endif
}

// Init
void init_polling(void)
{
	int i;

	while (InterlockedExchange((LONG *)&compat_spinlock, 1) == 1) {
		SleepEx(0, TRUE);
	}
	if (!is_polling_set) {
		for (i=0; i<MAX_FDS; i++) {
			poll_fd[i] = INVALID_WINFD;
			_poll_fd[i].marker = 0;
			InitializeCriticalSection(&_poll_fd[i].mutex);
		}
		is_polling_set = TRUE;
	}
	compat_spinlock = 0;
}

// Internal function to retrieve the table index (and lock the fd mutex)
int _fd_to_index_and_lock(int fd)
{
	int i;

	if (fd <= 0)
		return -1;

	for (i=0; i<MAX_FDS; i++) {
		if (poll_fd[i].fd == fd) {
			EnterCriticalSection(&_poll_fd[i].mutex);
			// fd might have changed before we got to critical
			if (poll_fd[i].fd != fd) {
				LeaveCriticalSection(&_poll_fd[i].mutex);
				continue;
			}<commit_msg>r138: additional DDK/SDK versions checks for cancel_io inline (MSVC7+ users only)<commit_after>/*
 * Windows compat: POSIX compatibility wrapper
 * Copyright (C) 2009 <NAME> <<EMAIL>>
 *
 * Parts of poll implementation from libusb-win32, by <NAME> et al.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 *
 */

/*
 * Posix poll() and pipe() Windows compatibility layer for libusb 1.0
 *
 * The way this layer works is by using OVERLAPPED with async I/O transfers, as
 * OVERLAPPED have an associated event which is flagged for I/O completion.
 * 
 * For USB pollable async I/O, you would typically:
 * - obtain a Windows HANDLE to a file or device that has been opened in
 *   OVERLAPPED mode
 * - call _libusb_create_fd with this handle to obtain a custom fd.
 *   Note that if you need simultaneous R/W access, you need to call create_fd
 *   twice, once in _O_RDONLY and once in _O_WRONLY mode to obtain 2 separate
 *   pollable fds
 * - leave the core functions call the poll routine and flag POLLIN/POLLOUT
 * 
 * For pipe pollable synchronous I/O (read end polling only), you would:
 * - create an anonymous pipe with _libusb_pipe to obtain 2 fds (r & w)
 * - use _libusb_write / _libusb_read to write to either end of the pipe
 * - use poll to check for data to read
 * Note that the _libusb_read/_libusb_write function actually perform 
 * asynchronous I/O internally, and could potentially be modified to support
 * O_NON_BLOCK
 *
 * The way the polling on _libusb_read works is by splitting all read I/O
 * into a dual 1 byte/n-1 bytes asynchronous read operation.
 * The 1 byte data (called the marker), is always armed for asynchronous
 * readout, so that as soon as data becomes available, an OVERLAPPED event
 * will be flagged, which poll can report.
 * Then during the _libusb_read routine itself, this 1 byte marker is copied
 * to the buffer, along with the rest of the data.
 *
 * Note that, since most I/O is buffered, being notified when only the first
 * byte of data is available is unlikely to delay read operations, since the
 * rest of the data should be available in system buffers by the time read
 * is called.
 *
 * Also note that if you don't use _libusb_read to read inbound data, but
 * use the OVERLAPPED directly (which is what we do in the USB async I/O 
 * functions), the marker is not used at all.
 */
#include <windows.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <io.h>
#if defined(_MSC_VER) && (_MSC_VER > 1200)
// For VER_PRODUCTBUILD used in the cancel_io inline
#include <ntverp.h>
#endif

#include "windows_compat.h"

// Uncomment to debug the polling layer
//#define DEBUG_WINDOWS_COMPAT
#if defined(DEBUG_WINDOWS_COMPAT)
#define printb printf
#else
// MSVC6 cannot use a variadic argument and non MSVC
// compilers produce warnings if parenthesis are ommitted.
#if defined(_MSC_VER)
#define printb
#else
#define printb(...)
#endif
#endif

#if defined(_PREFAST_)
#pragma warning(disable:28719)
#endif

#if defined(__CYGWIN__ )
// cygwin produces a warning unless these prototypes are defined
extern int _close(int fd);
extern int _snprintf(char *buffer, size_t count, const char *format, ...);
extern int cygwin_attach_handle_to_fd(char *name, int fd, HANDLE handle, int bin, int access); 
// _open_osfhandle() is not available on cygwin, but we can emulate
// it for our needs with cygwin_attach_handle_to_fd()
static inline int _open_osfhandle(intptr_t osfhandle, int flags)
{
	int access;
	switch (flags) {
	case _O_RDONLY:
		access = GENERIC_READ;
		break;
	case _O_WRONLY:
		access = GENERIC_WRITE;
		break;
	case _O_RDWR:
		access = GENERIC_READ|GENERIC_WRITE;
		break;
	default:
		printb("_open_osfhandle (emulated): unuspported access mode\n");
		return -1;
	}
	return cygwin_attach_handle_to_fd("/dev/null", -1, (HANDLE)osfhandle, -1, access);
}
#endif

#define CHECK_INIT_POLLING do {if(!is_polling_set) init_polling();} while(0)

// public fd data
const struct winfd INVALID_WINFD = {-1, NULL, NULL, RW_NONE, FALSE};
struct winfd poll_fd[MAX_FDS];
// internal fd data
struct {
	CRITICAL_SECTION mutex; // lock for fds
	BYTE marker;            // 1st byte of a _libusb_read operation gets stored here

} _poll_fd[MAX_FDS];

// globals
BOOLEAN is_polling_set = FALSE;
LONG pipe_number = 0;
static volatile LONG compat_spinlock = 0;

// Attempt to use CancelIoEx on platforms and environments supporting it
// NB: this fucntion doesn't care about the validity of the handles
__inline BOOL cancel_io(int index)
{
	if ((index < 0) || (index >= MAX_FDS)) {
		return FALSE;
	}
	if (windows_version < WINDOWS_VISTA_AND_LATER) {
		return CancelIo(poll_fd[index].handle);
	}
	// cygwin and MinGW don't have Ex for now...
#if !defined(_MSC_VER) || !defined(VER_PRODUCTBUILD) || (VER_PRODUCTBUILD<6000)
	return CancelIo(poll_fd[index].handle);
#else
	return CancelIoEx(poll_fd[index].handle, poll_fd[index].overlapped);
#endif
}

// Init
void init_polling(void)
{
	int i;

	while (InterlockedExchange((LONG *)&compat_spinlock, 1) == 1) {
		SleepEx(0, TRUE);
	}
	printf("\n\n\nver prod = %d\n\n\n", VER_PRODUCTBUILD);
	if (!is_polling_set) {
		for (i=0; i<MAX_FDS; i++) {
			poll_fd[i] = INVALID_WINFD;
			_poll_fd[i].marker = 0;
			InitializeCriticalSection(&_poll_fd[i].mutex);
		}
		is_polling_set = TRUE;
	}
	compat_spinlock = 0;
}

// Internal function to retrieve the table index (and lock the fd mutex)
int _fd_to_index_and_lock(int fd)
{
	int i;

	if (fd <= 0)
		return -1;

	for (i=0; i<MAX_FDS; i++) {
		if (poll_fd[i].fd == fd) {
			EnterCriticalSection(&_poll_fd[i].mutex);
			// fd might have changed before we got to critical
			if (poll_fd[i].fd != fd) {
				LeaveCriticalSection(&_poll_fd[i].mutex);
				continue;
			}
{'fraction_non_alphanumeric': 0.09005568814638026, 'fraction_numerical': 0.009069212410501194, 'mean_word_length': 4.060789049919484, 'pattern_counts': {'":': 0, '<': 37, '<?xml version=': 0, '>': 29, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5971714', 'n_tokens_mistral': 4316, 'n_tokens_neox': 4036, 'n_words': 1610}