Loading adb/sysdeps_win32.c +190 −15 Original line number Diff line number Diff line Loading @@ -1426,6 +1426,180 @@ event_looper_unhook( EventLooper looper, int fd, int events ) } } /* * A fixer for WaitForMultipleObjects on condition that there are more than 64 * handles to wait on. * * In cetain cases DDMS may establish more than 64 connections with ADB. For * instance, this may happen if there are more than 64 processes running on a * device, or there are multiple devices connected (including the emulator) with * the combined number of running processes greater than 64. In this case using * WaitForMultipleObjects to wait on connection events simply wouldn't cut, * because of the API limitations (64 handles max). So, we need to provide a way * to scale WaitForMultipleObjects to accept an arbitrary number of handles. The * easiest (and "Microsoft recommended") way to do that would be dividing the * handle array into chunks with the chunk size less than 64, and fire up as many * waiting threads as there are chunks. Then each thread would wait on a chunk of * handles, and will report back to the caller which handle has been set. * Here is the implementation of that algorithm. */ /* Number of handles to wait on in each wating thread. */ #define WAIT_ALL_CHUNK_SIZE 63 /* Descriptor for a wating thread */ typedef struct WaitForAllParam { /* A handle to an event to signal when waiting is over. This handle is shared * accross all the waiting threads, so each waiting thread knows when any * other thread has exited, so it can exit too. */ HANDLE main_event; /* Upon exit from a waiting thread contains the index of the handle that has * been signaled. The index is an absolute index of the signaled handle in * the original array. This pointer is shared accross all the waiting threads * and it's not guaranteed (due to a race condition) that when all the * waiting threads exit, the value contained here would indicate the first * handle that was signaled. This is fine, because the caller cares only * about any handle being signaled. It doesn't care about the order, nor * about the whole list of handles that were signaled. */ LONG volatile *signaled_index; /* Array of handles to wait on in a waiting thread. */ HANDLE* handles; /* Number of handles in 'handles' array to wait on. */ int handles_count; /* Index inside the main array of the first handle in the 'handles' array. */ int first_handle_index; /* Waiting thread handle. */ HANDLE thread; } WaitForAllParam; /* Waiting thread routine. */ static unsigned __stdcall _in_waiter_thread(void* arg) { HANDLE wait_on[WAIT_ALL_CHUNK_SIZE + 1]; int res; WaitForAllParam* const param = (WaitForAllParam*)arg; /* We have to wait on the main_event in order to be notified when any of the * sibling threads is exiting. */ wait_on[0] = param->main_event; /* The rest of the handles go behind the main event handle. */ memcpy(wait_on + 1, param->handles, param->handles_count * sizeof(HANDLE)); res = WaitForMultipleObjects(param->handles_count + 1, wait_on, FALSE, INFINITE); if (res > 0 && res < (param->handles_count + 1)) { /* One of the original handles got signaled. Save its absolute index into * the output variable. */ InterlockedCompareExchange(param->signaled_index, res - 1L + param->first_handle_index, -1L); } /* Notify the caller (and the siblings) that the wait is over. */ SetEvent(param->main_event); _endthreadex(0); return 0; } /* WaitForMultipeObjects fixer routine. * Param: * handles Array of handles to wait on. * handles_count Number of handles in the array. * Return: * (>= 0 && < handles_count) - Index of the signaled handle in the array, or * WAIT_FAILED on an error. */ static int _wait_for_all(HANDLE* handles, int handles_count) { WaitForAllParam* threads; HANDLE main_event; int chunks, chunk, remains; /* This variable is going to be accessed by several threads at the same time, * this is bound to fail randomly when the core is run on multi-core machines. * To solve this, we need to do the following (1 _and_ 2): * 1. Use the "volatile" qualifier to ensure the compiler doesn't optimize * out the reads/writes in this function unexpectedly. * 2. Ensure correct memory ordering. The "simple" way to do that is to wrap * all accesses inside a critical section. But we can also use * InterlockedCompareExchange() which always provide a full memory barrier * on Win32. */ volatile LONG sig_index = -1; /* Calculate number of chunks, and allocate thread param array. */ chunks = handles_count / WAIT_ALL_CHUNK_SIZE; remains = handles_count % WAIT_ALL_CHUNK_SIZE; threads = (WaitForAllParam*)malloc((chunks + (remains ? 1 : 0)) * sizeof(WaitForAllParam)); if (threads == NULL) { D("Unable to allocate thread array for %d handles.", handles_count); return (int)WAIT_FAILED; } /* Create main event to wait on for all waiting threads. This is a "manualy * reset" event that will remain set once it was set. */ main_event = CreateEvent(NULL, TRUE, FALSE, NULL); if (main_event == NULL) { D("Unable to create main event. Error: %d", GetLastError()); free(threads); return (int)WAIT_FAILED; } /* * Initialize waiting thread parameters. */ for (chunk = 0; chunk < chunks; chunk++) { threads[chunk].main_event = main_event; threads[chunk].signaled_index = &sig_index; threads[chunk].first_handle_index = WAIT_ALL_CHUNK_SIZE * chunk; threads[chunk].handles = handles + threads[chunk].first_handle_index; threads[chunk].handles_count = WAIT_ALL_CHUNK_SIZE; } if (remains) { threads[chunk].main_event = main_event; threads[chunk].signaled_index = &sig_index; threads[chunk].first_handle_index = WAIT_ALL_CHUNK_SIZE * chunk; threads[chunk].handles = handles + threads[chunk].first_handle_index; threads[chunk].handles_count = remains; chunks++; } /* Start the waiting threads. */ for (chunk = 0; chunk < chunks; chunk++) { /* Note that using adb_thread_create is not appropriate here, since we * need a handle to wait on for thread termination. */ threads[chunk].thread = (HANDLE)_beginthreadex(NULL, 0, _in_waiter_thread, &threads[chunk], 0, NULL); if (threads[chunk].thread == NULL) { /* Unable to create a waiter thread. Collapse. */ D("Unable to create a waiting thread %d of %d. errno=%d", chunk, chunks, errno); chunks = chunk; SetEvent(main_event); break; } } /* Wait on any of the threads to get signaled. */ WaitForSingleObject(main_event, INFINITE); /* Wait on all the waiting threads to exit. */ for (chunk = 0; chunk < chunks; chunk++) { WaitForSingleObject(threads[chunk].thread, INFINITE); CloseHandle(threads[chunk].thread); } CloseHandle(main_event); free(threads); const int ret = (int)InterlockedCompareExchange(&sig_index, -1, -1); return (ret >= 0) ? ret : (int)WAIT_FAILED; } static EventLooperRec win32_looper; static void fdevent_init(void) Loading Loading @@ -1525,10 +1699,11 @@ static void fdevent_process() D( "adb_win32: waiting for %d events\n", looper->htab_count ); if (looper->htab_count > MAXIMUM_WAIT_OBJECTS) { D("handle count %d exceeds MAXIMUM_WAIT_OBJECTS, aborting!\n", looper->htab_count); abort(); } D("handle count %d exceeds MAXIMUM_WAIT_OBJECTS.\n", looper->htab_count); wait_ret = _wait_for_all(looper->htab, looper->htab_count); } else { wait_ret = WaitForMultipleObjects( looper->htab_count, looper->htab, FALSE, INFINITE ); } if (wait_ret == (int)WAIT_FAILED) { D( "adb_win32: wait failed, error %ld\n", GetLastError() ); } else { Loading Loading
adb/sysdeps_win32.c +190 −15 Original line number Diff line number Diff line Loading @@ -1426,6 +1426,180 @@ event_looper_unhook( EventLooper looper, int fd, int events ) } } /* * A fixer for WaitForMultipleObjects on condition that there are more than 64 * handles to wait on. * * In cetain cases DDMS may establish more than 64 connections with ADB. For * instance, this may happen if there are more than 64 processes running on a * device, or there are multiple devices connected (including the emulator) with * the combined number of running processes greater than 64. In this case using * WaitForMultipleObjects to wait on connection events simply wouldn't cut, * because of the API limitations (64 handles max). So, we need to provide a way * to scale WaitForMultipleObjects to accept an arbitrary number of handles. The * easiest (and "Microsoft recommended") way to do that would be dividing the * handle array into chunks with the chunk size less than 64, and fire up as many * waiting threads as there are chunks. Then each thread would wait on a chunk of * handles, and will report back to the caller which handle has been set. * Here is the implementation of that algorithm. */ /* Number of handles to wait on in each wating thread. */ #define WAIT_ALL_CHUNK_SIZE 63 /* Descriptor for a wating thread */ typedef struct WaitForAllParam { /* A handle to an event to signal when waiting is over. This handle is shared * accross all the waiting threads, so each waiting thread knows when any * other thread has exited, so it can exit too. */ HANDLE main_event; /* Upon exit from a waiting thread contains the index of the handle that has * been signaled. The index is an absolute index of the signaled handle in * the original array. This pointer is shared accross all the waiting threads * and it's not guaranteed (due to a race condition) that when all the * waiting threads exit, the value contained here would indicate the first * handle that was signaled. This is fine, because the caller cares only * about any handle being signaled. It doesn't care about the order, nor * about the whole list of handles that were signaled. */ LONG volatile *signaled_index; /* Array of handles to wait on in a waiting thread. */ HANDLE* handles; /* Number of handles in 'handles' array to wait on. */ int handles_count; /* Index inside the main array of the first handle in the 'handles' array. */ int first_handle_index; /* Waiting thread handle. */ HANDLE thread; } WaitForAllParam; /* Waiting thread routine. */ static unsigned __stdcall _in_waiter_thread(void* arg) { HANDLE wait_on[WAIT_ALL_CHUNK_SIZE + 1]; int res; WaitForAllParam* const param = (WaitForAllParam*)arg; /* We have to wait on the main_event in order to be notified when any of the * sibling threads is exiting. */ wait_on[0] = param->main_event; /* The rest of the handles go behind the main event handle. */ memcpy(wait_on + 1, param->handles, param->handles_count * sizeof(HANDLE)); res = WaitForMultipleObjects(param->handles_count + 1, wait_on, FALSE, INFINITE); if (res > 0 && res < (param->handles_count + 1)) { /* One of the original handles got signaled. Save its absolute index into * the output variable. */ InterlockedCompareExchange(param->signaled_index, res - 1L + param->first_handle_index, -1L); } /* Notify the caller (and the siblings) that the wait is over. */ SetEvent(param->main_event); _endthreadex(0); return 0; } /* WaitForMultipeObjects fixer routine. * Param: * handles Array of handles to wait on. * handles_count Number of handles in the array. * Return: * (>= 0 && < handles_count) - Index of the signaled handle in the array, or * WAIT_FAILED on an error. */ static int _wait_for_all(HANDLE* handles, int handles_count) { WaitForAllParam* threads; HANDLE main_event; int chunks, chunk, remains; /* This variable is going to be accessed by several threads at the same time, * this is bound to fail randomly when the core is run on multi-core machines. * To solve this, we need to do the following (1 _and_ 2): * 1. Use the "volatile" qualifier to ensure the compiler doesn't optimize * out the reads/writes in this function unexpectedly. * 2. Ensure correct memory ordering. The "simple" way to do that is to wrap * all accesses inside a critical section. But we can also use * InterlockedCompareExchange() which always provide a full memory barrier * on Win32. */ volatile LONG sig_index = -1; /* Calculate number of chunks, and allocate thread param array. */ chunks = handles_count / WAIT_ALL_CHUNK_SIZE; remains = handles_count % WAIT_ALL_CHUNK_SIZE; threads = (WaitForAllParam*)malloc((chunks + (remains ? 1 : 0)) * sizeof(WaitForAllParam)); if (threads == NULL) { D("Unable to allocate thread array for %d handles.", handles_count); return (int)WAIT_FAILED; } /* Create main event to wait on for all waiting threads. This is a "manualy * reset" event that will remain set once it was set. */ main_event = CreateEvent(NULL, TRUE, FALSE, NULL); if (main_event == NULL) { D("Unable to create main event. Error: %d", GetLastError()); free(threads); return (int)WAIT_FAILED; } /* * Initialize waiting thread parameters. */ for (chunk = 0; chunk < chunks; chunk++) { threads[chunk].main_event = main_event; threads[chunk].signaled_index = &sig_index; threads[chunk].first_handle_index = WAIT_ALL_CHUNK_SIZE * chunk; threads[chunk].handles = handles + threads[chunk].first_handle_index; threads[chunk].handles_count = WAIT_ALL_CHUNK_SIZE; } if (remains) { threads[chunk].main_event = main_event; threads[chunk].signaled_index = &sig_index; threads[chunk].first_handle_index = WAIT_ALL_CHUNK_SIZE * chunk; threads[chunk].handles = handles + threads[chunk].first_handle_index; threads[chunk].handles_count = remains; chunks++; } /* Start the waiting threads. */ for (chunk = 0; chunk < chunks; chunk++) { /* Note that using adb_thread_create is not appropriate here, since we * need a handle to wait on for thread termination. */ threads[chunk].thread = (HANDLE)_beginthreadex(NULL, 0, _in_waiter_thread, &threads[chunk], 0, NULL); if (threads[chunk].thread == NULL) { /* Unable to create a waiter thread. Collapse. */ D("Unable to create a waiting thread %d of %d. errno=%d", chunk, chunks, errno); chunks = chunk; SetEvent(main_event); break; } } /* Wait on any of the threads to get signaled. */ WaitForSingleObject(main_event, INFINITE); /* Wait on all the waiting threads to exit. */ for (chunk = 0; chunk < chunks; chunk++) { WaitForSingleObject(threads[chunk].thread, INFINITE); CloseHandle(threads[chunk].thread); } CloseHandle(main_event); free(threads); const int ret = (int)InterlockedCompareExchange(&sig_index, -1, -1); return (ret >= 0) ? ret : (int)WAIT_FAILED; } static EventLooperRec win32_looper; static void fdevent_init(void) Loading Loading @@ -1525,10 +1699,11 @@ static void fdevent_process() D( "adb_win32: waiting for %d events\n", looper->htab_count ); if (looper->htab_count > MAXIMUM_WAIT_OBJECTS) { D("handle count %d exceeds MAXIMUM_WAIT_OBJECTS, aborting!\n", looper->htab_count); abort(); } D("handle count %d exceeds MAXIMUM_WAIT_OBJECTS.\n", looper->htab_count); wait_ret = _wait_for_all(looper->htab, looper->htab_count); } else { wait_ret = WaitForMultipleObjects( looper->htab_count, looper->htab, FALSE, INFINITE ); } if (wait_ret == (int)WAIT_FAILED) { D( "adb_win32: wait failed, error %ld\n", GetLastError() ); } else { Loading