static bool windows_init_clock(struct libusb_context *ctx) { DWORD_PTR affinity, dummy; HANDLE event = NULL; LARGE_INTEGER li_frequency; int i; if (QueryPerformanceFrequency(&li_frequency)) { // Load DLL imports if (windows_init_dlls() != LIBUSB_SUCCESS) { usbi_err(ctx, "could not resolve DLL functions"); return false; } // 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); 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 int windows_init(struct libusb_context *ctx) { struct windows_context_priv *priv = _context_priv(ctx); HANDLE semaphore; char sem_name[11 + 8 + 1]; // strlen("libusb_init") + (32-bit hex PID) + '\0' int r = LIBUSB_ERROR_OTHER; bool winusb_backend_init = false; sprintf(sem_name, "libusb_init%08X", (unsigned int)(GetCurrentProcessId() & 0xFFFFFFFF)); semaphore = CreateSemaphoreA(NULL, 1, 1, sem_name); if (semaphore == NULL) { usbi_err(ctx, "could not create semaphore: %s", windows_error_str(0)); return LIBUSB_ERROR_NO_MEM; } // A successful wait brings our semaphore count to 0 (unsignaled) // => any concurent wait stalls until the semaphore's release if (WaitForSingleObject(semaphore, INFINITE) != WAIT_OBJECT_0) { usbi_err(ctx, "failure to access semaphore: %s", windows_error_str(0)); CloseHandle(semaphore); return LIBUSB_ERROR_NO_MEM; } // 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) { usbi_err(ctx, "failed to detect Windows version"); r = LIBUSB_ERROR_NOT_SUPPORTED; goto init_exit; } if (!windows_init_clock(ctx)) goto init_exit; if (!htab_create(ctx)) goto init_exit; r = winusb_backend.init(ctx); if (r != LIBUSB_SUCCESS) goto init_exit; winusb_backend_init = true; r = usbdk_backend.init(ctx); if (r == LIBUSB_SUCCESS) { usbi_dbg("UsbDk backend is available"); usbdk_available = true; } else { usbi_info(ctx, "UsbDk backend is not available"); // Do not report this as an error r = LIBUSB_SUCCESS; } } // By default, new contexts will use the WinUSB backend priv->backend = &winusb_backend; r = LIBUSB_SUCCESS; init_exit: // Holds semaphore here if ((init_count == 1) && (r != LIBUSB_SUCCESS)) { // First init failed? if (winusb_backend_init) winusb_backend.exit(ctx); htab_destroy(); windows_destroy_clock(); windows_exit_dlls(); --init_count; } ReleaseSemaphore(semaphore, 1, NULL); // increase count back to 1 CloseHandle(semaphore); return r; }