On Sun, 2005-04-03 at 22:19 +0200, Paul J Stevens wrote:
> Geo Carncross wrote:
> > On Thu, 2005-03-31 at 09:25 +0200, Paul J Stevens wrote:
> 
> >>I'm not sure I understand what you mean by 'unsolicited updates'. Would 
> >>that be 
> >>like EXAMINE/SELECT or more like STATUS?
> 
> [..]
> 
> > 7.2.6.  FLAGS Response
> > 7.3.1.  EXISTS Response
> > 7.3.2.  RECENT Response
> > 7.4.1.  EXPUNGE Response
> > 
> > These responses can come at any time and clients are REQUIRED to record
> > them.
> > 
> > 7.4.2.  FETCH Response
> 
> [..]
> 
> Let me see if I get this straight.
> 
> We could change the blocking fread on the client stream to a non-blocking 
> select 
> based version while making sure we can safely interrupt anything we happen to 
> be 
> doing while waiting for commands from the client. This will give us plenty of 
> idle time on our hands to use for stuff like sending unsollicited responses 
> and 
> possibly idle output.

Not exactly. That would certainly be most elegant, but it's not really
how dbmail presently looks, and fortunately, it isn't necessary, because
clients aren't interested in _everything_, only the things that they've
recently seen or might ask for soon.


A better way to describe this [the changes I suggest] is to use THREE
processes (p1, p2, p3)- instead of fread(), you have a select() loop on
(p1):
        your socket (read-simplex)
        the self-pipe (to collect SIGCHLD)
        the map pipe (read- attached to p2)
        log pipes (for p1, and p2)

Reads from the socket are sent to the IMAP command workers (p2, p3):
        p2 handles the task directly (as dbmail-imapd normally does)
        p3 is notified of SELECT/EXAMINE/CLOSE changes (f1)

When p2 starts writing responses, it grabs a lock (lk: flock, fcntl,
doesn't matter), sends the data to the socket (write-simplex) and BLOCKS
if necessary. Use fwrite/fputs/whatever as it doesn't matter. It unlocks
it as soon as the "sequence" is done.

When p3 has stuff to send, it grabs the same lock as p2 (lk) and writes
it out directly as well.

Now, to keep p2,p3 in sync, you use a small amount of shared memory (or
a file- again, it doesn't matter) and a lock (lk).

p2 receives SELECT/EXAMINE/CLOSE/whatever, it locks lk, then writes the
new folder name/id to shared memory. it then writes a byte to the map-
pipe (to tell p1), and unlocks.

when p3 locks lk, _copies_ the current folder name out of shared memory,
and sees if it's been updated. Remember, at this point, p3 thinks it
needs to write some "unsolicited responses". If they've changed, p3
discards the write and returns to the top of its loop. If they haven't,
it sends the untagged response.

Now, notice that p1 spends all it's time idle. p2 is as idle as dbmail-
imapd was, but p3 (probably running nice'd) can use whatever db-specific
tricks needed to block and wait and send out replies as it needs to.


There's also no reason that p1 couldn't be the p1 for _all_ children,
and it wouldn't be THAT nasty either...



> > I recommend avoiding IDLE because of the confusion that is caused by
> > certain clients that enter that IDLE mode. If that can't be done, be
> > very careful to disable timeouts as mentioned earlier.
> 
> Interesting stuff. By using select on the command channel we can provide 
> unilateral server data, one special case of which is IDLE data.
> 
> Still have to absorb the self-pipe pattern though. I havent found a good 
> example 
> yet, and djbs description reads like a zen koan.

I use it frequently. Our http://gpl.internetconnection.net/ site has
http://gpl.internetconnection.net/files/cexec.tar.gz and in it,
cservice.c which uses it. The important bits are:

static int selfpipe[2];
static void selfpipe_write(int signo)
{
        signal(SIGCHLD, selfpipe_write);
        if (selfpipe[1] > -1) (void) write(selfpipe[1], "\x01", 1);
}

...

        if (pipe(selfpipe) == -1) exit(103);
        signal(SIGCHLD, selfpipe_write);

...

                                r = select(nm+1,&use,(fd_set*)0,(fd_set*)0,
                                        (struct timeval *)&tv);

...

                if (FD_ISSET(selfpipe[0], &use)) {
                        (void)read(selfpipe[0], header, sizeof(header));
                }


-- 
Internet Connection High Quality Web Hosting
http://www.internetconnection.net/

Reply via email to