diff -ur orig/meta1-1.0.PreAlpha28.0/include/sm/map.h meta1-1.0.PreAlpha28.0/include/sm/map.h
--- orig/meta1-1.0.PreAlpha28.0/include/sm/map.h	2008-02-04 17:44:11.000000000 +0200
+++ meta1-1.0.PreAlpha28.0/include/sm/map.h	2009-06-02 01:28:53.000000000 +0300
@@ -92,6 +92,13 @@
 
 #define SMPO_INIT_CB	(0x0200|SMPO_PTR) /* init cb function (hash map) */
 
+/* Additional flags for sockmap spools: */
+#define SMPO_MIN_OCONN    (0x0300|SMPO_INT) /* Minimal number of open
+					       connections */
+#define SMPO_MAX_OCONN    (0x0400|SMPO_INT) /* Maximum number of open
+					       connections */
+#define SMPO_IDLE         (0x0500|SMPO_INT) /* Idle timeout */
+
 #define SMPO_TYPE_MASK	0x0000F
 #define SMPO_GET_TYPE(opt)	((opt) & SMPO_TYPE_MASK)
 
diff -ur orig/meta1-1.0.PreAlpha28.0/include/sm/mapcnf.h meta1-1.0.PreAlpha28.0/include/sm/mapcnf.h
--- orig/meta1-1.0.PreAlpha28.0/include/sm/mapcnf.h	2006-10-05 07:27:35.000000000 +0300
+++ meta1-1.0.PreAlpha28.0/include/sm/mapcnf.h	2009-06-02 01:28:53.000000000 +0300
@@ -73,6 +73,8 @@
 	ipv4_T			 mst_socket_ipv4;
 	short			 mst_socket_port;
 	sm_intvl_T		 mst_socket_tmout;
+	uint                     mst_socket_min_oconn;
+	uint                     mst_socket_max_oconn;
 };
 typedef struct mst_socket_S mst_socket_T;
 
diff -ur orig/meta1-1.0.PreAlpha28.0/include/sm/mapcnfdef.h meta1-1.0.PreAlpha28.0/include/sm/mapcnfdef.h
--- orig/meta1-1.0.PreAlpha28.0/include/sm/mapcnfdef.h	2007-05-01 07:18:32.000000000 +0300
+++ meta1-1.0.PreAlpha28.0/include/sm/mapcnfdef.h	2009-06-02 11:08:39.000000000 +0300
@@ -157,6 +157,24 @@
 	0, NULL, NULL, NULL, NULL
 	SM_LC_NO_ISSET	SM_LC_SET_MAGIC(0) },
 
+{ SM_CONF_DEF_MAGIC, "min_connections",	sm_conf_type_u32,
+	offsetof(mapspec_T, mst_socket.mst_socket_min_oconn),	sizeof(uint),
+	NULL,
+	0, NULL, NULL, NULL, NULL
+	SM_LC_NO_ISSET	SM_LC_SET_MAGIC(0) },
+	
+{ SM_CONF_DEF_MAGIC, "max_connections",	sm_conf_type_u32,
+	offsetof(mapspec_T, mst_socket.mst_socket_max_oconn),	sizeof(uint),
+	NULL,
+	0, NULL, NULL, NULL, NULL
+	SM_LC_NO_ISSET	SM_LC_SET_MAGIC(0) },
+
+{ SM_CONF_DEF_MAGIC, "timeout",	sm_conf_type_u32,
+	offsetof(mapspec_T, mst_socket.mst_socket_tmout),	sizeof(sm_intvl_T),
+	NULL,
+	0, NULL, NULL, NULL, NULL
+	SM_LC_NO_ISSET	SM_LC_SET_MAGIC(0) },
+
 /* Sentinel */
 { SM_CONF_DEF_MAGIC, NULL, 0, 0, 0, NULL, 0, NULL, NULL, NULL, NULL SM_LC_NO_ISSET	SM_LC_SET_MAGIC(0)}
 }
diff -ur orig/meta1-1.0.PreAlpha28.0/libsmmap/Makefile.am meta1-1.0.PreAlpha28.0/libsmmap/Makefile.am
--- orig/meta1-1.0.PreAlpha28.0/libsmmap/Makefile.am	2008-01-09 07:28:00.000000000 +0200
+++ meta1-1.0.PreAlpha28.0/libsmmap/Makefile.am	2009-06-01 20:45:23.000000000 +0300
@@ -83,6 +83,7 @@
 ## TESTC= t-mapipr-0 t-mapdomr-0 t-mapip-0 t-mapdom-0
 TESTC=	t-mm \
 	t-sockmapr-0 \
+        t-sockmapr-1 \
 	t-mapipr-0 \
 	t-mapdomr-0 \
 	t-mapaddrr-0 \
@@ -216,6 +217,12 @@
 t_sockmapr_0_CFLAGS=-DMTA_USE_PTHREADS $(PTHREAD_CFLAGS)
 t_sockmapr_0_LDFLAGS=$(PTHREAD_CFLAGS)
 
+t_sockmapr_1_CC		= $(PTHREAD_CC)
+t_sockmapr_1_SOURCES	= t-sockmap-1.c
+t_sockmapr_1_LDADD	= $(LDSMMAPSR) $(LDCSTRR) $(LDADDR) $(LDCHK) $(PTHREAD_LIBS) $(LDREPL)
+t_sockmapr_1_CFLAGS=-DMTA_USE_PTHREADS $(PTHREAD_CFLAGS)
+t_sockmapr_1_LDFLAGS=$(PTHREAD_CFLAGS)
+
 t_seqr_0_CC		= $(PTHREAD_CC)
 t_seqr_0_SOURCES	= t-seq-0.c
 t_seqr_0_LDADD		= $(LDSMMAPSR) $(LDCSTRR) $(LDADDR) $(LDCHK) $(PTHREAD_LIBS) $(LDREPL)
diff -ur orig/meta1-1.0.PreAlpha28.0/libsmmap/mapconfopen.c meta1-1.0.PreAlpha28.0/libsmmap/mapconfopen.c
--- orig/meta1-1.0.PreAlpha28.0/libsmmap/mapconfopen.c	2007-01-11 19:51:20.000000000 +0200
+++ meta1-1.0.PreAlpha28.0/libsmmap/mapconfopen.c	2009-06-02 01:28:53.000000000 +0300
@@ -99,6 +99,8 @@
 					md_maps->mst_socket.mst_socket_path,
 				SMPO_PORT, md_maps->mst_socket.mst_socket_port,
 				SMPO_IPV4, md_maps->mst_socket.mst_socket_ipv4,
+				SMPO_MIN_OCONN, md_maps->mst_socket.mst_socket_min_oconn,
+				SMPO_MAX_OCONN, md_maps->mst_socket.mst_socket_max_oconn,	  
 				SMPO_END);
 			break;
 
diff -ur orig/meta1-1.0.PreAlpha28.0/libsmmap/sockmap.c meta1-1.0.PreAlpha28.0/libsmmap/sockmap.c
--- orig/meta1-1.0.PreAlpha28.0/libsmmap/sockmap.c	2007-01-22 19:25:06.000000000 +0200
+++ meta1-1.0.PreAlpha28.0/libsmmap/sockmap.c	2009-06-02 01:29:21.000000000 +0300
@@ -61,32 +61,11 @@
 	return ret;
 }
 
-/*
-**  SM_SOCKMAP_SETOPT -- set options for map
-**
-**	Parameters:
-**		map -- map
-**		ap -- options
-**
-**	Returns:
-**		usual sm_error code
-*/
-
 static sm_ret_T
-sm_sockmap_setopt(sm_map_P map, va_list ap)
+__sockmap_setopt(sm_sockmap_P db, va_list ap)
 {
-	sm_ret_T ret;
-	uint k, u;
-	sm_sockmap_P db;
-
-	SM_IS_MAP(map);
-	db = (sm_sockmap_P) map->sm_map_db;
-	if (NULL == db)
-		return sm_error_perm(SM_EM_MAP, ENOENT);
-	ret = SM_SUCCESS;
-
 	for (;;) {
-		k = va_arg(ap, uint);
+		uint u, k = va_arg(ap, uint);
 		if (SMPO_END == k)
 			break;
 
@@ -105,12 +84,50 @@
 		  case SMPO_SOCKPATH:
 			db->sockmap_path = va_arg(ap, char *);
 			break;
+		  case SMPO_MIN_OCONN:
+			db->sockmap_min = va_arg(ap, uint);
+			break;
+		  case SMPO_MAX_OCONN:
+			db->sockmap_max = va_arg(ap, uint);
+			break;
+		  case SMPO_IDLE:
+			db->sockmap_idle = va_arg(ap, sm_intvl_T);
+			break;
 		  default:
 			/* silently ignore bogus options? */
 			break;
 		}
 	}
-	return ret;
+	if (db->sockmap_min < 1)
+		db->sockmap_min = 1;
+	if (db->sockmap_max < 1)
+		db->sockmap_max = 1;
+	if (db->sockmap_max < db->sockmap_min)
+		db->sockmap_max = db->sockmap_min;
+	return SM_SUCCESS;
+}
+
+/*
+**  SM_SOCKMAP_SETOPT -- set options for map
+**
+**	Parameters:
+**		map -- map
+**		ap -- options
+**
+**	Returns:
+**		usual sm_error code
+*/
+
+static sm_ret_T
+sm_sockmap_setopt(sm_map_P map, va_list ap)
+{
+	sm_sockmap_P db;
+
+	SM_IS_MAP(map);
+	db = (sm_sockmap_P) map->sm_map_db;
+	if (NULL == db)
+		return sm_error_perm(SM_EM_MAP, ENOENT);
+	return __sockmap_setopt(db, ap);
 }
 
 /*
@@ -226,28 +243,9 @@
 	db = map->sm_map_db;
 	SM_REQUIRE(db != NULL);
 	db->sockmap_name = name;
-	for (;;) {
-		k = va_arg(ap, uint);
-		if (SMPO_END == k)
-			break;
-
-		switch (k) {
-		  case SMPO_SOCKPATH:
-			db->sockmap_path = va_arg(ap, char *);
-			break;
-		  case SMPO_PORT:
-			u = va_arg(ap, int);
-			db->sockmap_port = (short) u;
-			break;
-		  case SMPO_IPV4:
-			u = va_arg(ap, ipv4_T);
-			db->sockmap_ipv4 = u;
-			break;
-		  default:
-			/* silently ignore bogus options? */
-			break;
-		}
-	}
+	ret = __sockmap_setopt(db, ap);
+	if (sm_is_err(ret))
+		goto error;
 	ret = sockmap_open(db);
 	if (sm_is_err(ret))
 		goto error;
@@ -344,11 +342,6 @@
 
 	ret = sockmap_lookup(db, key, data);
 
-	/* this doesn't look right: violation of abstraction... */
-	if (sm_is_err(ret) && NULL == db->sockmap_fp) {
-		SMMAP_CLR_FL(map, SMMAP_FL_OPEN);
-		SMMAP_SET_FL(map, SMMAP_FL_CLOSED);
-	}
 	return ret;
 }
 
@@ -416,7 +409,7 @@
 	if (NULL == htype)
 		goto error;
 
-	ret = sm_mapc_create(maps, htype, SMMAPC_FL_LCK_FULL,
+	ret = sm_mapc_create(maps, htype, 0,
 			sm_sockmap_create,
 			sm_sockmap_open,
 			sm_sockmap_close,
diff -ur orig/meta1-1.0.PreAlpha28.0/libsmmap/sockmap.h meta1-1.0.PreAlpha28.0/libsmmap/sockmap.h
--- orig/meta1-1.0.PreAlpha28.0/libsmmap/sockmap.h	2005-04-30 00:37:08.000000000 +0300
+++ meta1-1.0.PreAlpha28.0/libsmmap/sockmap.h	2009-06-02 10:39:30.000000000 +0300
@@ -22,10 +22,25 @@
 /* arbitrary limit for sanity */
 #define SOCKETMAP_MAXL 1000000
 
+struct sm_sockmap_instance_S
+{
+#if MTA_USE_PTHREADS 
+	pthread_rwlock_t rwlock;
+#endif
+	sm_file_T *fp;
+	uint busy;
+};
+
 typedef struct sm_sockmap_S sm_sockmap_T, *sm_sockmap_P;
 struct sm_sockmap_S
 {
-	sm_file_T	*sockmap_fp;
+#if MTA_USE_PTHREADS 
+	pthread_rwlock_t  sockmap_lock;
+#endif
+	struct sm_sockmap_instance_S *sockmap_inst;
+	sm_intvl_T       sockmap_min;
+	sm_intvl_T       sockmap_max;
+	sm_intvl_T       sockmap_idle;
 	const char	*sockmap_name;
 	const char	*sockmap_path;
 	ipv4_T		 sockmap_ipv4;
diff -ur orig/meta1-1.0.PreAlpha28.0/libsmmap/sockmapimpl.c meta1-1.0.PreAlpha28.0/libsmmap/sockmapimpl.c
--- orig/meta1-1.0.PreAlpha28.0/libsmmap/sockmapimpl.c	2008-02-10 06:54:39.000000000 +0200
+++ meta1-1.0.PreAlpha28.0/libsmmap/sockmapimpl.c	2009-06-02 01:14:52.000000000 +0300
@@ -19,167 +19,175 @@
 
 /* --------- socket map basic implementation ------ */
 
-/*
-**  SOCKMAP_FREE -- free socket map context
-**
-**	Parameters:
-**		db -- pointer to socket map
-**
-**	Returns:
-**		usual sm_error code
-*/
-
-sm_ret_T
-sockmap_free(sm_sockmap_P db)
-{
-	if (db != NULL)
-		sm_free_size(db, sizeof(*db));
-	return SM_SUCCESS;
-}
-
-/*
-**  SOCKMAP_NEW -- allocate socket map context
-**
-**	Parameters:
-**		pdb -- pointer to pointer to socket map
-**
-**	Returns:
-**		usual sm_error code
-*/
+#if MTA_USE_PTHREADS 
+# define SOCKMAP_LOCK_INIT(map) 					\
+	pthread_rwlock_init(&(map)->sockmap_lock, NULL)
+# define SOCKMAP_LOCK_DESTROY(map)			                \
+	pthread_rwlock_destroy(&(map)->sockmap_lock)
+# define SOCKMAP_LOCK(map) do {						\
+		int r = pthread_rwlock_wrlock(&(map)->sockmap_lock);	\
+		if (r != 0)						\
+			return sm_error_perm(SM_EM_MAP, r);		\
+	} while(0)
+# define SOCKMAP_UNLOCK(map) do {					\
+		int r = pthread_rwlock_unlock(&(map)->sockmap_lock);	\
+		if (r != 0)						\
+			return sm_error_perm(SM_EM_MAP, r);		\
+	} while(0)
+# define SOCKMAP_INST_LOCK_INIT(map, i)				\
+	pthread_rwlock_init(&(map)->sockmap_inst[i].rwlock, NULL)
+# define SOCKMAP_INST_LOCK_DESTROY(map, i)			\
+	pthread_rwlock_destroy(&(map)->sockmap_inst[i].rwlock)
+# define SOCKMAP_INST_LOCK(map, i) do {					\
+		int r = pthread_rwlock_wrlock(&(map)->sockmap_inst[i].rwlock); \
+		if (r != 0)						\
+			return sm_error_perm(SM_EM_MAP, r);		\
+	} while(0)
+# define SOCKMAP_INST_UNLOCK(map, i) do {				\
+		int r = pthread_rwlock_unlock(&(map)->sockmap_inst[i].rwlock); \
+		if (r != 0)						\
+			return sm_error_perm(SM_EM_MAP, r);		\
+	} while(0)
+#else
+# define SOCKMAP_LOCK_INIT(map)	            0
+# define SOCKMAP_LOCK_DESTROY(map)	    0
+# define SOCKMAP_LOCK(map)
+# define SOCKMAP_UNLOCK(map)
+# define SOCKMAP_INST_LOCK_INIT(map, i)	    0
+# define SOCKMAP_INST_LOCK_DESTROY(map, i)  0
+# define SOCKMAP_INST_LOCK(map, i)
+# define SOCKMAP_INST_UNLOCK(map, i)
+#endif
 
-sm_ret_T
-sockmap_new(sm_sockmap_P *pdb)
+static sm_ret_T
+sockmap_init_all_instances(sm_sockmap_P db)
 {
-	sm_sockmap_P db;
-
-	SM_REQUIRE(pdb != NULL);
-	*pdb = NULL;
-	db = sm_zalloc(sizeof(*db));
-	if (db == NULL)
+	uint i;
+	db->sockmap_inst = sm_zalloc(db->sockmap_max
+				     * sizeof(db->sockmap_inst[0]));
+	if (!db->sockmap_inst)
 		return sm_error_perm(SM_EM_MAP, ENOMEM);
-
-	/* default for socket map timeout */
-	db->sockmap_tmout = 5;
-	*pdb = db;
+	
+	for (i = 0; i < db->sockmap_max; i++)  
+		SOCKMAP_INST_LOCK_INIT(db, i);
 	return SM_SUCCESS;
 }
-
-/*
-**  SOCKMAP_DESTROY -- destroy socket map
-**
-**	Parameters:
-**		pdb -- pointer to pointer to socket map
-**
-**	Returns:
-**		usual sm_error code
-*/
-
-sm_ret_T
-sockmap_destroy(sm_sockmap_P *pdb)
-{
-	sm_ret_T ret;
-
-	SM_REQUIRE(pdb != NULL);
-	ret = SM_SUCCESS;
-	if (*pdb != NULL)
-	{
-		if ((*pdb)->sockmap_fp != NULL)
-		{
-			ret = sm_io_close((*pdb)->sockmap_fp, SM_IO_CF_NONE);
-			(*pdb)->sockmap_fp = NULL;
-		}
-		sockmap_free(*pdb);
-		*pdb = NULL;
-	}
-	return ret;
-}
-
-/*
-**  SOCKMAP_CLOSE -- close socket map
-**
-**	Parameters:
-**		db -- pointer to socket map
-**
-**	Returns:
-**		usual sm_error code
-*/
-
-sm_ret_T
-sockmap_close(sm_sockmap_P db)
+	
+static sm_ret_T
+sockmap_close_instance(sm_sockmap_P db, uint i)
 {
-	sm_ret_T ret;
-
-	ret = SM_SUCCESS;
-	if (db != NULL && db->sockmap_fp != NULL)
-	{
-		ret = sm_io_close(db->sockmap_fp, SM_IO_CF_NONE);
-		db->sockmap_fp = NULL;
+	sm_ret_T ret = SM_SUCCESS;
+	struct sm_sockmap_instance_S *smp = db->sockmap_inst + i;
+	if (smp->fp) {
+		ret = sm_io_close(smp->fp, SM_IO_CF_NONE);
+		smp->fp = NULL;
+		smp->busy = 0;
 	}
 	return ret;
 }
 
-/*
-**  SOCKMAP_OPEN -- open socket map
-**
-**	Parameters:
-**		pdb -- pointer to pointer to socket map
-**
-**	Returns:
-**		usual sm_error code
-*/
-
-sm_ret_T
-sockmap_open(sm_sockmap_P db)
+static sm_ret_T
+sockmap_open_instance(sm_sockmap_P db, uint i)
 {
-	sm_ret_T ret;
+	sm_ret_T ret = SM_SUCCESS;
+	struct sm_sockmap_instance_S *smp = db->sockmap_inst + i;
 	int fd;
 
-	SM_REQUIRE(db != NULL);
-	db->sockmap_fp = NULL;
+	if (smp->fp)
+		return SM_SUCCESS;
+			
 	if (db->sockmap_path != NULL)
-	{
 		ret = unix_client_connect(db->sockmap_path, &fd);
-	}
 	else
-	{
 		ret = net_client_connectipv4(db->sockmap_ipv4,
-				db->sockmap_port, &fd);
-	}
+					     db->sockmap_port, &fd);
 	if (sm_is_err(ret))
 		goto fail;
 	ret = sm_io_open(SmStStdiofd, (void *) &fd, SM_IO_RDWR,
-			&db->sockmap_fp, SM_IO_WHAT_END);
-	if (sm_is_err(ret))
-		goto fail;
-	ret = sm_io_setinfo(db->sockmap_fp, SM_IO_DOUBLE, NULL);
+			 &smp->fp, SM_IO_WHAT_END);
 	if (sm_is_err(ret))
 		goto fail;
-	ret = sm_io_setinfo(db->sockmap_fp, SM_IO_WHAT_TIMEOUT,
-			(void *)&db->sockmap_tmout);
+	ret = sm_io_setinfo(smp->fp, SM_IO_DOUBLE, NULL);
 	if (sm_is_err(ret))
 		goto fail;
+	ret = sm_io_setinfo(smp->fp, SM_IO_WHAT_TIMEOUT,
+			    (void *)&db->sockmap_tmout);
+	if (sm_is_err(ret)) {
+	  fail:
+		sockmap_close_instance(db, i);
+	}
 	return ret;
+}
+
+static sm_ret_T
+sockmap_find_free_instance0(sm_sockmap_P db, uint *rind)
+{
+	int i;
+	int busy_ind = -1;
+	int free_ind = -1;
+	
+	for (i = 0; i < db->sockmap_max; i++) {
+		if (db->sockmap_inst[i].fp == NULL) {
+			if (free_ind == -1)
+				free_ind = i;
+		} else if (!db->sockmap_inst[i].busy) {
+			db->sockmap_inst[i].busy = 1;
+			*rind = i;
+			return SM_SUCCESS;
+		} else if (busy_ind != -1)
+			busy_ind = i;
+	}
+	if (free_ind != -1) {
+		sm_ret_T ret = sockmap_open_instance(db, free_ind);
+		if (!sm_is_error(ret))
+			*rind = free_ind;
+		return ret;
+	}
+	if (busy_ind != 0) {
+		/* Degrading to serialized access */
+		*rind = busy_ind;
+		return SM_SUCCESS;
+	}
+	/* Giving up... */
+	return sm_error_perm(SM_EM_MAP, EAGAIN);
+}
 
-  fail:
-	/* cleanup? */
-	sockmap_close(db);
+static sm_ret_T
+sockmap_find_free_instance(sm_sockmap_P db, uint *rind)
+{
+	sm_ret_T ret;
+	int r;
+	
+	SOCKMAP_LOCK(db);
+	ret = sockmap_find_free_instance0(db, rind);
+	SOCKMAP_UNLOCK(db);
 	return ret;
 }
 
-/*
-**  SOCKMAP_LOOKUP -- lookup a key in SOCKMAP, return data if found
-**
-**	Parameters:
-**		map -- map context
-**		key -- key
-**		data -- data (output)
-**
-**	Returns:
-**		usual sm_error code
-*/
+static sm_ret_T
+sockmap_release_instance(sm_sockmap_P db, uint i)
+{
+	db->sockmap_inst[i].busy = 0;
+	return SM_SUCCESS;
+}
 
-sm_ret_T
-sockmap_lookup(sm_sockmap_P map, sm_str_P key, sm_str_P data)
+/* FIXME: Return code? */
+static void
+sockmap_close_all_instances(sm_sockmap_P db, int destr)
+{
+	uint i;
+
+	if (!db->sockmap_inst)
+		return;
+	for (i = 0; i < db->sockmap_max; i++) {
+		sockmap_close_instance(db, i);
+		if (destr)
+			SOCKMAP_INST_LOCK_DESTROY(db, i);
+	}
+}
+
+static sm_ret_T
+sockmap_instance_lookup(sm_sockmap_P map, uint i, sm_str_P key, sm_str_P data)
 {
 	sm_ret_T ret;
 	uint len, replylen;
@@ -188,12 +196,9 @@
 	char *value, *status;
 	sm_file_T *fp;
 	char statbuf[16];
-
-	SM_REQUIRE(map != NULL);
-	SM_REQUIRE(key != NULL);
-	SM_REQUIRE(data != NULL);
-
-	fp = map->sockmap_fp;
+	struct sm_sockmap_instance_S *smp = map->sockmap_inst + i;
+	
+	fp = smp->fp;
 	if (fp == NULL)
 	{
 		ret = sockmap_open(map);
@@ -282,7 +287,183 @@
 	return ret;
 
   errcl:
-	sockmap_close(map);
+	sockmap_close_instance(map, i);
+
   error:
 	return ret;
 }
+
+
+/*
+**  SOCKMAP_FREE -- free socket map context
+**
+**	Parameters:
+**		db -- pointer to socket map
+**
+**	Returns:
+**		usual sm_error code
+*/
+
+sm_ret_T
+sockmap_free(sm_sockmap_P db)
+{
+	if (db != NULL) {
+		if (db->sockmap_inst)
+			sm_free(db->sockmap_inst);
+		sm_free_size(db, sizeof(*db));
+	}
+	return SM_SUCCESS;
+}
+
+/*
+**  SOCKMAP_NEW -- allocate socket map context
+**
+**	Parameters:
+**		pdb -- pointer to pointer to socket map
+**
+**	Returns:
+**		usual sm_error code
+*/
+
+sm_ret_T
+sockmap_new(sm_sockmap_P *pdb)
+{
+	sm_sockmap_P db;
+	int rc;
+	
+	SM_REQUIRE(pdb != NULL);
+	*pdb = NULL;
+	db = sm_zalloc(sizeof(*db));
+	if (db == NULL)
+		return sm_error_perm(SM_EM_MAP, ENOMEM);
+	rc = SOCKMAP_LOCK_INIT(db);
+	if (rc) {
+		sm_free(db);
+		return sm_error_perm(SM_EM_MAP, rc);
+	}
+
+        /* default for socket map timeout */
+	db->sockmap_tmout = 5;
+	db->sockmap_min = db->sockmap_max = 1;
+	*pdb = db;
+	return SM_SUCCESS;
+}
+
+/*
+**  SOCKMAP_DESTROY -- destroy socket map
+**
+**	Parameters:
+**		pdb -- pointer to pointer to socket map
+**
+**	Returns:
+**		usual sm_error code
+*/
+
+sm_ret_T
+sockmap_destroy(sm_sockmap_P *pdb)
+{
+	sm_ret_T ret;
+
+	SM_REQUIRE(pdb != NULL);
+	ret = SM_SUCCESS;
+	if (*pdb != NULL) {
+		sockmap_close_all_instances(*pdb, 1);
+		SOCKMAP_LOCK_DESTROY(*pdb);
+		sockmap_free(*pdb);
+		*pdb = NULL;
+	}
+	return ret;
+}
+
+/*
+**  SOCKMAP_CLOSE -- close socket map
+**
+**	Parameters:
+**		db -- pointer to socket map
+**
+**	Returns:
+**		usual sm_error code
+*/
+
+sm_ret_T
+sockmap_close(sm_sockmap_P db)
+{
+	sm_ret_T ret;
+
+	ret = SM_SUCCESS;
+	sockmap_close_all_instances(db, 0);
+	return ret;
+}
+
+/*
+**  SOCKMAP_OPEN -- open socket map
+**
+**	Parameters:
+**		pdb -- pointer to pointer to socket map
+**
+**	Returns:
+**		usual sm_error code
+*/
+
+sm_ret_T
+sockmap_open(sm_sockmap_P db)
+{
+	sm_ret_T ret;
+	uint i, j;
+	
+	SM_REQUIRE(db != NULL);
+	if (!db->sockmap_inst) {
+		ret = sockmap_init_all_instances(db);
+		if (sm_is_error(ret))
+			return ret;
+	}
+
+	for (i = 0, j = 0; i < db->sockmap_max && j < db->sockmap_min; i++) {
+		if (db->sockmap_inst[i].fp == NULL) {
+			ret = sockmap_open_instance(db, i);
+			if (sm_is_error(ret))
+				break;
+		}
+		j++;
+	}
+
+	if (j > 0 && ret)
+		/* Try to continue anyway */
+		ret = SM_SUCCESS;
+
+	if (sm_is_error(ret)) {
+		/* cleanup? */
+		sockmap_close(db);
+	}
+
+	return ret;
+}
+
+/*
+**  SOCKMAP_LOOKUP -- lookup a key in SOCKMAP, return data if found
+**
+**	Parameters:
+**		map -- map context
+**		key -- key
+**		data -- data (output)
+**
+**	Returns:
+**		usual sm_error code
+*/
+
+sm_ret_T
+sockmap_lookup(sm_sockmap_P map, sm_str_P key, sm_str_P data)
+{
+	sm_ret_T ret;
+	uint ninst;
+	
+	ret = sockmap_find_free_instance(map, &ninst);
+	if (sm_is_error(ret))
+		return ret;
+	SOCKMAP_INST_LOCK(map, ninst);
+	ret = sockmap_instance_lookup(map, ninst, key, data);
+	sockmap_release_instance(map, ninst);
+	SOCKMAP_INST_UNLOCK(map, ninst);
+	return ret;
+}
+
--- /dev/null	1994-07-18 02:46:18.000000000 +0300
+++ meta1-1.0.PreAlpha28.0/libsmmap/t-sockmap-1.c	2009-06-02 01:28:53.000000000 +0300
@@ -0,0 +1,264 @@
+/*
+ * Copyright (c) 2004-2006 Sendmail, Inc. and its suppliers.
+ *	All rights reserved.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ *
+ */
+
+#include "sm/generic.h"
+SM_RCSID("@(#)$Id: t-sockmap-0.c,v 1.10 2008/02/10 06:17:29 ca Exp $")
+
+#include "sm/error.h"
+#include "sm/heap.h"
+#include "sm/memops.h"
+#include "sm/sysexits.h"
+#include "sm/test.h"
+#include "sm/maps.h"
+#include "sm/mapc.h"
+#include "sm/map.h"
+#include "sm/mapclasses.h"
+#include "sm/net.h"
+#include <stdio.h>
+#include "sm/io.h"
+
+int Verbose = 0;
+
+#define MAPC_TYPE	"socket"
+#define MAPC_NAME	"name"
+
+sm_map_P map;
+size_t test_run_count;
+
+static pthread_cond_t signal_cond = PTHREAD_COND_INITIALIZER;
+static pthread_mutex_t signal_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+struct test_tab {
+	const char *key;
+	int (*is_ok)(sm_ret_T, char **);
+	pthread_t thr;
+};
+
+int
+test_success(sm_ret_T ret, char **pret)
+{
+	return sm_is_success(ret);
+}
+
+int
+test_notfound(sm_ret_T ret, char **pret)
+{
+	if (!sm_is_err(ret))
+		*pret = "unexpected successful return";
+	if (SM_MAP_NOTFOUND != ret) {
+		*pret = smerr2txt(ret);
+		return 0;
+	}
+	return 1;
+}
+
+int
+test_err(sm_ret_T ret, char **pret)
+{
+	if (!sm_is_err(ret)) {
+		*pret = "unexpected successful return";
+		return 0;
+	}
+	return 1;
+}
+
+struct test_tab test_tab[] = {
+	{ "bastiaan.bakker@example.com", test_success },
+	{ "wolter.eldering@example.com", test_success },
+	{ "no@example.com", test_notfound },
+	{ "TIMEOUT", test_err },
+	{ "wolter.eldering@example.com", test_success },
+	{ "bastiaan.bakker@example.com", test_success },
+	{ NULL }
+};
+
+#define incr_test_run_count() test_run_count++
+
+void
+decr_test_run_count()
+{
+	if (--test_run_count == 0) {
+		pthread_mutex_lock(&signal_mutex);
+		pthread_cond_signal(&signal_cond);
+		pthread_mutex_unlock(&signal_mutex);
+	}
+}
+
+void *
+test_thr(void *data)
+{
+	const char *arg = (const char*) data;
+	sm_str_P lhs, rhs;
+	sm_ret_T ret;
+	lhs = rhs = NULL;
+
+	incr_test_run_count();
+	if (Verbose)
+		fprintf(stderr, "%s: BEGIN\n", arg);
+	lhs = sm_str_new(NULL, 256, 1024);
+	SM_ASSERT(lhs != NULL);
+
+	rhs = sm_str_new(NULL, 256, 1024);
+	SM_ASSERT(rhs != NULL);
+
+	sm_str_clr(rhs);
+	sm_str_clr(lhs);
+	sm_str_scat(lhs, arg);
+	ret = sm_map_lookup(map, SMMAP_FL_NONE, lhs, rhs);
+	if (Verbose)
+		fprintf(stderr, "%s: FINISHED: %x = %s\n",
+			arg, ret, ret == 0 ? "OK" : smerr2txt(ret));
+  error:
+	SM_STR_FREE(lhs);
+	SM_STR_FREE(rhs);
+	decr_test_run_count();
+	return (void*) ret;
+}
+
+
+static void
+testh(const char *name, const char *path, ipv4_T ipv4, short port)
+{
+	sm_ret_T ret;
+	sm_maps_P maps;
+	sm_cstr_P mtype, mname;
+	int i;
+	
+	maps = NULL;
+	mtype = mname = NULL;
+
+	ret = sm_maps_init(&maps);
+	SM_TEST(maps != NULL);
+	if (maps == NULL)
+		return;
+	SM_TEST(sm_is_success(ret));
+
+	mtype = sm_cstr_scpyn0((const uchar *)MAPC_TYPE, strlen(MAPC_TYPE));
+	SM_TEST_E(mtype != NULL);
+
+	mname = sm_cstr_scpyn0((const uchar *)MAPC_NAME, strlen(MAPC_NAME));
+	SM_TEST_E(mname != NULL);
+
+	ret = sm_sockmap_class_create(maps);
+	SM_TEST(sm_is_success(ret));
+
+	map = NULL;
+	ret = sm_map_open(maps, mname, mtype, 0, name, SMAP_MODE_RDWR, &map,
+			  SMPO_SOCKPATH, path,
+			  SMPO_PORT, port, SMPO_IPV4, ipv4,
+			  SMPO_MIN_OCONN, 1, SMPO_MAX_OCONN, 10,
+//			  SMPO_TMOUT, 3600,
+			  SMPO_END);
+	SM_TEST_E(sm_is_success(ret));
+
+	for (i = 0; test_tab[i].key; i++) 
+		pthread_create(&test_tab[i].thr, NULL, test_thr,
+			       (void*)test_tab[i].key);
+
+	pthread_mutex_lock(&signal_mutex);
+	pthread_cond_wait(&signal_cond, &signal_mutex);
+	pthread_mutex_unlock(&signal_mutex);
+
+	for (i = 0; test_tab[i].key; i++) {
+		void *val;
+		sm_ret_T ret;
+		char *text = NULL; 
+		pthread_join(test_tab[i].thr, &val);
+		ret = val;
+		if (test_tab[i].is_ok(ret, &text) == 0) {
+			fprintf(stderr, "%d: %s: FAIL", i, test_tab[i].key);
+			if (text)
+				fprintf(stderr, " %s", text);
+			fprintf(stderr, "\n");
+		} else
+			fprintf(stderr, "%d: %s: OK\n", i, test_tab[i].key);
+	}
+	
+	ret = sm_map_close(map, 0);
+	SM_TEST(sm_is_success(ret));
+
+	ret = sm_maps_term(maps);
+	SM_TEST(sm_is_success(ret));
+	SM_CSTR_FREE(mtype);
+	SM_CSTR_FREE(mname);
+	return;
+
+  error:
+	sm_maps_term(maps);
+}
+
+#define T_SM_NAME "virtuser"
+#define T_SM_PATH NULL
+#define T_SM_PORT 1289
+#define T_SM_IP 0x7F000001
+
+static sm_ret_T
+usage(const char *prg)
+{
+	sm_io_fprintf(smioerr, "usage: %s [options]\n"
+		"-a ip     IPv4 address [%#x]\n"
+		"-N name   name of map [%s]\n"
+		"-P path   path [%s]\n"
+		"-p port   port [%d]\n"
+		"-v\n"      
+		, prg
+		, T_SM_IP
+		, T_SM_NAME
+		, T_SM_PATH
+		, T_SM_PORT
+		);
+	return EX_USAGE;
+}
+
+int
+main(int argc, char *argv[])
+{
+	char *path, *name, *prg;
+	ipv4_T ipv4;
+	short port;
+	int c;
+
+	prg = argv[0];
+	path = NULL;
+	name = "virtuser";
+	port = 1289;
+	ipv4 = ntohl(0x7F000001);
+	while ((c = getopt(argc, argv, "a:N:P:p:H:s:V")) != -1)
+	{
+		switch (c)
+		{
+		  case 'a':
+			ipv4 = (ipv4_T) atol(optarg);
+			break;
+		  case 'N':
+			name = strdup(optarg);
+			if (name == NULL)
+				return 1;
+			break;
+		  case 'P':
+			path = strdup(optarg);
+			if (path == NULL)
+				return 1;
+			break;
+		  case 'p':
+			port = atoi(optarg);
+			break;
+		  case 'V':
+			Verbose++;
+			break;
+		  default:
+			usage(prg);
+			return EX_USAGE;
+		}
+	}
+	sm_test_begin(argc, argv, "test socket map 0");
+	testh(name, path, ipv4, port);
+	return sm_test_end();
+}
--- orig/meta1-1.0.PreAlpha28.0/doc/README.tex	2009-01-02 05:48:13.000000000 +0200
+++ meta1-1.0.PreAlpha28.0/doc/README.tex	2009-06-03 10:14:42.000000000 +0300
@@ -1610,6 +1610,19 @@
 port for inet socket
 (type {\tt socket} only).
 \item
+\verb|min_connections|\index{min_connections}:
+minumum number of simultaneously open connections
+(type {\tt socket} only). Default is 1. See below for
+a description.
+\item
+\verb|max_connections|\index{min_connections}:
+maximum number of simultaneously open connections
+(type {\tt socket} only).
+\item
+\verb|timeout|\index{timeout}:
+time to wait for the answer from the socket map.
+Default is 5 seconds (type {\tt socket} only).
+\item
 \verb|maps|\index{maps}:
 list of map names to use in the map
 (type {\tt sequence} only).
@@ -1619,7 +1632,7 @@
 for {\tt socket} maps either
 a Unix domain socket (\verb|path|)
 or an inet socket (\verb|address| and \verb|port|)
-must be specified.
+must be specified. 
 
 Example:
 
@@ -1631,6 +1644,30 @@
 map seq2 { type = sequence; maps = { password, otherusers }; }
 \end{verbatim}
 
+By default, SMAR opens a single connection to each socket map
+configured, which means that accesses to this map are serialized. As a
+result, each incoming connection waits in a queue while all
+previous connections are served. This may create intolerable
+delays on loaded servers. To avoid this, use {\tt min\_connections}
+and {\tt max\_connections} to create a spool of map connections, e.g.:
+
+\begin{verbatim}
+map userdb {
+    type = socket;
+    path = "/var/spool/meta1/smap/userdb";
+    mapname = userdb;
+    min_connections = 10;
+    max_connections = 512;
+}
+\end{verbatim}
+
+This way, when a need to consult the map arrives, SMAR looks
+for the first free connection among the opened ones and uses it. If
+there are no more free connections and the number of opened
+connections is less than {\tt max\_connections}, SMAR will create a
+new connection to serve the request. Otherwise, if the maximum number
+of connections is reached, SMAR will wait until any of them becomes
+available again.
 
 \subsection{Configuration Options for SMAR}
 \label{ConfigurationOptionsforSMAR}
