mirror of
https://github.com/shadps4-emu/ext-libusb.git
synced 2026-01-31 00:55:21 +01:00
tests/stress_mt: Test open/transfer/close on available devices
Verify that you can open a device, do some transfer (a device descriptor control transfer was chosen as it should succeed with any connected device), and close a device from multiple threads in parallel as well. Print a warning if a device is readonly. Consistently add thread flags to the build. Closes #1347
This commit is contained in:
committed by
Tormod Volden
parent
bd91a0c145
commit
a8d3cd8031
@@ -1 +1 @@
|
||||
#define LIBUSB_NANO 11828
|
||||
#define LIBUSB_NANO 11829
|
||||
|
||||
@@ -7,6 +7,9 @@ stress_mt_SOURCES = stress_mt.c
|
||||
set_option_SOURCES = set_option.c testlib.c
|
||||
init_context_SOURCES = init_context.c testlib.c
|
||||
|
||||
stress_mt_CFLAGS = $(AM_CFLAGS) $(THREAD_CFLAGS)
|
||||
stress_mt_LDADD = $(LDADD) $(THREAD_LIBS)
|
||||
|
||||
if OS_EMSCRIPTEN
|
||||
# On the Web you can't block the main thread as this blocks the event loop itself,
|
||||
# causing deadlocks when trying to use async APIs like WebUSB.
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
|
||||
#include <libusb.h>
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#if defined(PLATFORM_POSIX)
|
||||
|
||||
@@ -41,6 +42,8 @@ static inline void thread_join(thread_t thread)
|
||||
(void)pthread_join(thread, NULL);
|
||||
}
|
||||
|
||||
#include <stdatomic.h>
|
||||
|
||||
#elif defined(PLATFORM_WINDOWS)
|
||||
|
||||
typedef HANDLE thread_t;
|
||||
@@ -70,12 +73,17 @@ static inline void thread_join(thread_t thread)
|
||||
(void)WaitForSingleObject(thread, INFINITE);
|
||||
(void)CloseHandle(thread);
|
||||
}
|
||||
|
||||
typedef volatile LONG atomic_bool;
|
||||
|
||||
#define atomic_exchange InterlockedExchange
|
||||
#endif /* PLATFORM_WINDOWS */
|
||||
|
||||
/* Test that creates and destroys contexts repeatedly */
|
||||
|
||||
#define NTHREADS 8
|
||||
#define ITERS 64
|
||||
#define MAX_DEVCOUNT 128
|
||||
|
||||
struct thread_info {
|
||||
int number;
|
||||
@@ -85,28 +93,99 @@ struct thread_info {
|
||||
int iteration;
|
||||
} tinfo[NTHREADS];
|
||||
|
||||
atomic_bool no_access[MAX_DEVCOUNT];
|
||||
|
||||
/* Function called by backend during device initialization to convert
|
||||
* multi-byte fields in the device descriptor to host-endian format.
|
||||
* Copied from libusbi.h as we want test to be realistic and not depend on internals.
|
||||
*/
|
||||
static inline void usbi_localize_device_descriptor(struct libusb_device_descriptor *desc)
|
||||
{
|
||||
desc->bcdUSB = libusb_le16_to_cpu(desc->bcdUSB);
|
||||
desc->idVendor = libusb_le16_to_cpu(desc->idVendor);
|
||||
desc->idProduct = libusb_le16_to_cpu(desc->idProduct);
|
||||
desc->bcdDevice = libusb_le16_to_cpu(desc->bcdDevice);
|
||||
}
|
||||
|
||||
static thread_return_t THREAD_CALL_TYPE init_and_exit(void * arg)
|
||||
{
|
||||
struct thread_info *ti = (struct thread_info *) arg;
|
||||
|
||||
for (int i = 0; i < ITERS; ++i) {
|
||||
for (ti->iteration = 0; ti->iteration < ITERS && !ti->err; ti->iteration++) {
|
||||
libusb_context *ctx = NULL;
|
||||
int r;
|
||||
|
||||
r = libusb_init_context(&ctx, /*options=*/NULL, /*num_options=*/0);
|
||||
if (r != LIBUSB_SUCCESS) {
|
||||
ti->err = r;
|
||||
ti->iteration = i;
|
||||
return (thread_return_t) THREAD_RETURN_VALUE;
|
||||
if ((ti->err = libusb_init_context(&ctx, /*options=*/NULL, /*num_options=*/0)) != 0) {
|
||||
break;
|
||||
}
|
||||
if (ti->enumerate) {
|
||||
libusb_device **devs;
|
||||
ti->devcount = libusb_get_device_list(ctx, &devs);
|
||||
if (ti->devcount < 0) {
|
||||
libusb_free_device_list(devs, 1);
|
||||
ti->iteration = i;
|
||||
ti->err = (int)ti->devcount;
|
||||
break;
|
||||
}
|
||||
for (int i = 0; i < ti->devcount; i++) {
|
||||
libusb_device *dev = devs[i];
|
||||
struct libusb_device_descriptor desc;
|
||||
if ((ti->err = libusb_get_device_descriptor(dev, &desc)) != 0) {
|
||||
break;
|
||||
}
|
||||
if (no_access[i]) {
|
||||
continue;
|
||||
}
|
||||
libusb_device_handle *dev_handle;
|
||||
int open_err = libusb_open(dev, &dev_handle);
|
||||
if (open_err == LIBUSB_ERROR_ACCESS) {
|
||||
/* Use atomic swap to ensure we print warning only once across all threads.
|
||||
This is a warning and not a hard error because it should be fine to run tests
|
||||
even if we don't have access to some devices. */
|
||||
if (!atomic_exchange(&no_access[i], true)) {
|
||||
fprintf(stderr, "No access to device %04x:%04x, skipping transfer tests.\n", desc.idVendor, desc.idProduct);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (open_err != 0) {
|
||||
ti->err = open_err;
|
||||
break;
|
||||
}
|
||||
/* Request raw descriptor via control transfer.
|
||||
This tests opening, transferring and closing from multiple threads in parallel. */
|
||||
struct libusb_device_descriptor raw_desc;
|
||||
int raw_desc_len = libusb_get_descriptor(dev_handle, LIBUSB_DT_DEVICE, 0, (unsigned char *)&raw_desc, sizeof(raw_desc));
|
||||
if (raw_desc_len < 0) {
|
||||
ti->err = raw_desc_len;
|
||||
goto close;
|
||||
}
|
||||
if (raw_desc_len != sizeof(raw_desc)) {
|
||||
fprintf(stderr, "Thread %d: device %d: unexpected raw descriptor length %d\n",
|
||||
ti->number, i, raw_desc_len);
|
||||
ti->err = LIBUSB_ERROR_OTHER;
|
||||
goto close;
|
||||
}
|
||||
usbi_localize_device_descriptor(&raw_desc);
|
||||
#define ASSERT_EQ(field) if (raw_desc.field != desc.field) { \
|
||||
fprintf(stderr, "Thread %d: device %d: mismatch in field " #field ": %d != %d\n", \
|
||||
ti->number, i, raw_desc.field, desc.field); \
|
||||
ti->err = LIBUSB_ERROR_OTHER; \
|
||||
goto close; \
|
||||
}
|
||||
ASSERT_EQ(bLength);
|
||||
ASSERT_EQ(bDescriptorType);
|
||||
ASSERT_EQ(bcdUSB);
|
||||
ASSERT_EQ(bDeviceClass);
|
||||
ASSERT_EQ(bDeviceSubClass);
|
||||
ASSERT_EQ(bDeviceProtocol);
|
||||
ASSERT_EQ(bMaxPacketSize0);
|
||||
ASSERT_EQ(idVendor);
|
||||
ASSERT_EQ(idProduct);
|
||||
ASSERT_EQ(bcdDevice);
|
||||
ASSERT_EQ(iManufacturer);
|
||||
ASSERT_EQ(iProduct);
|
||||
ASSERT_EQ(iSerialNumber);
|
||||
ASSERT_EQ(bNumConfigurations);
|
||||
close:
|
||||
libusb_close(dev_handle);
|
||||
}
|
||||
libusb_free_device_list(devs, 1);
|
||||
}
|
||||
|
||||
@@ -123,6 +202,7 @@ static int test_multi_init(int enumerate)
|
||||
|
||||
printf("Starting %d threads\n", NTHREADS);
|
||||
for (t = 0; t < NTHREADS; t++) {
|
||||
tinfo[t].err = 0;
|
||||
tinfo[t].number = t;
|
||||
tinfo[t].enumerate = enumerate;
|
||||
thread_create(&threadId[t], &init_and_exit, (void *) &tinfo[t]);
|
||||
@@ -138,17 +218,9 @@ static int test_multi_init(int enumerate)
|
||||
tinfo[t].iteration,
|
||||
libusb_error_name(tinfo[t].err));
|
||||
} else if (enumerate) {
|
||||
if (tinfo[t].devcount < 0) {
|
||||
errs++;
|
||||
fprintf(stderr,
|
||||
"Thread %d failed to enumerate devices (iteration %d)\n",
|
||||
tinfo[t].number,
|
||||
tinfo[t].iteration);
|
||||
} else {
|
||||
printf("Thread %d discovered %ld devices\n",
|
||||
printf("Thread %d discovered %ld devices\n",
|
||||
tinfo[t].number,
|
||||
(long int) tinfo[t].devcount);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user