/*
- * Copyright (c) 2008, 2009 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010 Nicira Networks.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#define STATES \
STATE(VOID, 1 << 0) \
STATE(BACKOFF, 1 << 1) \
- STATE(CONNECTING, 1 << 2) \
- STATE(ACTIVE, 1 << 3) \
- STATE(IDLE, 1 << 4) \
- STATE(RECONNECT, 1 << 5)
+ STATE(START_CONNECT, 1 << 2) \
+ STATE(CONNECT_IN_PROGRESS, 1 << 3) \
+ STATE(ACTIVE, 1 << 4) \
+ STATE(IDLE, 1 << 5) \
+ STATE(RECONNECT, 1 << 6)
enum state {
#define STATE(NAME, VALUE) S_##NAME = VALUE,
STATES
int backoff;
long long int last_received;
long long int last_connected;
+ unsigned int max_tries;
/* These values are simply for statistics reporting, not otherwise used
* directly by anything internal. */
static void reconnect_transition__(struct reconnect *, long long int now,
enum state state);
static long long int reconnect_deadline__(const struct reconnect *);
+static bool reconnect_may_retry(struct reconnect *);
static const char *
reconnect_state_name__(enum state state)
fsm->backoff = 0;
fsm->last_received = now;
fsm->last_connected = now;
+ fsm->max_tries = UINT_MAX;
fsm->creation_time = now;
return fsm;
return fsm->probe_interval;
}
+/* Limits the maximum number of times that 'fsm' will ask the client to try to
+ * reconnect to 'max_tries'. UINT_MAX (the default) means an unlimited number
+ * of tries.
+ *
+ * After the number of tries has expired, the 'fsm' will disable itself
+ * instead of backing off and retrying. */
+void
+reconnect_set_max_tries(struct reconnect *fsm, unsigned int max_tries)
+{
+ fsm->max_tries = max_tries;
+}
+
+/* Returns the current remaining number of connection attempts, UINT_MAX if
+ * the number is unlimited. */
+unsigned int
+reconnect_get_max_tries(struct reconnect *fsm)
+{
+ return fsm->max_tries;
+}
+
/* Configures the backoff parameters for 'fsm'. 'min_backoff' is the minimum
* number of milliseconds, and 'max_backoff' is the maximum, between connection
* attempts.
void
reconnect_enable(struct reconnect *fsm, long long int now)
{
- if (fsm->state == S_VOID) {
+ if (fsm->state == S_VOID && reconnect_may_retry(fsm)) {
reconnect_transition__(fsm, now, S_BACKOFF);
fsm->backoff = 0;
}
void
reconnect_force_reconnect(struct reconnect *fsm, long long int now)
{
- if (fsm->state & (S_CONNECTING | S_ACTIVE | S_IDLE)) {
+ if (fsm->state & (S_START_CONNECT | S_CONNECT_IN_PROGRESS
+ | S_ACTIVE | S_IDLE)) {
reconnect_transition__(fsm, now, S_RECONNECT);
}
}
void
reconnect_disconnected(struct reconnect *fsm, long long int now, int error)
{
- if (fsm->state != S_BACKOFF) {
+ if (!(fsm->state & (S_BACKOFF | S_VOID))) {
/* Report what happened. */
if (fsm->state & (S_ACTIVE | S_IDLE)) {
if (error > 0) {
VLOG_INFO("%s: waiting %.3g seconds before reconnect\n",
fsm->name, fsm->backoff / 1000.0);
}
- reconnect_transition__(fsm, now, S_BACKOFF);
+
+ reconnect_transition__(fsm, now,
+ reconnect_may_retry(fsm) ? S_BACKOFF : S_VOID);
}
}
void
reconnect_connecting(struct reconnect *fsm, long long int now)
{
- if (fsm->state != S_CONNECTING) {
+ if (fsm->state != S_CONNECT_IN_PROGRESS) {
VLOG_INFO("%s: connecting...", fsm->name);
- reconnect_transition__(fsm, now, S_CONNECTING);
+ reconnect_transition__(fsm, now, S_CONNECT_IN_PROGRESS);
}
}
reconnect_transition__(struct reconnect *fsm, long long int now,
enum state state)
{
- if (fsm->state == S_CONNECTING) {
+ if (fsm->state == S_CONNECT_IN_PROGRESS) {
fsm->n_attempted_connections++;
if (state == S_ACTIVE) {
fsm->n_successful_connections++;
case S_BACKOFF:
return fsm->state_entered + fsm->backoff;
- case S_CONNECTING:
+ case S_START_CONNECT:
+ case S_CONNECT_IN_PROGRESS:
return fsm->state_entered + MAX(1000, fsm->backoff);
case S_ACTIVE:
case S_BACKOFF:
return RECONNECT_CONNECT;
- case S_CONNECTING:
+ case S_START_CONNECT:
+ case S_CONNECT_IN_PROGRESS:
return RECONNECT_DISCONNECT;
case S_ACTIVE:
NOT_REACHED();
} else {
- return fsm->state == S_CONNECTING ? RECONNECT_CONNECT : 0;
+ return fsm->state == S_START_CONNECT ? RECONNECT_CONNECT : 0;
}
}
stats->state = reconnect_state_name__(fsm->state);
stats->state_elapsed = now - fsm->state_entered;
}
+
+static bool
+reconnect_may_retry(struct reconnect *fsm)
+{
+ bool may_retry = fsm->max_tries > 0;
+ if (may_retry && fsm->max_tries != UINT_MAX) {
+ fsm->max_tries--;
+ }
+ return may_retry;
+}