You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@httpd.apache.org by Jim Jagielski <ji...@jaguNET.com> on 2001/08/24 20:44:52 UTC
[PATCH] V2: Accept Mutex Runtime Option
Here's the 2nd, improved version of the make-accept-mutex-method-runtime
patch. It includes changes suggested... Tested under Solaris and OS X.
Please check out...
Index: src/PORTING
===================================================================
RCS file: /home/cvs/apache-1.3/src/PORTING,v
retrieving revision 1.34
diff -u -r1.34 PORTING
--- src/PORTING 1999/12/07 12:19:48 1.34
+++ src/PORTING 2001/08/24 18:26:31
@@ -244,6 +244,7 @@
USE_MMAP_FILES:
Enable the use of mmap() for sending static files. If HAVE_MMAP
is not #defined, this will automatically be unset.
+--
USE_*_SERIALIZED_ACCEPT:
See htdocs/manual/misc/perf-tuning.html for an in-depth discussion of
@@ -251,6 +252,10 @@
between children entering accept(). A complete port should define one
of these, many may work and it's worthwhile timing them. Without these
the server will not implement multiple Listen directives reliably.
+ Please note that as of 1.3.21, we can set the method at runtime.
+ To so do, we specify which methods are available at compile time
+ with the HAVE_FOO_SERIALIZED_ACCEPT #defines. The USE_FOO_SERIALIZED_ACCEPT
+ is used to pick the default version of all those available.
USE_FCNTL_SERIALIZED_ACCEPT:
Use fcntl() to implement the semaphore.
Index: src/include/ap_config.h
===================================================================
RCS file: /home/cvs/apache-1.3/src/include/ap_config.h,v
retrieving revision 1.309
diff -u -r1.309 ap_config.h
--- src/include/ap_config.h 2001/06/22 12:43:53 1.309
+++ src/include/ap_config.h 2001/08/24 18:26:34
@@ -118,7 +118,7 @@
typedef int rlim_t;
#define JMP_BUF sigjmp_buf
#define NO_LONG_DOUBLE
-#define USE_FLOCK_SERIALIZED_ACCEPT
+#define HAVE_FLOCK_SERIALIZED_ACCEPT
#define _BSD_SOURCE
#define EAGAIN EWOULDBLOCK
int initgroups (char *, int);
@@ -154,7 +154,7 @@
#define S_IREAD S_IRUSR
#define S_IWRITE S_IWUSR
#define PF_INET AF_INET
-#define USE_FCNTL_SERIALIZED_ACCEPT
+#define HAVE_FCNTL_SERIALIZED_ACCEPT
#elif defined(SUNOS4)
#define HAVE_GMTOFF 1
@@ -170,7 +170,7 @@
typedef int rlim_t;
#define memmove(a,b,c) bcopy(b,a,c)
#define NO_LINGCLOSE
-#define USE_FLOCK_SERIALIZED_ACCEPT
+#define HAVE_FLOCK_SERIALIZED_ACCEPT
#define NEED_DIFFTIME
#define HAVE_SYSLOG 1
@@ -179,6 +179,8 @@
#define NO_KILLPG
#undef NO_SETSID
#define bzero(a,b) memset(a,0,b)
+#define HAVE_SYSVSEM_SERIALIZED_ACCEPT
+#define HAVE_FCNTL_SERIALIZED_ACCEPT
#if !defined(USE_SYSVSEM_SERIALIZED_ACCEPT) && \
!defined(USE_PTHREAD_SERIALIZED_ACCEPT)
#define USE_FCNTL_SERIALIZED_ACCEPT
@@ -197,6 +199,10 @@
* there's some weird conflict with non-BSD signals */
#define NO_KILLPG
#undef NO_SETSID
+#define HAVE_FLOCK_SERIALIZED_ACCEPT
+#define HAVE_FCNTL_SERIALIZED_ACCEPT
+#define HAVE_USLOCK_SERIALIZED_ACCEPT
+#define HAVE_SYSVSEM_SERIALIZED_ACCEPT
#if !defined(USE_FLOCK_SERIALIZED_ACCEPT) && \
!defined(USE_USLOCK_SERIALIZED_ACCEPT) && \
!defined(USE_SYSVSEM_SERIALIZED_ACCEPT)
@@ -226,7 +232,7 @@
#undef HAVE_GMTOFF
#define NO_KILLPG
#undef NO_SETSID
-#define USE_FCNTL_SERIALIZED_ACCEPT
+#define HAVE_FCNTL_SERIALIZED_ACCEPT
#ifndef _HPUX_SOURCE
#define _HPUX_SOURCE
#endif
@@ -245,12 +251,12 @@
#define HAVE_SHMGET
#define USE_SHMGET_SCOREBOARD
#undef HAVE_GMTOFF
-#define USE_FCNTL_SERIALIZED_ACCEPT
+#define HAVE_FCNTL_SERIALIZED_ACCEPT
/* feeling brave? want to try using POSIX mutexes? */
/* #define HAVE_MMAP */
/* #define USE_MMAP_SCOREBOARD */
/* #define USE_MMAP_FILES */
-/* #define USE_PTHREAD_SERIALIZED_ACCEPT */
+/* #define HAVE_PTHREAD_SERIALIZED_ACCEPT */
#define NO_KILLPG
#undef NO_SETSID
#define HAVE_SYSLOG
@@ -274,6 +280,7 @@
#ifdef NEED_RLIM_T
typedef int rlim_t;
#endif
+#define HAVE_FCNTL_SERIALIZED_ACCEPT
#if !defined(USE_PTHREAD_SERIALIZED_ACCEPT)
#define USE_FCNTL_SERIALIZED_ACCEPT
#endif
@@ -307,7 +314,7 @@
#define USE_MMAP_FILES
#define NO_LONG_DOUBLE
#define HAVE_SYSLOG 1
-#define USE_FLOCK_SERIALIZED_ACCEPT
+#define HAVE_FLOCK_SERIALIZED_ACCEPT
#define SINGLE_LISTEN_UNSERIALIZED_ACCEPT
#elif defined(PARAGON)
@@ -329,7 +336,7 @@
#define HAVE_SYSLOG 1
#define USE_MMAP_FILES 1
#define USE_MMAP_SCOREBOARD 1
-#define USE_FCNTL_SERIALIZED_ACCEPT 1
+#define HAVE_FCNTL_SERIALIZED_ACCEPT 1
#define JMP_BUF sigjmp_buf
#undef NO_SETSID
#if SEQUENT < 40
@@ -432,6 +439,9 @@
#define HAVE_SNPRINTF
#define JMP_BUF jmp_buf
#define USE_LONGJMP
+#define HAVE_FLOCK_SERIALIZED_ACCEPT
+#define HAVE_FCNTL_SERIALIZED_ACCEPT
+#define HAVE_NONE_SERIALIZED_ACCEPT
#define USE_FLOCK_SERIALIZED_ACCEPT
#define SINGLE_LISTEN_UNSERIALIZED_ACCEPT
@@ -475,7 +485,8 @@
* in new-httpd archives for performance numbers indicating these
* are the right choices for linux 2.2.x and later
*/
-#define USE_SYSVSEM_SERIALIZED_ACCEPT
+#define HAVE_SYSVSEM_SERIALIZED_ACCEPT
+#define HAVE_FCNTL_SERIALIZED_ACCEPT
#define SINGLE_LISTEN_UNSERIALIZED_ACCEPT
#include <sys/sem.h>
#if _SEM_SEMUN_UNDEFINED
@@ -515,7 +526,7 @@
#elif defined(SCO5)
-#define USE_FCNTL_SERIALIZED_ACCEPT
+#define HAVE_FCNTL_SERIALIZED_ACCEPT
#define HAVE_MMAP 1
#define USE_MMAP_SCOREBOARD
#define USE_MMAP_FILES
@@ -547,7 +558,7 @@
#undef NO_SETSID
#define NEED_STRDUP
/* fcntl() locking is expensive with NFS */
-#define USE_FLOCK_SERIALIZED_ACCEPT
+#define HAVE_FLOCK_SERIALIZED_ACCEPT
#define SINGLE_LISTEN_UNSERIALIZED_ACCEPT
#define HAVE_SHMGET 1
#define USE_SHMGET_SCOREBOARD
@@ -576,6 +587,7 @@
#endif /* MPRAS */
#define bzero(a,b) memset(a,0,b)
/* A lot of SVR4 systems need this */
+#define HAVE_FCNTL_SERIALIZED_ACCEPT
#ifndef USE_SYSVSEM_SERIALIZED_ACCEPT
#define USE_FCNTL_SERIALIZED_ACCEPT
#endif
@@ -593,7 +605,7 @@
#elif defined(UW)
#if UW < 700
-#define USE_FCNTL_SERIALIZED_ACCEPT
+#define HAVE_FCNTL_SERIALIZED_ACCEPT
#define NO_LINGCLOSE
#define NO_KILLPG
#endif
@@ -627,7 +639,7 @@
#endif
#define bzero(a,b) memset(a,0,b)
/* A lot of SVR4 systems need this */
-#define USE_FCNTL_SERIALIZED_ACCEPT
+#define HAVE_FCNTL_SERIALIZED_ACCEPT
#define ap_inet_addr inet_network
#define HAVE_SYSLOG 1
@@ -646,7 +658,7 @@
#define HAVE_MMAP 1
#define USE_MMAP_SCOREBOARD
#define USE_MMAP_FILES
-#define USE_FLOCK_SERIALIZED_ACCEPT
+#define HAVE_FLOCK_SERIALIZED_ACCEPT
#define SINGLE_LISTEN_UNSERIALIZED_ACCEPT
#elif defined(UTS21)
@@ -700,7 +712,7 @@
(defined(__FreeBSD_version) && (__FreeBSD_version < 220000))
typedef quad_t rlim_t;
#endif
-#define USE_FLOCK_SERIALIZED_ACCEPT
+#define HAVE_FLOCK_SERIALIZED_ACCEPT
#define SINGLE_LISTEN_UNSERIALIZED_ACCEPT
#define HAVE_SYSLOG 1
#define SYS_SIGLIST sys_siglist
@@ -726,7 +738,7 @@
#include <unix.h>
#define HAVE_MMAP 1
#define USE_POSIX_SCOREBOARD
-#define USE_FLOCK_SERIALIZED_ACCEPT
+#define HAVE_FLOCK_SERIALIZED_ACCEPT
#define SINGLE_LISTEN_UNSERIALIZED_ACCEPT
#define HAVE_SYSLOG 1
@@ -734,8 +746,9 @@
#undef HAVE_GMTOFF
#undef USE_MMAP_SCOREBOARD
#undef USE_SHMGET_SCOREBOARD
-#undef USE_FCNTL_SERIALIZED_ACCEPT
-#undef USE_FLOCK_SERIALIZED_ACCEPT
+#undef HAVE_FCNTL_SERIALIZED_ACCEPT
+#undef HAVE_FLOCK_SERIALIZED_ACCEPT
+#define HAVE_NONE_SERIALIZED_ACCEPT
#define USE_LONGJMP
#undef NO_KILLPG
#undef NO_SETSID
@@ -753,7 +766,7 @@
#define NO_KILLPG
#undef NO_SETSID
#define bzero(a,b) memset(a,0,b)
-#define USE_FCNTL_SERIALIZED_ACCEPT
+#define HAVE_FCNTL_SERIALIZED_ACCEPT
#define HAVE_MMAP 1
#define USE_MMAP_SCOREBOARD
#define USE_MMAP_FILES
@@ -774,7 +787,7 @@
#define MAXSOCKETS 2048
#define USE_OS2_SCOREBOARD
#define NO_RELIABLE_PIPED_LOGS
-#define USE_OS2SEM_SERIALIZED_ACCEPT
+#define HAVE_OS2SEM_SERIALIZED_ACCEPT
#define SINGLE_LISTEN_UNSERIALIZED_ACCEPT
#define NO_SLACK
#define FOPEN_REQUIRES_T
@@ -788,7 +801,7 @@
#ifndef __MACHTEN_68K__
#define __MACHTEN_68K__
#endif
-#define USE_FLOCK_SERIALIZED_ACCEPT
+#define HAVE_FLOCK_SERIALIZED_ACCEPT
#define NO_USE_SIGACTION
#define JMP_BUF sigjmp_buf
#define USE_LONGJMP
@@ -796,7 +809,7 @@
#else
#define HAVE_SHMGET 1
#define USE_SHMGET_SCOREBOARD
-#define USE_FCNTL_SERIALIZED_ACCEPT
+#define HAVE_FCNTL_SERIALIZED_ACCEPT
#endif
/* Convex OS v11 */
@@ -821,7 +834,7 @@
#undef NO_SETSID
#define HAVE_SHMGET 1
#define USE_SHMGET_SCOREBOARD
-#define USE_FCNTL_SERIALIZED_ACCEPT
+#define HAVE_FCNTL_SERIALIZED_ACCEPT
#define HAVE_SYSLOG 1
#elif defined(NEWSOS)
@@ -909,7 +922,7 @@
#define USE_SHMGET_SCOREBOARD
/*#define USE_TPF_SCOREBOARD*/
#define USE_TPF_ACCEPT
-#define USE_TPF_CORE_SERIALIZED_ACCEPT
+#define HAVE_TPF_CORE_SERIALIZED_ACCEPT
#define USE_TPF_SELECT
#define S_IREAD S_IRUSR
#define S_IWRITE S_IWUSR
@@ -931,7 +944,7 @@
#define USE_SHMGET_SCOREBOARD
#define USE_MMAP_FILES
#define NEED_UNION_SEMUN
-#define USE_SYSVSEM_SERIALIZED_ACCEPT
+#define HAVE_SYSVSEM_SERIALIZED_ACCEPT
#define _POSIX_SOURCE
#include <signal.h>
#ifdef SIGDUMP /* SIGDUMP is not defined by OS/390 v1r2 */
@@ -1157,6 +1170,32 @@
#if defined(USE_SHMGET_SCOREBOARD) && (defined(NO_SHMGET) || !defined(HAVE_SHMGET))
#undef USE_SHMGET_SCOREBOARD
+#endif
+
+/* A USE_FOO_SERIALIZED_ACCEPT implies a HAVE_FOO_SERIALIZED_ACCEPT */
+#if defined(USE_USLOCK_SERIALIZED_ACCEPT) && !defined(HAVE_USLOCK_SERIALIZED_ACCEPT)
+#define HAVE_USLOCK_SERIALIZED_ACCEPT
+#endif
+#if defined(USE_PTHREAD_SERIALIZED_ACCEPT) && !defined(HAVE_PTHREAD_SERIALIZED_ACCEPT)
+#define HAVE_PTHREAD_SERIALIZED_ACCEPT
+#endif
+#if defined(USE_SYSVMEM_SERIALIZED_ACCEPT) && !defined(HAVE_SYSVMEM_SERIALIZED_ACCEPT)
+#define HAVE_SYSVMEM_SERIALIZED_ACCEPT
+#endif
+#if defined(USE_FCNTL_SERIALIZED_ACCEPT) && !defined(HAVE_FCNTL_SERIALIZED_ACCEPT)
+#define HAVE_FCNTL_SERIALIZED_ACCEPT
+#endif
+#if defined(USE_FLOCK_SERIALIZED_ACCEPT) && !defined(HAVE_FLOCK_SERIALIZED_ACCEPT)
+#define HAVE_FLOCK_SERIALIZED_ACCEPT
+#endif
+#if defined(USE_OS2SEM_SERIALIZED_ACCEPT) && !defined(HAVE_OS2SEM_SERIALIZED_ACCEPT)
+#define HAVE_OS2SEM_SERIALIZED_ACCEPT
+#endif
+#if defined(USE_TPF_CORE_SERIALIZED_ACCEPT) && !defined(HAVE_TPF_CORE_SERIALIZED_ACCEPT)
+#define HAVE_TPF_CORE_SERIALIZED_ACCEPT
+#endif
+#if defined(USE_NONE_SERIALIZED_ACCEPT) && !defined(HAVE_NONE_SERIALIZED_ACCEPT)
+#define HAVE_NONE_SERIALIZED_ACCEPT
#endif
#ifndef LOGNAME_MAX
Index: src/include/http_main.h
===================================================================
RCS file: /home/cvs/apache-1.3/src/include/http_main.h,v
retrieving revision 1.36
diff -u -r1.36 http_main.h
--- src/include/http_main.h 2001/01/15 17:04:34 1.36
+++ src/include/http_main.h 2001/08/24 18:26:34
@@ -130,6 +130,11 @@
void setup_signal_names(char *prefix);
+/* functions for determination and setting of accept() mutexing */
+char *default_mutex_method(void);
+char *init_mutex_method(char *t);
+char *init_single_listen(int flag);
+
#ifndef NO_OTHER_CHILD
/*
* register an other_child -- a child which the main loop keeps track of
Index: src/lib/sdbm/sdbm_lock.c
===================================================================
RCS file: /home/cvs/apache-1.3/src/lib/sdbm/sdbm_lock.c,v
retrieving revision 1.1
diff -u -r1.1 sdbm_lock.c
--- src/lib/sdbm/sdbm_lock.c 2000/09/21 13:06:14 1.1
+++ src/lib/sdbm/sdbm_lock.c 2001/08/24 18:26:35
@@ -9,12 +9,13 @@
/* The locking support:
* Try to determine whether we should use fcntl() or flock().
* Would be better ap_config.h could provide this... :-(
+ * Small monkey business to ensure that fcntl is preferred,
+ * unless we specified USE_FLOCK_SERIALIZED_ACCEPT during compile.
*/
-#if defined(USE_FCNTL_SERIALIZED_ACCEPT)
+#if defined(HAVE_FCNTL_SERIALIZED_ACCEPT) && !defined(USE_FLOCK_SERIALIZED_ACCEPT)
#define USE_FCNTL 1
#include <fcntl.h>
-#endif
-#if defined(USE_FLOCK_SERIALIZED_ACCEPT)
+#elif defined(HAVE_FLOCK_SERIALIZED_ACCEPT)
#define USE_FLOCK 1
#include <sys/file.h>
#endif
Index: src/main/http_core.c
===================================================================
RCS file: /home/cvs/apache-1.3/src/main/http_core.c,v
retrieving revision 1.296
diff -u -r1.296 http_core.c
--- src/main/http_core.c 2001/07/13 07:32:44 1.296
+++ src/main/http_core.c 2001/08/24 18:26:43
@@ -1135,6 +1135,14 @@
}
return NULL;
}
+static const char *set_accept_mutex(cmd_parms *cmd, void *dummy, char *arg)
+{
+ return init_mutex_method(arg);
+}
+static const char *set_single_listen(cmd_parms *cmd, void *dummy, int flag)
+{
+ return init_single_listen(flag ? 1 : 0);
+}
static const char *set_document_root(cmd_parms *cmd, void *dummy, char *arg)
{
@@ -3215,6 +3223,37 @@
(void*)XtOffsetOf(core_dir_config, limit_req_body),
OR_ALL, TAKE1,
"Limit (in bytes) on maximum size of request message body" },
+{ "AcceptMutex", set_accept_mutex, NULL, RSRC_CONF, TAKE1,
+ "Serialized Accept Mutex; the methods "
+#ifdef HAVE_FCNTL_SERIALIZED_ACCEPT
+ "'fcntl' "
+#endif
+#ifdef HAVE_FLOCK_SERIALIZED_ACCEPT
+ "'flock' "
+#endif
+#ifdef HAVE_USLOCK_SERIALIZED_ACCEPT
+ "'uslock "
+#endif
+#ifdef HAVE_SYSVSEM_SERIALIZED_ACCEPT
+ "'sysvsem' "
+#endif
+#ifdef HAVE_PTHREAD_SERIALIZED_ACCEPT
+ "'pthread' "
+#endif
+#ifdef HAVE_OS2SEM_SERIALIZED_ACCEPT
+ "'os2sem' "
+#endif
+#ifdef HAVE_TPF_CORE_SERIALIZED_ACCEPT
+ "'tpfcore' "
+#endif
+#ifdef HAVE_NONE_SERIALIZED_ACCEPT
+ "'none' "
+#endif
+ "are compiled in"
+},
+{ "SingleListen",set_single_listen,NULL,RSRC_CONF,FLAG,
+ "On - to serialize accept and Off to unserialize accept"
+},
/* EBCDIC Conversion directives: */
#ifdef CHARSET_EBCDIC
Index: src/main/http_main.c
===================================================================
RCS file: /home/cvs/apache-1.3/src/main/http_main.c,v
retrieving revision 1.546
diff -u -r1.546 http_main.c
--- src/main/http_main.c 2001/08/10 01:14:29 1.546
+++ src/main/http_main.c 2001/08/24 18:26:57
@@ -258,6 +258,27 @@
time_t ap_restart_time=0;
API_VAR_EXPORT int ap_suexec_enabled = 0;
int ap_listenbacklog=0;
+/* On some architectures it's safe to do unserialized accept()s in the single
+ * Listen case. But it's never safe to do it in the case where there's
+ * multiple Listen statements. Define SINGLE_LISTEN_UNSERIALIZED_ACCEPT
+ * when it's safe in the single Listen case.
+ */
+#ifdef SINGLE_LISTEN_UNSERIALIZED_ACCEPT
+static int ap_single_listen = 1;
+#else
+static int ap_single_listen = 0;
+#endif
+
+struct accept_mutex_methods_s {
+ void (*child_init)(pool *p);
+ void (*init)(pool *p);
+ void (*on)(void);
+ void (*off)(void);
+ char *name;
+};
+typedef struct accept_mutex_methods_s accept_mutex_methods_s;
+accept_mutex_methods_s *amutex;
+
#ifdef SO_ACCEPTFILTER
int ap_acceptfilter =
#ifdef AP_ACCEPTFILTER_OFF
@@ -502,8 +523,21 @@
chdir_for_gprof();
exit(code);
}
+
+/*
+ * Start of accept() mutex fluff:
+ * Concept: Each method has it's own distinct set of mutex functions,
+ * which it shoves in a nice struct for us. We then pick
+ * which struct to use. We tell Apache which methods we
+ * support via HAVE_FOO_SERIALIZED_ACCEPT. We can
+ * specify the default via USE_FOO_SERIALIZED_ACCEPT
+ * (this pre-1.3.21 builds which use that at the command-
+ * line during builds work as expected). Without a set
+ * method, we pick the 1st from the following order:
+ * uslock, pthread, sysvmem, fcntl, flock, os2sem, tpfcore and none.
+ */
-#if defined(USE_FCNTL_SERIALIZED_ACCEPT) || defined(USE_FLOCK_SERIALIZED_ACCEPT)
+#if defined(HAVE_FCNTL_SERIALIZED_ACCEPT) || defined(HAVE_FLOCK_SERIALIZED_ACCEPT)
static void expand_lock_fname(pool *p)
{
/* XXXX possibly bogus cast */
@@ -512,15 +546,13 @@
}
#endif
-#if defined (USE_USLOCK_SERIALIZED_ACCEPT)
-
+#if defined (HAVE_USLOCK_SERIALIZED_ACCEPT)
#include <ulocks.h>
-
static ulock_t uslock = NULL;
-#define accept_mutex_child_init(x)
+#define accept_mutex_child_init_uslock(x)
-static void accept_mutex_init(pool *p)
+static void accept_mutex_init_uslock(pool *p)
{
ptrdiff_t old;
usptr_t *us;
@@ -551,7 +583,7 @@
}
}
-static void accept_mutex_on(void)
+static void accept_mutex_on_uslock(void)
{
switch (ussetlock(uslock)) {
case 1:
@@ -566,15 +598,24 @@
}
}
-static void accept_mutex_off(void)
+static void accept_mutex_off_uslock(void)
{
if (usunsetlock(uslock) == -1) {
perror("usunsetlock");
clean_child_exit(APEXIT_CHILDFATAL);
}
}
+
+accept_mutex_methods_s accept_mutex_uslock_s = {
+ NULL,
+ accept_mutex_init_uslock,
+ accept_mutex_on_uslock,
+ accept_mutex_off_uslock,
+ "uslock"
+};
+#endif
-#elif defined (USE_PTHREAD_SERIALIZED_ACCEPT)
+#if defined (HAVE_PTHREAD_SERIALIZED_ACCEPT)
/* This code probably only works on Solaris ... but it works really fast
* on Solaris. Note that pthread mutexes are *NOT* released when a task
@@ -589,7 +630,7 @@
static sigset_t accept_block_mask;
static sigset_t accept_previous_mask;
-static void accept_mutex_child_cleanup(void *foo)
+static void accept_mutex_child_cleanup_pthread(void *foo)
{
if (accept_mutex != (void *)(caddr_t)-1
&& have_accept_mutex) {
@@ -597,12 +638,12 @@
}
}
-static void accept_mutex_child_init(pool *p)
+static void accept_mutex_child_init_pthread(pool *p)
{
- ap_register_cleanup(p, NULL, accept_mutex_child_cleanup, ap_null_cleanup);
+ ap_register_cleanup(p, NULL, accept_mutex_child_cleanup_pthread, ap_null_cleanup);
}
-static void accept_mutex_cleanup(void *foo)
+static void accept_mutex_cleanup_pthread(void *foo)
{
if (accept_mutex != (void *)(caddr_t)-1
&& munmap((caddr_t) accept_mutex, sizeof(*accept_mutex))) {
@@ -611,7 +652,7 @@
accept_mutex = (void *)(caddr_t)-1;
}
-static void accept_mutex_init(pool *p)
+static void accept_mutex_init_pthread(pool *p)
{
pthread_mutexattr_t mattr;
int fd;
@@ -645,10 +686,10 @@
sigdelset(&accept_block_mask, SIGHUP);
sigdelset(&accept_block_mask, SIGTERM);
sigdelset(&accept_block_mask, SIGUSR1);
- ap_register_cleanup(p, NULL, accept_mutex_cleanup, ap_null_cleanup);
+ ap_register_cleanup(p, NULL, accept_mutex_cleanup_pthread, ap_null_cleanup);
}
-static void accept_mutex_on(void)
+static void accept_mutex_on_pthread(void)
{
int err;
@@ -670,7 +711,7 @@
ap_unblock_alarms();
}
-static void accept_mutex_off(void)
+static void accept_mutex_off_pthread(void)
{
int err;
@@ -692,8 +733,17 @@
}
}
-#elif defined (USE_SYSVSEM_SERIALIZED_ACCEPT)
+accept_mutex_methods_s accept_mutex_pthread_s = {
+ accept_mutex_child_init_pthread,
+ accept_mutex_init_pthread,
+ accept_mutex_on_pthread,
+ accept_mutex_off_pthread,
+ "pthread"
+};
+#endif
+#if defined (HAVE_SYSVSEM_SERIALIZED_ACCEPT)
+
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
@@ -716,7 +766,7 @@
* means we have to be sure to clean this up or else we'll leak
* semaphores.
*/
-static void accept_mutex_cleanup(void *foo)
+static void accept_mutex_cleanup_sysvsem(void *foo)
{
union semun ick;
@@ -727,9 +777,9 @@
semctl(sem_id, 0, IPC_RMID, ick);
}
-#define accept_mutex_child_init(x)
+#define accept_mutex_child_init_sysvsem(x)
-static void accept_mutex_init(pool *p)
+static void accept_mutex_init_sysvsem(pool *p)
{
union semun ick;
struct semid_ds buf;
@@ -758,7 +808,7 @@
exit(APEXIT_INIT);
}
}
- ap_register_cleanup(p, NULL, accept_mutex_cleanup, ap_null_cleanup);
+ ap_register_cleanup(p, NULL, accept_mutex_cleanup_sysvsem, ap_null_cleanup);
/* pre-initialize these */
op_on.sem_num = 0;
@@ -769,7 +819,7 @@
op_off.sem_flg = SEM_UNDO;
}
-static void accept_mutex_on(void)
+static void accept_mutex_on_sysvsem(void)
{
while (semop(sem_id, &op_on, 1) < 0) {
if (errno != EINTR) {
@@ -779,7 +829,7 @@
}
}
-static void accept_mutex_off(void)
+static void accept_mutex_off_sysvsem(void)
{
while (semop(sem_id, &op_off, 1) < 0) {
if (errno != EINTR) {
@@ -789,19 +839,28 @@
}
}
-#elif defined(USE_FCNTL_SERIALIZED_ACCEPT)
+accept_mutex_methods_s accept_mutex_sysvmem_s = {
+ NULL,
+ accept_mutex_init_sysvmem,
+ accept_mutex_on_sysvmem,
+ accept_mutex_off_sysvmem,
+ "sysvmem"
+};
+#endif
+
+#if defined(HAVE_FCNTL_SERIALIZED_ACCEPT)
static struct flock lock_it;
static struct flock unlock_it;
static int lock_fd = -1;
-#define accept_mutex_child_init(x)
+#define accept_mutex_child_init_fcntl(x)
/*
* Initialize mutex lock.
* Must be safe to call this on a restart.
*/
-static void accept_mutex_init(pool *p)
+static void accept_mutex_init_fcntl(pool *p)
{
lock_it.l_whence = SEEK_SET; /* from current point */
@@ -825,7 +884,7 @@
unlink(ap_lock_fname);
}
-static void accept_mutex_on(void)
+static void accept_mutex_on_fcntl(void)
{
int ret;
@@ -842,7 +901,7 @@
}
}
-static void accept_mutex_off(void)
+static void accept_mutex_off_fcntl(void)
{
int ret;
@@ -857,12 +916,21 @@
clean_child_exit(APEXIT_CHILDFATAL);
}
}
+
+accept_mutex_methods_s accept_mutex_fcntl_s = {
+ NULL,
+ accept_mutex_init_fcntl,
+ accept_mutex_on_fcntl,
+ accept_mutex_off_fcntl,
+ "fcntl"
+};
+#endif
-#elif defined(USE_FLOCK_SERIALIZED_ACCEPT)
+#if defined(HAVE_FLOCK_SERIALIZED_ACCEPT)
-static int lock_fd = -1;
+static int flock_fd = -1;
-static void accept_mutex_cleanup(void *foo)
+static void accept_mutex_cleanup_flock(void *foo)
{
unlink(ap_lock_fname);
}
@@ -871,11 +939,11 @@
* Initialize mutex lock.
* Done by each child at it's birth
*/
-static void accept_mutex_child_init(pool *p)
+static void accept_mutex_child_init_flock(pool *p)
{
- lock_fd = ap_popenf(p, ap_lock_fname, O_WRONLY, 0600);
- if (lock_fd == -1) {
+ flock_fd = ap_popenf(p, ap_lock_fname, O_WRONLY, 0600);
+ if (flock_fd == -1) {
ap_log_error(APLOG_MARK, APLOG_EMERG, server_conf,
"Child cannot open lock file: %s", ap_lock_fname);
clean_child_exit(APEXIT_CHILDINIT);
@@ -886,24 +954,24 @@
* Initialize mutex lock.
* Must be safe to call this on a restart.
*/
-static void accept_mutex_init(pool *p)
+static void accept_mutex_init_flock(pool *p)
{
expand_lock_fname(p);
unlink(ap_lock_fname);
- lock_fd = ap_popenf(p, ap_lock_fname, O_CREAT | O_WRONLY | O_EXCL, 0600);
- if (lock_fd == -1) {
+ flock_fd = ap_popenf(p, ap_lock_fname, O_CREAT | O_WRONLY | O_EXCL, 0600);
+ if (flock_fd == -1) {
ap_log_error(APLOG_MARK, APLOG_EMERG, server_conf,
"Parent cannot open lock file: %s", ap_lock_fname);
exit(APEXIT_INIT);
}
- ap_register_cleanup(p, NULL, accept_mutex_cleanup, ap_null_cleanup);
+ ap_register_cleanup(p, NULL, accept_mutex_cleanup_flock, ap_null_cleanup);
}
-static void accept_mutex_on(void)
+static void accept_mutex_on_flock(void)
{
int ret;
- while ((ret = flock(lock_fd, LOCK_EX)) < 0 && errno == EINTR)
+ while ((ret = flock(flock_fd, LOCK_EX)) < 0 && errno == EINTR)
continue;
if (ret < 0) {
@@ -913,20 +981,29 @@
}
}
-static void accept_mutex_off(void)
+static void accept_mutex_off_flock(void)
{
- if (flock(lock_fd, LOCK_UN) < 0) {
+ if (flock(flock_fd, LOCK_UN) < 0) {
ap_log_error(APLOG_MARK, APLOG_EMERG, server_conf,
"flock: LOCK_UN: Error freeing accept lock. Exiting!");
clean_child_exit(APEXIT_CHILDFATAL);
}
}
-#elif defined(USE_OS2SEM_SERIALIZED_ACCEPT)
+accept_mutex_methods_s accept_mutex_flock_s = {
+ accept_mutex_child_init_flock,
+ accept_mutex_init_flock,
+ accept_mutex_on_flock,
+ accept_mutex_off_flock,
+ "flock"
+};
+#endif
+
+#if defined(HAVE_OS2SEM_SERIALIZED_ACCEPT)
static HMTX lock_sem = -1;
-static void accept_mutex_cleanup(void *foo)
+static void accept_mutex_cleanup_os2sem(void *foo)
{
DosReleaseMutexSem(lock_sem);
DosCloseMutexSem(lock_sem);
@@ -936,7 +1013,7 @@
* Initialize mutex lock.
* Done by each child at it's birth
*/
-static void accept_mutex_child_init(pool *p)
+static void accept_mutex_child_init_os2sem(pool *p)
{
int rc = DosOpenMutexSem(NULL, &lock_sem);
@@ -945,7 +1022,7 @@
"Child cannot open lock semaphore, rc=%d", rc);
clean_child_exit(APEXIT_CHILDINIT);
} else {
- ap_register_cleanup(p, NULL, accept_mutex_cleanup, ap_null_cleanup);
+ ap_register_cleanup(p, NULL, accept_mutex_cleanup_os2sem, ap_null_cleanup);
}
}
@@ -953,7 +1030,7 @@
* Initialize mutex lock.
* Must be safe to call this on a restart.
*/
-static void accept_mutex_init(pool *p)
+static void accept_mutex_init_os2sem(pool *p)
{
int rc = DosCreateMutexSem(NULL, &lock_sem, DC_SEM_SHARED, FALSE);
@@ -963,10 +1040,10 @@
exit(APEXIT_INIT);
}
- ap_register_cleanup(p, NULL, accept_mutex_cleanup, ap_null_cleanup);
+ ap_register_cleanup(p, NULL, accept_mutex_cleanup_os2sem, ap_null_cleanup);
}
-static void accept_mutex_on(void)
+static void accept_mutex_on_os2sem(void)
{
int rc = DosRequestMutexSem(lock_sem, SEM_INDEFINITE_WAIT);
@@ -977,7 +1054,7 @@
}
}
-static void accept_mutex_off(void)
+static void accept_mutex_off_os2sem(void)
{
int rc = DosReleaseMutexSem(lock_sem);
@@ -988,64 +1065,207 @@
}
}
-#elif defined(USE_TPF_CORE_SERIALIZED_ACCEPT)
+accept_mutex_methods_s accept_mutex_os2sem_s = {
+ accept_mutex_child_init_os2sem,
+ accept_mutex_init_os2sem,
+ accept_mutex_on_os2sem,
+ accept_mutex_off_os2sem,
+ "os2sem"
+};
+#endif
+#if defined(HAVE_TPF_CORE_SERIALIZED_ACCEPT)
+
static int tpf_core_held;
-static void accept_mutex_cleanup(void *foo)
+static void accept_mutex_cleanup_tpfcore(void *foo)
{
if(tpf_core_held)
coruc(RESOURCE_KEY);
}
-#define accept_mutex_init(x)
+#define accept_mutex_init_tpfcore(x)
-static void accept_mutex_child_init(pool *p)
+static void accept_mutex_child_init_tpfcore(pool *p)
{
- ap_register_cleanup(p, NULL, accept_mutex_cleanup, ap_null_cleanup);
+ ap_register_cleanup(p, NULL, accept_mutex_cleanup_tpfcore, ap_null_cleanup);
tpf_core_held = 0;
}
-static void accept_mutex_on(void)
+static void accept_mutex_on_tpfcore(void)
{
corhc(RESOURCE_KEY);
tpf_core_held = 1;
ap_check_signals();
}
-static void accept_mutex_off(void)
+static void accept_mutex_off_tpfcore(void)
{
coruc(RESOURCE_KEY);
tpf_core_held = 0;
ap_check_signals();
}
-#else
-/* Default --- no serialization. Other methods *could* go here,
- * as #elifs...
- */
+accept_mutex_methods_s accept_mutex_tpfcore_s = {
+ accept_mutex_child_init_tpfcore,
+ accept_mutex_init_tpfcore,
+ accept_mutex_on_tpfcore,
+ accept_mutex_off_tpfcore,
+ "tpfcore"
+};
+#endif
+
+#if defined HAVE_NONE_SERIALIZED_ACCEPT
#if !defined(MULTITHREAD)
/* Multithreaded systems don't complete between processes for
* the sockets. */
#define NO_SERIALIZED_ACCEPT
-#define accept_mutex_child_init(x)
-#define accept_mutex_init(x)
-#define accept_mutex_on()
-#define accept_mutex_off()
+#endif
+
+accept_mutex_methods_s accept_mutex_none_s = {
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ "none"
+};
+#endif
+
+#define AP_FPTR1(x,y) { if (x) ((* x)(y)); }
+#define AP_FPTR0(x) { if (x) ((* x)()); }
+
+#define accept_mutex_child_init(x) AP_FPTR1(amutex->child_init,x)
+#define accept_mutex_init(x) AP_FPTR1(amutex->init,x)
+#define accept_mutex_off() AP_FPTR0(amutex->off)
+#define accept_mutex_on() AP_FPTR0(amutex->on)
+
+char *default_mutex_method(void)
+{
+ char *t;
+#if defined USE_USLOCK_SERIALIZED_ACCEPT
+ t = "uslock";
+#elif defined USE_PTHREAD_SERIALIZED_ACCEPT
+ t = "pthread";
+#elif defined USE_SYSVMEM_SERIALIZED_ACCEPT
+ t = "sysvmem";
+#elif defined USE_FCNTL_SERIALIZED_ACCEPT
+ t = "fcntl";
+#elif defined USE_FLOCK_SERIALIZED_ACCEPT
+ t = "flock";
+#elif defined USE_OS2SEM_SERIALIZED_ACCEPT
+ t = "os2sem";
+#elif defined USE_TPF_CORE_SERIALIZED_ACCEPT
+ t = "tpfcore";
+#elif defined USE_NONE_SERIALIZED_ACCEPT
+ t = "none";
+#else
+ t = "default";
#endif
+#if defined HAVE_USLOCK_SERIALIZED_ACCEPT
+ if ((!(strcasecmp(t,"default"))) || (!(strcasecmp(t,"uslock")))) {
+ return "uslock";
+ } else
+#endif
+#if defined HAVE_PTHREAD_SERIALIZED_ACCEPT
+ if ((!(strcasecmp(t,"default"))) || (!(strcasecmp(t,"pthread")))) {
+ return "pthread";
+ } else
+#endif
+#if defined HAVE_SYSVSEM_SERIALIZED_ACCEPT
+ if ((!(strcasecmp(t,"default"))) || (!(strcasecmp(t,"sysvsem")))) {
+ return "sysvmem";
+ } else
+#endif
+#if defined HAVE_FCNTL_SERIALIZED_ACCEPT
+ if ((!(strcasecmp(t,"default"))) || (!(strcasecmp(t,"fcntl")))) {
+ return "fcntl";
+ } else
+#endif
+#if defined HAVE_FLOCK_SERIALIZED_ACCEPT
+ if ((!(strcasecmp(t,"default"))) || (!(strcasecmp(t,"flock")))) {
+ return "flock";
+ } else
+#endif
+#if defined HAVE_OS2SEM_SERIALIZED_ACCEPT
+ if ((!(strcasecmp(t,"default"))) || (!(strcasecmp(t,"os2sem")))) {
+ return "os2sem";
+ } else
+#endif
+#if defined HAVE_TPF_CORE_SERIALIZED_ACCEPT
+ if ((!(strcasecmp(t,"default"))) || (!(strcasecmp(t,"tpfcore")))) {
+ return "tpfcore";
+ } else
+#endif
+#if defined HAVE_NONE_SERIALIZED_ACCEPT
+ if ((!(strcasecmp(t,"default"))) || (!(strcasecmp(t,"none")))) {
+ return "none";
+ } else
#endif
+ return "Request serialized accept method not available";
+
+ return NULL;
+}
-/* On some architectures it's safe to do unserialized accept()s in the single
- * Listen case. But it's never safe to do it in the case where there's
- * multiple Listen statements. Define SINGLE_LISTEN_UNSERIALIZED_ACCEPT
- * when it's safe in the single Listen case.
- */
+char *init_mutex_method(char *t)
+{
+#if defined HAVE_USLOCK_SERIALIZED_ACCEPT
+ if (!(strcasecmp(t,"uslock"))) {
+ amutex = &accept_mutex_uslock_s;
+ } else
+#endif
+#if defined HAVE_PTHREAD_SERIALIZED_ACCEPT
+ if (!(strcasecmp(t,"pthread"))) {
+ amutex = &accept_mutex_pthread_s;
+ } else
+#endif
+#if defined HAVE_SYSVSEM_SERIALIZED_ACCEPT
+ if (!(strcasecmp(t,"sysvsem"))) {
+ amutex = &accept_mutex_sysvsem_s;
+ } else
+#endif
+#if defined HAVE_FCNTL_SERIALIZED_ACCEPT
+ if (!(strcasecmp(t,"fcntl"))) {
+ amutex = &accept_mutex_fcntl_s;
+ } else
+#endif
+#if defined HAVE_FLOCK_SERIALIZED_ACCEPT
+ if (!(strcasecmp(t,"flock"))) {
+ amutex = &accept_mutex_flock_s;
+ } else
+#endif
+#if defined HAVE_OS2SEM_SERIALIZED_ACCEPT
+ if (!(strcasecmp(t,"os2sem"))) {
+ amutex = &accept_mutex_os2sem_s;
+ } else
+#endif
+#if defined HAVE_TPF_CORE_SERIALIZED_ACCEPT
+ if (!(strcasecmp(t,"tpfcore"))) {
+ amutex = &accept_mutex_tpfcore_s;
+ } else
+#endif
+#if defined HAVE_NONE_SERIALIZED_ACCEPT
+ if (!(strcasecmp(t,"none"))) {
+ amutex = &accept_mutex_none_s;
+ } else
+#endif
+ return "Request serialized accept method not available";
+
+ return NULL;
+}
+
+char * init_single_listen( int flag )
+{
+ ap_single_listen = flag;
#ifdef SINGLE_LISTEN_UNSERIALIZED_ACCEPT
-#define SAFE_ACCEPT(stmt) do {if(ap_listeners->next != ap_listeners) {stmt;}} while(0)
+ return NULL;
#else
-#define SAFE_ACCEPT(stmt) do {stmt;} while(0)
+ return "Single Unserialized listen not compiled in - use with caution";
#endif
+}
+#define SAFE_ACCEPT(stmt) \
+ do {if(ap_listeners->next != ap_listeners || !ap_single_listen) {stmt;}} while(0);
+
static void usage(char *bin)
{
char pad[MAX_STRING_LEN];
@@ -3712,22 +3932,31 @@
#endif
#ifdef NO_LINGCLOSE
printf(" -D NO_LINGCLOSE\n");
+#endif
+#ifdef HAVE_FCNTL_SERIALIZED_ACCEPT
+ printf(" -D HAVE_FCNTL_SERIALIZED_ACCEPT\n");
#endif
-#ifdef USE_FCNTL_SERIALIZED_ACCEPT
- printf(" -D USE_FCNTL_SERIALIZED_ACCEPT\n");
+#ifdef HAVE_FLOCK_SERIALIZED_ACCEPT
+ printf(" -D HAVE_FLOCK_SERIALIZED_ACCEPT\n");
#endif
-#ifdef USE_FLOCK_SERIALIZED_ACCEPT
- printf(" -D USE_FLOCK_SERIALIZED_ACCEPT\n");
+#ifdef HAVE_USLOCK_SERIALIZED_ACCEPT
+ printf(" -D HAVE_USLOCK_SERIALIZED_ACCEPT\n");
#endif
-#ifdef USE_USLOCK_SERIALIZED_ACCEPT
- printf(" -D USE_USLOCK_SERIALIZED_ACCEPT\n");
+#ifdef HAVE_SYSVSEM_SERIALIZED_ACCEPT
+ printf(" -D HAVE_SYSVSEM_SERIALIZED_ACCEPT\n");
#endif
-#ifdef USE_SYSVSEM_SERIALIZED_ACCEPT
- printf(" -D USE_SYSVSEM_SERIALIZED_ACCEPT\n");
+#ifdef HAVE_PTHREAD_SERIALIZED_ACCEPT
+ printf(" -D HAVE_PTHREAD_SERIALIZED_ACCEPT\n");
#endif
-#ifdef USE_PTHREAD_SERIALIZED_ACCEPT
- printf(" -D USE_PTHREAD_SERIALIZED_ACCEPT\n");
+#ifdef HAVE_OS2SEM_SERIALIZED_ACCEPT
+ printf(" -D HAVE_OS2SEM_SERIALIZED_ACCEPT\n");
#endif
+#ifdef HAVE_TPF_CORE_SERIALIZED_ACCEPT
+ printf(" -D HAVE_TPF_CORE_SERIALIZED_ACCEPT\n");
+#endif
+#ifdef HAVE_NONE_SERIALIZED_ACCEPT
+ printf(" -D HAVE_NONE_SERIALIZED_ACCEPT\n");
+#endif
#ifdef SINGLE_LISTEN_UNSERIALIZED_ACCEPT
printf(" -D SINGLE_LISTEN_UNSERIALIZED_ACCEPT\n");
#endif
@@ -3884,7 +4113,7 @@
/* needs to be done before we switch UIDs so we have permissions */
reopen_scoreboard(pchild);
- SAFE_ACCEPT(accept_mutex_child_init(pchild));
+ SAFE_ACCEPT((accept_mutex_child_init(pchild)));
set_group_privs();
#ifdef MPE
@@ -3982,7 +4211,7 @@
*/
/* Lock around "accept", if necessary */
- SAFE_ACCEPT(accept_mutex_on());
+ SAFE_ACCEPT((accept_mutex_on()));
for (;;) {
if (ap_listeners->next != ap_listeners) {
@@ -4688,6 +4917,9 @@
ap_clear_pool(pconf);
ptrans = ap_make_sub_pool(pconf);
+ init_single_listen(ap_single_listen);
+ init_mutex_method(default_mutex_method());
+
server_conf = ap_read_config(pconf, ptrans, ap_server_confname);
setup_listeners(pconf);
ap_clear_pool(plog);
@@ -4743,6 +4975,10 @@
}
ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, server_conf,
"Server built: %s", ap_get_server_built());
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, server_conf,
+ "Accept mutex: %s (Default: %s); Single listen is %s",
+ amutex->name, default_mutex_method(),
+ (ap_single_listen ? "On" : "Off"));
restart_pending = shutdown_pending = 0;
while (!restart_pending && !shutdown_pending) {
Index: src/modules/standard/mod_rewrite.h
===================================================================
RCS file: /home/cvs/apache-1.3/src/modules/standard/mod_rewrite.h,v
retrieving revision 1.80
diff -u -r1.80 mod_rewrite.h
--- src/modules/standard/mod_rewrite.h 2001/05/06 22:00:02 1.80
+++ src/modules/standard/mod_rewrite.h 2001/08/24 18:26:59
@@ -144,12 +144,13 @@
/* The locking support:
* Try to determine whether we should use fcntl() or flock().
* Would be better ap_config.h could provide this... :-(
+ * Small monkey business to ensure that fcntl is preferred,
+ * unless we specified USE_FLOCK_SERIALIZED_ACCEPT during compile.
*/
-#if defined(USE_FCNTL_SERIALIZED_ACCEPT)
+#if defined(HAVE_FCNTL_SERIALIZED_ACCEPT) && !defined(USE_FLOCK_SERIALIZED_ACCEPT)
#define USE_FCNTL 1
#include <fcntl.h>
-#endif
-#if defined(USE_FLOCK_SERIALIZED_ACCEPT)
+#elif defined(HAVE_FLOCK_SERIALIZED_ACCEPT)
#define USE_FLOCK 1
#include <sys/file.h>
#endif
--
===========================================================================
Jim Jagielski [|] jim@jaguNET.com [|] http://www.jaguNET.com/
"A society that will trade a little liberty for a little order
will lose both and deserve neither"
Re: [PATCH] V2: Accept Mutex Runtime Option
Posted by Jeff Trawick <tr...@attglobal.net>.
Jim Jagielski <ji...@jaguNET.com> writes:
> Here's the 2nd, improved version of the make-accept-mutex-method-runtime
> patch. It includes changes suggested... Tested under Solaris and OS X.
> Please check out...
I've played with this a little (after changing SYSVMEM and sysvmem to
SYSVSEM and sysvsem) on Linux and AIX and haven't had any problems. I
haven't paid any real attention to SingleListen. Hopefully it still
defaults to very reasonable behavior :)
After you commit it I'll probably turn on sysvsem support for AIX (by
defining HAVE_SYSVSEM_SERIALIZED_ACCEPT and NEED_UNION_SEMUN).
--
Jeff Trawick | trawick@attglobal.net | PGP public key at web site:
http://www.geocities.com/SiliconValley/Park/9289/
Born in Roswell... married an alien...