Mark Williams wrote:

> > 2) Let the two threads read and write to your own two
> > independent queues and
> > service the application side of the SSL connection with your
> > own code to and from the read and write queues.

> Won't I still need to combine the reading and writing to the SSL object
> into a
> single thread for this?  This is the bit I am having difficulty
> visualising.

The data pump thread is more or less like this:

While (connection_is_alive)
{
 If (connection_is_not_corked_in)
 {
  SSL_read into temporary buffer.
  If we got data:
  {
   If a read thread is blocked, unblock it.
   If the receive queue is too full, set the 'corked_in' flag.
  }
  If we got a fatal error, mark the connection dead.
 }
 If(send queue not empty)
 {
  Try to send some data using SSL_write
  Put back what we didn't send
 }
 If we made no forward progress, block (see notes)
}
Tear down the connection

The read thread acquires the queue mutex, blocks on the condvar for data if
desired, pulls data off the queue, and clears the corked_in flag if it was
set (assuming the queue is still not full), and signals the data pump thread
if it uncorked.

The write thread acquires the mutex, checks if the send queue is full,
blocks on the condvar if it is, and signals the data pump thread if the queu
was empty.

The only trick left is the blocking logic in the data pump thread. This is
the hard part:

1) If you have no outbound data pending, and the connection is corked, block
only on an internal signal. (Since you don't want to do I/O either way
anyway.)

2) If you have outbound data pending and the connection is corked, block as
directed by SSL_write. If it said WANT_READ, block on read. If it said
WANT_WRITE, block on write.

3) If you have no outbound data pending (and hence, did not call SSL_write),
and the connection is uncorked, block as directed in SSL_read.

4) If you have outbound data pending, and the connection is uncorked, block
on the logical OR of the SSL_read result and the SSL_write result (block for
read on the socket if either one returned WANT_READ, block for write if
either returned WANT_WRITE).

Note that your data pump threads needs to block on a 'select' or 'poll' type
function but be unblocked when signaled. If necessary, add one end of a pipe
to the select/poll set and have you read/write threads write a byte to that
pipe to unblock the data pump thread.

This is from memory, but it should be basically correct.

By the way, I think only the logic in 4 is not obviously correct. Here's the
proof it's safe:
1) If nothing changed, and we block on the OR of both operations, we will
only unblock if one of those operations can make forward progress. (We only
unblock on X if one operation Xould make forward progress on X, and nothing
has changed since then.)
2) If something changed, then we already made some forward progress.
So either way, we make forward progress in each pass of the loop, which is
the best you can hope for.

DS



______________________________________________________________________
OpenSSL Project                                 http://www.openssl.org
User Support Mailing List                    openssl-users@openssl.org
Automated List Manager                           majord...@openssl.org

Reply via email to