On Thu, Jan 15, 2009 at 10:01:32PM +0000, Tim Bunce <[email protected]> wrote:
> Marc, what would need to be added to the DBI (or DBD::Gofer) to support
> asynchronous use via the Coro module?

Method 1, add hooks to DBD::Gofer:

Right now, the only sensible way to go is to use DBD::Gofer and add some
hooks. Basically, each time you block, e.g. in select or read (or write,
if pipelining is to be an option), you need to use some event mechanism
to wait.

There are many ways to achieve that. Coro has an optional (slowish) wrapper
around "select", so instead of doing:

   print $fh $data;

You could do:

   select .... $fh writable; # slightly messy
   print $fh $data;

Method 2: use unblock or subclass:

Another option is to let the user optionally modify the filehandle, e.g.
in addition to:

   nonblock($rfh);

one could do:

   $rfh = $user_callback_to_condition_fh->($rfh)

and the user callback could be:

   sub { Coro::Handle::unblock $_[0] }

"unblock" returns something that acts like a perl file handle on the perl
level, but allows other coroutines to schedule when blocking on it.

Yet another option would be to write a DBD::Gofer::Transport::corostream
which would hardcode the above. It probably should be part of the Coro
module itself (and was on my todo list). Now that I looked at it, it might
be as trivial as subclassing ::stream and overriding start_pipe_command.

The only added complexity is that DBD::Gofer suddenly might receive two
concurrent requests on the same backend - this would not work without some
synchronisation, but a simple "workaround" would be to simply disallow
that, i.e. "you must not make concurrent calls on the same gofer object
without locking yourself", which, in my experience, is enough.

Method 3: AnyEvent::DBI

Now, one can already use DBI "asynchronously": AnyEvent::DBI is an
event-based DBI interface (in its infancy), which incidentally also
supports pipelining. It's use in Coro is trivial (or maybe not, the
following is untested :)

   use Coro;
   use AnyEvent::DBI;

   my $dbh = new AnyEvent::DBI "DBI:SQLite:dbname=test.db", "", "";

   $dbh->exec ("select * from test", 10, rouse_cb);
   my ($rows, $rv) = rouse_wait;

Method 4: patch each and every dbi driver

All the above solutions need a proxy process. It would be vastly faster if
we could do it in-process.

In fatc, I am researching thread support for dynamic languages for almost
a decade now, and my verdict is, it cannot be done, and it makes no
sense, unless you do it to make existing interfaces (e.g. sysread or dbi)
non-blocking that way.

For this, you don't need concurrent access to perl variables.

So to get this to work, e.g. in the case of mysql, one needs to add hooks
to the driver. Assume DBD::mysql has some mysql_execute function that gets 
called like this:

   convert_perl_values_to_mysql_values ();
   mysql_execute ();
   convert_mysql_resaults_to_perl_values ();

This would need to be changed to:

   convert_perl_values_to_mysql_values ();
   release_perl_interpreter_to_do_other_things ();
   mysql_execute ();
   lock_perl_interpreter_again ();
   convert_mysql_resaults_to_perl_values ();

This is basically how python, ruby etc. works, which have thread support,
which perl has not.

To do this, Coro would need to be changed: currently it is a n:m model, i.e.
you can have any number of perl (cooperative) threads on any number of
C cooperative threads.

I have vague plans to change this into a three layer model, there you had any
number of perl threads running on a number of C cooperative threads, running
in turn on a number of kernel threads.

In that case, one could temporarily give control over the curent kernel
thread to e.g. libmysql, while the perl interpreter continues to run on
another kernel thread.

This actually does work already *iff* Coro is configured to use kernel
threads, which it has to use on the dreaded broken bsd platforms, but
which incurs roughly a 12 times slowdown, so on Linux/Solaris or other
working platforms, Coro uses more efficient userspace threads.

This last model is one I would prefer, as it combines the strengths
of threads (fast inter-thread communicaitons for the sql data) while
avoiding its overheads (threads are slow and not well-suited for parallel
processing).

=============================================================================

This might be a bit longer than you expected, but the idea is, DBI already
does work in a fashion (AnyEvent::DBI), and DBD::Gofer could be changed in
various ways to make this easier. I even have long-term plans to introduce
real kernel threads to perl in the same way as other scripting languages
support it, but that's strictly for the future.

-- 
                The choice of a       Deliantra, the free code+content MORPG
      -----==-     _GNU_              http://www.deliantra.net
      ----==-- _       generation
      ---==---(_)__  __ ____  __      Marc Lehmann
      --==---/ / _ \/ // /\ \/ /      [email protected]
      -=====/_/_//_/\_,_/ /_/\_\

Reply via email to