mirror of
				https://github.com/ossrs/srs.git
				synced 2025-03-09 15:49:59 +00:00 
			
		
		
		
	remove the unused files. add upp project file
This commit is contained in:
		
							parent
							
								
									7f4c113e57
								
							
						
					
					
						commit
						7692373e02
					
				
					 29 changed files with 19 additions and 8874 deletions
				
			
		| 
						 | 
				
			
			@ -1,394 +0,0 @@
 | 
			
		|||
WELCOME!
 | 
			
		||||
 | 
			
		||||
The State Threads Library is a small application library which provides
 | 
			
		||||
a foundation for writing fast and highly scalable Internet applications
 | 
			
		||||
(such as web servers, proxy servers, mail transfer agents, and so on,
 | 
			
		||||
really any network-data-driven application) on UNIX-like platforms.  It
 | 
			
		||||
combines the simplicity of the multithreaded programming paradigm, in
 | 
			
		||||
which one thread supports each simultaneous connection, with the
 | 
			
		||||
performance and scalability of an event-driven state machine
 | 
			
		||||
architecture.  In other words, this library offers a threading API for
 | 
			
		||||
structuring an Internet application as a state machine.  For more
 | 
			
		||||
details, please see the library documentation in the "docs" directory or
 | 
			
		||||
on-line at
 | 
			
		||||
 | 
			
		||||
    http://state-threads.sourceforge.net/docs/
 | 
			
		||||
 | 
			
		||||
The State Threads Project is an open source project for maintaining and
 | 
			
		||||
enhancing the State Threads Library.  For more information about this
 | 
			
		||||
project, please see
 | 
			
		||||
 | 
			
		||||
    http://state-threads.sourceforge.net/
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
BUILDING
 | 
			
		||||
 | 
			
		||||
To build the library by hand, use the GNU make utility.  Run the make
 | 
			
		||||
command (e.g., `gmake') with no arguments to display all supported
 | 
			
		||||
targets.
 | 
			
		||||
 | 
			
		||||
To build more or less automatically, first set the CONFIG_GUESS_PATH
 | 
			
		||||
variable in either osguess.sh or your environment then run "make
 | 
			
		||||
default" which guesses your OS and builds.  Requires the "config.guess"
 | 
			
		||||
utility from GNU autoconf (not included with ST).  You can use one from
 | 
			
		||||
a larger "main" software project or just use any config.guess available
 | 
			
		||||
on your system.  You can also get it directly from GNU:
 | 
			
		||||
ftp://ftp.gnu.org/gnu/autoconf/
 | 
			
		||||
 | 
			
		||||
To build rpms (RedHat Linux 6.2 or later, Linux/Mandrake, Solaris with
 | 
			
		||||
gnome, etc.):
 | 
			
		||||
    download the latest st-x.y.tar.gz
 | 
			
		||||
    # rpm -ta st-x.y.tar.gz
 | 
			
		||||
The .rpms will land in /usr/src/RPMS/<arch>.  Install them with:
 | 
			
		||||
    # rpm -i libst*.rpm
 | 
			
		||||
Requires GNU automake and rpm 3.0.3 or later.
 | 
			
		||||
 | 
			
		||||
Debian users:
 | 
			
		||||
  If you run potato, please upgrade to woody.
 | 
			
		||||
  If you run woody, "apt-get install libst-dev" will get you v1.3.
 | 
			
		||||
  If you run testing/unstable, you will get the newest available version.
 | 
			
		||||
  If you *must* have the newest libst in woody, you may follow these
 | 
			
		||||
  not-recommended instructions:
 | 
			
		||||
    1. Add "deb-src <your-favourite-debian-mirror> unstable main" to your
 | 
			
		||||
       /etc/apt/sources.list
 | 
			
		||||
    2. apt-get update
 | 
			
		||||
    3. apt-get source st
 | 
			
		||||
    4. cd st-1.4 (or whatever version you got)
 | 
			
		||||
    5. debuild
 | 
			
		||||
    6. dpkg -i ../*.deb
 | 
			
		||||
 | 
			
		||||
If your application uses autoconf to search for dependencies and you
 | 
			
		||||
want to search for a given version of libst, you can simply add
 | 
			
		||||
        PKG_CHECK_MODULES(MYAPP, st >= 1.3 mumble >= 0.2.23)
 | 
			
		||||
to your configure.ac/in.  This will define @MYAPP_LIBS@ and
 | 
			
		||||
@MYAPP_CFLAGS@ which you may then use in your Makefile.am/in files to
 | 
			
		||||
link against mumble and st.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
LICENSE
 | 
			
		||||
 | 
			
		||||
The State Threads library is a derivative of the Netscape Portable
 | 
			
		||||
Runtime library (NSPR).  All source code in this directory is
 | 
			
		||||
distributed under the terms of the Mozilla Public License (MPL) version
 | 
			
		||||
1.1 or the GNU General Public License (GPL) version 2 or later.  For
 | 
			
		||||
more information about these licenses please see
 | 
			
		||||
http://www.mozilla.org/MPL/ and http://www.gnu.org/copyleft/.
 | 
			
		||||
 | 
			
		||||
All source code in the "examples" directory is distributed under the BSD
 | 
			
		||||
style license.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
PLATFORMS
 | 
			
		||||
 | 
			
		||||
Please see the "docs/notes.html" file for the list of currently
 | 
			
		||||
supported platforms.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
DEBUGGER SUPPORT
 | 
			
		||||
 | 
			
		||||
It's almost impossible to print SP and PC in a portable way.  The only
 | 
			
		||||
way to see thread's stack platform-independently is to actually jump to
 | 
			
		||||
the saved context.  That's what the _st_iterate_threads() function does.
 | 
			
		||||
Do the following to iterate over all threads:
 | 
			
		||||
 | 
			
		||||
- set the _st_iterate_threads_flag to 1 in debugger
 | 
			
		||||
- set breakpoint at the _st_show_thread_stack() function
 | 
			
		||||
  (which does nothing)
 | 
			
		||||
- call the _st_iterate_threads() function which jumps to the
 | 
			
		||||
  next thread
 | 
			
		||||
- at each break you can explore thread's stack
 | 
			
		||||
- continue
 | 
			
		||||
- when iteration is complete, you return to the original
 | 
			
		||||
  point (you can see thread id and a message as arguments of
 | 
			
		||||
  the _st_show_thread_stack() function).
 | 
			
		||||
 | 
			
		||||
You can call _st_iterate_threads() in three ways:
 | 
			
		||||
 | 
			
		||||
- Insert it into your source code at the point you want to
 | 
			
		||||
  go over threads.
 | 
			
		||||
- Just run application and this function will be called at
 | 
			
		||||
  the first context switch.
 | 
			
		||||
- Call it directly from the debugger at any point.
 | 
			
		||||
 | 
			
		||||
This works with gdb and dbx.
 | 
			
		||||
 | 
			
		||||
Example using gdb:
 | 
			
		||||
 | 
			
		||||
(gdb) set _st_iterate_threads_flag = 1
 | 
			
		||||
(gdb) b _st_show_thread_stack
 | 
			
		||||
...
 | 
			
		||||
(gdb) call _st_iterate_threads()
 | 
			
		||||
...
 | 
			
		||||
(gdb) bt
 | 
			
		||||
...
 | 
			
		||||
(gdb) c
 | 
			
		||||
...
 | 
			
		||||
(gdb) bt
 | 
			
		||||
...
 | 
			
		||||
(gdb) c
 | 
			
		||||
...
 | 
			
		||||
and so on...
 | 
			
		||||
 | 
			
		||||
_st_iterate_threads_flag will be set to 0 automatically
 | 
			
		||||
after iteration is over or you can set it to 0 at any time
 | 
			
		||||
to stop iteration.
 | 
			
		||||
 | 
			
		||||
Sometimes gdb complains about SIGSEGV when you call a function
 | 
			
		||||
directly at gdb command-line.  It can be ignored -- just call the
 | 
			
		||||
same function right away again, it works just fine.  For example:
 | 
			
		||||
 | 
			
		||||
(gdb) set _st_iterate_threads_flag = 1
 | 
			
		||||
(gdb) b _st_show_thread_stack
 | 
			
		||||
Breakpoint 1 at 0x809bbbb: file sched.c, line 856.
 | 
			
		||||
(gdb) call _st_iterate_threads()
 | 
			
		||||
Program received signal SIGSEGV, Segmentation fault.
 | 
			
		||||
....
 | 
			
		||||
(gdb) # just call the function again:
 | 
			
		||||
(gdb) call _st_iterate_threads()
 | 
			
		||||
Breakpoint 1, _st_show_thread_stack (thread=0x4017aee4, messg=0x80ae7a2
 | 
			
		||||
"Iteration started")    at sched.c:856
 | 
			
		||||
856     }
 | 
			
		||||
....
 | 
			
		||||
 | 
			
		||||
You can use simple gdb command-line scripting to display
 | 
			
		||||
all threads and their stack traces at once:
 | 
			
		||||
 | 
			
		||||
(gdb) while _st_iterate_threads_flag
 | 
			
		||||
 >bt
 | 
			
		||||
 >c
 | 
			
		||||
 >end
 | 
			
		||||
....
 | 
			
		||||
 | 
			
		||||
Another script to stop at the thread with the specific thread id
 | 
			
		||||
(e.g., 0x40252ee4):
 | 
			
		||||
 | 
			
		||||
(gdb) # set the flag again:
 | 
			
		||||
(gdb) set _st_iterate_threads_flag = 1
 | 
			
		||||
(gdb) call _st_iterate_threads()
 | 
			
		||||
Breakpoint 1, _st_show_thread_stack (thread=0x4017aee4, messg=0x80ae7a2
 | 
			
		||||
"Iteration started")    at sched.c:856
 | 
			
		||||
856     }
 | 
			
		||||
....
 | 
			
		||||
(gdb) while thread != 0x40252ee4
 | 
			
		||||
 >c
 | 
			
		||||
 >end
 | 
			
		||||
....
 | 
			
		||||
....
 | 
			
		||||
Breakpoint 1, _st_show_thread_stack (thread=0x40252ee4, messg=0x0) at
 | 
			
		||||
sched.c:856
 | 
			
		||||
856     }
 | 
			
		||||
(gdb) bt
 | 
			
		||||
....
 | 
			
		||||
(gdb) # don't want to continue iteration, unset the flag:
 | 
			
		||||
(gdb) set _st_iterate_threads_flag = 0
 | 
			
		||||
(gdb) c
 | 
			
		||||
Continuing.
 | 
			
		||||
Breakpoint 1, _st_show_thread_stack (thread=0x0, messg=0x80ae78e "Iteration
 | 
			
		||||
completed")
 | 
			
		||||
    at sched.c:856
 | 
			
		||||
856     }
 | 
			
		||||
(gdb) c
 | 
			
		||||
Continuing.
 | 
			
		||||
(gdb) return
 | 
			
		||||
Make selected stack frame return now? (y or n) y
 | 
			
		||||
#0  0x4011254e in __select ()
 | 
			
		||||
   from /lib/libc.so.6
 | 
			
		||||
(gdb) detach
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
CHANGE LOG
 | 
			
		||||
 | 
			
		||||
Changes from 1.8 to 1.9.
 | 
			
		||||
------------------------
 | 
			
		||||
o  Support 32-bit and 64-bit Intel Macs.
 | 
			
		||||
 | 
			
		||||
o  Added ST_VERSION string, and ST_VERSION_MAJOR and ST_VERSION_MINOR
 | 
			
		||||
   [bug 1796801].
 | 
			
		||||
 | 
			
		||||
o  Fixed some compiler warnings, based on a patch from Brian Wellington
 | 
			
		||||
   [bug 1932741].
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Changes from 1.7 to 1.8.
 | 
			
		||||
--------------------------
 | 
			
		||||
o  Added support for kqueue and epoll on platforms that support them.
 | 
			
		||||
   Added ability to choose the event notification system at program
 | 
			
		||||
   startup.
 | 
			
		||||
 | 
			
		||||
o  Long-overdue public definitions of ST_UTIME_NO_TIMEOUT (-1ULL) and
 | 
			
		||||
   ST_UTIME_NO_WAIT (0) [bug 1514436].
 | 
			
		||||
 | 
			
		||||
o  Documentation patch for st_utime() [bug 1514484].
 | 
			
		||||
 | 
			
		||||
o  Documentation patch for st_timecache_set() [bug 1514486].
 | 
			
		||||
 | 
			
		||||
o  Documentation patch for st_netfd_serialize_accept() [bug 1514494].
 | 
			
		||||
 | 
			
		||||
o  Added st_writev_resid() [rfe 1538344].
 | 
			
		||||
 | 
			
		||||
o  Added st_readv_resid() [rfe 1538768] and, for symmetry, st_readv().
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Changes from 1.6 to 1.7.
 | 
			
		||||
------------------------
 | 
			
		||||
o  Support glibc 2.4, which breaks programs that manipulate jump buffers.
 | 
			
		||||
   Replaced Linux IA64 special cases with new md.S that covers all
 | 
			
		||||
   Linux.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Changes from 1.5.2 to 1.6.
 | 
			
		||||
--------------------------
 | 
			
		||||
none
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Changes from 1.5.1 to 1.5.2.
 | 
			
		||||
----------------------------
 | 
			
		||||
o  Alfred Perlstein's context switch callback feature.
 | 
			
		||||
 | 
			
		||||
o  Claus Assmann's st_recvmsg/st_sendmsg wrappers.
 | 
			
		||||
 | 
			
		||||
o  Extra stack padding for platforms that need it.
 | 
			
		||||
 | 
			
		||||
o  Ron Arts's timeout clarifications in the reference manual.
 | 
			
		||||
 | 
			
		||||
o  Raymond Bero and Anton Berezin's AMD64 FreeBSD port.
 | 
			
		||||
 | 
			
		||||
o  Claus Assmann's AMD64 SunOS 5.10 port.
 | 
			
		||||
 | 
			
		||||
o  Claus Assmann's AMD64 OpenBSD port.
 | 
			
		||||
 | 
			
		||||
o  Michael Abd-El-Malek's Mac OS X port.
 | 
			
		||||
 | 
			
		||||
o  Michael Abd-El-Malek's stack printing patch.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Changes from 1.5.0 to 1.5.1.
 | 
			
		||||
----------------------------
 | 
			
		||||
o  Andreas Gustafsson's USE_POLL fix.
 | 
			
		||||
 | 
			
		||||
o  Gene's st_set_utime_function() enhancement.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Changes from 1.4 to 1.5.0.
 | 
			
		||||
--------------------------
 | 
			
		||||
o  Andreas Gustafsson's performance patch.
 | 
			
		||||
 | 
			
		||||
o  New extensions:  Improved DNS resolver, generic LRU cache, in-process
 | 
			
		||||
   DNS cache, and a program to test the resolver and cache.
 | 
			
		||||
 | 
			
		||||
o  Support for AMD Opteron 64-bit CPUs under Linux.
 | 
			
		||||
 | 
			
		||||
o  Support for SPARC-64 under Solaris.
 | 
			
		||||
 | 
			
		||||
o  Andreas Gustafsson's support for VAX under NetBSD.
 | 
			
		||||
 | 
			
		||||
o  Changed unportable #warning directives in md.h to #error.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Changes from 1.3 to 1.4.
 | 
			
		||||
------------------------
 | 
			
		||||
o  Andreas Gustafsson's NetBSD port.
 | 
			
		||||
 | 
			
		||||
o  Wesley W. Terpstra's Darwin (MacOS X) port.
 | 
			
		||||
 | 
			
		||||
o  Support for many CPU architectures under Linux and *BSD.
 | 
			
		||||
 | 
			
		||||
o  Renamed private typedefs so they don't conflict with public ones any
 | 
			
		||||
   more.
 | 
			
		||||
 | 
			
		||||
o  common.h now includes public.h for strict prototyping.
 | 
			
		||||
 | 
			
		||||
o  Joshua Levy's recommendation to make st_connect() and st_sendto()
 | 
			
		||||
   accept const struct sockaddr pointers, as the originals do.
 | 
			
		||||
 | 
			
		||||
o  Clarified the documentation regarding blocking vs. non-blocking I/O.
 | 
			
		||||
 | 
			
		||||
o  Cygwin support.
 | 
			
		||||
 | 
			
		||||
o  Created the extensions directory.
 | 
			
		||||
 | 
			
		||||
o  Fixed warnings from ia64asm.S.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Changes from 1.2 to 1.3.
 | 
			
		||||
------------------------
 | 
			
		||||
o  Added st_read_resid() and st_write_resid() to allow the caller to know
 | 
			
		||||
   how much data was transferred before an error occurred.  Updated
 | 
			
		||||
   documentation.
 | 
			
		||||
 | 
			
		||||
o  Updated project link, copyrights, and documentation regarding
 | 
			
		||||
   timeouts.  Added comment to st_connect().
 | 
			
		||||
 | 
			
		||||
o  Optimized the _st_add_sleep_q() function in sched.c.  Now we walk the
 | 
			
		||||
   sleep queue *backward* when inserting a thread into it.  When you
 | 
			
		||||
   have lots (hundreds) of threads and several timeout values, it takes
 | 
			
		||||
   a while to insert a thread at the appropriate point in the sleep
 | 
			
		||||
   queue.  The idea is that often this appropriate point is closer to
 | 
			
		||||
   the end of the queue rather than the beginning.  Measurements show
 | 
			
		||||
   performance improves with this change.  In any case this change
 | 
			
		||||
   should do no harm.
 | 
			
		||||
 | 
			
		||||
o  Added a hint of when to define USE_POLL and when not to, to the
 | 
			
		||||
   Makefile.
 | 
			
		||||
 | 
			
		||||
o  Added debugging support (files common.h and sched.c).   See above.
 | 
			
		||||
 | 
			
		||||
o  Decreased the number of reallocations of _ST_POLLFDS in sched.c.
 | 
			
		||||
   Inspired by Lev Walkin.
 | 
			
		||||
 | 
			
		||||
o  Fixed st_usleep(-1) and st_sleep(-1), and added a warning to the
 | 
			
		||||
   documentation about too-large timeouts.
 | 
			
		||||
 | 
			
		||||
o  Linux/*BSD Alpha port.
 | 
			
		||||
 | 
			
		||||
o  Wesley W. Terpstra modernized the build process:
 | 
			
		||||
   - properly build relocatable libraries under bsd and linux
 | 
			
		||||
   - use library versioning
 | 
			
		||||
   - added rpm spec file
 | 
			
		||||
   - added debian/ files
 | 
			
		||||
   See above for build instructions.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Changes from 1.1 to 1.2.
 | 
			
		||||
------------------------
 | 
			
		||||
o  Added st_randomize_stacks().
 | 
			
		||||
 | 
			
		||||
o  Added a patch contributed by Sascha Schumann.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Changes from 1.0 to 1.1.
 | 
			
		||||
------------------------
 | 
			
		||||
o  Relicensed under dual MPL-GPL.
 | 
			
		||||
 | 
			
		||||
o  OpenBSD port.
 | 
			
		||||
 | 
			
		||||
o  Compile-time option to use poll() instead of select() for
 | 
			
		||||
   event polling (see Makefile).
 | 
			
		||||
   This is useful if you want to support a large number of open
 | 
			
		||||
   file descriptors (larger than FD_SETSIZE) within a single
 | 
			
		||||
   process.
 | 
			
		||||
 | 
			
		||||
o  Linux IA-64 port.
 | 
			
		||||
   Two issues make IA-64 different from other platforms:
 | 
			
		||||
 | 
			
		||||
   - Besides the traditional call stack in memory, IA-64 uses the
 | 
			
		||||
     general register stack.  Thus each thread needs a backing store
 | 
			
		||||
     for the register stack in addition to the memory stack.
 | 
			
		||||
 | 
			
		||||
   - Current implementation of setjmp()/longjmp() can not be used
 | 
			
		||||
     for thread context-switching since it assumes that only one
 | 
			
		||||
     register stack exists.  Using special assembly functions for
 | 
			
		||||
     context-switching is unavoidable.
 | 
			
		||||
    
 | 
			
		||||
o  Thread stack capping on IRIX.
 | 
			
		||||
   This allows some profiling tools (such as SpeedShop) to know when
 | 
			
		||||
   to stop unwinding the stack.  Without this libexc, used by SpeedShop,
 | 
			
		||||
   traces right off the stack and crashes.
 | 
			
		||||
 | 
			
		||||
o  Miscellaneous documentation additions.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
COPYRIGHTS
 | 
			
		||||
 | 
			
		||||
Portions created by SGI are Copyright (C) 2000 Silicon Graphics, Inc.
 | 
			
		||||
All Rights Reserved.
 | 
			
		||||
										
											Binary file not shown.
										
									
								
							| 
		 Before Width: | Height: | Size: 5.2 KiB  | 
| 
						 | 
				
			
			@ -1,434 +0,0 @@
 | 
			
		|||
<HTML>
 | 
			
		||||
<HEAD>
 | 
			
		||||
<TITLE>State Threads Library Programming Notes</TITLE>
 | 
			
		||||
</HEAD>
 | 
			
		||||
<BODY BGCOLOR=#FFFFFF>
 | 
			
		||||
<H2>Programming Notes</H2>
 | 
			
		||||
<P>
 | 
			
		||||
<B>
 | 
			
		||||
<UL>
 | 
			
		||||
<LI><A HREF=#porting>Porting</A></LI>
 | 
			
		||||
<LI><A HREF=#signals>Signals</A></LI>
 | 
			
		||||
<LI><A HREF=#intra>Intra-Process Synchronization</A></LI>
 | 
			
		||||
<LI><A HREF=#inter>Inter-Process Synchronization</A></LI>
 | 
			
		||||
<LI><A HREF=#nonnet>Non-Network I/O</A></LI>
 | 
			
		||||
<LI><A HREF=#timeouts>Timeouts</A></LI>
 | 
			
		||||
</UL>
 | 
			
		||||
</B>
 | 
			
		||||
<P>
 | 
			
		||||
<HR>
 | 
			
		||||
<P>
 | 
			
		||||
<A NAME="porting">
 | 
			
		||||
<H3>Porting</H3>
 | 
			
		||||
The State Threads library uses OS concepts that are available in some
 | 
			
		||||
form on most UNIX platforms, making the library very portable across
 | 
			
		||||
many flavors of UNIX.  However, there are several parts of the library
 | 
			
		||||
that rely on platform-specific features.  Here is the list of such parts:
 | 
			
		||||
<P>
 | 
			
		||||
<UL>
 | 
			
		||||
<LI><I>Thread context initialization</I>: Two ingredients of the
 | 
			
		||||
<TT>jmp_buf</TT>
 | 
			
		||||
data structure (the program counter and the stack pointer) have to be
 | 
			
		||||
manually set in the thread creation routine. The <TT>jmp_buf</TT> data
 | 
			
		||||
structure is defined in the <TT>setjmp.h</TT> header file and differs from
 | 
			
		||||
platform to platform.  Usually the program counter is a structure member
 | 
			
		||||
with <TT>PC</TT> in the name and the stack pointer is a structure member
 | 
			
		||||
with <TT>SP</TT> in the name.  One can also look in the
 | 
			
		||||
<A HREF="http://www.mozilla.org/source.html">Netscape's NSPR library source</A>
 | 
			
		||||
which already has this code for many UNIX-like platforms
 | 
			
		||||
(<TT>mozilla/nsprpub/pr/include/md/*.h</TT> files).
 | 
			
		||||
<P>
 | 
			
		||||
Note that on some BSD-derived platforms <TT>_setjmp(3)/_longjmp(3)</TT>
 | 
			
		||||
calls should be used instead of <TT>setjmp(3)/longjmp(3)</TT> (that is
 | 
			
		||||
the calls that manipulate only the stack and registers and do <I>not</I>
 | 
			
		||||
save and restore the process's signal mask).</LI>
 | 
			
		||||
<P>
 | 
			
		||||
Starting with glibc 2.4 on Linux the opacity of the <TT>jmp_buf</TT> data
 | 
			
		||||
structure is enforced by <TT>setjmp(3)/longjmp(3)</TT> so the
 | 
			
		||||
<TT>jmp_buf</TT> ingredients cannot be accessed directly anymore (unless
 | 
			
		||||
special environmental variable LD_POINTER_GUARD is set before application
 | 
			
		||||
execution). To avoid dependency on custom environment, the State Threads
 | 
			
		||||
library provides <TT>setjmp/longjmp</TT> replacement functions for
 | 
			
		||||
all Intel CPU architectures. Other CPU architectures can also be easily
 | 
			
		||||
supported (the <TT>setjmp/longjmp</TT> source code is widely available for
 | 
			
		||||
many CPU architectures).
 | 
			
		||||
<P>
 | 
			
		||||
<LI><I>High resolution time function</I>: Some platforms (IRIX, Solaris)
 | 
			
		||||
provide a high resolution time function based on the free running hardware
 | 
			
		||||
counter.  This function returns the time counted since some arbitrary
 | 
			
		||||
moment in the past (usually machine power up time).  It is not correlated in
 | 
			
		||||
any way to the time of day, and thus is not subject to resetting,
 | 
			
		||||
drifting, etc.  This type of time is ideal for tasks where cheap, accurate
 | 
			
		||||
interval timing is required.  If such a function is not available on a
 | 
			
		||||
particular platform, the <TT>gettimeofday(3)</TT> function can be used
 | 
			
		||||
(though on some platforms it involves a system call).
 | 
			
		||||
<P>
 | 
			
		||||
<LI><I>The stack growth direction</I>: The library needs to know whether the
 | 
			
		||||
stack grows toward lower (down) or higher (up) memory addresses.
 | 
			
		||||
One can write a simple test program that detects the stack growth direction
 | 
			
		||||
on a particular platform.</LI>
 | 
			
		||||
<P>
 | 
			
		||||
<LI><I>Non-blocking attribute inheritance</I>: On some platforms (e.g. IRIX)
 | 
			
		||||
the socket created as a result of the <TT>accept(2)</TT> call inherits the
 | 
			
		||||
non-blocking attribute of the listening socket. One needs to consult the manual
 | 
			
		||||
pages or write a simple test program to see if this applies to a specific
 | 
			
		||||
platform.</LI>
 | 
			
		||||
<P>
 | 
			
		||||
<LI><I>Anonymous memory mapping</I>: The library allocates memory segments
 | 
			
		||||
for thread stacks by doing anonymous memory mapping (<TT>mmap(2)</TT>). This
 | 
			
		||||
mapping is somewhat different on SVR4 and BSD4.3 derived platforms.
 | 
			
		||||
<P>
 | 
			
		||||
The memory mapping can be avoided altogether by using <TT>malloc(3)</TT> for
 | 
			
		||||
stack allocation.  In this case the <TT>MALLOC_STACK</TT> macro should be
 | 
			
		||||
defined.</LI>
 | 
			
		||||
</UL>
 | 
			
		||||
<P>
 | 
			
		||||
All machine-dependent feature test macros should be defined in the
 | 
			
		||||
<TT>md.h</TT> header file. The assembly code for <TT>setjmp/longjmp</TT>
 | 
			
		||||
replacement functions for all CPU architectures should be placed in
 | 
			
		||||
the <TT>md.S</TT> file.
 | 
			
		||||
<P>
 | 
			
		||||
The current version of the library is ported to:
 | 
			
		||||
<UL>
 | 
			
		||||
  <LI>IRIX 6.x (both 32 and 64 bit)</LI>
 | 
			
		||||
  <LI>Linux (kernel 2.x and glibc 2.x) on x86, Alpha, MIPS and MIPSEL,
 | 
			
		||||
  SPARC, ARM, PowerPC, 68k, HPPA, S390, IA-64, and Opteron (AMD-64)</LI>
 | 
			
		||||
  <LI>Solaris 2.x (SunOS 5.x) on x86, AMD64, SPARC, and SPARC-64</LI>
 | 
			
		||||
  <LI>AIX 4.x</LI>
 | 
			
		||||
  <LI>HP-UX 11 (both 32 and 64 bit)</LI>
 | 
			
		||||
  <LI>Tru64/OSF1</LI>
 | 
			
		||||
  <LI>FreeBSD on x86, AMD64, and Alpha</LI>
 | 
			
		||||
  <LI>OpenBSD on x86, AMD64, Alpha, and SPARC</LI>
 | 
			
		||||
  <LI>NetBSD on x86, Alpha, SPARC, and VAX</LI>
 | 
			
		||||
  <LI>MacOS X (Darwin) on PowerPC (32 bit) and Intel (both 32 and 64 bit) [universal]</LI>
 | 
			
		||||
  <LI>Cygwin</LI>
 | 
			
		||||
</UL>
 | 
			
		||||
<P>
 | 
			
		||||
 | 
			
		||||
<A NAME="signals">
 | 
			
		||||
<H3>Signals</H3>
 | 
			
		||||
Signal handling in an application using State Threads should be treated the
 | 
			
		||||
same way as in a classical UNIX process application. There is no such
 | 
			
		||||
thing as per-thread signal mask, all threads share the same signal handlers,
 | 
			
		||||
and only asynchronous-safe functions can be used in signal handlers.
 | 
			
		||||
However, there is a way to process signals synchronously by converting a
 | 
			
		||||
signal event to an I/O event: a signal catching function does a write to
 | 
			
		||||
a pipe which will be processed synchronously by a dedicated signal handling
 | 
			
		||||
thread.  The following code demonstrates this technique (error handling is
 | 
			
		||||
omitted for clarity):
 | 
			
		||||
<PRE>
 | 
			
		||||
 | 
			
		||||
/* Per-process pipe which is used as a signal queue. */
 | 
			
		||||
/* Up to PIPE_BUF/sizeof(int) signals can be queued up. */
 | 
			
		||||
int sig_pipe[2];
 | 
			
		||||
 | 
			
		||||
/* Signal catching function. */
 | 
			
		||||
/* Converts signal event to I/O event. */
 | 
			
		||||
void sig_catcher(int signo)
 | 
			
		||||
{
 | 
			
		||||
  int err;
 | 
			
		||||
 | 
			
		||||
  /* Save errno to restore it after the write() */
 | 
			
		||||
  err = errno;
 | 
			
		||||
  /* write() is reentrant/async-safe */
 | 
			
		||||
  write(sig_pipe[1], &signo, sizeof(int));
 | 
			
		||||
  errno = err;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Signal processing function. */
 | 
			
		||||
/* This is the "main" function of the signal processing thread. */
 | 
			
		||||
void *sig_process(void *arg)
 | 
			
		||||
{
 | 
			
		||||
  st_netfd_t nfd;
 | 
			
		||||
  int signo;
 | 
			
		||||
 | 
			
		||||
  nfd = st_netfd_open(sig_pipe[0]);
 | 
			
		||||
 | 
			
		||||
  for ( ; ; ) {
 | 
			
		||||
    /* Read the next signal from the pipe */
 | 
			
		||||
    st_read(nfd, &signo, sizeof(int), ST_UTIME_NO_TIMEOUT);
 | 
			
		||||
 | 
			
		||||
    /* Process signal synchronously */
 | 
			
		||||
    switch (signo) {
 | 
			
		||||
    case SIGHUP:
 | 
			
		||||
      /* do something here - reread config files, etc. */
 | 
			
		||||
      break;
 | 
			
		||||
    case SIGTERM:
 | 
			
		||||
      /* do something here - cleanup, etc. */
 | 
			
		||||
      break;
 | 
			
		||||
      /*      .
 | 
			
		||||
              .
 | 
			
		||||
         Other signals
 | 
			
		||||
              .
 | 
			
		||||
              .
 | 
			
		||||
      */
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int main(int argc, char *argv[])
 | 
			
		||||
{
 | 
			
		||||
  struct sigaction sa;
 | 
			
		||||
        .
 | 
			
		||||
        .
 | 
			
		||||
        .
 | 
			
		||||
 | 
			
		||||
  /* Create signal pipe */
 | 
			
		||||
  pipe(sig_pipe);
 | 
			
		||||
 | 
			
		||||
  /* Create signal processing thread */
 | 
			
		||||
  st_thread_create(sig_process, NULL, 0, 0);
 | 
			
		||||
 | 
			
		||||
  /* Install sig_catcher() as a signal handler */
 | 
			
		||||
  sa.sa_handler = sig_catcher;
 | 
			
		||||
  sigemptyset(&sa.sa_mask);
 | 
			
		||||
  sa.sa_flags = 0;
 | 
			
		||||
  sigaction(SIGHUP, &sa, NULL);
 | 
			
		||||
 | 
			
		||||
  sa.sa_handler = sig_catcher;
 | 
			
		||||
  sigemptyset(&sa.sa_mask);
 | 
			
		||||
  sa.sa_flags = 0;
 | 
			
		||||
  sigaction(SIGTERM, &sa, NULL);
 | 
			
		||||
 | 
			
		||||
        .
 | 
			
		||||
        .
 | 
			
		||||
        .
 | 
			
		||||
      
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
</PRE>
 | 
			
		||||
<P>
 | 
			
		||||
Note that if multiple processes are used (see below), the signal pipe should
 | 
			
		||||
be initialized after the <TT>fork(2)</TT> call so that each process has its
 | 
			
		||||
own private pipe.
 | 
			
		||||
<P>
 | 
			
		||||
 | 
			
		||||
<A NAME="intra">
 | 
			
		||||
<H3>Intra-Process Synchronization</H3>
 | 
			
		||||
Due to the event-driven nature of the library scheduler, the thread context
 | 
			
		||||
switch (process state change) can only happen in a well-known set of
 | 
			
		||||
library functions.  This set includes functions in which a thread may
 | 
			
		||||
"block":<TT>  </TT>I/O functions (<TT>st_read(), st_write(), </TT>etc.),
 | 
			
		||||
sleep functions (<TT>st_sleep(), </TT>etc.), and thread synchronization
 | 
			
		||||
functions (<TT>st_thread_join(), st_cond_wait(), </TT>etc.).  As a result,
 | 
			
		||||
process-specific global data need not to be protected by locks since a thread
 | 
			
		||||
cannot be rescheduled while in a critical section (and only one thread at a
 | 
			
		||||
time can access the same memory location).  By the same token,
 | 
			
		||||
non thread-safe functions (in a traditional sense) can be safely used with
 | 
			
		||||
the State Threads.  The library's mutex facilities are practically useless
 | 
			
		||||
for a correctly written application (no blocking functions in critical
 | 
			
		||||
section) and are provided mostly for completeness.  This absence of locking
 | 
			
		||||
greatly simplifies an application design and provides a foundation for
 | 
			
		||||
scalability.
 | 
			
		||||
<P>
 | 
			
		||||
 | 
			
		||||
<A NAME="inter">
 | 
			
		||||
<H3>Inter-Process Synchronization</H3>
 | 
			
		||||
The State Threads library makes it possible to multiplex a large number
 | 
			
		||||
of simultaneous connections onto a much smaller number of separate 
 | 
			
		||||
processes, where each process uses a many-to-one user-level threading
 | 
			
		||||
implementation (<B>N</B> of <B>M:1</B> mappings rather than one <B>M:N</B>
 | 
			
		||||
mapping used in native threading libraries on some platforms). This design
 | 
			
		||||
is key to the application's scalability.  One can think about it as if a
 | 
			
		||||
set of all threads is partitioned into separate groups (processes) where
 | 
			
		||||
each group has a separate pool of resources (virtual address space, file
 | 
			
		||||
descriptors, etc.).  An application designer has full control of how many
 | 
			
		||||
groups (processes) an application creates and what resources, if any,
 | 
			
		||||
are shared among different groups via standard UNIX inter-process
 | 
			
		||||
communication (IPC) facilities.<P>
 | 
			
		||||
There are several reasons for creating multiple processes:
 | 
			
		||||
<P>
 | 
			
		||||
<UL>
 | 
			
		||||
<LI>To take advantage of multiple hardware entities (CPUs, disks, etc.)
 | 
			
		||||
available in the system (hardware parallelism).</LI>
 | 
			
		||||
<P>
 | 
			
		||||
<LI>To reduce risk of losing a large number of user connections when one of
 | 
			
		||||
the processes crashes. For example, if <B>C</B> user connections (threads)
 | 
			
		||||
are multiplexed onto <B>P</B> processes and one of the processes crashes,
 | 
			
		||||
only a fraction (<B>C/P</B>) of all connections will be lost.</LI>
 | 
			
		||||
<P>
 | 
			
		||||
<LI>To overcome per-process resource limitations imposed by the OS.  For
 | 
			
		||||
example, if <TT>select(2)</TT> is used for event polling, the number of
 | 
			
		||||
simultaneous connections (threads) per process is
 | 
			
		||||
limited by the <TT>FD_SETSIZE</TT> parameter (see <TT>select(2)</TT>).
 | 
			
		||||
If <TT>FD_SETSIZE</TT> is equal to 1024 and each connection needs one file
 | 
			
		||||
descriptor, then an application should create 10 processes to support 10,000
 | 
			
		||||
simultaneous connections.</LI>
 | 
			
		||||
</UL>
 | 
			
		||||
<P>
 | 
			
		||||
Ideally all user sessions are completely independent, so there is no need for
 | 
			
		||||
inter-process communication.  It is always better to have several separate
 | 
			
		||||
smaller process-specific resources (e.g., data caches) than to have one large
 | 
			
		||||
resource shared (and modified) by all processes.  Sometimes, however, there
 | 
			
		||||
is a need to share a common resource among different processes.  In that case,
 | 
			
		||||
standard UNIX IPC facilities can be used.  In addition to that, there is a way
 | 
			
		||||
to synchronize different processes so that only the thread accessing the
 | 
			
		||||
shared resource will be suspended (but not the entire process) if that resource
 | 
			
		||||
is unavailable.  In the following code fragment a pipe is used as a counting
 | 
			
		||||
semaphore for inter-process synchronization:
 | 
			
		||||
<PRE>
 | 
			
		||||
#ifndef PIPE_BUF
 | 
			
		||||
#define PIPE_BUF 512  /* POSIX */
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* Semaphore data structure */
 | 
			
		||||
typedef struct ipc_sem {
 | 
			
		||||
  st_netfd_t rdfd;  /* read descriptor */
 | 
			
		||||
  st_netfd_t wrfd;  /* write descriptor */
 | 
			
		||||
} ipc_sem_t;
 | 
			
		||||
 | 
			
		||||
/* Create and initialize the semaphore. Should be called before fork(2). */
 | 
			
		||||
/* 'value' must be less than PIPE_BUF. */
 | 
			
		||||
/* If 'value' is 1, the semaphore works as mutex. */
 | 
			
		||||
ipc_sem_t *ipc_sem_create(int value)
 | 
			
		||||
{
 | 
			
		||||
  ipc_sem_t *sem;
 | 
			
		||||
  int p[2];
 | 
			
		||||
  char b[PIPE_BUF];
 | 
			
		||||
 | 
			
		||||
  /* Error checking is omitted for clarity */
 | 
			
		||||
  sem = malloc(sizeof(ipc_sem_t));
 | 
			
		||||
 | 
			
		||||
  /* Create the pipe */
 | 
			
		||||
  pipe(p);
 | 
			
		||||
  sem->rdfd = st_netfd_open(p[0]);
 | 
			
		||||
  sem->wrfd = st_netfd_open(p[1]);
 | 
			
		||||
 | 
			
		||||
  /* Initialize the semaphore: put 'value' bytes into the pipe */
 | 
			
		||||
  write(p[1], b, value);
 | 
			
		||||
 | 
			
		||||
  return sem;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Try to decrement the "value" of the semaphore. */
 | 
			
		||||
/* If "value" is 0, the calling thread blocks on the semaphore. */
 | 
			
		||||
int ipc_sem_wait(ipc_sem_t *sem)
 | 
			
		||||
{
 | 
			
		||||
  char c;
 | 
			
		||||
 | 
			
		||||
  /* Read one byte from the pipe */
 | 
			
		||||
  if (st_read(sem->rdfd, &c, 1, ST_UTIME_NO_TIMEOUT) != 1)
 | 
			
		||||
    return -1;
 | 
			
		||||
 | 
			
		||||
  return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Increment the "value" of the semaphore. */
 | 
			
		||||
int ipc_sem_post(ipc_sem_t *sem)
 | 
			
		||||
{
 | 
			
		||||
  char c;
 | 
			
		||||
 | 
			
		||||
  if (st_write(sem->wrfd, &c, 1, ST_UTIME_NO_TIMEOUT) != 1)
 | 
			
		||||
    return -1;
 | 
			
		||||
 | 
			
		||||
  return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
</PRE>
 | 
			
		||||
<P>
 | 
			
		||||
 | 
			
		||||
Generally, the following steps should be followed when writing an application
 | 
			
		||||
using the State Threads library:
 | 
			
		||||
<P>
 | 
			
		||||
<OL>
 | 
			
		||||
<LI>Initialize the library (<TT>st_init()</TT>).</LI>
 | 
			
		||||
<P>
 | 
			
		||||
<LI>Create resources that will be shared among different processes:
 | 
			
		||||
    create and bind listening sockets, create shared memory segments, IPC
 | 
			
		||||
    channels, synchronization primitives, etc.</LI>
 | 
			
		||||
<P>
 | 
			
		||||
<LI>Create several processes (<TT>fork(2)</TT>). The parent process should
 | 
			
		||||
    either exit or become a "watchdog" (e.g., it starts a new process when
 | 
			
		||||
    an existing one crashes, does a cleanup upon application termination,
 | 
			
		||||
    etc.).</LI>
 | 
			
		||||
<P>
 | 
			
		||||
<LI>In each child process create a pool of threads
 | 
			
		||||
    (<TT>st_thread_create()</TT>) to handle user connections.</LI>
 | 
			
		||||
</OL>
 | 
			
		||||
<P>
 | 
			
		||||
 | 
			
		||||
<A NAME="nonnet">
 | 
			
		||||
<H3>Non-Network I/O</H3>
 | 
			
		||||
 | 
			
		||||
The State Threads architecture uses non-blocking I/O on
 | 
			
		||||
<TT>st_netfd_t</TT> objects for concurrent processing of multiple user
 | 
			
		||||
connections.  This architecture has a drawback:  the entire process and
 | 
			
		||||
all its threads may block for the duration of a <I>disk</I> or other
 | 
			
		||||
non-network I/O operation, whether through State Threads I/O functions,
 | 
			
		||||
direct system calls, or standard I/O functions.  (This is applicable
 | 
			
		||||
mostly to disk <I>reads</I>; disk <I>writes</I> are usually performed
 | 
			
		||||
asynchronously -- data goes to the buffer cache to be written to disk
 | 
			
		||||
later.)  Fortunately, disk I/O (unlike network I/O) usually takes a
 | 
			
		||||
finite and predictable amount of time, but this may not be true for
 | 
			
		||||
special devices or user input devices (including stdin).  Nevertheless,
 | 
			
		||||
such I/O reduces throughput of the system and increases response times.
 | 
			
		||||
There are several ways to design an application to overcome this
 | 
			
		||||
drawback:
 | 
			
		||||
 | 
			
		||||
<P>
 | 
			
		||||
<UL>
 | 
			
		||||
<LI>Create several identical main processes as described above (symmetric
 | 
			
		||||
    architecture).  This will improve CPU utilization and thus improve the
 | 
			
		||||
    overall throughput of the system.</LI>
 | 
			
		||||
<P>
 | 
			
		||||
<LI>Create multiple "helper" processes in addition to the main process that
 | 
			
		||||
    will handle blocking I/O operations (asymmetric architecture).
 | 
			
		||||
    This approach was suggested for Web servers in a
 | 
			
		||||
    <A HREF="http://www.cs.rice.edu/~vivek/flash99/">paper</A> by Peter
 | 
			
		||||
    Druschel et al. In this architecture the main process communicates with
 | 
			
		||||
    a helper process via an IPC channel (<TT>pipe(2), socketpair(2)</TT>).
 | 
			
		||||
    The main process instructs a helper to perform the potentially blocking
 | 
			
		||||
    operation.  Once the operation completes, the helper returns a
 | 
			
		||||
    notification via IPC.
 | 
			
		||||
</UL>
 | 
			
		||||
<P>
 | 
			
		||||
 | 
			
		||||
<A NAME="timeouts">
 | 
			
		||||
<H3>Timeouts</H3>
 | 
			
		||||
 | 
			
		||||
The <TT>timeout</TT> parameter to <TT>st_cond_timedwait()</TT> and the
 | 
			
		||||
I/O functions, and the arguments to <TT>st_sleep()</TT> and
 | 
			
		||||
<TT>st_usleep()</TT> specify a maximum time to wait <I>since the last
 | 
			
		||||
context switch</I> not since the beginning of the function call.
 | 
			
		||||
 | 
			
		||||
<P>The State Threads' time resolution is actually the time interval
 | 
			
		||||
between context switches.  That time interval may be large in some
 | 
			
		||||
situations, for example, when a single thread does a lot of work
 | 
			
		||||
continuously.  Note that a steady, uninterrupted stream of network I/O
 | 
			
		||||
qualifies for this description; a context switch occurs only when a
 | 
			
		||||
thread blocks.
 | 
			
		||||
 | 
			
		||||
<P>If a specified I/O timeout is less than the time interval between
 | 
			
		||||
context switches the function may return with a timeout error before
 | 
			
		||||
that amount of time has elapsed since the beginning of the function
 | 
			
		||||
call.  For example, if eight milliseconds have passed since the last
 | 
			
		||||
context switch and an I/O function with a timeout of 10 milliseconds
 | 
			
		||||
blocks, causing a switch, the call may return with a timeout error as
 | 
			
		||||
little as two milliseconds after it was called.  (On Linux,
 | 
			
		||||
<TT>select()</TT>'s timeout is an <I>upper</I> bound on the amount of
 | 
			
		||||
time elapsed before select returns.)  Similarly, if 12 ms have passed
 | 
			
		||||
already, the function may return immediately.
 | 
			
		||||
 | 
			
		||||
<P>In almost all cases I/O timeouts should be used only for detecting a
 | 
			
		||||
broken network connection or for preventing a peer from holding an idle
 | 
			
		||||
connection for too long.  Therefore for most applications realistic I/O
 | 
			
		||||
timeouts should be on the order of seconds.  Furthermore, there's
 | 
			
		||||
probably no point in retrying operations that time out.  Rather than
 | 
			
		||||
retrying simply use a larger timeout in the first place.
 | 
			
		||||
 | 
			
		||||
<P>The largest valid timeout value is platform-dependent and may be
 | 
			
		||||
significantly less than <TT>INT_MAX</TT> seconds for <TT>select()</TT>
 | 
			
		||||
or <TT>INT_MAX</TT> milliseconds for <TT>poll()</TT>.  Generally, you
 | 
			
		||||
should not use timeouts exceeding several hours.  Use
 | 
			
		||||
<tt>ST_UTIME_NO_TIMEOUT</tt> (<tt>-1</tt>) as a special value to
 | 
			
		||||
indicate infinite timeout or indefinite sleep.  Use
 | 
			
		||||
<tt>ST_UTIME_NO_WAIT</tt> (<tt>0</tt>) to indicate no waiting at all.
 | 
			
		||||
 | 
			
		||||
<P>
 | 
			
		||||
<HR>
 | 
			
		||||
<P>
 | 
			
		||||
</BODY>
 | 
			
		||||
</HTML>
 | 
			
		||||
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							| 
						 | 
				
			
			@ -1,504 +0,0 @@
 | 
			
		|||
<HTML>
 | 
			
		||||
<HEAD>
 | 
			
		||||
<TITLE>State Threads for Internet Applications</TITLE>
 | 
			
		||||
</HEAD>
 | 
			
		||||
<BODY BGCOLOR=#FFFFFF>
 | 
			
		||||
<H2>State Threads for Internet Applications</H2>
 | 
			
		||||
<H3>Introduction</H3>
 | 
			
		||||
<P>
 | 
			
		||||
State Threads is an application library which provides a
 | 
			
		||||
foundation for writing fast and highly scalable Internet Applications
 | 
			
		||||
on UNIX-like platforms.  It combines the simplicity of the multithreaded 
 | 
			
		||||
programming paradigm, in which one thread supports each simultaneous 
 | 
			
		||||
connection, with the performance and scalability of an event-driven 
 | 
			
		||||
state machine architecture.</P>
 | 
			
		||||
 | 
			
		||||
<H3>1. Definitions</H3>
 | 
			
		||||
<P>
 | 
			
		||||
<A NAME="IA">
 | 
			
		||||
<H4>1.1 Internet Applications</H4>
 | 
			
		||||
</A>
 | 
			
		||||
<P>
 | 
			
		||||
An <I>Internet Application</I> (IA) is either a server or client network
 | 
			
		||||
application that accepts connections from clients and may or may not 
 | 
			
		||||
connect to servers.  In an IA the arrival or departure of network data
 | 
			
		||||
often controls processing (that is, IA is a <I>data-driven</I> application).
 | 
			
		||||
For each connection, an IA does some finite amount of work 
 | 
			
		||||
involving data exchange with its peer, where its peer may be either 
 | 
			
		||||
a client or a server.
 | 
			
		||||
The typical transaction steps of an IA are to accept a connection,
 | 
			
		||||
read a request, do some finite and predictable amount of work to 
 | 
			
		||||
process the request, then write a response to the peer that sent the 
 | 
			
		||||
request.  One example of an IA is a Web server; 
 | 
			
		||||
the most general example of an IA is a proxy server, because it both 
 | 
			
		||||
accepts connections from clients and connects to other servers.</P>
 | 
			
		||||
<P>
 | 
			
		||||
We assume that the performance of an IA is constrained by available CPU
 | 
			
		||||
cycles rather than network bandwidth or disk I/O (that is, CPU
 | 
			
		||||
is a bottleneck resource).
 | 
			
		||||
<P>
 | 
			
		||||
 | 
			
		||||
<A NAME="PS">
 | 
			
		||||
<H4>1.2 Performance and Scalability</H4>
 | 
			
		||||
</A>
 | 
			
		||||
<P>
 | 
			
		||||
The <I>performance</I> of an IA is usually evaluated as its
 | 
			
		||||
throughput measured in transactions per second or bytes per second (one
 | 
			
		||||
can be converted to the other, given the average transaction size).  There are
 | 
			
		||||
several benchmarks that can be used to measure throughput of Web serving
 | 
			
		||||
applications for specific workloads (such as 
 | 
			
		||||
<A HREF="http://www.spec.org/osg/web96/">SPECweb96</A>,
 | 
			
		||||
<A HREF="http://www.mindcraft.com/webstone/">WebStone</A>,
 | 
			
		||||
<A HREF="http://www.zdnet.com/zdbop/webbench/">WebBench</A>).
 | 
			
		||||
Although there is no common definition for <I>scalability</I>, in general it
 | 
			
		||||
expresses the ability of an application to sustain its performance when some
 | 
			
		||||
external condition changes.  For IAs this external condition is either the
 | 
			
		||||
number of clients (also known as "users," "simultaneous connections," or "load
 | 
			
		||||
generators") or the underlying hardware system size (number of CPUs, memory
 | 
			
		||||
size, and so on).  Thus there are two types of scalability: <I>load
 | 
			
		||||
scalability</I> and <I>system scalability</I>, respectively.
 | 
			
		||||
<P>
 | 
			
		||||
The figure below shows how the throughput of an idealized IA changes with
 | 
			
		||||
the increasing number of clients (solid blue line).  Initially the throughput
 | 
			
		||||
grows linearly (the slope represents the maximal throughput that one client
 | 
			
		||||
can provide). Within this initial range, the IA is underutilized and CPUs are
 | 
			
		||||
partially idle.  Further increase in the number of clients leads to a system
 | 
			
		||||
saturation, and the throughput gradually stops growing as all CPUs become fully
 | 
			
		||||
utilized.  After that point, the throughput stays flat because there are no
 | 
			
		||||
more CPU cycles available.
 | 
			
		||||
In the real world, however, each simultaneous connection
 | 
			
		||||
consumes some computational and memory resources, even when idle, and this
 | 
			
		||||
overhead grows with the number of clients.  Therefore, the throughput of the
 | 
			
		||||
real world IA starts dropping after some point (dashed blue line in the figure
 | 
			
		||||
below).  The rate at which the throughput drops depends, among other things, on
 | 
			
		||||
application design.
 | 
			
		||||
<P>
 | 
			
		||||
We say that an application has a good <I>load scalability</I> if it can
 | 
			
		||||
sustain its throughput over a wide range of loads.
 | 
			
		||||
Interestingly, the <A HREF="http://www.spec.org/osg/web99/">SPECweb99</A>
 | 
			
		||||
benchmark somewhat reflects the Web server's load scalability because it
 | 
			
		||||
measures the number of clients (load generators) given a mandatory minimal
 | 
			
		||||
throughput per client (that is, it measures the server's <I>capacity</I>).
 | 
			
		||||
This is unlike <A HREF="http://www.spec.org/osg/web96/">SPECweb96</A> and
 | 
			
		||||
other benchmarks that use the throughput as their main metric (see the figure
 | 
			
		||||
below).
 | 
			
		||||
<P>
 | 
			
		||||
<CENTER><IMG SRC="fig.gif" ALT="Figure: Throughput vs. Number of clients">
 | 
			
		||||
</CENTER>
 | 
			
		||||
<P>
 | 
			
		||||
<I>System scalability</I> is the ability of an application to sustain its
 | 
			
		||||
performance per hardware unit (such as a CPU) with the increasing number of
 | 
			
		||||
these units.  In other words, good system scalability means that doubling the
 | 
			
		||||
number of processors will roughly double the application's throughput (dashed
 | 
			
		||||
green line).  We assume here that the underlying operating system also scales
 | 
			
		||||
well.  Good system scalability allows you to initially run an application on 
 | 
			
		||||
the smallest system possible, while retaining the ability to move that
 | 
			
		||||
application to a larger system if necessary, without excessive effort or
 | 
			
		||||
expense.  That is, an application need not be rewritten or even undergo a
 | 
			
		||||
major porting effort when changing system size.
 | 
			
		||||
<P>
 | 
			
		||||
Although scalability and performance are more important in the case of server
 | 
			
		||||
IAs, they should also be considered for some client applications (such as 
 | 
			
		||||
benchmark load generators).
 | 
			
		||||
<P>
 | 
			
		||||
 | 
			
		||||
<A NAME="CONC">
 | 
			
		||||
<H4>1.3 Concurrency</H4>
 | 
			
		||||
</A>
 | 
			
		||||
<P>
 | 
			
		||||
Concurrency reflects the parallelism in a system.  The two unrelated types 
 | 
			
		||||
are <I>virtual</I> concurrency and <I>real</I> concurrency.
 | 
			
		||||
<UL>
 | 
			
		||||
<LI>Virtual (or apparent) concurrency is the number of simultaneous
 | 
			
		||||
connections that a system supports.
 | 
			
		||||
<BR><BR>
 | 
			
		||||
<LI>Real concurrency is the number of hardware devices, including
 | 
			
		||||
CPUs, network cards, and disks, that actually allow a system to perform 
 | 
			
		||||
tasks in parallel.
 | 
			
		||||
</UL>
 | 
			
		||||
<P>
 | 
			
		||||
An IA must provide virtual concurrency in order to serve many users
 | 
			
		||||
simultaneously.
 | 
			
		||||
To achieve maximum performance and scalability in doing so, the number of
 | 
			
		||||
programming entities than an IA creates to be scheduled by the OS kernel
 | 
			
		||||
should be
 | 
			
		||||
kept close to (within an order of magnitude of) the real concurrency found on
 | 
			
		||||
the system. These programming entities scheduled by the kernel are known as
 | 
			
		||||
<I>kernel execution vehicles</I>. Examples of kernel execution vehicles
 | 
			
		||||
include Solaris lightweight processes and IRIX kernel threads.
 | 
			
		||||
In other words, the number of kernel execution vehicles should be dictated by
 | 
			
		||||
the system size and not by the number of simultaneous connections.
 | 
			
		||||
<P>
 | 
			
		||||
 | 
			
		||||
<H3>2. Existing Architectures</H3>
 | 
			
		||||
<P>
 | 
			
		||||
There are a few different architectures that are commonly used by IAs. 
 | 
			
		||||
These include the <I>Multi-Process</I>, 
 | 
			
		||||
<I>Multi-Threaded</I>, and <I>Event-Driven State Machine</I> 
 | 
			
		||||
architectures.
 | 
			
		||||
<P>
 | 
			
		||||
<A NAME="MP">
 | 
			
		||||
<H4>2.1 Multi-Process Architecture</H4>
 | 
			
		||||
</A>
 | 
			
		||||
<P>
 | 
			
		||||
In the Multi-Process (MP) architecture, an individual process is 
 | 
			
		||||
dedicated to each simultaneous connection.
 | 
			
		||||
A process performs all of a transaction's initialization steps 
 | 
			
		||||
and services a connection completely before moving on to service 
 | 
			
		||||
a new connection.
 | 
			
		||||
<P>
 | 
			
		||||
User sessions in IAs are relatively independent; therefore, no 
 | 
			
		||||
synchronization between processes handling different connections is
 | 
			
		||||
necessary.  Because each process has its own private address space,
 | 
			
		||||
this architecture is very robust. If a process serving one of the connections
 | 
			
		||||
crashes, the other sessions will not be affected.  However, to serve many
 | 
			
		||||
concurrent connections, an equal number of processes must be employed.
 | 
			
		||||
Because processes are kernel entities (and are in fact the heaviest ones), 
 | 
			
		||||
the number of kernel entities will be at least as large as the number of 
 | 
			
		||||
concurrent sessions. On most systems, good performance will not be achieved 
 | 
			
		||||
when more than a few hundred processes are created because of the high 
 | 
			
		||||
context-switching overhead. In other words, MP applications have poor load 
 | 
			
		||||
scalability.
 | 
			
		||||
<P>
 | 
			
		||||
On the other hand, MP applications have very good system scalability, because
 | 
			
		||||
no resources are shared among different processes and there is no
 | 
			
		||||
synchronization overhead.
 | 
			
		||||
<P>
 | 
			
		||||
The Apache Web Server 1.x (<A HREF=#refs1>[Reference 1]</A>) uses the MP 
 | 
			
		||||
architecture on UNIX systems.
 | 
			
		||||
<P>
 | 
			
		||||
<A NAME="MT">
 | 
			
		||||
<H4>2.2 Multi-Threaded Architecture</H4>
 | 
			
		||||
</A>
 | 
			
		||||
<P>
 | 
			
		||||
In the Multi-Threaded (MT) architecture, multiple independent threads 
 | 
			
		||||
of control are employed within a single shared address space.  Like a 
 | 
			
		||||
process in the MP architecture, each thread performs all of a
 | 
			
		||||
transaction's initialization steps and services a connection completely
 | 
			
		||||
before moving on to service a new connection.
 | 
			
		||||
<P>
 | 
			
		||||
Many modern UNIX operating systems implement a <I>many-to-few</I> model when 
 | 
			
		||||
mapping user-level threads to kernel entities.  In this model, an 
 | 
			
		||||
arbitrarily large number of user-level threads is multiplexed onto a 
 | 
			
		||||
lesser number of kernel execution vehicles.  Kernel execution 
 | 
			
		||||
vehicles are also known as <I>virtual processors</I>.  Whenever a user-level
 | 
			
		||||
thread makes a blocking system call, the kernel execution vehicle it is using
 | 
			
		||||
will become blocked in the kernel.  If there are no other non-blocked kernel
 | 
			
		||||
execution vehicles and there are other runnable user-level threads, a new
 | 
			
		||||
kernel execution vehicle will be created automatically.  This prevents the
 | 
			
		||||
application from blocking when it can continue to make useful forward
 | 
			
		||||
progress.
 | 
			
		||||
<P>
 | 
			
		||||
Because IAs are by nature network I/O driven, all concurrent sessions block on
 | 
			
		||||
network I/O at various points.  As a result, the number of virtual processors
 | 
			
		||||
created in the kernel grows close to the number of user-level threads
 | 
			
		||||
(or simultaneous connections).  When this occurs, the many-to-few model
 | 
			
		||||
effectively degenerates to a <I>one-to-one</I> model.  Again, like in
 | 
			
		||||
the MP architecture, the number of kernel execution vehicles is dictated by
 | 
			
		||||
the number of simultaneous connections rather than by number of CPUs.  This
 | 
			
		||||
reduces an application's load scalability.  However, because kernel threads
 | 
			
		||||
(lightweight processes) use fewer resources and are more light-weight than
 | 
			
		||||
traditional UNIX processes, an MT application should scale better with load
 | 
			
		||||
than an MP application.
 | 
			
		||||
<P>
 | 
			
		||||
Unexpectedly, the small number of virtual processors sharing the same address
 | 
			
		||||
space in the MT architecture destroys an application's system scalability
 | 
			
		||||
because of contention among the threads on various locks.  Even if an
 | 
			
		||||
application itself is carefully
 | 
			
		||||
optimized to avoid lock contention around its own global data (a non-trivial
 | 
			
		||||
task), there are still standard library functions and system calls
 | 
			
		||||
that use common resources hidden from the application.  For example,
 | 
			
		||||
on many platforms thread safety of memory allocation routines
 | 
			
		||||
(<TT>malloc(3)</TT>, <TT>free(3)</TT>, and so on) is achieved by using a single
 | 
			
		||||
global lock.  Another example is a per-process file descriptor table.
 | 
			
		||||
This common resource table is shared by all kernel execution vehicles within
 | 
			
		||||
the same process and must be protected when one modifies it via
 | 
			
		||||
certain system calls (such as <TT>open(2)</TT>, <TT>close(2)</TT>, and so on).
 | 
			
		||||
In addition to that, maintaining the caches coherent
 | 
			
		||||
among CPUs on multiprocessor systems hurts performance when different threads
 | 
			
		||||
running on different CPUs modify data items on the same cache line.
 | 
			
		||||
<P>
 | 
			
		||||
In order to improve load scalability, some applications employ a different
 | 
			
		||||
type of MT architecture:  they create one or more thread(s) <I>per task</I>
 | 
			
		||||
rather than one thread <I>per connection</I>.  For example, one small group
 | 
			
		||||
of threads may be responsible for accepting client connections, another 
 | 
			
		||||
for request processing, and yet another for serving responses.  The main
 | 
			
		||||
advantage of this architecture is that it eliminates the tight coupling
 | 
			
		||||
between the number of threads and number of simultaneous connections. However,
 | 
			
		||||
in this architecture, different task-specific thread groups must share common
 | 
			
		||||
work queues that must be protected by mutual exclusion locks (a typical
 | 
			
		||||
producer-consumer problem).  This adds synchronization overhead that causes an
 | 
			
		||||
application to perform badly on multiprocessor systems.  In other words, in
 | 
			
		||||
this architecture, the application's system scalability is sacrificed for the
 | 
			
		||||
sake of load scalability.
 | 
			
		||||
<P>
 | 
			
		||||
Of course, the usual nightmares of threaded programming, including data
 | 
			
		||||
corruption, deadlocks, and race conditions, also make MT architecture (in any
 | 
			
		||||
form) non-simplistic to use.
 | 
			
		||||
<P>
 | 
			
		||||
 | 
			
		||||
<A NAME="EDSM">
 | 
			
		||||
<H4>2.3 Event-Driven State Machine Architecture</H4>
 | 
			
		||||
</A>
 | 
			
		||||
<P>
 | 
			
		||||
In the Event-Driven State Machine (EDSM) architecture, a single process
 | 
			
		||||
is employed to concurrently process multiple connections. The basics of this
 | 
			
		||||
architecture are described in Comer and Stevens
 | 
			
		||||
<A HREF=#refs2>[Reference 2]</A>.
 | 
			
		||||
The EDSM architecture performs one basic data-driven step associated with
 | 
			
		||||
a particular connection at a time, thus multiplexing many concurrent
 | 
			
		||||
connections.  The process operates as a state machine that receives an event
 | 
			
		||||
and then reacts to it.
 | 
			
		||||
<P>
 | 
			
		||||
In the idle state the EDSM calls <TT>select(2)</TT> or <TT>poll(2)</TT> to
 | 
			
		||||
wait for network I/O events.  When a particular file descriptor is ready for
 | 
			
		||||
I/O, the EDSM completes the corresponding basic step (usually by invoking a
 | 
			
		||||
handler function) and starts the next one.  This architecture uses
 | 
			
		||||
non-blocking system calls to perform asynchronous network I/O operations.
 | 
			
		||||
For more details on non-blocking I/O see Stevens
 | 
			
		||||
<A HREF=#refs3>[Reference 3]</A>.
 | 
			
		||||
<P>
 | 
			
		||||
To take advantage of hardware parallelism (real concurrency), multiple
 | 
			
		||||
identical processes may be created.  This is called Symmetric Multi-Process
 | 
			
		||||
EDSM and is used, for example, in the Zeus Web Server
 | 
			
		||||
(<A HREF=#refs4>[Reference 4]</A>).  To more efficiently multiplex disk I/O,
 | 
			
		||||
special "helper" processes may be created.  This is called Asymmetric
 | 
			
		||||
Multi-Process EDSM and was proposed for Web servers by Druschel
 | 
			
		||||
and others <A HREF=#refs5>[Reference 5]</A>.
 | 
			
		||||
<P>
 | 
			
		||||
EDSM is probably the most scalable architecture for IAs.
 | 
			
		||||
Because the number of simultaneous connections (virtual concurrency) is
 | 
			
		||||
completely decoupled from the number of kernel execution vehicles (processes),
 | 
			
		||||
this architecture has very good load scalability.  It requires only minimal 
 | 
			
		||||
user-level resources to create and maintain additional connection.
 | 
			
		||||
<P>
 | 
			
		||||
Like MP applications, Multi-Process EDSM has very good system scalability
 | 
			
		||||
because no resources are shared among different processes and there is no
 | 
			
		||||
synchronization overhead.
 | 
			
		||||
<P>
 | 
			
		||||
Unfortunately, the EDSM architecture is monolithic rather than based on the
 | 
			
		||||
concept of threads, so new applications generally need to be implemented from
 | 
			
		||||
the ground up.  In effect, the EDSM architecture simulates threads and their
 | 
			
		||||
stacks the hard way.
 | 
			
		||||
<P>
 | 
			
		||||
 | 
			
		||||
<A NAME="ST">
 | 
			
		||||
<H3>3. State Threads Library</H3>
 | 
			
		||||
</A>
 | 
			
		||||
<P>
 | 
			
		||||
The State Threads library combines the advantages of all of the above
 | 
			
		||||
architectures.  The interface preserves the programming simplicity of thread
 | 
			
		||||
abstraction, allowing each simultaneous connection to be treated as a separate
 | 
			
		||||
thread of execution within a single process. The underlying implementation is
 | 
			
		||||
close to the EDSM architecture as the state of each particular concurrent
 | 
			
		||||
session is saved in a separate memory segment.
 | 
			
		||||
<P>
 | 
			
		||||
 | 
			
		||||
<H4>3.1 State Changes and Scheduling</H4>
 | 
			
		||||
<P>
 | 
			
		||||
The state of each concurrent session includes its stack environment 
 | 
			
		||||
(stack pointer, program counter, CPU registers) and its stack.  Conceptually, 
 | 
			
		||||
a thread context switch can be viewed as a process changing its state.  There 
 | 
			
		||||
are no kernel entities involved other than processes.  
 | 
			
		||||
Unlike other general-purpose threading libraries, the State Threads library
 | 
			
		||||
is fully deterministic.  The thread context switch (process state change) can
 | 
			
		||||
only happen in a well-known set of functions (at I/O points or at explicit
 | 
			
		||||
synchronization points).  As a result, process-specific global data does not
 | 
			
		||||
have to be protected by mutual exclusion locks in most cases.  The entire
 | 
			
		||||
application is free to use all the static variables and non-reentrant library
 | 
			
		||||
functions it wants, greatly simplifying programming and debugging while
 | 
			
		||||
increasing performance.  This is somewhat similar to a <I>co-routine</I> model
 | 
			
		||||
(co-operatively multitasked threads), except that no explicit yield is needed
 | 
			
		||||
--
 | 
			
		||||
sooner or later, a thread performs a blocking I/O operation and thus surrenders
 | 
			
		||||
control.  All threads of execution (simultaneous connections) have the
 | 
			
		||||
same priority, so scheduling is non-preemptive, like in the EDSM architecture.
 | 
			
		||||
Because IAs are data-driven (processing is limited by the size of network 
 | 
			
		||||
buffers and data arrival rates), scheduling is non-time-slicing.
 | 
			
		||||
<P>
 | 
			
		||||
Only two types of external events are handled by the library's
 | 
			
		||||
scheduler, because only these events can be detected by
 | 
			
		||||
<TT>select(2)</TT> or <TT>poll(2)</TT>: I/O events (a file descriptor is ready
 | 
			
		||||
for I/O) and time events
 | 
			
		||||
(some timeout has expired).  However, other types of events (such as
 | 
			
		||||
a signal sent to a process) can also be handled by converting them to I/O
 | 
			
		||||
events.  For example, a signal handling function can perform a write to a pipe
 | 
			
		||||
(<TT>write(2)</TT> is reentrant/asynchronous-safe), thus converting a signal
 | 
			
		||||
event to an I/O event.
 | 
			
		||||
<P>
 | 
			
		||||
To take advantage of hardware parallelism, as in the EDSM architecture,
 | 
			
		||||
multiple processes can be created in either a symmetric or asymmetric manner.
 | 
			
		||||
Process management is not in the library's scope but instead is left up to the
 | 
			
		||||
application.
 | 
			
		||||
<P>
 | 
			
		||||
There are several general-purpose threading libraries that implement a
 | 
			
		||||
<I>many-to-one</I> model (many user-level threads to one kernel execution
 | 
			
		||||
vehicle), using the same basic techniques as the State Threads library 
 | 
			
		||||
(non-blocking I/O, event-driven scheduler, and so on).  For an example, see GNU
 | 
			
		||||
Portable Threads (<A HREF=#refs6>[Reference 6]</A>).  Because they are
 | 
			
		||||
general-purpose, these libraries have different objectives than the State 
 | 
			
		||||
Threads library.  The State Threads library is <I>not</I> a general-purpose
 | 
			
		||||
threading library,
 | 
			
		||||
but rather an application library that targets only certain types of
 | 
			
		||||
applications (IAs) in order to achieve the highest possible performance and
 | 
			
		||||
scalability for those applications.
 | 
			
		||||
<P>
 | 
			
		||||
 | 
			
		||||
<H4>3.2 Scalability</H4>
 | 
			
		||||
<P>
 | 
			
		||||
State threads are very lightweight user-level entities, and therefore creating
 | 
			
		||||
and maintaining user connections requires minimal resources.  An application
 | 
			
		||||
using the State Threads library scales very well with the increasing number
 | 
			
		||||
of connections.
 | 
			
		||||
<P>
 | 
			
		||||
On multiprocessor systems an application should create multiple processes
 | 
			
		||||
to take advantage of hardware parallelism.  Using multiple separate processes
 | 
			
		||||
is the <I>only</I> way to achieve the highest possible system scalability.
 | 
			
		||||
This is because duplicating per-process resources is the only way to avoid
 | 
			
		||||
significant synchronization overhead on multiprocessor systems.  Creating
 | 
			
		||||
separate UNIX processes naturally offers resource duplication.  Again,
 | 
			
		||||
as in the EDSM architecture, there is no connection between the number of
 | 
			
		||||
simultaneous connections (which may be very large and changes within a wide
 | 
			
		||||
range) and the number of kernel entities (which is usually small and constant).
 | 
			
		||||
In other words, the State Threads library makes it possible to multiplex a
 | 
			
		||||
large number of simultaneous connections onto a much smaller number of
 | 
			
		||||
separate processes, thus allowing an application to scale well with both
 | 
			
		||||
the load and system size.
 | 
			
		||||
<P>
 | 
			
		||||
 | 
			
		||||
<H4>3.3 Performance</H4>
 | 
			
		||||
<P>
 | 
			
		||||
Performance is one of the library's main objectives.  The State Threads
 | 
			
		||||
library is implemented to minimize the number of system calls and 
 | 
			
		||||
to make thread creation and context switching as fast as possible.
 | 
			
		||||
For example, per-thread signal mask does not exist (unlike
 | 
			
		||||
POSIX threads), so there is no need to save and restore a process's
 | 
			
		||||
signal mask on every thread context switch. This eliminates two system
 | 
			
		||||
calls per context switch.  Signal events can be handled much more
 | 
			
		||||
efficiently by converting them to I/O events (see above).
 | 
			
		||||
<P>
 | 
			
		||||
 | 
			
		||||
<H4>3.4 Portability</H4>
 | 
			
		||||
<P>
 | 
			
		||||
The library uses the same general, underlying concepts as the EDSM 
 | 
			
		||||
architecture, including non-blocking I/O, file descriptors, and 
 | 
			
		||||
I/O multiplexing.  These concepts are available in some form on most 
 | 
			
		||||
UNIX platforms, making the library very portable across many 
 | 
			
		||||
flavors of UNIX.  There are only a few platform-dependent sections in the
 | 
			
		||||
source.
 | 
			
		||||
<P>
 | 
			
		||||
 | 
			
		||||
<H4>3.5 State Threads and NSPR</H4>
 | 
			
		||||
<P>
 | 
			
		||||
The State Threads library is a derivative of the Netscape Portable 
 | 
			
		||||
Runtime library (NSPR) <A HREF=#refs7>[Reference 7]</A>. The primary goal of 
 | 
			
		||||
NSPR is to provide a platform-independent layer for system facilities, 
 | 
			
		||||
where system facilities include threads, thread synchronization, and I/O.
 | 
			
		||||
Performance and scalability are not the main concern of NSPR.  The 
 | 
			
		||||
State Threads library addresses performance and scalability while 
 | 
			
		||||
remaining much smaller than NSPR.  It is contained in 8 source files 
 | 
			
		||||
as opposed to more than 400, but provides all the functionality that 
 | 
			
		||||
is needed to write efficient IAs on UNIX-like platforms.
 | 
			
		||||
<P>
 | 
			
		||||
 | 
			
		||||
<TABLE CELLPADDING=3>
 | 
			
		||||
<TR>
 | 
			
		||||
<TD></TD>
 | 
			
		||||
<TH>NSPR</TH>
 | 
			
		||||
<TH>State Threads</TH>
 | 
			
		||||
</TR>
 | 
			
		||||
<TR>
 | 
			
		||||
<TD><B>Lines of code</B></TD>
 | 
			
		||||
<TD ALIGN=RIGHT>~150,000</TD>
 | 
			
		||||
<TD ALIGN=RIGHT>~3000</TD>
 | 
			
		||||
</TR>
 | 
			
		||||
<TR>
 | 
			
		||||
<TD><B>Dynamic library size  <BR>(debug version)</B></TD>
 | 
			
		||||
<TD></TD>
 | 
			
		||||
<TD></TD>
 | 
			
		||||
</TR>
 | 
			
		||||
<TR>
 | 
			
		||||
<TD>IRIX</TD>
 | 
			
		||||
<TD ALIGN=RIGHT>~700 KB</TD>
 | 
			
		||||
<TD ALIGN=RIGHT>~60 KB</TD>
 | 
			
		||||
</TR>
 | 
			
		||||
<TR>
 | 
			
		||||
<TD>Linux</TD>
 | 
			
		||||
<TD ALIGN=RIGHT>~900 KB</TD>
 | 
			
		||||
<TD ALIGN=RIGHT>~70 KB</TD>
 | 
			
		||||
</TR>
 | 
			
		||||
</TABLE>
 | 
			
		||||
<P>
 | 
			
		||||
 | 
			
		||||
<H3>Conclusion</H3>
 | 
			
		||||
<P>
 | 
			
		||||
State Threads is an application library which provides a foundation for
 | 
			
		||||
writing <A HREF=#IA>Internet Applications</A>.  To summarize, it has the
 | 
			
		||||
following <I>advantages</I>:
 | 
			
		||||
<P>
 | 
			
		||||
<UL>
 | 
			
		||||
<LI>It allows the design of fast and highly scalable applications.  An
 | 
			
		||||
application will scale well with both load and number of CPUs.
 | 
			
		||||
<P>
 | 
			
		||||
<LI>It greatly simplifies application programming and debugging because, as a
 | 
			
		||||
rule, no mutual exclusion locking is necessary and the entire application is
 | 
			
		||||
free to use static variables and non-reentrant library functions.
 | 
			
		||||
</UL>
 | 
			
		||||
<P>
 | 
			
		||||
The library's main <I>limitation</I>:
 | 
			
		||||
<P>
 | 
			
		||||
<UL>
 | 
			
		||||
<LI>All I/O operations on sockets must use the State Thread library's I/O
 | 
			
		||||
functions because only those functions perform thread scheduling and prevent
 | 
			
		||||
the application's processes from blocking.
 | 
			
		||||
</UL>
 | 
			
		||||
<P>
 | 
			
		||||
 | 
			
		||||
<H3>References</H3>
 | 
			
		||||
<OL>
 | 
			
		||||
<A NAME="refs1">
 | 
			
		||||
<LI> Apache Software Foundation,
 | 
			
		||||
<A HREF="http://www.apache.org">http://www.apache.org</A>.
 | 
			
		||||
<A NAME="refs2">
 | 
			
		||||
<LI> Douglas E. Comer, David L. Stevens, <I>Internetworking With TCP/IP,
 | 
			
		||||
Vol. III: Client-Server Programming And Applications</I>, Second Edition,
 | 
			
		||||
Ch. 8, 12.
 | 
			
		||||
<A NAME="refs3">
 | 
			
		||||
<LI> W. Richard Stevens, <I>UNIX Network Programming</I>, Second Edition,
 | 
			
		||||
Vol. 1, Ch. 15.
 | 
			
		||||
<A NAME="refs4">
 | 
			
		||||
<LI> Zeus Technology Limited,
 | 
			
		||||
<A HREF="http://www.zeus.co.uk/">http://www.zeus.co.uk</A>.
 | 
			
		||||
<A NAME="refs5">
 | 
			
		||||
<LI> Peter Druschel, Vivek S. Pai, Willy Zwaenepoel,
 | 
			
		||||
<A HREF="http://www.cs.rice.edu/~druschel/usenix99flash.ps.gz">
 | 
			
		||||
Flash: An Efficient and Portable Web Server</A>. In <I>Proceedings of the
 | 
			
		||||
USENIX 1999 Annual Technical Conference</I>, Monterey, CA, June 1999.
 | 
			
		||||
<A NAME="refs6">
 | 
			
		||||
<LI> GNU Portable Threads,
 | 
			
		||||
<A HREF="http://www.gnu.org/software/pth/">http://www.gnu.org/software/pth/</A>.
 | 
			
		||||
<A NAME="refs7">
 | 
			
		||||
<LI> Netscape Portable Runtime,
 | 
			
		||||
<A HREF="http://www.mozilla.org/docs/refList/refNSPR/">http://www.mozilla.org/docs/refList/refNSPR/</A>.
 | 
			
		||||
</OL>
 | 
			
		||||
 | 
			
		||||
<H3>Other resources covering various architectural issues in IAs</H3>
 | 
			
		||||
<OL START=8>
 | 
			
		||||
<LI> Dan Kegel, <I>The C10K problem</I>,
 | 
			
		||||
<A HREF="http://www.kegel.com/c10k.html">http://www.kegel.com/c10k.html</A>.
 | 
			
		||||
</LI>
 | 
			
		||||
<LI> James C. Hu, Douglas C. Schmidt, Irfan Pyarali, <I>JAWS: Understanding
 | 
			
		||||
High Performance Web Systems</I>,
 | 
			
		||||
<A HREF="http://www.cs.wustl.edu/~jxh/research/research.html">http://www.cs.wustl.edu/~jxh/research/research.html</A>.</LI>
 | 
			
		||||
</OL>
 | 
			
		||||
<P>
 | 
			
		||||
<HR>
 | 
			
		||||
<P>
 | 
			
		||||
 | 
			
		||||
<CENTER><FONT SIZE=-1>Portions created by SGI are Copyright © 2000
 | 
			
		||||
Silicon Graphics, Inc.  All rights reserved.</FONT></CENTER>
 | 
			
		||||
<P>
 | 
			
		||||
 | 
			
		||||
</BODY>
 | 
			
		||||
</HTML>
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1,60 +0,0 @@
 | 
			
		|||
How the timeout heap works
 | 
			
		||||
 | 
			
		||||
As of version 1.5, the State Threads Library represents the queue of
 | 
			
		||||
sleeping threads using a heap data structure rather than a sorted
 | 
			
		||||
linked list.  This improves performance when there is a large number
 | 
			
		||||
of sleeping threads, since insertion into a heap takes O(log N) time
 | 
			
		||||
while insertion into a sorted list takes O(N) time.  For example, in
 | 
			
		||||
one test 1000 threads were created, each thread called st_usleep()
 | 
			
		||||
with a random time interval, and then all the threads where
 | 
			
		||||
immediately interrupted and joined before the sleeps had a chance to
 | 
			
		||||
finish.  The whole process was repeated 1000 times, for a total of a
 | 
			
		||||
million sleep queue insertions and removals.  With the old list-based
 | 
			
		||||
sleep queue, this test took 100 seconds; now it takes only 12 seconds.
 | 
			
		||||
 | 
			
		||||
Heap data structures are typically based on dynamically resized
 | 
			
		||||
arrays.  However, since the existing ST code base was very nicely
 | 
			
		||||
structured around linking the thread objects into pointer-based lists
 | 
			
		||||
without the need for any auxiliary data structures, implementing the
 | 
			
		||||
heap using a similar nodes-and-pointers based approach seemed more
 | 
			
		||||
appropriate for ST than introducing a separate array.
 | 
			
		||||
 | 
			
		||||
Thus, the new ST timeout heap works by organizing the existing
 | 
			
		||||
_st_thread_t objects in a balanced binary tree, just as they were
 | 
			
		||||
previously organized into a doubly-linked, sorted list.  The global
 | 
			
		||||
_ST_SLEEPQ variable, formerly a linked list head, is now simply a
 | 
			
		||||
pointer to the root of this tree, and the root node of the tree is the
 | 
			
		||||
thread with the earliest timeout.  Each thread object has two child
 | 
			
		||||
pointers, "left" and "right", pointing to threads with later timeouts.
 | 
			
		||||
 | 
			
		||||
Each node in the tree is numbered with an integer index, corresponding
 | 
			
		||||
to the array index in an array-based heap, and the tree is kept fully
 | 
			
		||||
balanced and left-adjusted at all times.  In other words, the tree
 | 
			
		||||
consists of any number of fully populated top levels, followed by a
 | 
			
		||||
single bottom level which may be partially populated, such that any
 | 
			
		||||
existing nodes form a contiguous block to the left and the spaces for
 | 
			
		||||
missing nodes form a contiguous block to the right.  For example, if
 | 
			
		||||
there are nine threads waiting for a timeout, they are numbered and
 | 
			
		||||
arranged in a tree exactly as follows:
 | 
			
		||||
 | 
			
		||||
              1
 | 
			
		||||
           /     \
 | 
			
		||||
          2       3
 | 
			
		||||
         / \     / \
 | 
			
		||||
        4   5   6   7
 | 
			
		||||
       / \
 | 
			
		||||
      8   9
 | 
			
		||||
 | 
			
		||||
Each node has either no children, only a left child, or both a left
 | 
			
		||||
and a right child.  Children always time out later than their parents
 | 
			
		||||
(this is called the "heap invariant"), but when a node has two
 | 
			
		||||
children, their mutual order is unspecified - the left child may time
 | 
			
		||||
out before or after the right child.  If a node is numbered N, its
 | 
			
		||||
left child is numbered 2N, and its right child is numbered 2N+1.
 | 
			
		||||
 | 
			
		||||
There is no pointer from a child to its parent; all pointers point
 | 
			
		||||
downward.  Additions and deletions both work by starting at the root
 | 
			
		||||
and traversing the tree towards the leaves, going left or right
 | 
			
		||||
according to the binary digits forming the index of the destination
 | 
			
		||||
node.  As nodes are added or deleted, existing nodes are rearranged to
 | 
			
		||||
maintain the heap invariant.
 | 
			
		||||
| 
						 | 
				
			
			@ -1,115 +0,0 @@
 | 
			
		|||
#
 | 
			
		||||
# Portions created by SGI are Copyright (C) 2000 Silicon Graphics, Inc.
 | 
			
		||||
# All Rights Reserved.
 | 
			
		||||
#
 | 
			
		||||
# Redistribution and use in source and binary forms, with or without
 | 
			
		||||
# modification, are permitted provided that the following conditions
 | 
			
		||||
# are met: 
 | 
			
		||||
#
 | 
			
		||||
# 1. Redistributions of source code must retain the above copyright
 | 
			
		||||
#    notice, this list of conditions and the following disclaimer. 
 | 
			
		||||
# 2. Redistributions in binary form must reproduce the above copyright
 | 
			
		||||
#    notice, this list of conditions and the following disclaimer in the
 | 
			
		||||
#    documentation and/or other materials provided with the distribution.
 | 
			
		||||
# 3. Neither the name of Silicon Graphics, Inc. nor the names of its
 | 
			
		||||
#    contributors may be used to endorse or promote products derived from
 | 
			
		||||
#    this software without specific prior written permission. 
 | 
			
		||||
#
 | 
			
		||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 | 
			
		||||
# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 | 
			
		||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 | 
			
		||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 | 
			
		||||
# HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 | 
			
		||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
 | 
			
		||||
# TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 | 
			
		||||
# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 | 
			
		||||
# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 | 
			
		||||
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 | 
			
		||||
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | 
			
		||||
 | 
			
		||||
##########################
 | 
			
		||||
# Supported OSes:
 | 
			
		||||
#
 | 
			
		||||
# AIX
 | 
			
		||||
# FREEBSD
 | 
			
		||||
# HPUX
 | 
			
		||||
# HPUX_64
 | 
			
		||||
# IRIX
 | 
			
		||||
# IRIX_64
 | 
			
		||||
# LINUX
 | 
			
		||||
# LINUX_IA64
 | 
			
		||||
# NETBSD
 | 
			
		||||
# OPENBSD
 | 
			
		||||
# OSF1
 | 
			
		||||
# SOLARIS
 | 
			
		||||
# SOLARIS_64
 | 
			
		||||
 | 
			
		||||
##########################
 | 
			
		||||
 | 
			
		||||
CC          = cc
 | 
			
		||||
 | 
			
		||||
SHELL       = /bin/sh
 | 
			
		||||
ECHO        = /bin/echo
 | 
			
		||||
 | 
			
		||||
DEPTH       = ..
 | 
			
		||||
BUILD       =
 | 
			
		||||
TARGETDIR   =
 | 
			
		||||
 | 
			
		||||
DEFINES     =
 | 
			
		||||
CFLAGS      =
 | 
			
		||||
OTHER_FLAGS =
 | 
			
		||||
 | 
			
		||||
OBJDIR      = $(DEPTH)/$(TARGETDIR)
 | 
			
		||||
INCDIR      = $(DEPTH)/$(TARGETDIR)
 | 
			
		||||
LIBST       = $(OBJDIR)/libst.a
 | 
			
		||||
HEADER      = $(INCDIR)/st.h
 | 
			
		||||
 | 
			
		||||
LIBRESOLV   =
 | 
			
		||||
EXTRALIBS   =
 | 
			
		||||
 | 
			
		||||
ifeq ($(OS),)
 | 
			
		||||
EXAMPLES    = unknown
 | 
			
		||||
else
 | 
			
		||||
EXAMPLES    = $(OBJDIR)/lookupdns $(OBJDIR)/proxy $(OBJDIR)/server
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
##########################
 | 
			
		||||
# Platform section.
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
ifeq (DARWIN, $(findstring DARWIN, $(OS)))
 | 
			
		||||
LIBRESOLV   = -lresolv
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
ifeq (LINUX, $(findstring LINUX, $(OS)))
 | 
			
		||||
LIBRESOLV   = -lresolv
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
ifeq (SOLARIS, $(findstring SOLARIS, $(OS)))
 | 
			
		||||
LIBRESOLV   = -lresolv
 | 
			
		||||
EXTRALIBS   = -lsocket -lnsl
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
# End of platform section.
 | 
			
		||||
##########################
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
all: $(EXAMPLES)
 | 
			
		||||
 | 
			
		||||
$(OBJDIR)/lookupdns: lookupdns.c $(OBJDIR)/res.o $(LIBST) $(HEADER)
 | 
			
		||||
	$(CC) $(CFLAGS) -I$(INCDIR) lookupdns.c $(OBJDIR)/res.o $(LIBST) $(LIBRESOLV) $(EXTRALIBS) -o $@
 | 
			
		||||
 | 
			
		||||
$(OBJDIR)/proxy: proxy.c $(LIBST) $(HEADER)
 | 
			
		||||
	$(CC) $(CFLAGS) -I$(INCDIR) proxy.c $(LIBST) $(EXTRALIBS) -o $@
 | 
			
		||||
 | 
			
		||||
$(OBJDIR)/server: server.c $(OBJDIR)/error.o $(LIBST) $(HEADER)
 | 
			
		||||
	$(CC) $(CFLAGS) -I$(INCDIR) server.c $(OBJDIR)/error.o $(LIBST) $(EXTRALIBS) -o $@
 | 
			
		||||
 | 
			
		||||
$(OBJDIR)/%.o: %.c
 | 
			
		||||
	$(CC) $(CFLAGS) -I$(INCDIR) -c $< -o $@
 | 
			
		||||
 | 
			
		||||
.DEFAULT:
 | 
			
		||||
	@cd $(DEPTH); $(MAKE) $@
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1,98 +0,0 @@
 | 
			
		|||
Portions created by SGI are Copyright (C) 2000 Silicon Graphics, Inc.
 | 
			
		||||
All Rights Reserved.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
This directory contains three example programs.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
---------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
PROGRAM
 | 
			
		||||
 | 
			
		||||
    lookupdns
 | 
			
		||||
 | 
			
		||||
FILES
 | 
			
		||||
 | 
			
		||||
    lookupdns.c
 | 
			
		||||
    res.c
 | 
			
		||||
 | 
			
		||||
USAGE
 | 
			
		||||
 | 
			
		||||
    lookupdns <hostname1> [<hostname2>] ...
 | 
			
		||||
 | 
			
		||||
DESCRIPTION
 | 
			
		||||
 | 
			
		||||
    This program performs asynchronous DNS host name resolution and reports
 | 
			
		||||
    IP address for each <hostname> specified as a command line argument.
 | 
			
		||||
    One ST thread is created for each host name.  All threads do host name
 | 
			
		||||
    resolution concurrently.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
---------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
PROGRAM
 | 
			
		||||
 | 
			
		||||
    proxy
 | 
			
		||||
 | 
			
		||||
FILES
 | 
			
		||||
 | 
			
		||||
    proxy.c
 | 
			
		||||
 | 
			
		||||
USAGE
 | 
			
		||||
 | 
			
		||||
    proxy -l <local_addr> -r <remote_addr> [-p <num_processes>] [-S]
 | 
			
		||||
 | 
			
		||||
    -l <local_addr>      bind to local address specified as [<host>]:<port>
 | 
			
		||||
    -r <remote_addr>     connect to remote address specified as <host>:<port>
 | 
			
		||||
    -p <num_processes>   create specified number of processes
 | 
			
		||||
    -S                   serialize accept() calls from different processes
 | 
			
		||||
                         on the same listening socket (if needed).
 | 
			
		||||
 | 
			
		||||
DESCRIPTION
 | 
			
		||||
 | 
			
		||||
    This program acts as a generic gateway.  It listens for connections to a
 | 
			
		||||
    local address.  Upon accepting a client connection, it connects to the
 | 
			
		||||
    specified remote address and then just pumps the data through without any
 | 
			
		||||
    modification.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
---------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
PROGRAM
 | 
			
		||||
 | 
			
		||||
    server
 | 
			
		||||
 | 
			
		||||
FILES
 | 
			
		||||
 | 
			
		||||
    server.c
 | 
			
		||||
    error.c
 | 
			
		||||
 | 
			
		||||
USAGE
 | 
			
		||||
 | 
			
		||||
    server -l <log_directory> [<options>]
 | 
			
		||||
 | 
			
		||||
    -l <log_directory>      open all log files in specified directory.
 | 
			
		||||
 | 
			
		||||
    Possible options:
 | 
			
		||||
 | 
			
		||||
    -b <host>:<port>        bind to specified address (multiple addresses
 | 
			
		||||
                            are permitted)
 | 
			
		||||
    -p <num_processes>      create specified number of processes
 | 
			
		||||
    -t <min_thr>:<max_thr>  specify thread limits per listening socket
 | 
			
		||||
                            across all processes
 | 
			
		||||
    -u <user>               change server's user id to specified value
 | 
			
		||||
    -q <backlog>            set max length of pending connections queue
 | 
			
		||||
    -a                      enable access logging
 | 
			
		||||
    -i                      run in interactive mode (useful for debugging)
 | 
			
		||||
    -S                      serialize accept() calls from different processes
 | 
			
		||||
                            on the same listening socket (if needed).
 | 
			
		||||
 | 
			
		||||
DESCRIPTION
 | 
			
		||||
 | 
			
		||||
    This program is a general server example.  It accepts a client connection
 | 
			
		||||
    and outputs a short HTML page.  It can be easily adapted to provide
 | 
			
		||||
    other services.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
---------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1,168 +0,0 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Portions created by SGI are Copyright (C) 2000 Silicon Graphics, Inc.
 | 
			
		||||
 * All Rights Reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Redistribution and use in source and binary forms, with or without
 | 
			
		||||
 * modification, are permitted provided that the following conditions
 | 
			
		||||
 * are met: 
 | 
			
		||||
 *
 | 
			
		||||
 * 1. Redistributions of source code must retain the above copyright
 | 
			
		||||
 *    notice, this list of conditions and the following disclaimer. 
 | 
			
		||||
 * 2. Redistributions in binary form must reproduce the above copyright
 | 
			
		||||
 *    notice, this list of conditions and the following disclaimer in the
 | 
			
		||||
 *    documentation and/or other materials provided with the distribution.
 | 
			
		||||
 * 3. Neither the name of Silicon Graphics, Inc. nor the names of its
 | 
			
		||||
 *    contributors may be used to endorse or promote products derived from
 | 
			
		||||
 *    this software without specific prior written permission. 
 | 
			
		||||
 *
 | 
			
		||||
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 | 
			
		||||
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 | 
			
		||||
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 | 
			
		||||
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 | 
			
		||||
 * HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 | 
			
		||||
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
 | 
			
		||||
 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 | 
			
		||||
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 | 
			
		||||
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 | 
			
		||||
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 | 
			
		||||
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <stdarg.h>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include "st.h"
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Simple error reporting functions.
 | 
			
		||||
 * Suggested in W. Richard Stevens' "Advanced Programming in UNIX
 | 
			
		||||
 * Environment".
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#define MAXLINE 4096  /* max line length */
 | 
			
		||||
 | 
			
		||||
static void err_doit(int, int, const char *, va_list);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Nonfatal error related to a system call.
 | 
			
		||||
 * Print a message and return.
 | 
			
		||||
 */
 | 
			
		||||
void err_sys_report(int fd, const char *fmt, ...)
 | 
			
		||||
{
 | 
			
		||||
  va_list ap;
 | 
			
		||||
 | 
			
		||||
  va_start(ap, fmt);
 | 
			
		||||
  err_doit(fd, 1, fmt, ap);
 | 
			
		||||
  va_end(ap);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Fatal error related to a system call.
 | 
			
		||||
 * Print a message and terminate.
 | 
			
		||||
 */
 | 
			
		||||
void err_sys_quit(int fd, const char *fmt, ...)
 | 
			
		||||
{
 | 
			
		||||
  va_list ap;
 | 
			
		||||
 | 
			
		||||
  va_start(ap, fmt);
 | 
			
		||||
  err_doit(fd, 1, fmt, ap);
 | 
			
		||||
  va_end(ap);
 | 
			
		||||
  exit(1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Fatal error related to a system call.
 | 
			
		||||
 * Print a message, dump core, and terminate.
 | 
			
		||||
 */
 | 
			
		||||
void err_sys_dump(int fd, const char *fmt, ...)
 | 
			
		||||
{
 | 
			
		||||
  va_list ap;
 | 
			
		||||
 | 
			
		||||
  va_start(ap, fmt);
 | 
			
		||||
  err_doit(fd, 1, fmt, ap);
 | 
			
		||||
  va_end(ap);
 | 
			
		||||
  abort();  /* dump core and terminate */
 | 
			
		||||
  exit(1);  /* shouldn't get here */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Nonfatal error unrelated to a system call.
 | 
			
		||||
 * Print a message and return.
 | 
			
		||||
 */
 | 
			
		||||
void err_report(int fd, const char *fmt, ...)
 | 
			
		||||
{
 | 
			
		||||
  va_list ap;
 | 
			
		||||
 | 
			
		||||
  va_start(ap, fmt);
 | 
			
		||||
  err_doit(fd, 0, fmt, ap);
 | 
			
		||||
  va_end(ap);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Fatal error unrelated to a system call.
 | 
			
		||||
 * Print a message and terminate.
 | 
			
		||||
 */
 | 
			
		||||
void err_quit(int fd, const char *fmt, ...)
 | 
			
		||||
{
 | 
			
		||||
  va_list ap;
 | 
			
		||||
 | 
			
		||||
  va_start(ap, fmt);
 | 
			
		||||
  err_doit(fd, 0, fmt, ap);
 | 
			
		||||
  va_end(ap);
 | 
			
		||||
  exit(1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Return a pointer to a string containing current time.
 | 
			
		||||
 */
 | 
			
		||||
char *err_tstamp(void)
 | 
			
		||||
{
 | 
			
		||||
  static char *months[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
 | 
			
		||||
                            "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
 | 
			
		||||
  static char str[32];
 | 
			
		||||
  static time_t lastt = 0;
 | 
			
		||||
  struct tm *tmp;
 | 
			
		||||
  time_t currt = st_time();
 | 
			
		||||
 | 
			
		||||
  if (currt == lastt)
 | 
			
		||||
    return str;
 | 
			
		||||
 | 
			
		||||
  tmp = localtime(&currt);
 | 
			
		||||
  sprintf(str, "[%02d/%s/%d:%02d:%02d:%02d] ", tmp->tm_mday,
 | 
			
		||||
          months[tmp->tm_mon], 1900 + tmp->tm_year, tmp->tm_hour,
 | 
			
		||||
          tmp->tm_min, tmp->tm_sec);
 | 
			
		||||
  lastt = currt;
 | 
			
		||||
 | 
			
		||||
  return str;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Print a message and return to caller.
 | 
			
		||||
 * Caller specifies "errnoflag".
 | 
			
		||||
 */
 | 
			
		||||
static void err_doit(int fd, int errnoflag, const char *fmt, va_list ap)
 | 
			
		||||
{
 | 
			
		||||
  int errno_save;
 | 
			
		||||
  char buf[MAXLINE];
 | 
			
		||||
 | 
			
		||||
  errno_save = errno;         /* value caller might want printed   */
 | 
			
		||||
  strcpy(buf, err_tstamp());  /* prepend a message with time stamp */
 | 
			
		||||
  vsprintf(buf + strlen(buf), fmt, ap);
 | 
			
		||||
  if (errnoflag)
 | 
			
		||||
    sprintf(buf + strlen(buf), ": %s\n", strerror(errno_save));
 | 
			
		||||
  else
 | 
			
		||||
    strcat(buf, "\n");
 | 
			
		||||
  write(fd, buf, strlen(buf));
 | 
			
		||||
  errno = errno_save;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1,103 +0,0 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Portions created by SGI are Copyright (C) 2000 Silicon Graphics, Inc.
 | 
			
		||||
 * All Rights Reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Redistribution and use in source and binary forms, with or without
 | 
			
		||||
 * modification, are permitted provided that the following conditions
 | 
			
		||||
 * are met: 
 | 
			
		||||
 *
 | 
			
		||||
 * 1. Redistributions of source code must retain the above copyright
 | 
			
		||||
 *    notice, this list of conditions and the following disclaimer. 
 | 
			
		||||
 * 2. Redistributions in binary form must reproduce the above copyright
 | 
			
		||||
 *    notice, this list of conditions and the following disclaimer in the
 | 
			
		||||
 *    documentation and/or other materials provided with the distribution.
 | 
			
		||||
 * 3. Neither the name of Silicon Graphics, Inc. nor the names of its
 | 
			
		||||
 *    contributors may be used to endorse or promote products derived from
 | 
			
		||||
 *    this software without specific prior written permission. 
 | 
			
		||||
 *
 | 
			
		||||
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 | 
			
		||||
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 | 
			
		||||
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 | 
			
		||||
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 | 
			
		||||
 * HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 | 
			
		||||
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
 | 
			
		||||
 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 | 
			
		||||
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 | 
			
		||||
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 | 
			
		||||
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 | 
			
		||||
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
#include <sys/time.h>
 | 
			
		||||
#include <sys/types.h>
 | 
			
		||||
#include <sys/socket.h>
 | 
			
		||||
#include <netinet/in.h>
 | 
			
		||||
#include <arpa/inet.h>
 | 
			
		||||
#include <netdb.h>
 | 
			
		||||
#include "st.h"
 | 
			
		||||
 | 
			
		||||
#if !defined(NETDB_INTERNAL) && defined(h_NETDB_INTERNAL)
 | 
			
		||||
#define NETDB_INTERNAL h_NETDB_INTERNAL
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* Resolution timeout (in microseconds) */
 | 
			
		||||
#define TIMEOUT (2*1000000LL)
 | 
			
		||||
 | 
			
		||||
/* External function defined in the res.c file */
 | 
			
		||||
int dns_getaddr(const char *host, struct in_addr *addr, st_utime_t timeout);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void *do_resolve(void *host)
 | 
			
		||||
{
 | 
			
		||||
  struct in_addr addr;
 | 
			
		||||
 | 
			
		||||
  /* Use dns_getaddr() instead of gethostbyname(3) to get IP address */
 | 
			
		||||
  if (dns_getaddr(host, &addr, TIMEOUT) < 0) {
 | 
			
		||||
    fprintf(stderr, "dns_getaddr: can't resolve %s: ", (char *)host);
 | 
			
		||||
    if (h_errno == NETDB_INTERNAL)
 | 
			
		||||
      perror("");
 | 
			
		||||
    else
 | 
			
		||||
      herror("");
 | 
			
		||||
  } else
 | 
			
		||||
    printf("%-40s %s\n", (char *)host, inet_ntoa(addr));
 | 
			
		||||
 | 
			
		||||
  return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Asynchronous DNS host name resolution. This program creates one
 | 
			
		||||
 * ST thread for each host name (specified as command line arguments).
 | 
			
		||||
 * All threads do host name resolution concurrently.
 | 
			
		||||
 */
 | 
			
		||||
int main(int argc, char *argv[])
 | 
			
		||||
{
 | 
			
		||||
  int i;
 | 
			
		||||
 | 
			
		||||
  if (argc < 2) {
 | 
			
		||||
    fprintf(stderr, "Usage: %s <hostname1> [<hostname2>] ...\n", argv[0]);
 | 
			
		||||
    exit(1);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (st_init() < 0) {
 | 
			
		||||
    perror("st_init");
 | 
			
		||||
    exit(1);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  for (i = 1; i < argc; i++) {
 | 
			
		||||
    /* Create a separate thread for each host name */
 | 
			
		||||
    if (st_thread_create(do_resolve, argv[i], 0, 0) == NULL) {
 | 
			
		||||
      perror("st_thread_create");
 | 
			
		||||
      exit(1);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  st_thread_exit(NULL);
 | 
			
		||||
 | 
			
		||||
  /* NOTREACHED */
 | 
			
		||||
  return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1,541 +0,0 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Portions created by SGI are Copyright (C) 2000 Silicon Graphics, Inc.
 | 
			
		||||
 * All Rights Reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Redistribution and use in source and binary forms, with or without
 | 
			
		||||
 * modification, are permitted provided that the following conditions
 | 
			
		||||
 * are met: 
 | 
			
		||||
 *
 | 
			
		||||
 * 1. Redistributions of source code must retain the above copyright
 | 
			
		||||
 *    notice, this list of conditions and the following disclaimer. 
 | 
			
		||||
 * 2. Redistributions in binary form must reproduce the above copyright
 | 
			
		||||
 *    notice, this list of conditions and the following disclaimer in the
 | 
			
		||||
 *    documentation and/or other materials provided with the distribution.
 | 
			
		||||
 * 3. Neither the name of Silicon Graphics, Inc. nor the names of its
 | 
			
		||||
 *    contributors may be used to endorse or promote products derived from
 | 
			
		||||
 *    this software without specific prior written permission. 
 | 
			
		||||
 *
 | 
			
		||||
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 | 
			
		||||
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 | 
			
		||||
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 | 
			
		||||
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 | 
			
		||||
 * HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 | 
			
		||||
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
 | 
			
		||||
 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 | 
			
		||||
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 | 
			
		||||
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 | 
			
		||||
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 | 
			
		||||
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <signal.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
#include <fcntl.h>
 | 
			
		||||
#include <sys/types.h>
 | 
			
		||||
#include <sys/stat.h>
 | 
			
		||||
#include <sys/socket.h>
 | 
			
		||||
#include <netinet/in.h>
 | 
			
		||||
#include <arpa/inet.h>
 | 
			
		||||
#include <netdb.h>
 | 
			
		||||
#include "st.h"
 | 
			
		||||
 | 
			
		||||
#define IOBUFSIZE (16*1024)
 | 
			
		||||
 | 
			
		||||
#define IOV_LEN   256
 | 
			
		||||
#define IOV_COUNT (IOBUFSIZE / IOV_LEN)
 | 
			
		||||
 | 
			
		||||
#ifndef INADDR_NONE
 | 
			
		||||
#define INADDR_NONE 0xffffffff
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
static char *prog;                     /* Program name   */
 | 
			
		||||
static struct sockaddr_in rmt_addr;    /* Remote address */
 | 
			
		||||
 | 
			
		||||
static unsigned long testing;
 | 
			
		||||
#define TESTING_VERBOSE		0x1
 | 
			
		||||
#define TESTING_READV		0x2
 | 
			
		||||
#define	TESTING_READ_RESID	0x4
 | 
			
		||||
#define TESTING_WRITEV		0x8
 | 
			
		||||
#define TESTING_WRITE_RESID	0x10
 | 
			
		||||
 | 
			
		||||
static void read_address(const char *str, struct sockaddr_in *sin);
 | 
			
		||||
static void start_daemon(void);
 | 
			
		||||
static int  cpu_count(void);
 | 
			
		||||
static void set_concurrency(int nproc);
 | 
			
		||||
static void *handle_request(void *arg);
 | 
			
		||||
static void print_sys_error(const char *msg);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * This program acts as a generic gateway. It listens for connections
 | 
			
		||||
 * to a local address ('-l' option). Upon accepting a client connection,
 | 
			
		||||
 * it connects to the specified remote address ('-r' option) and then
 | 
			
		||||
 * just pumps the data through without any modification.
 | 
			
		||||
 */
 | 
			
		||||
int main(int argc, char *argv[])
 | 
			
		||||
{
 | 
			
		||||
  extern char *optarg;
 | 
			
		||||
  int opt, sock, n;
 | 
			
		||||
  int laddr, raddr, num_procs, alt_ev, one_process;
 | 
			
		||||
  int serialize_accept = 0;
 | 
			
		||||
  struct sockaddr_in lcl_addr, cli_addr;
 | 
			
		||||
  st_netfd_t cli_nfd, srv_nfd;
 | 
			
		||||
 | 
			
		||||
  prog = argv[0];
 | 
			
		||||
  num_procs = laddr = raddr = alt_ev = one_process = 0;
 | 
			
		||||
 | 
			
		||||
  /* Parse arguments */
 | 
			
		||||
  while((opt = getopt(argc, argv, "l:r:p:Saht:X")) != EOF) {
 | 
			
		||||
    switch (opt) {
 | 
			
		||||
    case 'a':
 | 
			
		||||
      alt_ev = 1;
 | 
			
		||||
      break;
 | 
			
		||||
    case 'l':
 | 
			
		||||
      read_address(optarg, &lcl_addr);
 | 
			
		||||
      laddr = 1;
 | 
			
		||||
      break;
 | 
			
		||||
    case 'r':
 | 
			
		||||
      read_address(optarg, &rmt_addr);
 | 
			
		||||
      if (rmt_addr.sin_addr.s_addr == INADDR_ANY) {
 | 
			
		||||
	fprintf(stderr, "%s: invalid remote address: %s\n", prog, optarg);
 | 
			
		||||
	exit(1);
 | 
			
		||||
      }
 | 
			
		||||
      raddr = 1;
 | 
			
		||||
      break;
 | 
			
		||||
    case 'p':
 | 
			
		||||
      num_procs = atoi(optarg);
 | 
			
		||||
      if (num_procs < 1) {
 | 
			
		||||
	fprintf(stderr, "%s: invalid number of processes: %s\n", prog, optarg);
 | 
			
		||||
	exit(1);
 | 
			
		||||
      }
 | 
			
		||||
      break;
 | 
			
		||||
    case 'S':
 | 
			
		||||
      /*
 | 
			
		||||
       * Serialization decision is tricky on some platforms. For example,
 | 
			
		||||
       * Solaris 2.6 and above has kernel sockets implementation, so supposedly
 | 
			
		||||
       * there is no need for serialization. The ST library may be compiled
 | 
			
		||||
       * on one OS version, but used on another, so the need for serialization
 | 
			
		||||
       * should be determined at run time by the application. Since it's just
 | 
			
		||||
       * an example, the serialization decision is left up to user.
 | 
			
		||||
       * Only on platforms where the serialization is never needed on any OS
 | 
			
		||||
       * version st_netfd_serialize_accept() is a no-op.
 | 
			
		||||
       */
 | 
			
		||||
      serialize_accept = 1;
 | 
			
		||||
      break;
 | 
			
		||||
    case 't':
 | 
			
		||||
      testing = strtoul(optarg, NULL, 0);
 | 
			
		||||
      break;
 | 
			
		||||
    case 'X':
 | 
			
		||||
      one_process = 1;
 | 
			
		||||
      break;
 | 
			
		||||
    case 'h':
 | 
			
		||||
    case '?':
 | 
			
		||||
      fprintf(stderr, "Usage: %s [options] -l <[host]:port> -r <host:port>\n",
 | 
			
		||||
       prog);
 | 
			
		||||
      fprintf(stderr, "options are:\n");
 | 
			
		||||
      fprintf(stderr, "  -p <num_processes>	number of parallel processes\n");
 | 
			
		||||
      fprintf(stderr, "  -S			serialize accepts\n");
 | 
			
		||||
      fprintf(stderr, "  -a			use alternate event system\n");
 | 
			
		||||
#ifdef DEBUG
 | 
			
		||||
      fprintf(stderr, "  -t mask		testing/debugging mode\n");
 | 
			
		||||
      fprintf(stderr, "  -X			one process, don't daemonize\n");
 | 
			
		||||
#endif
 | 
			
		||||
      exit(1);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  if (!laddr) {
 | 
			
		||||
    fprintf(stderr, "%s: local address required\n", prog);
 | 
			
		||||
    exit(1);
 | 
			
		||||
  }
 | 
			
		||||
  if (!raddr) {
 | 
			
		||||
    fprintf(stderr, "%s: remote address required\n", prog);
 | 
			
		||||
    exit(1);
 | 
			
		||||
  }
 | 
			
		||||
  if (num_procs == 0)
 | 
			
		||||
    num_procs = cpu_count();
 | 
			
		||||
 | 
			
		||||
  fprintf(stderr, "%s: starting proxy daemon on %s:%d\n", prog,
 | 
			
		||||
	  inet_ntoa(lcl_addr.sin_addr), ntohs(lcl_addr.sin_port));
 | 
			
		||||
 | 
			
		||||
  /* Start the daemon */
 | 
			
		||||
  if (one_process)
 | 
			
		||||
    num_procs = 1;
 | 
			
		||||
  else
 | 
			
		||||
    start_daemon();
 | 
			
		||||
 | 
			
		||||
  if (alt_ev)
 | 
			
		||||
    st_set_eventsys(ST_EVENTSYS_ALT);
 | 
			
		||||
 | 
			
		||||
  /* Initialize the ST library */
 | 
			
		||||
  if (st_init() < 0) {
 | 
			
		||||
    print_sys_error("st_init");
 | 
			
		||||
    exit(1);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /* Create and bind listening socket */
 | 
			
		||||
  if ((sock = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
 | 
			
		||||
    print_sys_error("socket");
 | 
			
		||||
    exit(1);
 | 
			
		||||
  }
 | 
			
		||||
  n = 1;
 | 
			
		||||
  if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&n, sizeof(n)) < 0) {
 | 
			
		||||
    print_sys_error("setsockopt");
 | 
			
		||||
    exit(1);
 | 
			
		||||
  }
 | 
			
		||||
  if (bind(sock, (struct sockaddr *)&lcl_addr, sizeof(lcl_addr)) < 0) {
 | 
			
		||||
    print_sys_error("bind");
 | 
			
		||||
    exit(1);
 | 
			
		||||
  }
 | 
			
		||||
  listen(sock, 128);
 | 
			
		||||
  if ((srv_nfd = st_netfd_open_socket(sock)) == NULL) {
 | 
			
		||||
    print_sys_error("st_netfd_open");
 | 
			
		||||
    exit(1);
 | 
			
		||||
  }
 | 
			
		||||
  /* See the comment regarding serialization decision above */
 | 
			
		||||
  if (num_procs > 1 && serialize_accept && st_netfd_serialize_accept(srv_nfd)
 | 
			
		||||
      < 0) {
 | 
			
		||||
    print_sys_error("st_netfd_serialize_accept");
 | 
			
		||||
    exit(1);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /* Start server processes */
 | 
			
		||||
  if (!one_process)
 | 
			
		||||
    set_concurrency(num_procs);
 | 
			
		||||
 | 
			
		||||
  for ( ; ; ) {
 | 
			
		||||
    n = sizeof(cli_addr);
 | 
			
		||||
    cli_nfd = st_accept(srv_nfd, (struct sockaddr *)&cli_addr, &n,
 | 
			
		||||
     ST_UTIME_NO_TIMEOUT);
 | 
			
		||||
    if (cli_nfd == NULL) {
 | 
			
		||||
      print_sys_error("st_accept");
 | 
			
		||||
      exit(1);
 | 
			
		||||
    }
 | 
			
		||||
    if (st_thread_create(handle_request, cli_nfd, 0, 0) == NULL) {
 | 
			
		||||
      print_sys_error("st_thread_create");
 | 
			
		||||
      exit(1);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /* NOTREACHED */
 | 
			
		||||
  return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void read_address(const char *str, struct sockaddr_in *sin)
 | 
			
		||||
{
 | 
			
		||||
  char host[128], *p;
 | 
			
		||||
  struct hostent *hp;
 | 
			
		||||
  unsigned short port;
 | 
			
		||||
 | 
			
		||||
  strcpy(host, str);
 | 
			
		||||
  if ((p = strchr(host, ':')) == NULL) {
 | 
			
		||||
    fprintf(stderr, "%s: invalid address: %s\n", prog, host);
 | 
			
		||||
    exit(1);
 | 
			
		||||
  }
 | 
			
		||||
  *p++ = '\0';
 | 
			
		||||
  port = (unsigned short) atoi(p);
 | 
			
		||||
  if (port < 1) {
 | 
			
		||||
    fprintf(stderr, "%s: invalid port: %s\n", prog, p);
 | 
			
		||||
    exit(1);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  memset(sin, 0, sizeof(struct sockaddr_in));
 | 
			
		||||
  sin->sin_family = AF_INET;
 | 
			
		||||
  sin->sin_port = htons(port);
 | 
			
		||||
  if (host[0] == '\0') {
 | 
			
		||||
    sin->sin_addr.s_addr = INADDR_ANY;
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  sin->sin_addr.s_addr = inet_addr(host);
 | 
			
		||||
  if (sin->sin_addr.s_addr == INADDR_NONE) {
 | 
			
		||||
    /* not dotted-decimal */
 | 
			
		||||
    if ((hp = gethostbyname(host)) == NULL) {
 | 
			
		||||
      fprintf(stderr, "%s: can't resolve address: %s\n", prog, host);
 | 
			
		||||
      exit(1);
 | 
			
		||||
    }
 | 
			
		||||
    memcpy(&sin->sin_addr, hp->h_addr, hp->h_length);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifdef DEBUG
 | 
			
		||||
static void show_iov(const struct iovec *iov, int niov)
 | 
			
		||||
{
 | 
			
		||||
  int i;
 | 
			
		||||
  size_t total;
 | 
			
		||||
 | 
			
		||||
  printf("iov %p has %d entries:\n", iov, niov);
 | 
			
		||||
  total = 0;
 | 
			
		||||
  for (i = 0; i < niov; i++) {
 | 
			
		||||
    printf("iov[%3d] iov_base=%p iov_len=0x%lx(%lu)\n",
 | 
			
		||||
     i, iov[i].iov_base, (unsigned long) iov[i].iov_len,
 | 
			
		||||
     (unsigned long) iov[i].iov_len);
 | 
			
		||||
    total += iov[i].iov_len;
 | 
			
		||||
  }
 | 
			
		||||
  printf("total 0x%lx(%ld)\n", (unsigned long) total, (unsigned long) total);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * This version is tricked out to test all the
 | 
			
		||||
 * st_(read|write)v?(_resid)? variants.  Use the non-DEBUG version for
 | 
			
		||||
 * anything serious.  st_(read|write) are all this function really
 | 
			
		||||
 * needs.
 | 
			
		||||
 */
 | 
			
		||||
static int pass(st_netfd_t in, st_netfd_t out)
 | 
			
		||||
{
 | 
			
		||||
  char buf[IOBUFSIZE];
 | 
			
		||||
  struct iovec iov[IOV_COUNT];
 | 
			
		||||
  int ioviter, nw, nr;
 | 
			
		||||
 | 
			
		||||
  if (testing & TESTING_READV) {
 | 
			
		||||
    for (ioviter = 0; ioviter < IOV_COUNT; ioviter++) {
 | 
			
		||||
      iov[ioviter].iov_base = &buf[ioviter * IOV_LEN];
 | 
			
		||||
      iov[ioviter].iov_len = IOV_LEN;
 | 
			
		||||
    }
 | 
			
		||||
    if (testing & TESTING_VERBOSE) {
 | 
			
		||||
      printf("readv(%p)...\n", in);
 | 
			
		||||
      show_iov(iov, IOV_COUNT);
 | 
			
		||||
    }
 | 
			
		||||
    if (testing & TESTING_READ_RESID) {
 | 
			
		||||
      struct iovec *riov = iov;
 | 
			
		||||
      int riov_cnt = IOV_COUNT;
 | 
			
		||||
      if (st_readv_resid(in, &riov, &riov_cnt, ST_UTIME_NO_TIMEOUT) == 0) {
 | 
			
		||||
	if (testing & TESTING_VERBOSE) {
 | 
			
		||||
	  printf("resid\n");
 | 
			
		||||
	  show_iov(riov, riov_cnt);
 | 
			
		||||
	  printf("full\n");
 | 
			
		||||
	  show_iov(iov, IOV_COUNT);
 | 
			
		||||
	}
 | 
			
		||||
	nr = 0;
 | 
			
		||||
	for (ioviter = 0; ioviter < IOV_COUNT; ioviter++)
 | 
			
		||||
	  nr += iov[ioviter].iov_len;
 | 
			
		||||
	nr = IOBUFSIZE - nr;
 | 
			
		||||
      } else
 | 
			
		||||
	nr = -1;
 | 
			
		||||
    } else
 | 
			
		||||
      nr = (int) st_readv(in, iov, IOV_COUNT, ST_UTIME_NO_TIMEOUT);
 | 
			
		||||
  } else {
 | 
			
		||||
    if (testing & TESTING_READ_RESID) {
 | 
			
		||||
      size_t resid = IOBUFSIZE;
 | 
			
		||||
      if (st_read_resid(in, buf, &resid, ST_UTIME_NO_TIMEOUT) == 0)
 | 
			
		||||
	nr = IOBUFSIZE - resid;
 | 
			
		||||
      else
 | 
			
		||||
	nr = -1;
 | 
			
		||||
    } else
 | 
			
		||||
      nr = (int) st_read(in, buf, IOBUFSIZE, ST_UTIME_NO_TIMEOUT);
 | 
			
		||||
  }
 | 
			
		||||
  if (testing & TESTING_VERBOSE)
 | 
			
		||||
    printf("got 0x%x(%d) E=%d\n", nr, nr, errno);
 | 
			
		||||
 | 
			
		||||
  if (nr <= 0)
 | 
			
		||||
    return 0;
 | 
			
		||||
 | 
			
		||||
  if (testing & TESTING_WRITEV) {
 | 
			
		||||
    for (nw = 0, ioviter = 0; nw < nr;
 | 
			
		||||
     nw += iov[ioviter].iov_len, ioviter++) {
 | 
			
		||||
      iov[ioviter].iov_base = &buf[nw];
 | 
			
		||||
      iov[ioviter].iov_len = nr - nw;
 | 
			
		||||
      if (iov[ioviter].iov_len > IOV_LEN)
 | 
			
		||||
	iov[ioviter].iov_len = IOV_LEN;
 | 
			
		||||
    }
 | 
			
		||||
    if (testing & TESTING_VERBOSE) {
 | 
			
		||||
      printf("writev(%p)...\n", out);
 | 
			
		||||
      show_iov(iov, ioviter);
 | 
			
		||||
    }
 | 
			
		||||
    if (testing & TESTING_WRITE_RESID) {
 | 
			
		||||
      struct iovec *riov = iov;
 | 
			
		||||
      int riov_cnt = ioviter;
 | 
			
		||||
      if (st_writev_resid(out, &riov, &riov_cnt, ST_UTIME_NO_TIMEOUT) == 0) {
 | 
			
		||||
	if (testing & TESTING_VERBOSE) {
 | 
			
		||||
	  printf("resid\n");
 | 
			
		||||
	  show_iov(riov, riov_cnt);
 | 
			
		||||
	  printf("full\n");
 | 
			
		||||
	  show_iov(iov, ioviter);
 | 
			
		||||
	}
 | 
			
		||||
	nw = 0;
 | 
			
		||||
	while (--ioviter >= 0)
 | 
			
		||||
	  nw += iov[ioviter].iov_len;
 | 
			
		||||
	nw = nr - nw;
 | 
			
		||||
      } else
 | 
			
		||||
	nw = -1;
 | 
			
		||||
    } else
 | 
			
		||||
      nw = st_writev(out, iov, ioviter, ST_UTIME_NO_TIMEOUT);
 | 
			
		||||
  } else {
 | 
			
		||||
    if (testing & TESTING_WRITE_RESID) {
 | 
			
		||||
      size_t resid = nr;
 | 
			
		||||
      if (st_write_resid(out, buf, &resid, ST_UTIME_NO_TIMEOUT) == 0)
 | 
			
		||||
	nw = nr - resid;
 | 
			
		||||
      else
 | 
			
		||||
	nw = -1;
 | 
			
		||||
    } else
 | 
			
		||||
      nw = st_write(out, buf, nr, ST_UTIME_NO_TIMEOUT);
 | 
			
		||||
  }
 | 
			
		||||
  if (testing & TESTING_VERBOSE)
 | 
			
		||||
    printf("put 0x%x(%d) E=%d\n", nw, nw, errno);
 | 
			
		||||
 | 
			
		||||
  if (nw != nr)
 | 
			
		||||
    return 0;
 | 
			
		||||
 | 
			
		||||
  return 1;
 | 
			
		||||
}
 | 
			
		||||
#else /* DEBUG */
 | 
			
		||||
/*
 | 
			
		||||
 * This version is the simple one suitable for serious use.
 | 
			
		||||
 */
 | 
			
		||||
static int pass(st_netfd_t in, st_netfd_t out)
 | 
			
		||||
{
 | 
			
		||||
  char buf[IOBUFSIZE];
 | 
			
		||||
  int nw, nr;
 | 
			
		||||
 | 
			
		||||
  nr = (int) st_read(in, buf, IOBUFSIZE, ST_UTIME_NO_TIMEOUT);
 | 
			
		||||
  if (nr <= 0)
 | 
			
		||||
    return 0;
 | 
			
		||||
 | 
			
		||||
  nw = st_write(out, buf, nr, ST_UTIME_NO_TIMEOUT);
 | 
			
		||||
  if (nw != nr)
 | 
			
		||||
    return 0;
 | 
			
		||||
 | 
			
		||||
  return 1;
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
static void *handle_request(void *arg)
 | 
			
		||||
{
 | 
			
		||||
  struct pollfd pds[2];
 | 
			
		||||
  st_netfd_t cli_nfd, rmt_nfd;
 | 
			
		||||
  int sock;
 | 
			
		||||
 | 
			
		||||
  cli_nfd = (st_netfd_t) arg;
 | 
			
		||||
  pds[0].fd = st_netfd_fileno(cli_nfd);
 | 
			
		||||
  pds[0].events = POLLIN;
 | 
			
		||||
 | 
			
		||||
  /* Connect to remote host */
 | 
			
		||||
  if ((sock = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
 | 
			
		||||
    print_sys_error("socket");
 | 
			
		||||
    goto done;
 | 
			
		||||
  }
 | 
			
		||||
  if ((rmt_nfd = st_netfd_open_socket(sock)) == NULL) {
 | 
			
		||||
    print_sys_error("st_netfd_open_socket");
 | 
			
		||||
    close(sock);
 | 
			
		||||
    goto done;
 | 
			
		||||
  }
 | 
			
		||||
  if (st_connect(rmt_nfd, (struct sockaddr *)&rmt_addr,
 | 
			
		||||
		 sizeof(rmt_addr), ST_UTIME_NO_TIMEOUT) < 0) {
 | 
			
		||||
    print_sys_error("st_connect");
 | 
			
		||||
    st_netfd_close(rmt_nfd);
 | 
			
		||||
    goto done;
 | 
			
		||||
  }
 | 
			
		||||
  pds[1].fd = sock;
 | 
			
		||||
  pds[1].events = POLLIN;
 | 
			
		||||
 | 
			
		||||
  /*
 | 
			
		||||
   * Now just pump the data through.
 | 
			
		||||
   * XXX This should use one thread for each direction for true full-duplex.
 | 
			
		||||
   */
 | 
			
		||||
  for ( ; ; ) {
 | 
			
		||||
    pds[0].revents = 0;
 | 
			
		||||
    pds[1].revents = 0;
 | 
			
		||||
 | 
			
		||||
    if (st_poll(pds, 2, ST_UTIME_NO_TIMEOUT) <= 0) {
 | 
			
		||||
      print_sys_error("st_poll");
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (pds[0].revents & POLLIN) {
 | 
			
		||||
      if (!pass(cli_nfd, rmt_nfd))
 | 
			
		||||
	break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (pds[1].revents & POLLIN) {
 | 
			
		||||
      if (!pass(rmt_nfd, cli_nfd))
 | 
			
		||||
	break;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  st_netfd_close(rmt_nfd);
 | 
			
		||||
 | 
			
		||||
done:
 | 
			
		||||
 | 
			
		||||
  st_netfd_close(cli_nfd);
 | 
			
		||||
 | 
			
		||||
  return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void start_daemon(void)
 | 
			
		||||
{
 | 
			
		||||
  pid_t pid;
 | 
			
		||||
 | 
			
		||||
  /* Start forking */
 | 
			
		||||
  if ((pid = fork()) < 0) {
 | 
			
		||||
    print_sys_error("fork");
 | 
			
		||||
    exit(1);
 | 
			
		||||
  }
 | 
			
		||||
  if (pid > 0)
 | 
			
		||||
    exit(0);                        /* parent */
 | 
			
		||||
 | 
			
		||||
  /* First child process */
 | 
			
		||||
  setsid();                         /* become session leader */
 | 
			
		||||
 | 
			
		||||
  if ((pid = fork()) < 0) {
 | 
			
		||||
    print_sys_error("fork");
 | 
			
		||||
    exit(1);
 | 
			
		||||
  }
 | 
			
		||||
  if (pid > 0)                      /* first child */
 | 
			
		||||
    exit(0);
 | 
			
		||||
 | 
			
		||||
  chdir("/");
 | 
			
		||||
  umask(022);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Create separate processes ("virtual processors"). Since it's just an
 | 
			
		||||
 * example, there is no watchdog - the parent just exits leaving children
 | 
			
		||||
 * on their own.
 | 
			
		||||
 */
 | 
			
		||||
static void set_concurrency(int nproc)
 | 
			
		||||
{
 | 
			
		||||
  pid_t pid;
 | 
			
		||||
  int i;
 | 
			
		||||
 | 
			
		||||
  if (nproc < 1)
 | 
			
		||||
    nproc = 1;
 | 
			
		||||
 | 
			
		||||
  for (i = 0; i < nproc; i++) {
 | 
			
		||||
    if ((pid = fork()) < 0) {
 | 
			
		||||
      print_sys_error("fork");
 | 
			
		||||
      exit(1);
 | 
			
		||||
    }
 | 
			
		||||
    /* Child returns */
 | 
			
		||||
    if (pid == 0)
 | 
			
		||||
      return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /* Parent just exits */
 | 
			
		||||
  exit(0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int cpu_count(void)
 | 
			
		||||
{
 | 
			
		||||
  int n;
 | 
			
		||||
 | 
			
		||||
#if defined (_SC_NPROCESSORS_ONLN)
 | 
			
		||||
  n = (int) sysconf(_SC_NPROCESSORS_ONLN);
 | 
			
		||||
#elif defined (_SC_NPROC_ONLN)
 | 
			
		||||
  n = (int) sysconf(_SC_NPROC_ONLN);
 | 
			
		||||
#elif defined (HPUX)
 | 
			
		||||
#include <sys/mpctl.h>
 | 
			
		||||
  n = mpctl(MPC_GETNUMSPUS, 0, 0);
 | 
			
		||||
#else
 | 
			
		||||
  n = -1;
 | 
			
		||||
  errno = ENOSYS;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
  return n;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void print_sys_error(const char *msg)
 | 
			
		||||
{
 | 
			
		||||
  fprintf(stderr, "%s: %s: %s\n", prog, msg, strerror(errno));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1,305 +0,0 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright (c) 1985, 1988, 1993
 | 
			
		||||
 *    The Regents of the University of California.  All rights reserved.
 | 
			
		||||
 * 
 | 
			
		||||
 * Redistribution and use in source and binary forms, with or without
 | 
			
		||||
 * modification, are permitted provided that the following conditions
 | 
			
		||||
 * are met:
 | 
			
		||||
 * 1. Redistributions of source code must retain the above copyright
 | 
			
		||||
 *    notice, this list of conditions and the following disclaimer.
 | 
			
		||||
 * 2. Redistributions in binary form must reproduce the above copyright
 | 
			
		||||
 *    notice, this list of conditions and the following disclaimer in the
 | 
			
		||||
 *    documentation and/or other materials provided with the distribution.
 | 
			
		||||
 * 3. All advertising materials mentioning features or use of this software
 | 
			
		||||
 *    must display the following acknowledgement:
 | 
			
		||||
 *      This product includes software developed by the University of
 | 
			
		||||
 *      California, Berkeley and its contributors.
 | 
			
		||||
 * 4. Neither the name of the University nor the names of its contributors
 | 
			
		||||
 *    may be used to endorse or promote products derived from this software
 | 
			
		||||
 *    without specific prior written permission.
 | 
			
		||||
 * 
 | 
			
		||||
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 | 
			
		||||
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 | 
			
		||||
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 | 
			
		||||
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 | 
			
		||||
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 | 
			
		||||
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 | 
			
		||||
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 | 
			
		||||
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 | 
			
		||||
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 | 
			
		||||
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 | 
			
		||||
 * SUCH DAMAGE.
 | 
			
		||||
 *
 | 
			
		||||
 * Portions created by SGI are Copyright (C) 2000 Silicon Graphics, Inc.
 | 
			
		||||
 * All Rights Reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Redistribution and use in source and binary forms, with or without
 | 
			
		||||
 * modification, are permitted provided that the following conditions
 | 
			
		||||
 * are met: 
 | 
			
		||||
 *
 | 
			
		||||
 * 1. Redistributions of source code must retain the above copyright
 | 
			
		||||
 *    notice, this list of conditions and the following disclaimer. 
 | 
			
		||||
 * 2. Redistributions in binary form must reproduce the above copyright
 | 
			
		||||
 *    notice, this list of conditions and the following disclaimer in the
 | 
			
		||||
 *    documentation and/or other materials provided with the distribution.
 | 
			
		||||
 * 3. Neither the name of Silicon Graphics, Inc. nor the names of its
 | 
			
		||||
 *    contributors may be used to endorse or promote products derived from
 | 
			
		||||
 *    this software without specific prior written permission. 
 | 
			
		||||
 *
 | 
			
		||||
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 | 
			
		||||
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 | 
			
		||||
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 | 
			
		||||
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 | 
			
		||||
 * HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 | 
			
		||||
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
 | 
			
		||||
 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 | 
			
		||||
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 | 
			
		||||
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 | 
			
		||||
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 | 
			
		||||
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#if defined (DARWIN)
 | 
			
		||||
#define BIND_8_COMPAT
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
#include <sys/types.h>
 | 
			
		||||
#include <sys/socket.h>
 | 
			
		||||
#include <netinet/in.h>
 | 
			
		||||
#include <arpa/nameser.h>
 | 
			
		||||
#include <resolv.h>
 | 
			
		||||
#include <netdb.h>
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include "st.h"
 | 
			
		||||
 | 
			
		||||
#define MAXPACKET 1024
 | 
			
		||||
 | 
			
		||||
#if !defined(NETDB_INTERNAL) && defined(h_NETDB_INTERNAL)
 | 
			
		||||
#define NETDB_INTERNAL h_NETDB_INTERNAL
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* New in Solaris 7 */
 | 
			
		||||
#if !defined(_getshort) && defined(ns_get16)
 | 
			
		||||
#define _getshort(cp) ns_get16(cp)
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
typedef union {
 | 
			
		||||
    HEADER hdr;
 | 
			
		||||
    u_char buf[MAXPACKET];
 | 
			
		||||
} querybuf_t;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static int parse_answer(querybuf_t *ans, int len, struct in_addr *addr)
 | 
			
		||||
{
 | 
			
		||||
  char buf[MAXPACKET];
 | 
			
		||||
  HEADER *ahp;
 | 
			
		||||
  u_char *cp, *eoa;
 | 
			
		||||
  int type, n;
 | 
			
		||||
 | 
			
		||||
  ahp = &ans->hdr;
 | 
			
		||||
  eoa = ans->buf + len;
 | 
			
		||||
  cp = ans->buf + sizeof(HEADER);
 | 
			
		||||
 | 
			
		||||
  while (ahp->qdcount > 0) {
 | 
			
		||||
    ahp->qdcount--;
 | 
			
		||||
    cp += dn_skipname(cp, eoa) + QFIXEDSZ;
 | 
			
		||||
  }
 | 
			
		||||
  while (ahp->ancount > 0 && cp < eoa) {
 | 
			
		||||
    ahp->ancount--;
 | 
			
		||||
    if ((n = dn_expand(ans->buf, eoa, cp, buf, sizeof(buf))) < 0)
 | 
			
		||||
      break;
 | 
			
		||||
    cp += n;
 | 
			
		||||
    type = _getshort(cp);
 | 
			
		||||
    cp += 8;
 | 
			
		||||
    n = _getshort(cp);
 | 
			
		||||
    cp += 2;
 | 
			
		||||
    if (type == T_CNAME) {
 | 
			
		||||
      cp += n;
 | 
			
		||||
      continue;
 | 
			
		||||
    }
 | 
			
		||||
    memcpy(addr, cp, n);
 | 
			
		||||
    return 0;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  h_errno = TRY_AGAIN;
 | 
			
		||||
  return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static int query_domain(st_netfd_t nfd, const char *name, struct in_addr *addr,
 | 
			
		||||
			st_utime_t timeout)
 | 
			
		||||
{
 | 
			
		||||
  querybuf_t qbuf;
 | 
			
		||||
  u_char *buf = qbuf.buf;
 | 
			
		||||
  HEADER *hp = &qbuf.hdr;
 | 
			
		||||
  int blen = sizeof(qbuf);
 | 
			
		||||
  int i, len, id;
 | 
			
		||||
 | 
			
		||||
  for (i = 0; i < _res.nscount; i++) {
 | 
			
		||||
    len = res_mkquery(QUERY, name, C_IN, T_A, NULL, 0, NULL, buf, blen);
 | 
			
		||||
    if (len <= 0) {
 | 
			
		||||
      h_errno = NO_RECOVERY;
 | 
			
		||||
      return -1;
 | 
			
		||||
    }
 | 
			
		||||
    id = hp->id;
 | 
			
		||||
 | 
			
		||||
    if (st_sendto(nfd, buf, len, (struct sockaddr *)&(_res.nsaddr_list[i]),
 | 
			
		||||
		  sizeof(struct sockaddr), timeout) != len) {
 | 
			
		||||
      h_errno = NETDB_INTERNAL;
 | 
			
		||||
      /* EINTR means interrupt by other thread, NOT by a caught signal */
 | 
			
		||||
      if (errno == EINTR)
 | 
			
		||||
	return -1;
 | 
			
		||||
      continue;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Wait for reply */
 | 
			
		||||
    do {
 | 
			
		||||
      len = st_recvfrom(nfd, buf, blen, NULL, NULL, timeout);
 | 
			
		||||
      if (len <= 0)
 | 
			
		||||
	break;
 | 
			
		||||
    } while (id != hp->id);
 | 
			
		||||
 | 
			
		||||
    if (len < HFIXEDSZ) {
 | 
			
		||||
      h_errno = NETDB_INTERNAL;
 | 
			
		||||
      if (len >= 0)
 | 
			
		||||
	errno = EMSGSIZE;
 | 
			
		||||
      else if (errno == EINTR)  /* see the comment above */
 | 
			
		||||
	return -1;
 | 
			
		||||
      continue;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    hp->ancount = ntohs(hp->ancount);
 | 
			
		||||
    hp->qdcount = ntohs(hp->qdcount);
 | 
			
		||||
    if ((hp->rcode != NOERROR) || (hp->ancount == 0)) {
 | 
			
		||||
      switch (hp->rcode) {
 | 
			
		||||
      case NXDOMAIN:
 | 
			
		||||
	h_errno = HOST_NOT_FOUND;
 | 
			
		||||
	break;
 | 
			
		||||
      case SERVFAIL:
 | 
			
		||||
	h_errno = TRY_AGAIN;
 | 
			
		||||
	break;
 | 
			
		||||
      case NOERROR:
 | 
			
		||||
	h_errno = NO_DATA;
 | 
			
		||||
	break;
 | 
			
		||||
      case FORMERR:
 | 
			
		||||
      case NOTIMP:
 | 
			
		||||
      case REFUSED:
 | 
			
		||||
      default:
 | 
			
		||||
	h_errno = NO_RECOVERY;
 | 
			
		||||
      }
 | 
			
		||||
      continue;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (parse_answer(&qbuf, len, addr) == 0)
 | 
			
		||||
      return 0;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#define CLOSE_AND_RETURN(ret) \
 | 
			
		||||
  {                           \
 | 
			
		||||
    n = errno;                \
 | 
			
		||||
    st_netfd_close(nfd);      \
 | 
			
		||||
    errno = n;                \
 | 
			
		||||
    return (ret);             \
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
int dns_getaddr(const char *host, struct in_addr *addr, st_utime_t timeout)
 | 
			
		||||
{
 | 
			
		||||
  char name[MAXDNAME], **domain;
 | 
			
		||||
  const char *cp;
 | 
			
		||||
  int s, n, maxlen, dots;
 | 
			
		||||
  int trailing_dot, tried_as_is;
 | 
			
		||||
  st_netfd_t nfd;
 | 
			
		||||
 | 
			
		||||
  if ((_res.options & RES_INIT) == 0 && res_init() == -1) {
 | 
			
		||||
    h_errno = NETDB_INTERNAL;
 | 
			
		||||
    return -1;
 | 
			
		||||
  }
 | 
			
		||||
  if (_res.options & RES_USEVC) {
 | 
			
		||||
    h_errno = NETDB_INTERNAL;
 | 
			
		||||
    errno = ENOSYS;
 | 
			
		||||
    return -1;
 | 
			
		||||
  }
 | 
			
		||||
  if (!host || *host == '\0') {
 | 
			
		||||
    h_errno = HOST_NOT_FOUND;
 | 
			
		||||
    return -1;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /* Create UDP socket */
 | 
			
		||||
  if ((s = socket(PF_INET, SOCK_DGRAM, 0)) < 0) {
 | 
			
		||||
    h_errno = NETDB_INTERNAL;
 | 
			
		||||
    return -1;
 | 
			
		||||
  }
 | 
			
		||||
  if ((nfd = st_netfd_open_socket(s)) == NULL) {
 | 
			
		||||
    h_errno = NETDB_INTERNAL;
 | 
			
		||||
    n = errno;
 | 
			
		||||
    close(s);
 | 
			
		||||
    errno = n;
 | 
			
		||||
    return -1;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  maxlen = sizeof(name) - 1;
 | 
			
		||||
  n = 0;
 | 
			
		||||
  dots = 0;
 | 
			
		||||
  trailing_dot = 0;
 | 
			
		||||
  tried_as_is = 0;
 | 
			
		||||
 | 
			
		||||
  for (cp = host; *cp && n < maxlen; cp++) {
 | 
			
		||||
    dots += (*cp == '.');
 | 
			
		||||
    name[n++] = *cp;
 | 
			
		||||
  }
 | 
			
		||||
  if (name[n - 1] == '.')
 | 
			
		||||
    trailing_dot = 1;
 | 
			
		||||
 | 
			
		||||
  /*
 | 
			
		||||
   * If there are dots in the name already, let's just give it a try
 | 
			
		||||
   * 'as is'.  The threshold can be set with the "ndots" option.
 | 
			
		||||
   */
 | 
			
		||||
  if (dots >= _res.ndots) {
 | 
			
		||||
    if (query_domain(nfd, host, addr, timeout) == 0)
 | 
			
		||||
      CLOSE_AND_RETURN(0);
 | 
			
		||||
    if (h_errno == NETDB_INTERNAL && errno == EINTR)
 | 
			
		||||
      CLOSE_AND_RETURN(-1);
 | 
			
		||||
    tried_as_is = 1;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /*
 | 
			
		||||
   * We do at least one level of search if
 | 
			
		||||
   *     - there is no dot and RES_DEFNAME is set, or
 | 
			
		||||
   *     - there is at least one dot, there is no trailing dot,
 | 
			
		||||
   *       and RES_DNSRCH is set.
 | 
			
		||||
   */
 | 
			
		||||
  if ((!dots && (_res.options & RES_DEFNAMES)) ||
 | 
			
		||||
      (dots && !trailing_dot && (_res.options & RES_DNSRCH))) {
 | 
			
		||||
    name[n++] = '.';
 | 
			
		||||
    for (domain = _res.dnsrch; *domain; domain++) {
 | 
			
		||||
      strncpy(name + n, *domain, maxlen - n);
 | 
			
		||||
      if (query_domain(nfd, name, addr, timeout) == 0)
 | 
			
		||||
	CLOSE_AND_RETURN(0);
 | 
			
		||||
      if (h_errno == NETDB_INTERNAL && errno == EINTR)
 | 
			
		||||
	CLOSE_AND_RETURN(-1);
 | 
			
		||||
      if (!(_res.options & RES_DNSRCH))
 | 
			
		||||
	break;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /*
 | 
			
		||||
   * If we have not already tried the name "as is", do that now.
 | 
			
		||||
   * note that we do this regardless of how many dots were in the
 | 
			
		||||
   * name or whether it ends with a dot.
 | 
			
		||||
   */
 | 
			
		||||
  if (!tried_as_is) {
 | 
			
		||||
    if (query_domain(nfd, host, addr, timeout) == 0)
 | 
			
		||||
      CLOSE_AND_RETURN(0);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  CLOSE_AND_RETURN(-1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							| 
						 | 
				
			
			@ -1,91 +0,0 @@
 | 
			
		|||
#
 | 
			
		||||
# Portions created by SGI are Copyright (C) 2000 Silicon Graphics, Inc.
 | 
			
		||||
# All Rights Reserved.
 | 
			
		||||
#
 | 
			
		||||
# Redistribution and use in source and binary forms, with or without
 | 
			
		||||
# modification, are permitted provided that the following conditions
 | 
			
		||||
# are met: 
 | 
			
		||||
#
 | 
			
		||||
# 1. Redistributions of source code must retain the above copyright
 | 
			
		||||
#    notice, this list of conditions and the following disclaimer. 
 | 
			
		||||
# 2. Redistributions in binary form must reproduce the above copyright
 | 
			
		||||
#    notice, this list of conditions and the following disclaimer in the
 | 
			
		||||
#    documentation and/or other materials provided with the distribution.
 | 
			
		||||
# 3. Neither the name of Silicon Graphics, Inc. nor the names of its
 | 
			
		||||
#    contributors may be used to endorse or promote products derived from
 | 
			
		||||
#    this software without specific prior written permission. 
 | 
			
		||||
#
 | 
			
		||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 | 
			
		||||
# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 | 
			
		||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 | 
			
		||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 | 
			
		||||
# HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 | 
			
		||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
 | 
			
		||||
# TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 | 
			
		||||
# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 | 
			
		||||
# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 | 
			
		||||
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 | 
			
		||||
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | 
			
		||||
 | 
			
		||||
CC          = cc
 | 
			
		||||
 | 
			
		||||
SHELL       = /bin/sh
 | 
			
		||||
ECHO        = /bin/echo
 | 
			
		||||
 | 
			
		||||
DEPTH       = ..
 | 
			
		||||
BUILD       =
 | 
			
		||||
TARGETDIR   = obj
 | 
			
		||||
 | 
			
		||||
DEFINES     =
 | 
			
		||||
OTHER_FLAGS =
 | 
			
		||||
CFLAGS      =
 | 
			
		||||
 | 
			
		||||
OBJDIR      = $(DEPTH)/$(TARGETDIR)
 | 
			
		||||
INCDIR      = $(DEPTH)/$(TARGETDIR)
 | 
			
		||||
 | 
			
		||||
LIBRESOLV   =
 | 
			
		||||
EXTRALIBS   =
 | 
			
		||||
 | 
			
		||||
SLIBRARY    = $(OBJDIR)/libstx.a
 | 
			
		||||
OBJS        = $(OBJDIR)/dnscache.o $(OBJDIR)/dnsres.o $(OBJDIR)/lrucache.o
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
CFLAGS      += -Wall -I$(INCDIR)
 | 
			
		||||
AR          = ar
 | 
			
		||||
ARFLAGS     = rv
 | 
			
		||||
RANLIB      = ranlib
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
##########################
 | 
			
		||||
# Platform section.
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
ifeq (LINUX, $(findstring LINUX, $(OS)))
 | 
			
		||||
LIBRESOLV   = -lresolv
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
ifeq ($(OS), SOLARIS)
 | 
			
		||||
LIBRESOLV   = -lresolv
 | 
			
		||||
EXTRALIBS   = -lsocket -lnsl
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
# End of platform section.
 | 
			
		||||
##########################
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
all: $(SLIBRARY)
 | 
			
		||||
 | 
			
		||||
$(SLIBRARY): $(OBJS)
 | 
			
		||||
	$(AR) $(ARFLAGS) $@ $(OBJS)
 | 
			
		||||
	$(RANLIB) $@
 | 
			
		||||
 | 
			
		||||
$(OBJDIR)/%.o: %.c stx.h common.h
 | 
			
		||||
	$(CC) $(CFLAGS) -c $< -o $@
 | 
			
		||||
 | 
			
		||||
clean:
 | 
			
		||||
	rm -rf $(OBJS) $(SLIBRARY)
 | 
			
		||||
 | 
			
		||||
#.DEFAULT:
 | 
			
		||||
#	@cd $(DEPTH); $(MAKE) $@
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1,42 +0,0 @@
 | 
			
		|||
This directory contains extensions to the core State Threads Library
 | 
			
		||||
that were contributed by users.  All files hereunder are not part of the
 | 
			
		||||
State Threads Library itself.  They are provided as-is, without warranty
 | 
			
		||||
or support, and under whatever license terms their authors provided.  To
 | 
			
		||||
contribute your own extensions, just mail them to the project
 | 
			
		||||
administrators or to one of the project's mailing lists; see
 | 
			
		||||
state-threads.sourceforge.net.  Please indicate the license terms under
 | 
			
		||||
which the project may distribute your contribution.
 | 
			
		||||
 | 
			
		||||
========================================================================
 | 
			
		||||
 | 
			
		||||
stx_fileio
 | 
			
		||||
----------
 | 
			
		||||
Contributed by Jeff <jlb-st@houseofdistraction.com>, 4 Nov 2002.
 | 
			
		||||
 | 
			
		||||
Provides non-blocking random access file reading capability for
 | 
			
		||||
programs using the State Threads library.  There is one public function:
 | 
			
		||||
 | 
			
		||||
ssize_t stx_file_read(st_netfd_t fd, off_t offset,
 | 
			
		||||
                      void *buf, size_t nbytes, st_utime_t timeout);
 | 
			
		||||
 | 
			
		||||
The implementation is not optimal in that the data is copied at least once
 | 
			
		||||
more than should be necessary.  Its usefulness is limited to cases where
 | 
			
		||||
random access to a file is required and where starvation of other threads
 | 
			
		||||
is unacceptable.
 | 
			
		||||
 | 
			
		||||
The particular application which motivated this implementation was a UDP
 | 
			
		||||
file transfer protocol.  Because the OS does very little buffering of UDP
 | 
			
		||||
traffic it is important that UDP transmission threads are not starved for
 | 
			
		||||
periods of time which are long relative to the interval required to
 | 
			
		||||
maintain a steady send rate.
 | 
			
		||||
 | 
			
		||||
Licensed under the same dual MPL/GPL as core State Threads.
 | 
			
		||||
 | 
			
		||||
========================================================================
 | 
			
		||||
 | 
			
		||||
stx_dns
 | 
			
		||||
-------
 | 
			
		||||
 | 
			
		||||
Documentation coming.
 | 
			
		||||
 | 
			
		||||
========================================================================
 | 
			
		||||
| 
						 | 
				
			
			@ -1,77 +0,0 @@
 | 
			
		|||
#ifndef _STX_COMMON_H_
 | 
			
		||||
#define _STX_COMMON_H_
 | 
			
		||||
 | 
			
		||||
#include <stddef.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#define STX_BEGIN_MACRO  {
 | 
			
		||||
#define STX_END_MACRO    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*****************************************
 | 
			
		||||
 * Circular linked list definitions
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
typedef struct _stx_clist {
 | 
			
		||||
  struct _stx_clist *next;
 | 
			
		||||
  struct _stx_clist *prev;
 | 
			
		||||
} stx_clist_t;
 | 
			
		||||
 | 
			
		||||
/* Insert element "_e" into the list, before "_l" */
 | 
			
		||||
#define STX_CLIST_INSERT_BEFORE(_e,_l) \
 | 
			
		||||
    STX_BEGIN_MACRO              \
 | 
			
		||||
        (_e)->next = (_l);       \
 | 
			
		||||
        (_e)->prev = (_l)->prev; \
 | 
			
		||||
        (_l)->prev->next = (_e); \
 | 
			
		||||
        (_l)->prev = (_e);       \
 | 
			
		||||
    STX_END_MACRO
 | 
			
		||||
 | 
			
		||||
/* Insert element "_e" into the list, after "_l" */
 | 
			
		||||
#define STX_CLIST_INSERT_AFTER(_e,_l) \
 | 
			
		||||
    STX_BEGIN_MACRO              \
 | 
			
		||||
        (_e)->next = (_l)->next; \
 | 
			
		||||
        (_e)->prev = (_l);       \
 | 
			
		||||
        (_l)->next->prev = (_e); \
 | 
			
		||||
        (_l)->next = (_e);       \
 | 
			
		||||
    STX_END_MACRO
 | 
			
		||||
 | 
			
		||||
/* Append an element "_e" to the end of the list "_l" */
 | 
			
		||||
#define STX_CLIST_APPEND_LINK(_e,_l) STX_CLIST_INSERT_BEFORE(_e,_l)
 | 
			
		||||
 | 
			
		||||
/* Remove the element "_e" from it's circular list */
 | 
			
		||||
#define STX_CLIST_REMOVE_LINK(_e)      \
 | 
			
		||||
    STX_BEGIN_MACRO                    \
 | 
			
		||||
        (_e)->prev->next = (_e)->next; \
 | 
			
		||||
        (_e)->next->prev = (_e)->prev; \
 | 
			
		||||
    STX_END_MACRO
 | 
			
		||||
 | 
			
		||||
/* Return the head/tail of the list */
 | 
			
		||||
#define STX_CLIST_HEAD(_l) (_l)->next
 | 
			
		||||
#define STX_CLIST_TAIL(_l) (_l)->prev
 | 
			
		||||
 | 
			
		||||
/* Return non-zero if the given circular list "_l" is empty, */
 | 
			
		||||
/* zero if the circular list is not empty */
 | 
			
		||||
#define STX_CLIST_IS_EMPTY(_l) \
 | 
			
		||||
    ((_l)->next == (_l))
 | 
			
		||||
 | 
			
		||||
/* Initialize a circular list */
 | 
			
		||||
#define STX_CLIST_INIT_CLIST(_l) \
 | 
			
		||||
    STX_BEGIN_MACRO        \
 | 
			
		||||
        (_l)->next = (_l); \
 | 
			
		||||
        (_l)->prev = (_l); \
 | 
			
		||||
    STX_END_MACRO
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*****************************************
 | 
			
		||||
 * Useful macros
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef offsetof
 | 
			
		||||
#define offsetof(type, identifier) ((size_t)&(((type *)0)->identifier))
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#define STX_MIN(a, b) (((a) < (b)) ? (a) : (b))
 | 
			
		||||
 | 
			
		||||
#endif /* !_STX_COMMON_H_ */
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1,190 +0,0 @@
 | 
			
		|||
#include "stx.h"
 | 
			
		||||
#include "common.h"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*****************************************
 | 
			
		||||
 * Basic types definitions
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
typedef struct _stx_dns_data {
 | 
			
		||||
    struct in_addr *addrs;
 | 
			
		||||
    int num_addrs;
 | 
			
		||||
    int cur;
 | 
			
		||||
    time_t expires;
 | 
			
		||||
} stx_dns_data_t;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#define MAX_HOST_ADDRS 1024
 | 
			
		||||
 | 
			
		||||
static struct in_addr addr_list[MAX_HOST_ADDRS];
 | 
			
		||||
 | 
			
		||||
stx_cache_t *_stx_dns_cache = NULL;
 | 
			
		||||
 | 
			
		||||
extern int _stx_dns_ttl;
 | 
			
		||||
extern int _stx_dns_getaddrlist(const char *hostname, struct in_addr *addrs,
 | 
			
		||||
				int *num_addrs, st_utime_t timeout);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static unsigned long hash_hostname(const void *key)
 | 
			
		||||
{
 | 
			
		||||
    const char *name = (const char *)key;
 | 
			
		||||
    unsigned long hash = 0;
 | 
			
		||||
 | 
			
		||||
    while (*name)
 | 
			
		||||
	hash = (hash << 4) - hash + *name++; /* hash = hash * 15 + *name++ */
 | 
			
		||||
 | 
			
		||||
    return hash;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void cleanup_entry(void *key, void *data)
 | 
			
		||||
{
 | 
			
		||||
    if (key)
 | 
			
		||||
	free(key);
 | 
			
		||||
 | 
			
		||||
    if (data) {
 | 
			
		||||
	if (((stx_dns_data_t *)data)->addrs)
 | 
			
		||||
	    free(((stx_dns_data_t *)data)->addrs);
 | 
			
		||||
	free(data);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int lookup_entry(const char *host, struct in_addr *addrs,
 | 
			
		||||
			int *num_addrs, int rotate)
 | 
			
		||||
{
 | 
			
		||||
    stx_cache_entry_t *entry;
 | 
			
		||||
    stx_dns_data_t *data;
 | 
			
		||||
    int n;
 | 
			
		||||
 | 
			
		||||
    entry = stx_cache_entry_lookup(_stx_dns_cache, host);
 | 
			
		||||
    if (entry) {
 | 
			
		||||
	data = (stx_dns_data_t *)stx_cache_entry_getdata(entry);
 | 
			
		||||
	if (st_time() <= data->expires) {
 | 
			
		||||
	    if (*num_addrs == 1) {
 | 
			
		||||
		if (rotate) {
 | 
			
		||||
		    *addrs = data->addrs[data->cur++];
 | 
			
		||||
		    if (data->cur >= data->num_addrs)
 | 
			
		||||
			data->cur = 0;
 | 
			
		||||
		} else {
 | 
			
		||||
		    *addrs = data->addrs[0];
 | 
			
		||||
		}
 | 
			
		||||
	    } else {
 | 
			
		||||
		n = STX_MIN(*num_addrs, data->num_addrs);
 | 
			
		||||
		memcpy(addrs, data->addrs, n * sizeof(*addrs));
 | 
			
		||||
		*num_addrs = n;
 | 
			
		||||
	    }
 | 
			
		||||
 | 
			
		||||
	    stx_cache_entry_release(_stx_dns_cache, entry);
 | 
			
		||||
	    return 1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Cache entry expired: decrement its refcount and purge it from cache.
 | 
			
		||||
	 */
 | 
			
		||||
	stx_cache_entry_release(_stx_dns_cache, entry);
 | 
			
		||||
	stx_cache_entry_delete(_stx_dns_cache, entry);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void insert_entry(const char *host, struct in_addr *addrs, int count)
 | 
			
		||||
{
 | 
			
		||||
    stx_cache_entry_t *entry;
 | 
			
		||||
    stx_dns_data_t *data;
 | 
			
		||||
    char *key;
 | 
			
		||||
    size_t n;
 | 
			
		||||
 | 
			
		||||
    if (_stx_dns_ttl > 0) {
 | 
			
		||||
	key = strdup(host);
 | 
			
		||||
	data = (stx_dns_data_t *)malloc(sizeof(stx_dns_data_t));
 | 
			
		||||
	n = count * sizeof(*addrs);
 | 
			
		||||
	if (data) {
 | 
			
		||||
	    data->addrs = (struct in_addr *)malloc(n);
 | 
			
		||||
	    if (data->addrs)
 | 
			
		||||
		memcpy(data->addrs, addrs, n);
 | 
			
		||||
	    data->num_addrs = count;
 | 
			
		||||
	    data->cur = 0;
 | 
			
		||||
	    data->expires = st_time() + _stx_dns_ttl;
 | 
			
		||||
	}
 | 
			
		||||
	entry = stx_cache_entry_create(key, data, strlen(host) + 1 +
 | 
			
		||||
				       sizeof(stx_dns_data_t) + n +
 | 
			
		||||
				       stx_cache_entry_sizeof());
 | 
			
		||||
	if (key && data && data->addrs && entry &&
 | 
			
		||||
	    stx_cache_entry_insert(_stx_dns_cache, entry) == 0) {
 | 
			
		||||
	    stx_cache_entry_release(_stx_dns_cache, entry);
 | 
			
		||||
	    return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (entry)
 | 
			
		||||
	    stx_cache_entry_delete(_stx_dns_cache, entry);
 | 
			
		||||
	else
 | 
			
		||||
	    cleanup_entry(key, data);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
int _stx_dns_cache_getaddrlist(const char *hostname, struct in_addr *addrs,
 | 
			
		||||
			       int *num_addrs, st_utime_t timeout,
 | 
			
		||||
			       int rotate)
 | 
			
		||||
{
 | 
			
		||||
    char host[128];
 | 
			
		||||
    int n, count;
 | 
			
		||||
 | 
			
		||||
    if (!_stx_dns_cache)
 | 
			
		||||
	return _stx_dns_getaddrlist(hostname, addrs, num_addrs, timeout);
 | 
			
		||||
 | 
			
		||||
    for (n = 0; n < sizeof(host) - 1 && hostname[n]; n++) {
 | 
			
		||||
	host[n] = tolower(hostname[n]);
 | 
			
		||||
    }
 | 
			
		||||
    host[n] = '\0';
 | 
			
		||||
 | 
			
		||||
    if (lookup_entry(host, addrs, num_addrs, rotate))
 | 
			
		||||
	return 0;
 | 
			
		||||
 | 
			
		||||
    count = MAX_HOST_ADDRS;
 | 
			
		||||
    if (_stx_dns_getaddrlist(host, addr_list, &count, timeout) < 0)
 | 
			
		||||
	return -1;
 | 
			
		||||
    n = STX_MIN(*num_addrs, count);
 | 
			
		||||
    memcpy(addrs, addr_list, n * sizeof(*addrs));
 | 
			
		||||
    *num_addrs = n;
 | 
			
		||||
 | 
			
		||||
    insert_entry(host, addr_list, count);
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
int stx_dns_cache_init(size_t max_size, size_t max_bytes, size_t hash_size)
 | 
			
		||||
{
 | 
			
		||||
    _stx_dns_cache = stx_cache_create(max_size, max_bytes, hash_size,
 | 
			
		||||
				  hash_hostname,
 | 
			
		||||
				  (long (*)(const void *, const void *))strcmp,
 | 
			
		||||
				  cleanup_entry);
 | 
			
		||||
    if (!_stx_dns_cache)
 | 
			
		||||
	return -1;
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void stx_dns_cache_getinfo(stx_cache_info_t *info)
 | 
			
		||||
{
 | 
			
		||||
    if (_stx_dns_cache)
 | 
			
		||||
	stx_cache_getinfo(_stx_dns_cache, info);
 | 
			
		||||
    else
 | 
			
		||||
	memset(info, 0, sizeof(stx_cache_info_t));
 | 
			
		||||
}	
 | 
			
		||||
 | 
			
		||||
int stx_dns_getaddrlist(const char *hostname, struct in_addr *addrs,
 | 
			
		||||
			int *num_addrs, st_utime_t timeout)
 | 
			
		||||
{
 | 
			
		||||
    return _stx_dns_cache_getaddrlist(hostname, addrs, num_addrs, timeout, 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int stx_dns_getaddr(const char *hostname, struct in_addr *addr,
 | 
			
		||||
		    st_utime_t timeout)
 | 
			
		||||
{
 | 
			
		||||
    int n = 1;
 | 
			
		||||
 | 
			
		||||
    return _stx_dns_cache_getaddrlist(hostname, addr, &n, timeout, 1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1,305 +0,0 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright (c) 1985, 1988, 1993
 | 
			
		||||
 *    The Regents of the University of California.  All rights reserved.
 | 
			
		||||
 * 
 | 
			
		||||
 * Redistribution and use in source and binary forms, with or without
 | 
			
		||||
 * modification, are permitted provided that the following conditions
 | 
			
		||||
 * are met:
 | 
			
		||||
 * 1. Redistributions of source code must retain the above copyright
 | 
			
		||||
 *    notice, this list of conditions and the following disclaimer.
 | 
			
		||||
 * 2. Redistributions in binary form must reproduce the above copyright
 | 
			
		||||
 *    notice, this list of conditions and the following disclaimer in the
 | 
			
		||||
 *    documentation and/or other materials provided with the distribution.
 | 
			
		||||
 * 3. All advertising materials mentioning features or use of this software
 | 
			
		||||
 *    must display the following acknowledgement:
 | 
			
		||||
 *      This product includes software developed by the University of
 | 
			
		||||
 *      California, Berkeley and its contributors.
 | 
			
		||||
 * 4. Neither the name of the University nor the names of its contributors
 | 
			
		||||
 *    may be used to endorse or promote products derived from this software
 | 
			
		||||
 *    without specific prior written permission.
 | 
			
		||||
 * 
 | 
			
		||||
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 | 
			
		||||
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 | 
			
		||||
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 | 
			
		||||
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 | 
			
		||||
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 | 
			
		||||
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 | 
			
		||||
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 | 
			
		||||
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 | 
			
		||||
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 | 
			
		||||
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 | 
			
		||||
 * SUCH DAMAGE.
 | 
			
		||||
 *
 | 
			
		||||
 * Portions created by SGI are Copyright (C) 2000 Silicon Graphics, Inc.
 | 
			
		||||
 * All Rights Reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Redistribution and use in source and binary forms, with or without
 | 
			
		||||
 * modification, are permitted provided that the following conditions
 | 
			
		||||
 * are met: 
 | 
			
		||||
 *
 | 
			
		||||
 * 1. Redistributions of source code must retain the above copyright
 | 
			
		||||
 *    notice, this list of conditions and the following disclaimer. 
 | 
			
		||||
 * 2. Redistributions in binary form must reproduce the above copyright
 | 
			
		||||
 *    notice, this list of conditions and the following disclaimer in the
 | 
			
		||||
 *    documentation and/or other materials provided with the distribution.
 | 
			
		||||
 * 3. Neither the name of Silicon Graphics, Inc. nor the names of its
 | 
			
		||||
 *    contributors may be used to endorse or promote products derived from
 | 
			
		||||
 *    this software without specific prior written permission. 
 | 
			
		||||
 *
 | 
			
		||||
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 | 
			
		||||
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 | 
			
		||||
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 | 
			
		||||
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 | 
			
		||||
 * HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 | 
			
		||||
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
 | 
			
		||||
 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 | 
			
		||||
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 | 
			
		||||
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 | 
			
		||||
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 | 
			
		||||
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "stx.h"
 | 
			
		||||
 | 
			
		||||
#define MAXPACKET 1024
 | 
			
		||||
 | 
			
		||||
#if !defined(NETDB_INTERNAL) && defined(h_NETDB_INTERNAL)
 | 
			
		||||
#define NETDB_INTERNAL h_NETDB_INTERNAL
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* New in Solaris 7 */
 | 
			
		||||
#if !defined(_getshort) && defined(ns_get16)
 | 
			
		||||
#define _getshort(cp) ns_get16(cp)
 | 
			
		||||
#define _getlong(cp)  ns_get32(cp)
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
typedef union {
 | 
			
		||||
    HEADER hdr;
 | 
			
		||||
    u_char buf[MAXPACKET];
 | 
			
		||||
} querybuf_t;
 | 
			
		||||
 | 
			
		||||
int _stx_dns_ttl;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static int parse_answer(querybuf_t *ans, int len, struct in_addr *addrs,
 | 
			
		||||
			int *num_addrs)
 | 
			
		||||
{
 | 
			
		||||
    char buf[MAXPACKET];
 | 
			
		||||
    HEADER *ahp;
 | 
			
		||||
    u_char *cp, *eoa;
 | 
			
		||||
    int type, n, i;
 | 
			
		||||
 | 
			
		||||
    ahp = &ans->hdr;
 | 
			
		||||
    eoa = ans->buf + len;
 | 
			
		||||
    cp = ans->buf + sizeof(HEADER);
 | 
			
		||||
    h_errno = TRY_AGAIN;
 | 
			
		||||
    _stx_dns_ttl = -1;
 | 
			
		||||
    i = 0;
 | 
			
		||||
 | 
			
		||||
    while (ahp->qdcount > 0) {
 | 
			
		||||
	ahp->qdcount--;
 | 
			
		||||
	cp += dn_skipname(cp, eoa) + QFIXEDSZ;
 | 
			
		||||
    }
 | 
			
		||||
    while (ahp->ancount > 0 && cp < eoa && i < *num_addrs) {
 | 
			
		||||
	ahp->ancount--;
 | 
			
		||||
	if ((n = dn_expand(ans->buf, eoa, cp, buf, sizeof(buf))) < 0)
 | 
			
		||||
	    return -1;
 | 
			
		||||
	cp += n;
 | 
			
		||||
	if (cp + 4 + 4 + 2 >= eoa)
 | 
			
		||||
	    return -1;
 | 
			
		||||
	type = _getshort(cp);
 | 
			
		||||
	cp += 4;
 | 
			
		||||
	if (type == T_A)
 | 
			
		||||
	    _stx_dns_ttl = _getlong(cp);
 | 
			
		||||
	cp += 4;
 | 
			
		||||
	n = _getshort(cp);
 | 
			
		||||
	cp += 2;
 | 
			
		||||
	if (type == T_A) {
 | 
			
		||||
	    if (n > sizeof(*addrs) || cp + n > eoa)
 | 
			
		||||
		return -1;
 | 
			
		||||
	    memcpy(&addrs[i++], cp, n);
 | 
			
		||||
	}
 | 
			
		||||
	cp += n;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    *num_addrs = i;
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static int query_domain(st_netfd_t nfd, const char *name,
 | 
			
		||||
			struct in_addr *addrs, int *num_addrs,
 | 
			
		||||
			st_utime_t timeout)
 | 
			
		||||
{
 | 
			
		||||
    querybuf_t qbuf;
 | 
			
		||||
    u_char *buf = qbuf.buf;
 | 
			
		||||
    HEADER *hp = &qbuf.hdr;
 | 
			
		||||
    int blen = sizeof(qbuf);
 | 
			
		||||
    int i, len, id;
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < _res.nscount; i++) {
 | 
			
		||||
	len = res_mkquery(QUERY, name, C_IN, T_A, NULL, 0, NULL, buf, blen);
 | 
			
		||||
	if (len <= 0) {
 | 
			
		||||
	    h_errno = NO_RECOVERY;
 | 
			
		||||
	    return -1;
 | 
			
		||||
	}
 | 
			
		||||
	id = hp->id;
 | 
			
		||||
 | 
			
		||||
	if (st_sendto(nfd, buf, len, (struct sockaddr *)&(_res.nsaddr_list[i]),
 | 
			
		||||
		      sizeof(struct sockaddr), timeout) != len) {
 | 
			
		||||
	    h_errno = NETDB_INTERNAL;
 | 
			
		||||
	    /* EINTR means interrupt by other thread, NOT by a caught signal */
 | 
			
		||||
	    if (errno == EINTR)
 | 
			
		||||
		return -1;
 | 
			
		||||
	    continue;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Wait for reply */
 | 
			
		||||
	do {
 | 
			
		||||
	    len = st_recvfrom(nfd, buf, blen, NULL, NULL, timeout);
 | 
			
		||||
	    if (len <= 0)
 | 
			
		||||
		break;
 | 
			
		||||
	} while (id != hp->id);
 | 
			
		||||
 | 
			
		||||
	if (len < HFIXEDSZ) {
 | 
			
		||||
	    h_errno = NETDB_INTERNAL;
 | 
			
		||||
	    if (len >= 0)
 | 
			
		||||
		errno = EMSGSIZE;
 | 
			
		||||
	    else if (errno == EINTR)  /* see the comment above */
 | 
			
		||||
		return -1;
 | 
			
		||||
	    continue;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	hp->ancount = ntohs(hp->ancount);
 | 
			
		||||
	hp->qdcount = ntohs(hp->qdcount);
 | 
			
		||||
	if ((hp->rcode != NOERROR) || (hp->ancount == 0)) {
 | 
			
		||||
	    switch (hp->rcode) {
 | 
			
		||||
	    case NXDOMAIN:
 | 
			
		||||
		h_errno = HOST_NOT_FOUND;
 | 
			
		||||
		break;
 | 
			
		||||
	    case SERVFAIL:
 | 
			
		||||
		h_errno = TRY_AGAIN;
 | 
			
		||||
		break;
 | 
			
		||||
	    case NOERROR:
 | 
			
		||||
		h_errno = NO_DATA;
 | 
			
		||||
		break;
 | 
			
		||||
	    case FORMERR:
 | 
			
		||||
	    case NOTIMP:
 | 
			
		||||
	    case REFUSED:
 | 
			
		||||
	    default:
 | 
			
		||||
		h_errno = NO_RECOVERY;
 | 
			
		||||
	    }
 | 
			
		||||
	    continue;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (parse_answer(&qbuf, len, addrs, num_addrs) == 0)
 | 
			
		||||
	    return 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#define CLOSE_AND_RETURN(ret) \
 | 
			
		||||
  {                           \
 | 
			
		||||
    n = errno;                \
 | 
			
		||||
    st_netfd_close(nfd);      \
 | 
			
		||||
    errno = n;                \
 | 
			
		||||
    return (ret);             \
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
int _stx_dns_getaddrlist(const char *host, struct in_addr *addrs,
 | 
			
		||||
                         int *num_addrs, st_utime_t timeout)
 | 
			
		||||
{
 | 
			
		||||
    char name[MAXDNAME], **domain;
 | 
			
		||||
    const char *cp;
 | 
			
		||||
    int s, n, maxlen, dots;
 | 
			
		||||
    int trailing_dot, tried_as_is;
 | 
			
		||||
    st_netfd_t nfd;
 | 
			
		||||
 | 
			
		||||
    if ((_res.options & RES_INIT) == 0 && res_init() == -1) {
 | 
			
		||||
	h_errno = NETDB_INTERNAL;
 | 
			
		||||
	return -1;
 | 
			
		||||
    }
 | 
			
		||||
    if (_res.options & RES_USEVC) {
 | 
			
		||||
	h_errno = NETDB_INTERNAL;
 | 
			
		||||
	errno = ENOSYS;
 | 
			
		||||
	return -1;
 | 
			
		||||
    }
 | 
			
		||||
    if (!host || *host == '\0') {
 | 
			
		||||
	h_errno = HOST_NOT_FOUND;
 | 
			
		||||
	return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Create UDP socket */
 | 
			
		||||
    if ((s = socket(PF_INET, SOCK_DGRAM, 0)) < 0) {
 | 
			
		||||
	h_errno = NETDB_INTERNAL;
 | 
			
		||||
	return -1;
 | 
			
		||||
    }
 | 
			
		||||
    if ((nfd = st_netfd_open_socket(s)) == NULL) {
 | 
			
		||||
	h_errno = NETDB_INTERNAL;
 | 
			
		||||
	n = errno;
 | 
			
		||||
	close(s);
 | 
			
		||||
	errno = n;
 | 
			
		||||
	return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    maxlen = sizeof(name) - 1;
 | 
			
		||||
    n = 0;
 | 
			
		||||
    dots = 0;
 | 
			
		||||
    trailing_dot = 0;
 | 
			
		||||
    tried_as_is = 0;
 | 
			
		||||
 | 
			
		||||
    for (cp = host; *cp && n < maxlen; cp++) {
 | 
			
		||||
	dots += (*cp == '.');
 | 
			
		||||
	name[n++] = *cp;
 | 
			
		||||
    }
 | 
			
		||||
    if (name[n - 1] == '.')
 | 
			
		||||
	trailing_dot = 1;
 | 
			
		||||
 | 
			
		||||
    /*
 | 
			
		||||
     * If there are dots in the name already, let's just give it a try
 | 
			
		||||
     * 'as is'.  The threshold can be set with the "ndots" option.
 | 
			
		||||
     */
 | 
			
		||||
    if (dots >= _res.ndots) {
 | 
			
		||||
	if (query_domain(nfd, host, addrs, num_addrs, timeout) == 0)
 | 
			
		||||
	    CLOSE_AND_RETURN(0);
 | 
			
		||||
	if (h_errno == NETDB_INTERNAL && errno == EINTR)
 | 
			
		||||
	    CLOSE_AND_RETURN(-1);
 | 
			
		||||
	tried_as_is = 1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /*
 | 
			
		||||
     * We do at least one level of search if
 | 
			
		||||
     *     - there is no dot and RES_DEFNAME is set, or
 | 
			
		||||
     *     - there is at least one dot, there is no trailing dot,
 | 
			
		||||
     *       and RES_DNSRCH is set.
 | 
			
		||||
     */
 | 
			
		||||
    if ((!dots && (_res.options & RES_DEFNAMES)) ||
 | 
			
		||||
	(dots && !trailing_dot && (_res.options & RES_DNSRCH))) {
 | 
			
		||||
	name[n++] = '.';
 | 
			
		||||
	for (domain = _res.dnsrch; *domain; domain++) {
 | 
			
		||||
	    strncpy(name + n, *domain, maxlen - n);
 | 
			
		||||
	    if (query_domain(nfd, name, addrs, num_addrs, timeout) == 0)
 | 
			
		||||
		CLOSE_AND_RETURN(0);
 | 
			
		||||
	    if (h_errno == NETDB_INTERNAL && errno == EINTR)
 | 
			
		||||
		CLOSE_AND_RETURN(-1);
 | 
			
		||||
	    if (!(_res.options & RES_DNSRCH))
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /*
 | 
			
		||||
     * If we have not already tried the name "as is", do that now.
 | 
			
		||||
     * note that we do this regardless of how many dots were in the
 | 
			
		||||
     * name or whether it ends with a dot.
 | 
			
		||||
     */
 | 
			
		||||
    if (!tried_as_is) {
 | 
			
		||||
	if (query_domain(nfd, host, addrs, num_addrs, timeout) == 0)
 | 
			
		||||
	    CLOSE_AND_RETURN(0);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    CLOSE_AND_RETURN(-1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1,343 +0,0 @@
 | 
			
		|||
#include "stx.h"
 | 
			
		||||
#include "common.h"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*****************************************
 | 
			
		||||
 * Basic types definitions
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
struct _stx_centry {
 | 
			
		||||
  void               *key;           /* key for doing lookups */
 | 
			
		||||
  void               *data;          /* data in the cache */
 | 
			
		||||
  size_t             weight;         /* "weight" of this entry */
 | 
			
		||||
  struct _stx_centry *next;          /* next entry */
 | 
			
		||||
  struct _stx_centry **pthis;
 | 
			
		||||
  stx_clist_t        lru_link;       /* for putting this entry on LRU list */
 | 
			
		||||
  int                ref_count;      /* use count for this entry */
 | 
			
		||||
  int                delete_pending; /* pending delete flag */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct _stx_cache {
 | 
			
		||||
  size_t	    max_size;    /* max size of cache */
 | 
			
		||||
  size_t            cur_size;    /* current size of cache */
 | 
			
		||||
 | 
			
		||||
  size_t            max_weight;  /* cache capacity */
 | 
			
		||||
  size_t            cur_weight;  /* current total "weight" of all entries */
 | 
			
		||||
 | 
			
		||||
  size_t	    hash_size;   /* size of hash table */
 | 
			
		||||
  stx_cache_entry_t **table;     /* hash table for this cache */
 | 
			
		||||
 | 
			
		||||
  stx_clist_t       lru_list;    /* least-recently-used list */
 | 
			
		||||
 | 
			
		||||
  /* Cache stats */
 | 
			
		||||
  unsigned long     hits;        /* num cache hits */
 | 
			
		||||
  unsigned long     lookups;     /* num cache lookups */
 | 
			
		||||
  unsigned long     inserts;     /* num inserts */
 | 
			
		||||
  unsigned long     deletes;     /* num deletes */
 | 
			
		||||
 | 
			
		||||
  /* Functions */
 | 
			
		||||
  unsigned long (*key_hash_fn)(const void *);
 | 
			
		||||
  long          (*key_cmp_fn)(const void *, const void *);
 | 
			
		||||
  void          (*cleanup_fn)(void *, void *);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#define STX_CACHE_ENTRY_PTR(_qp) \
 | 
			
		||||
  ((stx_cache_entry_t *)((char *)(_qp) - offsetof(stx_cache_entry_t, lru_link)))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*****************************************
 | 
			
		||||
 * Cache methods
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
stx_cache_t *stx_cache_create(size_t max_size, size_t max_weight,
 | 
			
		||||
			      size_t hash_size,
 | 
			
		||||
			      unsigned long (*key_hash_fn)(const void *key),
 | 
			
		||||
			      long (*key_cmp_fn)(const void *key1,
 | 
			
		||||
						 const void *key2),
 | 
			
		||||
			      void (*cleanup_fn)(void *key, void *data))
 | 
			
		||||
{
 | 
			
		||||
    stx_cache_t *newcache;
 | 
			
		||||
 | 
			
		||||
    newcache = (stx_cache_t *)calloc(1, sizeof(stx_cache_t));
 | 
			
		||||
    if (newcache == NULL)
 | 
			
		||||
	return NULL;
 | 
			
		||||
    newcache->table = (stx_cache_entry_t **)calloc(hash_size,
 | 
			
		||||
					         sizeof(stx_cache_entry_t *));
 | 
			
		||||
    if (newcache->table == NULL) {
 | 
			
		||||
	free(newcache);
 | 
			
		||||
	return NULL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    newcache->max_size = max_size;
 | 
			
		||||
    newcache->max_weight = max_weight;
 | 
			
		||||
    newcache->hash_size = hash_size;
 | 
			
		||||
    STX_CLIST_INIT_CLIST(&(newcache->lru_list));
 | 
			
		||||
    newcache->key_hash_fn = key_hash_fn;
 | 
			
		||||
    newcache->key_cmp_fn = key_cmp_fn;
 | 
			
		||||
    newcache->cleanup_fn = cleanup_fn;
 | 
			
		||||
 | 
			
		||||
    return newcache;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void stx_cache_empty(stx_cache_t *cache)
 | 
			
		||||
{
 | 
			
		||||
    size_t i;
 | 
			
		||||
    stx_cache_entry_t *entry, *next_entry;
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < cache->hash_size; i++) {
 | 
			
		||||
	entry = cache->table[i];
 | 
			
		||||
	while (entry) {
 | 
			
		||||
	    next_entry = entry->next;
 | 
			
		||||
	    stx_cache_entry_delete(cache, entry);
 | 
			
		||||
	    entry = next_entry;
 | 
			
		||||
	}
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void stx_cache_traverse(stx_cache_t *cache,
 | 
			
		||||
			void (*callback)(void *key, void *data))
 | 
			
		||||
{
 | 
			
		||||
    size_t i;
 | 
			
		||||
    stx_cache_entry_t *entry;
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < cache->hash_size; i++) {
 | 
			
		||||
	for (entry = cache->table[i]; entry; entry = entry->next) {
 | 
			
		||||
	    if (!entry->delete_pending)
 | 
			
		||||
		(*callback)(entry->key, entry->data);
 | 
			
		||||
	}
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void stx_cache_traverse_lru(stx_cache_t *cache,
 | 
			
		||||
			    void (*callback)(void *key, void *data),
 | 
			
		||||
			    unsigned int n)
 | 
			
		||||
{
 | 
			
		||||
    stx_clist_t *q;
 | 
			
		||||
    stx_cache_entry_t *entry;
 | 
			
		||||
 | 
			
		||||
    for (q = STX_CLIST_HEAD(&cache->lru_list); q != &cache->lru_list && n;
 | 
			
		||||
	 q = q->next, n--) {
 | 
			
		||||
	entry = STX_CACHE_ENTRY_PTR(q);
 | 
			
		||||
	(*callback)(entry->key, entry->data);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void stx_cache_traverse_mru(stx_cache_t *cache,
 | 
			
		||||
			    void (*callback)(void *key, void *data),
 | 
			
		||||
			    unsigned int n)
 | 
			
		||||
{
 | 
			
		||||
    stx_clist_t *q;
 | 
			
		||||
    stx_cache_entry_t *entry;
 | 
			
		||||
 | 
			
		||||
    for (q = STX_CLIST_TAIL(&cache->lru_list); q != &cache->lru_list && n;
 | 
			
		||||
	 q = q->prev, n--) {
 | 
			
		||||
	entry = STX_CACHE_ENTRY_PTR(q);
 | 
			
		||||
	(*callback)(entry->key, entry->data);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
size_t stx_cache_getsize(stx_cache_t *cache)
 | 
			
		||||
{
 | 
			
		||||
    return cache->cur_size;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
size_t stx_cache_getweight(stx_cache_t *cache)
 | 
			
		||||
{
 | 
			
		||||
    return cache->cur_weight;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void stx_cache_getinfo(stx_cache_t *cache, stx_cache_info_t *info)
 | 
			
		||||
{
 | 
			
		||||
    info->max_size = cache->max_size;
 | 
			
		||||
    info->max_weight = cache->max_weight;
 | 
			
		||||
    info->hash_size = cache->hash_size;
 | 
			
		||||
    info->cur_size = cache->cur_size;
 | 
			
		||||
    info->cur_weight = cache->cur_weight;
 | 
			
		||||
    info->hits = cache->hits;
 | 
			
		||||
    info->lookups = cache->lookups;
 | 
			
		||||
    info->inserts = cache->inserts;
 | 
			
		||||
    info->deletes = cache->deletes;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*****************************************
 | 
			
		||||
 * Cache entry methods
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
stx_cache_entry_t *stx_cache_entry_create(void *key, void *data,
 | 
			
		||||
					  size_t weight)
 | 
			
		||||
{
 | 
			
		||||
    stx_cache_entry_t *newentry;
 | 
			
		||||
 | 
			
		||||
    newentry = (stx_cache_entry_t *)calloc(1, sizeof(stx_cache_entry_t));
 | 
			
		||||
    if (newentry == NULL)
 | 
			
		||||
	return NULL;
 | 
			
		||||
 | 
			
		||||
    newentry->key = key;
 | 
			
		||||
    newentry->data = data;
 | 
			
		||||
    newentry->weight = weight;
 | 
			
		||||
 | 
			
		||||
    return newentry;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void stx_cache_entry_delete(stx_cache_t *cache, stx_cache_entry_t *entry)
 | 
			
		||||
{
 | 
			
		||||
    entry->delete_pending = 1;
 | 
			
		||||
 | 
			
		||||
    if (entry->ref_count > 0)
 | 
			
		||||
	return;
 | 
			
		||||
 | 
			
		||||
    if (entry->pthis) {
 | 
			
		||||
	*entry->pthis = entry->next;
 | 
			
		||||
	if (entry->next)
 | 
			
		||||
	    entry->next->pthis = entry->pthis;
 | 
			
		||||
 | 
			
		||||
	cache->cur_size--;
 | 
			
		||||
	cache->cur_weight -= entry->weight;
 | 
			
		||||
	cache->deletes++;
 | 
			
		||||
	STX_CLIST_REMOVE_LINK(&(entry->lru_link));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (cache->cleanup_fn)
 | 
			
		||||
	cache->cleanup_fn(entry->key, entry->data);
 | 
			
		||||
 | 
			
		||||
    entry->pthis = NULL;
 | 
			
		||||
    entry->key   = NULL;
 | 
			
		||||
    entry->data  = NULL;
 | 
			
		||||
    free(entry);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
stx_cache_entry_t *stx_cache_entry_lookup(stx_cache_t *cache, const void *key)
 | 
			
		||||
{
 | 
			
		||||
    unsigned long bucket;
 | 
			
		||||
    stx_cache_entry_t *entry;
 | 
			
		||||
 | 
			
		||||
    cache->lookups++;
 | 
			
		||||
    bucket = cache->key_hash_fn(key) % cache->hash_size;
 | 
			
		||||
    for (entry = cache->table[bucket]; entry; entry = entry->next) {
 | 
			
		||||
	if (!entry->delete_pending && cache->key_cmp_fn(key, entry->key) == 0)
 | 
			
		||||
	    break;
 | 
			
		||||
    }
 | 
			
		||||
    if (entry) {
 | 
			
		||||
	cache->hits++;
 | 
			
		||||
	if (entry->ref_count == 0)
 | 
			
		||||
	    STX_CLIST_REMOVE_LINK(&(entry->lru_link));
 | 
			
		||||
	entry->ref_count++;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return entry;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void stx_cache_entry_release(stx_cache_t *cache, stx_cache_entry_t *entry)
 | 
			
		||||
{
 | 
			
		||||
    if (entry->ref_count == 0)
 | 
			
		||||
	return;
 | 
			
		||||
 | 
			
		||||
    entry->ref_count--;
 | 
			
		||||
 | 
			
		||||
    if (entry->ref_count == 0) {
 | 
			
		||||
	STX_CLIST_APPEND_LINK(&(entry->lru_link), &(cache->lru_list));
 | 
			
		||||
	if (entry->delete_pending)
 | 
			
		||||
	    stx_cache_entry_delete(cache, entry);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
int stx_cache_entry_insert(stx_cache_t *cache, stx_cache_entry_t *entry)
 | 
			
		||||
{
 | 
			
		||||
    stx_cache_entry_t *old_entry;
 | 
			
		||||
    unsigned long bucket;
 | 
			
		||||
 | 
			
		||||
    /*
 | 
			
		||||
     * If cache capacity is exceeded, try to remove LRU entries till there is
 | 
			
		||||
     * enough room or LRU list is empty.
 | 
			
		||||
     */
 | 
			
		||||
    while (cache->cur_weight + entry->weight > cache->max_weight) {
 | 
			
		||||
	old_entry = stx_cache_entry_getlru(cache);
 | 
			
		||||
	if (!old_entry) {
 | 
			
		||||
	    /* cache capacity is exceeded and all entries are in use */
 | 
			
		||||
	    return -1;
 | 
			
		||||
	}
 | 
			
		||||
	stx_cache_entry_delete(cache, old_entry);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* If cache size is exceeded, remove LRU entry */
 | 
			
		||||
    if (cache->cur_size >= cache->max_size) {
 | 
			
		||||
	old_entry = stx_cache_entry_getlru(cache);
 | 
			
		||||
	if (!old_entry) {
 | 
			
		||||
	    /* cache size is exceeded and all entries are in use */
 | 
			
		||||
	    return -1;
 | 
			
		||||
	}
 | 
			
		||||
	stx_cache_entry_delete(cache, old_entry);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Don't add duplicate entries in the cache */
 | 
			
		||||
    bucket = cache->key_hash_fn(entry->key) % cache->hash_size;
 | 
			
		||||
    for (old_entry = cache->table[bucket]; old_entry;
 | 
			
		||||
	 old_entry = old_entry->next) {
 | 
			
		||||
	if (!old_entry->delete_pending &&
 | 
			
		||||
	    cache->key_cmp_fn(entry->key, old_entry->key) == 0)
 | 
			
		||||
	    break;
 | 
			
		||||
    }
 | 
			
		||||
    if (old_entry)
 | 
			
		||||
	stx_cache_entry_delete(cache, old_entry);
 | 
			
		||||
 | 
			
		||||
    /* Insert in the hash table */
 | 
			
		||||
    entry->next = cache->table[bucket];
 | 
			
		||||
    cache->table[bucket] = entry;
 | 
			
		||||
    entry->pthis = &cache->table[bucket];
 | 
			
		||||
    if (entry->next)
 | 
			
		||||
	entry->next->pthis = &entry->next;
 | 
			
		||||
    entry->ref_count++;
 | 
			
		||||
 | 
			
		||||
    cache->inserts++;
 | 
			
		||||
    cache->cur_size++;
 | 
			
		||||
    cache->cur_weight += entry->weight;
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
stx_cache_entry_t *stx_cache_entry_getlru(stx_cache_t *cache)
 | 
			
		||||
{
 | 
			
		||||
    if (STX_CLIST_IS_EMPTY(&(cache->lru_list)))
 | 
			
		||||
	return NULL;
 | 
			
		||||
 | 
			
		||||
    return STX_CACHE_ENTRY_PTR(STX_CLIST_HEAD(&(cache->lru_list)));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
int stx_cache_entry_sizeof(void)
 | 
			
		||||
{
 | 
			
		||||
    return (int)sizeof(stx_cache_entry_t);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void *stx_cache_entry_getdata(stx_cache_entry_t *entry)
 | 
			
		||||
{
 | 
			
		||||
    return entry->data;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void *stx_cache_entry_getkey(stx_cache_entry_t *entry)
 | 
			
		||||
{
 | 
			
		||||
    return entry->key;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
size_t stx_cache_entry_getweight(stx_cache_entry_t *entry)
 | 
			
		||||
{
 | 
			
		||||
    return entry->weight;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1,367 +0,0 @@
 | 
			
		|||
Michael Abd-El-Malek contributed this patch.  He wrote:
 | 
			
		||||
----------------------------------------
 | 
			
		||||
Hello,
 | 
			
		||||
 | 
			
		||||
This is a patch that enables programmatically dumping the stack of  
 | 
			
		||||
every thread.  This has been useful in debugging deadlocks, etc...   
 | 
			
		||||
Our usage model is that the SIGUSR2 handler calls the new  
 | 
			
		||||
_st_print_thread_stacks function, which dumps the stack for all  
 | 
			
		||||
threads.  A convenient feature is that for thread stacks that are the  
 | 
			
		||||
same (which is common for application with a lot of worker threads  
 | 
			
		||||
waiting for work), only one stack trace is printed, along with a  
 | 
			
		||||
count of how many threads have that same stack.
 | 
			
		||||
 | 
			
		||||
I use the glibc backtrace function to get the backtrace, and then use  
 | 
			
		||||
popen to execute addr2line and convert memory addresses to file  
 | 
			
		||||
names, function names, and line numbers.  If glibc isn't available,  
 | 
			
		||||
_st_print_thread_stacks just prints a warning.  And this feature is  
 | 
			
		||||
only available if DEBUG is turned on.
 | 
			
		||||
 | 
			
		||||
We've found this feature extremely helpful when debugging.
 | 
			
		||||
 | 
			
		||||
The patch can be a bit more robust (it assumes addr2line exists).   
 | 
			
		||||
But I didn't want to go through the hassle of doing this, if the  
 | 
			
		||||
StateThreads community doesn't want to use this patch.  (In our  
 | 
			
		||||
environment, addr2line will always be there.)
 | 
			
		||||
 | 
			
		||||
Cheers,
 | 
			
		||||
Mike
 | 
			
		||||
----------------------------------------
 | 
			
		||||
Invoking complex functions from a signal handler is not recommended,
 | 
			
		||||
plus this patch changes the behavior of existing API hooks.  It will
 | 
			
		||||
not become part of State Threads proper but you may find it useful
 | 
			
		||||
nonetheless.  This patch applies to st-1.5.2.
 | 
			
		||||
 | 
			
		||||
diff -Nur Makefile.1.5.2 Makefile
 | 
			
		||||
--- Makefile.1.5.2	Wed Sep  7 14:19:50 2005
 | 
			
		||||
+++ Makefile	Wed Sep  7 14:33:08 2005
 | 
			
		||||
@@ -255,7 +255,8 @@
 | 
			
		||||
               $(TARGETDIR)/stk.o   \
 | 
			
		||||
               $(TARGETDIR)/sync.o  \
 | 
			
		||||
               $(TARGETDIR)/key.o   \
 | 
			
		||||
-              $(TARGETDIR)/io.o
 | 
			
		||||
+              $(TARGETDIR)/io.o    \
 | 
			
		||||
+	      $(TARGETDIR)/backtrace.o
 | 
			
		||||
 OBJS        += $(EXTRA_OBJS)
 | 
			
		||||
 HEADER      = $(TARGETDIR)/st.h
 | 
			
		||||
 SLIBRARY    = $(TARGETDIR)/libst.a
 | 
			
		||||
diff -Nur backtrace.c.1.5.2 backtrace.c
 | 
			
		||||
--- backtrace.c.1.5.2	Wed Dec 31 16:00:00 1969
 | 
			
		||||
+++ backtrace.c	Wed Sep  7 13:40:21 2005
 | 
			
		||||
@@ -0,0 +1,211 @@
 | 
			
		||||
+/*
 | 
			
		||||
+ * The contents of this file are subject to the Mozilla Public
 | 
			
		||||
+ * License Version 1.1 (the "License"); you may not use this file
 | 
			
		||||
+ * except in compliance with the License. You may obtain a copy of
 | 
			
		||||
+ * the License at http://www.mozilla.org/MPL/
 | 
			
		||||
+ *
 | 
			
		||||
+ * Software distributed under the License is distributed on an "AS
 | 
			
		||||
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
 | 
			
		||||
+ * implied. See the License for the specific language governing
 | 
			
		||||
+ * rights and limitations under the License.
 | 
			
		||||
+ *
 | 
			
		||||
+ * Contributor(s):  Michael Abd-El-Malek (mabdelmalek@cmu.edu)
 | 
			
		||||
+ *                  Carnegie Mellon University
 | 
			
		||||
+ *
 | 
			
		||||
+ * Alternatively, the contents of this file may be used under the
 | 
			
		||||
+ * terms of the GNU General Public License Version 2 or later (the
 | 
			
		||||
+ * "GPL"), in which case the provisions of the GPL are applicable
 | 
			
		||||
+ * instead of those above.  If you wish to allow use of your
 | 
			
		||||
+ * version of this file only under the terms of the GPL and not to
 | 
			
		||||
+ * allow others to use your version of this file under the MPL,
 | 
			
		||||
+ * indicate your decision by deleting the provisions above and
 | 
			
		||||
+ * replace them with the notice and other provisions required by
 | 
			
		||||
+ * the GPL.  If you do not delete the provisions above, a recipient
 | 
			
		||||
+ * may use your version of this file under either the MPL or the
 | 
			
		||||
+ * GPL.
 | 
			
		||||
+ */
 | 
			
		||||
+
 | 
			
		||||
+
 | 
			
		||||
+
 | 
			
		||||
+/*
 | 
			
		||||
+ * This file contains routines for printing a stack trace of all threads.
 | 
			
		||||
+ * Only works when DEBUG is defined and where glibc is available, since it
 | 
			
		||||
+ * provides the backtrace() function.
 | 
			
		||||
+ */
 | 
			
		||||
+
 | 
			
		||||
+#define _GNU_SOURCE  /* to get program_invocation_name */
 | 
			
		||||
+
 | 
			
		||||
+#include <stdio.h>
 | 
			
		||||
+#include <stdlib.h>
 | 
			
		||||
+
 | 
			
		||||
+
 | 
			
		||||
+#if defined(DEBUG) && defined(__GLIBC__)
 | 
			
		||||
+
 | 
			
		||||
+#include <errno.h>
 | 
			
		||||
+#include "common.h"
 | 
			
		||||
+#include <execinfo.h>
 | 
			
		||||
+#include <inttypes.h>
 | 
			
		||||
+#include <string.h>
 | 
			
		||||
+
 | 
			
		||||
+
 | 
			
		||||
+/* The maximum number of frames to get a stack trace for.  If a thread has more
 | 
			
		||||
+ * frames than this, then we only show the latest X frames. */
 | 
			
		||||
+#define MAX_NUM_FRAMES 64
 | 
			
		||||
+
 | 
			
		||||
+
 | 
			
		||||
+typedef struct thread_stack_s {
 | 
			
		||||
+   uint32_t        num_frames;
 | 
			
		||||
+   void*           addresses[MAX_NUM_FRAMES]; /* frame pointers */
 | 
			
		||||
+   char*           locations[MAX_NUM_FRAMES]; /* file/function/line numbers  */
 | 
			
		||||
+   uint32_t        num_matches;
 | 
			
		||||
+
 | 
			
		||||
+   struct thread_stack_s* next;
 | 
			
		||||
+} thread_stack_t;
 | 
			
		||||
+
 | 
			
		||||
+static thread_stack_t* stacks = NULL;
 | 
			
		||||
+
 | 
			
		||||
+
 | 
			
		||||
+/* Converts the function's memory addresses to function names, file names, and
 | 
			
		||||
+ * line numbers.  Calls binutil's addr2line program. */
 | 
			
		||||
+static void get_symbol_names(thread_stack_t *stack)
 | 
			
		||||
+{
 | 
			
		||||
+     char program_to_run[1024], function[256], filename_lineno[256], temp[19];
 | 
			
		||||
+     FILE* output;
 | 
			
		||||
+     int num_bytes_left;
 | 
			
		||||
+     uint32_t i;
 | 
			
		||||
+
 | 
			
		||||
+     /* Construct the arguments to addr2line */
 | 
			
		||||
+     num_bytes_left = sizeof(program_to_run);
 | 
			
		||||
+     num_bytes_left -= snprintf(program_to_run, sizeof(program_to_run),
 | 
			
		||||
+                                "addr2line -fCe %s", program_invocation_name);
 | 
			
		||||
+     for (i = 0; i < stack->num_frames && num_bytes_left > 0; ++i) {
 | 
			
		||||
+         num_bytes_left -= snprintf(temp, sizeof(temp), " %p", stack->addresses[i]);
 | 
			
		||||
+         strncat(program_to_run, temp, num_bytes_left);
 | 
			
		||||
+     }
 | 
			
		||||
+
 | 
			
		||||
+     /* Use popen to execute addr2line and read its ouput */
 | 
			
		||||
+     output = popen(program_to_run, "r");
 | 
			
		||||
+     for (i = 0; i < stack->num_frames; ++i) {
 | 
			
		||||
+         char* function_listing = (char*) malloc(512);
 | 
			
		||||
+         fscanf(output, "%255s\n", function);
 | 
			
		||||
+         fscanf(output, "%255s\n", filename_lineno);
 | 
			
		||||
+         snprintf(function_listing, 512, "%s at %s", function, filename_lineno);
 | 
			
		||||
+         stack->locations[i] = function_listing;
 | 
			
		||||
+     }
 | 
			
		||||
+     pclose(output);
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
+
 | 
			
		||||
+static void print_stack(thread_stack_t* stack)
 | 
			
		||||
+{
 | 
			
		||||
+     int skip_offset = 0, cmp_len;
 | 
			
		||||
+     uint32_t i;
 | 
			
		||||
+
 | 
			
		||||
+     /* Get the function names/filenames/line numbers */
 | 
			
		||||
+     get_symbol_names(stack);
 | 
			
		||||
+
 | 
			
		||||
+     cmp_len = strlen("_st_iterate_threads_helper");
 | 
			
		||||
+
 | 
			
		||||
+     /* Print the backtrace */
 | 
			
		||||
+     for (i = 0; i < stack->num_frames; ++i) {
 | 
			
		||||
+         /* Skip frames we don't have location info for */
 | 
			
		||||
+         if (!strncmp(stack->locations[i], "??", 2)) {
 | 
			
		||||
+             continue;
 | 
			
		||||
+         }
 | 
			
		||||
+
 | 
			
		||||
+         /* Skip the frames that are used for printing the stack trace */
 | 
			
		||||
+         if (skip_offset) {
 | 
			
		||||
+             printf("\t#%2d %s %p\n", i - skip_offset, stack->locations[i],
 | 
			
		||||
+                    stack->addresses[i]);
 | 
			
		||||
+         } else if (!strncmp(stack->locations[i], "_st_iterate_threads_helper",
 | 
			
		||||
+                             cmp_len)) {
 | 
			
		||||
+             skip_offset = i + 1;
 | 
			
		||||
+         }
 | 
			
		||||
+     }
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
+
 | 
			
		||||
+static void add_current_thread_stack(void)
 | 
			
		||||
+{
 | 
			
		||||
+     thread_stack_t *new_stack = malloc(sizeof(thread_stack_t));
 | 
			
		||||
+     thread_stack_t *search;
 | 
			
		||||
+
 | 
			
		||||
+     /* Call glibc function to get the backtrace */
 | 
			
		||||
+     new_stack->num_frames = backtrace(new_stack->addresses, MAX_NUM_FRAMES);
 | 
			
		||||
+
 | 
			
		||||
+     /* Check if we have another stacks that is equivalent.  If so, then coaelsce
 | 
			
		||||
+      *  two stacks into one, to minimize output to user. */
 | 
			
		||||
+     search = stacks;
 | 
			
		||||
+     while (search) {
 | 
			
		||||
+         if (search->num_frames == new_stack->num_frames &&
 | 
			
		||||
+             !memcmp(search->addresses, new_stack->addresses,
 | 
			
		||||
+                     search->num_frames * sizeof(void*))) {
 | 
			
		||||
+             /* Found an existing stack that is the same as this thread's stack */
 | 
			
		||||
+             ++search->num_matches;
 | 
			
		||||
+             free(new_stack);
 | 
			
		||||
+             return;
 | 
			
		||||
+         } else {
 | 
			
		||||
+             search = search->next;
 | 
			
		||||
+         }
 | 
			
		||||
+     }
 | 
			
		||||
+
 | 
			
		||||
+     /* This is a new stack.  Add it to the list of stacks. */
 | 
			
		||||
+     new_stack->num_matches = 1;
 | 
			
		||||
+     new_stack->next = stacks;
 | 
			
		||||
+     stacks = new_stack;
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
+static void print_stack_frames(void)
 | 
			
		||||
+{
 | 
			
		||||
+     while (stacks) {
 | 
			
		||||
+         printf("\n%u thread(s) with this backtrace:\n", stacks->num_matches);
 | 
			
		||||
+         print_stack(stacks);
 | 
			
		||||
+         stacks = stacks->next;
 | 
			
		||||
+     }
 | 
			
		||||
+     printf("\n");
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
+static void free_stacks(void)
 | 
			
		||||
+{
 | 
			
		||||
+     uint32_t i;
 | 
			
		||||
+     while (stacks) {
 | 
			
		||||
+         thread_stack_t *next = stacks->next;
 | 
			
		||||
+         for (i = 0; i < stacks->num_frames; ++i) {
 | 
			
		||||
+             free(stacks->locations[i]);
 | 
			
		||||
+         }
 | 
			
		||||
+         free(stacks);
 | 
			
		||||
+         stacks = next;
 | 
			
		||||
+     }
 | 
			
		||||
+     stacks = NULL;
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
+
 | 
			
		||||
+static void st_print_thread_stack(_st_thread_t *thread, int start_flag,
 | 
			
		||||
+                                   int end_flag)
 | 
			
		||||
+{
 | 
			
		||||
+     if (end_flag == 0) {
 | 
			
		||||
+         add_current_thread_stack();
 | 
			
		||||
+     } else {
 | 
			
		||||
+         print_stack_frames();
 | 
			
		||||
+     }
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
+
 | 
			
		||||
+void _st_print_thread_stacks(int ignore)
 | 
			
		||||
+{
 | 
			
		||||
+     _st_iterate_threads_flag = 1;
 | 
			
		||||
+     _st_iterate_threads_helper(st_print_thread_stack);
 | 
			
		||||
+     _st_iterate_threads_flag = 0;
 | 
			
		||||
+
 | 
			
		||||
+     /* Deallocate memory */
 | 
			
		||||
+     free_stacks();
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
+#else  /* defined(DEBUG) && defined(__GLIBC__) */
 | 
			
		||||
+
 | 
			
		||||
+void _st_print_thread_stacks(int ignore)
 | 
			
		||||
+{
 | 
			
		||||
+     printf("%s: need DEBUG mode and glibc-specific functions to read stack.\n",
 | 
			
		||||
+            __FUNCTION__);
 | 
			
		||||
+}
 | 
			
		||||
+#endif /* defined(DEBUG) && defined(__GLIBC__) */
 | 
			
		||||
diff -Nur common.h.1.5.2 common.h
 | 
			
		||||
--- common.h.1.5.2	Wed Sep  7 14:18:37 2005
 | 
			
		||||
+++ common.h	Wed Sep  7 14:35:36 2005
 | 
			
		||||
@@ -371,8 +371,18 @@
 | 
			
		||||
  */
 | 
			
		||||
 
 | 
			
		||||
 #ifdef DEBUG
 | 
			
		||||
-void _st_iterate_threads(void);
 | 
			
		||||
-#define ST_DEBUG_ITERATE_THREADS() _st_iterate_threads()
 | 
			
		||||
+typedef void(*_st_func_ptr_t)(_st_thread_t *thread,
 | 
			
		||||
+                              int           start_flag,
 | 
			
		||||
+                              int           end_flag);
 | 
			
		||||
+/* Pointer to function that will be called on thread switch */
 | 
			
		||||
+extern _st_func_ptr_t _st_iterate_func_ptr;
 | 
			
		||||
+extern int _st_iterate_threads_flag;
 | 
			
		||||
+/* Thread iteration function that will call an arbitrary function */
 | 
			
		||||
+extern void _st_iterate_threads_helper(_st_func_ptr_t func);
 | 
			
		||||
+#define ST_DEBUG_ITERATE_THREADS()                               \
 | 
			
		||||
+          if (_st_iterate_func_ptr) {                            \
 | 
			
		||||
+              _st_iterate_threads_helper(_st_iterate_func_ptr);  \
 | 
			
		||||
+          }
 | 
			
		||||
 #else
 | 
			
		||||
 #define ST_DEBUG_ITERATE_THREADS()
 | 
			
		||||
 #endif
 | 
			
		||||
diff -Nur public.h.1.5.2 public.h
 | 
			
		||||
--- public.h.1.5.2	Wed Sep  7 11:46:58 2005
 | 
			
		||||
+++ public.h	Wed Sep  7 13:38:46 2005
 | 
			
		||||
@@ -171,8 +171,10 @@
 | 
			
		||||
 extern st_netfd_t st_open(const char *path, int oflags, mode_t mode);
 | 
			
		||||
 
 | 
			
		||||
 #ifdef DEBUG
 | 
			
		||||
-extern void _st_show_thread_stack(st_thread_t thread, const char *messg);
 | 
			
		||||
+extern void _st_show_thread_stack(st_thread_t thread, int start_flag,
 | 
			
		||||
+				  int end_flag);
 | 
			
		||||
 extern void _st_iterate_threads(void);
 | 
			
		||||
+extern void _st_print_thread_stacks(int ignore);
 | 
			
		||||
 #endif
 | 
			
		||||
 
 | 
			
		||||
 #ifdef __cplusplus
 | 
			
		||||
diff -Nur sched.c.1.5.2 sched.c
 | 
			
		||||
--- sched.c.1.5.2	Wed Sep  7 10:48:05 2005
 | 
			
		||||
+++ sched.c	Wed Sep  7 13:38:46 2005
 | 
			
		||||
@@ -919,16 +919,13 @@
 | 
			
		||||
 
 | 
			
		||||
 
 | 
			
		||||
 #ifdef DEBUG
 | 
			
		||||
-/* ARGSUSED */
 | 
			
		||||
-void _st_show_thread_stack(_st_thread_t *thread, const char *messg)
 | 
			
		||||
-{
 | 
			
		||||
-
 | 
			
		||||
-}
 | 
			
		||||
-
 | 
			
		||||
 /* To be set from debugger */
 | 
			
		||||
 int _st_iterate_threads_flag = 0;
 | 
			
		||||
+/* Thread iteration function that will call an arbitrary function */
 | 
			
		||||
+_st_func_ptr_t _st_iterate_func_ptr = NULL;
 | 
			
		||||
 
 | 
			
		||||
-void _st_iterate_threads(void)
 | 
			
		||||
+/* This function iterates over all threads, calling "func" for each thread. */
 | 
			
		||||
+void _st_iterate_threads_helper(_st_func_ptr_t func)
 | 
			
		||||
 {
 | 
			
		||||
   static _st_thread_t *thread = NULL;
 | 
			
		||||
   static jmp_buf orig_jb, save_jb;
 | 
			
		||||
@@ -944,16 +941,20 @@
 | 
			
		||||
 
 | 
			
		||||
   if (thread) {
 | 
			
		||||
     memcpy(thread->context, save_jb, sizeof(jmp_buf));
 | 
			
		||||
-    _st_show_thread_stack(thread, NULL);
 | 
			
		||||
+    func(thread, 0, 0);
 | 
			
		||||
   } else {
 | 
			
		||||
     if (MD_SETJMP(orig_jb)) {
 | 
			
		||||
       _st_iterate_threads_flag = 0;
 | 
			
		||||
+      _st_iterate_func_ptr = NULL;
 | 
			
		||||
       thread = NULL;
 | 
			
		||||
-      _st_show_thread_stack(thread, "Iteration completed");
 | 
			
		||||
+      /* Last thread to iterate through  */
 | 
			
		||||
+      func(thread, 0, 1);
 | 
			
		||||
       return;
 | 
			
		||||
     }
 | 
			
		||||
+    /* First thread to iterate through  */
 | 
			
		||||
     thread = _ST_CURRENT_THREAD();
 | 
			
		||||
-    _st_show_thread_stack(thread, "Iteration started");
 | 
			
		||||
+    _st_iterate_func_ptr = func;
 | 
			
		||||
+    func(thread, 1, 0);
 | 
			
		||||
   }
 | 
			
		||||
 
 | 
			
		||||
   q = thread->tlink.next;
 | 
			
		||||
@@ -966,5 +967,17 @@
 | 
			
		||||
   memcpy(save_jb, thread->context, sizeof(jmp_buf));
 | 
			
		||||
   MD_LONGJMP(thread->context, 1);
 | 
			
		||||
 }
 | 
			
		||||
+
 | 
			
		||||
+/* ARGSUSED */
 | 
			
		||||
+void _st_show_thread_stack(_st_thread_t *thread, int start_flag, int end_flag)
 | 
			
		||||
+{
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
+/* Iterate over threads inside debugger; see st/README */
 | 
			
		||||
+void _st_iterate_threads(void)
 | 
			
		||||
+{
 | 
			
		||||
+  _st_iterate_threads_helper(_st_show_thread_stack);
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
 #endif /* DEBUG */
 | 
			
		||||
 
 | 
			
		||||
| 
						 | 
				
			
			@ -1,91 +0,0 @@
 | 
			
		|||
#ifndef _STX_H_
 | 
			
		||||
#define _STX_H_
 | 
			
		||||
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <ctype.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
#include <sys/types.h>
 | 
			
		||||
#include <sys/socket.h>
 | 
			
		||||
#include <netinet/in.h>
 | 
			
		||||
#include <arpa/nameser.h>
 | 
			
		||||
#include <resolv.h>
 | 
			
		||||
#include <netdb.h>
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include "st.h"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
extern "C" {
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*****************************************
 | 
			
		||||
 * Basic types definitions
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
typedef struct _stx_centry stx_cache_entry_t;
 | 
			
		||||
typedef struct _stx_cache  stx_cache_t;
 | 
			
		||||
 | 
			
		||||
/* This is public type */
 | 
			
		||||
typedef struct _stx_cache_info {
 | 
			
		||||
  size_t max_size;
 | 
			
		||||
  size_t max_weight;
 | 
			
		||||
  size_t hash_size;
 | 
			
		||||
  size_t cur_size;
 | 
			
		||||
  size_t cur_weight;
 | 
			
		||||
  unsigned long hits;
 | 
			
		||||
  unsigned long lookups;
 | 
			
		||||
  unsigned long inserts;
 | 
			
		||||
  unsigned long deletes;
 | 
			
		||||
} stx_cache_info_t;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*****************************************
 | 
			
		||||
 * Cache and cache entry methods
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
stx_cache_t *stx_cache_create(size_t max_size, size_t max_weight,
 | 
			
		||||
			      size_t hash_size,
 | 
			
		||||
			      unsigned long (*key_hash_fn)(const void *key),
 | 
			
		||||
			      long (*key_cmp_fn)(const void *key1,
 | 
			
		||||
						 const void *key2),
 | 
			
		||||
			      void (*cleanup_fn)(void *key, void *data));
 | 
			
		||||
void stx_cache_empty(stx_cache_t *cache);
 | 
			
		||||
void stx_cache_traverse(stx_cache_t *cache,
 | 
			
		||||
			void (*callback)(void *key, void *data));
 | 
			
		||||
void stx_cache_traverse_lru(stx_cache_t *, void (*)(void *, void *),
 | 
			
		||||
			    unsigned int);
 | 
			
		||||
void stx_cache_traverse_mru(stx_cache_t *, void (*)(void *, void *),
 | 
			
		||||
			    unsigned int);
 | 
			
		||||
void stx_cache_getinfo(stx_cache_t *cache, stx_cache_info_t *info);
 | 
			
		||||
size_t stx_cache_getsize(stx_cache_t *cache);
 | 
			
		||||
size_t stx_cache_getweight(stx_cache_t *cache);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
stx_cache_entry_t *stx_cache_entry_create(void *key, void *data,
 | 
			
		||||
					  size_t weight);
 | 
			
		||||
void stx_cache_entry_delete(stx_cache_t *cache, stx_cache_entry_t *entry);
 | 
			
		||||
stx_cache_entry_t *stx_cache_entry_lookup(stx_cache_t *cache, const void *key);
 | 
			
		||||
void stx_cache_entry_release(stx_cache_t *, stx_cache_entry_t *);
 | 
			
		||||
int stx_cache_entry_insert(stx_cache_t *cache, stx_cache_entry_t *entry);
 | 
			
		||||
stx_cache_entry_t *stx_cache_entry_getlru(stx_cache_t *cache);
 | 
			
		||||
int stx_cache_entry_sizeof(void);
 | 
			
		||||
void *stx_cache_entry_getdata(stx_cache_entry_t *entry);
 | 
			
		||||
void *stx_cache_entry_getkey(stx_cache_entry_t *entry);
 | 
			
		||||
size_t stx_cache_entry_getweight(stx_cache_entry_t *entry);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
int stx_dns_cache_init(size_t max_size, size_t max_bytes, size_t hash_size);
 | 
			
		||||
void stx_dns_cache_getinfo(stx_cache_info_t *info);
 | 
			
		||||
int stx_dns_getaddrlist(const char *hostname, struct in_addr *addrs,
 | 
			
		||||
			int *num_addrs, st_utime_t timeout);
 | 
			
		||||
int stx_dns_getaddr(const char *hostname, struct in_addr *addr,
 | 
			
		||||
		    st_utime_t timeout);
 | 
			
		||||
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#endif /* !_STX_H_ */
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1,197 +0,0 @@
 | 
			
		|||
/*
 | 
			
		||||
 * File I/O extension to the State Threads Library.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/* 
 | 
			
		||||
 * The contents of this file are subject to the Mozilla Public
 | 
			
		||||
 * License Version 1.1 (the "License"); you may not use this file
 | 
			
		||||
 * except in compliance with the License. You may obtain a copy of
 | 
			
		||||
 * the License at http://www.mozilla.org/MPL/
 | 
			
		||||
 * 
 | 
			
		||||
 * Software distributed under the License is distributed on an "AS
 | 
			
		||||
 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
 | 
			
		||||
 * implied. See the License for the specific language governing
 | 
			
		||||
 * rights and limitations under the License.
 | 
			
		||||
 * 
 | 
			
		||||
 * The Original Code is the file I/O extension to the State Threads Library.
 | 
			
		||||
 * 
 | 
			
		||||
 * The Initial Developer of the Original Code is Jeff
 | 
			
		||||
 * <jlb-st@houseofdistraction.com>.  Portions created by the Initial
 | 
			
		||||
 * Developer are Copyright (C) 2002 the Initial Developer.  All Rights
 | 
			
		||||
 * Reserved.
 | 
			
		||||
 * 
 | 
			
		||||
 * Contributor(s): (none)
 | 
			
		||||
 * 
 | 
			
		||||
 * Alternatively, the contents of this file may be used under the
 | 
			
		||||
 * terms of the GNU General Public License Version 2 or later (the
 | 
			
		||||
 * "GPL"), in which case the provisions of the GPL are applicable 
 | 
			
		||||
 * instead of those above.  If you wish to allow use of your 
 | 
			
		||||
 * version of this file only under the terms of the GPL and not to
 | 
			
		||||
 * allow others to use your version of this file under the MPL,
 | 
			
		||||
 * indicate your decision by deleting the provisions above and
 | 
			
		||||
 * replace them with the notice and other provisions required by
 | 
			
		||||
 * the GPL.  If you do not delete the provisions above, a recipient
 | 
			
		||||
 * may use your version of this file under either the MPL or the
 | 
			
		||||
 * GPL.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
 | 
			
		||||
#include "stx_fileio.h"
 | 
			
		||||
 | 
			
		||||
#define STX_FILEIO_SIGNUM SIGUSR2
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
    st_netfd_t data_fd;
 | 
			
		||||
    st_netfd_t control_fd;
 | 
			
		||||
    pid_t pid;
 | 
			
		||||
} fileio_data_t;
 | 
			
		||||
 | 
			
		||||
#define FILEREADER_MAX_READ 1024
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
    off_t offset;
 | 
			
		||||
    ssize_t nbytes;
 | 
			
		||||
} file_reader_cb_t;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Fork a process to read a file and return its pid.  Receives
 | 
			
		||||
 * offset/length commands from control stream and sends corresponding data
 | 
			
		||||
 * to out stream.  A zero length on the control stream signals an end.
 | 
			
		||||
 *
 | 
			
		||||
 * @param fd stream from which to read
 | 
			
		||||
 * @param control_out receives the file descriptor to which control commands can be sent
 | 
			
		||||
 * @param fd_out receives the file descriptor from which the output of the command can be read.
 | 
			
		||||
 * @return PID of the process created to execute the command
 | 
			
		||||
 */
 | 
			
		||||
pid_t
 | 
			
		||||
file_reader(int fd, int *fd_control, int *fd_out)
 | 
			
		||||
{
 | 
			
		||||
    pid_t pid;
 | 
			
		||||
    int control_pipe[2], out_pipe[2];
 | 
			
		||||
 | 
			
		||||
    if (pipe(control_pipe) < 0 || pipe(out_pipe) < 0)
 | 
			
		||||
        return (pid_t)-1;
 | 
			
		||||
 | 
			
		||||
    pid = fork();
 | 
			
		||||
    if (pid == (pid_t) -1)
 | 
			
		||||
    {
 | 
			
		||||
        close(control_pipe[0]);
 | 
			
		||||
        close(control_pipe[1]);
 | 
			
		||||
        close(out_pipe[0]);
 | 
			
		||||
        close(out_pipe[1]);
 | 
			
		||||
        return pid;
 | 
			
		||||
    }
 | 
			
		||||
    else if (pid == (pid_t) 0)
 | 
			
		||||
    {
 | 
			
		||||
        // child
 | 
			
		||||
        off_t pos = 0;
 | 
			
		||||
        file_reader_cb_t cb;
 | 
			
		||||
        char buf[FILEREADER_MAX_READ];
 | 
			
		||||
        if (fd == -1)
 | 
			
		||||
            _exit(EXIT_FAILURE);
 | 
			
		||||
 | 
			
		||||
        while (sizeof(cb) == read(control_pipe[0], &cb, sizeof(cb))) {
 | 
			
		||||
            ssize_t nb;
 | 
			
		||||
            if (0 >= cb.nbytes)
 | 
			
		||||
                goto clean_exit;
 | 
			
		||||
            if (pos != cb.offset) {
 | 
			
		||||
                pos = lseek(fd, cb.offset, SEEK_SET);
 | 
			
		||||
                if (pos == (off_t)-1)
 | 
			
		||||
                    break;
 | 
			
		||||
            }
 | 
			
		||||
            nb = read(fd, buf, cb.nbytes);
 | 
			
		||||
            if (nb == (ssize_t)-1)
 | 
			
		||||
                break;
 | 
			
		||||
            pos += nb;
 | 
			
		||||
            write(out_pipe[1], (char *)&nb, sizeof(nb));
 | 
			
		||||
            write(out_pipe[1], buf, nb);
 | 
			
		||||
        }
 | 
			
		||||
        perror("ERROR: file_reader: ");
 | 
			
		||||
    clean_exit:
 | 
			
		||||
        close(control_pipe[0]);
 | 
			
		||||
        close(control_pipe[1]);
 | 
			
		||||
        close(out_pipe[0]);
 | 
			
		||||
        close(out_pipe[1]);
 | 
			
		||||
        _exit(EXIT_SUCCESS);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // parent
 | 
			
		||||
    close(out_pipe[1]);
 | 
			
		||||
    close(control_pipe[0]);
 | 
			
		||||
    *fd_out = out_pipe[0];
 | 
			
		||||
    *fd_control = control_pipe[1];
 | 
			
		||||
    return pid;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * fileio_data_t destructor callback
 | 
			
		||||
 */
 | 
			
		||||
static void
 | 
			
		||||
fileio_data_destructor(void *dat_in)
 | 
			
		||||
{
 | 
			
		||||
    if (dat_in) {
 | 
			
		||||
        fileio_data_t *dat = (fileio_data_t *)dat_in;
 | 
			
		||||
        file_reader_cb_t cb;
 | 
			
		||||
        cb.offset = 0;
 | 
			
		||||
        cb.nbytes = 0;
 | 
			
		||||
        st_write(dat->control_fd, (char *)&cb, sizeof(cb),
 | 
			
		||||
	 ST_UTIME_NO_TIMEOUT);
 | 
			
		||||
        waitpid(dat->pid, NULL, 0);
 | 
			
		||||
        st_netfd_close(dat->control_fd);
 | 
			
		||||
        st_netfd_close(dat->data_fd);
 | 
			
		||||
        free(dat_in);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Retrieve fileio_data_t struct from an st descriptor.  Create and store
 | 
			
		||||
 * a new one if needed.
 | 
			
		||||
 */
 | 
			
		||||
static fileio_data_t *get_fileio_data(st_netfd_t fd)
 | 
			
		||||
{
 | 
			
		||||
    fileio_data_t *dat = (fileio_data_t *)st_netfd_getspecific(fd);
 | 
			
		||||
    if (!dat) {
 | 
			
		||||
        int fd_control, fd_out;
 | 
			
		||||
        pid_t pid = file_reader(st_netfd_fileno(fd), &fd_control, &fd_out);
 | 
			
		||||
        if (pid != (pid_t)-1) {
 | 
			
		||||
            dat = (fileio_data_t *)calloc(1, sizeof(fileio_data_t));
 | 
			
		||||
            dat->control_fd = st_netfd_open(fd_control);
 | 
			
		||||
            dat->data_fd = st_netfd_open(fd_out);
 | 
			
		||||
            dat->pid = pid;
 | 
			
		||||
            st_netfd_setspecific(fd, dat, fileio_data_destructor);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return dat;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Read data from the specified section of a file.  Uses a forked
 | 
			
		||||
 * file_reader process to do the actual reading so as to avoid causing all
 | 
			
		||||
 * State Threads to block.
 | 
			
		||||
 *
 | 
			
		||||
 * @param fd must refer to a seekable file.
 | 
			
		||||
 * @param offset absolute offset within the file
 | 
			
		||||
 * @param buf output buffer
 | 
			
		||||
 * @param nbytes size of the output buffer
 | 
			
		||||
 * @param timeout
 | 
			
		||||
 */
 | 
			
		||||
ssize_t
 | 
			
		||||
stx_file_read(st_netfd_t fd, off_t offset, void *buf, size_t nbytes, st_utime_t timeout)
 | 
			
		||||
{
 | 
			
		||||
    fileio_data_t *dat = get_fileio_data(fd);
 | 
			
		||||
    if (dat) {
 | 
			
		||||
        file_reader_cb_t cb;
 | 
			
		||||
        ssize_t ret = (ssize_t)-1;
 | 
			
		||||
        cb.offset = offset;
 | 
			
		||||
        cb.nbytes = nbytes;
 | 
			
		||||
        st_write(dat->control_fd, (char *)&cb, sizeof(cb), timeout);
 | 
			
		||||
        if (sizeof(ret) == st_read(dat->data_fd, (char *)&ret, sizeof(ret), timeout) && 0 < ret && ret <= nbytes) {
 | 
			
		||||
            return st_read(dat->data_fd, buf, ret, timeout);
 | 
			
		||||
        } else {
 | 
			
		||||
            return ret;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return (ssize_t)-1;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,52 +0,0 @@
 | 
			
		|||
/*
 | 
			
		||||
 * File I/O extension to the State Threads Library.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/* 
 | 
			
		||||
 * The contents of this file are subject to the Mozilla Public
 | 
			
		||||
 * License Version 1.1 (the "License"); you may not use this file
 | 
			
		||||
 * except in compliance with the License. You may obtain a copy of
 | 
			
		||||
 * the License at http://www.mozilla.org/MPL/
 | 
			
		||||
 * 
 | 
			
		||||
 * Software distributed under the License is distributed on an "AS
 | 
			
		||||
 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
 | 
			
		||||
 * implied. See the License for the specific language governing
 | 
			
		||||
 * rights and limitations under the License.
 | 
			
		||||
 * 
 | 
			
		||||
 * The Original Code is the file I/O extension to the State Threads Library.
 | 
			
		||||
 * 
 | 
			
		||||
 * The Initial Developer of the Original Code is Jeff
 | 
			
		||||
 * <jlb-st@houseofdistraction.com>.  Portions created by the Initial
 | 
			
		||||
 * Developer are Copyright (C) 2002 the Initial Developer.  All Rights
 | 
			
		||||
 * Reserved.
 | 
			
		||||
 * 
 | 
			
		||||
 * Contributor(s): (none)
 | 
			
		||||
 * 
 | 
			
		||||
 * Alternatively, the contents of this file may be used under the
 | 
			
		||||
 * terms of the GNU General Public License Version 2 or later (the
 | 
			
		||||
 * "GPL"), in which case the provisions of the GPL are applicable 
 | 
			
		||||
 * instead of those above.  If you wish to allow use of your 
 | 
			
		||||
 * version of this file only under the terms of the GPL and not to
 | 
			
		||||
 * allow others to use your version of this file under the MPL,
 | 
			
		||||
 * indicate your decision by deleting the provisions above and
 | 
			
		||||
 * replace them with the notice and other provisions required by
 | 
			
		||||
 * the GPL.  If you do not delete the provisions above, a recipient
 | 
			
		||||
 * may use your version of this file under either the MPL or the
 | 
			
		||||
 * GPL.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef __STX_FILEIO_H__
 | 
			
		||||
#define __STX_FILEIO_H__
 | 
			
		||||
 | 
			
		||||
#include <st.h>
 | 
			
		||||
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
extern "C" {
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
extern ssize_t stx_file_read(st_netfd_t fd, off_t offset, void *buf, size_t nbytes, st_utime_t timeout);
 | 
			
		||||
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#endif /* !__STX_FILEIO_H__ */
 | 
			
		||||
| 
						 | 
				
			
			@ -1,112 +0,0 @@
 | 
			
		|||
#include "stx.h"
 | 
			
		||||
#include <netinet/in.h>
 | 
			
		||||
#include <arpa/inet.h>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#define MAX_ADDRS 128
 | 
			
		||||
#define TIMEOUT (4*1000000LL)
 | 
			
		||||
 | 
			
		||||
static void do_resolve(const char *host)
 | 
			
		||||
{
 | 
			
		||||
    struct in_addr addrs[MAX_ADDRS];
 | 
			
		||||
    int i, n = MAX_ADDRS;
 | 
			
		||||
 | 
			
		||||
    if (stx_dns_getaddrlist(host, addrs, &n, TIMEOUT) < 0) {
 | 
			
		||||
	fprintf(stderr, "stx_dns_getaddrlist: can't resolve %s: ", host);
 | 
			
		||||
	if (h_errno == NETDB_INTERNAL)
 | 
			
		||||
	    perror("");
 | 
			
		||||
	else
 | 
			
		||||
	    herror("");
 | 
			
		||||
    } else {
 | 
			
		||||
	if (n > 0)
 | 
			
		||||
	    printf("%-40s %s\n", (char *)host, inet_ntoa(addrs[0]));
 | 
			
		||||
	for (i = 1; i < n; i++)
 | 
			
		||||
	    printf("%-40s %s\n", "", inet_ntoa(addrs[i]));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void show_info(void)
 | 
			
		||||
{
 | 
			
		||||
    stx_cache_info_t info;
 | 
			
		||||
 | 
			
		||||
    stx_dns_cache_getinfo(&info);
 | 
			
		||||
    printf("DNS cache info:\n\n");
 | 
			
		||||
    printf("max_size:  %8d\n", (int)info.max_size);
 | 
			
		||||
    printf("capacity:  %8d bytes\n", (int)info.max_weight);
 | 
			
		||||
    printf("hash_size: %8d\n", (int)info.hash_size);
 | 
			
		||||
    printf("cur_size:  %8d\n"
 | 
			
		||||
	   "cur_mem:   %8d bytes\n"
 | 
			
		||||
	   "hits:      %8d\n"
 | 
			
		||||
	   "lookups:   %8d\n"
 | 
			
		||||
	   "inserts:   %8d\n"
 | 
			
		||||
	   "deletes:   %8d\n",
 | 
			
		||||
	   (int)info.cur_size, (int)info.cur_weight, (int)info.hits,
 | 
			
		||||
	   (int)info.lookups, (int)info.inserts, (int)info.deletes);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
extern stx_cache_t *_stx_dns_cache;
 | 
			
		||||
 | 
			
		||||
static void printhost(void *host, void *data)
 | 
			
		||||
{
 | 
			
		||||
    printf("%s\n", (char *)host);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void show_lru(void)
 | 
			
		||||
{
 | 
			
		||||
    printf("LRU hosts:\n\n");
 | 
			
		||||
    stx_cache_traverse_lru(_stx_dns_cache, printhost, 10);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void show_mru(void)
 | 
			
		||||
{
 | 
			
		||||
    printf("MRU hosts:\n\n");
 | 
			
		||||
    stx_cache_traverse_mru(_stx_dns_cache, printhost, 10);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void flush_cache(void)
 | 
			
		||||
{
 | 
			
		||||
    stx_cache_empty(_stx_dns_cache);
 | 
			
		||||
    printf("DNS cache is empty\n");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
int main()
 | 
			
		||||
{
 | 
			
		||||
    char line[256];
 | 
			
		||||
    char str[sizeof(line)];
 | 
			
		||||
 | 
			
		||||
    st_init();
 | 
			
		||||
    stx_dns_cache_init(100, 10000, 101);
 | 
			
		||||
 | 
			
		||||
    for ( ; ; ) {
 | 
			
		||||
	fputs("> ", stdout);
 | 
			
		||||
	fflush(stdout);
 | 
			
		||||
	if (!fgets(line, sizeof(line), stdin))
 | 
			
		||||
	    break;
 | 
			
		||||
	if (sscanf(line, "%s", str) != 1)
 | 
			
		||||
	    continue;
 | 
			
		||||
	if (strcmp(str, "exit") == 0 || strcmp(str, "quit") == 0)
 | 
			
		||||
	    break;
 | 
			
		||||
	if (strcmp(str, "info") == 0) {
 | 
			
		||||
	    show_info();
 | 
			
		||||
	    continue;
 | 
			
		||||
	}
 | 
			
		||||
	if (strcmp(str, "lru") == 0) {
 | 
			
		||||
	    show_lru();
 | 
			
		||||
	    continue;
 | 
			
		||||
	}
 | 
			
		||||
	if (strcmp(str, "mru") == 0) {
 | 
			
		||||
	    show_mru();
 | 
			
		||||
	    continue;
 | 
			
		||||
	}
 | 
			
		||||
	if (strcmp(str, "flush") == 0) {
 | 
			
		||||
	    flush_cache();
 | 
			
		||||
	    continue;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	do_resolve(str);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1,51 +0,0 @@
 | 
			
		|||
EXPORTS
 | 
			
		||||
    st_accept @62
 | 
			
		||||
    st_cond_broadcast @63
 | 
			
		||||
    st_cond_destroy @64
 | 
			
		||||
    st_cond_new @65
 | 
			
		||||
    st_cond_signal @66
 | 
			
		||||
    st_cond_timedwait @67
 | 
			
		||||
    st_cond_wait @68
 | 
			
		||||
    st_connect @69
 | 
			
		||||
    st_getfdlimit @70
 | 
			
		||||
    st_init @71
 | 
			
		||||
    st_key_create @72
 | 
			
		||||
    st_key_getlimit @73
 | 
			
		||||
    st_mutex_destroy @74
 | 
			
		||||
    st_mutex_lock @75
 | 
			
		||||
    st_mutex_new @76
 | 
			
		||||
    st_mutex_trylock @77
 | 
			
		||||
    st_mutex_unlock @78
 | 
			
		||||
    st_netfd_close @79
 | 
			
		||||
    st_netfd_fileno @80
 | 
			
		||||
    st_netfd_free @81
 | 
			
		||||
    st_netfd_getspecific @82
 | 
			
		||||
    st_netfd_open @83
 | 
			
		||||
    st_netfd_open_socket @84
 | 
			
		||||
    st_netfd_poll @85
 | 
			
		||||
    st_netfd_serialize_accept @86
 | 
			
		||||
    st_netfd_setspecific @87
 | 
			
		||||
    st_open @88
 | 
			
		||||
    st_poll @89
 | 
			
		||||
    st_randomize_stacks @90
 | 
			
		||||
    st_read @91
 | 
			
		||||
    st_read_fully @92
 | 
			
		||||
    st_read_resid @93
 | 
			
		||||
    st_recvfrom @94
 | 
			
		||||
    st_sendto @95
 | 
			
		||||
    st_sleep @96
 | 
			
		||||
    st_thread_create @97
 | 
			
		||||
    st_thread_exit @98
 | 
			
		||||
    st_thread_getspecific @99
 | 
			
		||||
    st_thread_interrupt @100
 | 
			
		||||
    st_thread_join @101
 | 
			
		||||
    st_thread_self @102
 | 
			
		||||
    st_thread_setspecific @103
 | 
			
		||||
    st_time @104
 | 
			
		||||
    st_timecache_set @105
 | 
			
		||||
    st_usleep @106
 | 
			
		||||
    st_utime @107
 | 
			
		||||
    st_utime_last_clock @108
 | 
			
		||||
    st_write @109
 | 
			
		||||
    st_write_resid @110
 | 
			
		||||
    st_writev @111
 | 
			
		||||
| 
						 | 
				
			
			@ -1,10 +0,0 @@
 | 
			
		|||
prefix=@prefix@
 | 
			
		||||
exec_prefix=${prefix}
 | 
			
		||||
libdir=${exec_prefix}/lib
 | 
			
		||||
includedir=${prefix}/include
 | 
			
		||||
 | 
			
		||||
Name: libst
 | 
			
		||||
Description: State Thread Library
 | 
			
		||||
Version: @VERSION@
 | 
			
		||||
Libs: -L${libdir} -lst
 | 
			
		||||
Cflags: -I${includedir}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,79 +0,0 @@
 | 
			
		|||
Summary:	State Threads Library
 | 
			
		||||
Name:		st
 | 
			
		||||
Version:	1.9
 | 
			
		||||
Release:	1
 | 
			
		||||
Copyright:	MPL 1.2 or GPL 2+
 | 
			
		||||
Packager:	Wesley W. Terpstra <wesley@terpstra.ca>
 | 
			
		||||
Source:		http://prdownloads.sourceforge.net/state-threads/st-%{version}.tar.gz
 | 
			
		||||
Prefix:		/usr
 | 
			
		||||
BuildRoot:	/tmp/%{name}-%{version}-build
 | 
			
		||||
Group:		Development/Libraries
 | 
			
		||||
 | 
			
		||||
%description
 | 
			
		||||
The State Threads library has an interface similar to POSIX threads.
 | 
			
		||||
 | 
			
		||||
However, the threads are actually all run in-process. This type of
 | 
			
		||||
threading allows for controlled schedualing points. It is highly useful 
 | 
			
		||||
for designing robust and extremely scalable internet applications since
 | 
			
		||||
there is no resource contention and locking is generally unnecessary.
 | 
			
		||||
 | 
			
		||||
It can be combined with traditional threading or multiple process
 | 
			
		||||
parallelism to take advantage of multiple processors.
 | 
			
		||||
 | 
			
		||||
See: <http://state-threads.sourceforge.net/docs/st.html> for further
 | 
			
		||||
information about how state threads improve performance.
 | 
			
		||||
 | 
			
		||||
%package -n libst-devel
 | 
			
		||||
Summary:	State Threads Library - Development Files
 | 
			
		||||
Group:		Development/Libraries
 | 
			
		||||
Requires:	libst1
 | 
			
		||||
 | 
			
		||||
%description -n libst-devel
 | 
			
		||||
Development headers and documentation for libst
 | 
			
		||||
 | 
			
		||||
%package -n libst1
 | 
			
		||||
Summary:	State Threads Library - Shared Libs Major 1
 | 
			
		||||
Group:		System/Libraries
 | 
			
		||||
 | 
			
		||||
%description -n libst1
 | 
			
		||||
Shared libraries for running applications linked against api version 1.
 | 
			
		||||
 | 
			
		||||
%prep
 | 
			
		||||
%setup -q
 | 
			
		||||
 | 
			
		||||
%build
 | 
			
		||||
make CONFIG_GUESS_PATH=/usr/share/automake default-optimized
 | 
			
		||||
 | 
			
		||||
%install
 | 
			
		||||
if [ -d ${RPM_BUILD_ROOT} ]; then rm -rf ${RPM_BUILD_ROOT}; fi
 | 
			
		||||
 | 
			
		||||
mkdir -m 0755 -p ${RPM_BUILD_ROOT}/%{prefix}/lib/pkgconfig
 | 
			
		||||
mkdir -m 0755 -p ${RPM_BUILD_ROOT}/%{prefix}/include
 | 
			
		||||
mkdir -m 0755 -p ${RPM_BUILD_ROOT}/%{prefix}/share/doc/libst-devel
 | 
			
		||||
cp -a obj/libst.* ${RPM_BUILD_ROOT}/%{prefix}/lib
 | 
			
		||||
cp -a obj/st.h    ${RPM_BUILD_ROOT}/%{prefix}/include
 | 
			
		||||
sed "s*@prefix@*%{prefix}*g" <st.pc >${RPM_BUILD_ROOT}/%{prefix}/lib/pkgconfig/st.pc
 | 
			
		||||
cp -a docs/*      ${RPM_BUILD_ROOT}/%{prefix}/share/doc/libst-devel/
 | 
			
		||||
cp -a examples    ${RPM_BUILD_ROOT}/%{prefix}/share/doc/libst-devel/
 | 
			
		||||
 | 
			
		||||
%post -n libst1
 | 
			
		||||
/sbin/ldconfig %{prefix}/lib
 | 
			
		||||
 | 
			
		||||
%files -n libst1
 | 
			
		||||
%defattr(-,root,root)
 | 
			
		||||
%{prefix}/lib/lib*.so.*
 | 
			
		||||
 | 
			
		||||
%files -n libst-devel
 | 
			
		||||
%defattr(-,root,root)
 | 
			
		||||
%{prefix}/include/*
 | 
			
		||||
%{prefix}/lib/lib*.a
 | 
			
		||||
%{prefix}/lib/lib*.so
 | 
			
		||||
%{prefix}/lib/pkgconfig/st.pc
 | 
			
		||||
%{prefix}/share/doc/libst-devel/*
 | 
			
		||||
 | 
			
		||||
%clean
 | 
			
		||||
if [ -d ${RPM_BUILD_ROOT} ]; then rm -rf ${RPM_BUILD_ROOT}; fi
 | 
			
		||||
 | 
			
		||||
%changelog
 | 
			
		||||
* Wed Dec 26 2001 Wesley W. Terpstra <wesley@terpstra.ca>
 | 
			
		||||
- first rpms for libst-1.3.tar.gz
 | 
			
		||||
							
								
								
									
										3
									
								
								trunk/research/st-1.9/st/init
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								trunk/research/st-1.9/st/init
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,3 @@
 | 
			
		|||
#ifndef _st_icpp_init_stub
 | 
			
		||||
#define _st_icpp_init_stub
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										16
									
								
								trunk/research/st-1.9/st/st.upp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								trunk/research/st-1.9/st/st.upp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,16 @@
 | 
			
		|||
file
 | 
			
		||||
	main readonly separator,
 | 
			
		||||
	..\common.h,
 | 
			
		||||
	..\event.c,
 | 
			
		||||
	..\io.c,
 | 
			
		||||
	..\key.c,
 | 
			
		||||
	..\md.h,
 | 
			
		||||
	..\md.S,
 | 
			
		||||
	..\public.h,
 | 
			
		||||
	..\sched.c,
 | 
			
		||||
	..\stk.c,
 | 
			
		||||
	..\sync.c;
 | 
			
		||||
 | 
			
		||||
mainconfig
 | 
			
		||||
	"" = "MAIN";
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue