diff options
Diffstat (limited to 'libusb/os/windows_common.c')
-rw-r--r-- | libusb/os/windows_common.c | 213 |
1 files changed, 18 insertions, 195 deletions
diff --git a/libusb/os/windows_common.c b/libusb/os/windows_common.c index c170f43..6704cb0 100644 --- a/libusb/os/windows_common.c +++ b/libusb/os/windows_common.c @@ -44,28 +44,6 @@ static bool usbdk_available = false; static uint64_t hires_ticks_to_ps; static uint64_t hires_frequency; -#define TIMER_REQUEST_RETRY_MS 100 -#define WM_TIMER_REQUEST (WM_USER + 1) -#define WM_TIMER_EXIT (WM_USER + 2) - -// used for monotonic clock_gettime() -struct timer_request { - struct timespec *tp; - HANDLE event; -}; - -// Timer thread -static HANDLE timer_thread = NULL; -static DWORD timer_thread_id = 0; - -/* User32 dependencies */ -DLL_DECLARE_HANDLE(User32); -DLL_DECLARE_FUNC_PREFIXED(WINAPI, BOOL, p, GetMessageA, (LPMSG, HWND, UINT, UINT)); -DLL_DECLARE_FUNC_PREFIXED(WINAPI, BOOL, p, PeekMessageA, (LPMSG, HWND, UINT, UINT, UINT)); -DLL_DECLARE_FUNC_PREFIXED(WINAPI, BOOL, p, PostThreadMessageA, (DWORD, UINT, WPARAM, LPARAM)); - -static unsigned __stdcall windows_clock_gettime_threaded(void *param); - /* * Converts a windows error to human readable string * uses retval as errorcode, or, if 0, use GetLastError() @@ -279,99 +257,19 @@ void windows_force_sync_completion(OVERLAPPED *overlapped, ULONG size) SetEvent(overlapped->hEvent); } -static BOOL windows_init_dlls(void) +static void windows_init_clock(void) { - DLL_GET_HANDLE(User32); - DLL_LOAD_FUNC_PREFIXED(User32, p, GetMessageA, TRUE); - DLL_LOAD_FUNC_PREFIXED(User32, p, PeekMessageA, TRUE); - DLL_LOAD_FUNC_PREFIXED(User32, p, PostThreadMessageA, TRUE); - - return TRUE; -} - -static void windows_exit_dlls(void) -{ - DLL_FREE_HANDLE(User32); -} - -static bool windows_init_clock(struct libusb_context *ctx) -{ - DWORD_PTR affinity, dummy; - HANDLE event; LARGE_INTEGER li_frequency; - int i; - - if (QueryPerformanceFrequency(&li_frequency)) { - // The hires frequency can go as high as 4 GHz, so we'll use a conversion - // to picoseconds to compute the tv_nsecs part in clock_gettime - hires_frequency = li_frequency.QuadPart; - hires_ticks_to_ps = UINT64_C(1000000000000) / hires_frequency; - usbi_dbg("hires timer available (Frequency: %"PRIu64" Hz)", hires_frequency); - - // Because QueryPerformanceCounter might report different values when - // running on different cores, we create a separate thread for the timer - // calls, which we glue to the first available core always to prevent timing discrepancies. - if (!GetProcessAffinityMask(GetCurrentProcess(), &affinity, &dummy) || (affinity == 0)) { - usbi_err(ctx, "could not get process affinity: %s", windows_error_str(0)); - return false; - } - // The process affinity mask is a bitmask where each set bit represents a core on - // which this process is allowed to run, so we find the first set bit - for (i = 0; !(affinity & (DWORD_PTR)(1 << i)); i++); - affinity = (DWORD_PTR)(1 << i); + // Microsoft says that the QueryPerformanceFrequency() and + // QueryPerformanceCounter() functions always succeed on XP and later + QueryPerformanceFrequency(&li_frequency); - usbi_dbg("timer thread will run on core #%d", i); - - event = CreateEvent(NULL, FALSE, FALSE, NULL); - if (event == NULL) { - usbi_err(ctx, "could not create event: %s", windows_error_str(0)); - return false; - } - - timer_thread = (HANDLE)_beginthreadex(NULL, 0, windows_clock_gettime_threaded, (void *)event, - 0, (unsigned int *)&timer_thread_id); - if (timer_thread == NULL) { - usbi_err(ctx, "unable to create timer thread - aborting"); - CloseHandle(event); - return false; - } - - if (!SetThreadAffinityMask(timer_thread, affinity)) - usbi_warn(ctx, "unable to set timer thread affinity, timer discrepancies may arise"); - - // Wait for timer thread to init before continuing. - if (WaitForSingleObject(event, INFINITE) != WAIT_OBJECT_0) { - usbi_err(ctx, "failed to wait for timer thread to become ready - aborting"); - CloseHandle(event); - return false; - } - - CloseHandle(event); - } else { - usbi_dbg("no hires timer available on this platform"); - hires_frequency = 0; - hires_ticks_to_ps = UINT64_C(0); - } - - return true; -} - -static void windows_destroy_clock(void) -{ - if (timer_thread) { - // actually the signal to quit the thread. - if (!pPostThreadMessageA(timer_thread_id, WM_TIMER_EXIT, 0, 0) - || (WaitForSingleObject(timer_thread, INFINITE) != WAIT_OBJECT_0)) { - usbi_dbg("could not wait for timer thread to quit"); - TerminateThread(timer_thread, 1); - // shouldn't happen, but we're destroying - // all objects it might have held anyway. - } - CloseHandle(timer_thread); - timer_thread = NULL; - timer_thread_id = 0; - } + // The hires frequency can go as high as 4 GHz, so we'll use a conversion + // to picoseconds to compute the tv_nsecs part in clock_gettime + hires_frequency = li_frequency.QuadPart; + hires_ticks_to_ps = UINT64_C(1000000000000) / hires_frequency; + usbi_dbg("hires timer frequency: %"PRIu64" Hz", hires_frequency); } /* Windows version detection */ @@ -478,50 +376,6 @@ static void get_windows_version(void) usbi_dbg("Windows %s %s", w, arch); } -/* -* Monotonic and real time functions -*/ -static unsigned __stdcall windows_clock_gettime_threaded(void *param) -{ - struct timer_request *request; - LARGE_INTEGER hires_counter; - MSG msg; - - // The following call will create this thread's message queue - // See https://msdn.microsoft.com/en-us/library/windows/desktop/ms644946.aspx - pPeekMessageA(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE); - - // Signal windows_init_clock() that we're ready to service requests - if (!SetEvent((HANDLE)param)) - usbi_dbg("SetEvent failed for timer init event: %s", windows_error_str(0)); - param = NULL; - - // Main loop - wait for requests - while (1) { - if (pGetMessageA(&msg, NULL, WM_TIMER_REQUEST, WM_TIMER_EXIT) == -1) { - usbi_err(NULL, "GetMessage failed for timer thread: %s", windows_error_str(0)); - return 1; - } - - switch (msg.message) { - case WM_TIMER_REQUEST: - // Requests to this thread are for hires always - // Microsoft says that this function always succeeds on XP and later - // See https://msdn.microsoft.com/en-us/library/windows/desktop/ms644904.aspx - request = (struct timer_request *)msg.lParam; - QueryPerformanceCounter(&hires_counter); - request->tp->tv_sec = (long)(hires_counter.QuadPart / hires_frequency); - request->tp->tv_nsec = (long)(((hires_counter.QuadPart % hires_frequency) / 1000) * hires_ticks_to_ps); - if (!SetEvent(request->event)) - usbi_err(NULL, "SetEvent failed for timer request: %s", windows_error_str(0)); - break; - case WM_TIMER_EXIT: - usbi_dbg("timer thread quitting"); - return 0; - } - } -} - static void windows_transfer_callback(const struct windows_backend *backend, struct usbi_transfer *itransfer, DWORD io_result, DWORD io_size) { @@ -612,12 +466,6 @@ static int windows_init(struct libusb_context *ctx) // NB: concurrent usage supposes that init calls are equally balanced with // exit calls. If init is called more than exit, we will not exit properly if (++init_count == 1) { // First init? - // Load DLL imports - if (!windows_init_dlls()) { - usbi_err(ctx, "could not resolve DLL functions"); - goto init_exit; - } - get_windows_version(); if (windows_version == WINDOWS_UNDEFINED) { @@ -626,8 +474,7 @@ static int windows_init(struct libusb_context *ctx) goto init_exit; } - if (!windows_init_clock(ctx)) - goto init_exit; + windows_init_clock(); if (!htab_create(ctx)) goto init_exit; @@ -658,8 +505,6 @@ init_exit: // Holds semaphore here if (winusb_backend_init) winusb_backend.exit(ctx); htab_destroy(); - windows_destroy_clock(); - windows_exit_dlls(); --init_count; } @@ -694,8 +539,6 @@ static void windows_exit(struct libusb_context *ctx) } winusb_backend.exit(ctx); htab_destroy(); - windows_destroy_clock(); - windows_exit_dlls(); } ReleaseSemaphore(semaphore, 1, NULL); // increase count back to 1 @@ -897,45 +740,25 @@ static int windows_handle_events(struct libusb_context *ctx, struct pollfd *fds, static int windows_clock_gettime(int clk_id, struct timespec *tp) { - struct timer_request request; + LARGE_INTEGER hires_counter; #if !defined(_MSC_VER) || (_MSC_VER < 1900) FILETIME filetime; ULARGE_INTEGER rtime; #endif - DWORD r; switch (clk_id) { case USBI_CLOCK_MONOTONIC: - if (timer_thread) { - request.tp = tp; - request.event = CreateEvent(NULL, FALSE, FALSE, NULL); - if (request.event == NULL) - return LIBUSB_ERROR_NO_MEM; - - if (!pPostThreadMessageA(timer_thread_id, WM_TIMER_REQUEST, 0, (LPARAM)&request)) { - usbi_err(NULL, "PostThreadMessage failed for timer thread: %s", windows_error_str(0)); - CloseHandle(request.event); - return LIBUSB_ERROR_OTHER; - } - - do { - r = WaitForSingleObject(request.event, TIMER_REQUEST_RETRY_MS); - if (r == WAIT_TIMEOUT) - usbi_dbg("could not obtain a timer value within reasonable timeframe - too much load?"); - else if (r == WAIT_FAILED) - usbi_err(NULL, "WaitForSingleObject failed: %s", windows_error_str(0)); - } while (r == WAIT_TIMEOUT); - CloseHandle(request.event); - - if (r == WAIT_OBJECT_0) - return LIBUSB_SUCCESS; - else - return LIBUSB_ERROR_OTHER; + if (hires_frequency) { + QueryPerformanceCounter(&hires_counter); + tp->tv_sec = (long)(hires_counter.QuadPart / hires_frequency); + tp->tv_nsec = (long)(((hires_counter.QuadPart % hires_frequency) * hires_ticks_to_ps) / UINT64_C(1000)); + return LIBUSB_SUCCESS; } // Fall through and return real-time if monotonic was not detected @ timer init case USBI_CLOCK_REALTIME: #if defined(_MSC_VER) && (_MSC_VER >= 1900) - timespec_get(tp, TIME_UTC); + if (!timespec_get(tp, TIME_UTC)) + return LIBUSB_ERROR_OTHER; #else // We follow http://msdn.microsoft.com/en-us/library/ms724928%28VS.85%29.aspx // with a predef epoch time to have an epoch that starts at 1970.01.01 00:00 |