Original description by Dave Hart:
[This patch contains] the addition of a signature field to debug_lock
initialized by the alloc routine and verified by the rest, to catch
invalid lock pointers sooner. That helped me track down a nasty
problem I had before adding the time.h include to
libevent-internal.h, where different .c files had different ideas of
whether event_base had a tod_tv_cache member depending on which
included time.h before libevent-internal.h.
This requires us to have a separate implementation of the lock macros
that indirects to a set of functions. Fortunately, this isn't too
hard to do.
This may be a fix for bug 3042969, where our openssl dll and our
libevent dll each got their own version of the thread stuff.
The interface from the user's POV is similar to the locking
implementation: either provide a structure full of function
pointers, or just call evthread_use_*_threads() and everything will
be okay.
The internal interface is meant to vaguely resemble pthread_cond_*,
which Windows people will better recognize as *ConditionVariable*.
These were added in 2.0.1, and deprecated in 2.0.4 and 2.0.5; we've
promised that they would be removed, and warned whenever they were
invoked. Users should call evthread_set_lock_callbacks instead... or
ideally just call evthread_use_windows_threads or
evthread_use_pthreads.
There were a couple of places in the code where we manually kept lock
counts to make sure we never accessed resources without holding a
lock, and that we never released a lock we didn't have. The
lock-debugging code already puts counts on _every_ lock when lock
debugging is enabled, so there is no need to keep these counts around
otherwise. This patch rewrites the ASSERT_FOO_LOCKED macros to all
use a common EVLOCK_ASSERT_LOCKED().
We also teach the lock debugging code to keep track of who exactly
holds each lock, so that EVLOCK_ASSERT_LOCKED() means "locked by this
thread."
Previously, our default lock model kind of assumed that every lock was
potentially a read-write lock. This was a poor choice, since
read-write locks are far more expensive than regular locks, and so the
lock API should only use them when we can actually take advantage of
them. Neither our pthreads or win32 lock implementation provided rw
locks.
Now that we have a way (not currently used!) to indicate that we
really want a read-write lock, we shouldn't actually say "lock this
for reading" or "lock this for writing" unless we mean it.
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.