Hi List,
attached you can find v3 of boxc patch. For more details for what it should be
good, see Message-Id: <[EMAIL PROTECTED]>.
Comments and votes please...
--
Best regards / Mit besten Gr��en aus D�sseldorf
Dipl.-Ing.
Alexander Malysh
___________________________________________
Centrium GmbH
Vogelsanger Weg 80
40470 D�sseldorf
Fon: +49 (0211) 74 84 51 80
Fax: +49 (0211) 277 49 109
email: [EMAIL PROTECTED]
web: www.centrium.de
msn: [EMAIL PROTECTED]
icq: 98063111
___________________________________________
Please avoid sending me Word or PowerPoint attachments.
See http://www.fsf.org/philosophy/no-word-attachments.html
Index: gw/bb_boxc.c
===================================================================
RCS file: /home/cvs/gateway/gw/bb_boxc.c,v
retrieving revision 1.70
diff -a -u -r1.70 bb_boxc.c
--- gw/bb_boxc.c 12 Oct 2003 21:46:09 -0000 1.70
+++ gw/bb_boxc.c 16 Oct 2003 16:28:25 -0000
@@ -59,6 +59,9 @@
static long boxid = 0;
extern Mutex *boxid_mutex;
+/* sms_to_smsboxes thread-id */
+static long sms_dequeue_thread;
+
typedef struct _boxc {
Connection *conn;
@@ -72,10 +75,15 @@
List *outgoing;
volatile sig_atomic_t alive;
Octstr *boxc_id; /* identifies the connected smsbox instance */
- Mutex *boxc_id_mutex; /* stops boxc_sender until smsbox identification*/
+ /* used to mark connection usable or still waiting for ident. msg */
+ volatile int routable;
} Boxc;
+/* forward declaration */
+static void sms_to_smsboxes(void *arg);
+
+
/*-------------------------------------------------
* receiver thingies
*/
@@ -88,6 +96,7 @@
pack = NULL;
while (bb_status != BB_DEAD && boxconn->alive) {
+ /* XXX: if box doesn't send (just keep conn open) we block here while shutdown */
pack = conn_read_withlen(boxconn->conn);
gw_claim_area(pack);
if (pack != NULL)
@@ -110,7 +119,7 @@
return NULL;
}
}
-
+
if (pack == NULL)
return NULL;
@@ -140,6 +149,7 @@
gw_assert(mack != NULL);
mack->ack.id = msg->sms.id;
mack->ack.time = msg->sms.time;
+
store_save(msg);
rc = smsc2_rout(msg);
@@ -150,8 +160,9 @@
case 0:
mack->ack.nack = ack_buffered;
break;
- case -1:
- warning(0, "Message rejected by bearerbox, no router!");
+ case -1: /* no router at all */
+ case -2: /* outgoing queue too long */
+ warning(0, "Message rejected by bearerbox, %s!", (rc==-1) ? "no router": "queue full");
/*
* first create nack for store-file, in order to delete
* message from store-file.
@@ -160,12 +171,12 @@
gw_assert(mack_store != NULL);
mack_store->ack.id = msg->sms.id;
mack_store->ack.time = msg->sms.time;
- mack_store->ack.nack = ack_failed;
+ mack_store->ack.nack = (rc==-1?ack_failed:ack_failed_tmp);
store_save(mack_store);
msg_destroy(mack_store);
/* create failed nack */
- mack->ack.nack = ack_failed;
+ mack->ack.nack = (rc==-1?ack_failed:ack_failed_tmp);
/* destroy original message */
msg_destroy(msg);
@@ -180,7 +191,7 @@
static void boxc_receiver(void *arg)
{
Boxc *conn = arg;
- Msg *msg;
+ Msg *msg, *mack;
/* remove messages from socket until it is closed */
while (bb_status != BB_DEAD && conn->alive) {
@@ -194,15 +205,33 @@
break;
}
+ /* we don't accept new messages in shutdown phase */
+ if ((bb_status == BB_SHUTDOWN || bb_status == BB_DEAD) && msg_type(msg) == sms) {
+ mack = msg_create(ack);
+ mack->ack.id = msg->sms.id;
+ mack->ack.time = msg->sms.time;
+ mack->ack.nack = ack_failed_tmp;
+ msg_destroy(msg);
+ list_produce(conn->incoming, mack);
+ continue;
+ }
+
if (msg_type(msg) == sms && conn->is_wap == 0) {
debug("bb.boxc", 0, "boxc_receiver: sms received");
/* deliver message to queue */
deliver_sms_to_queue(msg, conn);
+ if (conn->routable == 0) {
+ conn->routable = 1;
+ /* wakeup the dequeue thread */
+ gwthread_wakeup(sms_dequeue_thread);
+ }
} else if (msg_type(msg) == wdp_datagram && conn->is_wap) {
debug("bb.boxc", 0, "boxc_receiver: got wdp from wapbox");
-
+
+ /* XXX we should block these in SHUTDOWN phase too, but
+ we need ack/nack msgs implemented first. */
list_produce(conn->outgoing, msg);
} else if (msg_type(msg) == sms && conn->is_wap) {
@@ -211,6 +240,11 @@
/* should be a WAP push message, so tried it the same way */
deliver_sms_to_queue(msg, conn);
+ if (conn->routable == 0) {
+ conn->routable = 1;
+ /* wakeup the dequeue thread */
+ gwthread_wakeup(sms_dequeue_thread);
+ }
} else {
if (msg_type(msg) == heartbeat) {
if (msg->heartbeat.load != conn->load)
@@ -224,41 +258,40 @@
}
/* if this is an identification message from an smsbox instance */
else if (msg_type(msg) == admin && msg->admin.command == cmd_identify) {
-
- /*
+
+ /*
* any smsbox sends this command even if boxc_id is NULL,
* but we will only consider real identified boxes
*/
if (msg->admin.boxc_id != NULL) {
- List *newlist;
/* and add the boxc_id into conn for boxc_status() output */
- if (conn->boxc_id == NULL)
- conn->boxc_id = octstr_duplicate(msg->admin.boxc_id);
- /*
- * re-link the incoming queue for this connection to
- * an own independent queue
- */
- newlist = list_create();
- list_add_producer(newlist);
- conn->incoming = newlist;
- conn->retry = newlist;
-
- /* add this identified smsbox to the dictionary */
- dict_put(smsbox_by_id, msg->admin.boxc_id, conn);
+ if (conn->boxc_id != NULL) {
+ dict_remove(smsbox_by_id, msg->admin.boxc_id);
+ octstr_destroy(conn->boxc_id);
+ }
+
+ conn->boxc_id = msg->admin.boxc_id;
+ msg->admin.boxc_id = NULL;
+
+ /* add this identified smsbox to the dictionary */
+ /* XXX check for equal boxc_id in Dict, otherwise we overwrite it */
+ dict_put(smsbox_by_id, conn->boxc_id, conn);
debug("bb.boxc", 0, "boxc_receiver: got boxc_id <%s> from <%s>",
- octstr_get_cstr(msg->admin.boxc_id),
+ octstr_get_cstr(conn->boxc_id),
octstr_get_cstr(conn->client_ip));
}
- debug("bb.boxc", 0, "boxc_receiver: unlocking sender");
- mutex_unlock(conn->boxc_id_mutex);
+
+ conn->routable = 1;
+ /* wakeup the dequeue thread */
+ gwthread_wakeup(sms_dequeue_thread);
}
else
warning(0, "boxc_receiver: unknown msg received from <%s>, "
"ignored", octstr_get_cstr(conn->client_ip));
msg_destroy(msg);
}
- }
+ }
}
@@ -271,17 +304,24 @@
Octstr *pack;
pack = msg_pack(pmsg);
+
+ if (pack == NULL)
+ return -1;
+
if (boxconn->boxc_id != NULL)
debug("bb.boxc", 0, "send_msg: sending msg to boxc: <%s>",
octstr_get_cstr(boxconn->boxc_id));
else
debug("bb.boxc", 0, "send_msg: sending msg to box: <%s>",
octstr_get_cstr(boxconn->client_ip));
+
if (conn_write_withlen(boxconn->conn, pack) == -1) {
error(0, "Couldn't write Msg to box <%s>, disconnecting",
octstr_get_cstr(boxconn->client_ip));
- return -1;
+ octstr_destroy(pack);
+ return -1;
}
+
octstr_destroy(pack);
return 0;
}
@@ -294,58 +334,62 @@
list_add_producer(flow_threads);
- /* wait for smsbox identification */
- if (bb_status != BB_DEAD && conn->alive && conn->is_wap == 0) {
- mutex_lock(conn->boxc_id_mutex);
- debug("bb.boxc", 0, "boxc_sender: sender unlocked");
- mutex_unlock(conn->boxc_id_mutex);
- }
-
while (bb_status != BB_DEAD && conn->alive) {
- /* Make sure there's no data left in the outgoing connection before
- * doing the potentially blocking list_consume()s */
- conn_flush(conn->conn);
-
- list_consume(suspended); /* block here if suspended */
-
/*
- * XXX This list_comsume() makes us block until *all* producers of
- * it have been removed, which is sort of a problem, because this
- * thread keeps running, until there are no more clients connected.
+ * Make sure there's no data left in the outgoing connection before
+ * doing the potentially blocking list_consume()s
*/
- if ((msg = list_consume(conn->incoming)) == NULL) {
+ conn_flush(conn->conn);
- /* tell sms/wapbox to die */
- msg = msg_create(admin);
- msg->admin.command = restart ? cmd_restart : cmd_shutdown;
- send_msg(conn, msg);
- msg_destroy(msg);
- break;
- }
- if (msg_type(msg) == heartbeat) {
- debug("bb.boxc", 0, "boxc_sender: catch an heartbeat - we are alive");
- msg_destroy(msg);
- continue;
- }
- if (!conn->alive) {
- /* we got message here */
- list_produce(conn->retry, msg);
- break;
- }
- if (send_msg(conn, msg) == -1) {
- /* if we fail to send, return msg to the list it came from
- * before dying off */
- list_produce(conn->retry, msg);
- break;
- }
- msg_destroy(msg);
- debug("bb.boxc", 0, "boxc_sender: sent message to <%s>",
- octstr_get_cstr(conn->client_ip));
+ list_consume(suspended); /* block here if suspended */
+
+ if ((msg = list_consume(conn->incoming)) == NULL) {
+ /* tell sms/wapbox to die */
+ msg = msg_create(admin);
+ msg->admin.command = restart ? cmd_restart : cmd_shutdown;
+ send_msg(conn, msg);
+ msg_destroy(msg);
+ break;
+ }
+ if (msg_type(msg) == heartbeat) {
+ debug("bb.boxc", 0, "boxc_sender: catch an heartbeat - we are alive");
+ msg_destroy(msg);
+ continue;
+ }
+ if (!conn->alive || send_msg(conn, msg) == -1) {
+ /* we got message here */
+ if (msg_type(msg) != ack) /* XXX can't route ack's */
+ list_produce(conn->retry, msg);
+ break;
+ }
+ msg_destroy(msg);
+ debug("bb.boxc", 0, "boxc_sender: sent message to <%s>",
+ octstr_get_cstr(conn->client_ip));
}
/* the client closes the connection, after that die in receiver */
/* conn->alive = 0; */
+ /* set conn to unroutable */
+ list_lock(smsbox_list); /* to be sure no router are there */
+ conn->routable = 0;
+ list_unlock(smsbox_list);
+
+ /* clear our send queue */
+ while((msg = list_extract_first(conn->incoming)) != NULL) {
+ /*
+ * XXX: we can't route ack's at the moment, so just drop these
+ * from the queue. In future, we should have boxc_id for ack's
+ * so we can properly route these when smsbox will connect again.
+ */
+ if (msg_type(msg) == ack) {
+ warning(0, "bb_boxc: not sent ACK found, drop them.");
+ msg_destroy(msg);
+ }
+ else
+ list_produce(incoming_sms, msg);
+ }
+
list_remove_producer(flow_threads);
}
@@ -357,37 +401,35 @@
static Boxc *boxc_create(int fd, Octstr *ip, int ssl)
{
Boxc *boxc;
-
+
boxc = gw_malloc(sizeof(Boxc));
boxc->is_wap = 0;
boxc->load = 0;
boxc->conn = conn_wrap_fd(fd, ssl);
- mutex_lock(boxid_mutex);
+ mutex_lock(boxid_mutex);
boxc->id = boxid++;
mutex_unlock(boxid_mutex);
boxc->client_ip = ip;
boxc->alive = 1;
boxc->connect_time = time(NULL);
- boxc->boxc_id_mutex = mutex_create();
- mutex_lock(boxc->boxc_id_mutex);
boxc->boxc_id = NULL;
+ boxc->routable = 0;
return boxc;
-}
+}
static void boxc_destroy(Boxc *boxc)
{
if (boxc == NULL)
return;
-
+
/* do nothing to the lists, as they are only references */
if (boxc->conn)
conn_destroy(boxc->conn);
octstr_destroy(boxc->client_ip);
octstr_destroy(boxc->boxc_id);
- mutex_destroy(boxc->boxc_id_mutex);
gw_free(boxc);
-}
+}
@@ -416,7 +458,7 @@
return NULL;
}
newconn = boxc_create(newfd, ip, ssl);
-
+
/*
* check if the SSL handshake was successfull, otherwise
* this is no valid box connection any more
@@ -430,7 +472,7 @@
info(0, "Client connected from <%s> using SSL", octstr_get_cstr(ip));
else
info(0, "Client connected from <%s>", octstr_get_cstr(ip));
-
+
/* XXX TODO: do the hand-shake, baby, yeah-yeah! */
@@ -444,7 +486,7 @@
int fd;
Boxc *newconn;
long sender;
-
+
list_add_producer(flow_threads);
fd = (int)arg;
newconn = accept_boxc(fd, smsbox_port_ssl);
@@ -452,11 +494,10 @@
list_remove_producer(flow_threads);
return;
}
- newconn->incoming = incoming_sms;
+ newconn->incoming = list_create();
+ list_add_producer(newconn->incoming);
newconn->retry = incoming_sms;
newconn->outgoing = outgoing_sms;
-
- list_append(smsbox_list, newconn);
sender = gwthread_create(boxc_sender, newconn);
if (sender == -1) {
@@ -464,26 +505,50 @@
octstr_get_cstr(newconn->client_ip));
goto cleanup;
}
+ /*
+ * We register newconn in the smsbox_list here but mark newconn as routable
+ * after identification or first message received from smsbox. So we can avoid
+ * a race condition for routable smsboxes (otherwise between startup and
+ * registration we will forward some messages to smsbox).
+ */
+ list_lock(smsbox_list);
+ list_append(smsbox_list, newconn);
+ list_unlock(smsbox_list);
+
list_add_producer(newconn->outgoing);
boxc_receiver(newconn);
list_remove_producer(newconn->outgoing);
- /* remove producer only if that not a global incoming list */
- if (incoming_sms != newconn->incoming)
+ /* remove us from smsbox routing list */
+ list_lock(smsbox_list);
+ list_delete_equal(smsbox_list, newconn);
+ list_unlock(smsbox_list);
+ if (newconn->boxc_id) {
+ dict_remove(smsbox_by_id, newconn->boxc_id);
+ }
+
+ /*
+ * check if we in the shutdown phase and sms dequeueing thread
+ * has removed the producer already
+ */
+ if (list_producer_count(newconn->incoming) > 0)
list_remove_producer(newconn->incoming);
+
gwthread_join(sender);
-cleanup:
- if (newconn->boxc_id) {
- dict_remove(smsbox_by_id, newconn->boxc_id);
- while (list_producer_count(newconn->incoming) > 0)
- list_remove_producer(newconn->incoming);
- gw_assert(list_len(newconn->incoming) == 0);
- list_destroy(newconn->incoming, NULL);
+cleanup:
+ {
+ long i;
+ for (i=0; i < list_len(newconn->incoming); i++)
+ msg_dump(list_get(newconn->incoming, i), 0);
}
- list_delete_equal(smsbox_list, newconn);
+ gw_assert(list_len(newconn->incoming) == 0);
+ list_destroy(newconn->incoming, NULL);
boxc_destroy(newconn);
+ /* wakeup the dequeueing thread */
+ gwthread_wakeup(sms_dequeue_thread);
+
list_remove_producer(flow_threads);
}
@@ -709,39 +774,37 @@
}
-
-
-
-
static void wait_for_connections(int fd, void (*function) (void *arg),
List *waited)
{
- fd_set rf;
- struct timeval tv;
int ret;
+ int timeout = 10; /* 10 sec. */
+
+ gw_assert(function != NULL);
while(bb_status != BB_DEAD) {
- /* XXX: if we are being shutdowned, as long as there is
+ /* if we are being shutdowned, as long as there is
* messages in incoming list allow new connections, but when
- * list is empty, exit
+ * list is empty, exit.
+ * Note: We have timeout (defined above) for which we allow new connections.
+ * Otherwise we wait here for ever!
*/
if (bb_status == BB_SHUTDOWN) {
ret = list_wait_until_nonempty(waited);
- if (ret == -1) break;
+ if (ret == -1 || !timeout)
+ break;
+ else
+ timeout--;
}
- FD_ZERO(&rf);
- tv.tv_sec = 1;
- tv.tv_usec = 0;
-
- if (bb_status != BB_SUSPENDED)
- FD_SET(fd, &rf);
+ /* block here if suspended */
+ list_consume(suspended);
- ret = select(FD_SETSIZE, &rf, NULL, NULL, &tv);
+ ret = gwthread_pollfd(fd, POLLIN, 1.0);
if (ret > 0) {
gwthread_create(function, (void *)fd);
- sleep(1);
+ gwthread_sleep(1.0);
} else if (ret < 0) {
if(errno==EINTR) continue;
if(errno==EAGAIN) continue;
@@ -776,6 +839,8 @@
wait_for_connections(fd, run_smsbox, incoming_sms);
+ list_remove_producer(smsbox_list);
+
/* continue avalanche */
list_remove_producer(outgoing_sms);
@@ -783,13 +848,15 @@
* is completely over
*/
- /* XXX KLUDGE fix when list_wait_until_empty() exists */
- while(list_wait_until_nonempty(smsbox_list)!= -1)
- sleep(1);
+ while(list_wait_until_nonempty(smsbox_list) == 1)
+ gwthread_sleep(1.0);
/* close listen socket */
close(fd);
+ gwthread_wakeup(sms_dequeue_thread);
+ gwthread_join(sms_dequeue_thread);
+
list_destroy(smsbox_list, NULL);
smsbox_list = NULL;
@@ -830,9 +897,8 @@
/* wait for all connections to die and then remove list
*/
- /* XXX KLUDGE fix when list_wait_until_empty() exists */
- while(list_wait_until_nonempty(wapbox_list)== 1)
- sleep(1);
+ while(list_wait_until_nonempty(wapbox_list) == 1)
+ gwthread_sleep(1.0);
/* wait for wdp_to_wapboxes to exit */
while(list_consume(wapbox_list)!=NULL)
@@ -892,8 +958,9 @@
dict_put(smsbox_by_smsc, item, boxc_id);
}
list_destroy(items, octstr_destroy_item);
+ octstr_destroy(smsc_ids);
}
-
+
if (shortcuts) {
items = octstr_split(shortcuts, octstr_imm(";"));
for (i = 0; i < list_len(items); i++) {
@@ -906,8 +973,11 @@
dict_put(smsbox_by_receiver, item, boxc_id);
}
list_destroy(items, octstr_destroy_item);
+ octstr_destroy(shortcuts);
}
- }
+ }
+
+ list_destroy(list, NULL);
}
@@ -948,9 +1018,13 @@
init_smsbox_routes(cfg);
list_add_producer(outgoing_sms);
+ list_add_producer(smsbox_list);
smsbox_running = 1;
+ if ((sms_dequeue_thread = gwthread_create(sms_to_smsboxes, NULL)) == -1)
+ panic(0, "Failed to start a new thread for smsbox routing");
+
if (gwthread_create(smsboxc_run, (void *)smsbox_port) == -1)
panic(0, "Failed to start a new thread for smsbox connections");
@@ -1094,10 +1168,12 @@
if (status_type == BBSTATUS_XML)
octstr_format_append(tmp, "<box>\n\t\t<type>smsbox</type>\n"
"\t\t<id>%s</id>\n\t\t<IP>%s</IP>\n"
+ "\t\t<queue>%ld</queue>\n"
"\t\t<status>on-line %ldd %ldh %ldm %lds</status>\n"
"\t\t<ssl>%s</ssl>\n\t</box>",
(bi->boxc_id ? octstr_get_cstr(bi->boxc_id) : ""),
octstr_get_cstr(bi->client_ip),
+ list_len(bi->incoming),
t/3600/24, t/3600%24, t/60%60, t%60,
#ifdef HAVE_LIBSSL
conn_get_ssl(bi->conn) != NULL ? "yes" : "no"
@@ -1106,9 +1182,9 @@
#endif
);
else
- octstr_format_append(tmp, "%ssmsbox:%s, IP %s (on-line %ldd %ldh %ldm %lds) %s %s",
+ octstr_format_append(tmp, "%ssmsbox:%s, IP %s (%ld queued), (on-line %ldd %ldh %ldm %lds) %s %s",
ws, (bi->boxc_id ? octstr_get_cstr(bi->boxc_id) : "(none)"),
- octstr_get_cstr(bi->client_ip),
+ octstr_get_cstr(bi->client_ip), list_len(bi->incoming),
t/3600/24, t/3600%24, t/60%60, t%60,
#ifdef HAVE_LIBSSL
conn_get_ssl(bi->conn) != NULL ? "using SSL" : "",
@@ -1169,10 +1245,11 @@
* optimized, because every single MO message passes this function and we
* have to ensure that no unncessary overhead is done.
*/
-void route_incoming_to_boxc(Msg *msg)
+int route_incoming_to_boxc(Msg *msg)
{
- Boxc *bc = NULL;
+ Boxc *bc = NULL, *best = NULL;
Octstr *s, *r;
+ long len, b, i;
s = r = NULL;
gw_assert(msg_type(msg) == sms);
@@ -1186,14 +1263,20 @@
if (msg->sms.boxc_id != NULL) {
bc = dict_get(smsbox_by_id, msg->sms.boxc_id);
- if (bc == 0) {
- /*
- * something is wrong, this was the smsbox connection we used
+ if (bc == NULL) {
+ /*
+ * something is wrong, this was the smsbox connection we used
* for sending, so it seems this smsbox is gone
*/
- error(0,"Could not route message to smsbox id <%s>, smsbox is gone!",
+ warning(0,"Could not route message to smsbox id <%s>, smsbox is gone!",
octstr_get_cstr(msg->sms.boxc_id));
- }
+ /*
+ * put msg into global incoming queue and wait until smsbox
+ * with this boxc_id connected agin
+ */
+ list_produce(incoming_sms, msg);
+ return -1;
+ }
}
/*
@@ -1206,14 +1289,109 @@
bc = r ? dict_get(smsbox_by_id, r) : (s ? dict_get(smsbox_by_id, s) : NULL);
}
- /*
+ /* check if we found our routing */
+ if (bc != NULL) {
+ list_produce(bc->incoming, msg);
+ return 0; /* we are done */
+ }
+ else if (s != NULL || r != NULL) {
+ /*
+ * we have routing defined, but no smsbox connected at the moment.
+ * put msg into global incoming queue and wait until smsbox with
+ * such boxc_id connected.
+ */
+ list_produce(incoming_sms, msg);
+ return -1;
+ }
+
+
+ /*
* ok, none of the routing things applied previously, so route it to
- * a random smsbox via the shared incoming_sms queue, otherwise to the
- * smsc specific incoming queue
+ * a random smsbox.
*/
- if (bc == NULL)
+ list_lock(smsbox_list);
+
+ /* take random smsbox from list, and then check all smsboxes
+ * and select the one with lowest load level - if tied, the first
+ * one
+ */
+ len = list_len(smsbox_list);
+ b = gw_rand() % len;
+ best = list_get(smsbox_list, b);
+ best = (best->boxc_id == NULL ? best : NULL);
+
+ for(i = 0; i < list_len(smsbox_list); i++) {
+ bc = list_get(smsbox_list, (i+b) % len);
+ if (bc->boxc_id != NULL || bc->routable == 0)
+ bc = NULL;
+ if ((bc != NULL && best != NULL && bc->load < best->load) ||
+ (bc != NULL && best == NULL)) {
+ best = bc;
+ }
+ }
+
+ if (best != NULL) {
+ best->load++;
+ list_produce(best->incoming, msg);
+ }
+
+ list_unlock(smsbox_list);
+
+ if (best == NULL) {
+ warning(0, "smsbox_list empty!");
list_produce(incoming_sms, msg);
- else
- list_produce(bc->incoming, msg);
+ return -1;
+ }
+
+ return 0;
}
+static void sms_to_smsboxes(void *arg)
+{
+ Msg *newmsg, *startmsg, *msg;
+ long i, len;
+ int ret = -1;
+ Boxc *boxc;
+
+ list_add_producer(flow_threads);
+
+ newmsg = startmsg = msg = NULL;
+
+ while(bb_status != BB_DEAD) {
+
+ if (newmsg == startmsg) {
+ /* check if we are in shutdown phase */
+ if (list_producer_count(smsbox_list) == 0)
+ break;
+
+ if (ret == -1)
+ gwthread_sleep(-1.0);
+
+ startmsg = list_consume(incoming_sms);
+ msg = startmsg;
+ newmsg = NULL;
+ }
+ else {
+ newmsg = list_consume(incoming_sms);
+ msg = newmsg;
+ }
+
+ if (msg == NULL)
+ break;
+
+ gw_assert(msg_type(msg) == sms);
+
+ if ((ret = route_incoming_to_boxc(msg)) == 0)
+ startmsg = newmsg = NULL;
+ }
+
+ list_lock(smsbox_list);
+ len = list_len(smsbox_list);
+ for (i=0; i < len; i++) {
+ boxc = list_get(smsbox_list, i);
+ list_remove_producer(boxc->incoming);
+ }
+ list_unlock(smsbox_list);
+
+ list_remove_producer(flow_threads);
+}