[tor-commits] [tor/master] Add a timeout to tor_cond_wait; add tor_cond impl from libevent
nickm at torproject.org
nickm at torproject.org
Wed Jan 21 19:50:30 UTC 2015
commit e865248156a8512d756be003118de446d29611d1
Author: Nick Mathewson <nickm at torproject.org>
Date: Sun Sep 22 22:08:41 2013 -0400
Add a timeout to tor_cond_wait; add tor_cond impl from libevent
The windows code may need some tweaks for it to compile; I've not
tested it yet.
---
src/common/compat_pthreads.c | 24 +++++--
src/common/compat_threads.h | 19 ++++-
src/common/compat_winthreads.c | 152 ++++++++++++++++++++++++----------------
3 files changed, 126 insertions(+), 69 deletions(-)
diff --git a/src/common/compat_pthreads.c b/src/common/compat_pthreads.c
index 276b244..0e5d33a 100644
--- a/src/common/compat_pthreads.c
+++ b/src/common/compat_pthreads.c
@@ -148,10 +148,6 @@ tor_get_thread_id(void)
/* Conditions. */
-/** Cross-platform condition implementation. */
-struct tor_cond_t {
- pthread_cond_t cond;
-};
/** Return a newly allocated condition, with nobody waiting on it. */
tor_cond_t *
tor_cond_new(void)
@@ -177,11 +173,25 @@ tor_cond_free(tor_cond_t *cond)
}
/** Wait until one of the tor_cond_signal functions is called on <b>cond</b>.
* All waiters on the condition must wait holding the same <b>mutex</b>.
- * Returns 0 on success, negative on failure. */
+ * Returns 0 on success, -1 on failure, 1 on timeout. */
int
-tor_cond_wait(tor_cond_t *cond, tor_mutex_t *mutex)
+tor_cond_wait(tor_cond_t *cond, tor_mutex_t *mutex, const struct timeval *tv)
{
- return pthread_cond_wait(&cond->cond, &mutex->mutex) ? -1 : 0;
+ if (tv == NULL) {
+ return pthread_cond_wait(&cond->cond, &mutex->mutex) ? -1 : 0;
+ } else {
+ struct timespec ts;
+ int r;
+ ts.tv_sec = tv->tv_sec;
+ ts.tv_nsec = tv->tv_usec * 1000;
+ r = pthread_cond_timedwait(&cond->cond, &mutex->mutex, &ts);
+ if (r == 0)
+ return 0;
+ else if (r == ETIMEDOUT)
+ return 1;
+ else
+ return -1;
+ }
}
/** Wake up one of the waiters on <b>cond</b>. */
void
diff --git a/src/common/compat_threads.h b/src/common/compat_threads.h
index f43e74a..bbd782f 100644
--- a/src/common/compat_threads.h
+++ b/src/common/compat_threads.h
@@ -57,10 +57,25 @@ void tor_threads_init(void);
void set_main_thread(void);
int in_main_thread(void);
-typedef struct tor_cond_t tor_cond_t;
+typedef struct tor_cond_t {
+#ifdef USE_PTHREADS
+ pthread_cond_t cond;
+#elif defined(USE_WIN32_THREADS)
+ HANDLE event;
+
+ CRITICAL_SECTION lock;
+ int n_waiting;
+ int n_to_wake;
+ int generation;
+#else
+#error no known condition implementation.
+#endif
+} tor_cond_t;
+
tor_cond_t *tor_cond_new(void);
void tor_cond_free(tor_cond_t *cond);
-int tor_cond_wait(tor_cond_t *cond, tor_mutex_t *mutex);
+int tor_cond_wait(tor_cond_t *cond, tor_mutex_t *mutex,
+ const struct timeval *tv);
void tor_cond_signal_one(tor_cond_t *cond);
void tor_cond_signal_all(tor_cond_t *cond);
diff --git a/src/common/compat_winthreads.c b/src/common/compat_winthreads.c
index 01332fd..634dfbe 100644
--- a/src/common/compat_winthreads.c
+++ b/src/common/compat_winthreads.c
@@ -70,17 +70,20 @@ tor_get_thread_id(void)
return (unsigned long)GetCurrentThreadId();
}
-static DWORD cond_event_tls_index;
-struct tor_cond_t {
- CRITICAL_SECTION mutex;
- smartlist_t *events;
-};
tor_cond_t *
tor_cond_new(void)
{
- tor_cond_t *cond = tor_malloc_zero(sizeof(tor_cond_t));
- InitializeCriticalSection(&cond->mutex);
- cond->events = smartlist_new();
+ tor_cond_t *cond = tor_malloc(sizeof(tor_cond_t));
+ if (InitializeCriticalSectionAndSpinCount(&cond->lock, SPIN_COUNT)==0) {
+ tor_free(cond);
+ return NULL;
+ }
+ if ((cond->event = CreateEvent(NULL,TRUE,FALSE,NULL)) == NULL) {
+ DeleteCriticalSection(&cond->lock);
+ tor_free(cond);
+ return NULL;
+ }
+ cond->n_waiting = cond->n_to_wake = cond->generation = 0;
return cond;
}
void
@@ -88,74 +91,103 @@ tor_cond_free(tor_cond_t *cond)
{
if (!cond)
return;
- DeleteCriticalSection(&cond->mutex);
- /* XXXX notify? */
- smartlist_free(cond->events);
- tor_free(cond);
+ DeleteCriticalSection(&cond->lock);
+ CloseHandle(cond->event);
+ mm_free(cond);
}
-int
-tor_cond_wait(tor_cond_t *cond, tor_mutex_t *mutex)
+
+static void
+tor_cond_signal_impl(tor_cond_t *cond, int broadcast)
{
- HANDLE event;
- int r;
- tor_assert(cond);
- tor_assert(mutex);
- event = TlsGetValue(cond_event_tls_index);
- if (!event) {
- event = CreateEvent(0, FALSE, FALSE, NULL);
- TlsSetValue(cond_event_tls_index, event);
- }
- EnterCriticalSection(&cond->mutex);
-
- tor_assert(WaitForSingleObject(event, 0) == WAIT_TIMEOUT);
- tor_assert(!smartlist_contains(cond->events, event));
- smartlist_add(cond->events, event);
-
- LeaveCriticalSection(&cond->mutex);
-
- tor_mutex_release(mutex);
- r = WaitForSingleObject(event, INFINITE);
- tor_mutex_acquire(mutex);
-
- switch (r) {
- case WAIT_OBJECT_0: /* we got the mutex normally. */
- break;
- case WAIT_ABANDONED: /* holding thread exited. */
- case WAIT_TIMEOUT: /* Should never happen. */
- tor_assert(0);
- break;
- case WAIT_FAILED:
- log_warn(LD_GENERAL, "Failed to acquire mutex: %d",(int) GetLastError());
- }
+ EnterCriticalSection(&cond->lock);
+ if (broadcast)
+ cond->n_to_wake = cond->n_waiting;
+ else
+ ++cond->n_to_wake;
+ cond->generation++;
+ SetEvent(cond->event);
+ LeaveCriticalSection(&cond->lock);
return 0;
}
void
tor_cond_signal_one(tor_cond_t *cond)
{
- HANDLE event;
- tor_assert(cond);
-
- EnterCriticalSection(&cond->mutex);
-
- if ((event = smartlist_pop_last(cond->events)))
- SetEvent(event);
-
- LeaveCriticalSection(&cond->mutex);
+ tor_cond_signal_impl(cond, 0);
}
void
tor_cond_signal_all(tor_cond_t *cond)
{
- tor_assert(cond);
+ tor_cond_signal_impl(cond, 1);
+}
- EnterCriticalSection(&cond->mutex);
- SMARTLIST_FOREACH(cond->events, HANDLE, event, SetEvent(event));
- smartlist_clear(cond->events);
- LeaveCriticalSection(&cond->mutex);
+int
+tor_cond_wait(tor_cond_t *cond, tor_mutex_t *lock, const struct timeval *tv)
+{
+ CRITICAL_SECTION *lock = &lock->mutex;
+ int generation_at_start;
+ int waiting = 1;
+ int result = -1;
+ DWORD ms = INFINITE, ms_orig = INFINITE, startTime, endTime;
+ if (tv)
+ ms_orig = ms = evutil_tv_to_msec_(tv);
+
+ EnterCriticalSection(&cond->lock);
+ ++cond->n_waiting;
+ generation_at_start = cond->generation;
+ LeaveCriticalSection(&cond->lock);
+
+ LeaveCriticalSection(lock);
+
+ startTime = GetTickCount();
+ do {
+ DWORD res;
+ res = WaitForSingleObject(cond->event, ms);
+ EnterCriticalSection(&cond->lock);
+ if (cond->n_to_wake &&
+ cond->generation != generation_at_start) {
+ --cond->n_to_wake;
+ --cond->n_waiting;
+ result = 0;
+ waiting = 0;
+ goto out;
+ } else if (res != WAIT_OBJECT_0) {
+ result = (res==WAIT_TIMEOUT) ? 1 : -1;
+ --cond->n_waiting;
+ waiting = 0;
+ goto out;
+ } else if (ms != INFINITE) {
+ endTime = GetTickCount();
+ if (startTime + ms_orig <= endTime) {
+ result = 1; /* Timeout */
+ --cond->n_waiting;
+ waiting = 0;
+ goto out;
+ } else {
+ ms = startTime + ms_orig - endTime;
+ }
+ }
+ /* If we make it here, we are still waiting. */
+ if (cond->n_to_wake == 0) {
+ /* There is nobody else who should wake up; reset
+ * the event. */
+ ResetEvent(cond->event);
+ }
+ out:
+ LeaveCriticalSection(&cond->lock);
+ } while (waiting);
+
+ EnterCriticalSection(lock);
+
+ EnterCriticalSection(&cond->lock);
+ if (!cond->n_waiting)
+ ResetEvent(cond->event);
+ LeaveCriticalSection(&cond->lock);
+
+ return result;
}
void
tor_threads_init(void)
{
- cond_event_tls_index = TlsAlloc();
set_main_thread();
}
More information about the tor-commits
mailing list