* @author Christian Grothoff
*
* TODO:
- * - Optimization: keep per-connection performance metrics (?)
+ * - keep per-connection performance metrics
+ * - in particular, interact with channel (!) to see
+ * if we get ACKs indicating successful payload delivery.
*/
#include "platform.h"
#include "gnunet-service-cadet-new.h"
*/
enum CadetConnectionState state;
+ /**
+ * Options for the route, control buffering.
+ */
+ enum GNUNET_CADET_ChannelOption options;
+
/**
* Offset of our @e destination in @e path.
*/
};
+/**
+ * Update the connection state. Also triggers the necessary
+ * MQM notifications.
+ *
+ * @param cc connection to update the state for
+ * @param new_state new state for @a cc
+ * @param new_mqm_ready new `mqm_ready` state for @a cc
+ */
+static void
+update_state (struct CadetConnection *cc,
+ enum CadetConnectionState new_state,
+ int new_mqm_ready)
+{
+ int old_ready;
+ int new_ready;
+
+ if ( (new_state == cc->state) &&
+ (new_mqm_ready == cc->mqm_ready) )
+ return; /* no change, nothing to do */
+ old_ready = ( (CADET_CONNECTION_READY == cc->state) &&
+ (GNUNET_YES == cc->mqm_ready) );
+ new_ready = ( (CADET_CONNECTION_READY == new_state) &&
+ (GNUNET_YES == new_mqm_ready) );
+ cc->state = new_state;
+ cc->mqm_ready = new_mqm_ready;
+ if (old_ready != new_ready)
+ cc->ready_cb (cc->ready_cb_cls,
+ new_ready);
+}
+
+
/**
* Destroy a connection, part of the internal implementation. Called
* only from #GCC_destroy_from_core() or #GCC_destroy_from_tunnel().
GCPP_del_connection (cc->path,
cc->off,
cc);
+ for (unsigned int i=0;i<cc->off;i++)
+ GCP_remove_connection (GCPP_get_peer_at_offset (cc->path,
+ i),
+ cc);
GNUNET_assert (GNUNET_YES ==
GNUNET_CONTAINER_multishortmap_remove (connections,
&GCC_get_id (cc)->connection_of_tunnel,
GCC_2s (cc),
cc->state,
(GNUNET_YES == cc->mqm_ready) ? "MQM ready" : "MQM busy");
+ if (CADET_CONNECTION_READY == cc->state)
+ return; /* Duplicate ACK, ignore */
if (NULL != cc->task)
{
GNUNET_SCHEDULER_cancel (cc->task);
cc->task = NULL;
}
- cc->state = CADET_CONNECTION_READY;
- if (GNUNET_YES == cc->mqm_ready)
- {
- cc->ready_cb (cc->ready_cb_cls,
- GNUNET_YES);
- if ( (NULL == cc->keepalive_qe) &&
- (GNUNET_YES == cc->mqm_ready) &&
- (NULL == cc->task) )
- cc->task = GNUNET_SCHEDULER_add_delayed (keepalive_period,
- &send_keepalive,
- cc);
- }
+ update_state (cc,
+ CADET_CONNECTION_READY,
+ cc->mqm_ready);
+ if ( (NULL == cc->keepalive_qe) &&
+ (GNUNET_YES == cc->mqm_ready) &&
+ (NULL == cc->task) )
+ cc->task = GNUNET_SCHEDULER_add_delayed (keepalive_period,
+ &send_keepalive,
+ cc);
}
env = GNUNET_MQ_msg_extra (create_msg,
(1 + path_length) * sizeof (struct GNUNET_PeerIdentity),
GNUNET_MESSAGE_TYPE_CADET_CONNECTION_CREATE);
+ create_msg->options = htonl ((uint32_t) cc->options);
create_msg->cid = cc->cid;
pids = (struct GNUNET_PeerIdentity *) &create_msg[1];
pids[0] = my_full_id;
"Sending CADET_CONNECTION_CREATE message for %s\n",
GCC_2s (cc));
cc->env = env;
- cc->mqm_ready = GNUNET_NO;
- cc->state = CADET_CONNECTION_SENT;
+ update_state (cc,
+ CADET_CONNECTION_SENT,
+ GNUNET_NO);
GCP_send (cc->mq_man,
env);
}
GNUNET_MESSAGE_TYPE_CADET_CONNECTION_CREATE_ACK);
ack_msg->cid = cc->cid;
cc->env = env;
- cc->mqm_ready = GNUNET_NO;
- cc->state = CADET_CONNECTION_READY;
+ update_state (cc,
+ CADET_CONNECTION_READY,
+ GNUNET_NO);
GCP_send (cc->mq_man,
env);
}
"Got duplicate CREATE for %s, scheduling another ACK (%s)\n",
GCC_2s (cc),
(GNUNET_YES == cc->mqm_ready) ? "MQM ready" : "MQM busy");
- /* Tell tunnel that we are not ready for transmission anymore
- (until CREATE_ACK is done) */
- cc->ready_cb (cc->ready_cb_cls,
- GNUNET_NO);
/* Revert back to the state of having only received the 'CREATE',
and immediately proceed to send the CREATE_ACK. */
- cc->state = CADET_CONNECTION_CREATE_RECEIVED;
+ update_state (cc,
+ CADET_CONNECTION_CREATE_RECEIVED,
+ cc->mqm_ready);
if (NULL != cc->task)
GNUNET_SCHEDULER_cancel (cc->task);
cc->task = GNUNET_SCHEDULER_add_now (&send_create_ack,
LOG (GNUNET_ERROR_TYPE_DEBUG,
"Core MQ for %s went down\n",
GCC_2s (cc));
- cc->mqm_ready = GNUNET_NO;
- cc->state = CADET_CONNECTION_NEW;
+ update_state (cc,
+ CADET_CONNECTION_NEW,
+ GNUNET_NO);
cc->retry_delay = GNUNET_TIME_UNIT_ZERO;
if (NULL != cc->task)
{
GNUNET_SCHEDULER_cancel (cc->task);
cc->task = NULL;
}
- cc->ready_cb (cc->ready_cb_cls,
- GNUNET_NO);
return;
}
- cc->mqm_ready = GNUNET_YES;
+ update_state (cc,
+ cc->state,
+ GNUNET_YES);
LOG (GNUNET_ERROR_TYPE_DEBUG,
"Core MQ for %s became available in state %d\n",
GCC_2s (cc),
cc);
break;
case CADET_CONNECTION_READY:
- cc->ready_cb (cc->ready_cb_cls,
- GNUNET_YES);
if ( (NULL == cc->keepalive_qe) &&
(GNUNET_YES == cc->mqm_ready) &&
(NULL == cc->task) )
*
* @param destination where to go
* @param path which path to take (may not be the full path)
+ * @param options options for the connection
* @param ct which tunnel uses this connection
* @param init_state initial state for the connection
* @param ready_cb function to call when ready to transmit
static struct CadetConnection *
connection_create (struct CadetPeer *destination,
struct CadetPeerPath *path,
+ enum GNUNET_CADET_ChannelOption options,
struct CadetTConnection *ct,
const struct GNUNET_CADET_ConnectionTunnelIdentifier *cid,
enum CadetConnectionState init_state,
destination);
GNUNET_assert (UINT_MAX > off);
cc = GNUNET_new (struct CadetConnection);
+ cc->options = options;
cc->state = init_state;
cc->ct = ct;
cc->cid = *cid;
cc);
for (unsigned int i=0;i<off;i++)
GCP_add_connection (GCPP_get_peer_at_offset (path,
- off),
+ i),
cc);
first_hop = GCPP_get_peer_at_offset (path,
*
* @param destination where to go
* @param path which path to take (may not be the full path)
+ * @param options options for the connection
* @param ct which tunnel uses this connection
* @param ready_cb function to call when ready to transmit
* @param ready_cb_cls closure for @a cb
struct CadetConnection *
GCC_create_inbound (struct CadetPeer *destination,
struct CadetPeerPath *path,
+ enum GNUNET_CADET_ChannelOption options,
struct CadetTConnection *ct,
const struct GNUNET_CADET_ConnectionTunnelIdentifier *cid,
GCC_ReadyCallback ready_cb,
return connection_create (destination,
path,
+ options,
ct,
cid,
CADET_CONNECTION_CREATE_RECEIVED,
*
* @param destination where to go
* @param path which path to take (may not be the full path)
+ * @param options options for the connection
* @param ct tunnel that uses the connection
* @param ready_cb function to call when ready to transmit
* @param ready_cb_cls closure for @a cb
struct CadetConnection *
GCC_create (struct CadetPeer *destination,
struct CadetPeerPath *path,
+ enum GNUNET_CADET_ChannelOption options,
struct CadetTConnection *ct,
GCC_ReadyCallback ready_cb,
void *ready_cb_cls)
sizeof (cid));
return connection_create (destination,
path,
+ options,
ct,
&cid,
CADET_CONNECTION_NEW,