On Tue, May 12, 2009 at 05:31:23PM +0800, Biping MENG wrote:
>      Well, maybe not everywhere. Initially doing it on socket I/O would
>      be a big win, and then eventually on disk I/O. We may need to on the
>      mutexes to prevent deadlocks (depending on the lock), but most mutexes
>      should be pretty short lived, so we may want to always switch on those.

Oops, this should have been "so we may _not_ want..."

>    Actually one user level thread corresponds to one session and many user
>    level threads may run on one kernel thread.
>    So if we switch between user-level threads, mutexes shared by
>    sessions(equal to user-level threads) may cause a deadlock by invoking
>    yield(). Brain also reminded me of this on the IRC. I'm thinking about the
>    relation of these two levels of threads and locks on these two levels. I
>    thought a simple way of avoiding deadlock is to alway require locks
>    sequentially. To keep this principle is an easy way to avoid deadlock. I
>    may need some time to check through the locks and build up a sequence on
>    them.

In theory, yes, but we don't always have that control. Storage engines
can manage their own locking, and we can't depend on them to follow
a particular locking protocol (they manage deadlocks in their own
way). We need to make sure any user-level thread scheduler we create
doesn't create special requirements such as this, and should work
like any other threading libraries.

>    I guess we have to set up a strategy to arrange user-level threads onto
>    kernel thread. I'm thinking of these done in the following ways:
>    A. All kernel threads share one pair of queues of sessions(mixed up with
>    newly connected sessions and switched out sessions by calling yield()).
>    Sessions that have just called yield() must have been blocked for some
>    reason, so should be pushed back into the queue, and may wait for long
>    enough to be swapped in.
>    B. Each kernel thread have its own pair of queues of sessions. The main
>    thread is responsible for assigning newly connected sessions to these
>    queues so as to keep the number of sessions in the queues as even as
>    possible. This may avoid some lock races on access to the queue between
>    kernel threads.
>    Just as in the current implementation, sessions are separated into two
>    groups, that is need_processing and wait_for_io, so queues are always in
>    pairs. Maybe sessions_wait_for_io would rather be sessions_blocked. We can
>    monitor FDs by using libevent when they are ready to read or write. But
>    how could we monitor on mutexes to tell when they are unlocked?

You can't, really, unless you do some expensive tracking. You need
to just try to run threads when they may potentially be unlocked. For
example, a thread blocks because some other thread holds the lock. What
is the other thread waiting for? Another lock or I/O. Anytime a thread
complete the I/O wait or ends up running, the other threads that may
potentially be waiting for a lock the running thread held need to be
run to see if they can proceed. This gets complicated and busy for
the threads, but the goal is to try to eliminate thread contention
to minimize the cases.

So, we've been going down the path of own userland thread context
switching here, but there is still the option of trying to make the
execution engine stateful for non-blocking I/O and non-blocking event
triggers (for wrapping locks). To be honest I'm not sure which is
easier, but the non-blocking execution engine will most likely be more
portable, easier to debug, and can be done incrementally. Just wanted
to point out we have at least two directions we could go in here.

-Eric

_______________________________________________
Mailing list: https://launchpad.net/~drizzle-discuss
Post to     : [email protected]
Unsubscribe : https://launchpad.net/~drizzle-discuss
More help   : https://help.launchpad.net/ListHelp

Reply via email to