diff options
-rw-r--r-- | camel/ChangeLog | 13 | ||||
-rw-r--r-- | camel/camel-operation.c | 40 | ||||
-rw-r--r-- | camel/camel-operation.h | 3 | ||||
-rw-r--r-- | camel/camel-tcp-stream-ssl.c | 165 |
4 files changed, 201 insertions, 20 deletions
diff --git a/camel/ChangeLog b/camel/ChangeLog index d3913173d6..78386adf70 100644 --- a/camel/ChangeLog +++ b/camel/ChangeLog @@ -1,3 +1,16 @@ +2002-11-21 Jeffrey Stedfast <fejj@ximian.com> + + * camel-tcp-stream-ssl.c (stream_read): Use the new + camel_operation_cancel_prfd() function to get the cancellation fd + so we can poll on it for cancellation stuff. + (stream_write): Same. + +2002-11-22 Not Zed <NotZed@Ximian.com> + + * camel-operation.c (camel_operation_cancel_prfd): Implement, gets + a nspr pr filedesc to poll/wait on + (struct _CamelOperation): include a pr filedesc. + 2002-11-21 Not Zed <NotZed@Ximian.com> * providers/imap/camel-imap-store-summary.c diff --git a/camel/camel-operation.c b/camel/camel-operation.c index 6c490df73f..8befacce19 100644 --- a/camel/camel-operation.c +++ b/camel/camel-operation.c @@ -5,6 +5,9 @@ #include <stdio.h> #ifdef ENABLE_THREADS #include <pthread.h> +#ifdef HAVE_NSS +#include <nspr.h> +#endif #endif #include <sys/time.h> @@ -42,6 +45,9 @@ struct _CamelOperation { #ifdef ENABLE_THREADS EMsgPort *cancel_port; int cancel_fd; +#ifdef HAVE_NSS + PRFileDesc *cancel_prfd; +#endif #endif }; @@ -436,6 +442,40 @@ int camel_operation_cancel_fd(CamelOperation *cc) return cc->cancel_fd; } +#ifdef HAVE_NSS +/** + * camel_operation_cancel_prfd: + * @cc: + * + * Retrieve a file descriptor that can be waited on (select, or poll) + * for read, to asynchronously detect cancellation. + * + * Return value: The fd, or NULL if cancellation is not available + * (blocked, or has not been registered for this thread). + **/ +PRFileDesc *camel_operation_cancel_prfd(CamelOperation *cc) +{ + CAMEL_ACTIVE_LOCK(); + + if (cc == NULL && operation_active) { + cc = g_hash_table_lookup(operation_active, (void *)pthread_self()); + } + + if (cc == NULL + || cc->blocked) { + CAMEL_ACTIVE_UNLOCK(); + return NULL; + } + + if (cc->cancel_prfd == NULL) + cc->cancel_prfd = e_msgport_prfd(cc->cancel_port); + + CAMEL_ACTIVE_UNLOCK(); + + return cc->cancel_prfd; +} +#endif /* HAVE_NSS */ + /** * camel_operation_start: * @cc: diff --git a/camel/camel-operation.h b/camel/camel-operation.h index 08da410b68..367d916b67 100644 --- a/camel/camel-operation.h +++ b/camel/camel-operation.h @@ -52,6 +52,9 @@ void camel_operation_cancel_block(CamelOperation *cc); void camel_operation_cancel_unblock(CamelOperation *cc); int camel_operation_cancel_check(CamelOperation *cc); int camel_operation_cancel_fd(CamelOperation *cc); +#ifdef HAVE_NSS +struct PRFileDesc *camel_operation_cancel_prfd(CamelOperation *cc); +#endif /* return the registered operation for this thread, if there is one */ CamelOperation *camel_operation_registered(void); diff --git a/camel/camel-tcp-stream-ssl.c b/camel/camel-tcp-stream-ssl.c index 3aa841d9df..b20b6d255e 100644 --- a/camel/camel-tcp-stream-ssl.c +++ b/camel/camel-tcp-stream-ssl.c @@ -299,18 +299,71 @@ static ssize_t stream_read (CamelStream *stream, char *buffer, size_t n) { CamelTcpStreamSSL *tcp_stream_ssl = CAMEL_TCP_STREAM_SSL (stream); + PRFileDesc *cancel_fd; ssize_t nread; - do { - if (camel_operation_cancel_check (NULL)) { - errno = EINTR; - return -1; - } + if (camel_operation_cancel_check (NULL)) { + errno = EINTR; + return -1; + } + + cancel_fd = camel_operation_cancel_prfd (NULL); + if (cancel_fd == NULL) { + do { + nread = PR_Read (tcp_stream_ssl->priv->sockfd, buffer, n); + if (nread == -1) + set_errno (PR_GetError ()); + } while (nread == -1 && (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)); + } else { + PRSocketOptionData sockopts; + PRPollDesc pollfds[2]; + gboolean nonblock; + int error; - nread = PR_Read (tcp_stream_ssl->priv->sockfd, buffer, n); - if (nread == -1) - set_errno (PR_GetError ()); - } while (nread == -1 && (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)); + /* get O_NONBLOCK options */ + sockopts.option = PR_SockOpt_Nonblocking; + PR_GetSocketOption (tcp_stream_ssl->priv->sockfd, &sockopts); + sockopts.option = PR_SockOpt_Nonblocking; + nonblock = sockopts.value.non_blocking; + sockopts.value.non_blocking = TRUE; + PR_SetSocketOption (tcp_stream_ssl->priv->sockfd, &sockopts); + + pollfds[0].fd = tcp_stream_ssl->priv->sockfd; + pollfds[0].in_flags = PR_POLL_READ; + pollfds[1].fd = cancel_fd; + pollfds[1].in_flags = PR_POLL_READ; + + do { + pollfds[0].out_flags = 0; + pollfds[1].out_flags = 0; + + nread = -1; + if (PR_Poll (pollfds, 2, -1) != -1) { + if (pollfds[1].out_flags == PR_POLL_READ) { + sockopts.option = PR_SockOpt_Nonblocking; + sockopts.value.non_blocking = nonblock; + PR_SetSocketOption (tcp_stream_ssl->priv->sockfd, &sockopts); + errno = EINTR; + return -1; + } + + do { + nread = PR_Read (tcp_stream_ssl->priv->sockfd, buffer, n); + if (nread == -1) + set_errno (PR_GetError ()); + } while (nread == -1 && errno == EINTR); + } else { + errno = EAGAIN; + } + } while (nread == -1 && (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)); + + /* restore O_NONBLOCK options */ + error = errno; + sockopts.option = PR_SockOpt_Nonblocking; + sockopts.value.non_blocking = nonblock; + PR_SetSocketOption (tcp_stream_ssl->priv->sockfd, &sockopts); + errno = error; + } return nread; } @@ -320,22 +373,94 @@ stream_write (CamelStream *stream, const char *buffer, size_t n) { CamelTcpStreamSSL *tcp_stream_ssl = CAMEL_TCP_STREAM_SSL (stream); ssize_t w, written = 0; + PRFileDesc *cancel_fd; - do { - if (camel_operation_cancel_check (NULL)) { - errno = EINTR; - return -1; - } + if (camel_operation_cancel_check (NULL)) { + errno = EINTR; + return -1; + } + + cancel_fd = camel_operation_cancel_prfd (NULL); + if (cancel_fd == NULL) { + do { + do { + w = PR_Write (tcp_stream_ssl->priv->sockfd, buffer + written, n - written); + if (w == -1) + set_errno (PR_GetError ()); + } while (w == -1 && (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)); + + if (w > 0) + written += w; + } while (w != -1 && written < n); + } else { + PRSocketOptionData sockopts; + PRPollDesc pollfds[2]; + gboolean nonblock; + int error; + + /* get O_NONBLOCK options */ + sockopts.option = PR_SockOpt_Nonblocking; + PR_GetSocketOption (tcp_stream_ssl->priv->sockfd, &sockopts); + sockopts.option = PR_SockOpt_Nonblocking; + nonblock = sockopts.value.non_blocking; + sockopts.value.non_blocking = TRUE; + PR_SetSocketOption (tcp_stream_ssl->priv->sockfd, &sockopts); + + pollfds[0].fd = tcp_stream_ssl->priv->sockfd; + pollfds[0].in_flags = PR_POLL_WRITE; + pollfds[1].fd = cancel_fd; + pollfds[1].in_flags = PR_POLL_READ; do { - w = PR_Write (tcp_stream_ssl->priv->sockfd, buffer + written, n - written); - if (w == -1) + pollfds[0].out_flags = 0; + pollfds[1].out_flags = 0; + + w = -1; + if (PR_Poll (pollfds, 2, -1) != -1) { + if (pollfds[1].out_flags == PR_POLL_READ) { + sockopts.option = PR_SockOpt_Nonblocking; + sockopts.value.non_blocking = nonblock; + PR_SetSocketOption (tcp_stream_ssl->priv->sockfd, &sockopts); + errno = EINTR; + return -1; + } + + do { + w = PR_Write (tcp_stream_ssl->priv->sockfd, buffer + written, n - written); + if (w == -1) + set_errno (PR_GetError ()); + } while (w == -1 && errno == EINTR); + + if (w == -1) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + w = 0; + } else { + error = errno; + sockopts.option = PR_SockOpt_Nonblocking; + sockopts.value.non_blocking = nonblock; + PR_SetSocketOption (tcp_stream_ssl->priv->sockfd, &sockopts); + errno = error; + return -1; + } + } else + written += w; + } else { set_errno (PR_GetError ()); - } while (w == -1 && (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)); + if (errno == EINTR) + w = 0; + } + } while (w != -1 && written < n); - if (w > 0) - written += w; - } while (w != -1 && written < n); + /* restore O_NONBLOCK options */ + error = errno; + sockopts.option = PR_SockOpt_Nonblocking; + sockopts.value.non_blocking = nonblock; + PR_SetSocketOption (tcp_stream_ssl->priv->sockfd, &sockopts); + errno = error; + } + + if (w == -1) + return -1; return written; } |