浏览代码

performance and bug fixes

Jiri Kuthan 24 年之前
父节点
当前提交
dda9dab1f8
共有 52 个文件被更改,包括 2495 次插入1261 次删除
  1. 1 1
      Makefile
  2. 11 3
      Makefile.defs
  3. 1 1
      Makefile.sources
  4. 1 1
      action.c
  5. 20 4
      config.h
  6. 4 1
      crc.c
  7. 11 0
      crc.h
  8. 1 1
      data_lump.c
  9. 6 21
      forward.c
  10. 17 24
      main.c
  11. 32 7
      md5utils.c
  12. 0 56
      mem.h
  13. 51 0
      mem/mem.c
  14. 68 0
      mem/mem.h
  15. 133 0
      mem/memtest.c
  16. 22 13
      mem/q_malloc.c
  17. 1 1
      mem/q_malloc.h
  18. 13 3
      mem/shm_mem.c
  19. 20 9
      mem/shm_mem.h
  20. 438 0
      mem/vq_malloc.c
  21. 130 0
      mem/vq_malloc.h
  22. 2 15
      modules/tm/TODO
  23. 36 0
      modules/tm/config.h
  24. 0 12
      modules/tm/globals.h
  25. 36 50
      modules/tm/h_table.c
  26. 23 30
      modules/tm/h_table.h
  27. 88 1
      modules/tm/hash_func.c
  28. 4 2
      modules/tm/hash_func.h
  29. 9 2
      modules/tm/lock.c
  30. 2 2
      modules/tm/lock.h
  31. 2 2
      modules/tm/sh_malloc.h
  32. 1 18
      modules/tm/sip_msg.c
  33. 458 670
      modules/tm/t_funcs.c
  34. 41 6
      modules/tm/t_funcs.h
  35. 380 0
      modules/tm/t_lookup.c
  36. 138 158
      modules/tm/timer.c
  37. 37 35
      modules/tm/timer.h
  38. 12 9
      modules/tm/tm.c
  39. 10 6
      msg_parser.c
  40. 1 1
      msg_parser.h
  41. 5 3
      msg_translator.c
  42. 1 1
      parse_fline.c
  43. 1 1
      parse_via.c
  44. 8 21
      receive.c
  45. 52 38
      stats.h
  46. 2 2
      test/tp.cfg
  47. 27 24
      test/tx.cfg
  48. 53 0
      test/xx.cfg
  49. 51 0
      test/xy.cfg
  50. 2 1
      timer.c
  51. 13 5
      udp_server.c
  52. 19 0
      ut.h

+ 1 - 1
Makefile

@@ -11,7 +11,7 @@ auto_gen=lex.yy.c cfg.tab.c   #lexx, yacc etc
 include Makefile.sources
 
 exclude_modules=CVS usrloc mysql auth
-static_modules=
+static_modules=tm
 static_modules_path=$(addprefix modules/, $(static_modules))
 extra_sources=$(wildcard $(addsuffix /*.c, $(static_modules_path)))
 extra_objs=$(extra_sources:.c=.o)

+ 11 - 3
Makefile.defs

@@ -44,8 +44,16 @@ ARCH = $(shell uname -s)
 #		extra error checking (trying to free the same pointer
 #		twice, trying to free a pointer alloc'ed with a different
 #		malloc etc.)
+# -DVQ_MALLOC
+#		additional option to PKG_MALLOC which utilizes a fater then
+#		qm version
+#
+
 DEFS+= -DNAME='"$(NAME)"' -DVERSION='"$(RELEASE)"' -DARCH='"$(ARCH)"' \
 	 -DDNS_IP_HACK  -DPKG_MALLOC -DSHM_MEM  -DSHM_MMAP \
+	-DEXTRA_DEBUG \
+	-DVQ_MALLOC 
+	  #-DDBG_QM_MALLOC #-DVQ_MALLOC #-DNO_DEBUG
 	  #-DNO_DEBUG #-DDBG_QM_MALLOC
 #-DEXTRA_DEBUG
 # -DUSE_SHM_MEM
@@ -54,9 +62,9 @@ DEFS+= -DNAME='"$(NAME)"' -DVERSION='"$(RELEASE)"' -DARCH='"$(ARCH)"' \
 #-DNO_DEBUG#-DSTATS -DNO_DEBUG 
 #-DNO_LOG
 
-PROFILE=  #-pg #set this if you want profiling
-#mode = debug
-mode = release
+PROFILE=  -pg #set this if you want profiling
+mode = debug
+#mode = release
 
 # platform dependent settings
 

+ 1 - 1
Makefile.sources

@@ -11,7 +11,7 @@
 # defines: sources, objs, depends
 #
 
-sources=$(filter-out $(auto_gen), $(wildcard *.c)) $(auto_gen)
+sources=$(filter-out $(auto_gen), $(wildcard *.c) $(wildcard mem/*.c) ) $(auto_gen)
 objs=$(sources:.c=.o)
 extra_objs=
 depends=$(sources:.c=.d)

+ 1 - 1
action.c

@@ -15,7 +15,7 @@
 #include "msg_parser.h"
 #include "ut.h"
 #include "sr_module.h"
-#include "mem.h"
+#include "mem/mem.h"
 
 #include <sys/types.h>
 #include <sys/socket.h>

+ 20 - 4
config.h

@@ -1,5 +1,5 @@
 /*
- *  $Id
+ *  $Id $
  */
 
 
@@ -12,8 +12,6 @@
 
 #define CFG_FILE "./ser.cfg"
 
-/* receive buffer size */
-#define BUF_SIZE 65507
 
 /* maximum number of addresses on which we will listen */
 #define MAX_LISTEN 16
@@ -47,11 +45,29 @@
 /*used only if PKG_MALLOC is defined*/
 #define PKG_MEM_POOL_SIZE 1024*1024
 
-/*used is SH_MEM is defined*/
+/*used if SH_MEM is defined*/
 #define SHM_MEM_SIZE 128*1024*1024
 
 #define TIMER_TICK 1
 #define LONG_SLEEP	3600
 
+/* dimensioning buckets in q_malloc */
+/* size of the size2bucket table; everything beyond that asks for
+   a variable-size kilo-bucket
+ */
+#define MAX_FIXED_BLOCK         3072
+/* distance of kilo-buckets */
+#define BLOCK_STEP                      512
+/* maximum number of possible buckets */
+#define MAX_BUCKET		15
+
+/* receive buffer size -- preferably set low to
+   avoid terror of excessively huge messages
+*/
+#define BUF_SIZE (MAX_FIXED_BLOCK-32)
+
+/* forwarding */
+#define MAX_VIA_LINE_SIZE	240
+#define MAX_RECEIVED_SIZE	57
 
 #endif

+ 4 - 1
crc.c

@@ -1,8 +1,11 @@
 /*
+ * $Id$
+ *
  *  Crc - 32 + 16 BIT ANSI X3.66 + CCITT CRC checksum files
  */
 
 #include <stdio.h>
+#include "crc.h"
 
 #define OK 0
 #define ERROR (-1)
@@ -60,7 +63,7 @@
 /*     hardware you could probably optimize the shift in assembler by  */
 /*     using byte-swap instructions.                                   */
 
-static unsigned long int crc_32_tab[] = { /* CRC polynomial 0xedb88320 */
+unsigned long int crc_32_tab[] = { /* CRC polynomial 0xedb88320 */
 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,

+ 11 - 0
crc.h

@@ -0,0 +1,11 @@
+/* $Id$*/
+
+#ifndef _CRC_H_
+#define _CRC_H_
+
+extern unsigned long int crc_32_tab[];
+extern unsigned short int ccitt_tab[];
+extern unsigned short int crc_16_tab[];
+
+#endif
+

+ 1 - 1
data_lump.c

@@ -4,7 +4,7 @@
 
 #include "data_lump.h"
 #include "dprint.h"
-#include "mem.h"
+#include "mem/mem.h"
 
 #include <stdlib.h>
 

+ 6 - 21
forward.c

@@ -21,21 +21,15 @@
 #include "globals.h"
 #include "data_lump.h"
 #include "ut.h"
-#include "mem.h"
+#include "mem/mem.h"
 #include "msg_translator.h"
 #include "sr_module.h"
+#include "stats.h"
 
 #ifdef DEBUG_DMALLOC
 #include <dmalloc.h>
 #endif
 
-#define MAX_VIA_LINE_SIZE      240
-#define MAX_RECEIVED_SIZE  57
-
-#ifdef STATS
-#include "stats.h"
-#endif
-
 
 
 int forward_request( struct sip_msg* msg, struct proxy_l * p)
@@ -79,15 +73,11 @@ int forward_request( struct sip_msg* msg, struct proxy_l * p)
 				sizeof(struct sockaddr_in))==-1){
 			p->errors++;
 			p->ok=0;
-#ifdef STATS
-			update_fail_on_send;
-#endif
+			STATS_TX_DROPS;
 			goto error;
 	}
-#ifdef STATS
 	/* sent requests stats */
-	else update_sent_request( msg->first_line.u.request.method_value );
-#endif
+	else STATS_TX_REQUEST(  msg->first_line.u.request.method_value );
 	free(buf);
 	free(to);
 	/* received_buf & line_buf will be freed in receiv_msg by free_lump_list*/
@@ -189,15 +179,10 @@ int forward_reply(struct sip_msg* msg)
 	if (udp_send(new_buf,new_len, (struct sockaddr*) to,
 					sizeof(struct sockaddr_in))==-1)
 	{
-#ifdef STATS
-		update_fail_on_send;
-#endif
+		STATS_TX_DROPS;
 		goto error;
 	}
-#ifdef STATS
-	else update_sent_response(  msg->first_line.u.reply.statusclass );
-#endif
-
+	else STATS_TX_RESPONSE(  msg->first_line.u.reply.statusclass );
 	free(new_buf);
 	free(to);
 skip:

+ 17 - 24
main.c

@@ -23,9 +23,9 @@
 #include "route.h"
 #include "udp_server.h"
 #include "globals.h"
-#include "mem.h"
+#include "mem/mem.h"
 #ifdef SHM_MEM
-#include "shm_mem.h"
+#include "mem/shm_mem.h"
 #endif
 #include "sr_module.h"
 #include "timer.h"
@@ -33,9 +33,7 @@
 
 #include <signal.h>
 
-#ifdef STATS
 #include "stats.h"
-#endif
 
 #ifdef DEBUG_DMALLOC
 #include <dmalloc.h>
@@ -72,6 +70,9 @@ static char flags[]=
 #ifdef PKG_MALLOC
 ", PKG_MALLOC"
 #endif
+#ifdef VQ_MALLOC
+", VQ_MALLOC"
+#endif
 #ifdef USE_SHM_MEM
 ", USE_SHM_MEM"
 #endif
@@ -171,12 +172,6 @@ int process_no = 0;
 /* cfg parsing */
 int cfg_errors=0;
 
-#ifdef PKG_MALLOC
-char mem_pool[PKG_MEM_POOL_SIZE];
-struct qm_block* mem_block;
-#endif
-
-
 #define MAX_FD 32 /* maximum number of inherited open file descriptors,
 		    (normally it shouldn't  be bigger  than 3) */
 
@@ -332,7 +327,7 @@ int main_loop()
 static void sig_usr(int signo)
 {
 	DPrint("INT received, program terminates\n");
-	if (signo==SIGINT) {	/* exit gracefuly */
+	if (signo==SIGINT || signo==SIGPIPE) {	/* exit gracefuly */
 #ifdef STATS
 		/* print statistics on exit only for the first process */
 
@@ -380,6 +375,7 @@ static void sig_usr(int signo)
 }
 
 
+void test();
 
 int main(int argc, char** argv)
 {
@@ -396,12 +392,20 @@ int main(int argc, char** argv)
 		DPrint("ERROR: no SIGINT signal handler can be installed\n");
 		goto error;
 	}
+	/* if we debug and write to a pipe, we want to exit nicely too */
+	if (signal(SIGPIPE, sig_usr) == SIG_ERR ) {
+		DPrint("ERROR: no SIGINT signal handler can be installed\n");
+		goto error;
+	}
 
 	if (signal(SIGUSR1, sig_usr)  == SIG_ERR ) {
 		DPrint("ERROR: no SIGUSR1 signal handler can be installed\n");
 		goto error;
 	}
 
+	//memtest();
+	//hashtest();
+
 
 	/* process command line (get port no, cfg. file path etc) */
 	opterr=0;
@@ -522,21 +526,9 @@ int main(int argc, char** argv)
 	}
 
 	/*init mallocs (before parsing cfg !)*/
-#ifdef PKG_MALLOC
-	/*init mem*/
-	mem_block=qm_malloc_init(mem_pool, PKG_MEM_POOL_SIZE);
-	if (mem_block==0){
-		LOG(L_CRIT, "could not initialize memory pool\n");
+	if (init_mallocs()==-1)
 		goto error;
-	}
-#endif
 
-#ifdef SHM_MEM
-	if (shm_mem_init()<0) {
-		LOG(L_CRIT, "could not initialize shared memory pool, exiting...\n");
-		goto error;
-	}
-#endif
 	/*init timer, before parsing the cfg!*/
 	if (init_timer()<0){
 		LOG(L_CRIT, "could not initialize timer, exiting...\n");
@@ -641,3 +633,4 @@ error:
 	return -1;
 
 }
+

+ 32 - 7
md5utils.c

@@ -28,8 +28,8 @@ jku: added support to deal with vectors
 #include "md5global.h"
 #include "md5.h"
 #include "md5utils.h"
-
 #include "dprint.h"
+#include "ut.h"
 
 
 static void MDString PROTO_LIST ((char *));
@@ -45,13 +45,38 @@ static void MDString PROTO_LIST ((char *));
  */
 void MDStringArray (char *dst, str src[], int size)
 {
-  MD_CTX context;
-  unsigned char digest[16];
-  int i;
+	MD_CTX context;
+	unsigned char digest[16];
+ 	int i;
+	int len;
+	char *s;
+
+/*
+#	ifdef EXTRA_DEBUG
+	int j;
+	int sum;
+#endif
+*/
 
-  MDInit (&context);
-  for (i=0; i<size; i++) {
-  	MDUpdate (&context, src[i].s, src[i].len);
+	MDInit (&context);
+	for (i=0; i<size; i++) {
+		trim_len( len, s, src[i] );
+/*
+#		ifdef EXTRA_DEBUG
+		fprintf(stderr, "EXTRA_DEBUG: %d. (%d) {", i+1, len);
+		sum=0;
+		for (j=0; j<len; j++) {
+			fprintf( stderr, "%c ", *(s+j));
+			sum+=*(s+j);
+		}
+		for (j=0; j<len; j++) {
+			fprintf( stderr, "%d ", *(s+j));
+			sum+=*(s+j);
+		}
+		fprintf(stderr, " [%d]\n", sum );	
+#		endif
+*/
+  		MDUpdate (&context, s, len);
   }
   MDFinal (digest, &context);
 

+ 0 - 56
mem.h

@@ -1,56 +0,0 @@
-/* $Id$
- *
- * memory related stuff (malloc & friends)
- * 
- */
-
-
-#ifndef mem_h
-#define mem_h
-#include "dprint.h"
-
-#ifdef PKG_MALLOC
-#include "q_malloc.h"
-
-extern struct qm_block* mem_block;
-
-
-#ifdef DBG_QM_MALLOC
-
-#define pkg_malloc(s) qm_malloc(mem_block, (s),__FILE__, __FUNCTION__, \
-								__LINE__);
-#define pkg_free(p)   qm_free(mem_block, (p), __FILE__,  __FUNCTION__, \
-								__LINE__);
-
-#else
-
-#define pkg_malloc(s) qm_malloc(mem_block, (s))
-#define pkg_free(p)   qm_free(mem_block, (p))
-
-#endif
-
-#define pkg_status()  qm_status(mem_block)
-
-#elif defined(SHM_MEM) && defined(USE_SHM_MEM)
-
-#include "shm_mem.h"
-
-#define pkg_malloc(s) shm_malloc((s))
-#define pkg_free(p)   shm_free((p))
-#define pkg_status()  shm_status()
-
-#else
-
-#include <stdlib.h>
-
-#define pkg_malloc(s) \
-	(  { void *v; v=malloc((s)); \
-	   DBG("malloc %x size %d end %x\n", v, s, (unsigned int)v+(s));\
-	   v; } )
-#define pkg_free(p)  do{ DBG("free %x\n", (p)); free((p)); }while(0);
-#define pkg_status()
-
-#endif
-
-
-#endif

+ 51 - 0
mem/mem.c

@@ -0,0 +1,51 @@
+/*
+ * $Id *
+ */
+
+#include "../config.h"
+#include "../dprint.h"
+#include "mem.h"
+
+#ifdef PKG_MALLOC
+#	ifdef VQ_MALLOC
+#		include "vq_malloc.h"
+#	else
+#		include "q_malloc.h"
+#	endif
+#endif
+
+#ifdef PKG_MALLOC
+	char mem_pool[PKG_MEM_POOL_SIZE];
+	#ifdef VQ_MALLOC
+		struct vqm_block* mem_block;
+	#else
+		struct qm_block* mem_block;
+	#endif
+#endif
+
+int init_mallocs()
+{
+#ifdef PKG_MALLOC
+        /*init mem*/
+	#ifdef VQ_MALLOC
+        	mem_block=vqm_malloc_init(mem_pool, PKG_MEM_POOL_SIZE);
+	#else
+        	mem_block=qm_malloc_init(mem_pool, PKG_MEM_POOL_SIZE);
+	#endif
+        if (mem_block==0){
+                LOG(L_CRIT, "could not initialize memory pool\n");
+		return -1;
+        }
+#endif
+
+#ifdef SHM_MEM
+        if (shm_mem_init()<0) {
+                LOG(L_CRIT, "could not initialize shared memory pool, exiting...\n");
+                return -1;
+        }
+#endif
+	return 0;
+
+}
+
+

+ 68 - 0
mem/mem.h

@@ -0,0 +1,68 @@
+/* $Id$
+ *
+ * memory related stuff (malloc & friends)
+ * 
+ */
+
+
+#ifndef mem_h
+#define mem_h
+#include "../config.h"
+#include "../dprint.h"
+
+#ifdef PKG_MALLOC
+#	ifdef VQ_MALLOC
+#		include "vq_malloc.h"
+		extern struct vqm_block* mem_block;
+#	else
+#		include "q_malloc.h"
+		extern struct qm_block* mem_block;
+#	endif
+
+	extern char mem_pool[PKG_MEM_POOL_SIZE];
+
+
+#	ifdef DBG_QM_MALLOC
+#		ifdef VQ_MALLOC
+#			define pkg_malloc(s) vqm_malloc(mem_block, (s),__FILE__, \
+				__FUNCTION__, __LINE__);
+#			define pkg_free(p)   vqm_free(mem_block, (p), __FILE__,  \
+				__FUNCTION__, __LINE__);
+#		else
+#			define pkg_malloc(s) qm_malloc(mem_block, (s),__FILE__, \
+				__FUNCTION__, __LINE__);
+#			define pkg_free(p)   qm_free(mem_block, (p), __FILE__,  \
+				__FUNCTION__, __LINE__);
+#		endif
+#	else
+#		ifdef VQ_MALLOC
+#			define pkg_malloc(s) vqm_malloc(mem_block, (s))
+#			define pkg_free(p)   vqm_free(mem_block, (p))
+#		else
+#			define pkg_malloc(s) qm_malloc(mem_block, (s))
+#			define pkg_free(p)   qm_free(mem_block, (p))
+#		endif
+#	endif
+#	ifdef VQ_MALLOC
+#		define pkg_status()  vqm_status(mem_block)
+#	else
+#		define pkg_status()  qm_status(mem_block)
+#	endif
+#elif defined(SHM_MEM) && defined(USE_SHM_MEM)
+#	include "shm_mem.h"
+#	define pkg_malloc(s) shm_malloc((s))
+#	define pkg_free(p)   shm_free((p))
+#	define pkg_status()  shm_status()
+#else
+#	include <stdlib.h>
+#	define pkg_malloc(s) \
+	(  { void *v; v=malloc((s)); \
+	   DBG("malloc %x size %d end %x\n", v, s, (unsigned int)v+(s));\
+	   v; } )
+#	define pkg_free(p)  do{ DBG("free %x\n", (p)); free((p)); }while(0);
+#	define pkg_status()
+#endif
+
+int init_mallocs();
+
+#endif

+ 133 - 0
mem/memtest.c

@@ -0,0 +1,133 @@
+#ifdef DBG_QM_MALLOC
+
+#include "../globals.h"
+#include "../config.h"
+
+#ifdef PKG_MALLOC
+#       ifdef VQ_MALLOC
+#               include "vq_malloc.h"
+#		define MY_MALLOC vqm_malloc
+#		define MY_FREE vqm_free
+#		define MY_INIT vqm_malloc_init
+#		define MY_BLOCK vqm_block
+#		define MY_STATUS vqm_status
+#       else
+#               include "q_malloc.h"
+#		define MY_MALLOC qm_malloc
+#		define MY_FREE qm_free
+#		define MY_INIT qm_malloc_init
+#		define MY_BLOCK qm_block
+#		define MY_STATUS qm_status
+#       endif
+#endif
+
+
+void memtest()
+{
+#define	TEST_SIZE 1024*1024
+#define	TEST_RUN 1024
+#define LONG_RUN 100000
+#define ma(s) MY_MALLOC(mem_block, (s),__FILE__, __FUNCTION__, \
+                                                                __LINE__);
+#define mf(p)   MY_FREE(mem_block, (p), __FILE__,  __FUNCTION__, \
+                                                                __LINE__);
+	char tst_mem[TEST_SIZE];
+	struct MY_BLOCK* mem_block;
+	char *p0,*p1,*p2,*p3,*p4,*p5,*p6,*p7,*p8,*p9;
+	int i, j, f;
+	char *p[TEST_RUN];
+	int t;
+
+	debug=7;
+	log_stderr=1;
+
+	printf("entering test\n");
+
+	mem_block=MY_INIT( tst_mem, TEST_SIZE );
+
+	/* coalesing test w/big fragments */
+	p0=ma(8194);
+	p1=ma(8194);
+	p2=ma(8194);
+	MY_STATUS(mem_block);
+	mf(p1);
+	mf(p0);
+	MY_STATUS(mem_block);
+	mf(p2);
+	MY_STATUS(mem_block);
+
+	/* reuse test w/big fragments */
+	p0=ma(8194);
+	p1=ma(4196);
+	mf(p0);
+	p0=ma(8190);
+	MY_STATUS(mem_block);
+	mf(p1);
+	mf(p0);
+	MY_STATUS(mem_block);
+
+
+	exit(0);
+
+	p0=ma(8);
+	p1=ma(24);
+	p2=ma(32);
+	p3=ma(32);
+	p4=ma(32);
+	p5=ma(1024);
+	p6=ma(2048);
+
+//	MY_STATUS(mem_block);
+
+//	*(p0+9)=0;
+	mf(p0);
+	mf(p2);
+	mf(p5);
+	mf(p6);
+	
+//	MY_STATUS(mem_block);
+
+	mf(p1);
+	mf(p4);
+	mf(p3);
+//	mf(p3);
+
+//	MY_STATUS(mem_block);
+
+	for (i=0;i<TEST_RUN;i++)
+		p[i]=ma( random() & 1023 );
+//	MY_STATUS(mem_block);
+	for (i=0;i<TEST_RUN;i++)
+		mf( p[i] );
+//	MY_STATUS(mem_block);
+
+	f = 0;
+#define GRANULARITY 100
+	for (j=0; j<LONG_RUN; j++) {
+		for (i=0;i<TEST_RUN;i++) {
+			t=random() & 1023;
+			if (! (t%24) ) t=(t+4096)*2;
+			p[i]=ma( random() & 1023 );
+		}
+		for (i=TEST_RUN/3;i<2*TEST_RUN/3;i++)
+			mf( p[i] );
+		for (i=TEST_RUN/3;i<2*TEST_RUN/3;i++) {
+			t=random() & 1023;
+			if (! (t%24) ) t=(t+4096)*2;
+			p[i]=ma( random() & 1023 );
+		}
+		for (i=0;i<TEST_RUN;i++)
+			mf( p[i] );
+		if ( GRANULARITY*j/LONG_RUN > f ) {
+			f=GRANULARITY*j/LONG_RUN ;
+			printf("%d%% done\n", f);
+		}
+	}
+	printf("now I'm really done\n");
+	MY_STATUS(mem_block);
+	printf("And I'm done with dumping final report too\n");
+	
+	exit(0);
+}
+
+#endif

+ 22 - 13
q_malloc.c → mem/q_malloc.c

@@ -2,29 +2,29 @@
  *
  */
 
+#if !defined(q_malloc) && !(defined VQ_MALLOC)
 #define q_malloc
-#ifdef q_malloc
 
 #include "q_malloc.h"
-#include "dprint.h"
+#include "../dprint.h"
 
 
-/*usefull macros*/
+/*useful macros*/
 #define FRAG_END(f)  \
-			((struct qm_frag_end*)((char*)(f)+sizeof(struct qm_frag)+ \
-								   (f)->size))
+	((struct qm_frag_end*)((char*)(f)+sizeof(struct qm_frag)+ \
+	   (f)->size))
 
 #define FRAG_NEXT(f) \
-			((struct qm_frag*)((char*)(f)+sizeof(struct qm_frag)+(f)->size+ \
-							   sizeof(struct qm_frag_end)))
+	((struct qm_frag*)((char*)(f)+sizeof(struct qm_frag)+(f)->size+ \
+	   sizeof(struct qm_frag_end)))
 			
 #define FRAG_PREV(f) \
-		( (struct qm_frag*) ( ((char*)(f)-sizeof(struct qm_frag_end))- \
-		((struct qm_frag_end*)((char*)(f)-sizeof(struct qm_frag_end)))->size- \
-			sizeof(struct qm_frag) ) )
+	( (struct qm_frag*) ( ((char*)(f)-sizeof(struct qm_frag_end))- \
+	((struct qm_frag_end*)((char*)(f)-sizeof(struct qm_frag_end)))->size- \
+	   sizeof(struct qm_frag) ) )
 
 #define PREV_FRAG_END(f) \
-		((struct qm_frag_end*)((char*)(f)-sizeof(struct qm_frag_end)))
+	((struct qm_frag_end*)((char*)(f)-sizeof(struct qm_frag_end)))
 
 #ifdef DBG_QM_MALLOC
 #define ST_CHECK_PATTERN   0xf0f0f0f0
@@ -162,6 +162,9 @@ void* qm_malloc(struct qm_block* qm, unsigned int size)
 	unsigned int overhead;
 	
 #ifdef DBG_QM_MALLOC
+	unsigned int list_cntr;
+
+	list_cntr = 0;
 	DBG("qm_malloc(%x, %d) called from %s: %s(%d)\n", qm, size, file, func,
 			line);
 #endif
@@ -171,6 +174,10 @@ void* qm_malloc(struct qm_block* qm, unsigned int size)
 	if (qm->free_lst.u.nxt_free==&(qm->free_lst)) return 0;
 	/*search for a suitable free frag*/
 	for (f=qm->free_lst.u.nxt_free; f!=&(qm->free_lst); f=f->u.nxt_free){
+#ifdef DBG_QM_MALLOC
+		list_cntr++;
+#endif
+		
 		if (f->size>=size){
 			/* we found it!*/
 			/*detach it from the free list*/
@@ -220,8 +227,8 @@ void* qm_malloc(struct qm_block* qm, unsigned int size)
 			f->check=ST_CHECK_PATTERN;
 		/*  FRAG_END(f)->check1=END_CHECK_PATTERN1;
 			FRAG_END(f)->check2=END_CHECK_PATTERN2;*/
-	DBG("qm_malloc(%x, %d) returns address %x\n", qm, size,
-			(char*)f+sizeof(struct qm_frag) );
+	DBG("qm_malloc(%x, %d) returns address %x on %d -th hit\n", qm, size,
+			(char*)f+sizeof(struct qm_frag), list_cntr );
 #endif
 			return (char*)f+sizeof(struct qm_frag);
 		}
@@ -318,6 +325,8 @@ void qm_status(struct qm_block* qm)
 	int i;
 
 	LOG(L_INFO, "qm_status (%x):\n", qm);
+	if (!qm) return;
+
 	LOG(L_INFO, " heap size= %d\n", qm->size);
 	LOG(L_INFO, " used= %d, used+overhead=%d, free=%d\n",
 			qm->used, qm->real_used, qm->size-qm->real_used);

+ 1 - 1
q_malloc.h → mem/q_malloc.h

@@ -3,7 +3,7 @@
  * simple & fast malloc library
  */
 
-#ifndef q_malloc_h
+#if !defined(q_malloc_h) && !defined(VQ_MALLOC)
 #define q_malloc_h
 
 

+ 13 - 3
shm_mem.c → mem/shm_mem.c

@@ -6,7 +6,7 @@
 #ifdef SHM_MEM
 
 #include "shm_mem.h"
-#include "config.h"
+#include "../config.h"
 
 #ifdef  SHM_MMAP
 
@@ -37,9 +37,15 @@
 #ifndef SHM_MMAP
 static int shm_shmid=-1; /*shared memory id*/
 #endif
+
+
 int shm_semid=-1; /*semaphore id*/
 static void* shm_mempool=(void*)-1;
-struct qm_block* shm_block;
+#ifdef VQ_MALLOC
+	struct vqm_block* shm_block;
+#else
+	struct qm_block* shm_block;
+#endif
 
 
 
@@ -110,7 +116,11 @@ int shm_mem_init()
 		return -1;
 	}
 	/* init it for malloc*/
-	shm_block=qm_malloc_init(shm_mempool, SHM_MEM_SIZE);
+#	ifdef VQ_MALLOC
+		shm_block=vqm_malloc_init(shm_mempool, SHM_MEM_SIZE);
+#	else
+		shm_block=qm_malloc_init(shm_mempool, SHM_MEM_SIZE);
+#	endif
 	if (shm_block==0){
 		LOG(L_CRIT, "ERROR: shm_mem_init: could not initialize shared"
 				" malloc\n");

+ 20 - 9
shm_mem.h → mem/shm_mem.h

@@ -25,10 +25,21 @@
 
 
 
-#include "q_malloc.h"
-#include "dprint.h"
-
-extern struct qm_block* shm_block;
+#include "../dprint.h"
+
+#ifdef VQ_MALLOC
+#	include "vq_malloc.h"
+	extern struct vqm_block* shm_block;
+#	define MY_MALLOC vqm_malloc
+#	define MY_FREE vqm_free
+#	define MY_STATUS vqm_status
+#else
+#	include "q_malloc.h"
+	extern struct qm_block* shm_block;
+#	define MY_MALLOC qm_malloc
+#	define MY_FREE qm_free
+#	define MY_STATUS qm_status
+#endif
 extern int shm_semid;
 
 int shm_mem_init();
@@ -94,7 +105,7 @@ again:
 	\
 	/*if (shm_lock()==0){*/\
 		shm_lock();\
-		p=qm_malloc(shm_block, (size), __FILE__, __FUNCTION__, __LINE__);\
+		p=MY_MALLOC(shm_block, (size), __FILE__, __FUNCTION__, __LINE__);\
 		shm_unlock();\
 	/* \
 	}else{ \
@@ -108,7 +119,7 @@ again:
 #define shm_free(p) \
 do { \
 		shm_lock(); \
-		qm_free(shm_block, (p), __FILE__, __FUNCTION__, __LINE__); \
+		MY_FREE(shm_block, (p), __FILE__, __FUNCTION__, __LINE__); \
 		shm_unlock(); \
 }while(0)
 
@@ -122,7 +133,7 @@ do { \
 	\
 	/*if (shm_lock()==0){*/\
 		shm_lock();\
-		p=qm_malloc(shm_block, (size));\
+		p=MY_MALLOC(shm_block, (size));\
 		shm_unlock();\
 	/* \
 	}else{ \
@@ -136,7 +147,7 @@ do { \
 #define shm_free(p) \
 do { \
 		shm_lock(); \
-		qm_free(shm_block, (p)); \
+		MY_FREE(shm_block, (p)); \
 		shm_unlock(); \
 }while(0)
 
@@ -147,7 +158,7 @@ do { \
 #define shm_status() \
 do { \
 		shm_lock(); \
-		qm_status(shm_block); \
+		MY_STATUS(shm_block); \
 		shm_unlock(); \
 }while(0)
 

+ 438 - 0
mem/vq_malloc.c

@@ -0,0 +1,438 @@
+/* $Id$
+ *
+ * History: 
+ * merged from Andrei's qmalloc and many fragments from Regents 
+ * University of California NetBSD malloc used; see
+ * http://www.ajk.tele.fi/libc/stdlib/malloc.c.html#malloc for more
+ * details including redistribution policy; this policy asks for
+ * displaying the copyright:
+ * Copyright (c) 1983 Regents of the University of California.
+ * All rights reserved.
+ *
+ *
+ * About:
+ * aggressive, wasteful and very quick malloc library built for
+ * servers that continuously alocate and release chunks of only
+ * few sizes:
+ * - free lists are organized by size (which eliminates long list traversal 
+ *   thru short free chunks if a long one is asked)
+ * - quite a few sizes are supported --> this results in more waste
+ *    (unused place in a chunk) however memory can be well reused after
+ *    freeing
+ * - the last bucket holds unlikely, huge, variable length chunks;
+ *   they are maintained as stack growing from the opposite direction
+ *   of our heap; coalesing is enabled; stack-like first-fit used to
+ *   find free fragments
+ *
+ * TODO: possibly, some delayed coalescation wouldn't hurt; also, further
+ * optimization will certainly speed-up the entire process a lot; using
+ * knowledge of application as well as trying to make pipeline happy
+ * (from which I am sadly far away now)
+ * provides optimization space; trying to use other memory allocators
+ * to compare would be a great thing to do too;
+ *
+ * also, comparing to other memory allocaters (like Horde) would not
+ * be a bad idea: those folks have been doing that for ages and specifically
+ * Horde has been heavily optimized for multi-processor machines
+ *
+ * References:
+ *   - list of malloc implementations: http://www.cs.colorado.edu/~zorn/Malloc.html
+ *   - a white-paper: http://g.oswego.edu/dl/html/malloc.html
+ *   - Paul R. Wilson, Mark S. Johnstone, Michael Neely, and David Boles: 
+       ``Dynamic Storage Allocation: A Survey and Critical Review'' in International 
+       Workshop on Memory Management, September 1995, 
+       ftp://ftp.cs.utexas.edu/pub/garbage/allocsrv.ps
+ *   - ptmalloc: http://www.malloc.de/en/
+ *   - GNU C-lib malloc: http://www.gnu.org/manual/glibc-2.0.6/html_chapter/libc_3.html
+ *   - delorie malocs: http://www.delorie.com/djgpp/malloc/
+ */
+
+#ifdef VQ_MALLOC
+
+#include "../config.h"
+#include "../globals.h"
+#include "vq_malloc.h"
+#include "../dprint.h"
+
+#define BIG_BUCKET(_qm) ((_qm)->max_small_bucket+1)
+#define IS_BIGBUCKET(_qm, _bucket) ((_bucket)==BIG_BUCKET(_qm)) 
+
+#ifdef DBG_QM_MALLOC
+#define ASSERT(a)	\
+	my_assert(a, __LINE__, __FILE__, __FUNCTION__ )
+#else
+#define ASSERT(a)
+#endif
+
+#ifdef DBG_QM_MALLOC
+#	define MORE_CORE(_q,_b,_s) (more_core( (_q), (_b), (_s), file, func, line ))
+#else
+#	define MORE_CORE(_q,_b,_s) (more_core( (_q), (_b), (_s) ))
+#endif
+
+
+
+/* dimensioning buckets: define the step function constants for size2bucket */
+int s2b_step[] = {8, 16, 32, 64, 128, 256, 512, 1024, 1536, 2048, 2560, MAX_FIXED_BLOCK, EO_STEP };
+
+void my_assert( int assertation, int line, char *file, char *function )
+{
+	if (assertation) return;
+
+	LOG(L_CRIT,"CRIT: assertation failed in $s (%s:%d)\n",
+		function, file, line);
+	abort();
+}
+
+#ifdef DBG_QM_MALLOC
+static  void vqm_debug_frag(struct vqm_block* qm, struct vqm_frag* f)
+{
+
+	int r;
+
+	if (f->check!=ST_CHECK_PATTERN){
+		LOG(L_CRIT, "BUG: vqm_*: fragm. %x beginning overwritten(%x)!\n",
+				f, f->check);
+		vqm_status(qm);
+		abort();
+	};
+	if (memcmp(f->end_check, END_CHECK_PATTERN, END_CHECK_PATTERN_LEN)!=0) {
+		LOG(L_CRIT, "BUG: vqm_*: fragm. %x end overwritten(%*s)!\n",
+				f, END_CHECK_PATTERN_LEN, f->end_check );
+		vqm_status(qm);
+		abort();
+	}
+}
+#endif
+
+
+/* takes  demanded size without overhead as input, returns bucket number
+   and changed the demanded size to size really used including all
+   possible overhead
+ */
+unsigned char size2bucket( struct vqm_block* qm, int *size )
+{
+	unsigned char b;	
+	unsigned int real_size;
+	unsigned int exceeds;
+
+
+	real_size = *size+ sizeof(struct vqm_frag)+
+			sizeof(struct vqm_frag_end);
+#ifdef DBG_QM_MALLOC
+	real_size+=END_CHECK_PATTERN_LEN;
+#endif
+	real_size+=((exceeds = (real_size % 8 )) ? 8 - exceeds : 0);
+	ASSERT( !(real_size%8) );
+	/* "small" chunk sizes <=1k translated using a table */
+	if ( real_size < MAX_FIXED_BLOCK ) {
+		b = qm->s2b[ real_size ];
+		*size = qm->s2s[ real_size ];
+	/* there might be various allocations slightly 1>1k, I still
+	   don't want to be too agressive and increase useless 
+	   allocations in small steps
+    	*/	
+	} else {
+		b = BIG_BUCKET(qm); 
+		*size = MAX_FIXED_BLOCK + 
+			(real_size-MAX_FIXED_BLOCK+BLOCK_STEP) 
+			/ BLOCK_STEP * BLOCK_STEP;
+	}
+	/*size must be a multiple of 8*/
+	ASSERT( !(*size%8) );
+	return b;
+}
+
+
+/* init malloc and return a qm_block */
+struct vqm_block* vqm_malloc_init(char* address, unsigned int size)
+{
+	char* start;
+	struct vqm_block* qm;
+	unsigned int init_overhead;
+	unsigned char b;	/* bucket iterator */
+	unsigned int s;		/* size iterator */
+	char *end;
+	
+	/* make address and size multiple of 8*/
+	start=(char*)( ((unsigned int)address%8)?((unsigned int)address+8)/8*8:
+			(unsigned int)address);
+	if (size<start-address) return 0;
+	size-=(start-address);
+	if (size <8) return 0;
+	size=(size%8)?(size-8)/8*8:size;
+
+	init_overhead=sizeof(struct vqm_block);
+	if (size < init_overhead)
+	{
+		/* not enough mem to create our control structures !!!*/
+		return 0;
+	}
+	end = start + size;
+	qm=(struct vqm_block*)start;
+	memset(qm, 0, sizeof(struct vqm_block));
+	size-=init_overhead;
+
+	/* definition of the size2bucket table */
+	for (s=0, b=0; s<MAX_FIXED_BLOCK ; s++) {
+		while (s>s2b_step[b]) b++;
+		if (b>MAX_BUCKET) {
+			LOG(L_CRIT, "CRIT: vqm_malloc_init: attempt to install too many buckets,"
+				"s2b_step > MAX_BUCKET\n");
+			return 0;
+		}
+		qm->s2b[s] = b;
+		qm->s2s[s] = s2b_step[b];
+	}
+	qm->max_small_bucket = b;
+
+	/* from where we will draw memory */
+	qm->core = (char *) ( start + sizeof( struct vqm_block ) );
+	qm->free_core = size;
+	/* remember for bound checking */
+	qm->init_core = qm->core;
+	qm->core_end = end;
+	/* allocate big chunks from end */
+	qm->big_chunks = end;
+	
+	return qm;
+}
+
+
+
+struct vqm_frag *more_core(	struct vqm_block* qm, 
+				unsigned char bucket, unsigned int size
+#ifdef DBG_QM_MALLOC
+				, char *file, char *func, unsigned int line
+#endif
+			 ) 
+{
+	struct vqm_frag *new_chunk;
+	struct vqm_frag_end *end;
+
+	if (qm->free_core<size) return 0;
+
+	/* update core */
+	if (IS_BIGBUCKET(qm, bucket)) {
+		qm->big_chunks-=size;
+		new_chunk = (struct vqm_frag *) qm->big_chunks ;	
+	} else {
+		new_chunk = (struct vqm_frag *) qm->core;	
+		qm->core+=size;
+	}
+	qm->free_core-=size;
+
+	/* initialize the new fragment */
+	new_chunk->u.inuse.bucket = bucket;
+	new_chunk->size = size;
+
+	end=FRAG_END( new_chunk );
+	end->size=size;
+
+	return new_chunk;
+}
+
+static inline void vqm_detach_free( struct vqm_block* qm, struct vqm_frag* frag)
+{
+
+	struct vqm_frag *prev, *next;
+	struct vqm_frag_end *end;
+
+	prev=FRAG_END(frag)->prv_free; 
+	next=frag->u.nxt_free;
+
+	if (prev) prev->u.nxt_free=next; 
+	else qm->next_free[BIG_BUCKET(qm)]=next;
+
+	if (next) FRAG_END(next)->prv_free=prev; 
+	 
+}
+
+
+#ifdef DBG_QM_MALLOC
+void* vqm_malloc(struct vqm_block* qm, unsigned int size, 
+	char* file, char* func, unsigned int line)
+#else
+void* vqm_malloc(struct vqm_block* qm, unsigned int size)
+#endif
+{
+	struct vqm_frag *new_chunk, *f;
+	unsigned char bucket;
+	
+#ifdef DBG_QM_MALLOC
+	unsigned int demanded_size;
+	DBG("vqm_malloc(%x, %d) called from %s: %s(%d)\n", qm, size, file, func,
+			line);
+	demanded_size = size;
+#endif
+	new_chunk=0;
+    	/* what's the bucket? what's the total size incl. overhead? */
+	bucket = size2bucket( qm, &size );
+
+	if (IS_BIGBUCKET(qm, bucket)) {	/* the kilo-bucket uses first-fit */
+		DBG("vqm_malloc: processing a big fragment\n");
+		for (f=qm->next_free[bucket] ; f; f=f->u.nxt_free ) 
+			if (f->size>=size) { /* first-fit */
+				new_chunk=f;
+				VQM_DEBUG_FRAG(qm, f);
+				vqm_detach_free(qm,f);
+				break;
+			}
+	} else if (  (new_chunk=qm->next_free[ bucket ]) ) { /*fixed size bucket*/
+			VQM_DEBUG_FRAG(qm, new_chunk);
+			/*detach it from the head of bucket's free list*/
+			qm->next_free[ bucket ] = new_chunk->u.nxt_free;
+	}
+
+	if (!new_chunk) { /* no chunk can be reused; slice one from the core */
+		new_chunk=MORE_CORE( qm, bucket, size );
+		if (!new_chunk) return 0;
+	}
+	new_chunk->u.inuse.magic = FR_USED;
+	new_chunk->u.inuse.bucket=bucket;
+#ifdef DBG_QM_MALLOC
+	new_chunk->file=file;
+	new_chunk->func=func;
+	new_chunk->line=line;
+	new_chunk->demanded_size=demanded_size;
+	qm->usage[ bucket ]++;
+	DBG("vqm_malloc( %x, %d ) returns address %x in bucket %d, real-size %d \n",
+		qm, demanded_size, (char*)new_chunk+sizeof(struct vqm_frag), 
+		bucket, size );
+
+	new_chunk->end_check=(char*)new_chunk+sizeof(struct vqm_frag)+demanded_size;
+	memcpy(  new_chunk->end_check, END_CHECK_PATTERN, END_CHECK_PATTERN_LEN );
+	new_chunk->check=ST_CHECK_PATTERN;
+#endif
+	return (char*)new_chunk+sizeof(struct vqm_frag);
+}
+
+
+
+
+#ifdef DBG_QM_MALLOC
+void vqm_free(struct vqm_block* qm, void* p, char* file, char* func, 
+				unsigned int line)
+#else
+void vqm_free(struct vqm_block* qm, void* p)
+#endif
+{
+	struct vqm_frag *f, *next, *prev, *first_big;
+	unsigned char b;
+
+#ifdef DBG_QM_MALLOC
+	DBG("vqm_free(%x, %x), called from %s: %s(%d)\n", qm, p, file, func, line);
+	if (p>(void *)qm->core_end || p<(void*)qm->init_core){
+		LOG(L_CRIT, "BUG: vqm_free: bad pointer %x (out of memory block!) - "
+				"aborting\n", p);
+		abort();
+	}
+#endif
+	if (p==0) {
+		DBG("WARNING:vqm_free: free(0) called\n");
+		return;
+	}
+	f=(struct  vqm_frag*) ((char*)p-sizeof(struct vqm_frag));
+	b=f->u.inuse.bucket;
+#ifdef DBG_QM_MALLOC
+	VQM_DEBUG_FRAG(qm, f);
+	if ( ! FRAG_ISUSED(f) ) {
+		LOG(L_CRIT, "BUG: vqm_free: freeing already freed pointer,"
+				" first freed: %s: %s(%d) - aborting\n",
+				f->file, f->func, f->line);
+		abort();
+	}
+	if ( b>MAX_BUCKET ) {
+		LOG(L_CRIT, "BUG: vqm_free: fragment with too high bucket nr: "
+				"%d, allocated: %s: %s(%d) - aborting\n",
+				b, f->file, f->func, f->line); 
+		abort();
+	}
+	DBG("vqm_free: freeing %d bucket block alloc'ed from %s: %s(%d)\n", 
+		f->u.inuse.bucket, f->file, f->func, f->line);
+	f->file=file; f->func=func; f->line=line;
+	qm->usage[ f->u.inuse.bucket ]--;
+#endif
+	if (IS_BIGBUCKET(qm,b)) {
+		next=FRAG_NEXT(f);
+		if  ((char *)next +sizeof( struct vqm_frag) < qm->core_end) {
+			VQM_DEBUG_FRAG(qm, next);
+			if (! FRAG_ISUSED(next)) { /* coalescate with next fragment */
+				DBG("vqm_free: coalescated with next\n");
+				vqm_detach_free(qm, next);
+				f->size+=next->size;
+				FRAG_END(f)->size=f->size;
+			}
+		}
+		first_big = qm->next_free[b];
+		if (first_big &&  f>first_big) {
+			prev=FRAG_PREV(f);
+			VQM_DEBUG_FRAG(qm, prev);
+			if (!FRAG_ISUSED(prev)) { /* coalescate with prev fragment */
+				DBG("vqm_free: coalescated with prev\n");
+				vqm_detach_free(qm, prev );
+				prev->size+=f->size;
+				f=prev;
+				FRAG_END(f)->size=f->size;
+			}
+		}
+		if ((char *)f==qm->big_chunks) { /* release unused core */
+			DBG("vqm_free: big chunk released\n");
+			qm->free_core+=f->size;
+			qm->big_chunks+=f->size;
+			return;
+		}		
+		first_big = qm->next_free[b];
+		/* fix reverse link (used only for BIG_BUCKET */
+		if (first_big) FRAG_END(first_big)->prv_free=f;
+		FRAG_END(f)->prv_free=0;
+	} else first_big = qm->next_free[b];
+	f->u.nxt_free = first_big; /* also clobbers magic */
+	qm->next_free[b] = f;
+}
+
+void dump_frag( struct vqm_frag* f, int i )
+{
+	LOG(L_INFO, "    %3d. address=%x  real size=%d bucket=%d\n", i, 
+		(char*)f+sizeof(struct vqm_frag), f->size, f->u.inuse.bucket);
+#ifdef DBG_QM_MALLOC
+	LOG(L_INFO, "            demanded size=%d\n", f->demanded_size );
+	LOG(L_INFO, "            alloc'd from %s: %s(%d)\n",
+		f->file, f->func, f->line);
+	LOG(L_INFO, "        start check=%x, end check= %*s\n",
+			f->check, END_CHECK_PATTERN_LEN, f->end_check );
+#endif
+}
+
+void vqm_status(struct vqm_block* qm)
+{
+	struct vqm_frag* f;
+	unsigned int i,j,on_list;
+
+	LOG(L_INFO, "vqm_status (%x):\n", qm);
+	if (!qm) return;
+	LOG(L_INFO, " heap size= %d, available: %d\n", 
+		qm->core_end-qm->init_core, qm->free_core );
+	
+	LOG(L_INFO, "dumping unfreed fragments:\n");
+	for (f=(struct vqm_frag*)qm->init_core, i=0;(char*)f<(char*)qm->core;
+		f=FRAG_NEXT(f) ,i++) if ( FRAG_ISUSED(f) ) dump_frag(f, i);
+
+	LOG(L_INFO, "dumping unfreed big fragments:\n");
+    for (f=(struct vqm_frag*)qm->big_chunks,i=0;(char*)f<(char*)qm->core_end;
+		f=FRAG_NEXT(f) ,i++) if ( FRAG_ISUSED(f) ) dump_frag( f, i );
+
+#ifdef DBG_QM_MALLOC
+	DBG("dumping bucket statistics:\n");
+	for (i=0; i<=BIG_BUCKET(qm); i++) {
+		for(on_list=0, f=qm->next_free[i]; f; f=f->u.nxt_free ) on_list++;
+		LOG(L_DBG, "    %3d. bucket: in use: %d, on free list: %d\n", 
+			i, qm->usage[i], on_list );
+	}
+#endif
+	LOG(L_INFO, "-----------------------------\n");
+}
+
+
+
+#endif

+ 130 - 0
mem/vq_malloc.h

@@ -0,0 +1,130 @@
+/* $Id$
+ *
+ */
+
+#if !defined(VQ_MALLOC_H) && defined(VQ_MALLOC)
+#define VQ_MALLOC_H
+
+#include "../config.h"
+
+
+/* indicates this fragment is not in use (must not be offset of valid
+   aligned fragment beginning
+*/
+#define	FR_USED		0xef
+
+/*useful macros*/
+#define FRAG_END(f)  \
+	((struct vqm_frag_end*)((char*)(f)-sizeof(struct vqm_frag_end)+ \
+	(f)->size))
+
+#define FRAG_NEXT(f) \
+	((struct vqm_frag*)((char*)(f)+(f)->size))
+
+#define PREV_FRAG_END(f) \
+	((struct vqm_frag_end*)((char*)(f)-sizeof(struct vqm_frag_end)))
+
+#define FRAG_PREV(f) \
+	( (struct vqm_frag*) ( (char*)(f) - PREV_FRAG_END(f)->size ))
+
+#define FRAG_ISUSED(f) \
+	((f)->u.inuse.magic==FR_USED)
+
+/* just a bumper for the step function */
+#define EO_STEP                         -1
+
+
+#ifdef DBG_QM_MALLOC
+#define ST_CHECK_PATTERN   	0xf0f0f0f0
+#define END_CHECK_PATTERN  	"sExP"
+#define END_CHECK_PATTERN_LEN 	4
+
+#define VQM_DEBUG_FRAG(qm, f) vqm_debug_frag( (qm), (f))
+#else
+#define VQM_DEBUG_FRAG(qm, f)
+#endif
+
+
+struct vqm_frag {
+	/* XXX */
+	/* total chunk size including all overhead/bellowfoot/roundings/etc */
+	/* useless as otherwise size implied by bucket (if I really want to save 
+       bytes, I'll remove it  from here */
+	unsigned int size;
+	union{
+		/* pointer to next chunk in a bucket if free */
+		struct vqm_frag* nxt_free; 
+		struct {   /* or bucket number if busy */
+			unsigned char magic;
+			unsigned char bucket;
+        } inuse;
+	} u;
+#ifdef DBG_QM_MALLOC
+	/* source code info */
+	char* file;
+	char* func;
+	unsigned int line;
+	/* your safety is important to us! safety signatures */
+	unsigned int check;
+	char *end_check;
+	/* the size user was originally asking for */
+	unsigned int demanded_size;
+#endif
+};
+
+struct vqm_frag_end{
+	/* total chunk size including all overhead/bellowfoot/roundings/etc */
+	unsigned int size; 
+	/* XXX */
+	/* used only for variable-size chunks; might have different
+           data structures for variable/fixed length chunks */
+	struct vqm_frag* prv_free;
+};
+
+
+struct vqm_block{
+	/* size to bucket table */
+	unsigned char s2b[ MAX_FIXED_BLOCK ];
+	/* size to rounded size */
+	unsigned short s2s[ MAX_FIXED_BLOCK ];
+	unsigned char max_small_bucket;
+
+	/* core gained on init ... */
+	char *core, *init_core, *core_end;
+	/* ... and its available net amount; note that there's lot of
+           free memory in buckets too -- this just tells about memory
+	   which has not been assigned to chunks  */
+	unsigned int free_core;
+	/* we allocate huge chunks from the end on; this is the
+	   pointer to big chunks
+    */
+	char *big_chunks;
+
+	struct vqm_frag* next_free[ MAX_BUCKET +1];
+#ifdef DBG_QM_MALLOC
+	unsigned long usage[ MAX_BUCKET +1];
+#endif
+};
+
+
+
+struct vqm_block* vqm_malloc_init(char* address, unsigned int size);
+
+#ifdef DBG_QM_MALLOC
+void* vqm_malloc(struct vqm_block*, unsigned int size, char* file, char* func, 
+					unsigned int line);
+#else
+void* vqm_malloc(struct vqm_block*, unsigned int size);
+#endif
+
+#ifdef DBG_QM_MALLOC
+void  vqm_free(struct vqm_block*, void* p, char* file, char* func, 
+				unsigned int line);
+#else
+void  vqm_free(struct vqm_block*, void* p);
+#endif
+
+void  vqm_status(struct vqm_block*);
+
+
+#endif

+ 2 - 15
modules/tm/TODO

@@ -7,22 +7,9 @@ Things we have omitted for now:
 - no SIP-wise HF comparison within T-matching
   (just memcmp)
 - 6xx should be delayed indeed
+- relaying CANCEL should be delayed until a reply to
+  INVITE received if not yet
 - ACK of 2xx INV caching (avoid e2e retransmission
   of an ACK hit the proxy too)
-- timers (408)
 
-- Via:
-	- branch
-		- loop check
-		- hash table & index
-		- branch
-	- mid & response routing
-
-- T-language
-
-To improve:
-- too many memcpies
-- faster syncing
-
-Double-check: revire the T-state-machine
 To-do: semaphore clean-up on exit (even better: w/sibling check)

+ 36 - 0
modules/tm/config.h

@@ -0,0 +1,36 @@
+/*
+ * $Id$
+ *
+ */
+
+#ifndef _TM_CONFIG_H
+#define _TM_CONFIG_H
+
+/* always use a power of 2 for hash table size */
+#define T_TABLE_POWER		10
+#define TABLE_ENTRIES  		(2 << (T_TABLE_POWER-1))
+
+/* maximum number of forks per transaction */
+#define MAX_FORK		2
+
+/* maximumum length of localy generated acknowledgement */
+#define MAX_ACK_LEN 		1024
+
+/* FINAL_RESPONSE_TIMER ... tells how long should the transaction engine
+   wait if no final response comes back*/
+#define FR_TIME_OUT		16
+#define INV_FR_TIME_OUT     	30
+
+/* WAIT timer ... tells how long state should persist in memory after
+   a transaction was finalized*/
+#define WT_TIME_OUT      	5
+
+/* DELETE timer ... tells how long should the transaction persist in memory
+   after it was removed from the hash table and before it will be deleted */
+#define DEL_TIME_OUT      	2
+ 
+/* retransmission timers */
+#define RETR_T1  		1
+#define RETR_T2  		4
+
+#endif

+ 0 - 12
modules/tm/globals.h

@@ -1,12 +0,0 @@
-/*
- * $Id$
- */
-
-
-
-#ifndef _H_GLOBALS
-#define _H_GLOBALS
-
-/* #define DBG( s ) printf( (s) ); */
-
-#endif

+ 36 - 50
modules/tm/h_table.c

@@ -2,7 +2,7 @@
  * $Id$
  */
 
-
+#include "hash_func.h"
 #include "h_table.h"
 #include "../../dprint.h"
 #include "sh_malloc.h"
@@ -12,53 +12,39 @@
   */
 void free_cell( struct cell* dead_cell )
 {
-   int i;
-   struct retrans_buff* rb;
-   char *b;
-
-   DBG("DEBUG: free_cell: start\n");
-   /* UA Server */
-   DBG("DEBUG: free_cell: inbound request %p\n",dead_cell->inbound_request);
-   if ( dead_cell->inbound_request )
-      sip_msg_free( dead_cell->inbound_request );
-   DBG("DEBUG: free_cell: outbound response %p\n",dead_cell->outbound_response);
-   if ( dead_cell->outbound_response )
-   {
-      b = dead_cell->outbound_response->retr_buffer ;
-      dead_cell->outbound_response->retr_buffer = NULL;
-      sh_free( b );
-
-      rb = dead_cell->outbound_response;
-      dead_cell->outbound_response = NULL;
-      sh_free( rb );
-
-   }
-
-  /* UA Clients */
-   for ( i =0 ; i<dead_cell->nr_of_outgoings;  i++ )
-   {
-      /* outbound requests*/
-      DBG("DEBUG: free_cell: outbound_request[%d] %p\n",i,dead_cell->outbound_request[i]);
-      if ( dead_cell->outbound_request[i] )
-      {
-	 b = dead_cell->outbound_request[i]->retr_buffer;
-         dead_cell->outbound_request[i]->retr_buffer = NULL;
-         sh_free( b );
-
-	 rb = dead_cell->outbound_request[i];
-	 dead_cell->outbound_request[i] = NULL;
-         sh_free( rb );
-      }
-      /* outbound requests*/
-      DBG("DEBUG: free_cell: inbound_response[%d] %p\n",i,dead_cell->inbound_response[i]);
-      if ( dead_cell -> inbound_response[i] )
-         sip_msg_free( dead_cell->inbound_response[i] );
-   }
-   /* mutex */
-   release_cell_lock( dead_cell );
-   /* the cell's body */
-   sh_free( dead_cell );
-   DBG("DEBUG: free_cell: done\n");
+	int i;
+	struct retrans_buff* rb;
+	char *b;
+
+	DBG("DEBUG: free_cell: start\n");
+	/* UA Server */
+	DBG("DEBUG: free_cell: inbound request %p\n",dead_cell->inbound_request);
+	if ( dead_cell->inbound_request )
+		sip_msg_free( dead_cell->inbound_request );
+	DBG("DEBUG: free_cell: outbound response %p\n",dead_cell->outbound_response);
+	if (b=dead_cell->outbound_response.retr_buffer) sh_free( b );
+
+	/* UA Clients */
+	for ( i =0 ; i<dead_cell->nr_of_outgoings;  i++ )
+	{
+		/* outbound requests*/
+		DBG("DEBUG: free_cell: outbound_request[%d] %p\n",i,dead_cell->outbound_request[i]);
+		if ( rb=dead_cell->outbound_request[i] )
+      		{
+			if (rb->retr_buffer) sh_free( rb->retr_buffer );
+	 		dead_cell->outbound_request[i] = NULL;
+         		sh_free( rb );
+      		}
+      		/* outbound requests*/
+      		DBG("DEBUG: free_cell: inbound_response[%d] %p\n",i,dead_cell->inbound_response[i]);
+      		if ( dead_cell -> inbound_response[i] )
+         		sip_msg_free( dead_cell->inbound_response[i] );
+   	}
+   	/* mutex */
+   	/* release_cell_lock( dead_cell ); */
+   	/* the cell's body */
+   	sh_free( dead_cell );
+   	DBG("DEBUG: free_cell: done\n");
 }
 
 
@@ -123,7 +109,7 @@ struct s_table* init_hash_table()
 
    /* inits the timers*/
    for(  i=0 ; i<NR_OF_TIMER_LISTS ; i++ )
-      init_timerlist_lock( hash_table, i );
+      init_timer_list( hash_table, i );
 
    return  hash_table;
 
@@ -182,7 +168,7 @@ struct cell*  build_cell( struct sip_msg* p_msg )
    new_cell->T_canceled  = T_UNDEFINED;
    new_cell->T_canceler  = T_UNDEFINED;
 
-   init_cell_lock(  new_cell );
+   /* init_cell_lock(  new_cell ); */
 
    DBG("DEBUG: build_cell : done\n");
    return new_cell;

+ 23 - 30
modules/tm/h_table.h

@@ -12,6 +12,7 @@
 #include <arpa/inet.h>
 
 #include "../../msg_parser.h"
+#include "config.h"
 
 struct s_table;
 struct entry;
@@ -25,27 +26,10 @@ struct timer;
 #include "sip_msg.h"
 
 
-
-/* always use a power of 2 for hash table size */
-#define TABLE_ENTRIES  256
-#define MAX_FORK           20
-
-
 #define T_UNDEFINED 	( (struct cell*) -1 )
 #define T_NULL		( (struct cell*) 0 )
 
 
-/* timer list: includes head, tail and protection semaphore */
-typedef struct  timer
-{
-   struct timer_link *first_tl;
-   struct timer_link *last_tl;
-   ser_lock_t             mutex;
-   void                      (*timeout_handler)(void*);
-} timer_type;
-
-
-
 
 typedef struct retrans_buff
 {
@@ -59,15 +43,22 @@ typedef struct retrans_buff
    size_t tolen;
 
    /* a message can be linked just to retransmission and FR list */
-   struct timer_link tl[2];
+   struct timer_link retr_timer;
+   struct timer_link fr_timer;
+/*
    unsigned int timeout_ceiling;
    unsigned int timeout_value;
+*/
 
    /*the cell that containes this retrans_buff*/
    struct cell* my_T;
+
+	enum lists retr_list;
+
 }retrans_buff_type;
 
 
+/* transaction context */
 
 typedef struct cell
 {
@@ -76,7 +67,9 @@ typedef struct cell
    struct cell*     prev_cell;
 
    /*sync data */
-   ser_lock_t   mutex;
+   /*
+	/* we use hash table mutexes now */
+   /* ser_lock_t   mutex; */
    int       ref_counter;
 
    /* cell payload data */
@@ -96,7 +89,7 @@ typedef struct cell
    /* usefull data */
    /* UA Server */
    struct sip_msg         *inbound_request;
-   struct retrans_buff   *outbound_response;
+   struct retrans_buff   outbound_response;
    unsigned int             status;
    str*                             tag;
    unsigned int             inbound_request_isACKed;
@@ -106,6 +99,11 @@ typedef struct cell
    struct retrans_buff   *outbound_request[ MAX_FORK ];
    struct sip_msg          *inbound_response[ MAX_FORK ];
    unsigned int             outbound_request_isACKed[MAX_FORK];
+
+#ifdef	EXTRA_DEBUG
+	/* scheduled for deletion ? */
+	short damocles;
+#endif
 }cell_type;
 
 
@@ -123,7 +121,7 @@ typedef struct entry
 
 
 
-/* hash table */
+/* transaction table */
 struct s_table
 {
    /* table of hash entries; each of them is a list of synonyms  */
@@ -133,16 +131,11 @@ struct s_table
 };
 
 
-
-
-
 struct s_table* init_hash_table();
-void                  free_hash_table( struct s_table* hash_table );
-
-void             free_cell( struct cell* dead_cell );
+void free_hash_table( struct s_table* hash_table );
+void free_cell( struct cell* dead_cell );
 struct cell*  build_cell( struct sip_msg* p_msg );
-
-void remove_from_hash_table( struct s_table *hash_table,  struct cell * p_cell );
-void    insert_into_hash_table( struct s_table *hash_table,  struct cell * p_cell );
+void remove_from_hash_table( struct s_table *hash_table, struct cell * p_cell );
+void insert_into_hash_table( struct s_table *hash_table, struct cell * p_cell );
 
 #endif

+ 88 - 1
modules/tm/hash_func.c

@@ -3,11 +3,22 @@
  */
 
 
+#ifndef _CRC_H_
+#define _CRC_H_
+
+extern unsigned long int crc_32_tab[];
+extern unsigned short int ccitt_tab[];
+extern unsigned short int crc_16_tab[];
+
+#endif
+
 
 #include "hash_func.h"
 #include "../../dprint.h"
+#include "../../crc.h"
+#include "../../ut.h"
 
-int hash( str  call_id, str cseq_nr )
+int old_hash( str  call_id, str cseq_nr )
 {
    int  hash_code = 0;
    int  i;
@@ -19,3 +30,79 @@ int hash( str  call_id, str cseq_nr )
    return hash_code %= TABLE_ENTRIES;
 }
 
+int new_hash( str call_id, str cseq_nr )
+{
+	int hash_code = 0;
+	int i,j, k, third;
+	int ci_len, cs_len;
+	char c;
+	char *ci, *cs;
+
+	/* trim EoLs */
+/*
+	ci_len = call_id.len;
+	while (ci_len && ((c=call_id.s[ci_len-1])==0 || c=='\r' || c=='\n'))
+		ci_len--;
+	cs_len = cseq_nr.len;
+	while (cs_len && ((c=cseq_nr.s[cs_len-1])==0 || c=='\r' || c=='\n'))
+		cs_len--;
+*/
+	trim_len( ci_len, ci, call_id );
+	trim_len( cs_len, cs, cseq_nr );
+
+	/* run the cycle from the end ... we are interested in the
+	   most-right digits ... and just take the %10 value of it
+	*/
+	third=(ci_len-1)/3;
+	for ( i=ci_len-1, j=2*third, k=third;
+		k>0 ; i--, j--, k-- ) {
+		hash_code+=crc_16_tab[*(ci+i) /*+7*/ ]+
+			ccitt_tab[*(ci+k)+63]+	
+			ccitt_tab[*(ci+j)+13];
+	}
+	for( i=0 ; i<cs_len ; i++ )
+		//hash_code+=crc_32_tab[(cseq_nr.s[i]+hash_code)%243];
+		hash_code+=ccitt_tab[*(cs+i)+123];
+
+	hash_code %= (TABLE_ENTRIES-1);
+   	return hash_code;
+}
+
+void hashtest_cycle( int hits[TABLE_ENTRIES], char *ip )
+{
+	long int i,j,k, l;
+	int len1, len2, hashv;
+	static char buf1[1024];
+	static char buf2[1024];
+	str call_id; 
+	str cseq;
+
+	call_id.s=buf1;
+	cseq.s=buf2;
+
+	for (i=987654328;i<987654328+10;i++)
+		for (j=85296341;j<85296341+10;j++)
+			for (k=987654;k<=987654+10;k++)
+				for (l=101;l<201;l++) {
+					call_id.len=sprintf( buf1, "%d-%d-%d@%s", i,j,k, ip );
+					cseq.len=sprintf( buf2, "%d", l );
+					printf("%s\t%s\n", buf1, buf2 );
+					hashv=hash( call_id, cseq );
+					hits[ hashv ]++;
+				}
+}
+
+void hashtest()
+{
+	int hits[TABLE_ENTRIES];
+	int i;
+	
+	memset( hits, 0, sizeof hits );
+	hashtest_cycle( hits, "192.168.99.100" );
+	hashtest_cycle( hits, "172.168.99.100" );
+	hashtest_cycle( hits, "142.168.99.100" );
+	for (i=0; i<TABLE_ENTRIES; i++)
+		printf("[%d. %d]\n", i, hits[i] );
+	exit(0);
+}
+

+ 4 - 2
modules/tm/hash_func.h

@@ -6,10 +6,12 @@
 #ifndef _HASH_H
 #define _HASH_H
 
-#include "globals.h"
 #include "../../str.h"
 #include "h_table.h"
 
-int hash( str  call_id, str cseq_nr );
+int new_hash( str  call_id, str cseq_nr );
+int old_hash( str  call_id, str cseq_nr );
+
+#define hash( cid, cseq) new_hash( cid, cseq )
 
 #endif

+ 9 - 2
modules/tm/lock.c

@@ -6,7 +6,6 @@
 #include <errno.h>
 
 #include "lock.h"
-#include "globals.h"
 #include "timer.h"
 #include "../../dprint.h"
 
@@ -209,19 +208,22 @@ tryagain:
     }
    return r;
 }
-
+/*
 int init_cell_lock( struct cell *cell )
 {
+*/
 	/* just advice which of the available semaphores to use;
 		shared with the lock belonging to the next hash entry lock
             (so that there are no collisions if one wants to try to
              lock on a cell as well as its list)
 
         */
+/*
 	cell->mutex.semaphore_set=entry_semaphore,
 	cell->mutex.semaphore_index=(cell->hash_index % sem_nr + 1)%sem_nr;
 
 }
+*/
 
 int init_entry_lock( struct s_table* hash_table, struct entry *entry )
 {
@@ -249,13 +251,18 @@ int init_retr_timer_lock( struct s_table* hash_table, enum retransmission_lists
 }
 */
 
+/*
 int release_cell_lock( struct cell *cell )
 {
+*/
 	/* don't do anything here -- the init_*_lock procedures
 	   just advised on usage of shared semaphores but did not
 	   generate them
 	*/
+/*
 }
+*/
+
 int release_entry_lock( struct entry *entry )
 {
 	/* the same as above */

+ 2 - 2
modules/tm/lock.h

@@ -10,7 +10,6 @@
 #include <sys/ipc.h>
 #include <sys/sem.h>
 
-#include "globals.h"
 
 /* typedef to structure we use for mutexing;
    currently, index to a semaphore set identifier now */
@@ -21,6 +20,7 @@ typedef struct {
 
 
 #include "h_table.h"
+#include "timer.h"
 
 /* Uni*x permissions for IPC */
 #define IPC_PERMISSIONS 0666
@@ -36,7 +36,7 @@ int change_semaphore( ser_lock_t s  , int val );
 
 int init_cell_lock( struct cell *cell );
 int init_entry_lock( struct s_table* hash_table, struct entry *entry );
-int init_timerlist_lock( struct s_table* hash_table, enum lists timerlist_id);
+// int init_timerlist_lock( struct s_table* hash_table, enum lists timerlist_id);
 //int init_retr_timer_lock( struct s_table* hash_table, enum retransmission_lists list_id );
 
 int release_cell_lock( struct cell *cell );

+ 2 - 2
modules/tm/sh_malloc.h

@@ -6,11 +6,11 @@
 #ifndef _SH_MALLOC_H
 #define _SH_MALLOC_H
 
-#include "../../shm_mem.h"
+#include "../../mem/shm_mem.h"
 
 #if defined SHM_MEM
 
-#include "../../shm_mem.h"
+#include "../../mem/shm_mem.h"
 
 #define sh_malloc(size)		shm_malloc((size))
 #define sh_free(ptr)		shm_free((ptr))

+ 1 - 18
modules/tm/sip_msg.c

@@ -5,7 +5,7 @@
 
 #include "sip_msg.h"
 #include "../../dprint.h"
-#include "../../mem.h"
+#include "../../mem/mem.h"
 
 char*   translate_pointer( char* new_buf , char *org_buf , char* p);
 struct via_body* via_body_cloner( char* new_buf , char *org_buf , struct via_body *org_via);
@@ -445,7 +445,6 @@ void sh_free_hdr_field_lst(struct hdr_field* hf)
 
 
 
-/*only the content*/
 void sip_msg_free_1(struct sip_msg* msg)
 {
    if (!msg) return;
@@ -694,23 +693,7 @@ struct via_body* via_body_cloner_2( char* new_buf , char *org_buf , struct via_b
    return new_via;
 }
 
-
-
-
-
-
-
 void sip_msg_free_2(struct sip_msg* msg)
 {
    sh_free( (char*)msg );
 }
-
-
-
-
-
-
-
-
-
-

文件差异内容过多而无法显示
+ 458 - 670
modules/tm/t_funcs.c


+ 41 - 6
modules/tm/t_funcs.h

@@ -13,13 +13,18 @@
 #include "../../globals.h"
 #include "../../udp_server.h"
 #include "../../msg_translator.h"
-#include "../../mem.h"
+#include "../../mem/mem.h"
 
 struct s_table;
 struct timer;
 struct entry;
 struct cell;
 
+extern struct cell         *T;
+extern unsigned int     global_msg_id;
+extern struct s_table*  hash_table;
+
+
 #include "sh_malloc.h"
 
 #include "timer.h"
@@ -27,12 +32,41 @@ struct cell;
 #include "sip_msg.h"
 
 
-/* already defined in msg_parser.h
-#define get_cseq( p_msg)    ((struct cseq_body*)p_msg->cseq->parsed)
+
+/* convenience short-cut macros */
+#define REQ_METHOD first_line.u.request.method_value
+#define REPLY_STATUS first_line.u.reply.statuscode
+#define REPLY_CLASS(_reply) ((_reply)->REPLY_STATUS/100)
+#define SEND_BUFFER( _rb ) ({ if ((_rb)->retr_buffer) \
+	{ udp_send( (_rb)->retr_buffer, \
+	  (_rb)->bufflen, (struct sockaddr*)&((_rb)->to) , \
+	  sizeof(struct sockaddr_in) ); \
+	} else \
+	DBG("ERROR: attempt to send an empty buffer from %s (%d)", \
+	__FUNCTION__, __LINE__ ); })
+
+
+/* to avoid too many locks/unlocks, we gave up using separate locks
+   for cells and use those of transaction table entries
 */
 
-/* maximumum length of localy generated acknowledgement */
-#define MAX_ACK_LEN 1024
+#define DBG_REF(_action, _t) DBG("DEBUG: XXXXX %s (%s:%d): T=%p , ref=%d\n",\
+			(_action), __FUNCTION__, __LINE__, (_t),(_t)->ref_counter);
+
+#define unref_T(_T_cell) \
+	( {\
+		lock( hash_table->entrys[(_T_cell)->hash_index].mutex );\
+		(_T_cell)->ref_counter--;\
+		DBG_REF("unref", (_T_cell)); \
+		unlock( hash_table->entrys[(_T_cell)->hash_index].mutex );\
+	} );
+
+/* we assume that ref_T is only called from places where
+   the associated locks are set up and we don't need to
+   lock/unlock
+*/
+#define ref_T(_T_cell) ({ ((_T_cell)->ref_counter++); \
+		DBG_REF("ref", (_T_cell));	})
 
 
 int   tm_startup();
@@ -107,7 +141,8 @@ int t_retransmit_reply( struct sip_msg *, char* , char* );
 int t_send_reply( struct sip_msg * , unsigned int , char *  );
 
 
-
+/* releases T-context */
+int t_unref( struct sip_msg* p_msg, char* foo, char* bar );
 
 
 

+ 380 - 0
modules/tm/t_lookup.c

@@ -0,0 +1,380 @@
+/*
+ * $Id$
+ *
+ */
+
+#include "hash_func.h"
+#include "t_funcs.h"
+#include "../../dprint.h"
+#include "../../config.h"
+#include "../../parser_f.h"
+#include "../../ut.h"
+#include "../../timer.h"
+
+static int reverse_hex2int( char *c, int len )
+{
+	char *pc;
+	int r;
+	char mychar;
+
+	r=0;
+	for (pc=c+len-1; len>0; pc--, len--) {
+		r <<= 4 ;
+		mychar=*pc;
+		if ( mychar >='0' && mychar <='9') r+=mychar -'0';
+		else if (mychar >='a' && mychar <='f') r+=mychar -'a'+10;
+		else if (mychar  >='A' && mychar <='F') r+=mychar -'A'+10;
+		else return -1;
+	}
+	return r;
+}
+
+inline static int int2reverse_hex( char **c, int *size, int nr )
+{
+	unsigned short digit;
+
+	if (*size && nr==0) {
+		**c = '0';
+		(*c)++;
+		(*size)--;
+		return 1;
+	}
+
+	while (*size && nr ) {
+		digit = nr & 0xf ;
+		**c= digit >= 10 ? digit + 'a' - 10 : digit + '0';
+		nr >>= 4;
+		(*c)++;
+		(*size)--;
+	}
+	return nr ? -1 /* number not processed; too little space */ : 1;
+}
+
+/* function returns:
+ *      -1 - transaction wasn't found
+ *       1  - transaction found
+ */
+int t_lookup_request( struct sip_msg* p_msg )
+{
+   struct cell      *p_cell;
+   struct cell      *tmp_cell;
+   unsigned int  hash_index=0;
+   unsigned int  isACK;
+   struct sip_msg	*t_msg;
+
+   DBG("t_lookup_request: start searching\n");
+
+   /* parse all*/
+   if (check_transaction_quadruple(p_msg)==0)
+   {
+      LOG(L_ERR, "ERROR: TM module: t_lookup_request: too few headers\n");
+      T=0;
+      return -1;
+   }
+
+   /* start searching into the table */
+   hash_index = hash( p_msg->callid->body , get_cseq(p_msg)->number ) ;
+   isACK = p_msg->REQ_METHOD==METHOD_ACK;
+   DBG("t_lookup_request: continue searching;  hash=%d, isACK=%d\n",hash_index,isACK);
+
+   /* lock the hole entry*/
+   lock( hash_table->entrys[hash_index].mutex );
+
+   /* all the transactions from the entry are compared */
+   p_cell     = hash_table->entrys[hash_index].first_cell;
+   tmp_cell = 0;
+   while( p_cell )
+   {
+		int abba;
+
+		t_msg = p_cell->inbound_request;
+
+#define EQ_LEN(_hf) (t_msg->_hf->body.len==p_msg->_hf->body.len)
+#define EQ_STR(_hf) (memcmp(t_msg->_hf->body.s, p_msg->_hf->body.s, \
+		p_msg->_hf->body.len)==0)
+
+
+		if ( EQ_LEN(from) && EQ_LEN(callid) &&
+		  EQ_STR(callid) && EQ_STR(callid) &&
+		  /* we compare only numerical parts of CSEQ ...
+		     the method name should be the same as in first line */
+		  memcmp( get_cseq(t_msg)->number.s , get_cseq(p_msg)->number.s , 
+				get_cseq(p_msg)->number.len ) ==0 )
+		{
+			if (!isACK) {
+				if (t_msg->REQ_METHOD == p_msg->REQ_METHOD &&
+					EQ_LEN(to) && EQ_STR(to))
+					goto found;
+			} else { /* ACK */
+				if (t_msg->REQ_METHOD == METHOD_INVITE  &&
+					//p_cell->tag &&  p_cell->tag->len==p_msg->tag->body.len &&
+            		//if ( /*tag*/ memcmp( p_cell->tag->s , p_msg->tag->body.s , 
+					// p_msg->tag->body.len ) ==0 )
+					EQ_STR( to ) ) {
+					goto found;
+				}
+			} /* ACK */
+		} /* common HFs equal */
+
+      /* next transaction */
+      tmp_cell = p_cell;
+      p_cell = p_cell->next_cell;
+   } /* synonym loop */
+
+   /* no transaction found */
+   T = 0;
+   unlock( hash_table->entrys[hash_index].mutex );
+   DBG("DEBUG: t_lookup_request: no transaction found\n");
+   return -1;
+
+found:
+	T=p_cell;
+	ref_T( T );
+	DBG("DEBUG:XXXXXXXXXXXXXXXXXXXXX t_lookup_request: "
+			"transaction found ( T=%p , ref=%d)\n",T,T->ref_counter);
+	unlock( hash_table->entrys[hash_index].mutex );
+	return 1;
+}
+
+
+
+
+/* function returns:
+ *       0 - transaction wasn't found
+ *       T - transaction found
+ */
+struct cell* t_lookupOriginalT(  struct s_table* hash_table , struct sip_msg* p_msg )
+{
+   struct cell      *p_cell;
+   struct cell      *tmp_cell;
+   unsigned int  hash_index=0;
+
+   /* it's a CANCEL request for sure */
+
+   /* start searching into the table */
+   hash_index = hash( p_msg->callid->body , get_cseq(p_msg)->number  ) ;
+   DBG("DEBUG: t_lookupOriginalT: searching on hash entry %d\n",hash_index );
+
+   /* all the transactions from the entry are compared */
+   p_cell     = hash_table->entrys[hash_index].first_cell;
+   tmp_cell = 0;
+   while( p_cell )
+   {
+      /* is it the wanted transaction ? */
+      /* first only the length are checked */
+      if ( /*from length*/ p_cell->inbound_request->from->body.len == p_msg->from->body.len )
+         if ( /*to length*/ p_cell->inbound_request->to->body.len == p_msg->to->body.len )
+            //if ( /*tag length*/ (!p_cell->inbound_request->tag && !p_msg->tag) || (p_cell->inbound_request->tag && p_msg->tag && p_cell->inbound_request->tag->body.len == p_msg->tag->body.len) )
+               if ( /*callid length*/ p_cell->inbound_request->callid->body.len == p_msg->callid->body.len )
+                  if ( /*cseq_nr length*/ get_cseq(p_cell->inbound_request)->number.len == get_cseq(p_msg)->number.len )
+                      if ( /*cseq_method type*/ p_cell->inbound_request->REQ_METHOD!=METHOD_CANCEL )
+                         if ( /*req_uri length*/ p_cell->inbound_request->first_line.u.request.uri.len == p_msg->first_line.u.request.uri.len )
+                             /* so far the lengths are the same -> let's check the contents */
+                             if ( /*from*/ memcmp( p_cell->inbound_request->from->body.s , p_msg->from->body.s , p_msg->from->body.len )==0 )
+                                if ( /*to*/ memcmp( p_cell->inbound_request->to->body.s , p_msg->to->body.s , p_msg->to->body.len)==0  )
+                                   //if ( /*tag*/ (!p_cell->inbound_request->tag && !p_msg->tag) || (p_cell->inbound_request->tag && p_msg->tag && memcmp( p_cell->inbound_request->tag->body.s , p_msg->tag->body.s , p_msg->tag->body.len )==0) )
+                                      if ( /*callid*/ memcmp( p_cell->inbound_request->callid->body.s , p_msg->callid->body.s , p_msg->callid->body.len )==0 )
+                                          if ( /*cseq_nr*/ memcmp( get_cseq(p_cell->inbound_request)->number.s , get_cseq(p_msg)->number.s , get_cseq(p_msg)->number.len )==0 )
+                                             if ( /*req_uri*/ memcmp( p_cell->inbound_request->first_line.u.request.uri.s , p_msg->first_line.u.request.uri.s , p_msg->first_line.u.request.uri.len )==0 )
+                                             { /* WE FOUND THE GOLDEN EGG !!!! */
+                                                return p_cell;
+                                             }
+      /* next transaction */
+      tmp_cell = p_cell;
+      p_cell = p_cell->next_cell;
+   }
+
+   /* no transaction found */
+   T = 0;
+   return 0;
+
+
+   return 0;
+}
+
+
+
+
+/* Returns 0 - nothing found
+  *              1  - T found
+  */
+int t_reply_matching( struct sip_msg *p_msg , unsigned int *p_branch )
+{
+   struct cell*  p_cell;
+   struct cell* tmp_cell;
+   unsigned int hash_index = 0;
+   unsigned int entry_label  = 0;
+   unsigned int branch_id    = 0;
+   char  *hashi, *syni, *branchi, *p, *n;
+   int hashl, synl, branchl;
+   int scan_space;
+
+   /* split the branch into pieces: loop_detection_check(ignored),
+      hash_table_id, synonym_id, branch_id*/
+
+   if (! ( p_msg->via1 && p_msg->via1->branch && p_msg->via1->branch->value.s) )
+	goto nomatch2;
+
+   p=p_msg->via1->branch->value.s;
+   scan_space=p_msg->via1->branch->value.len;
+
+   /* loop detection ... ignore */
+   n=eat_token2_end( p, p+scan_space, '.');
+   scan_space-=n-p;
+   if (n==p || scan_space<2 || *n!='.') goto nomatch2;
+   p=n+1; scan_space--;
+
+   /* hash_id */
+   n=eat_token2_end( p, p+scan_space, '.');
+   hashl=n-p;
+   scan_space-=hashl;
+   if (!hashl || scan_space<2 || *n!='.') goto nomatch2;
+   hashi=p;
+   p=n+1;scan_space--;
+
+
+   /* sequence id */
+   n=eat_token2_end( p, p+scan_space, '.');
+   synl=n-p;
+   scan_space-=synl;
+   if (!synl || scan_space<2 || *n!='.') goto nomatch2;
+   syni=p;
+   p=n+1;scan_space--;
+
+   /* branch id */  /*  should exceed the scan_space */
+   n=eat_token_end( p, p+scan_space );
+   branchl=n-p;
+   if (!branchl ) goto nomatch2;
+   branchi=p;
+
+
+   hash_index=reverse_hex2int(hashi, hashl);
+   entry_label=reverse_hex2int(syni, synl);
+   branch_id=reverse_hex2int(branchi, branchl);
+	if (hash_index==-1 || entry_label==-1 || branch_id==-1) {
+		DBG("DEBUG: t_reply_matching: poor reply lables %d label %d branch %d\n",
+			hash_index, entry_label, branch_id );
+		goto nomatch2;
+	}
+
+
+   DBG("DEBUG: t_reply_matching: hash %d label %d branch %d\n",
+	hash_index, entry_label, branch_id );
+
+   /* sanity check */
+   if (hash_index<0 || hash_index >=TABLE_ENTRIES ||
+       entry_label<0 || branch_id<0 || branch_id>=MAX_FORK ) {
+			DBG("DBG: t_reply_matching: snaity check failed\n");
+          	goto nomatch2;
+	}
+
+   /* lock the hole entry*/
+   lock( hash_table->entrys[hash_index].mutex );
+
+   /*all the cells from the entry are scan to detect an entry_label matching */
+   p_cell     = hash_table->entrys[hash_index].first_cell;
+   tmp_cell = 0;
+   while( p_cell )
+   {
+      /* is it the cell with the wanted entry_label? */
+      if ( p_cell->label == entry_label )
+      /* has the transaction the wanted branch? */
+      if ( p_cell->nr_of_outgoings>branch_id && p_cell->outbound_request[branch_id] )
+      {/* WE FOUND THE GOLDEN EGG !!!! */
+          T = p_cell;
+          *p_branch = branch_id;
+          /* T->ref_counter ++; */
+		  ref_T( T );
+          unlock( hash_table->entrys[hash_index].mutex );
+          DBG("DEBUG:XXXXXXXXXXXXXXXXXXXXX t_reply_matching: reply matched (T=%p, ref=%d)!\n",T,T->ref_counter);
+        return 1;
+      }
+      /* next cell */
+      tmp_cell = p_cell;
+      p_cell = p_cell->next_cell;
+
+   } /* while p_cell */
+
+   /* nothing found */
+   DBG("DEBUG: t_reply_matching: no matching transaction exists\n");
+
+nomatch:
+   unlock( hash_table->entrys[hash_index].mutex );
+nomatch2:
+   DBG("DEBUG: t_reply_matching: failure to match a transaction\n");
+   *p_branch = -1;
+   T = 0;
+   return -1;
+}
+
+
+
+
+/* Functions update T (T gets either a valid pointer in it or it equals zero) if no transaction
+  * for current message exists;
+  */
+int t_check( struct sip_msg* p_msg , int *param_branch)
+{
+   int local_branch;
+
+   /* is T still up-to-date ? */
+   DBG("DEBUG: t_check : msg id=%d , global msg id=%d , T=%p\n", p_msg->id,global_msg_id,T);
+   if ( p_msg->id != global_msg_id || T==T_UNDEFINED )
+   {
+      global_msg_id = p_msg->id;
+      if ( T && T!=T_UNDEFINED )
+         unref_T(T);
+      T = T_UNDEFINED;
+      /* transaction lookup */
+     if ( p_msg->first_line.type==SIP_REQUEST )
+         t_lookup_request( p_msg );
+     else
+         t_reply_matching( p_msg , ((param_branch!=0)?(param_branch):(&local_branch)) );
+#ifdef EXTRA_DEBUG
+	if ( T && T!=T_UNDEFINED && T->damocles) {
+		LOG( L_ERR, "ERROR: transaction %p scheduled for deletion and called from t_check\n", T);
+		abort();
+	}
+#endif
+
+   }
+   else
+   {
+      if (T)
+         DBG("DEBUG: t_check: T alredy found!\n");
+      else
+          DBG("DEBUG: t_check: T previously sought and not found\n");
+   }
+
+   return ((T)?1:-1) ;
+}
+
+
+
+/* append appropriate branch labels for fast reply-transaction matching
+   to outgoing requests
+*/
+int add_branch_label( struct cell *trans, struct sip_msg *p_msg, int branch )
+{
+	char *c;
+
+	char *begin;
+	unsigned int size, orig_size, n;
+
+	begin=p_msg->add_to_branch_s+p_msg->add_to_branch_len;
+	orig_size = size=MAX_BRANCH_PARAM_LEN - p_msg->add_to_branch_len;
+
+	if (size) { *begin='.'; begin++; size--; } else return -1;
+	if (int2reverse_hex( &begin, &size, trans->hash_index)==-1) return -1;
+	if (size) { *begin='.'; begin++; size--; } else return -1;
+	if (int2reverse_hex( &begin, &size, trans->label)==-1) return -1;
+	if (size) { *begin='.'; begin++; size--; } else return -1;
+	if (int2reverse_hex( &begin, &size, branch)==-1) return -1;
+
+	p_msg->add_to_branch_len+=(orig_size-size);
+	DBG("DEBUG: XXX branch label created now: %*s (%d)\n",
+		p_msg->add_to_branch_len, p_msg->add_to_branch_s );
+	return 0;
+
+}
+

+ 138 - 158
modules/tm/timer.c

@@ -3,115 +3,196 @@
  */
 
 
+#include "config.h"
+#include "h_table.h"
 #include "timer.h"
 #include "../../dprint.h"
 
+void reset_timer_list( struct s_table* hash_table, enum lists list_id)
+{
+	hash_table->timers[ list_id ].first_tl.next_tl = & (hash_table->timers[ list_id ].last_tl );
+	hash_table->timers[ list_id ].last_tl.prev_tl = & (hash_table->timers[ list_id ].first_tl );
+	hash_table->timers[ list_id ].first_tl.prev_tl = 
+		hash_table->timers[ list_id ].last_tl.next_tl = NULL;
+	hash_table->timers[ list_id ].last_tl.time_out = -1;
+}
 
-void print_timer_list(struct s_table* hash_table, int list_id)
+void init_timer_list( struct s_table* hash_table, enum lists list_id)
+{
+	reset_timer_list( hash_table, list_id );
+	init_timerlist_lock( hash_table, list_id );
+}
+
+void print_timer_list(struct s_table* hash_table, enum lists list_id)
 {
    struct timer* timer_list=&(hash_table->timers[ list_id ]);
    struct timer_link *tl ;
 
-   tl = timer_list->first_tl;
-   while (tl)
+   tl = timer_list->first_tl.next_tl;
+   while (tl!=& timer_list->last_tl)
    {
       DBG("DEBUG: print_timer_list[%d]: %p, next=%p \n",list_id, tl, tl->next_tl);
       tl = tl->next_tl;
    }
 }
 
-
-void remove_from_timer_list_dummy( struct s_table* hash_table , struct timer_link* tl , int list_id)
+static void remove_from_timer_list_dummy(  struct timer_link* tl )
 {
-   struct timer* timer_list=&(hash_table->timers[ list_id ]);
-   DBG("DEBUG: remove_from_timer[%d]: %d, %p \n",list_id,tl->time_out,tl);
-
-   if ( tl->prev_tl )
-       tl->prev_tl->next_tl = tl->next_tl;
-    else
-         timer_list->first_tl = tl->next_tl;
-
-   if ( tl->next_tl )
-         tl->next_tl->prev_tl = tl->prev_tl;
-    else
-         timer_list->last_tl = tl->prev_tl;
-
+	DBG("DEBUG: remove_from_timer[%d]: %p \n",tl->list->id,tl);
+	tl->prev_tl->next_tl = tl->next_tl;
+	tl->next_tl->prev_tl = tl->prev_tl;
     tl->next_tl = 0;
     tl->prev_tl = 0;
+	tl->list = NULL;
 }
 
+/* put a new cell into a list nr. list_id within a hash_table;
+  * set initial timeout
+  */
+void add_to_tail_of_timer_list( struct timer *timer_list, 
+	struct timer_link *tl, unsigned int time_out )
+{
+	remove_from_timer_list( tl );
+	/* the entire timer list is locked now -- noone else can manipulate it */
+	lock( timer_list->mutex );
+	tl->time_out = time_out;
+	tl->prev_tl = timer_list->last_tl.prev_tl;
+	tl->next_tl = & timer_list->last_tl;
+	timer_list->last_tl.prev_tl = tl;
+	tl->prev_tl->next_tl = tl;
+	tl->list = timer_list;
+	//print_timer_list(hash_table, list_id);
+	/* give the list lock away */
+	unlock( timer_list->mutex );
+	DBG("DEBUG: add_to_tail_of_timer[%d]: %p\n",timer_list->id,tl);
+}
 
 
 
 
 
-
-
-/* put a new cell into a list nr. list_id within a hash_table;
-  * set initial timeout
-  */
-void add_to_tail_of_timer_list( struct s_table* hash_table , struct timer_link* tl, int list_id , unsigned int time_out )
+/* remove a cell from a list nr. list_id within a hash_table;
+*/
+void remove_from_timer_list( struct timer_link* tl)
 {
-   struct timer* timer_list = &(hash_table->timers[ list_id ]);
+	ser_lock_t	m;
+
+	if (is_in_timer_list2( tl )) {
+		m=tl->list->mutex;
+		/* the entire timer list is locked now -- noone else can manipulate it */
+		lock( m );
+		if ( is_in_timer_list2( tl )  ) remove_from_timer_list_dummy( tl );
+		//print_timer_list(hash_table, list_id);
+		/* give the list lock away */
+		unlock( m );
+	}
+}
 
-   /* the entire timer list is locked now -- noone else can manipulate it */
-   lock( timer_list->mutex );
 
-   /* if the element is already in list->first remove it */
-   if ( is_in_timer_list( tl,list_id)  )
-      remove_from_timer_list_dummy( hash_table , tl , list_id);
 
-   tl->time_out = time_out;
-   tl->next_tl= 0;
 
-   DBG("DEBUG: add_to_tail_of_timer[%d]: %d, %p\n",list_id,tl->time_out,tl);
-   /* link it into list */
-   if (timer_list->last_tl)
-   {
-       tl->prev_tl=timer_list->last_tl;
-       timer_list->last_tl->next_tl = tl;
-       timer_list->last_tl = tl ;
-   } else {
-       tl->prev_tl = 0;
-       timer_list->first_tl = tl;
-       timer_list->last_tl = tl;
-   }
+/*
+	detach items passed by the time from timer list
+*/
+struct timer_link  *check_and_split_time_list( struct timer *timer_list, int time )
+
+{
+	struct timer_link *tl , *tmp , *end, *ret;
+
+	//DBG("DEBUG : check_and_split_time_list: start\n");
+	/* the entire timer list is locked now -- noone else can manipulate it */
+	lock( timer_list->mutex );
+
+	end = &timer_list->last_tl;
+	tl = timer_list->first_tl.next_tl;
+	while( tl!=end && tl->time_out <= time) tl=tl->next_tl;
+
+	/* nothing to delete found */
+	if (tl->prev_tl==&(timer_list->first_tl)) {
+		ret = NULL;
+	} else { /* we did find timers to be fired! */
+		/* the detached list begins with current beginning */
+		ret = timer_list->first_tl.next_tl;
+		/* and we mark the end of the split list */
+		tl->prev_tl->next_tl = NULL;
+		/* the shortened list starts from where we suspended */
+		timer_list->first_tl.next_tl = tl;	
+		tl->prev_tl = & timer_list->first_tl;
+	}
 
-   //print_timer_list(hash_table, list_id);
    /* give the list lock away */
    unlock( timer_list->mutex );
+
+   //DBG("DEBUG : check_and_split_time_list: done, returns %p\n",tl);
+   //print_timer_list(hash_table, list_id);
+   return ret;
 }
 
 
 
 
-/*
+
+void timer_routine(unsigned int ticks , void * attr)
+{
+	struct s_table       *hash_table = (struct s_table *)attr;
+	struct timer*          timers= hash_table->timers;
+	struct timer_link  *tl, *tmp_tl;
+	int                           id;
+
+	DBG("%d\n", ticks);
+
+	for( id=0 ; id<NR_OF_TIMER_LISTS ; id++ )
+	{
+		/* to waste as little time in lock as possible, detach list
+		   with expired items and process them after leaving the
+		   lock
+		*/
+		tl = check_and_split_time_list( & (hash_table->timers[ id ]), ticks );
+		/* process items now */
+		while (tl)
+		{
+			/* reset the timer list linkage */
+			tmp_tl = tl->next_tl;
+			tl->next_tl = tl->prev_tl =0 ; 
+			tl->list = NULL;
+			DBG("DEBUG: timer routine: timer[%d] , tl=%p next=%p\n",id,tl,tmp_tl);
+			timers[id].timeout_handler( tl->payload );
+			tl = tmp_tl;
+		}
+	}
+}
+
+
+
+/* deprecated -- too CPU expensive 
   */
-void insert_into_timer_list( struct s_table* hash_table , struct timer_link* new_tl, int list_id , unsigned int time_out )
+/*
+void insert_into_timer_list( struct s_table* hash_table , 
+	struct timer_link* new_tl, enum lists list_id , unsigned int time_out )
 {
    struct timer          *timer_list = &(hash_table->timers[ list_id ]);
    struct timer_link  *tl;
 
-   /* the entire timer list is locked now -- noone else can manipulate it */
+   // the entire timer list is locked now -- noone else can manipulate it 
    lock( timer_list->mutex );
 
-   /* if the element is already in list->first remove it */
+   // if the element is already in list->first remove it 
    if ( is_in_timer_list( new_tl,list_id)  )
       remove_from_timer_list_dummy( hash_table , new_tl , list_id);
 
    new_tl->time_out = time_out ;
-   DBG("DEBUG: insert_into_timer[%d]: %d, %p\n",list_id,new_tl->time_out,new_tl);
-    /*seeks the position for insertion */
+   DBG("DEBUG: insert_into_timer[%d]:%d, %p\n",list_id,new_tl->time_out,new_tl);
+    // seeks the position for insertion 
    for( tl=timer_list->first_tl ; tl && tl->time_out<new_tl->time_out ; tl=tl->next_tl );
 
-   /* link it into list */
+   // link it into list
     if ( tl )
-    {  /* insert before tl*/
+    {  // insert before tl
        new_tl->prev_tl = tl->prev_tl;
        tl->prev_tl = new_tl;
     }
    else
-    {  /* at the end or empty list */
+    {  // at the end or empty list 
        new_tl->prev_tl = timer_list->last_tl;
        timer_list->last_tl = new_tl;
     }
@@ -120,114 +201,13 @@ void insert_into_timer_list( struct s_table* hash_table , struct timer_link* new
     else
        timer_list->first_tl = new_tl;
     new_tl->next_tl = tl;
+	tl->list_id = list_id;
 
    //print_timer_list(hash_table, list_id);
-   /* give the list lock away */
-    unlock( timer_list->mutex );
-}
-
-
-
+    // give the list lock away 
 
-/* remove a cell from a list nr. list_id within a hash_table;
-*/
-void remove_from_timer_list( struct s_table* hash_table , struct timer_link* tl , int list_id)
-{
-   struct timer* timer_list=&(hash_table->timers[ list_id ]);
-
-   /* the entire timer list is locked now -- noone else can manipulate it */
-   lock( timer_list->mutex );
-
-   /* if the element is already in list->first remove it */
-   if ( is_in_timer_list( tl,list_id)  )
-   {
-      remove_from_timer_list_dummy( hash_table , tl , list_id);
-   }
-
-   //print_timer_list(hash_table, list_id);
-   /* give the list lock away */
-   unlock( timer_list->mutex );
+    unlock( timer_list->mutex );
 }
 
 
-
-
-/*
 */
-struct timer_link  *check_and_split_time_list( struct s_table* hash_table, int list_id ,int time)
-{
-   struct timer* timer_list=&(hash_table->timers[ list_id ]);
-   struct timer_link *tl , *tmp;
-
-   //DBG("DEBUG : check_and_split_time_list: start\n");
-   /* the entire timer list is locked now -- noone else can manipulate it */
-   lock( timer_list->mutex );
-
-   tl = timer_list->first_tl;
-   if ( !tl )
-      goto exit;
-
-   for(  ; tl && tl->time_out <= time ; tl = tl->next_tl );
-
-    /*if I don't have to remove anything*/
-    if ( tl==timer_list->first_tl )
-    {
-      tl =0;
-      goto exit;
-    }
-
-    /*if I have to remove everything*/
-    if (tl==0)
-    {
-      tl = timer_list->first_tl;
-      timer_list->first_tl = timer_list->last_tl = 0;
-      //DBG("DEBUG : check_and_split_time_list: done, EVERY returns %p , list=%p\n",tl,timer_list->first_tl);
-      goto exit;
-    }
-
-    /*I have to split it somewhere in the middle */
-    tl->prev_tl->next_tl=0;
-    tl->prev_tl = 0;
-    tmp = timer_list->first_tl;
-    timer_list->first_tl = tl;
-    tl = tmp;
-   //DBG("DEBUG : check_and_split_time_list: done, SPLIT returns %p , list=%p\n",tl,timer_list->first_tl);
-
-exit:
-   /* give the list lock away */
-   unlock( timer_list->mutex );
-
-   //DBG("DEBUG : check_and_split_time_list: done, returns %p\n",tl);
-   //print_timer_list(hash_table, list_id);
-   return tl;
-}
-
-
-
-
-
-void timer_routine(unsigned int ticks , void * attr)
-{
-   struct s_table       *hash_table = (struct s_table *)attr;
-   struct timer*          timers= hash_table->timers;
-   struct timer_link  *tl, *tmp_tl;
-   int                           id;
-
-   DBG("%d\n", ticks);
-
-   for( id=0 ; id<NR_OF_TIMER_LISTS ; id++ )
-   {
-      tl = check_and_split_time_list( hash_table, id , ticks );
-      while (tl)
-      {
-         tmp_tl = tl->next_tl;
-         tl->next_tl = tl->prev_tl =0 ;
-         DBG("DEBUG: timer routine: timer[%d] , tl=%p next=%p\n",id,tl,tmp_tl);
-         timers[id].timeout_handler( tl->payload );
-         tl = tmp_tl;
-      }
-   }
-}
-
-
-

+ 37 - 35
modules/tm/timer.h

@@ -2,58 +2,60 @@
  * $Id$
  */
 
-
 #ifndef _TIMER_H
 #define _TIMER_H
 
-enum lists { RETRASMISSIONS_LIST, FR_TIMER_LIST, WT_TIMER_LIST, DELETE_LIST, NR_OF_TIMER_LISTS };
+#include "lock.h"
 
-/* we maintain separate retransmission lists for each of retransmission
-   periods; that allows us to keep the lists ordered while just adding
-   new items to list's tail (FIFO)
+/* identifiers of timer lists; 
 */
-//enum retransmission_lists { RT_T1_TO1, RT_T1_TO_2, RT_T1_TO_3, RT_T2, NR_OF_RT_LISTS };
-
-
-/* FINAL_RESPONSE_TIMER ... tells how long should the transaction engine
-   wait if no final response comes back*/
-#define FR_TIME_OUT            16
-#define INV_FR_TIME_OUT     30
-
-/* WAIT timer ... tells how long state should persist in memory after
-   a transaction was finalized*/
-#define WT_TIME_OUT      5
-
-/* DELETE timer ... tells how long should the transaction persist in memory
-   after it was removed from the hash table and before it will be deleted */
-#define DEL_TIME_OUT      2
-
+enum lists {	FR_TIMER_LIST, FR_INV_TIMER_LIST,
+				WT_TIMER_LIST, DELETE_LIST, 
+				/* fixed-timer retransmission lists (benefit: fixed timer
+				   length allows for appending new items to the list as
+					opposed to inserting them which is costly */
+				RT_T1_TO_1, RT_T1_TO_2, RT_T1_TO_3, RT_T2, 
+				NR_OF_TIMER_LISTS };
 
-#define RETR_T1  1
-#define RETR_T2  4
+#define is_in_timer_list2(_tl) ( (_tl)->list )
 
 
-#define is_in_timer_list(tl,id)  \
-             (tl->next_tl || tl->prev_tl || (!tl->next_tl && !tl->prev_tl && tl==hash_table->timers[id].first_tl) )
-
+struct timer;
 
 /* all you need to put a cell in a timer list:
    links to neighbours and timer value         */
 typedef struct timer_link
 {
-   struct timer_link *next_tl;
-   struct timer_link *prev_tl;
-   unsigned int        time_out;
-   void                      *payload;
+	struct timer_link 	*next_tl;
+	struct timer_link 	*prev_tl;
+	unsigned int       	time_out;
+	void				*payload;
+	struct timer		*list;
 }timer_link_type ;
 
-#include "h_table.h"
 
+/* timer list: includes head, tail and protection semaphore */
+typedef struct  timer
+{
+   struct timer_link first_tl;
+   struct timer_link last_tl;
+   ser_lock_t             mutex;
+   enum lists id;
+   void                      (*timeout_handler)(void*);
+} timer_type;
+
+void init_timer_list( struct s_table* hash_table, enum lists list_id);
+void reset_timer_list( struct s_table* hash_table, enum lists list_id);
 
-void                        add_to_tail_of_timer_list( struct s_table* hash_table , struct timer_link * tl , int list_id, unsigned int time_out );
-void                        insert_into_timer_list( struct s_table* hash_table , struct timer_link* tl, int list_id , unsigned int time_out );
-void                        remove_from_timer_list( struct s_table* hash_table , struct timer_link* tl , int list_id);
-void                        timer_routine(unsigned int, void *);
+void add_to_tail_of_timer_list( struct timer *timer_list, 
+	struct timer_link *tl, unsigned int time_out );
+void remove_from_timer_list( struct timer_link *tl);
+void timer_routine(unsigned int, void *);
 
 
+/* deprecated -- too expensive -- use appending instead 
+void insert_into_timer_list( struct s_table* hash_table , 
+	struct timer_link* tl, enum lists list_id , unsigned int time_out );
+*/
+
 #endif

+ 12 - 9
modules/tm/tm.c

@@ -5,17 +5,16 @@
  *
  */
 
-
+#include <stdio.h>
+#include <string.h>
+#include <netdb.h>
 
 #include "../../sr_module.h"
 #include "../../dprint.h"
 #include "../../error.h"
 #include "../../ut.h"
-#include "sip_msg.h"
-#include <stdio.h>
-#include <string.h>
-#include <netdb.h>
 
+#include "sip_msg.h"
 #include "h_table.h"
 #include "t_funcs.h"
 
@@ -40,7 +39,8 @@ static struct module_exports nm_exports= {
 				"t_forward_uri",
 				"t_send_reply",
 				"t_retransmit_reply",
-				"t_release"
+				"t_release",
+				"t_unref"
 			},
 	(cmd_function[]){
 					t_add_transaction,
@@ -50,7 +50,8 @@ static struct module_exports nm_exports= {
 					t_forward_uri,
 					w_t_send_reply,
 					t_retransmit_reply,
-					w_t_release
+					w_t_release,
+					t_unref
 					},
 	(int[]){
 				0,
@@ -60,6 +61,7 @@ static struct module_exports nm_exports= {
 				0,
 				2,
 				0,
+				0,
 				0
 			},
 	(fixup_function[]){
@@ -70,9 +72,10 @@ static struct module_exports nm_exports= {
 				0,
 				fixup_t_send_reply,
 				0,
-				0
+				0,
+				0,
 		},
-	8,
+	9,
 	(response_function) t_on_reply_received,
 	(destroy_function) tm_shutdown
 };

+ 10 - 6
msg_parser.c

@@ -13,10 +13,10 @@
 #include "ut.h"
 #include "error.h"
 #include "dprint.h"
-#include "mem.h"
+#include "mem/mem.h"
 
 #ifdef DEBUG_DMALLOC
-#include <dmalloc.h>
+#include <mem/dmalloc.h>
 #endif
 
 
@@ -54,7 +54,7 @@ char* parse_first_line(char* buffer, unsigned int len, struct msg_start * fl)
 	   token 
         */
 	if (len <=16 ) {
-		LOG(L_INFO, "ERROR: parse_first_line: message too short\n");
+		LOG(L_INFO, "ERROR: parse_first_line: message too short: %d\n", len);
 		goto error1;
 	}
 
@@ -131,8 +131,7 @@ char* parse_first_line(char* buffer, unsigned int len, struct msg_start * fl)
 		if (s1>='0' && s1<='9' && 
 		    s2>='0' && s2<='9' &&
 		    s3>='0' && s3<='9' ) {
-			fl->u.reply.statusclass=s1-'0';
-			fl->u.reply.statuscode=fl->u.reply.statusclass*100+10*(s2-'0')+(s3-'0');
+			fl->u.reply.statuscode=(s1-'0')*100+10*(s2-'0')+(s3-'0');
 		} else {
 			LOG(L_INFO, "ERROR:parse_first_line: status_code non-numerical: %s\n",
 				second );
@@ -278,6 +277,8 @@ char* get_hdr_field(char* buf, char* end, struct hdr_field* hdr)
 			goto error;
 	}
 
+	/* jku: if \r covered by current length, shrink it */
+	trim_r( hdr->body );
 	return tmp;
 error:
 	DBG("get_hdr_field: error exit\n");
@@ -806,7 +807,10 @@ void free_sip_msg(struct sip_msg* msg)
 	if (msg->add_rm)      free_lump_list(msg->add_rm);
 	if (msg->repl_add_rm) free_lump_list(msg->repl_add_rm);
 	pkg_free(msg->orig);
-	pkg_free(msg->buf);
+	/* don't free anymore -- now a pointer to a static buffer */
+#	ifdef DYN_BUF
+	pkg_free(msg->buf); */
+#	endif
 }
 
 

+ 1 - 1
msg_parser.h

@@ -84,7 +84,7 @@ struct msg_start{
 			str version;
 			str status;
 			str reason;
-			unsigned short statusclass, statuscode;
+			unsigned short /* statusclass,*/ statuscode;
 		}reply;
 	}u;
 };

+ 5 - 3
msg_translator.c

@@ -6,7 +6,7 @@
 #include <sys/socket.h>
 
 #include "msg_translator.h"
-#include "mem.h"
+#include "mem/mem.h"
 #include "dprint.h"
 #include "config.h"
 #include "md5utils.h"
@@ -556,7 +556,7 @@ char * build_res_buf_from_sip_req(	unsigned int code ,
 		if ( hdr->type==HDR_VIA || hdr->type==HDR_FROM ||
 				hdr->type==HDR_CALLID || hdr->type==HDR_TO ||
 				hdr->type==HDR_CSEQ )
-			len += ((hdr->body.s+hdr->body.len ) - hdr->name.s ) ;
+			len += ((hdr->body.s+hdr->body.len ) - hdr->name.s ) + CRLF_LEN;
 	/* end of message */
 	len += CRLF_LEN; /*new line*/
 
@@ -592,7 +592,9 @@ char * build_res_buf_from_sip_req(	unsigned int code ,
 			memcpy( p , msg->orig+(hdr->name.s-msg->buf) ,
 					((hdr->body.s+hdr->body.len ) -
 					hdr->name.s ) );
-			p += ((hdr->body.s+hdr->body.len ) - hdr->name.s );
+			p += ((hdr->body.s+hdr->body.len ) - hdr->name.s ) ;
+			memcpy( p, CRLF, CRLF_LEN );
+			p+=CRLF_LEN;
 		}
 
 	memcpy( p, CRLF, CRLF_LEN );

+ 1 - 1
parse_fline.c

@@ -1123,7 +1123,7 @@ char* parse_fline(char* buffer, char* end, struct msg_start* fl)
 skip:
 	if (fl->type==SIP_REPLY){
 		fl->u.reply.statuscode=stat;
-		fl->u.reply.statusclass=stat/100;
+		/* fl->u.reply.statusclass=stat/100; */
 	}
 	return tmp;
 	

+ 1 - 1
parse_via.c

@@ -21,7 +21,7 @@
 #include "dprint.h"
 #include "msg_parser.h"
 #include "ut.h"
-#include "mem.h"
+#include "mem/mem.h"
 
 
 

+ 8 - 21
receive.c

@@ -11,15 +11,12 @@
 #include "msg_parser.h"
 #include "forward.h"
 #include "action.h"
-#include "mem.h"
+#include "mem/mem.h"
+#include "stats.h"
 
 
 #ifdef DEBUG_DMALLOC
-#include <dmalloc.h>
-#endif
-
-#ifdef STATS
-#include "stats.h"
+#include <mem/dmalloc.h>
 #endif
 
 unsigned int msg_no=0;
@@ -78,10 +75,7 @@ int receive_msg(char* buf, unsigned int len, unsigned long src_ip)
 			goto error;
 		}
 		DBG("succesfully ran routing scripts...\n");
-#ifdef STATS
-		/* jku -- update request statistics  */
-		else update_received_request(msg->first_line.u.request.method_value );
-#endif
+		STATS_RX_REQUEST( msg->first_line.u.request.method_value );
 	}else if (msg->first_line.type==SIP_REPLY){
 		DBG("msg= reply\n");
 		/* sanity checks */
@@ -97,10 +91,7 @@ int receive_msg(char* buf, unsigned int len, unsigned long src_ip)
 		}
 		/* check if via1 == us */
 
-#ifdef STATS
-		/* jku -- update statistics  */
-		update_received_response( msg->first_line.u.reply.statusclass );
-#endif
+		STATS_RX_RESPONSE ( msg->first_line.u.reply.statusclass );
 		
 		/* send the msg */
 		if (forward_reply(msg)==0){
@@ -120,23 +111,19 @@ skip:
 	free_sip_msg(msg);
 	pkg_free(msg);
 #ifdef STATS
-	if (skipped) update_received_drops;
+	if (skipped) STATS_RX_DROPS;
 #endif
 	return 0;
 error:
 	DBG("error:...\n");
 	free_sip_msg(msg);
 	pkg_free(msg);
-#ifdef STATS
-	update_received_drops;
-#endif
+	STATS_RX_DROPS;
 	return -1;
 error1:
 	if (msg) pkg_free(msg);
 	pkg_free(buf);
-#ifdef STATS
-	update_received_drops;
-#endif
+	STATS_RX_DROPS;
 	return -1;
 }
 

+ 52 - 38
stats.h

@@ -12,6 +12,58 @@
 #include <errno.h>
 
 
+#define _update_request( method, dir )			\
+	{ if (stat_file!=NULL) switch( method ) {	\
+          	case METHOD_INVITE: stats->dir##_requests_inv++; break;	\
+          	case METHOD_ACK: stats->dir##_requests_ack++; break;		\
+          	case METHOD_CANCEL: stats->dir##_requests_cnc++; break;	\
+          	case METHOD_BYE: stats->dir##_requests_bye++; break;		\
+          	case METHOD_OTHER: stats->dir##_requests_other++; break;	\
+          	default: LOG(L_ERR, "ERROR: unknown method in rq stats (%s)\n", #dir);	\
+		}	\
+        }
+
+
+/*
+#define update_received_request( method ) _update_request( method, received )
+#define update_sent_request( method ) _update_request( method, sent )
+
+#define update_received_response( statusclass ) _update_response( statusclass, received )
+#define update_sent_response( statusclass ) _update_response( statusclass, sent )
+#define update_received_drops	{  stats->received_drops++; }
+#define update_fail_on_send	{  stats->failed_on_send++; }
+*/
+
+#define         _statusline(class, dir )       case class: stats->dir##_responses_##class++; break;
+
+#define _update_response( statusclass, dir )		\
+        { if (stat_file!=NULL)                          \
+                switch( statusclass ) {                 \
+                        _statusline(1, dir)                   \
+                        _statusline(2, dir)                   \
+                        _statusline(3, dir)                   \
+                        _statusline(4, dir)                   \
+                        _statusline(5, dir)                   \
+                        _statusline(6, dir)                   \
+                        default: LOG(L_INFO, "ERROR: unusual status code received in stats (%s)\n", #dir);    \
+                }       \
+        }
+
+#ifdef STATS
+#	define STATS_RX_REQUEST(method) _update_request(method, received)
+#	define STATS_TX_REQUEST(method) _update_request(method, sent )
+#	define STATS_RX_RESPONSE(class) _update_response( class, received )
+#	define STATS_TX_RESPONSE(class) _update_response( class, sent )
+#	define STATS_RX_DROPS {  stats->received_drops++; }
+#	define STATS_TX_DROPS {  stats->failed_on_send++; }
+#else
+#	define STATS_RX_REQUEST(method)
+#	define STATS_TX_REQUEST(method)
+#	define STATS_RX_RESPONSE(class) 
+#	define STATS_TX_RESPONSE(class) 
+#	define STATS_RX_DROPS 
+#	define STATS_TX_DROPS 
+#endif
 
 #ifdef STATS
 
@@ -76,44 +128,6 @@ void dump_statistic( FILE *fp, struct stats_s *istats );
 int dump_all_statistic();
 int init_stats( int nr_of_processes );
 
-#define _update_request( method, dir )			\
-	{ if (stat_file!=NULL) switch( method ) {	\
-          	case METHOD_INVITE: stats->dir##_requests_inv++; break;	\
-          	case METHOD_ACK: stats->dir##_requests_ack++; break;		\
-          	case METHOD_CANCEL: stats->dir##_requests_cnc++; break;	\
-          	case METHOD_BYE: stats->dir##_requests_bye++; break;		\
-          	case METHOD_OTHER: stats->dir##_requests_other++; break;	\
-          	default: LOG(L_ERR, "ERROR: unknown method in rq stats (%s)\n", #dir);	\
-		}	\
-        }
-
-#define update_received_request( method ) _update_request( method, received )
-#define update_sent_request( method ) _update_request( method, sent )
-
-#define         _statusline(class, dir )       case class: stats->dir##_responses_##class++; break;
-/*
-#define		statusline( class )	_statusline( class, received )
-#define		statusline2( class )	_statusline( class, sent )
-*/
-
-#define _update_response( statusclass, dir )		\
-        { if (stat_file!=NULL)                          \
-                switch( statusclass ) {                 \
-                        _statusline(1, dir)                   \
-                        _statusline(2, dir)                   \
-                        _statusline(3, dir)                   \
-                        _statusline(4, dir)                   \
-                        _statusline(5, dir)                   \
-                        _statusline(6, dir)                   \
-                        default: LOG(L_INFO, "ERROR: unusual status code received in stats (%s)\n", #dir);    \
-                }       \
-        }
-
-#define update_received_response( statusclass ) _update_response( statusclass, received )
-#define update_sent_response( statusclass ) _update_response( statusclass, sent )
-
-#define update_received_drops	{  stats->received_drops++; }
-#define update_fail_on_send	{  stats->failed_on_send++; }
 
 
 #endif

+ 2 - 2
test/tp.cfg

@@ -1,4 +1,4 @@
-debug=2          # debug level (cmd line: -dddddddddd)
+debug=9          # debug level (cmd line: -dddddddddd)
 log_stderror=yes # (cmd line: -E)
 check_via=yes     # (cmd. line: -v)
 dns=on           # (cmd. line: -r)
@@ -11,7 +11,7 @@ loop_checks=1
 
 #modules
 loadmodule "modules/print/print.so"
-loadmodule "modules/tm/tm.so"
+#loadmodule "modules/tm/tm.so"
 
 route{
 	if ( t_lookup_request()) {

+ 27 - 24
t_debug.cfg → test/tx.cfg

@@ -1,24 +1,32 @@
-debug=9          # debug level (cmd line: -dddddddddd)
+#
+# configuration for TurboSIP testing
+#
+# $ID: $
+#
+
+debug=1          # debug level (cmd line: -dddddddddd)
 check_via=yes     # (cmd. line: -v)
 dns=on           # (cmd. line: -r)
 rev_dns=yes      # (cmd. line: -R)
-fork=no          # (cmd. line: -D)
-children=10
-log_stderror=yes # (cmd line: -E)
-#port=5080
+fork=yes          # (cmd. line: -D)
+children=16
+#log_stderror=yes # (cmd line: -E)
+log_stderror=no	# (cmd line: -E)
+port=5080
 #listen=127.0.0.1
+listen=192.168.99.100
 loop_checks=1
 # for more info: sip_router -h
 
 #modules
 loadmodule "modules/print/print.so"
-loadmodule "modules/tm/tm.so"
+#loadmodule "modules/tm/tm.so"
 
 route{
 	if ( t_lookup_request()) {
 		if ( method=="ACK" )	{
 			log("SER: ACK received -> t_release\n");
-			if (! t_forward("iptel.org", "5060" )) {
+			if (! t_forward("bat.iptel.org", "5090" )) {
 				log("SER: WARNING: bad forward\n");
 			};
 			if (! t_release()) {
@@ -30,15 +38,17 @@ route{
 			};
 			log("SER: yet another annoying retranmission\n");
 		};
+		t_unref();
 	} else {
-		if (! t_add_transaction()){
-			log("ERROR in ser: t_add_transaction\n");
-			if (method=="BYE") {
-				forward("iptel.org", 5060);
-			}else{
-				forward("iptel.org", 5060 );
-			};
+		if (method=="ACK") {
+			# no established transaction ... forward ACK just statelessly
+			forward("bat.iptel.org", 5090);
 		} else {
+			# establish transaction
+			if (! t_add_transaction()){
+				log("ERROR in ser: t_add_transaction\n");
+			};
+			# reply
 			if (method=="CANCEL") {
 				log("SER: new CANCEL\n");
 				if (! t_send_reply( "200", "glad to cancel")){
@@ -51,17 +61,10 @@ route{
 					log("SER: ERROR: t_send_reply (100)\n");
 				};
 			};
-			if (method=="BYE") {
-				log("SER: BYE received, HACK: forwarding to client\n");
-				if (! t_forward("iptel.org", "5060")){
-					log("SER:ERROR: t_forward (..., 5555)\n");
-				};
-			}else{
-				if (! t_forward("iptel.org", "5060" )){
-					log("SER:ERROR: t_forward (..., 6666)\n");
-				};
+			if (! t_forward("bat.iptel.org", "5090")){
+				log("SER:ERROR: t_forward (..., 5555)\n");
 			};
+			t_unref();
 		};
 	};
-
 }

+ 53 - 0
test/xx.cfg

@@ -0,0 +1,53 @@
+# forwarding to Cisco phone
+#
+# $Id$
+#
+
+
+debug=9          # debug level (cmd line: -dddddddddd)
+log_stderror=yes # (cmd line: -E)
+check_via=yes     # (cmd. line: -v)
+dns=on           # (cmd. line: -r)
+rev_dns=yes      # (cmd. line: -R)
+fork=no          # (cmd. line: -D)
+port=5080
+#listen=127.0.0.1
+listen=192.168.99.100
+loop_checks=1
+# for more info: sip_router -h
+
+#modules
+loadmodule "modules/print/print.so"
+#loadmodule "modules/tm/tm.so"
+
+route{
+	if ( t_lookup_request()) {
+		if ( method=="ACK" )	{
+			t_release();
+#			forward(195.37.77.100, 5090 );
+#			forward(195.37.78.146, 5060 );
+# once it supports ACK too
+#			t_forward(195.37.77.100, 5090 );
+			t_forward("195.37.78.146", "5060" );
+		} else {
+			t_retransmit_reply();
+		};
+		t_unref();
+	} else {
+		if (method=="ACK") {
+#			forward(195.37.77.100, 5090 );
+			forward(195.37.78.146, 5060 );
+		} else {
+			t_add_transaction();
+			if (method=="CANCEL") {
+				t_send_reply( "200", "glad to cancel");
+			} else {
+				t_send_reply("100", "trying -- your call is important to us");
+			};
+#			t_forward("195.37.77.100", "5090" );
+			t_forward("195.37.78.146", "5060" );
+			t_unref();
+		};
+	};
+		
+}

+ 51 - 0
test/xy.cfg

@@ -0,0 +1,51 @@
+# forwarding to a fixed non-responding destination
+#
+# $Id$
+#
+
+debug=9          # debug level (cmd line: -dddddddddd)
+log_stderror=yes # (cmd line: -E)
+check_via=yes     # (cmd. line: -v)
+dns=on           # (cmd. line: -r)
+rev_dns=yes      # (cmd. line: -R)
+fork=no          # (cmd. line: -D)
+port=5080
+#listen=127.0.0.1
+listen=192.168.99.100
+loop_checks=1
+# for more info: sip_router -h
+
+#modules
+loadmodule "modules/print/print.so"
+#loadmodule "modules/tm/tm.so"
+
+route{
+	if ( t_lookup_request()) {
+		if ( method=="ACK" )	{
+			t_release();
+#			forward(195.37.77.100, 5090 );
+			forward(195.37.78.146, 5030 );
+# once it supports ACK too
+#			t_forward(195.37.77.100, 5090 );
+		} else {
+			t_retransmit_reply();
+		};
+		t_unref();
+	} else {
+		if (method=="ACK") {
+#			forward(195.37.77.100, 5090 );
+			forward(195.37.78.146, 5030 );
+		} else {
+			t_add_transaction();
+			if (method=="CANCEL") {
+				t_send_reply( "200", "glad to cancel");
+			} else {
+				t_send_reply("100", "trying -- your call is important to us");
+			};
+#			t_forward("195.37.77.100", "5090" );
+			t_forward("195.37.78.146", "5030" );
+			t_unref();
+		};
+	};
+		
+}

+ 2 - 1
timer.c

@@ -6,8 +6,9 @@
 #include "dprint.h"
 #include "error.h"
 #include "config.h"
+#include "mem/mem.h"
 #ifdef SHM_MEM
-#include "shm_mem.h"
+#include "mem/shm_mem.h"
 #endif
 
 #include <stdlib.h>

+ 13 - 5
udp_server.c

@@ -15,10 +15,10 @@
 #include "config.h"
 #include "dprint.h"
 #include "receive.h"
-#include "mem.h"
+#include "mem/mem.h"
 
 #ifdef DEBUG_DMALLOC
-#include <dmalloc.h>
+#include <mem/dmalloc.h>
 #endif
 
 int udp_sock;
@@ -151,10 +151,16 @@ error:
 int udp_rcv_loop()
 {
 	unsigned len;
+#ifdef DYN_BUF
 	char* buf;
+#else
+	char buf [BUF_SIZE+1];
+#endif
+
 	struct sockaddr* from;
 	int fromlen;
 
+
 	from=(struct sockaddr*) malloc(sizeof(struct sockaddr));
 	if (from==0){
 		LOG(L_ERR, "ERROR: udp_rcv_loop: out of memory\n");
@@ -162,12 +168,14 @@ int udp_rcv_loop()
 	}
 
 	for(;;){
+#ifdef DYN_BUF
 		buf=pkg_malloc(BUF_SIZE+1);
 		if (buf==0){
 			LOG(L_ERR, "ERROR: udp_rcv_loop: could not allocate receive"
 					 " buffer\n");
 			goto error;
 		}
+#endif
 		fromlen=sizeof(struct sockaddr);
 		len=recvfrom(udp_sock, buf, BUF_SIZE, 0, from, &fromlen);
 		if (len==-1){
@@ -203,7 +211,6 @@ int udp_send(char *buf, unsigned len, struct sockaddr*  to, unsigned tolen)
 	int n;
 
 /*	struct sockaddr_in a2;*/
-/*
 #ifndef NO_DEBUG
 #define MAX_IP_LENGTH 18
 	char ip_txt[MAX_IP_LENGTH];
@@ -227,8 +234,9 @@ int udp_send(char *buf, unsigned len, struct sockaddr*  to, unsigned tolen)
 
 	DBG(" destination: IP=%s, port=%u; packet:\n", ip_txt, p);
 	DBG(" destination (hex): IP=%x, port=%x;\n", a->sin_addr.s_addr, a->sin_port );
-	DBG("%*s\n", len, buf );
-#endif*/
+	DBG(" packet: {%*s...}\n", 24, buf );
+	/* DBG("%*s\n", len, buf ); */
+#endif
 /*
 	memset(&a2, 0, sizeof(struct sockaddr_in));
 	a2.sin_family = a->sin_family;

+ 19 - 0
ut.h

@@ -9,6 +9,25 @@
 
 #include "dprint.h"
 
+/* returns string beginning and length without insignificant chars */
+#define trim_len( _len, _begin, _mystr ) \
+	({ 	static char _c; \
+		(_len)=(_mystr).len; \
+		while ((_len) && ((_c=(_mystr).s[(_len)-1])==0 || _c=='\r' || _c=='\n' || _c==' ' || _c=='\t' )) \
+			(_len)--; \
+		(_begin)=(_mystr).s; \
+		while ((_len) && ((_c=*(_begin))==' ' || _c=='\t')) { \
+			(_len)--;\
+			(_begin)++; \
+		} \
+	})
+
+#define trim_r( _mystr ) \
+	({	static _c; \
+		while( ((_mystr).len) && ((_c=(_mystr).s[(_mystr).len-1]))==0 || _c=='\r' || _c=='\n') \
+			(_mystr).len--; \
+	})
+
 /* converts a str to an u. short, returns the u. short and sets *err on 
  * error and if err!=null
  * */

部分文件因为文件数量过多而无法显示