commit 9d22641b62fdfb6c9eb1a47f49db444890df5e84
Author: Oswald Buddenhagen <[email protected]>
Date: Sun May 24 11:37:15 2015 +0200
make server connection a cancellable operation
this entails splitting drv->open_store() into alloc_store() and
connect_store().
src/driver.h | 21 ++++---
src/drv_imap.c | 106 +++++++++++++++++--------------
src/drv_maildir.c | 42 +++++++------
src/main.c | 150 ++++++++++++++++++++++++++-------------------
src/socket.c | 8 +++
5 files changed, 189 insertions(+), 138 deletions(-)
diff --git a/src/driver.h b/src/driver.h
index dcf9255..358f52d 100644
--- a/src/driver.h
+++ b/src/driver.h
@@ -121,8 +121,10 @@ typedef struct {
#define DRV_MSG_BAD 1
/* Something is wrong with the current mailbox - probably it is somehow
inaccessible. */
#define DRV_BOX_BAD 2
+/* Failed to connect store. */
+#define DRV_STORE_BAD 3
/* The command has been cancel()ed or cancel_store()d. */
-#define DRV_CANCELED 3
+#define DRV_CANCELED 4
/* All memory belongs to the driver's user, unless stated otherwise. */
@@ -146,16 +148,19 @@ struct driver {
/* Parse configuration. */
int (*parse_store)( conffile_t *cfg, store_conf_t **storep );
- /* Close remaining server connections. All stores must be disowned
first. */
+ /* Close remaining server connections. All stores must be discarded
first. */
void (*cleanup)( void );
- /* Open a store with the given configuration. This may recycle existing
- * server connections. Upon failure, a null store is passed to the
callback. */
- void (*open_store)( store_conf_t *conf, const char *label,
- void (*cb)( store_t *ctx, void *aux ), void *aux );
+ /* Allocate a store with the given configuration. This is expected to
+ * return quickly, and must not fail. */
+ store_t *(*alloc_store)( store_conf_t *conf, const char *label );
- /* Mark the store as available for recycling. Server connection may be
kept alive. */
- void (*disown_store)( store_t *ctx );
+ /* Open/connect the store. This may recycle existing server
connections. */
+ void (*connect_store)( store_t *ctx,
+ void (*cb)( int sts, void *aux ), void *aux );
+
+ /* Discard the store. Underlying server connection may be kept alive. */
+ void (*free_store)( store_t *ctx );
/* Discard the store after a bad_callback. The server connections will
be closed.
* Pending commands will have their callbacks synchronously invoked
with DRV_CANCELED. */
diff --git a/src/drv_imap.c b/src/drv_imap.c
index a1f73d6..474125b 100644
--- a/src/drv_imap.c
+++ b/src/drv_imap.c
@@ -100,6 +100,7 @@ typedef struct imap_store {
const char *prefix;
const char *name;
int ref_count;
+ enum { SST_BAD, SST_HALF, SST_GOOD } state;
/* trash folder's existence is not confirmed yet */
enum { TrashUnknown, TrashChecking, TrashKnown } trashnc;
uint got_namespace:1;
@@ -121,7 +122,7 @@ typedef struct imap_store {
int expectEOF; /* received LOGOUT's OK or unsolicited BYE */
int canceling; /* imap_cancel() is in progress */
union {
- void (*imap_open)( store_t *srv, void *aux );
+ void (*imap_open)( int sts, void *aux );
void (*imap_cancel)( void *aux );
} callbacks;
void *callback_aux;
@@ -1423,6 +1424,15 @@ get_cmd_result_p2( imap_store_t *ctx, struct imap_cmd
*cmd, int response )
/******************* imap_cancel_store *******************/
+
+static void
+imap_cleanup_store( imap_store_t *ctx )
+{
+ free_generic_messages( ctx->gen.msgs );
+ free_string_list( ctx->gen.boxes );
+ free( ctx->delimiter );
+}
+
static void
imap_cancel_store( store_t *gctx )
{
@@ -1434,13 +1444,11 @@ imap_cancel_store( store_t *gctx )
socket_close( &ctx->conn );
cancel_sent_imap_cmds( ctx );
cancel_pending_imap_cmds( ctx );
- free_generic_messages( ctx->gen.msgs );
- free_string_list( ctx->gen.boxes );
free_list( ctx->ns_personal );
free_list( ctx->ns_other );
free_list( ctx->ns_shared );
free_string_list( ctx->auth_mechs );
- free( ctx->delimiter );
+ imap_cleanup_store( ctx );
imap_deref( ctx );
}
@@ -1460,7 +1468,7 @@ imap_invoke_bad_callback( imap_store_t *ctx )
ctx->gen.bad_callback( ctx->gen.bad_callback_aux );
}
-/******************* imap_disown_store *******************/
+/******************* imap_free_store *******************/
static store_t *unowned;
@@ -1478,7 +1486,7 @@ imap_cancel_unowned( void *gctx )
}
static void
-imap_disown_store( store_t *gctx )
+imap_free_store( store_t *gctx )
{
free_generic_messages( gctx->msgs );
gctx->msgs = 0;
@@ -1542,61 +1550,64 @@ static void imap_open_store_ssl_bail( imap_store_t * );
#endif
static void imap_open_store_bail( imap_store_t *, int );
-static void
-imap_open_store_bad( void *aux )
-{
- imap_open_store_bail( (imap_store_t *)aux, FAIL_TEMP );
-}
-
-static void
-imap_open_store( store_conf_t *conf, const char *label,
- void (*cb)( store_t *srv, void *aux ), void *aux )
+static store_t *
+imap_alloc_store( store_conf_t *conf, const char *label )
{
imap_store_conf_t *cfg = (imap_store_conf_t *)conf;
imap_server_conf_t *srvc = cfg->server;
imap_store_t *ctx;
store_t **ctxp;
+ /* First try to recycle a whole store. */
for (ctxp = &unowned; (ctx = (imap_store_t *)*ctxp); ctxp =
&ctx->gen.next)
- if (ctx->gen.conf == conf) {
+ if (ctx->state == SST_GOOD && ctx->gen.conf == conf) {
*ctxp = ctx->gen.next;
ctx->label = label;
- cb( &ctx->gen, aux );
- return;
+ return &ctx->gen;
}
+
+ /* Then try to recycle a server connection. */
for (ctxp = &unowned; (ctx = (imap_store_t *)*ctxp); ctxp =
&ctx->gen.next)
- if (((imap_store_conf_t *)ctx->gen.conf)->server == srvc) {
+ if (ctx->state != SST_BAD && ((imap_store_conf_t
*)ctx->gen.conf)->server == srvc) {
*ctxp = ctx->gen.next;
- ctx->label = label;
+ imap_cleanup_store( ctx );
/* One could ping the server here, but given that the
idle timeout
* is at least 30 minutes, this sounds pretty
pointless. */
- free_string_list( ctx->gen.boxes );
- ctx->gen.boxes = 0;
- ctx->gen.listed = 0;
- ctx->gen.conf = conf;
- free( ctx->delimiter );
- ctx->delimiter = 0;
- ctx->callbacks.imap_open = cb;
- ctx->callback_aux = aux;
- set_bad_callback( &ctx->gen, imap_open_store_bad, ctx );
- imap_open_store_namespace( ctx );
- return;
+ ctx->state = SST_HALF;
+ goto gotsrv;
}
+ /* Finally, schedule opening a new server connection. */
ctx = nfcalloc( sizeof(*ctx) );
+ socket_init( &ctx->conn, &srvc->sconf,
+ (void (*)( void * ))imap_invoke_bad_callback,
+ imap_socket_read, (void (*)(void *))flush_imap_cmds, ctx );
+ ctx->in_progress_append = &ctx->in_progress;
+ ctx->pending_append = &ctx->pending;
+
+ gotsrv:
ctx->gen.conf = conf;
ctx->label = label;
ctx->ref_count = 1;
- ctx->callbacks.imap_open = cb;
- ctx->callback_aux = aux;
- set_bad_callback( &ctx->gen, imap_open_store_bad, ctx );
- ctx->in_progress_append = &ctx->in_progress;
- ctx->pending_append = &ctx->pending;
+ return &ctx->gen;
+}
- socket_init( &ctx->conn, &srvc->sconf,
- (void (*)( void * ))imap_invoke_bad_callback,
- imap_socket_read, (void (*)(void *))flush_imap_cmds, ctx );
- socket_connect( &ctx->conn, imap_open_store_connected );
+static void
+imap_connect_store( store_t *gctx,
+ void (*cb)( int sts, void *aux ), void *aux )
+{
+ imap_store_t *ctx = (imap_store_t *)gctx;
+
+ if (ctx->state == SST_GOOD) {
+ cb( DRV_OK, aux );
+ } else {
+ ctx->callbacks.imap_open = cb;
+ ctx->callback_aux = aux;
+ if (ctx->state == SST_HALF)
+ imap_open_store_namespace( ctx );
+ else
+ socket_connect( &ctx->conn, imap_open_store_connected );
+ }
}
static void
@@ -2091,6 +2102,7 @@ imap_open_store_namespace( imap_store_t *ctx )
{
imap_store_conf_t *cfg = (imap_store_conf_t *)ctx->gen.conf;
+ ctx->state = SST_HALF;
ctx->prefix = cfg->gen.path;
ctx->delimiter = cfg->delimiter ? nfstrdup( cfg->delimiter ) : 0;
if (((!ctx->prefix && cfg->use_namespace) || !cfg->delimiter) &&
CAP(NAMESPACE)) {
@@ -2140,11 +2152,11 @@ imap_open_store_namespace2( imap_store_t *ctx )
static void
imap_open_store_finalize( imap_store_t *ctx )
{
- set_bad_callback( &ctx->gen, 0, 0 );
+ ctx->state = SST_GOOD;
if (!ctx->prefix)
ctx->prefix = "";
ctx->trashnc = TrashUnknown;
- ctx->callbacks.imap_open( &ctx->gen, ctx->callback_aux );
+ ctx->callbacks.imap_open( DRV_OK, ctx->callback_aux );
}
#ifdef HAVE_LIBSSL
@@ -2160,11 +2172,8 @@ imap_open_store_ssl_bail( imap_store_t *ctx )
static void
imap_open_store_bail( imap_store_t *ctx, int failed )
{
- void (*cb)( store_t *srv, void *aux ) = ctx->callbacks.imap_open;
- void *aux = ctx->callback_aux;
((imap_store_conf_t *)ctx->gen.conf)->server->failed = failed;
- imap_cancel_store( &ctx->gen );
- cb( 0, aux );
+ ctx->callbacks.imap_open( DRV_STORE_BAD, ctx->callback_aux );
}
/******************* imap_open_box *******************/
@@ -2945,8 +2954,9 @@ struct driver imap_driver = {
DRV_CRLF | DRV_VERBOSE,
imap_parse_store,
imap_cleanup,
- imap_open_store,
- imap_disown_store,
+ imap_alloc_store,
+ imap_connect_store,
+ imap_free_store,
imap_cancel_store,
imap_list_store,
imap_select_box,
diff --git a/src/drv_maildir.c b/src/drv_maildir.c
index b336ba9..3f000ff 100644
--- a/src/drv_maildir.c
+++ b/src/drv_maildir.c
@@ -194,35 +194,40 @@ maildir_validate_path( maildir_store_conf_t *conf )
static void lcktmr_timeout( void *aux );
-static void
-maildir_open_store( store_conf_t *gconf, const char *label ATTR_UNUSED,
- void (*cb)( store_t *ctx, void *aux ), void *aux )
+static store_t *
+maildir_alloc_store( store_conf_t *gconf, const char *label ATTR_UNUSED )
{
- maildir_store_conf_t *conf = (maildir_store_conf_t *)gconf;
maildir_store_t *ctx;
ctx = nfcalloc( sizeof(*ctx) );
ctx->gen.conf = gconf;
ctx->uvfd = -1;
init_wakeup( &ctx->lcktmr, lcktmr_timeout, ctx );
- if (gconf->path && maildir_validate_path( conf ) < 0) {
- free( ctx );
- cb( 0, aux );
+ return &ctx->gen;
+}
+
+static void
+maildir_connect_store( store_t *gctx,
+ void (*cb)( int sts, void *aux ), void *aux )
+{
+ maildir_store_t *ctx = (maildir_store_t *)gctx;
+ maildir_store_conf_t *conf = (maildir_store_conf_t *)ctx->gen.conf;
+
+ if (conf->gen.path && maildir_validate_path( conf ) < 0) {
+ cb( DRV_STORE_BAD, aux );
return;
}
- if (gconf->trash) {
+ if (conf->gen.trash) {
if (maildir_ensure_path( conf ) < 0) {
- free( ctx );
- cb( 0, aux );
+ cb( DRV_STORE_BAD, aux );
return;
}
- if (!(ctx->trash = maildir_join_path( conf, gconf->path,
gconf->trash ))) {
- free( ctx );
- cb( 0, aux );
+ if (!(ctx->trash = maildir_join_path( conf, conf->gen.path,
conf->gen.trash ))) {
+ cb( DRV_STORE_BAD, aux );
return;
}
}
- cb( &ctx->gen, aux );
+ cb( DRV_OK, aux );
}
static void
@@ -256,7 +261,7 @@ maildir_cleanup( store_t *gctx )
}
static void
-maildir_disown_store( store_t *gctx )
+maildir_free_store( store_t *gctx )
{
maildir_store_t *ctx = (maildir_store_t *)gctx;
@@ -1761,9 +1766,10 @@ struct driver maildir_driver = {
0, /* XXX DRV_CRLF? */
maildir_parse_store,
maildir_cleanup_drv,
- maildir_open_store,
- maildir_disown_store,
- maildir_disown_store, /* _cancel_, but it's the same */
+ maildir_alloc_store,
+ maildir_connect_store,
+ maildir_free_store,
+ maildir_free_store, /* _cancel_, but it's the same */
maildir_list_store,
maildir_select_box,
maildir_create_box,
diff --git a/src/main.c b/src/main.c
index 85a6b06..fa0b936 100644
--- a/src/main.c
+++ b/src/main.c
@@ -727,10 +727,33 @@ main( int argc, char **argv )
}
#define ST_FRESH 0
-#define ST_OPEN 1
-#define ST_CLOSED 2
+#define ST_CONNECTED 1
+#define ST_OPEN 2
+#define ST_CANCELING 3
+#define ST_CLOSED 4
-static void store_opened( store_t *ctx, void *aux );
+static void
+cancel_prep_done( void *aux )
+{
+ MVARS(aux)
+
+ mvars->drv[t]->free_store( mvars->ctx[t] );
+ mvars->state[t] = ST_CLOSED;
+ sync_chans( mvars, E_OPEN );
+}
+
+static void
+store_bad( void *aux )
+{
+ MVARS(aux)
+
+ mvars->drv[t]->cancel_store( mvars->ctx[t] );
+ mvars->state[t] = ST_CLOSED;
+ mvars->ret = mvars->skip = 1;
+ sync_chans( mvars, E_OPEN );
+}
+
+static void store_connected( int sts, void *aux );
static void store_listed( int sts, void *aux );
static int sync_listed_boxes( main_vars_t *mvars, box_ent_t *mbox );
static void done_sync_2_dyn( int sts, void *aux );
@@ -771,17 +794,18 @@ sync_chans( main_vars_t *mvars, int ent )
labels[M] = "M: ", labels[S] = "S: ";
else
labels[M] = labels[S] = "";
+ for (t = 0; t < 2; t++) {
+ mvars->drv[t] = mvars->chan->stores[t]->driver;
+ mvars->ctx[t] = mvars->drv[t]->alloc_store(
mvars->chan->stores[t], labels[t] );
+ set_bad_callback( mvars->ctx[t], store_bad, AUX );
+ }
for (t = 0; ; t++) {
info( "Opening %s store %s...\n", str_ms[t],
mvars->chan->stores[t]->name );
- mvars->drv[t] = mvars->chan->stores[t]->driver;
- mvars->drv[t]->open_store( mvars->chan->stores[t],
labels[t], store_opened, AUX );
- if (t)
+ mvars->drv[t]->connect_store( mvars->ctx[t],
store_connected, AUX );
+ if (t || mvars->skip)
break;
- if (mvars->skip) {
- mvars->state[1] = ST_CLOSED;
- break;
- }
}
+
mvars->cben = 1;
opened:
if (mvars->skip)
@@ -856,13 +880,19 @@ sync_chans( main_vars_t *mvars, int ent )
}
next:
+ mvars->cben = 0;
for (t = 0; t < 2; t++)
- if (mvars->state[t] == ST_OPEN) {
- mvars->drv[t]->disown_store( mvars->ctx[t] );
+ if (mvars->state[t] == ST_FRESH) {
+ /* An unconnected store may be only cancelled.
*/
mvars->state[t] = ST_CLOSED;
+ mvars->drv[t]->cancel_store( mvars->ctx[t] );
+ } else if (mvars->state[t] == ST_CONNECTED ||
mvars->state[t] == ST_OPEN) {
+ mvars->state[t] = ST_CANCELING;
+ mvars->drv[t]->cancel_cmds( mvars->ctx[t],
cancel_prep_done, AUX );
}
+ mvars->cben = 1;
if (mvars->state[M] != ST_CLOSED || mvars->state[S] !=
ST_CLOSED) {
- mvars->skip = mvars->cben = 1;
+ mvars->skip = 1;
return;
}
if (mvars->chanptr->boxlist == 2) {
@@ -885,72 +915,64 @@ sync_chans( main_vars_t *mvars, int ent )
}
static void
-store_bad( void *aux )
-{
- MVARS(aux)
-
- mvars->drv[t]->cancel_store( mvars->ctx[t] );
- mvars->ret = mvars->skip = 1;
- mvars->state[t] = ST_CLOSED;
- sync_chans( mvars, E_OPEN );
-}
-
-static void
-store_opened( store_t *ctx, void *aux )
+store_connected( int sts, void *aux )
{
MVARS(aux)
string_list_t *cpat;
int cflags;
- if (!ctx) {
- mvars->ret = mvars->skip = 1;
- mvars->state[t] = ST_CLOSED;
- sync_chans( mvars, E_OPEN );
+ switch (sts) {
+ case DRV_CANCELED:
return;
- }
- mvars->ctx[t] = ctx;
- if (!mvars->skip && !mvars->chanptr->boxlist && mvars->chan->patterns
&& !ctx->listed) {
- for (cflags = 0, cpat = mvars->chan->patterns; cpat; cpat =
cpat->next) {
- const char *pat = cpat->string;
- if (*pat != '!') {
- char buf[8];
- int bufl = snprintf( buf, sizeof(buf), "%s%s",
nz( mvars->chan->boxes[t], "" ), pat );
- int flags = 0;
- /* Partial matches like "INB*" or even "*" are
not considered,
- * except implicity when the INBOX lives under
Path. */
- if (starts_with( buf, bufl, "INBOX", 5 )) {
- char c = buf[5];
- if (!c) {
- /* User really wants the INBOX.
*/
- flags |= LIST_INBOX;
- } else if (c == '/') {
- /* Flattened sub-folders of
INBOX actually end up in Path. */
- if (ctx->conf->flat_delim)
- flags |= LIST_PATH;
- else
+ case DRV_OK:
+ if (!mvars->skip && !mvars->chanptr->boxlist &&
mvars->chan->patterns && !mvars->ctx[t]->listed) {
+ for (cflags = 0, cpat = mvars->chan->patterns; cpat;
cpat = cpat->next) {
+ const char *pat = cpat->string;
+ if (*pat != '!') {
+ char buf[8];
+ int bufl = snprintf( buf, sizeof(buf),
"%s%s", nz( mvars->chan->boxes[t], "" ), pat );
+ int flags = 0;
+ /* Partial matches like "INB*" or even
"*" are not considered,
+ * except implicity when the INBOX
lives under Path. */
+ if (starts_with( buf, bufl, "INBOX", 5
)) {
+ char c = buf[5];
+ if (!c) {
+ /* User really wants
the INBOX. */
flags |= LIST_INBOX;
+ } else if (c == '/') {
+ /* Flattened
sub-folders of INBOX actually end up in Path. */
+ if
(mvars->ctx[t]->conf->flat_delim)
+ flags |=
LIST_PATH;
+ else
+ flags |=
LIST_INBOX;
+ } else {
+ /* User may not want
the INBOX after all ... */
+ flags |= LIST_PATH;
+ /* ... but maybe he
does.
+ * The flattened
sub-folder case is implicitly covered by the previous line. */
+ if (c == '*' || c ==
'%')
+ flags |=
LIST_INBOX;
+ }
} else {
- /* User may not want the INBOX
after all ... */
flags |= LIST_PATH;
- /* ... but maybe he does.
- * The flattened sub-folder
case is implicitly covered by the previous line. */
- if (c == '*' || c == '%')
- flags |= LIST_INBOX;
}
- } else {
- flags |= LIST_PATH;
+ debug( "pattern '%s' (effective '%s'):
%sPath, %sINBOX\n",
+ pat, buf, (flags & LIST_PATH) ?
"" : "no ", (flags & LIST_INBOX) ? "" : "no ");
+ cflags |= flags;
}
- debug( "pattern '%s' (effective '%s'): %sPath,
%sINBOX\n",
- pat, buf, (flags & LIST_PATH) ? "" : "no
", (flags & LIST_INBOX) ? "" : "no ");
- cflags |= flags;
}
+ mvars->state[t] = ST_CONNECTED;
+ mvars->drv[t]->list_store( mvars->ctx[t], cflags,
store_listed, AUX );
+ return;
}
- set_bad_callback( ctx, store_bad, AUX );
- mvars->drv[t]->list_store( ctx, cflags, store_listed, AUX );
- } else {
mvars->state[t] = ST_OPEN;
- sync_chans( mvars, E_OPEN );
+ break;
+ default:
+ mvars->ret = mvars->skip = 1;
+ mvars->state[t] = ST_OPEN;
+ break;
}
+ sync_chans( mvars, E_OPEN );
}
static void
diff --git a/src/socket.c b/src/socket.c
index 49586ac..27bc8cd 100644
--- a/src/socket.c
+++ b/src/socket.c
@@ -516,6 +516,7 @@ socket_connected( conn_t *conn )
{
#ifdef HAVE_IPV6
freeaddrinfo( conn->addrs );
+ conn->addrs = 0;
#endif
conf_notifier( &conn->notify, 0, POLLIN );
socket_expect_read( conn, 0 );
@@ -528,6 +529,7 @@ socket_connect_bail( conn_t *conn )
{
#ifdef HAVE_IPV6
freeaddrinfo( conn->addrs );
+ conn->addrs = 0;
#endif
free( conn->name );
conn->name = 0;
@@ -543,6 +545,12 @@ socket_close( conn_t *sock )
socket_close_internal( sock );
free( sock->name );
sock->name = 0;
+#ifdef HAVE_IPV6
+ if (sock->addrs) {
+ freeaddrinfo( sock->addrs );
+ sock->addrs = 0;
+ }
+#endif
#ifdef HAVE_LIBSSL
if (sock->ssl) {
SSL_free( sock->ssl );
------------------------------------------------------------------------------
One dashboard for servers and applications across Physical-Virtual-Cloud
Widest out-of-the-box monitoring support with 50+ applications
Performance metrics, stats and reports that give you Actionable Insights
Deep dive visibility with transaction tracing using APM Insight.
http://ad.doubleclick.net/ddm/clk/290420510;117567292;y
_______________________________________________
isync-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/isync-devel