]> granicus.if.org Git - libevent/commitdiff
Revise the locking API: deprecate the old locking callbacks and add trylock.
authorNick Mathewson <nickm@torproject.org>
Fri, 27 Nov 2009 20:20:43 +0000 (15:20 -0500)
committerNick Mathewson <nickm@torproject.org>
Fri, 27 Nov 2009 22:36:24 +0000 (17:36 -0500)
Previously, there was no good way to request different kinds of lock
(say, read/write vs writeonly or recursive vs nonrecursive), or for a
lock function to signal failure (which would be important for a
trylock mode).

This patch revises the lock API to be a bit more useful.  The older
lock calls are still supported for now.

We also add a debugging mode to catch common errors in using the
locking APIs.

12 files changed:
Makefile.am
buffer.c
bufferevent.c
defer-internal.h
evdns.c
event.c
evthread-internal.h
evthread.c [new file with mode: 0644]
evthread_pthread.c
evthread_win32.c
include/event2/thread.h
test/regress_main.c

index b1fa3ca227bc58a11f778a11522fb6815a7117e0..4608911ac2b6a0de5da97c8556cb94092ec7e3ae 100644 (file)
@@ -107,7 +107,7 @@ event-config.h: config.h
            -e 's/#ifndef /#ifndef _EVENT_/' < config.h >> $@
        echo "#endif" >> $@
 
-CORE_SRC = event.c buffer.c \
+CORE_SRC = event.c evthread.c buffer.c \
        bufferevent.c bufferevent_sock.c bufferevent_filter.c \
        bufferevent_pair.c listener.c \
        evmap.c log.c evutil.c strlcpy.c $(SYS_SRC)
index 633f2d219270ea755aba6aacf5059d77820bbe66..a184c8dc5e357de74ff9a901b321db7c39e6a850 100644 (file)
--- a/buffer.c
+++ b/buffer.c
@@ -311,7 +311,7 @@ evbuffer_enable_locking(struct evbuffer *buf, void *lock)
                 return -1;
 
         if (!lock) {
-                EVTHREAD_ALLOC_LOCK(lock);
+                EVTHREAD_ALLOC_LOCK(lock, EVTHREAD_LOCKTYPE_RECURSIVE);
                 if (!lock)
                         return -1;
                 buf->lock = lock;
@@ -441,7 +441,7 @@ _evbuffer_decref_and_unlock(struct evbuffer *buffer)
 
        EVBUFFER_UNLOCK(buffer, EVTHREAD_WRITE);
         if (buffer->own_lock)
-                EVTHREAD_FREE_LOCK(buffer->lock);
+                EVTHREAD_FREE_LOCK(buffer->lock, EVTHREAD_LOCKTYPE_RECURSIVE);
        mm_free(buffer);
 }
 
index 39a062c1e3b0010c4efe4db2ba36134241944f38..20b2afb02a3364c413049cc834056c87adbd759f 100644 (file)
@@ -508,7 +508,8 @@ _bufferevent_decref_and_unlock(struct bufferevent *bufev)
 
        BEV_UNLOCK(bufev);
        if (bufev_private->own_lock)
-               EVTHREAD_FREE_LOCK(bufev_private->lock);
+               EVTHREAD_FREE_LOCK(bufev_private->lock,
+                   EVTHREAD_LOCKTYPE_RECURSIVE);
 
        /* Free the actual allocated memory. */
        mm_free(bufev - bufev->be_ops->mem_offset);
@@ -549,7 +550,7 @@ bufferevent_enable_locking(struct bufferevent *bufev, void *lock)
                BEV_UPCAST(bufev)->lock = lock;
                BEV_UPCAST(bufev)->own_lock = 0;
        } else if (!lock) {
-               EVTHREAD_ALLOC_LOCK(lock);
+               EVTHREAD_ALLOC_LOCK(lock, EVTHREAD_LOCKTYPE_RECURSIVE);
                if (!lock)
                        return -1;
                BEV_UPCAST(bufev)->lock = lock;
index a1b1d2c8a9b253f5f72db71b3faea5523f1cbdce..8a73dccce1580be8bcef6a85e1c44b8360574457 100644 (file)
@@ -88,15 +88,13 @@ void event_deferred_cb_schedule(struct deferred_cb_queue *, struct deferred_cb *
 #define LOCK_DEFERRED_QUEUE(q)                                         \
        do {                                                            \
                if ((q)->lock)                                          \
-                       _evthread_locking_fn(EVTHREAD_LOCK|EVTHREAD_WRITE, \
-                           (q)->lock);                                 \
+                       _evthread_lock_fns.lock(0, (q)->lock);          \
        } while (0)
 
 #define UNLOCK_DEFERRED_QUEUE(q)                                       \
        do {                                                            \
                if ((q)->lock)                                          \
-                       _evthread_locking_fn(EVTHREAD_UNLOCK|EVTHREAD_WRITE, \
-                           (q)->lock);                                 \
+                       _evthread_lock_fns.unlock(0, (q)->lock);        \
        } while (0)
 #endif
 
diff --git a/evdns.c b/evdns.c
index 9a4179996b7ffa7d740ccdb797cce8f90685200b..ce2983abd6fab537d72d75b219c97ecde12251f7 100644 (file)
--- a/evdns.c
+++ b/evdns.c
@@ -1688,7 +1688,7 @@ evdns_add_server_port_with_base(struct event_base *base, evutil_socket_t socket,
                mm_free(port);
                return NULL;
        }
-       EVTHREAD_ALLOC_LOCK(port->lock);
+       EVTHREAD_ALLOC_LOCK(port->lock, EVTHREAD_LOCKTYPE_RECURSIVE);
        return port;
 }
 
@@ -2082,7 +2082,7 @@ server_port_free(struct evdns_server_port *port)
                port->socket = -1;
        }
        (void) event_del(&port->event);
-       EVTHREAD_FREE_LOCK(port->lock);
+       EVTHREAD_FREE_LOCK(port->lock, EVTHREAD_LOCKTYPE_RECURSIVE);
        mm_free(port);
 }
 
@@ -3654,7 +3654,7 @@ evdns_base_new(struct event_base *event_base, int initialize_nameservers)
        memset(base, 0, sizeof(struct evdns_base));
        base->req_waiting_head = NULL;
 
-       EVTHREAD_ALLOC_LOCK(base->lock);
+       EVTHREAD_ALLOC_LOCK(base->lock, EVTHREAD_LOCKTYPE_RECURSIVE);
        EVDNS_LOCK(base);
 
        /* Set max requests inflight and allocate req_heads. */
@@ -3773,7 +3773,7 @@ evdns_base_free_and_unlock(struct evdns_base *base, int fail_requests)
                base->global_search_state = NULL;
        }
        EVDNS_UNLOCK(base);
-       EVTHREAD_FREE_LOCK(base->lock);
+       EVTHREAD_FREE_LOCK(base->lock, EVTHREAD_LOCKTYPE_RECURSIVE);
 
        mm_free(base);
 }
diff --git a/event.c b/event.c
index c403d603f1bd294015dbf945dc88e8f92b653217..d58416c3444e53bd85e8c148c5013c84b815c372 100644 (file)
--- a/event.c
+++ b/event.c
@@ -352,9 +352,11 @@ event_base_new_with_config(struct event_config *cfg)
 #ifndef _EVENT_DISABLE_THREAD_SUPPORT
        if (!cfg || !(cfg->flags & EVENT_BASE_FLAG_NOLOCK)) {
                int r;
-               EVTHREAD_ALLOC_LOCK(base->th_base_lock);
+               EVTHREAD_ALLOC_LOCK(base->th_base_lock,
+                   EVTHREAD_LOCKTYPE_RECURSIVE);
                base->defer_queue.lock = base->th_base_lock;
-               EVTHREAD_ALLOC_LOCK(base->current_event_lock);
+               EVTHREAD_ALLOC_LOCK(base->current_event_lock,
+                   EVTHREAD_LOCKTYPE_RECURSIVE);
                r = evthread_make_base_notifiable(base);
                if (r<0) {
                        event_base_free(base);
@@ -475,8 +477,9 @@ event_base_free(struct event_base *base)
        evmap_io_clear(&base->io);
        evmap_signal_clear(&base->sigmap);
 
-       EVTHREAD_FREE_LOCK(base->th_base_lock);
-       EVTHREAD_FREE_LOCK(base->current_event_lock);
+       EVTHREAD_FREE_LOCK(base->th_base_lock, EVTHREAD_LOCKTYPE_RECURSIVE);
+       EVTHREAD_FREE_LOCK(base->current_event_lock,
+           EVTHREAD_LOCKTYPE_RECURSIVE);
 
        mm_free(base);
 }
@@ -2083,20 +2086,6 @@ event_set_mem_functions(void *(*malloc_fn)(size_t sz),
 }
 #endif
 
-#ifndef _EVENT_DISABLE_THREAD_SUPPORT
-/* support for threading */
-void (*_evthread_locking_fn)(int mode, void *lock) = NULL;
-unsigned long (*_evthread_id_fn)(void) = NULL;
-void *(*_evthread_lock_alloc_fn)(void) = NULL;
-void (*_evthread_lock_free_fn)(void *) = NULL;
-
-void
-evthread_set_locking_callback(void (*locking_fn)(int mode, void *lock))
-{
-       _evthread_locking_fn = locking_fn;
-}
-#endif
-
 #if defined(_EVENT_HAVE_EVENTFD) && defined(_EVENT_HAVE_SYS_EVENTFD_H)
 static void
 evthread_notify_drain_eventfd(int fd, short what, void *arg)
@@ -2120,14 +2109,6 @@ evthread_notify_drain_default(evutil_socket_t fd, short what, void *arg)
 #endif
 }
 
-#ifndef _EVENT_DISABLE_THREAD_SUPPORT
-void
-evthread_set_id_callback(unsigned long (*id_fn)(void))
-{
-       _evthread_id_fn = id_fn;
-}
-#endif
-
 int
 evthread_make_base_notifiable(struct event_base *base)
 {
@@ -2191,16 +2172,6 @@ evthread_make_base_notifiable(struct event_base *base)
        return event_add(&base->th_notify, NULL);
 }
 
-#ifndef _EVENT_DISABLE_THREAD_SUPPORT
-void
-evthread_set_lock_create_callbacks(void *(*alloc_fn)(void),
-    void (*free_fn)(void *))
-{
-       _evthread_lock_alloc_fn = alloc_fn;
-       _evthread_lock_free_fn = free_fn;
-}
-#endif
-
 void
 event_base_dump_events(struct event_base *base, FILE *output)
 {
index 7022e0d61b7010e96d070a57379683816a402104..623bac9e01da4e3e7e0bda975c9fe9e7bb80f250 100644 (file)
@@ -38,10 +38,8 @@ struct event_base;
 #ifndef _EVENT_DISABLE_THREAD_SUPPORT
 /* Global function pointers to lock-related functions. NULL if locking isn't
    enabled. */
-extern void (*_evthread_locking_fn)(int mode, void *lock);
+extern struct evthread_lock_callbacks _evthread_lock_fns;
 extern unsigned long (*_evthread_id_fn)(void);
-extern void *(*_evthread_lock_alloc_fn)(void);
-extern void (*_evthread_lock_free_fn)(void *);
 
 /** True iff the given event_base is set up to use locking */
 #define EVBASE_USING_LOCKS(base)                       \
@@ -59,29 +57,30 @@ extern void (*_evthread_lock_free_fn)(void *);
 
 /** Allocate a new lock, and store it in lockvar, a void*.  Sets lockvar to
     NULL if locking is not enabled. */
-#define EVTHREAD_ALLOC_LOCK(lockvar)           \
-       ((lockvar) = _evthread_lock_alloc_fn ?  \
-           _evthread_lock_alloc_fn() : NULL)
+#define EVTHREAD_ALLOC_LOCK(lockvar, locktype)         \
+       ((lockvar) = _evthread_lock_fns.alloc ?         \
+           _evthread_lock_fns.alloc(locktype) : NULL)
 
 /** Free a given lock, if it is present and locking is enabled. */
-#define EVTHREAD_FREE_LOCK(lockvar)                            \
-       do {                                                    \
-               if (lockvar && _evthread_lock_free_fn)          \
-                       _evthread_lock_free_fn(lockvar);        \
+#define EVTHREAD_FREE_LOCK(lockvar, locktype)                          \
+       do {                                                            \
+               void *_lock_tmp_ = (lockvar);                           \
+               if (_lock_tmp_ && _evthread_lock_fns.free)              \
+                       _evthread_lock_fns.free(_lock_tmp_, (locktype)); \
        } while (0)
 
 /** Acquire a lock. */
-#define EVLOCK_LOCK(lock,mode)                                 \
+#define EVLOCK_LOCK(lockvar,mode)                                      \
        do {                                                            \
-               if (lock)                                               \
-                       _evthread_locking_fn(EVTHREAD_LOCK|mode, lock); \
+               if (lockvar)                                            \
+                       _evthread_lock_fns.lock(mode, lockvar);         \
        } while (0)
 
 /** Release a lock */
-#define EVLOCK_UNLOCK(lock,mode)                                       \
+#define EVLOCK_UNLOCK(lockvar,mode)                                    \
        do {                                                            \
-               if (lock)                                               \
-                       _evthread_locking_fn(EVTHREAD_UNLOCK|mode, lock); \
+               if (lockvar)                                            \
+                       _evthread_lock_fns.unlock(mode, lockvar);       \
        } while (0)
 
 /** Helper: put lockvar1 and lockvar2 into pointerwise ascending order. */
@@ -119,24 +118,22 @@ extern void (*_evthread_lock_free_fn)(void *);
 
 
 /** Lock an event_base, if it is set up for locking.  Acquires the lock
-    in the base structure whose field is named 'lock'. */
-#define EVBASE_ACQUIRE_LOCK(base, mode, lock) do {                     \
+    in the base structure whose field is named 'lck'. */
+#define EVBASE_ACQUIRE_LOCK(base, mode, lockvar) do {                  \
                if (EVBASE_USING_LOCKS(base))                           \
-                       _evthread_locking_fn(EVTHREAD_LOCK | mode,      \
-                           (base)->lock);                              \
+                       _evthread_lock_fns.lock(mode, (base)->lockvar); \
        } while (0)
 
 /** Unlock an event_base, if it is set up for locking. */
-#define EVBASE_RELEASE_LOCK(base, mode, lock) do {                     \
+#define EVBASE_RELEASE_LOCK(base, mode, lockvar) do {                  \
                if (EVBASE_USING_LOCKS(base))                           \
-                       _evthread_locking_fn(EVTHREAD_UNLOCK | mode,    \
-                           (base)->lock);                              \
+                       _evthread_lock_fns.unlock(mode, (base)->lockvar); \
        } while (0)
 #else /* _EVENT_DISABLE_THREAD_SUPPORT */
 
 #define EVTHREAD_GET_ID()      1
-#define EVTHREAD_ALLOC_LOCK(lockvar) _EVUTIL_NIL_STMT
-#define EVTHREAD_FREE_LOCK(lockvar) _EVUTIL_NIL_STMT
+#define EVTHREAD_ALLOC_LOCK(lockvar, locktype) _EVUTIL_NIL_STMT
+#define EVTHREAD_FREE_LOCK(lockvar, locktype) _EVUTIL_NIL_STMT
 
 #define EVLOCK_LOCK(lockvar, mode) _EVUTIL_NIL_STMT
 #define EVLOCK_UNLOCK(lockvar, mode) _EVUTIL_NIL_STMT
diff --git a/evthread.c b/evthread.c
new file mode 100644 (file)
index 0000000..c412160
--- /dev/null
@@ -0,0 +1,233 @@
+/*
+ * Copyright (c) 2008-2009 Niels Provos, Nick Mathewson
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "event-config.h"
+
+#ifndef _EVENT_DISABLE_THREAD_SUPPORT
+
+#include <event2/thread.h>
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "log-internal.h"
+#include "mm-internal.h"
+#include "util-internal.h"
+
+/* globals */
+static int lock_debugging_enabled = 0;
+struct evthread_lock_callbacks _evthread_lock_fns = {
+       0, 0, NULL, NULL, NULL, NULL
+};
+/* Used for debugging */
+static struct evthread_lock_callbacks _original_lock_fns = {
+       0, 0, NULL, NULL, NULL, NULL
+};
+unsigned long (*_evthread_id_fn)(void) = NULL;
+
+void
+evthread_set_id_callback(unsigned long (*id_fn)(void))
+{
+       _evthread_id_fn = id_fn;
+}
+
+int
+evthread_set_lock_callbacks(const struct evthread_lock_callbacks *cbs)
+{
+       struct evthread_lock_callbacks *target =
+           lock_debugging_enabled ? &_original_lock_fns : &_evthread_lock_fns;
+
+       if (!cbs) {
+               memset(target, 0, sizeof(_evthread_lock_fns));
+               return 0;
+       }
+       if (cbs->alloc && cbs->free && cbs->lock && cbs->unlock) {
+               memcpy(target, cbs, sizeof(_evthread_lock_fns));
+               return 0;
+       } else {
+               return -1;
+       }
+}
+
+#ifndef DISABLE_OBSOLETE_LOCK_API
+/* Obsolete: for compatibility only.  Remove these before 2.0.x-stable! */
+
+static void (*_obsolete_locking_fn)(int, void *) = NULL;
+static void *(*_obsolete_lock_alloc_fn)(void) = NULL;
+static void (*_obsolete_lock_free_fn)(void *) = NULL;
+
+static void
+api_warn(void)
+{
+       static int warned = 0;
+       if (!warned) {
+               warned = 1;
+               event_warnx("evthread_set_locking_callback and "
+                   "evthread_set_lock_create_callbacks are obsolete; use "
+                   "evthread_set_lock_callbacks instead.");
+       }
+}
+
+static void *
+compat_lock_alloc(unsigned locktype)
+{
+       if (_obsolete_lock_alloc_fn)
+               return _obsolete_lock_alloc_fn();
+       return NULL;
+}
+
+static void
+compat_lock_free(void *lock, unsigned locktype)
+{
+       if (_obsolete_lock_free_fn)
+               _obsolete_lock_free_fn(lock);
+}
+
+static int
+compat_lock_lock(unsigned mode, void *lock)
+{
+       _obsolete_locking_fn(EVTHREAD_LOCK|EVTHREAD_WRITE, lock);
+       return 0;
+}
+
+
+static int
+compat_lock_unlock(unsigned mode, void *lock)
+{
+       _obsolete_locking_fn(EVTHREAD_UNLOCK|EVTHREAD_WRITE, lock);
+       return 0;
+}
+
+void
+evthread_set_locking_callback(void (*locking_fn)(int mode, void *lock))
+{
+       api_warn();
+       if (locking_fn) {
+               _evthread_lock_fns.lock = compat_lock_lock;
+               _evthread_lock_fns.unlock = compat_lock_unlock;
+       } else {
+               _evthread_lock_fns.lock = NULL;
+               _evthread_lock_fns.unlock = NULL;
+       }
+       _obsolete_locking_fn = locking_fn;
+}
+
+void
+evthread_set_lock_create_callbacks(void *(*alloc_fn)(void),
+    void (*free_fn)(void *))
+{
+       api_warn();
+       _obsolete_lock_alloc_fn = alloc_fn;
+       _obsolete_lock_free_fn = free_fn;
+       _evthread_lock_fns.alloc = alloc_fn ? compat_lock_alloc : NULL;
+       _evthread_lock_fns.free = free_fn ? compat_lock_free : NULL;
+}
+#endif
+
+struct debug_lock {
+       unsigned locktype;
+       int count;
+       void *lock;
+};
+
+static void *
+debug_lock_alloc(unsigned locktype)
+{
+       struct debug_lock *result = mm_malloc(sizeof(struct debug_lock));
+       if (!result)
+               return NULL;
+       if (_original_lock_fns.alloc) {
+               if (!(result->lock = _original_lock_fns.alloc(
+                               locktype|EVTHREAD_LOCKTYPE_RECURSIVE))) {
+                       mm_free(result);
+                       return NULL;
+               }
+       }
+       result->locktype = locktype;
+       result->count = 0;
+       return result;
+}
+
+static void
+debug_lock_free(void *lock_, unsigned locktype)
+{
+       struct debug_lock *lock = lock_;
+       EVUTIL_ASSERT(lock->count == 0);
+       if (_original_lock_fns.free) {
+               _original_lock_fns.free(lock->lock,
+                   lock->locktype|EVTHREAD_LOCKTYPE_RECURSIVE);
+       }
+       lock->lock = NULL;
+       lock->count = -100;
+       mm_free(lock);
+}
+
+static int
+debug_lock_lock(unsigned mode, void *lock_)
+{
+       struct debug_lock *lock = lock_;
+       int res = 0;
+       if (_original_lock_fns.lock)
+               res = _original_lock_fns.lock(mode, lock->lock);
+       if (!res) {
+               ++lock->count;
+               if (!(lock->locktype & EVTHREAD_LOCKTYPE_RECURSIVE))
+                       EVUTIL_ASSERT(lock->count == 1);
+       }
+       return res;
+}
+
+static int
+debug_lock_unlock(unsigned mode, void *lock_)
+{
+       struct debug_lock *lock = lock_;
+       int res = 0;
+       --lock->count;
+       EVUTIL_ASSERT(lock->count >= 0);
+       if (_original_lock_fns.unlock)
+               res = _original_lock_fns.unlock(mode, lock->lock);
+       return res;
+}
+
+void
+evthread_enable_lock_debuging(void)
+{
+       struct evthread_lock_callbacks cbs = {
+               EVTHREAD_LOCK_API_VERSION,
+               EVTHREAD_LOCKTYPE_RECURSIVE,
+               debug_lock_alloc,
+               debug_lock_free,
+               debug_lock_lock,
+               debug_lock_unlock
+       };
+       memcpy(&_original_lock_fns, &_evthread_lock_fns,
+           sizeof(struct evthread_lock_callbacks));
+       memcpy(&_evthread_lock_fns, &cbs,
+           sizeof(struct evthread_lock_callbacks));
+       lock_debugging_enabled = 1;
+}
+
+#endif
index e8e2314ed83083fda7eaf69f6f7b90c83e8fcc37..324777ad31845fb87d835d7aeaf155b8bab42fd9 100644 (file)
@@ -38,12 +38,15 @@ struct event_base;
 static pthread_mutexattr_t attr_recursive;
 
 static void *
-evthread_posix_lock_create(void)
+evthread_posix_lock_alloc(unsigned locktype)
 {
+       pthread_mutexattr_t *attr = NULL;
        pthread_mutex_t *lock = mm_malloc(sizeof(pthread_mutex_t));
        if (!lock)
                return NULL;
-       if (pthread_mutex_init(lock, &attr_recursive)) {
+       if (locktype & EVTHREAD_LOCKTYPE_RECURSIVE)
+               attr = &attr_recursive;
+       if (pthread_mutex_init(lock, attr)) {
                mm_free(lock);
                return NULL;
        }
@@ -51,21 +54,28 @@ evthread_posix_lock_create(void)
 }
 
 static void
-evthread_posix_lock_free(void *_lock)
+evthread_posix_lock_free(void *_lock, unsigned locktype)
 {
        pthread_mutex_t *lock = _lock;
        pthread_mutex_destroy(lock);
        mm_free(lock);
 }
 
-static void
-evthread_posix_lock(int mode, void *_lock)
+static int
+evthread_posix_lock(unsigned mode, void *_lock)
 {
        pthread_mutex_t *lock = _lock;
-       if (0 != (mode & EVTHREAD_LOCK))
-               pthread_mutex_lock(lock);
+       if (mode & EVTHREAD_TRY)
+               return pthread_mutex_trylock(lock);
        else
-               pthread_mutex_unlock(lock);
+               return pthread_mutex_lock(lock);
+}
+
+static int
+evthread_posix_unlock(unsigned mode, void *_lock)
+{
+       pthread_mutex_t *lock = _lock;
+       return pthread_mutex_unlock(lock);
 }
 
 static unsigned long
@@ -82,16 +92,21 @@ evthread_posix_get_id(void)
 int
 evthread_use_pthreads(void)
 {
+       struct evthread_lock_callbacks cbs = {
+               EVTHREAD_LOCK_API_VERSION,
+               EVTHREAD_LOCKTYPE_RECURSIVE,
+               evthread_posix_lock_alloc,
+               evthread_posix_lock_free,
+               evthread_posix_lock,
+               evthread_posix_unlock
+       };
        /* Set ourselves up to get recursive locks. */
        if (pthread_mutexattr_init(&attr_recursive))
                return -1;
        if (pthread_mutexattr_settype(&attr_recursive, PTHREAD_MUTEX_RECURSIVE))
                return -1;
 
-       evthread_set_lock_create_callbacks(
-           evthread_posix_lock_create,
-           evthread_posix_lock_free);
-       evthread_set_locking_callback(evthread_posix_lock);
+       evthread_set_lock_callbacks(&cbs);
        evthread_set_id_callback(evthread_posix_get_id);
        return 0;
 }
index 5e341f8839ab6fe98ab137326c98b1643eb5e634..05ed9217318660123fa6a21c938361173bd90c83 100644 (file)
@@ -39,7 +39,7 @@ struct event_base;
 #include "mm-internal.h"
 
 static void *
-evthread_win32_lock_create(void)
+evthread_win32_lock_create(unsigned locktype)
 {
        CRITICAL_SECTION *lock = mm_malloc(sizeof(CRITICAL_SECTION));
        if (!lock)
@@ -49,20 +49,29 @@ evthread_win32_lock_create(void)
 }
 
 static void
-evthread_win32_lock_free(void *_lock)
+evthread_win32_lock_free(void *_lock, unsigned locktype)
 {
        CRITICAL_SECTION *lock = _lock;
        DeleteCriticalSection(lock);
 }
 
 static void
-evthread_win32_lock(int mode, void *_lock)
+evthread_win32_lock(unsigned mode, void *_lock)
 {
        CRITICAL_SECTION *lock = _lock;
-       if (0 != (mode & EVTHREAD_LOCK))
+       if ((mode & EVTHREAD_TRY)) {
+               return TryEnterCriticalSection(lock) != 0;
+       } else {
                EnterCriticalSection(lock);
-       else
-               LeaveCriticalSection(lock);
+               return 0;
+       }
+}
+
+static void
+evthread_win32_unlock(unsigned mode, void *_lock)
+{
+       CRITICAL_SECTION *lock = _lock;
+       LeaveCriticalSection(lock)
 }
 
 static unsigned long
@@ -74,10 +83,16 @@ evthread_win32_get_id(void)
 int
 evthread_use_windows_threads(void)
 {
-       evthread_set_lock_create_callbacks(
-            evthread_win32_lock_create,
-            evthread_win32_lock_free);
-       evthread_set_locking_callback(evthread_win32_lock);
+       struct evthread_lock_callbacks cbs = {
+               EVTHREAD_LOCK_API_VERSION,
+               EVTHREAD_LOCKTYPE_RECURSIVE,
+               evthread_win32_lock_alloc,
+               evthread_win32_lock_free,
+               evthread_win32_lock,
+               evthread_win32_unlock
+       };
+
+       evthread_set_lock_callbacks(&cbs);
        evthread_set_id_callback(evthread_win32_get_id);
        return 0;
 }
index 73593e7596e4a2a56c8ce3f4507ebfe389241550..f9deca820be7331c7906d7555a8fc4ac3b6770b1 100644 (file)
@@ -59,39 +59,89 @@ extern "C" {
 
 #include <event-config.h>
 
-/* combine (lock|unlock) with (read|write) */
+/** A flag passed to an obsolete locking callback to indicate that this call
+ * is for acquiring a lock. */
 #define EVTHREAD_LOCK  0x01
+/** A flag passed to an obsolete locking callback to indicate that this call
+ * is for releasing a lock. */
 #define EVTHREAD_UNLOCK        0x02
+/** A flag passed to a locking callback when the lock was allocated as a
+ * read-write lock, and we want to acquire or release the lock for writing. */
 #define EVTHREAD_WRITE 0x04
+/** A flag passed to a locking callback when the lock was allocated as a
+ * read-write lock, and we want to acquire or release the lock for reading. */
 #define EVTHREAD_READ  0x08
+/** A flag passed to a locking callback when we don't want to block waiting
+ * for the lock; if we can't get the lock immediately, we will instead
+ * return nonzero from the locking callback. */
+#define EVTHREAD_TRY    0x10
 
 #ifndef _EVENT_DISABLE_THREAD_SUPPORT
 
 /**
-   Sets the functions Libevent should use for allocating and freeing
-   locks.  This needs to be called in addition to
-   evthread_set_locking_callback() before using Libevent in a
-   multi-threaded application.
-
-   Locks must be recursive.  That is, it must be safe for a thread to
-   acquire a lock that it already holds.
-
-   @param alloc_fn function to be called when allocating a new lock
-   @param free_fn function to be called to a free a lock
+   @deprecated Use evthread_set_lock_callbacks instead.  This API will
+     go away before Libevent 2.0.x-stable.
 */
 void evthread_set_lock_create_callbacks(
     void *(*alloc_fn)(void), void (*free_fn)(void *));
 
 /**
-   Sets the function Libevent should use for locking.
-
-   @param locking_fn the function that Libevent should invoke to acquire
-     or release a lock.  mode has either EVTHREAD_LOCK or EVTHREAD_UNLOCK
-     set, and in addition, either EVTHREAD_WRITE or EVTHREAD_READ.
+   @deprecated Use evthread_set_lock_callbacks instead.  This API will
+     go away before Libevent 2.0.x-stable.
  */
 void evthread_set_locking_callback(
     void (*locking_fn)(int mode, void *lock));
 
+#define EVTHREAD_LOCK_API_VERSION 1
+
+/** A recursive lock is one that can be acquired multiple times at once by the
+ * same thread.  No other process can allocate the lock until the thread that
+ * has been holding it has unlocked it as many times as it locked it. */
+#define EVTHREAD_LOCKTYPE_RECURSIVE 1
+/* A read-write lock is one that allows multiple simultaneous readers, but
+ * where any one writer excludes all other writers and readers. */
+#define EVTHREAD_LOCKTYPE_READWRITE 2
+
+/** This structure describes the interface a threading library uses for
+ * locking.   It's used to tell evthread_set_lock_callbacks how to use
+ * locking on this platform.
+ */
+struct evthread_lock_callbacks {
+       /** The current version of the locking API.  Set this to
+        * EVTHREAD_LOCK_API_VERSION */
+       int lock_api_version;
+       /** Which kinds of locks does this version of the locking API
+        * support?  A bitfield of EVTHREAD_LOCKTYPE_RECURSIVE and
+        * EVTHREAD_LOCKTYPE_READWRITE.
+        *
+        * (Note that RECURSIVE locks are currently mandatory, and
+        * READWRITE locks are not currently used.)
+        **/
+       unsigned supported_locktypes;
+       /** Function to allocate and initialize new lock of type 'locktype'.
+        * Returns NULL on failure. */
+       void *(*alloc)(unsigned locktype);
+       /** Funtion to release all storage held in 'lock', which was created
+        * with type 'locktype'. */
+       void (*free)(void *lock, unsigned locktype);
+       /** Acquire an already-allocated lock at 'lock' with mode 'mode'.
+        * Returns 0 on success, and nonzero on failure. */
+       int (*lock)(unsigned mode, void *lock);
+       /** Release a lock at 'lock' using mode 'mode'.  Returns 0 on success,
+        * and nonzero on failure. */
+       int (*unlock)(unsigned mode, void *lock);
+};
+
+/** Sets a group of functions that Libevent should use for locking.
+ * For full information on the required callback API, see the
+ * documentation for the individual members of evthread_lock_callbacks.
+ *
+ * Note that if you're using Windows or the Pthreads threading library, you
+ * probably shouldn't call this function; instead, use
+ * evthread_use_windos_threads() or evthread_use_posix_threads() if you can.
+ */
+int evthread_set_lock_callbacks(const struct evthread_lock_callbacks *);
+
 /**
    Sets the function for determining the thread id.
 
@@ -119,10 +169,15 @@ int evthread_use_windows_threads(void);
        @return 0 on success, -1 on failure. */
 int evthread_use_pthreads(void);
 #define EVTHREAD_USE_PTHREADS_IMPLEMENTED 1
+
+/* XXXXX */
+void evthread_enable_lock_debuging(void);
+
 #endif
 
 #endif /* _EVENT_DISABLE_THREAD_SUPPORT */
 
+struct event_base;
 /** Make sure it's safe to tell an event base to wake up from another thread.
     or a signal handler.
 
index fab48c1e6f3f279cb82c39d4b8aec427c4383c60..b8460db007dbeae47a23ab2e50ec1fff403ed927 100644 (file)
@@ -350,6 +350,11 @@ main(int argc, const char **argv)
        tinytest_skip(testgroups, "http/connection_retry");
 #endif
 
+#ifndef _EVENT_DISABLE_THREAD_SUPPORT
+       if (!getenv("EVENT_NO_DEBUG_LOCKS"))
+               evthread_enable_lock_debuging();
+#endif
+
         if (tinytest_main(argc,argv,testgroups))
                 return 1;