check
[oweals/gnunet.git] / src / core / core_api.c
index d2308d8455a383e73b555f0571e405a3a903e8d0..195e94e29d0dcf56a8cda2491dbb669b2c9ab4a0 100644 (file)
@@ -82,6 +82,11 @@ struct PeerRecord
    */
   void *pcic_cls;
 
+  /**
+   * Pointer to free when we call pcic.
+   */
+  void *pcic_ptr;
+
   /**
    * Request information ID for the given pcic (needed in case a
    * request is cancelled after being submitted to core and a new
@@ -129,12 +134,10 @@ struct ControlMessage
   struct ControlMessage *prev;
 
   /**
-   * Function to run after successful transmission (or call with
-   * reason 'TIMEOUT' on error); called with scheduler context 'NULL'
-   * on disconnect.
+   * Function to run after transmission failed/succeeded.
    */
-  GNUNET_SCHEDULER_Task cont;
-
+  GNUNET_CORE_ControlContinuation cont;
+  
   /**
    * Closure for 'cont'.
    */
@@ -348,6 +351,11 @@ struct GNUNET_CORE_TransmitHandle
    */
   uint16_t smr_id;
 
+  /**
+   * Is corking allowed?
+   */
+  int cork;
+
 };
 
 
@@ -416,10 +424,14 @@ disconnect_and_free_peer_entry (void *cls,
   if (NULL != (pcic = pr->pcic))
     {
       pr->pcic = NULL;
+      GNUNET_free_non_null (pr->pcic_ptr);
+      pr->pcic_ptr = NULL;
       pcic (pr->pcic_cls,
            &pr->peer,
            zero,
-           0, 0);
+           0, 
+           GNUNET_TIME_UNIT_FOREVER_REL,
+           0);
     }
   if (pr->timeout_task != GNUNET_SCHEDULER_NO_TASK)
     {
@@ -462,17 +474,7 @@ reconnect_later (struct GNUNET_CORE_Handle *h)
   struct ControlMessage *cm;
   struct PeerRecord *pr;
 
-  while (NULL != (cm = h->control_pending_head))
-    {
-      GNUNET_CONTAINER_DLL_remove (h->control_pending_head,
-                                  h->control_pending_tail,
-                                  cm);
-      if (cm->th != NULL)
-       cm->th->cm = NULL; 
-      if (cm->cont != NULL)
-       cm->cont (cm->cont_cls, NULL);
-      GNUNET_free (cm);
-    }
+  GNUNET_assert (h->reconnect_task == GNUNET_SCHEDULER_NO_TASK);
   if (h->client != NULL)
     {
       GNUNET_CLIENT_disconnect (h->client, GNUNET_NO);
@@ -486,15 +488,24 @@ reconnect_later (struct GNUNET_CORE_Handle *h)
     GNUNET_CONTAINER_DLL_remove (h->ready_peer_head,
                                 h->ready_peer_tail,
                                 pr);
-  
-  GNUNET_assert (h->control_pending_head == NULL);
   h->currently_down = GNUNET_YES;
-  GNUNET_assert (h->reconnect_task == GNUNET_SCHEDULER_NO_TASK);
-  h->retry_backoff = GNUNET_TIME_relative_min (GNUNET_TIME_UNIT_SECONDS,
-                                              h->retry_backoff);
   h->reconnect_task = GNUNET_SCHEDULER_add_delayed (h->retry_backoff,
                                                    &reconnect_task,
                                                    h);
+  while (NULL != (cm = h->control_pending_head))
+    {
+      GNUNET_CONTAINER_DLL_remove (h->control_pending_head,
+                                  h->control_pending_tail,
+                                  cm);
+      if (cm->th != NULL)
+       cm->th->cm = NULL; 
+      if (cm->cont != NULL)
+       cm->cont (cm->cont_cls, GNUNET_NO);
+      GNUNET_free (cm);
+    }
+  GNUNET_assert (h->control_pending_head == NULL);
+  h->retry_backoff = GNUNET_TIME_relative_min (GNUNET_TIME_UNIT_SECONDS,
+                                              h->retry_backoff);
   h->retry_backoff = GNUNET_TIME_relative_multiply (h->retry_backoff, 2);
 }
 
@@ -547,6 +558,8 @@ request_next_transmission (struct PeerRecord *pr)
       trigger_next_request (h, GNUNET_NO);
       return;
     }
+  if (th->cm != NULL)
+    return; /* already done */
   GNUNET_assert (pr->prev == NULL);
   GNUNET_assert (pr->next == NULL);
   pr->timeout_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_absolute_get_remaining (th->timeout),
@@ -636,6 +649,7 @@ transmit_message (void *cls,
   uint16_t msize;
   size_t ret;
 
+  GNUNET_assert (h->reconnect_task == GNUNET_SCHEDULER_NO_TASK);
   h->cth = NULL;
   if (buf == NULL)
     {
@@ -669,9 +683,7 @@ transmit_message (void *cls,
       if (cm->th != NULL)
        cm->th->cm = NULL;
       if (NULL != cm->cont)
-       GNUNET_SCHEDULER_add_continuation (cm->cont, 
-                                          cm->cont_cls,
-                                          GNUNET_SCHEDULER_REASON_PREREQ_DONE);
+       cm->cont (cm->cont_cls, GNUNET_OK);
       GNUNET_free (cm);
       trigger_next_request (h, GNUNET_NO);
       return msize;
@@ -709,6 +721,8 @@ transmit_message (void *cls,
       sm->priority = htonl (th->priority);
       sm->deadline = GNUNET_TIME_absolute_hton (th->timeout);
       sm->peer = pr->peer;
+      sm->cork = htonl ((uint32_t) th->cork);
+      sm->reserved = htonl (0);
       ret = th->get_message (th->get_message_cls,
                             size - sizeof (struct SendMessage),
                             &sm[1]);
@@ -722,7 +736,8 @@ transmit_message (void *cls,
 #endif
          /* client decided to send nothing! */
          request_next_transmission (pr);
-         return 0;       
+         GNUNET_free (th);
+         return 0;       
        }
 #if DEBUG_CORE
       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
@@ -734,7 +749,8 @@ transmit_message (void *cls,
        {
          GNUNET_break (0);
          request_next_transmission (pr);
-         return 0;
+         GNUNET_free (th);
+         return 0;
        }
       ret += sizeof (struct SendMessage);
       sm->header.size = htons (ret);
@@ -923,7 +939,7 @@ main_notify_handler (void *cls,
                       &cnm->peer,
                       sizeof (struct GNUNET_PeerIdentity)))
        {
-         /* disconnect from self!? */
+         /* connect to self!? */
          GNUNET_break (0);
          return;
        }
@@ -989,7 +1005,7 @@ main_notify_handler (void *cls,
       if (NULL == h->status_events)
         {
           GNUNET_break (0);
-          break;
+         return;
         }
       if (msize < sizeof (struct PeerStatusNotifyMessage))
         {
@@ -1042,14 +1058,7 @@ main_notify_handler (void *cls,
          return;
         }
       ntm = (const struct NotifyTrafficMessage *) msg;
-      if (0 == memcmp (&h->me,
-                      &ntm->peer,
-                      sizeof (struct GNUNET_PeerIdentity)))
-       {
-         /* self-change!? */
-         GNUNET_break (0);
-         return;
-       }
+
       ats_count = ntohl (ntm->ats_count);
       if ( (msize < sizeof (struct NotifyTrafficMessage) + ats_count * sizeof (struct GNUNET_TRANSPORT_ATS_Information)
            + sizeof (struct GNUNET_MessageHeader)) ||
@@ -1241,14 +1250,23 @@ main_notify_handler (void *cls,
          return;
        }
       if (pr->rim_id != ntohl (cim->rim_id))
-       break;
+       {
+#if DEBUG_CORE
+         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                     "Reservation ID mismatch in notification...\n");
+#endif
+         break;
+       }
       pcic = pr->pcic;
       pr->pcic = NULL;
+      GNUNET_free_non_null (pr->pcic_ptr);
+      pr->pcic_ptr = NULL;
       if (pcic != NULL)
        pcic (pr->pcic_cls,
              &pr->peer,
              cim->bw_out,
              ntohl (cim->reserved_amount),
+             GNUNET_TIME_relative_ntoh (cim->reserve_delay),
              GNUNET_ntohll (cim->preference));
       break;
     default:
@@ -1266,23 +1284,24 @@ main_notify_handler (void *cls,
  * Starts our 'receive' loop.
  *
  * @param cls the 'struct GNUNET_CORE_Handle'
- * @param tc task context
+ * @param success were we successful
  */
 static void
 init_done_task (void *cls, 
-               const struct GNUNET_SCHEDULER_TaskContext *tc)
+               int success)
 {
   struct GNUNET_CORE_Handle *h = cls;
 
-  if (tc == NULL)
-    return; /* error */
-  if (0 == (tc->reason & GNUNET_SCHEDULER_REASON_PREREQ_DONE))
+  if (success == GNUNET_SYSERR)
+    return; /* shutdown */
+  if (success == GNUNET_NO)
     {
 #if DEBUG_CORE
       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
                  "Failed to exchange INIT with core, retrying\n");
 #endif
-      reconnect_later (h);
+      if (h->reconnect_task == GNUNET_SCHEDULER_NO_TASK)
+       reconnect_later (h);
       return;
     }
   GNUNET_CLIENT_receive (h->client,
@@ -1454,11 +1473,6 @@ GNUNET_CORE_disconnect (struct GNUNET_CORE_Handle *handle)
       GNUNET_CLIENT_disconnect (handle->client, GNUNET_NO);
       handle->client = NULL;
     }
-  if (handle->reconnect_task != GNUNET_SCHEDULER_NO_TASK)
-    {
-      GNUNET_SCHEDULER_cancel (handle->reconnect_task);
-      handle->reconnect_task = GNUNET_SCHEDULER_NO_TASK;
-    }
   while (NULL != (cm = handle->control_pending_head))
     {
       GNUNET_CONTAINER_DLL_remove (handle->control_pending_head,
@@ -1467,9 +1481,14 @@ GNUNET_CORE_disconnect (struct GNUNET_CORE_Handle *handle)
       if (cm->th != NULL)
        cm->th->cm = NULL;
       if (cm->cont != NULL)
-       cm->cont (cm->cont_cls, NULL);
+       cm->cont (cm->cont_cls, GNUNET_SYSERR);
       GNUNET_free (cm);
     }
+  if (handle->reconnect_task != GNUNET_SCHEDULER_NO_TASK)
+    {
+      GNUNET_SCHEDULER_cancel (handle->reconnect_task);
+      handle->reconnect_task = GNUNET_SCHEDULER_NO_TASK;
+    }
   GNUNET_CONTAINER_multihashmap_iterate (handle->peers,
                                         &disconnect_and_free_peer_entry,
                                         handle);
@@ -1481,11 +1500,12 @@ GNUNET_CORE_disconnect (struct GNUNET_CORE_Handle *handle)
 
 /**
  * Ask the core to call "notify" once it is ready to transmit the
- * given number of bytes to the specified "target".  If we are not yet
- * connected to the specified peer, a call to this function will cause
- * us to try to establish a connection.
+ * given number of bytes to the specified "target".    Must only be
+ * called after a connection to the respective peer has been
+ * established (and the client has been informed about this).
  *
  * @param handle connection to core service
+ * @param cork is corking allowed for this transmission?
  * @param priority how important is the message?
  * @param maxdelay how long can the message wait?
  * @param target who should receive the message,
@@ -1499,6 +1519,7 @@ GNUNET_CORE_disconnect (struct GNUNET_CORE_Handle *handle)
  */
 struct GNUNET_CORE_TransmitHandle *
 GNUNET_CORE_notify_transmit_ready (struct GNUNET_CORE_Handle *handle,
+                                  int cork,
                                    uint32_t priority,
                                    struct GNUNET_TIME_Relative maxdelay,
                                    const struct GNUNET_PeerIdentity *target,
@@ -1527,11 +1548,13 @@ GNUNET_CORE_notify_transmit_ready (struct GNUNET_CORE_Handle *handle,
                  GNUNET_SERVER_MAX_MESSAGE_SIZE);
   th = GNUNET_malloc (sizeof (struct GNUNET_CORE_TransmitHandle));
   th->peer = pr;
+  GNUNET_assert(NULL != notify);
   th->get_message = notify;
   th->get_message_cls = notify_cls;
   th->timeout = GNUNET_TIME_relative_to_absolute (maxdelay);
   th->priority = priority;
   th->msize = notify_size;
+  th->cork = cork;
   /* bound queue size */
   if (pr->queue_size == handle->queue_size)
     {
@@ -1547,8 +1570,12 @@ GNUNET_CORE_notify_transmit_ready (struct GNUNET_CORE_Handle *handle,
       if (minp == NULL) 
        {
          GNUNET_break (handle->queue_size != 0);
-         GNUNET_break (pr->queue_size == 0);
+         GNUNET_break (pr->queue_size == 1);
          GNUNET_free(th);
+#if DEBUG_CORE
+         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                     "Dropping transmission request: cannot drop queue head and limit is one\n");
+#endif
          return NULL;
        }
       if (priority <= minp->priority)
@@ -1557,6 +1584,7 @@ GNUNET_CORE_notify_transmit_ready (struct GNUNET_CORE_Handle *handle,
          GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
                      "Dropping transmission request: priority too low\n");
 #endif
+         GNUNET_free(th);
          return NULL; /* priority too low */
        }
       GNUNET_CONTAINER_DLL_remove (pr->pending_head,
@@ -1672,7 +1700,7 @@ struct GNUNET_CORE_PeerRequestHandle
   /**
    * Continuation to run when done.
    */
-  GNUNET_SCHEDULER_Task cont;
+  GNUNET_CORE_ControlContinuation cont;
 
   /**
    * Closure for 'cont'.
@@ -1688,22 +1716,16 @@ struct GNUNET_CORE_PeerRequestHandle
  * resources.
  *
  * @param cls the 'struct GNUNET_CORE_PeerRequestHandle'
- * @param tc scheduler context
+ * @param success was the request transmitted?
  */
 static void
 peer_request_connect_cont (void *cls,
-                          const struct GNUNET_SCHEDULER_TaskContext *tc)
+                          int success)
 {
   struct GNUNET_CORE_PeerRequestHandle *ret = cls;
   
   if (ret->cont != NULL)
-    {
-      if (tc == NULL)
-       GNUNET_SCHEDULER_add_now (ret->cont,
-                                 ret->cont_cls);
-      else
-       ret->cont (ret->cont_cls, tc);
-    }
+    ret->cont (ret->cont_cls, success);    
   GNUNET_free (ret);
 }
 
@@ -1724,18 +1746,30 @@ peer_request_connect_cont (void *cls,
  * @param peer who should we connect to
  * @param cont function to call once the request has been completed (or timed out)
  * @param cont_cls closure for cont
- * @return NULL on error (cont will not be called), otherwise handle for cancellation
+ *
+ * @return NULL on error or already connected,
+ *         otherwise handle for cancellation
  */
 struct GNUNET_CORE_PeerRequestHandle *
 GNUNET_CORE_peer_request_connect (struct GNUNET_CORE_Handle *h,
                                  struct GNUNET_TIME_Relative timeout,
                                  const struct GNUNET_PeerIdentity * peer,
-                                 GNUNET_SCHEDULER_Task cont,
+                                 GNUNET_CORE_ControlContinuation cont,
                                  void *cont_cls)
 {
   struct GNUNET_CORE_PeerRequestHandle *ret;
   struct ControlMessage *cm;
   struct ConnectMessage *msg;
+
+  if (NULL != GNUNET_CONTAINER_multihashmap_get (h->peers,
+                                          &peer->hashPubKey))
+    {
+#if DEBUG_CORE
+      GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, 
+                "Peers are already connected!\n");
+#endif
+      return NULL;
+    }
   
   cm = GNUNET_malloc (sizeof (struct ControlMessage) + 
                      sizeof (struct ConnectMessage));
@@ -1745,9 +1779,9 @@ GNUNET_CORE_peer_request_connect (struct GNUNET_CORE_Handle *h,
   msg->reserved = htonl (0);
   msg->timeout = GNUNET_TIME_relative_hton (timeout);
   msg->peer = *peer;
-  GNUNET_CONTAINER_DLL_insert (h->control_pending_head,
-                              h->control_pending_tail,
-                              cm);
+  GNUNET_CONTAINER_DLL_insert_tail (h->control_pending_head,
+                                   h->control_pending_tail,
+                                   cm);
   ret = GNUNET_malloc (sizeof (struct GNUNET_CORE_PeerRequestHandle));
   ret->h = h;
   ret->cm = cm;
@@ -1796,16 +1830,6 @@ struct GNUNET_CORE_InformationRequestContext
    */
   struct GNUNET_CORE_Handle *h;
 
-  /**
-   * Function to call with the information.
-   */
-  GNUNET_CORE_PeerConfigurationInfoCallback info;
-
-  /**
-   * Closure for info.
-   */
-  void *info_cls;
-
   /**
    * Link to control message, NULL if CM was sent.
    */ 
@@ -1822,11 +1846,11 @@ struct GNUNET_CORE_InformationRequestContext
  * CM was sent, remove link so we don't double-free.
  *
  * @param cls the 'struct GNUNET_CORE_InformationRequestContext'
- * @param tc scheduler context
+ * @param success were we successful?
  */
 static void
 change_preference_send_continuation (void *cls,
-                                    const struct GNUNET_SCHEDULER_TaskContext *tc)
+                                    int success)
 {
   struct GNUNET_CORE_InformationRequestContext *irc = cls;
 
@@ -1890,8 +1914,6 @@ GNUNET_CORE_peer_change_preference (struct GNUNET_CORE_Handle *h,
   irc = GNUNET_malloc (sizeof (struct GNUNET_CORE_InformationRequestContext));
   irc->h = h;
   irc->pr = pr;
-  irc->info = info;
-  irc->info_cls = info_cls;
   cm = GNUNET_malloc (sizeof (struct ControlMessage) +
                      sizeof (struct RequestInfoMessage));
   cm->cont = &change_preference_send_continuation;
@@ -1909,11 +1931,12 @@ GNUNET_CORE_peer_change_preference (struct GNUNET_CORE_Handle *h,
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
              "Queueing CHANGE PREFERENCE request\n");
 #endif
-  GNUNET_CONTAINER_DLL_insert (h->control_pending_head,
-                              h->control_pending_tail,
-                              cm); 
+  GNUNET_CONTAINER_DLL_insert_tail (h->control_pending_head,
+                                   h->control_pending_tail,
+                                   cm); 
   pr->pcic = info;
   pr->pcic_cls = info_cls;
+  pr->pcic_ptr = irc; /* for free'ing irc */
   if (h->control_pending_head == cm)
     trigger_next_request (h, GNUNET_NO);
   return irc;
@@ -1937,6 +1960,7 @@ GNUNET_CORE_peer_change_preference_cancel (struct GNUNET_CORE_InformationRequest
   struct GNUNET_CORE_Handle *h = irc->h;
   struct PeerRecord *pr = irc->pr;
 
+  GNUNET_assert (pr->pcic_ptr == irc);
   if (irc->cm != NULL)
     {
       GNUNET_CONTAINER_DLL_remove (h->control_pending_head,
@@ -1946,6 +1970,7 @@ GNUNET_CORE_peer_change_preference_cancel (struct GNUNET_CORE_InformationRequest
     }
   pr->pcic = NULL;
   pr->pcic_cls = NULL;
+  pr->pcic_ptr = NULL;
   GNUNET_free (irc);
 }