Added silc_packet_stream_link, silc_packet_stream_unlink.
authorPekka Riikonen <priikone@silcnet.org>
Thu, 29 Jun 2006 21:20:38 +0000 (21:20 +0000)
committerPekka Riikonen <priikone@silcnet.org>
Thu, 29 Jun 2006 21:20:38 +0000 (21:20 +0000)
Added silc_packet_wait_init, _uninit, silc_packet_wait.

lib/silccore/silcpacket.c
lib/silccore/silcpacket.h

index bea26e774325834672e75954eea541a102513517..5f3c55d092ba0c94c75f41ba55c1d2488bc38fcd 100644 (file)
@@ -4,7 +4,7 @@
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 1997 - 2005 Pekka Riikonen
+  Copyright (C) 1997 - 2006 Pekka Riikonen
 
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
@@ -211,8 +211,8 @@ static void silc_packet_stream_io(SilcStream stream, SilcStreamStatus status,
     break;
 
   case SILC_STREAM_CAN_READ:
-    /* Packet receiving can only happen in one thread, so locking is not
-       required in packet receiving procedure. */
+    /* Packet receiving can only happen in one thread for one SilcPacketStream,
+       so locking is not required in packet receiving procedure. */
     silc_mutex_unlock(ps->lock);
 
     SILC_LOG_DEBUG(("Reading data from stream"));
@@ -463,14 +463,14 @@ void silc_packet_stream_set_router(SilcPacketStream stream)
   stream->is_router = TRUE;
 }
 
+
 /* Links `callbacks' to `stream' for specified packet types */
 
-SilcBool silc_packet_stream_link(SilcPacketStream stream,
-                                SilcPacketCallbacks *callbacks,
-                                void *callback_context,
-                                int priority, ...)
+static SilcBool silc_packet_stream_link_va(SilcPacketStream stream,
+                                          SilcPacketCallbacks *callbacks,
+                                          void *callback_context,
+                                          int priority, va_list ap)
 {
-  va_list ap;
   SilcPacketProcess p, e;
   SilcInt32 packet_type;
   int i;
@@ -510,10 +510,7 @@ SilcBool silc_packet_stream_link(SilcPacketStream stream,
   if (!e)
     silc_dlist_add(stream->process, p);
 
-  silc_mutex_unlock(stream->lock);
-
   /* Get packet types to process */
-  va_start(ap, priority);
   i = 1;
   while (1) {
     packet_type = va_arg(ap, SilcInt32);
@@ -525,21 +522,42 @@ SilcBool silc_packet_stream_link(SilcPacketStream stream,
       break;
 
     p->types = silc_realloc(p->types, sizeof(*p->types) * (i + 1));
-    if (!p->types)
+    if (!p->types) {
+      silc_mutex_unlock(stream->lock);
       return FALSE;
+    }
 
     p->types[i - 1] = (SilcPacketType)packet_type;
     i++;
   }
   if (p->types)
     p->types[i - 1] = 0;
-  va_end(ap);
+
+  silc_mutex_unlock(stream->lock);
 
   silc_packet_stream_ref(stream);
 
   return TRUE;
 }
 
+/* Links `callbacks' to `stream' for specified packet types */
+
+SilcBool silc_packet_stream_link(SilcPacketStream stream,
+                                SilcPacketCallbacks *callbacks,
+                                void *callback_context,
+                                int priority, ...)
+{
+  va_list ap;
+  SilcBool ret;
+
+  va_start(ap, priority);
+  ret = silc_packet_stream_link_va(stream, callbacks, callback_context,
+                                  priority, ap);
+  va_end(ap);
+
+  return ret;
+}
+
 /* Unlinks `callbacks' from `stream'. */
 
 void silc_packet_stream_unlink(SilcPacketStream stream,
@@ -886,13 +904,15 @@ static SilcBool silc_packet_send_raw(SilcPacketStream stream,
                   packet.data, silc_buffer_len(&packet));
 
   /* Encrypt the packet */
-  if (cipher)
+  if (cipher) {
+    SILC_LOG_DEBUG(("Encrypting packet"));
     if (!silc_cipher_encrypt(cipher, packet.data, packet.data,
                             enclen, NULL)) {
       SILC_LOG_ERROR(("Packet encryption failed"));
       silc_mutex_unlock(stream->lock);
       return FALSE;
     }
+  }
 
   /* Compute HMAC */
   if (hmac) {
@@ -1378,3 +1398,142 @@ static void silc_packet_read_process(SilcPacketStream stream)
 
   silc_buffer_reset(&stream->inbuf);
 }
+
+
+/****************************** Packet Waiting ******************************/
+
+/* Packet wait receive callback */
+static SilcBool
+silc_packet_wait_packet_receive(SilcPacketEngine engine,
+                               SilcPacketStream stream,
+                               SilcPacket packet,
+                               void *callback_context,
+                               void *stream_context);
+
+/* Packet waiting callbacks */
+static SilcPacketCallbacks silc_packet_wait_cbs =
+{
+  silc_packet_wait_packet_receive, NULL, NULL
+};
+
+/* Packet waiting context */
+typedef struct {
+  SilcMutex wait_lock;
+  SilcCond wait_cond;
+  SilcList packet_queue;
+  SilcBool waiting;
+} *SilcPacketWait;
+
+/* Packet wait receive callback */
+
+static SilcBool
+silc_packet_wait_packet_receive(SilcPacketEngine engine,
+                               SilcPacketStream stream,
+                               SilcPacket packet,
+                               void *callback_context,
+                               void *stream_context)
+{
+  SilcPacketWait pw = callback_context;
+
+  /* Signal the waiting thread for a new packet */
+  silc_mutex_lock(pw->wait_lock);
+
+  if (!pw->waiting) {
+    silc_mutex_unlock(pw->wait_lock);
+    return FALSE;
+  }
+
+  silc_list_add(pw->packet_queue, packet);
+  silc_cond_broadcast(pw->wait_cond);
+
+  silc_mutex_unlock(pw->wait_lock);
+
+  return TRUE;
+}
+
+/* Initialize packet waiting */
+
+void *silc_packet_wait_init(SilcPacketStream stream, ...)
+{
+  SilcPacketWait pw;
+  SilcBool ret;
+  va_list ap;
+
+  pw = silc_calloc(1, sizeof(*pw));
+  if (!pw)
+    return NULL;
+
+  /* Allocate mutex and conditional variable */
+  if (!silc_mutex_alloc(&pw->wait_lock)) {
+    silc_free(pw);
+    return NULL;
+  }
+  if (!silc_cond_alloc(&pw->wait_cond)) {
+    silc_mutex_free(pw->wait_lock);
+    silc_free(pw);
+    return NULL;
+  }
+
+  /* Link to the packet stream for the requested packet types */
+  va_start(ap, stream);
+  ret = silc_packet_stream_link_va(stream, &silc_packet_wait_cbs, pw,
+                                  10000000, ap);
+  va_end(ap);
+  if (!ret) {
+    silc_cond_free(pw->wait_cond);
+    silc_mutex_free(pw->wait_lock);
+    silc_free(pw);
+    return NULL;
+  }
+
+  /* Initialize packet queue */
+  silc_list_init(pw->packet_queue, struct SilcPacketStruct, next);
+
+  return (void *)pw;
+}
+
+/* Uninitialize packet waiting */
+
+void silc_packet_wait_uninit(void *context, SilcPacketStream stream)
+{
+  SilcPacketWait pw = context;
+  SilcPacket packet;
+
+  silc_mutex_lock(pw->wait_lock);
+  silc_packet_stream_unlink(stream, &silc_packet_wait_cbs, pw);
+
+  /* Free any remaining packets */
+  silc_list_start(pw->packet_queue);
+  while ((packet = silc_list_get(pw->packet_queue)) != SILC_LIST_END)
+    silc_packet_free(packet);
+
+  silc_mutex_unlock(pw->wait_lock);
+
+  silc_cond_free(pw->wait_cond);
+  silc_mutex_free(pw->wait_lock);
+  silc_free(pw);
+}
+
+/* Blocks thread until a packet has been received. */
+
+int silc_packet_wait(void *context, int timeout, SilcPacket *return_packet)
+{
+  SilcPacketWait pw = context;
+  SilcBool ret = FALSE;
+
+  silc_mutex_lock(pw->wait_lock);
+
+  /* Wait here until packet has arrived */
+  pw->waiting = TRUE;
+  while (silc_list_count(pw->packet_queue) == 0)
+    ret = silc_cond_timedwait(pw->wait_cond, pw->wait_lock, timeout);
+
+  /* Return packet */
+  silc_list_start(pw->packet_queue);
+  *return_packet = silc_list_get(pw->packet_queue);
+  silc_list_del(pw->packet_queue, *return_packet);
+
+  silc_mutex_unlock(pw->wait_lock);
+
+  return ret == TRUE ? 1 : 0;
+}
index f7c6853cdcc6687d76e8e80f678031a799de310e..6f2c15a7f2891bc620ff99b98ef2891262181332 100644 (file)
@@ -4,7 +4,7 @@
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 1997 - 2005 Pekka Riikonen
+  Copyright (C) 1997 - 2006 Pekka Riikonen
 
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
@@ -789,6 +789,100 @@ SilcBool silc_packet_send_ext(SilcPacketStream stream,
                              const unsigned char *data, SilcUInt32 data_len,
                              SilcCipher cipher, SilcHmac hmac);
 
+/****f* silccore/SilcPacketAPI/silc_packet_wait
+ *
+ * SYNOPSIS
+ *
+ *    void *silc_packet_wait_init(SilcPacketStream stream, ...);
+ *
+ * DESCRIPTION
+ *
+ *    Initializes a packet waiter for the packet stream `stream' and
+ *    for the variable argument list of packet types.  The function
+ *    silc_packet_wait can be used to block the thread until a packet
+ *    has been received.  This function is used to initialize the waiting
+ *    and to give the list of packet types that caller wish to receive.
+ *    The variable argument list must end with -1.  To receive all
+ *    packets use SILC_PACKET_ANY.  Returns a context that must be given
+ *    to the silc_packet_wait function as argument.  Returns NULL on
+ *    error.  To uninitialize the waiting call silc_packet_wait_uninit.
+ *
+ * EXAMPLE
+ *
+ *      void *waiter;
+ *
+ *      // Will wait for private message packets
+ *      waiter = silc_packet_wait_init(stream,
+ *                                     SILC_PACKET_PRIVATE_MESSAGE, -1);
+ *
+ *
+ ***/
+void *silc_packet_wait_init(SilcPacketStream stream, ...);
+
+/****f* silccore/SilcPacketAPI/silc_packet_wait
+ *
+ * SYNOPSIS
+ *
+ *    void silc_packet_wait_uninit(void *waiter, SilcPacketStream stream);
+ *
+ * DESCRIPTION
+ *
+ *    Uninitializes the waiting context.
+ *
+ ***/
+void silc_packet_wait_uninit(void *waiter, SilcPacketStream stream);
+
+/****f* silccore/SilcPacketAPI/silc_packet_wait
+ *
+ * SYNOPSIS
+ *
+ *    int silc_packet_wait(void *waiter, int timeout,
+ *                         SilcPacket *return_packet)
+ *
+ * DESCRIPTION
+ *
+ *    A special function that can be used to wait for a packet to arrive.
+ *    This function will block the calling process or thread until either
+ *    a packet is received into the `return_packet' pointer or the specified
+ *    timeout value `timeout', which is in milliseconds, will expire.  If
+ *    the timeout is 0, no timeout exist.  Before calling this function the
+ *    silc_packet_wait_init must be called.  The caller is responsible for
+ *    freeing the returned packet with silc_packet_free.
+ *
+ *    This function can be used for example from a thread that wants to
+ *    block until SILC packet has been received.
+ *
+ *    Returns 1 when packet was received, 0 if timeout occurred and -1 if
+ *    error occurred.
+ *
+ * EXAMPLE
+ *
+ *    static int foo_read_data(FooContext c)
+ *    {
+ *      SilcPacket packet;
+ *      void *waiter;
+ *      ...
+ *
+ *      // Will wait for private message packets
+ *      if (c->initialized == FALSE) {
+ *        waiter = silc_packet_wait_init(stream,
+ *                                       SILC_PACKET_PRIVATE_MESSAGE, -1);
+ *        c->initialized = TRUE;
+ *      }
+ *
+ *      ...
+ *      // Wait here until packet is received
+ *      if ((silc_packet_wait(waiter, 0, &packet)) != -1)
+ *        return -1;
+ *
+ *      ... process packet ...
+ *
+ *      return 1;
+ *    }
+ *
+ ***/
+int silc_packet_wait(void *waiter, int timeout, SilcPacket *return_packet);
+
 /****f* silccore/SilcPacketAPI/silc_packet_free
  *
  * SYNOPSIS