正在显示
19 个修改的文件
包含
0 行增加
和
4795 行删除
trunk/research/st-1.9/README
已删除
100644 → 0
| 1 | -WELCOME! | ||
| 2 | - | ||
| 3 | -The State Threads Library is a small application library which provides | ||
| 4 | -a foundation for writing fast and highly scalable Internet applications | ||
| 5 | -(such as web servers, proxy servers, mail transfer agents, and so on, | ||
| 6 | -really any network-data-driven application) on UNIX-like platforms. It | ||
| 7 | -combines the simplicity of the multithreaded programming paradigm, in | ||
| 8 | -which one thread supports each simultaneous connection, with the | ||
| 9 | -performance and scalability of an event-driven state machine | ||
| 10 | -architecture. In other words, this library offers a threading API for | ||
| 11 | -structuring an Internet application as a state machine. For more | ||
| 12 | -details, please see the library documentation in the "docs" directory or | ||
| 13 | -on-line at | ||
| 14 | - | ||
| 15 | - http://state-threads.sourceforge.net/docs/ | ||
| 16 | - | ||
| 17 | -The State Threads Project is an open source project for maintaining and | ||
| 18 | -enhancing the State Threads Library. For more information about this | ||
| 19 | -project, please see | ||
| 20 | - | ||
| 21 | - http://state-threads.sourceforge.net/ | ||
| 22 | - | ||
| 23 | - | ||
| 24 | -BUILDING | ||
| 25 | - | ||
| 26 | -To build the library by hand, use the GNU make utility. Run the make | ||
| 27 | -command (e.g., `gmake') with no arguments to display all supported | ||
| 28 | -targets. | ||
| 29 | - | ||
| 30 | -To build more or less automatically, first set the CONFIG_GUESS_PATH | ||
| 31 | -variable in either osguess.sh or your environment then run "make | ||
| 32 | -default" which guesses your OS and builds. Requires the "config.guess" | ||
| 33 | -utility from GNU autoconf (not included with ST). You can use one from | ||
| 34 | -a larger "main" software project or just use any config.guess available | ||
| 35 | -on your system. You can also get it directly from GNU: | ||
| 36 | -ftp://ftp.gnu.org/gnu/autoconf/ | ||
| 37 | - | ||
| 38 | -To build rpms (RedHat Linux 6.2 or later, Linux/Mandrake, Solaris with | ||
| 39 | -gnome, etc.): | ||
| 40 | - download the latest st-x.y.tar.gz | ||
| 41 | - # rpm -ta st-x.y.tar.gz | ||
| 42 | -The .rpms will land in /usr/src/RPMS/<arch>. Install them with: | ||
| 43 | - # rpm -i libst*.rpm | ||
| 44 | -Requires GNU automake and rpm 3.0.3 or later. | ||
| 45 | - | ||
| 46 | -Debian users: | ||
| 47 | - If you run potato, please upgrade to woody. | ||
| 48 | - If you run woody, "apt-get install libst-dev" will get you v1.3. | ||
| 49 | - If you run testing/unstable, you will get the newest available version. | ||
| 50 | - If you *must* have the newest libst in woody, you may follow these | ||
| 51 | - not-recommended instructions: | ||
| 52 | - 1. Add "deb-src <your-favourite-debian-mirror> unstable main" to your | ||
| 53 | - /etc/apt/sources.list | ||
| 54 | - 2. apt-get update | ||
| 55 | - 3. apt-get source st | ||
| 56 | - 4. cd st-1.4 (or whatever version you got) | ||
| 57 | - 5. debuild | ||
| 58 | - 6. dpkg -i ../*.deb | ||
| 59 | - | ||
| 60 | -If your application uses autoconf to search for dependencies and you | ||
| 61 | -want to search for a given version of libst, you can simply add | ||
| 62 | - PKG_CHECK_MODULES(MYAPP, st >= 1.3 mumble >= 0.2.23) | ||
| 63 | -to your configure.ac/in. This will define @MYAPP_LIBS@ and | ||
| 64 | -@MYAPP_CFLAGS@ which you may then use in your Makefile.am/in files to | ||
| 65 | -link against mumble and st. | ||
| 66 | - | ||
| 67 | - | ||
| 68 | -LICENSE | ||
| 69 | - | ||
| 70 | -The State Threads library is a derivative of the Netscape Portable | ||
| 71 | -Runtime library (NSPR). All source code in this directory is | ||
| 72 | -distributed under the terms of the Mozilla Public License (MPL) version | ||
| 73 | -1.1 or the GNU General Public License (GPL) version 2 or later. For | ||
| 74 | -more information about these licenses please see | ||
| 75 | -http://www.mozilla.org/MPL/ and http://www.gnu.org/copyleft/. | ||
| 76 | - | ||
| 77 | -All source code in the "examples" directory is distributed under the BSD | ||
| 78 | -style license. | ||
| 79 | - | ||
| 80 | - | ||
| 81 | -PLATFORMS | ||
| 82 | - | ||
| 83 | -Please see the "docs/notes.html" file for the list of currently | ||
| 84 | -supported platforms. | ||
| 85 | - | ||
| 86 | - | ||
| 87 | -DEBUGGER SUPPORT | ||
| 88 | - | ||
| 89 | -It's almost impossible to print SP and PC in a portable way. The only | ||
| 90 | -way to see thread's stack platform-independently is to actually jump to | ||
| 91 | -the saved context. That's what the _st_iterate_threads() function does. | ||
| 92 | -Do the following to iterate over all threads: | ||
| 93 | - | ||
| 94 | -- set the _st_iterate_threads_flag to 1 in debugger | ||
| 95 | -- set breakpoint at the _st_show_thread_stack() function | ||
| 96 | - (which does nothing) | ||
| 97 | -- call the _st_iterate_threads() function which jumps to the | ||
| 98 | - next thread | ||
| 99 | -- at each break you can explore thread's stack | ||
| 100 | -- continue | ||
| 101 | -- when iteration is complete, you return to the original | ||
| 102 | - point (you can see thread id and a message as arguments of | ||
| 103 | - the _st_show_thread_stack() function). | ||
| 104 | - | ||
| 105 | -You can call _st_iterate_threads() in three ways: | ||
| 106 | - | ||
| 107 | -- Insert it into your source code at the point you want to | ||
| 108 | - go over threads. | ||
| 109 | -- Just run application and this function will be called at | ||
| 110 | - the first context switch. | ||
| 111 | -- Call it directly from the debugger at any point. | ||
| 112 | - | ||
| 113 | -This works with gdb and dbx. | ||
| 114 | - | ||
| 115 | -Example using gdb: | ||
| 116 | - | ||
| 117 | -(gdb) set _st_iterate_threads_flag = 1 | ||
| 118 | -(gdb) b _st_show_thread_stack | ||
| 119 | -... | ||
| 120 | -(gdb) call _st_iterate_threads() | ||
| 121 | -... | ||
| 122 | -(gdb) bt | ||
| 123 | -... | ||
| 124 | -(gdb) c | ||
| 125 | -... | ||
| 126 | -(gdb) bt | ||
| 127 | -... | ||
| 128 | -(gdb) c | ||
| 129 | -... | ||
| 130 | -and so on... | ||
| 131 | - | ||
| 132 | -_st_iterate_threads_flag will be set to 0 automatically | ||
| 133 | -after iteration is over or you can set it to 0 at any time | ||
| 134 | -to stop iteration. | ||
| 135 | - | ||
| 136 | -Sometimes gdb complains about SIGSEGV when you call a function | ||
| 137 | -directly at gdb command-line. It can be ignored -- just call the | ||
| 138 | -same function right away again, it works just fine. For example: | ||
| 139 | - | ||
| 140 | -(gdb) set _st_iterate_threads_flag = 1 | ||
| 141 | -(gdb) b _st_show_thread_stack | ||
| 142 | -Breakpoint 1 at 0x809bbbb: file sched.c, line 856. | ||
| 143 | -(gdb) call _st_iterate_threads() | ||
| 144 | -Program received signal SIGSEGV, Segmentation fault. | ||
| 145 | -.... | ||
| 146 | -(gdb) # just call the function again: | ||
| 147 | -(gdb) call _st_iterate_threads() | ||
| 148 | -Breakpoint 1, _st_show_thread_stack (thread=0x4017aee4, messg=0x80ae7a2 | ||
| 149 | -"Iteration started") at sched.c:856 | ||
| 150 | -856 } | ||
| 151 | -.... | ||
| 152 | - | ||
| 153 | -You can use simple gdb command-line scripting to display | ||
| 154 | -all threads and their stack traces at once: | ||
| 155 | - | ||
| 156 | -(gdb) while _st_iterate_threads_flag | ||
| 157 | - >bt | ||
| 158 | - >c | ||
| 159 | - >end | ||
| 160 | -.... | ||
| 161 | - | ||
| 162 | -Another script to stop at the thread with the specific thread id | ||
| 163 | -(e.g., 0x40252ee4): | ||
| 164 | - | ||
| 165 | -(gdb) # set the flag again: | ||
| 166 | -(gdb) set _st_iterate_threads_flag = 1 | ||
| 167 | -(gdb) call _st_iterate_threads() | ||
| 168 | -Breakpoint 1, _st_show_thread_stack (thread=0x4017aee4, messg=0x80ae7a2 | ||
| 169 | -"Iteration started") at sched.c:856 | ||
| 170 | -856 } | ||
| 171 | -.... | ||
| 172 | -(gdb) while thread != 0x40252ee4 | ||
| 173 | - >c | ||
| 174 | - >end | ||
| 175 | -.... | ||
| 176 | -.... | ||
| 177 | -Breakpoint 1, _st_show_thread_stack (thread=0x40252ee4, messg=0x0) at | ||
| 178 | -sched.c:856 | ||
| 179 | -856 } | ||
| 180 | -(gdb) bt | ||
| 181 | -.... | ||
| 182 | -(gdb) # don't want to continue iteration, unset the flag: | ||
| 183 | -(gdb) set _st_iterate_threads_flag = 0 | ||
| 184 | -(gdb) c | ||
| 185 | -Continuing. | ||
| 186 | -Breakpoint 1, _st_show_thread_stack (thread=0x0, messg=0x80ae78e "Iteration | ||
| 187 | -completed") | ||
| 188 | - at sched.c:856 | ||
| 189 | -856 } | ||
| 190 | -(gdb) c | ||
| 191 | -Continuing. | ||
| 192 | -(gdb) return | ||
| 193 | -Make selected stack frame return now? (y or n) y | ||
| 194 | -#0 0x4011254e in __select () | ||
| 195 | - from /lib/libc.so.6 | ||
| 196 | -(gdb) detach | ||
| 197 | - | ||
| 198 | - | ||
| 199 | -CHANGE LOG | ||
| 200 | - | ||
| 201 | -Changes from 1.8 to 1.9. | ||
| 202 | ------------------------- | ||
| 203 | -o Support 32-bit and 64-bit Intel Macs. | ||
| 204 | - | ||
| 205 | -o Added ST_VERSION string, and ST_VERSION_MAJOR and ST_VERSION_MINOR | ||
| 206 | - [bug 1796801]. | ||
| 207 | - | ||
| 208 | -o Fixed some compiler warnings, based on a patch from Brian Wellington | ||
| 209 | - [bug 1932741]. | ||
| 210 | - | ||
| 211 | - | ||
| 212 | -Changes from 1.7 to 1.8. | ||
| 213 | --------------------------- | ||
| 214 | -o Added support for kqueue and epoll on platforms that support them. | ||
| 215 | - Added ability to choose the event notification system at program | ||
| 216 | - startup. | ||
| 217 | - | ||
| 218 | -o Long-overdue public definitions of ST_UTIME_NO_TIMEOUT (-1ULL) and | ||
| 219 | - ST_UTIME_NO_WAIT (0) [bug 1514436]. | ||
| 220 | - | ||
| 221 | -o Documentation patch for st_utime() [bug 1514484]. | ||
| 222 | - | ||
| 223 | -o Documentation patch for st_timecache_set() [bug 1514486]. | ||
| 224 | - | ||
| 225 | -o Documentation patch for st_netfd_serialize_accept() [bug 1514494]. | ||
| 226 | - | ||
| 227 | -o Added st_writev_resid() [rfe 1538344]. | ||
| 228 | - | ||
| 229 | -o Added st_readv_resid() [rfe 1538768] and, for symmetry, st_readv(). | ||
| 230 | - | ||
| 231 | - | ||
| 232 | -Changes from 1.6 to 1.7. | ||
| 233 | ------------------------- | ||
| 234 | -o Support glibc 2.4, which breaks programs that manipulate jump buffers. | ||
| 235 | - Replaced Linux IA64 special cases with new md.S that covers all | ||
| 236 | - Linux. | ||
| 237 | - | ||
| 238 | - | ||
| 239 | -Changes from 1.5.2 to 1.6. | ||
| 240 | --------------------------- | ||
| 241 | -none | ||
| 242 | - | ||
| 243 | - | ||
| 244 | -Changes from 1.5.1 to 1.5.2. | ||
| 245 | ----------------------------- | ||
| 246 | -o Alfred Perlstein's context switch callback feature. | ||
| 247 | - | ||
| 248 | -o Claus Assmann's st_recvmsg/st_sendmsg wrappers. | ||
| 249 | - | ||
| 250 | -o Extra stack padding for platforms that need it. | ||
| 251 | - | ||
| 252 | -o Ron Arts's timeout clarifications in the reference manual. | ||
| 253 | - | ||
| 254 | -o Raymond Bero and Anton Berezin's AMD64 FreeBSD port. | ||
| 255 | - | ||
| 256 | -o Claus Assmann's AMD64 SunOS 5.10 port. | ||
| 257 | - | ||
| 258 | -o Claus Assmann's AMD64 OpenBSD port. | ||
| 259 | - | ||
| 260 | -o Michael Abd-El-Malek's Mac OS X port. | ||
| 261 | - | ||
| 262 | -o Michael Abd-El-Malek's stack printing patch. | ||
| 263 | - | ||
| 264 | - | ||
| 265 | -Changes from 1.5.0 to 1.5.1. | ||
| 266 | ----------------------------- | ||
| 267 | -o Andreas Gustafsson's USE_POLL fix. | ||
| 268 | - | ||
| 269 | -o Gene's st_set_utime_function() enhancement. | ||
| 270 | - | ||
| 271 | - | ||
| 272 | -Changes from 1.4 to 1.5.0. | ||
| 273 | --------------------------- | ||
| 274 | -o Andreas Gustafsson's performance patch. | ||
| 275 | - | ||
| 276 | -o New extensions: Improved DNS resolver, generic LRU cache, in-process | ||
| 277 | - DNS cache, and a program to test the resolver and cache. | ||
| 278 | - | ||
| 279 | -o Support for AMD Opteron 64-bit CPUs under Linux. | ||
| 280 | - | ||
| 281 | -o Support for SPARC-64 under Solaris. | ||
| 282 | - | ||
| 283 | -o Andreas Gustafsson's support for VAX under NetBSD. | ||
| 284 | - | ||
| 285 | -o Changed unportable #warning directives in md.h to #error. | ||
| 286 | - | ||
| 287 | - | ||
| 288 | -Changes from 1.3 to 1.4. | ||
| 289 | ------------------------- | ||
| 290 | -o Andreas Gustafsson's NetBSD port. | ||
| 291 | - | ||
| 292 | -o Wesley W. Terpstra's Darwin (MacOS X) port. | ||
| 293 | - | ||
| 294 | -o Support for many CPU architectures under Linux and *BSD. | ||
| 295 | - | ||
| 296 | -o Renamed private typedefs so they don't conflict with public ones any | ||
| 297 | - more. | ||
| 298 | - | ||
| 299 | -o common.h now includes public.h for strict prototyping. | ||
| 300 | - | ||
| 301 | -o Joshua Levy's recommendation to make st_connect() and st_sendto() | ||
| 302 | - accept const struct sockaddr pointers, as the originals do. | ||
| 303 | - | ||
| 304 | -o Clarified the documentation regarding blocking vs. non-blocking I/O. | ||
| 305 | - | ||
| 306 | -o Cygwin support. | ||
| 307 | - | ||
| 308 | -o Created the extensions directory. | ||
| 309 | - | ||
| 310 | -o Fixed warnings from ia64asm.S. | ||
| 311 | - | ||
| 312 | - | ||
| 313 | -Changes from 1.2 to 1.3. | ||
| 314 | ------------------------- | ||
| 315 | -o Added st_read_resid() and st_write_resid() to allow the caller to know | ||
| 316 | - how much data was transferred before an error occurred. Updated | ||
| 317 | - documentation. | ||
| 318 | - | ||
| 319 | -o Updated project link, copyrights, and documentation regarding | ||
| 320 | - timeouts. Added comment to st_connect(). | ||
| 321 | - | ||
| 322 | -o Optimized the _st_add_sleep_q() function in sched.c. Now we walk the | ||
| 323 | - sleep queue *backward* when inserting a thread into it. When you | ||
| 324 | - have lots (hundreds) of threads and several timeout values, it takes | ||
| 325 | - a while to insert a thread at the appropriate point in the sleep | ||
| 326 | - queue. The idea is that often this appropriate point is closer to | ||
| 327 | - the end of the queue rather than the beginning. Measurements show | ||
| 328 | - performance improves with this change. In any case this change | ||
| 329 | - should do no harm. | ||
| 330 | - | ||
| 331 | -o Added a hint of when to define USE_POLL and when not to, to the | ||
| 332 | - Makefile. | ||
| 333 | - | ||
| 334 | -o Added debugging support (files common.h and sched.c). See above. | ||
| 335 | - | ||
| 336 | -o Decreased the number of reallocations of _ST_POLLFDS in sched.c. | ||
| 337 | - Inspired by Lev Walkin. | ||
| 338 | - | ||
| 339 | -o Fixed st_usleep(-1) and st_sleep(-1), and added a warning to the | ||
| 340 | - documentation about too-large timeouts. | ||
| 341 | - | ||
| 342 | -o Linux/*BSD Alpha port. | ||
| 343 | - | ||
| 344 | -o Wesley W. Terpstra modernized the build process: | ||
| 345 | - - properly build relocatable libraries under bsd and linux | ||
| 346 | - - use library versioning | ||
| 347 | - - added rpm spec file | ||
| 348 | - - added debian/ files | ||
| 349 | - See above for build instructions. | ||
| 350 | - | ||
| 351 | - | ||
| 352 | -Changes from 1.1 to 1.2. | ||
| 353 | ------------------------- | ||
| 354 | -o Added st_randomize_stacks(). | ||
| 355 | - | ||
| 356 | -o Added a patch contributed by Sascha Schumann. | ||
| 357 | - | ||
| 358 | - | ||
| 359 | -Changes from 1.0 to 1.1. | ||
| 360 | ------------------------- | ||
| 361 | -o Relicensed under dual MPL-GPL. | ||
| 362 | - | ||
| 363 | -o OpenBSD port. | ||
| 364 | - | ||
| 365 | -o Compile-time option to use poll() instead of select() for | ||
| 366 | - event polling (see Makefile). | ||
| 367 | - This is useful if you want to support a large number of open | ||
| 368 | - file descriptors (larger than FD_SETSIZE) within a single | ||
| 369 | - process. | ||
| 370 | - | ||
| 371 | -o Linux IA-64 port. | ||
| 372 | - Two issues make IA-64 different from other platforms: | ||
| 373 | - | ||
| 374 | - - Besides the traditional call stack in memory, IA-64 uses the | ||
| 375 | - general register stack. Thus each thread needs a backing store | ||
| 376 | - for the register stack in addition to the memory stack. | ||
| 377 | - | ||
| 378 | - - Current implementation of setjmp()/longjmp() can not be used | ||
| 379 | - for thread context-switching since it assumes that only one | ||
| 380 | - register stack exists. Using special assembly functions for | ||
| 381 | - context-switching is unavoidable. | ||
| 382 | - | ||
| 383 | -o Thread stack capping on IRIX. | ||
| 384 | - This allows some profiling tools (such as SpeedShop) to know when | ||
| 385 | - to stop unwinding the stack. Without this libexc, used by SpeedShop, | ||
| 386 | - traces right off the stack and crashes. | ||
| 387 | - | ||
| 388 | -o Miscellaneous documentation additions. | ||
| 389 | - | ||
| 390 | - | ||
| 391 | -COPYRIGHTS | ||
| 392 | - | ||
| 393 | -Portions created by SGI are Copyright (C) 2000 Silicon Graphics, Inc. | ||
| 394 | -All Rights Reserved. |
trunk/research/st-1.9/docs/fig.gif
已删除
100644 → 0
5.2 KB
trunk/research/st-1.9/docs/notes.html
已删除
100644 → 0
| 1 | -<HTML> | ||
| 2 | -<HEAD> | ||
| 3 | -<TITLE>State Threads Library Programming Notes</TITLE> | ||
| 4 | -</HEAD> | ||
| 5 | -<BODY BGCOLOR=#FFFFFF> | ||
| 6 | -<H2>Programming Notes</H2> | ||
| 7 | -<P> | ||
| 8 | -<B> | ||
| 9 | -<UL> | ||
| 10 | -<LI><A HREF=#porting>Porting</A></LI> | ||
| 11 | -<LI><A HREF=#signals>Signals</A></LI> | ||
| 12 | -<LI><A HREF=#intra>Intra-Process Synchronization</A></LI> | ||
| 13 | -<LI><A HREF=#inter>Inter-Process Synchronization</A></LI> | ||
| 14 | -<LI><A HREF=#nonnet>Non-Network I/O</A></LI> | ||
| 15 | -<LI><A HREF=#timeouts>Timeouts</A></LI> | ||
| 16 | -</UL> | ||
| 17 | -</B> | ||
| 18 | -<P> | ||
| 19 | -<HR> | ||
| 20 | -<P> | ||
| 21 | -<A NAME="porting"> | ||
| 22 | -<H3>Porting</H3> | ||
| 23 | -The State Threads library uses OS concepts that are available in some | ||
| 24 | -form on most UNIX platforms, making the library very portable across | ||
| 25 | -many flavors of UNIX. However, there are several parts of the library | ||
| 26 | -that rely on platform-specific features. Here is the list of such parts: | ||
| 27 | -<P> | ||
| 28 | -<UL> | ||
| 29 | -<LI><I>Thread context initialization</I>: Two ingredients of the | ||
| 30 | -<TT>jmp_buf</TT> | ||
| 31 | -data structure (the program counter and the stack pointer) have to be | ||
| 32 | -manually set in the thread creation routine. The <TT>jmp_buf</TT> data | ||
| 33 | -structure is defined in the <TT>setjmp.h</TT> header file and differs from | ||
| 34 | -platform to platform. Usually the program counter is a structure member | ||
| 35 | -with <TT>PC</TT> in the name and the stack pointer is a structure member | ||
| 36 | -with <TT>SP</TT> in the name. One can also look in the | ||
| 37 | -<A HREF="http://www.mozilla.org/source.html">Netscape's NSPR library source</A> | ||
| 38 | -which already has this code for many UNIX-like platforms | ||
| 39 | -(<TT>mozilla/nsprpub/pr/include/md/*.h</TT> files). | ||
| 40 | -<P> | ||
| 41 | -Note that on some BSD-derived platforms <TT>_setjmp(3)/_longjmp(3)</TT> | ||
| 42 | -calls should be used instead of <TT>setjmp(3)/longjmp(3)</TT> (that is | ||
| 43 | -the calls that manipulate only the stack and registers and do <I>not</I> | ||
| 44 | -save and restore the process's signal mask).</LI> | ||
| 45 | -<P> | ||
| 46 | -Starting with glibc 2.4 on Linux the opacity of the <TT>jmp_buf</TT> data | ||
| 47 | -structure is enforced by <TT>setjmp(3)/longjmp(3)</TT> so the | ||
| 48 | -<TT>jmp_buf</TT> ingredients cannot be accessed directly anymore (unless | ||
| 49 | -special environmental variable LD_POINTER_GUARD is set before application | ||
| 50 | -execution). To avoid dependency on custom environment, the State Threads | ||
| 51 | -library provides <TT>setjmp/longjmp</TT> replacement functions for | ||
| 52 | -all Intel CPU architectures. Other CPU architectures can also be easily | ||
| 53 | -supported (the <TT>setjmp/longjmp</TT> source code is widely available for | ||
| 54 | -many CPU architectures). | ||
| 55 | -<P> | ||
| 56 | -<LI><I>High resolution time function</I>: Some platforms (IRIX, Solaris) | ||
| 57 | -provide a high resolution time function based on the free running hardware | ||
| 58 | -counter. This function returns the time counted since some arbitrary | ||
| 59 | -moment in the past (usually machine power up time). It is not correlated in | ||
| 60 | -any way to the time of day, and thus is not subject to resetting, | ||
| 61 | -drifting, etc. This type of time is ideal for tasks where cheap, accurate | ||
| 62 | -interval timing is required. If such a function is not available on a | ||
| 63 | -particular platform, the <TT>gettimeofday(3)</TT> function can be used | ||
| 64 | -(though on some platforms it involves a system call). | ||
| 65 | -<P> | ||
| 66 | -<LI><I>The stack growth direction</I>: The library needs to know whether the | ||
| 67 | -stack grows toward lower (down) or higher (up) memory addresses. | ||
| 68 | -One can write a simple test program that detects the stack growth direction | ||
| 69 | -on a particular platform.</LI> | ||
| 70 | -<P> | ||
| 71 | -<LI><I>Non-blocking attribute inheritance</I>: On some platforms (e.g. IRIX) | ||
| 72 | -the socket created as a result of the <TT>accept(2)</TT> call inherits the | ||
| 73 | -non-blocking attribute of the listening socket. One needs to consult the manual | ||
| 74 | -pages or write a simple test program to see if this applies to a specific | ||
| 75 | -platform.</LI> | ||
| 76 | -<P> | ||
| 77 | -<LI><I>Anonymous memory mapping</I>: The library allocates memory segments | ||
| 78 | -for thread stacks by doing anonymous memory mapping (<TT>mmap(2)</TT>). This | ||
| 79 | -mapping is somewhat different on SVR4 and BSD4.3 derived platforms. | ||
| 80 | -<P> | ||
| 81 | -The memory mapping can be avoided altogether by using <TT>malloc(3)</TT> for | ||
| 82 | -stack allocation. In this case the <TT>MALLOC_STACK</TT> macro should be | ||
| 83 | -defined.</LI> | ||
| 84 | -</UL> | ||
| 85 | -<P> | ||
| 86 | -All machine-dependent feature test macros should be defined in the | ||
| 87 | -<TT>md.h</TT> header file. The assembly code for <TT>setjmp/longjmp</TT> | ||
| 88 | -replacement functions for all CPU architectures should be placed in | ||
| 89 | -the <TT>md.S</TT> file. | ||
| 90 | -<P> | ||
| 91 | -The current version of the library is ported to: | ||
| 92 | -<UL> | ||
| 93 | - <LI>IRIX 6.x (both 32 and 64 bit)</LI> | ||
| 94 | - <LI>Linux (kernel 2.x and glibc 2.x) on x86, Alpha, MIPS and MIPSEL, | ||
| 95 | - SPARC, ARM, PowerPC, 68k, HPPA, S390, IA-64, and Opteron (AMD-64)</LI> | ||
| 96 | - <LI>Solaris 2.x (SunOS 5.x) on x86, AMD64, SPARC, and SPARC-64</LI> | ||
| 97 | - <LI>AIX 4.x</LI> | ||
| 98 | - <LI>HP-UX 11 (both 32 and 64 bit)</LI> | ||
| 99 | - <LI>Tru64/OSF1</LI> | ||
| 100 | - <LI>FreeBSD on x86, AMD64, and Alpha</LI> | ||
| 101 | - <LI>OpenBSD on x86, AMD64, Alpha, and SPARC</LI> | ||
| 102 | - <LI>NetBSD on x86, Alpha, SPARC, and VAX</LI> | ||
| 103 | - <LI>MacOS X (Darwin) on PowerPC (32 bit) and Intel (both 32 and 64 bit) [universal]</LI> | ||
| 104 | - <LI>Cygwin</LI> | ||
| 105 | -</UL> | ||
| 106 | -<P> | ||
| 107 | - | ||
| 108 | -<A NAME="signals"> | ||
| 109 | -<H3>Signals</H3> | ||
| 110 | -Signal handling in an application using State Threads should be treated the | ||
| 111 | -same way as in a classical UNIX process application. There is no such | ||
| 112 | -thing as per-thread signal mask, all threads share the same signal handlers, | ||
| 113 | -and only asynchronous-safe functions can be used in signal handlers. | ||
| 114 | -However, there is a way to process signals synchronously by converting a | ||
| 115 | -signal event to an I/O event: a signal catching function does a write to | ||
| 116 | -a pipe which will be processed synchronously by a dedicated signal handling | ||
| 117 | -thread. The following code demonstrates this technique (error handling is | ||
| 118 | -omitted for clarity): | ||
| 119 | -<PRE> | ||
| 120 | - | ||
| 121 | -/* Per-process pipe which is used as a signal queue. */ | ||
| 122 | -/* Up to PIPE_BUF/sizeof(int) signals can be queued up. */ | ||
| 123 | -int sig_pipe[2]; | ||
| 124 | - | ||
| 125 | -/* Signal catching function. */ | ||
| 126 | -/* Converts signal event to I/O event. */ | ||
| 127 | -void sig_catcher(int signo) | ||
| 128 | -{ | ||
| 129 | - int err; | ||
| 130 | - | ||
| 131 | - /* Save errno to restore it after the write() */ | ||
| 132 | - err = errno; | ||
| 133 | - /* write() is reentrant/async-safe */ | ||
| 134 | - write(sig_pipe[1], &signo, sizeof(int)); | ||
| 135 | - errno = err; | ||
| 136 | -} | ||
| 137 | - | ||
| 138 | -/* Signal processing function. */ | ||
| 139 | -/* This is the "main" function of the signal processing thread. */ | ||
| 140 | -void *sig_process(void *arg) | ||
| 141 | -{ | ||
| 142 | - st_netfd_t nfd; | ||
| 143 | - int signo; | ||
| 144 | - | ||
| 145 | - nfd = st_netfd_open(sig_pipe[0]); | ||
| 146 | - | ||
| 147 | - for ( ; ; ) { | ||
| 148 | - /* Read the next signal from the pipe */ | ||
| 149 | - st_read(nfd, &signo, sizeof(int), ST_UTIME_NO_TIMEOUT); | ||
| 150 | - | ||
| 151 | - /* Process signal synchronously */ | ||
| 152 | - switch (signo) { | ||
| 153 | - case SIGHUP: | ||
| 154 | - /* do something here - reread config files, etc. */ | ||
| 155 | - break; | ||
| 156 | - case SIGTERM: | ||
| 157 | - /* do something here - cleanup, etc. */ | ||
| 158 | - break; | ||
| 159 | - /* . | ||
| 160 | - . | ||
| 161 | - Other signals | ||
| 162 | - . | ||
| 163 | - . | ||
| 164 | - */ | ||
| 165 | - } | ||
| 166 | - } | ||
| 167 | - | ||
| 168 | - return NULL; | ||
| 169 | -} | ||
| 170 | - | ||
| 171 | -int main(int argc, char *argv[]) | ||
| 172 | -{ | ||
| 173 | - struct sigaction sa; | ||
| 174 | - . | ||
| 175 | - . | ||
| 176 | - . | ||
| 177 | - | ||
| 178 | - /* Create signal pipe */ | ||
| 179 | - pipe(sig_pipe); | ||
| 180 | - | ||
| 181 | - /* Create signal processing thread */ | ||
| 182 | - st_thread_create(sig_process, NULL, 0, 0); | ||
| 183 | - | ||
| 184 | - /* Install sig_catcher() as a signal handler */ | ||
| 185 | - sa.sa_handler = sig_catcher; | ||
| 186 | - sigemptyset(&sa.sa_mask); | ||
| 187 | - sa.sa_flags = 0; | ||
| 188 | - sigaction(SIGHUP, &sa, NULL); | ||
| 189 | - | ||
| 190 | - sa.sa_handler = sig_catcher; | ||
| 191 | - sigemptyset(&sa.sa_mask); | ||
| 192 | - sa.sa_flags = 0; | ||
| 193 | - sigaction(SIGTERM, &sa, NULL); | ||
| 194 | - | ||
| 195 | - . | ||
| 196 | - . | ||
| 197 | - . | ||
| 198 | - | ||
| 199 | -} | ||
| 200 | - | ||
| 201 | -</PRE> | ||
| 202 | -<P> | ||
| 203 | -Note that if multiple processes are used (see below), the signal pipe should | ||
| 204 | -be initialized after the <TT>fork(2)</TT> call so that each process has its | ||
| 205 | -own private pipe. | ||
| 206 | -<P> | ||
| 207 | - | ||
| 208 | -<A NAME="intra"> | ||
| 209 | -<H3>Intra-Process Synchronization</H3> | ||
| 210 | -Due to the event-driven nature of the library scheduler, the thread context | ||
| 211 | -switch (process state change) can only happen in a well-known set of | ||
| 212 | -library functions. This set includes functions in which a thread may | ||
| 213 | -"block":<TT> </TT>I/O functions (<TT>st_read(), st_write(), </TT>etc.), | ||
| 214 | -sleep functions (<TT>st_sleep(), </TT>etc.), and thread synchronization | ||
| 215 | -functions (<TT>st_thread_join(), st_cond_wait(), </TT>etc.). As a result, | ||
| 216 | -process-specific global data need not to be protected by locks since a thread | ||
| 217 | -cannot be rescheduled while in a critical section (and only one thread at a | ||
| 218 | -time can access the same memory location). By the same token, | ||
| 219 | -non thread-safe functions (in a traditional sense) can be safely used with | ||
| 220 | -the State Threads. The library's mutex facilities are practically useless | ||
| 221 | -for a correctly written application (no blocking functions in critical | ||
| 222 | -section) and are provided mostly for completeness. This absence of locking | ||
| 223 | -greatly simplifies an application design and provides a foundation for | ||
| 224 | -scalability. | ||
| 225 | -<P> | ||
| 226 | - | ||
| 227 | -<A NAME="inter"> | ||
| 228 | -<H3>Inter-Process Synchronization</H3> | ||
| 229 | -The State Threads library makes it possible to multiplex a large number | ||
| 230 | -of simultaneous connections onto a much smaller number of separate | ||
| 231 | -processes, where each process uses a many-to-one user-level threading | ||
| 232 | -implementation (<B>N</B> of <B>M:1</B> mappings rather than one <B>M:N</B> | ||
| 233 | -mapping used in native threading libraries on some platforms). This design | ||
| 234 | -is key to the application's scalability. One can think about it as if a | ||
| 235 | -set of all threads is partitioned into separate groups (processes) where | ||
| 236 | -each group has a separate pool of resources (virtual address space, file | ||
| 237 | -descriptors, etc.). An application designer has full control of how many | ||
| 238 | -groups (processes) an application creates and what resources, if any, | ||
| 239 | -are shared among different groups via standard UNIX inter-process | ||
| 240 | -communication (IPC) facilities.<P> | ||
| 241 | -There are several reasons for creating multiple processes: | ||
| 242 | -<P> | ||
| 243 | -<UL> | ||
| 244 | -<LI>To take advantage of multiple hardware entities (CPUs, disks, etc.) | ||
| 245 | -available in the system (hardware parallelism).</LI> | ||
| 246 | -<P> | ||
| 247 | -<LI>To reduce risk of losing a large number of user connections when one of | ||
| 248 | -the processes crashes. For example, if <B>C</B> user connections (threads) | ||
| 249 | -are multiplexed onto <B>P</B> processes and one of the processes crashes, | ||
| 250 | -only a fraction (<B>C/P</B>) of all connections will be lost.</LI> | ||
| 251 | -<P> | ||
| 252 | -<LI>To overcome per-process resource limitations imposed by the OS. For | ||
| 253 | -example, if <TT>select(2)</TT> is used for event polling, the number of | ||
| 254 | -simultaneous connections (threads) per process is | ||
| 255 | -limited by the <TT>FD_SETSIZE</TT> parameter (see <TT>select(2)</TT>). | ||
| 256 | -If <TT>FD_SETSIZE</TT> is equal to 1024 and each connection needs one file | ||
| 257 | -descriptor, then an application should create 10 processes to support 10,000 | ||
| 258 | -simultaneous connections.</LI> | ||
| 259 | -</UL> | ||
| 260 | -<P> | ||
| 261 | -Ideally all user sessions are completely independent, so there is no need for | ||
| 262 | -inter-process communication. It is always better to have several separate | ||
| 263 | -smaller process-specific resources (e.g., data caches) than to have one large | ||
| 264 | -resource shared (and modified) by all processes. Sometimes, however, there | ||
| 265 | -is a need to share a common resource among different processes. In that case, | ||
| 266 | -standard UNIX IPC facilities can be used. In addition to that, there is a way | ||
| 267 | -to synchronize different processes so that only the thread accessing the | ||
| 268 | -shared resource will be suspended (but not the entire process) if that resource | ||
| 269 | -is unavailable. In the following code fragment a pipe is used as a counting | ||
| 270 | -semaphore for inter-process synchronization: | ||
| 271 | -<PRE> | ||
| 272 | -#ifndef PIPE_BUF | ||
| 273 | -#define PIPE_BUF 512 /* POSIX */ | ||
| 274 | -#endif | ||
| 275 | - | ||
| 276 | -/* Semaphore data structure */ | ||
| 277 | -typedef struct ipc_sem { | ||
| 278 | - st_netfd_t rdfd; /* read descriptor */ | ||
| 279 | - st_netfd_t wrfd; /* write descriptor */ | ||
| 280 | -} ipc_sem_t; | ||
| 281 | - | ||
| 282 | -/* Create and initialize the semaphore. Should be called before fork(2). */ | ||
| 283 | -/* 'value' must be less than PIPE_BUF. */ | ||
| 284 | -/* If 'value' is 1, the semaphore works as mutex. */ | ||
| 285 | -ipc_sem_t *ipc_sem_create(int value) | ||
| 286 | -{ | ||
| 287 | - ipc_sem_t *sem; | ||
| 288 | - int p[2]; | ||
| 289 | - char b[PIPE_BUF]; | ||
| 290 | - | ||
| 291 | - /* Error checking is omitted for clarity */ | ||
| 292 | - sem = malloc(sizeof(ipc_sem_t)); | ||
| 293 | - | ||
| 294 | - /* Create the pipe */ | ||
| 295 | - pipe(p); | ||
| 296 | - sem->rdfd = st_netfd_open(p[0]); | ||
| 297 | - sem->wrfd = st_netfd_open(p[1]); | ||
| 298 | - | ||
| 299 | - /* Initialize the semaphore: put 'value' bytes into the pipe */ | ||
| 300 | - write(p[1], b, value); | ||
| 301 | - | ||
| 302 | - return sem; | ||
| 303 | -} | ||
| 304 | - | ||
| 305 | -/* Try to decrement the "value" of the semaphore. */ | ||
| 306 | -/* If "value" is 0, the calling thread blocks on the semaphore. */ | ||
| 307 | -int ipc_sem_wait(ipc_sem_t *sem) | ||
| 308 | -{ | ||
| 309 | - char c; | ||
| 310 | - | ||
| 311 | - /* Read one byte from the pipe */ | ||
| 312 | - if (st_read(sem->rdfd, &c, 1, ST_UTIME_NO_TIMEOUT) != 1) | ||
| 313 | - return -1; | ||
| 314 | - | ||
| 315 | - return 0; | ||
| 316 | -} | ||
| 317 | - | ||
| 318 | -/* Increment the "value" of the semaphore. */ | ||
| 319 | -int ipc_sem_post(ipc_sem_t *sem) | ||
| 320 | -{ | ||
| 321 | - char c; | ||
| 322 | - | ||
| 323 | - if (st_write(sem->wrfd, &c, 1, ST_UTIME_NO_TIMEOUT) != 1) | ||
| 324 | - return -1; | ||
| 325 | - | ||
| 326 | - return 0; | ||
| 327 | -} | ||
| 328 | - | ||
| 329 | -</PRE> | ||
| 330 | -<P> | ||
| 331 | - | ||
| 332 | -Generally, the following steps should be followed when writing an application | ||
| 333 | -using the State Threads library: | ||
| 334 | -<P> | ||
| 335 | -<OL> | ||
| 336 | -<LI>Initialize the library (<TT>st_init()</TT>).</LI> | ||
| 337 | -<P> | ||
| 338 | -<LI>Create resources that will be shared among different processes: | ||
| 339 | - create and bind listening sockets, create shared memory segments, IPC | ||
| 340 | - channels, synchronization primitives, etc.</LI> | ||
| 341 | -<P> | ||
| 342 | -<LI>Create several processes (<TT>fork(2)</TT>). The parent process should | ||
| 343 | - either exit or become a "watchdog" (e.g., it starts a new process when | ||
| 344 | - an existing one crashes, does a cleanup upon application termination, | ||
| 345 | - etc.).</LI> | ||
| 346 | -<P> | ||
| 347 | -<LI>In each child process create a pool of threads | ||
| 348 | - (<TT>st_thread_create()</TT>) to handle user connections.</LI> | ||
| 349 | -</OL> | ||
| 350 | -<P> | ||
| 351 | - | ||
| 352 | -<A NAME="nonnet"> | ||
| 353 | -<H3>Non-Network I/O</H3> | ||
| 354 | - | ||
| 355 | -The State Threads architecture uses non-blocking I/O on | ||
| 356 | -<TT>st_netfd_t</TT> objects for concurrent processing of multiple user | ||
| 357 | -connections. This architecture has a drawback: the entire process and | ||
| 358 | -all its threads may block for the duration of a <I>disk</I> or other | ||
| 359 | -non-network I/O operation, whether through State Threads I/O functions, | ||
| 360 | -direct system calls, or standard I/O functions. (This is applicable | ||
| 361 | -mostly to disk <I>reads</I>; disk <I>writes</I> are usually performed | ||
| 362 | -asynchronously -- data goes to the buffer cache to be written to disk | ||
| 363 | -later.) Fortunately, disk I/O (unlike network I/O) usually takes a | ||
| 364 | -finite and predictable amount of time, but this may not be true for | ||
| 365 | -special devices or user input devices (including stdin). Nevertheless, | ||
| 366 | -such I/O reduces throughput of the system and increases response times. | ||
| 367 | -There are several ways to design an application to overcome this | ||
| 368 | -drawback: | ||
| 369 | - | ||
| 370 | -<P> | ||
| 371 | -<UL> | ||
| 372 | -<LI>Create several identical main processes as described above (symmetric | ||
| 373 | - architecture). This will improve CPU utilization and thus improve the | ||
| 374 | - overall throughput of the system.</LI> | ||
| 375 | -<P> | ||
| 376 | -<LI>Create multiple "helper" processes in addition to the main process that | ||
| 377 | - will handle blocking I/O operations (asymmetric architecture). | ||
| 378 | - This approach was suggested for Web servers in a | ||
| 379 | - <A HREF="http://www.cs.rice.edu/~vivek/flash99/">paper</A> by Peter | ||
| 380 | - Druschel et al. In this architecture the main process communicates with | ||
| 381 | - a helper process via an IPC channel (<TT>pipe(2), socketpair(2)</TT>). | ||
| 382 | - The main process instructs a helper to perform the potentially blocking | ||
| 383 | - operation. Once the operation completes, the helper returns a | ||
| 384 | - notification via IPC. | ||
| 385 | -</UL> | ||
| 386 | -<P> | ||
| 387 | - | ||
| 388 | -<A NAME="timeouts"> | ||
| 389 | -<H3>Timeouts</H3> | ||
| 390 | - | ||
| 391 | -The <TT>timeout</TT> parameter to <TT>st_cond_timedwait()</TT> and the | ||
| 392 | -I/O functions, and the arguments to <TT>st_sleep()</TT> and | ||
| 393 | -<TT>st_usleep()</TT> specify a maximum time to wait <I>since the last | ||
| 394 | -context switch</I> not since the beginning of the function call. | ||
| 395 | - | ||
| 396 | -<P>The State Threads' time resolution is actually the time interval | ||
| 397 | -between context switches. That time interval may be large in some | ||
| 398 | -situations, for example, when a single thread does a lot of work | ||
| 399 | -continuously. Note that a steady, uninterrupted stream of network I/O | ||
| 400 | -qualifies for this description; a context switch occurs only when a | ||
| 401 | -thread blocks. | ||
| 402 | - | ||
| 403 | -<P>If a specified I/O timeout is less than the time interval between | ||
| 404 | -context switches the function may return with a timeout error before | ||
| 405 | -that amount of time has elapsed since the beginning of the function | ||
| 406 | -call. For example, if eight milliseconds have passed since the last | ||
| 407 | -context switch and an I/O function with a timeout of 10 milliseconds | ||
| 408 | -blocks, causing a switch, the call may return with a timeout error as | ||
| 409 | -little as two milliseconds after it was called. (On Linux, | ||
| 410 | -<TT>select()</TT>'s timeout is an <I>upper</I> bound on the amount of | ||
| 411 | -time elapsed before select returns.) Similarly, if 12 ms have passed | ||
| 412 | -already, the function may return immediately. | ||
| 413 | - | ||
| 414 | -<P>In almost all cases I/O timeouts should be used only for detecting a | ||
| 415 | -broken network connection or for preventing a peer from holding an idle | ||
| 416 | -connection for too long. Therefore for most applications realistic I/O | ||
| 417 | -timeouts should be on the order of seconds. Furthermore, there's | ||
| 418 | -probably no point in retrying operations that time out. Rather than | ||
| 419 | -retrying simply use a larger timeout in the first place. | ||
| 420 | - | ||
| 421 | -<P>The largest valid timeout value is platform-dependent and may be | ||
| 422 | -significantly less than <TT>INT_MAX</TT> seconds for <TT>select()</TT> | ||
| 423 | -or <TT>INT_MAX</TT> milliseconds for <TT>poll()</TT>. Generally, you | ||
| 424 | -should not use timeouts exceeding several hours. Use | ||
| 425 | -<tt>ST_UTIME_NO_TIMEOUT</tt> (<tt>-1</tt>) as a special value to | ||
| 426 | -indicate infinite timeout or indefinite sleep. Use | ||
| 427 | -<tt>ST_UTIME_NO_WAIT</tt> (<tt>0</tt>) to indicate no waiting at all. | ||
| 428 | - | ||
| 429 | -<P> | ||
| 430 | -<HR> | ||
| 431 | -<P> | ||
| 432 | -</BODY> | ||
| 433 | -</HTML> | ||
| 434 | - |
此 diff 太大无法显示。
trunk/research/st-1.9/docs/st.html
已删除
100644 → 0
| 1 | -<HTML> | ||
| 2 | -<HEAD> | ||
| 3 | -<TITLE>State Threads for Internet Applications</TITLE> | ||
| 4 | -</HEAD> | ||
| 5 | -<BODY BGCOLOR=#FFFFFF> | ||
| 6 | -<H2>State Threads for Internet Applications</H2> | ||
| 7 | -<H3>Introduction</H3> | ||
| 8 | -<P> | ||
| 9 | -State Threads is an application library which provides a | ||
| 10 | -foundation for writing fast and highly scalable Internet Applications | ||
| 11 | -on UNIX-like platforms. It combines the simplicity of the multithreaded | ||
| 12 | -programming paradigm, in which one thread supports each simultaneous | ||
| 13 | -connection, with the performance and scalability of an event-driven | ||
| 14 | -state machine architecture.</P> | ||
| 15 | - | ||
| 16 | -<H3>1. Definitions</H3> | ||
| 17 | -<P> | ||
| 18 | -<A NAME="IA"> | ||
| 19 | -<H4>1.1 Internet Applications</H4> | ||
| 20 | -</A> | ||
| 21 | -<P> | ||
| 22 | -An <I>Internet Application</I> (IA) is either a server or client network | ||
| 23 | -application that accepts connections from clients and may or may not | ||
| 24 | -connect to servers. In an IA the arrival or departure of network data | ||
| 25 | -often controls processing (that is, IA is a <I>data-driven</I> application). | ||
| 26 | -For each connection, an IA does some finite amount of work | ||
| 27 | -involving data exchange with its peer, where its peer may be either | ||
| 28 | -a client or a server. | ||
| 29 | -The typical transaction steps of an IA are to accept a connection, | ||
| 30 | -read a request, do some finite and predictable amount of work to | ||
| 31 | -process the request, then write a response to the peer that sent the | ||
| 32 | -request. One example of an IA is a Web server; | ||
| 33 | -the most general example of an IA is a proxy server, because it both | ||
| 34 | -accepts connections from clients and connects to other servers.</P> | ||
| 35 | -<P> | ||
| 36 | -We assume that the performance of an IA is constrained by available CPU | ||
| 37 | -cycles rather than network bandwidth or disk I/O (that is, CPU | ||
| 38 | -is a bottleneck resource). | ||
| 39 | -<P> | ||
| 40 | - | ||
| 41 | -<A NAME="PS"> | ||
| 42 | -<H4>1.2 Performance and Scalability</H4> | ||
| 43 | -</A> | ||
| 44 | -<P> | ||
| 45 | -The <I>performance</I> of an IA is usually evaluated as its | ||
| 46 | -throughput measured in transactions per second or bytes per second (one | ||
| 47 | -can be converted to the other, given the average transaction size). There are | ||
| 48 | -several benchmarks that can be used to measure throughput of Web serving | ||
| 49 | -applications for specific workloads (such as | ||
| 50 | -<A HREF="http://www.spec.org/osg/web96/">SPECweb96</A>, | ||
| 51 | -<A HREF="http://www.mindcraft.com/webstone/">WebStone</A>, | ||
| 52 | -<A HREF="http://www.zdnet.com/zdbop/webbench/">WebBench</A>). | ||
| 53 | -Although there is no common definition for <I>scalability</I>, in general it | ||
| 54 | -expresses the ability of an application to sustain its performance when some | ||
| 55 | -external condition changes. For IAs this external condition is either the | ||
| 56 | -number of clients (also known as "users," "simultaneous connections," or "load | ||
| 57 | -generators") or the underlying hardware system size (number of CPUs, memory | ||
| 58 | -size, and so on). Thus there are two types of scalability: <I>load | ||
| 59 | -scalability</I> and <I>system scalability</I>, respectively. | ||
| 60 | -<P> | ||
| 61 | -The figure below shows how the throughput of an idealized IA changes with | ||
| 62 | -the increasing number of clients (solid blue line). Initially the throughput | ||
| 63 | -grows linearly (the slope represents the maximal throughput that one client | ||
| 64 | -can provide). Within this initial range, the IA is underutilized and CPUs are | ||
| 65 | -partially idle. Further increase in the number of clients leads to a system | ||
| 66 | -saturation, and the throughput gradually stops growing as all CPUs become fully | ||
| 67 | -utilized. After that point, the throughput stays flat because there are no | ||
| 68 | -more CPU cycles available. | ||
| 69 | -In the real world, however, each simultaneous connection | ||
| 70 | -consumes some computational and memory resources, even when idle, and this | ||
| 71 | -overhead grows with the number of clients. Therefore, the throughput of the | ||
| 72 | -real world IA starts dropping after some point (dashed blue line in the figure | ||
| 73 | -below). The rate at which the throughput drops depends, among other things, on | ||
| 74 | -application design. | ||
| 75 | -<P> | ||
| 76 | -We say that an application has a good <I>load scalability</I> if it can | ||
| 77 | -sustain its throughput over a wide range of loads. | ||
| 78 | -Interestingly, the <A HREF="http://www.spec.org/osg/web99/">SPECweb99</A> | ||
| 79 | -benchmark somewhat reflects the Web server's load scalability because it | ||
| 80 | -measures the number of clients (load generators) given a mandatory minimal | ||
| 81 | -throughput per client (that is, it measures the server's <I>capacity</I>). | ||
| 82 | -This is unlike <A HREF="http://www.spec.org/osg/web96/">SPECweb96</A> and | ||
| 83 | -other benchmarks that use the throughput as their main metric (see the figure | ||
| 84 | -below). | ||
| 85 | -<P> | ||
| 86 | -<CENTER><IMG SRC="fig.gif" ALT="Figure: Throughput vs. Number of clients"> | ||
| 87 | -</CENTER> | ||
| 88 | -<P> | ||
| 89 | -<I>System scalability</I> is the ability of an application to sustain its | ||
| 90 | -performance per hardware unit (such as a CPU) with the increasing number of | ||
| 91 | -these units. In other words, good system scalability means that doubling the | ||
| 92 | -number of processors will roughly double the application's throughput (dashed | ||
| 93 | -green line). We assume here that the underlying operating system also scales | ||
| 94 | -well. Good system scalability allows you to initially run an application on | ||
| 95 | -the smallest system possible, while retaining the ability to move that | ||
| 96 | -application to a larger system if necessary, without excessive effort or | ||
| 97 | -expense. That is, an application need not be rewritten or even undergo a | ||
| 98 | -major porting effort when changing system size. | ||
| 99 | -<P> | ||
| 100 | -Although scalability and performance are more important in the case of server | ||
| 101 | -IAs, they should also be considered for some client applications (such as | ||
| 102 | -benchmark load generators). | ||
| 103 | -<P> | ||
| 104 | - | ||
| 105 | -<A NAME="CONC"> | ||
| 106 | -<H4>1.3 Concurrency</H4> | ||
| 107 | -</A> | ||
| 108 | -<P> | ||
| 109 | -Concurrency reflects the parallelism in a system. The two unrelated types | ||
| 110 | -are <I>virtual</I> concurrency and <I>real</I> concurrency. | ||
| 111 | -<UL> | ||
| 112 | -<LI>Virtual (or apparent) concurrency is the number of simultaneous | ||
| 113 | -connections that a system supports. | ||
| 114 | -<BR><BR> | ||
| 115 | -<LI>Real concurrency is the number of hardware devices, including | ||
| 116 | -CPUs, network cards, and disks, that actually allow a system to perform | ||
| 117 | -tasks in parallel. | ||
| 118 | -</UL> | ||
| 119 | -<P> | ||
| 120 | -An IA must provide virtual concurrency in order to serve many users | ||
| 121 | -simultaneously. | ||
| 122 | -To achieve maximum performance and scalability in doing so, the number of | ||
| 123 | -programming entities than an IA creates to be scheduled by the OS kernel | ||
| 124 | -should be | ||
| 125 | -kept close to (within an order of magnitude of) the real concurrency found on | ||
| 126 | -the system. These programming entities scheduled by the kernel are known as | ||
| 127 | -<I>kernel execution vehicles</I>. Examples of kernel execution vehicles | ||
| 128 | -include Solaris lightweight processes and IRIX kernel threads. | ||
| 129 | -In other words, the number of kernel execution vehicles should be dictated by | ||
| 130 | -the system size and not by the number of simultaneous connections. | ||
| 131 | -<P> | ||
| 132 | - | ||
| 133 | -<H3>2. Existing Architectures</H3> | ||
| 134 | -<P> | ||
| 135 | -There are a few different architectures that are commonly used by IAs. | ||
| 136 | -These include the <I>Multi-Process</I>, | ||
| 137 | -<I>Multi-Threaded</I>, and <I>Event-Driven State Machine</I> | ||
| 138 | -architectures. | ||
| 139 | -<P> | ||
| 140 | -<A NAME="MP"> | ||
| 141 | -<H4>2.1 Multi-Process Architecture</H4> | ||
| 142 | -</A> | ||
| 143 | -<P> | ||
| 144 | -In the Multi-Process (MP) architecture, an individual process is | ||
| 145 | -dedicated to each simultaneous connection. | ||
| 146 | -A process performs all of a transaction's initialization steps | ||
| 147 | -and services a connection completely before moving on to service | ||
| 148 | -a new connection. | ||
| 149 | -<P> | ||
| 150 | -User sessions in IAs are relatively independent; therefore, no | ||
| 151 | -synchronization between processes handling different connections is | ||
| 152 | -necessary. Because each process has its own private address space, | ||
| 153 | -this architecture is very robust. If a process serving one of the connections | ||
| 154 | -crashes, the other sessions will not be affected. However, to serve many | ||
| 155 | -concurrent connections, an equal number of processes must be employed. | ||
| 156 | -Because processes are kernel entities (and are in fact the heaviest ones), | ||
| 157 | -the number of kernel entities will be at least as large as the number of | ||
| 158 | -concurrent sessions. On most systems, good performance will not be achieved | ||
| 159 | -when more than a few hundred processes are created because of the high | ||
| 160 | -context-switching overhead. In other words, MP applications have poor load | ||
| 161 | -scalability. | ||
| 162 | -<P> | ||
| 163 | -On the other hand, MP applications have very good system scalability, because | ||
| 164 | -no resources are shared among different processes and there is no | ||
| 165 | -synchronization overhead. | ||
| 166 | -<P> | ||
| 167 | -The Apache Web Server 1.x (<A HREF=#refs1>[Reference 1]</A>) uses the MP | ||
| 168 | -architecture on UNIX systems. | ||
| 169 | -<P> | ||
| 170 | -<A NAME="MT"> | ||
| 171 | -<H4>2.2 Multi-Threaded Architecture</H4> | ||
| 172 | -</A> | ||
| 173 | -<P> | ||
| 174 | -In the Multi-Threaded (MT) architecture, multiple independent threads | ||
| 175 | -of control are employed within a single shared address space. Like a | ||
| 176 | -process in the MP architecture, each thread performs all of a | ||
| 177 | -transaction's initialization steps and services a connection completely | ||
| 178 | -before moving on to service a new connection. | ||
| 179 | -<P> | ||
| 180 | -Many modern UNIX operating systems implement a <I>many-to-few</I> model when | ||
| 181 | -mapping user-level threads to kernel entities. In this model, an | ||
| 182 | -arbitrarily large number of user-level threads is multiplexed onto a | ||
| 183 | -lesser number of kernel execution vehicles. Kernel execution | ||
| 184 | -vehicles are also known as <I>virtual processors</I>. Whenever a user-level | ||
| 185 | -thread makes a blocking system call, the kernel execution vehicle it is using | ||
| 186 | -will become blocked in the kernel. If there are no other non-blocked kernel | ||
| 187 | -execution vehicles and there are other runnable user-level threads, a new | ||
| 188 | -kernel execution vehicle will be created automatically. This prevents the | ||
| 189 | -application from blocking when it can continue to make useful forward | ||
| 190 | -progress. | ||
| 191 | -<P> | ||
| 192 | -Because IAs are by nature network I/O driven, all concurrent sessions block on | ||
| 193 | -network I/O at various points. As a result, the number of virtual processors | ||
| 194 | -created in the kernel grows close to the number of user-level threads | ||
| 195 | -(or simultaneous connections). When this occurs, the many-to-few model | ||
| 196 | -effectively degenerates to a <I>one-to-one</I> model. Again, like in | ||
| 197 | -the MP architecture, the number of kernel execution vehicles is dictated by | ||
| 198 | -the number of simultaneous connections rather than by number of CPUs. This | ||
| 199 | -reduces an application's load scalability. However, because kernel threads | ||
| 200 | -(lightweight processes) use fewer resources and are more light-weight than | ||
| 201 | -traditional UNIX processes, an MT application should scale better with load | ||
| 202 | -than an MP application. | ||
| 203 | -<P> | ||
| 204 | -Unexpectedly, the small number of virtual processors sharing the same address | ||
| 205 | -space in the MT architecture destroys an application's system scalability | ||
| 206 | -because of contention among the threads on various locks. Even if an | ||
| 207 | -application itself is carefully | ||
| 208 | -optimized to avoid lock contention around its own global data (a non-trivial | ||
| 209 | -task), there are still standard library functions and system calls | ||
| 210 | -that use common resources hidden from the application. For example, | ||
| 211 | -on many platforms thread safety of memory allocation routines | ||
| 212 | -(<TT>malloc(3)</TT>, <TT>free(3)</TT>, and so on) is achieved by using a single | ||
| 213 | -global lock. Another example is a per-process file descriptor table. | ||
| 214 | -This common resource table is shared by all kernel execution vehicles within | ||
| 215 | -the same process and must be protected when one modifies it via | ||
| 216 | -certain system calls (such as <TT>open(2)</TT>, <TT>close(2)</TT>, and so on). | ||
| 217 | -In addition to that, maintaining the caches coherent | ||
| 218 | -among CPUs on multiprocessor systems hurts performance when different threads | ||
| 219 | -running on different CPUs modify data items on the same cache line. | ||
| 220 | -<P> | ||
| 221 | -In order to improve load scalability, some applications employ a different | ||
| 222 | -type of MT architecture: they create one or more thread(s) <I>per task</I> | ||
| 223 | -rather than one thread <I>per connection</I>. For example, one small group | ||
| 224 | -of threads may be responsible for accepting client connections, another | ||
| 225 | -for request processing, and yet another for serving responses. The main | ||
| 226 | -advantage of this architecture is that it eliminates the tight coupling | ||
| 227 | -between the number of threads and number of simultaneous connections. However, | ||
| 228 | -in this architecture, different task-specific thread groups must share common | ||
| 229 | -work queues that must be protected by mutual exclusion locks (a typical | ||
| 230 | -producer-consumer problem). This adds synchronization overhead that causes an | ||
| 231 | -application to perform badly on multiprocessor systems. In other words, in | ||
| 232 | -this architecture, the application's system scalability is sacrificed for the | ||
| 233 | -sake of load scalability. | ||
| 234 | -<P> | ||
| 235 | -Of course, the usual nightmares of threaded programming, including data | ||
| 236 | -corruption, deadlocks, and race conditions, also make MT architecture (in any | ||
| 237 | -form) non-simplistic to use. | ||
| 238 | -<P> | ||
| 239 | - | ||
| 240 | -<A NAME="EDSM"> | ||
| 241 | -<H4>2.3 Event-Driven State Machine Architecture</H4> | ||
| 242 | -</A> | ||
| 243 | -<P> | ||
| 244 | -In the Event-Driven State Machine (EDSM) architecture, a single process | ||
| 245 | -is employed to concurrently process multiple connections. The basics of this | ||
| 246 | -architecture are described in Comer and Stevens | ||
| 247 | -<A HREF=#refs2>[Reference 2]</A>. | ||
| 248 | -The EDSM architecture performs one basic data-driven step associated with | ||
| 249 | -a particular connection at a time, thus multiplexing many concurrent | ||
| 250 | -connections. The process operates as a state machine that receives an event | ||
| 251 | -and then reacts to it. | ||
| 252 | -<P> | ||
| 253 | -In the idle state the EDSM calls <TT>select(2)</TT> or <TT>poll(2)</TT> to | ||
| 254 | -wait for network I/O events. When a particular file descriptor is ready for | ||
| 255 | -I/O, the EDSM completes the corresponding basic step (usually by invoking a | ||
| 256 | -handler function) and starts the next one. This architecture uses | ||
| 257 | -non-blocking system calls to perform asynchronous network I/O operations. | ||
| 258 | -For more details on non-blocking I/O see Stevens | ||
| 259 | -<A HREF=#refs3>[Reference 3]</A>. | ||
| 260 | -<P> | ||
| 261 | -To take advantage of hardware parallelism (real concurrency), multiple | ||
| 262 | -identical processes may be created. This is called Symmetric Multi-Process | ||
| 263 | -EDSM and is used, for example, in the Zeus Web Server | ||
| 264 | -(<A HREF=#refs4>[Reference 4]</A>). To more efficiently multiplex disk I/O, | ||
| 265 | -special "helper" processes may be created. This is called Asymmetric | ||
| 266 | -Multi-Process EDSM and was proposed for Web servers by Druschel | ||
| 267 | -and others <A HREF=#refs5>[Reference 5]</A>. | ||
| 268 | -<P> | ||
| 269 | -EDSM is probably the most scalable architecture for IAs. | ||
| 270 | -Because the number of simultaneous connections (virtual concurrency) is | ||
| 271 | -completely decoupled from the number of kernel execution vehicles (processes), | ||
| 272 | -this architecture has very good load scalability. It requires only minimal | ||
| 273 | -user-level resources to create and maintain additional connection. | ||
| 274 | -<P> | ||
| 275 | -Like MP applications, Multi-Process EDSM has very good system scalability | ||
| 276 | -because no resources are shared among different processes and there is no | ||
| 277 | -synchronization overhead. | ||
| 278 | -<P> | ||
| 279 | -Unfortunately, the EDSM architecture is monolithic rather than based on the | ||
| 280 | -concept of threads, so new applications generally need to be implemented from | ||
| 281 | -the ground up. In effect, the EDSM architecture simulates threads and their | ||
| 282 | -stacks the hard way. | ||
| 283 | -<P> | ||
| 284 | - | ||
| 285 | -<A NAME="ST"> | ||
| 286 | -<H3>3. State Threads Library</H3> | ||
| 287 | -</A> | ||
| 288 | -<P> | ||
| 289 | -The State Threads library combines the advantages of all of the above | ||
| 290 | -architectures. The interface preserves the programming simplicity of thread | ||
| 291 | -abstraction, allowing each simultaneous connection to be treated as a separate | ||
| 292 | -thread of execution within a single process. The underlying implementation is | ||
| 293 | -close to the EDSM architecture as the state of each particular concurrent | ||
| 294 | -session is saved in a separate memory segment. | ||
| 295 | -<P> | ||
| 296 | - | ||
| 297 | -<H4>3.1 State Changes and Scheduling</H4> | ||
| 298 | -<P> | ||
| 299 | -The state of each concurrent session includes its stack environment | ||
| 300 | -(stack pointer, program counter, CPU registers) and its stack. Conceptually, | ||
| 301 | -a thread context switch can be viewed as a process changing its state. There | ||
| 302 | -are no kernel entities involved other than processes. | ||
| 303 | -Unlike other general-purpose threading libraries, the State Threads library | ||
| 304 | -is fully deterministic. The thread context switch (process state change) can | ||
| 305 | -only happen in a well-known set of functions (at I/O points or at explicit | ||
| 306 | -synchronization points). As a result, process-specific global data does not | ||
| 307 | -have to be protected by mutual exclusion locks in most cases. The entire | ||
| 308 | -application is free to use all the static variables and non-reentrant library | ||
| 309 | -functions it wants, greatly simplifying programming and debugging while | ||
| 310 | -increasing performance. This is somewhat similar to a <I>co-routine</I> model | ||
| 311 | -(co-operatively multitasked threads), except that no explicit yield is needed | ||
| 312 | --- | ||
| 313 | -sooner or later, a thread performs a blocking I/O operation and thus surrenders | ||
| 314 | -control. All threads of execution (simultaneous connections) have the | ||
| 315 | -same priority, so scheduling is non-preemptive, like in the EDSM architecture. | ||
| 316 | -Because IAs are data-driven (processing is limited by the size of network | ||
| 317 | -buffers and data arrival rates), scheduling is non-time-slicing. | ||
| 318 | -<P> | ||
| 319 | -Only two types of external events are handled by the library's | ||
| 320 | -scheduler, because only these events can be detected by | ||
| 321 | -<TT>select(2)</TT> or <TT>poll(2)</TT>: I/O events (a file descriptor is ready | ||
| 322 | -for I/O) and time events | ||
| 323 | -(some timeout has expired). However, other types of events (such as | ||
| 324 | -a signal sent to a process) can also be handled by converting them to I/O | ||
| 325 | -events. For example, a signal handling function can perform a write to a pipe | ||
| 326 | -(<TT>write(2)</TT> is reentrant/asynchronous-safe), thus converting a signal | ||
| 327 | -event to an I/O event. | ||
| 328 | -<P> | ||
| 329 | -To take advantage of hardware parallelism, as in the EDSM architecture, | ||
| 330 | -multiple processes can be created in either a symmetric or asymmetric manner. | ||
| 331 | -Process management is not in the library's scope but instead is left up to the | ||
| 332 | -application. | ||
| 333 | -<P> | ||
| 334 | -There are several general-purpose threading libraries that implement a | ||
| 335 | -<I>many-to-one</I> model (many user-level threads to one kernel execution | ||
| 336 | -vehicle), using the same basic techniques as the State Threads library | ||
| 337 | -(non-blocking I/O, event-driven scheduler, and so on). For an example, see GNU | ||
| 338 | -Portable Threads (<A HREF=#refs6>[Reference 6]</A>). Because they are | ||
| 339 | -general-purpose, these libraries have different objectives than the State | ||
| 340 | -Threads library. The State Threads library is <I>not</I> a general-purpose | ||
| 341 | -threading library, | ||
| 342 | -but rather an application library that targets only certain types of | ||
| 343 | -applications (IAs) in order to achieve the highest possible performance and | ||
| 344 | -scalability for those applications. | ||
| 345 | -<P> | ||
| 346 | - | ||
| 347 | -<H4>3.2 Scalability</H4> | ||
| 348 | -<P> | ||
| 349 | -State threads are very lightweight user-level entities, and therefore creating | ||
| 350 | -and maintaining user connections requires minimal resources. An application | ||
| 351 | -using the State Threads library scales very well with the increasing number | ||
| 352 | -of connections. | ||
| 353 | -<P> | ||
| 354 | -On multiprocessor systems an application should create multiple processes | ||
| 355 | -to take advantage of hardware parallelism. Using multiple separate processes | ||
| 356 | -is the <I>only</I> way to achieve the highest possible system scalability. | ||
| 357 | -This is because duplicating per-process resources is the only way to avoid | ||
| 358 | -significant synchronization overhead on multiprocessor systems. Creating | ||
| 359 | -separate UNIX processes naturally offers resource duplication. Again, | ||
| 360 | -as in the EDSM architecture, there is no connection between the number of | ||
| 361 | -simultaneous connections (which may be very large and changes within a wide | ||
| 362 | -range) and the number of kernel entities (which is usually small and constant). | ||
| 363 | -In other words, the State Threads library makes it possible to multiplex a | ||
| 364 | -large number of simultaneous connections onto a much smaller number of | ||
| 365 | -separate processes, thus allowing an application to scale well with both | ||
| 366 | -the load and system size. | ||
| 367 | -<P> | ||
| 368 | - | ||
| 369 | -<H4>3.3 Performance</H4> | ||
| 370 | -<P> | ||
| 371 | -Performance is one of the library's main objectives. The State Threads | ||
| 372 | -library is implemented to minimize the number of system calls and | ||
| 373 | -to make thread creation and context switching as fast as possible. | ||
| 374 | -For example, per-thread signal mask does not exist (unlike | ||
| 375 | -POSIX threads), so there is no need to save and restore a process's | ||
| 376 | -signal mask on every thread context switch. This eliminates two system | ||
| 377 | -calls per context switch. Signal events can be handled much more | ||
| 378 | -efficiently by converting them to I/O events (see above). | ||
| 379 | -<P> | ||
| 380 | - | ||
| 381 | -<H4>3.4 Portability</H4> | ||
| 382 | -<P> | ||
| 383 | -The library uses the same general, underlying concepts as the EDSM | ||
| 384 | -architecture, including non-blocking I/O, file descriptors, and | ||
| 385 | -I/O multiplexing. These concepts are available in some form on most | ||
| 386 | -UNIX platforms, making the library very portable across many | ||
| 387 | -flavors of UNIX. There are only a few platform-dependent sections in the | ||
| 388 | -source. | ||
| 389 | -<P> | ||
| 390 | - | ||
| 391 | -<H4>3.5 State Threads and NSPR</H4> | ||
| 392 | -<P> | ||
| 393 | -The State Threads library is a derivative of the Netscape Portable | ||
| 394 | -Runtime library (NSPR) <A HREF=#refs7>[Reference 7]</A>. The primary goal of | ||
| 395 | -NSPR is to provide a platform-independent layer for system facilities, | ||
| 396 | -where system facilities include threads, thread synchronization, and I/O. | ||
| 397 | -Performance and scalability are not the main concern of NSPR. The | ||
| 398 | -State Threads library addresses performance and scalability while | ||
| 399 | -remaining much smaller than NSPR. It is contained in 8 source files | ||
| 400 | -as opposed to more than 400, but provides all the functionality that | ||
| 401 | -is needed to write efficient IAs on UNIX-like platforms. | ||
| 402 | -<P> | ||
| 403 | - | ||
| 404 | -<TABLE CELLPADDING=3> | ||
| 405 | -<TR> | ||
| 406 | -<TD></TD> | ||
| 407 | -<TH>NSPR</TH> | ||
| 408 | -<TH>State Threads</TH> | ||
| 409 | -</TR> | ||
| 410 | -<TR> | ||
| 411 | -<TD><B>Lines of code</B></TD> | ||
| 412 | -<TD ALIGN=RIGHT>~150,000</TD> | ||
| 413 | -<TD ALIGN=RIGHT>~3000</TD> | ||
| 414 | -</TR> | ||
| 415 | -<TR> | ||
| 416 | -<TD><B>Dynamic library size <BR>(debug version)</B></TD> | ||
| 417 | -<TD></TD> | ||
| 418 | -<TD></TD> | ||
| 419 | -</TR> | ||
| 420 | -<TR> | ||
| 421 | -<TD>IRIX</TD> | ||
| 422 | -<TD ALIGN=RIGHT>~700 KB</TD> | ||
| 423 | -<TD ALIGN=RIGHT>~60 KB</TD> | ||
| 424 | -</TR> | ||
| 425 | -<TR> | ||
| 426 | -<TD>Linux</TD> | ||
| 427 | -<TD ALIGN=RIGHT>~900 KB</TD> | ||
| 428 | -<TD ALIGN=RIGHT>~70 KB</TD> | ||
| 429 | -</TR> | ||
| 430 | -</TABLE> | ||
| 431 | -<P> | ||
| 432 | - | ||
| 433 | -<H3>Conclusion</H3> | ||
| 434 | -<P> | ||
| 435 | -State Threads is an application library which provides a foundation for | ||
| 436 | -writing <A HREF=#IA>Internet Applications</A>. To summarize, it has the | ||
| 437 | -following <I>advantages</I>: | ||
| 438 | -<P> | ||
| 439 | -<UL> | ||
| 440 | -<LI>It allows the design of fast and highly scalable applications. An | ||
| 441 | -application will scale well with both load and number of CPUs. | ||
| 442 | -<P> | ||
| 443 | -<LI>It greatly simplifies application programming and debugging because, as a | ||
| 444 | -rule, no mutual exclusion locking is necessary and the entire application is | ||
| 445 | -free to use static variables and non-reentrant library functions. | ||
| 446 | -</UL> | ||
| 447 | -<P> | ||
| 448 | -The library's main <I>limitation</I>: | ||
| 449 | -<P> | ||
| 450 | -<UL> | ||
| 451 | -<LI>All I/O operations on sockets must use the State Thread library's I/O | ||
| 452 | -functions because only those functions perform thread scheduling and prevent | ||
| 453 | -the application's processes from blocking. | ||
| 454 | -</UL> | ||
| 455 | -<P> | ||
| 456 | - | ||
| 457 | -<H3>References</H3> | ||
| 458 | -<OL> | ||
| 459 | -<A NAME="refs1"> | ||
| 460 | -<LI> Apache Software Foundation, | ||
| 461 | -<A HREF="http://www.apache.org">http://www.apache.org</A>. | ||
| 462 | -<A NAME="refs2"> | ||
| 463 | -<LI> Douglas E. Comer, David L. Stevens, <I>Internetworking With TCP/IP, | ||
| 464 | -Vol. III: Client-Server Programming And Applications</I>, Second Edition, | ||
| 465 | -Ch. 8, 12. | ||
| 466 | -<A NAME="refs3"> | ||
| 467 | -<LI> W. Richard Stevens, <I>UNIX Network Programming</I>, Second Edition, | ||
| 468 | -Vol. 1, Ch. 15. | ||
| 469 | -<A NAME="refs4"> | ||
| 470 | -<LI> Zeus Technology Limited, | ||
| 471 | -<A HREF="http://www.zeus.co.uk/">http://www.zeus.co.uk</A>. | ||
| 472 | -<A NAME="refs5"> | ||
| 473 | -<LI> Peter Druschel, Vivek S. Pai, Willy Zwaenepoel, | ||
| 474 | -<A HREF="http://www.cs.rice.edu/~druschel/usenix99flash.ps.gz"> | ||
| 475 | -Flash: An Efficient and Portable Web Server</A>. In <I>Proceedings of the | ||
| 476 | -USENIX 1999 Annual Technical Conference</I>, Monterey, CA, June 1999. | ||
| 477 | -<A NAME="refs6"> | ||
| 478 | -<LI> GNU Portable Threads, | ||
| 479 | -<A HREF="http://www.gnu.org/software/pth/">http://www.gnu.org/software/pth/</A>. | ||
| 480 | -<A NAME="refs7"> | ||
| 481 | -<LI> Netscape Portable Runtime, | ||
| 482 | -<A HREF="http://www.mozilla.org/docs/refList/refNSPR/">http://www.mozilla.org/docs/refList/refNSPR/</A>. | ||
| 483 | -</OL> | ||
| 484 | - | ||
| 485 | -<H3>Other resources covering various architectural issues in IAs</H3> | ||
| 486 | -<OL START=8> | ||
| 487 | -<LI> Dan Kegel, <I>The C10K problem</I>, | ||
| 488 | -<A HREF="http://www.kegel.com/c10k.html">http://www.kegel.com/c10k.html</A>. | ||
| 489 | -</LI> | ||
| 490 | -<LI> James C. Hu, Douglas C. Schmidt, Irfan Pyarali, <I>JAWS: Understanding | ||
| 491 | -High Performance Web Systems</I>, | ||
| 492 | -<A HREF="http://www.cs.wustl.edu/~jxh/research/research.html">http://www.cs.wustl.edu/~jxh/research/research.html</A>.</LI> | ||
| 493 | -</OL> | ||
| 494 | -<P> | ||
| 495 | -<HR> | ||
| 496 | -<P> | ||
| 497 | - | ||
| 498 | -<CENTER><FONT SIZE=-1>Portions created by SGI are Copyright © 2000 | ||
| 499 | -Silicon Graphics, Inc. All rights reserved.</FONT></CENTER> | ||
| 500 | -<P> | ||
| 501 | - | ||
| 502 | -</BODY> | ||
| 503 | -</HTML> | ||
| 504 | - |
| 1 | -How the timeout heap works | ||
| 2 | - | ||
| 3 | -As of version 1.5, the State Threads Library represents the queue of | ||
| 4 | -sleeping threads using a heap data structure rather than a sorted | ||
| 5 | -linked list. This improves performance when there is a large number | ||
| 6 | -of sleeping threads, since insertion into a heap takes O(log N) time | ||
| 7 | -while insertion into a sorted list takes O(N) time. For example, in | ||
| 8 | -one test 1000 threads were created, each thread called st_usleep() | ||
| 9 | -with a random time interval, and then all the threads where | ||
| 10 | -immediately interrupted and joined before the sleeps had a chance to | ||
| 11 | -finish. The whole process was repeated 1000 times, for a total of a | ||
| 12 | -million sleep queue insertions and removals. With the old list-based | ||
| 13 | -sleep queue, this test took 100 seconds; now it takes only 12 seconds. | ||
| 14 | - | ||
| 15 | -Heap data structures are typically based on dynamically resized | ||
| 16 | -arrays. However, since the existing ST code base was very nicely | ||
| 17 | -structured around linking the thread objects into pointer-based lists | ||
| 18 | -without the need for any auxiliary data structures, implementing the | ||
| 19 | -heap using a similar nodes-and-pointers based approach seemed more | ||
| 20 | -appropriate for ST than introducing a separate array. | ||
| 21 | - | ||
| 22 | -Thus, the new ST timeout heap works by organizing the existing | ||
| 23 | -_st_thread_t objects in a balanced binary tree, just as they were | ||
| 24 | -previously organized into a doubly-linked, sorted list. The global | ||
| 25 | -_ST_SLEEPQ variable, formerly a linked list head, is now simply a | ||
| 26 | -pointer to the root of this tree, and the root node of the tree is the | ||
| 27 | -thread with the earliest timeout. Each thread object has two child | ||
| 28 | -pointers, "left" and "right", pointing to threads with later timeouts. | ||
| 29 | - | ||
| 30 | -Each node in the tree is numbered with an integer index, corresponding | ||
| 31 | -to the array index in an array-based heap, and the tree is kept fully | ||
| 32 | -balanced and left-adjusted at all times. In other words, the tree | ||
| 33 | -consists of any number of fully populated top levels, followed by a | ||
| 34 | -single bottom level which may be partially populated, such that any | ||
| 35 | -existing nodes form a contiguous block to the left and the spaces for | ||
| 36 | -missing nodes form a contiguous block to the right. For example, if | ||
| 37 | -there are nine threads waiting for a timeout, they are numbered and | ||
| 38 | -arranged in a tree exactly as follows: | ||
| 39 | - | ||
| 40 | - 1 | ||
| 41 | - / \ | ||
| 42 | - 2 3 | ||
| 43 | - / \ / \ | ||
| 44 | - 4 5 6 7 | ||
| 45 | - / \ | ||
| 46 | - 8 9 | ||
| 47 | - | ||
| 48 | -Each node has either no children, only a left child, or both a left | ||
| 49 | -and a right child. Children always time out later than their parents | ||
| 50 | -(this is called the "heap invariant"), but when a node has two | ||
| 51 | -children, their mutual order is unspecified - the left child may time | ||
| 52 | -out before or after the right child. If a node is numbered N, its | ||
| 53 | -left child is numbered 2N, and its right child is numbered 2N+1. | ||
| 54 | - | ||
| 55 | -There is no pointer from a child to its parent; all pointers point | ||
| 56 | -downward. Additions and deletions both work by starting at the root | ||
| 57 | -and traversing the tree towards the leaves, going left or right | ||
| 58 | -according to the binary digits forming the index of the destination | ||
| 59 | -node. As nodes are added or deleted, existing nodes are rearranged to | ||
| 60 | -maintain the heap invariant. |
trunk/research/st-1.9/examples/Makefile
已删除
100644 → 0
| 1 | -# | ||
| 2 | -# Portions created by SGI are Copyright (C) 2000 Silicon Graphics, Inc. | ||
| 3 | -# All Rights Reserved. | ||
| 4 | -# | ||
| 5 | -# Redistribution and use in source and binary forms, with or without | ||
| 6 | -# modification, are permitted provided that the following conditions | ||
| 7 | -# are met: | ||
| 8 | -# | ||
| 9 | -# 1. Redistributions of source code must retain the above copyright | ||
| 10 | -# notice, this list of conditions and the following disclaimer. | ||
| 11 | -# 2. Redistributions in binary form must reproduce the above copyright | ||
| 12 | -# notice, this list of conditions and the following disclaimer in the | ||
| 13 | -# documentation and/or other materials provided with the distribution. | ||
| 14 | -# 3. Neither the name of Silicon Graphics, Inc. nor the names of its | ||
| 15 | -# contributors may be used to endorse or promote products derived from | ||
| 16 | -# this software without specific prior written permission. | ||
| 17 | -# | ||
| 18 | -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
| 19 | -# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
| 20 | -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||
| 21 | -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||
| 22 | -# HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
| 23 | -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED | ||
| 24 | -# TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | ||
| 25 | -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | ||
| 26 | -# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | ||
| 27 | -# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
| 28 | -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
| 29 | - | ||
| 30 | -########################## | ||
| 31 | -# Supported OSes: | ||
| 32 | -# | ||
| 33 | -# AIX | ||
| 34 | -# FREEBSD | ||
| 35 | -# HPUX | ||
| 36 | -# HPUX_64 | ||
| 37 | -# IRIX | ||
| 38 | -# IRIX_64 | ||
| 39 | -# LINUX | ||
| 40 | -# LINUX_IA64 | ||
| 41 | -# NETBSD | ||
| 42 | -# OPENBSD | ||
| 43 | -# OSF1 | ||
| 44 | -# SOLARIS | ||
| 45 | -# SOLARIS_64 | ||
| 46 | - | ||
| 47 | -########################## | ||
| 48 | - | ||
| 49 | -CC = cc | ||
| 50 | - | ||
| 51 | -SHELL = /bin/sh | ||
| 52 | -ECHO = /bin/echo | ||
| 53 | - | ||
| 54 | -DEPTH = .. | ||
| 55 | -BUILD = | ||
| 56 | -TARGETDIR = | ||
| 57 | - | ||
| 58 | -DEFINES = | ||
| 59 | -CFLAGS = | ||
| 60 | -OTHER_FLAGS = | ||
| 61 | - | ||
| 62 | -OBJDIR = $(DEPTH)/$(TARGETDIR) | ||
| 63 | -INCDIR = $(DEPTH)/$(TARGETDIR) | ||
| 64 | -LIBST = $(OBJDIR)/libst.a | ||
| 65 | -HEADER = $(INCDIR)/st.h | ||
| 66 | - | ||
| 67 | -LIBRESOLV = | ||
| 68 | -EXTRALIBS = | ||
| 69 | - | ||
| 70 | -ifeq ($(OS),) | ||
| 71 | -EXAMPLES = unknown | ||
| 72 | -else | ||
| 73 | -EXAMPLES = $(OBJDIR)/lookupdns $(OBJDIR)/proxy $(OBJDIR)/server | ||
| 74 | -endif | ||
| 75 | - | ||
| 76 | - | ||
| 77 | -########################## | ||
| 78 | -# Platform section. | ||
| 79 | -# | ||
| 80 | - | ||
| 81 | -ifeq (DARWIN, $(findstring DARWIN, $(OS))) | ||
| 82 | -LIBRESOLV = -lresolv | ||
| 83 | -endif | ||
| 84 | - | ||
| 85 | -ifeq (LINUX, $(findstring LINUX, $(OS))) | ||
| 86 | -LIBRESOLV = -lresolv | ||
| 87 | -endif | ||
| 88 | - | ||
| 89 | -ifeq (SOLARIS, $(findstring SOLARIS, $(OS))) | ||
| 90 | -LIBRESOLV = -lresolv | ||
| 91 | -EXTRALIBS = -lsocket -lnsl | ||
| 92 | -endif | ||
| 93 | - | ||
| 94 | -# | ||
| 95 | -# End of platform section. | ||
| 96 | -########################## | ||
| 97 | - | ||
| 98 | - | ||
| 99 | -all: $(EXAMPLES) | ||
| 100 | - | ||
| 101 | -$(OBJDIR)/lookupdns: lookupdns.c $(OBJDIR)/res.o $(LIBST) $(HEADER) | ||
| 102 | - $(CC) $(CFLAGS) -I$(INCDIR) lookupdns.c $(OBJDIR)/res.o $(LIBST) $(LIBRESOLV) $(EXTRALIBS) -o $@ | ||
| 103 | - | ||
| 104 | -$(OBJDIR)/proxy: proxy.c $(LIBST) $(HEADER) | ||
| 105 | - $(CC) $(CFLAGS) -I$(INCDIR) proxy.c $(LIBST) $(EXTRALIBS) -o $@ | ||
| 106 | - | ||
| 107 | -$(OBJDIR)/server: server.c $(OBJDIR)/error.o $(LIBST) $(HEADER) | ||
| 108 | - $(CC) $(CFLAGS) -I$(INCDIR) server.c $(OBJDIR)/error.o $(LIBST) $(EXTRALIBS) -o $@ | ||
| 109 | - | ||
| 110 | -$(OBJDIR)/%.o: %.c | ||
| 111 | - $(CC) $(CFLAGS) -I$(INCDIR) -c $< -o $@ | ||
| 112 | - | ||
| 113 | -.DEFAULT: | ||
| 114 | - @cd $(DEPTH); $(MAKE) $@ | ||
| 115 | - |
trunk/research/st-1.9/examples/README
已删除
100644 → 0
| 1 | -Portions created by SGI are Copyright (C) 2000 Silicon Graphics, Inc. | ||
| 2 | -All Rights Reserved. | ||
| 3 | - | ||
| 4 | - | ||
| 5 | -This directory contains three example programs. | ||
| 6 | - | ||
| 7 | - | ||
| 8 | ---------------------------------------------------------------------------- | ||
| 9 | - | ||
| 10 | -PROGRAM | ||
| 11 | - | ||
| 12 | - lookupdns | ||
| 13 | - | ||
| 14 | -FILES | ||
| 15 | - | ||
| 16 | - lookupdns.c | ||
| 17 | - res.c | ||
| 18 | - | ||
| 19 | -USAGE | ||
| 20 | - | ||
| 21 | - lookupdns <hostname1> [<hostname2>] ... | ||
| 22 | - | ||
| 23 | -DESCRIPTION | ||
| 24 | - | ||
| 25 | - This program performs asynchronous DNS host name resolution and reports | ||
| 26 | - IP address for each <hostname> specified as a command line argument. | ||
| 27 | - One ST thread is created for each host name. All threads do host name | ||
| 28 | - resolution concurrently. | ||
| 29 | - | ||
| 30 | - | ||
| 31 | ---------------------------------------------------------------------------- | ||
| 32 | - | ||
| 33 | -PROGRAM | ||
| 34 | - | ||
| 35 | - proxy | ||
| 36 | - | ||
| 37 | -FILES | ||
| 38 | - | ||
| 39 | - proxy.c | ||
| 40 | - | ||
| 41 | -USAGE | ||
| 42 | - | ||
| 43 | - proxy -l <local_addr> -r <remote_addr> [-p <num_processes>] [-S] | ||
| 44 | - | ||
| 45 | - -l <local_addr> bind to local address specified as [<host>]:<port> | ||
| 46 | - -r <remote_addr> connect to remote address specified as <host>:<port> | ||
| 47 | - -p <num_processes> create specified number of processes | ||
| 48 | - -S serialize accept() calls from different processes | ||
| 49 | - on the same listening socket (if needed). | ||
| 50 | - | ||
| 51 | -DESCRIPTION | ||
| 52 | - | ||
| 53 | - This program acts as a generic gateway. It listens for connections to a | ||
| 54 | - local address. Upon accepting a client connection, it connects to the | ||
| 55 | - specified remote address and then just pumps the data through without any | ||
| 56 | - modification. | ||
| 57 | - | ||
| 58 | - | ||
| 59 | ---------------------------------------------------------------------------- | ||
| 60 | - | ||
| 61 | -PROGRAM | ||
| 62 | - | ||
| 63 | - server | ||
| 64 | - | ||
| 65 | -FILES | ||
| 66 | - | ||
| 67 | - server.c | ||
| 68 | - error.c | ||
| 69 | - | ||
| 70 | -USAGE | ||
| 71 | - | ||
| 72 | - server -l <log_directory> [<options>] | ||
| 73 | - | ||
| 74 | - -l <log_directory> open all log files in specified directory. | ||
| 75 | - | ||
| 76 | - Possible options: | ||
| 77 | - | ||
| 78 | - -b <host>:<port> bind to specified address (multiple addresses | ||
| 79 | - are permitted) | ||
| 80 | - -p <num_processes> create specified number of processes | ||
| 81 | - -t <min_thr>:<max_thr> specify thread limits per listening socket | ||
| 82 | - across all processes | ||
| 83 | - -u <user> change server's user id to specified value | ||
| 84 | - -q <backlog> set max length of pending connections queue | ||
| 85 | - -a enable access logging | ||
| 86 | - -i run in interactive mode (useful for debugging) | ||
| 87 | - -S serialize accept() calls from different processes | ||
| 88 | - on the same listening socket (if needed). | ||
| 89 | - | ||
| 90 | -DESCRIPTION | ||
| 91 | - | ||
| 92 | - This program is a general server example. It accepts a client connection | ||
| 93 | - and outputs a short HTML page. It can be easily adapted to provide | ||
| 94 | - other services. | ||
| 95 | - | ||
| 96 | - | ||
| 97 | ---------------------------------------------------------------------------- | ||
| 98 | - |
trunk/research/st-1.9/examples/error.c
已删除
100644 → 0
| 1 | -/* | ||
| 2 | - * Portions created by SGI are Copyright (C) 2000 Silicon Graphics, Inc. | ||
| 3 | - * All Rights Reserved. | ||
| 4 | - * | ||
| 5 | - * Redistribution and use in source and binary forms, with or without | ||
| 6 | - * modification, are permitted provided that the following conditions | ||
| 7 | - * are met: | ||
| 8 | - * | ||
| 9 | - * 1. Redistributions of source code must retain the above copyright | ||
| 10 | - * notice, this list of conditions and the following disclaimer. | ||
| 11 | - * 2. Redistributions in binary form must reproduce the above copyright | ||
| 12 | - * notice, this list of conditions and the following disclaimer in the | ||
| 13 | - * documentation and/or other materials provided with the distribution. | ||
| 14 | - * 3. Neither the name of Silicon Graphics, Inc. nor the names of its | ||
| 15 | - * contributors may be used to endorse or promote products derived from | ||
| 16 | - * this software without specific prior written permission. | ||
| 17 | - * | ||
| 18 | - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
| 19 | - * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
| 20 | - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||
| 21 | - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||
| 22 | - * HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
| 23 | - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED | ||
| 24 | - * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | ||
| 25 | - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | ||
| 26 | - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | ||
| 27 | - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
| 28 | - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
| 29 | - */ | ||
| 30 | - | ||
| 31 | -#include <stdarg.h> | ||
| 32 | -#include <stdio.h> | ||
| 33 | -#include <stdlib.h> | ||
| 34 | -#include <string.h> | ||
| 35 | -#include <unistd.h> | ||
| 36 | -#include <errno.h> | ||
| 37 | -#include "st.h" | ||
| 38 | - | ||
| 39 | -/* | ||
| 40 | - * Simple error reporting functions. | ||
| 41 | - * Suggested in W. Richard Stevens' "Advanced Programming in UNIX | ||
| 42 | - * Environment". | ||
| 43 | - */ | ||
| 44 | - | ||
| 45 | -#define MAXLINE 4096 /* max line length */ | ||
| 46 | - | ||
| 47 | -static void err_doit(int, int, const char *, va_list); | ||
| 48 | - | ||
| 49 | - | ||
| 50 | -/* | ||
| 51 | - * Nonfatal error related to a system call. | ||
| 52 | - * Print a message and return. | ||
| 53 | - */ | ||
| 54 | -void err_sys_report(int fd, const char *fmt, ...) | ||
| 55 | -{ | ||
| 56 | - va_list ap; | ||
| 57 | - | ||
| 58 | - va_start(ap, fmt); | ||
| 59 | - err_doit(fd, 1, fmt, ap); | ||
| 60 | - va_end(ap); | ||
| 61 | -} | ||
| 62 | - | ||
| 63 | - | ||
| 64 | -/* | ||
| 65 | - * Fatal error related to a system call. | ||
| 66 | - * Print a message and terminate. | ||
| 67 | - */ | ||
| 68 | -void err_sys_quit(int fd, const char *fmt, ...) | ||
| 69 | -{ | ||
| 70 | - va_list ap; | ||
| 71 | - | ||
| 72 | - va_start(ap, fmt); | ||
| 73 | - err_doit(fd, 1, fmt, ap); | ||
| 74 | - va_end(ap); | ||
| 75 | - exit(1); | ||
| 76 | -} | ||
| 77 | - | ||
| 78 | - | ||
| 79 | -/* | ||
| 80 | - * Fatal error related to a system call. | ||
| 81 | - * Print a message, dump core, and terminate. | ||
| 82 | - */ | ||
| 83 | -void err_sys_dump(int fd, const char *fmt, ...) | ||
| 84 | -{ | ||
| 85 | - va_list ap; | ||
| 86 | - | ||
| 87 | - va_start(ap, fmt); | ||
| 88 | - err_doit(fd, 1, fmt, ap); | ||
| 89 | - va_end(ap); | ||
| 90 | - abort(); /* dump core and terminate */ | ||
| 91 | - exit(1); /* shouldn't get here */ | ||
| 92 | -} | ||
| 93 | - | ||
| 94 | - | ||
| 95 | -/* | ||
| 96 | - * Nonfatal error unrelated to a system call. | ||
| 97 | - * Print a message and return. | ||
| 98 | - */ | ||
| 99 | -void err_report(int fd, const char *fmt, ...) | ||
| 100 | -{ | ||
| 101 | - va_list ap; | ||
| 102 | - | ||
| 103 | - va_start(ap, fmt); | ||
| 104 | - err_doit(fd, 0, fmt, ap); | ||
| 105 | - va_end(ap); | ||
| 106 | -} | ||
| 107 | - | ||
| 108 | - | ||
| 109 | -/* | ||
| 110 | - * Fatal error unrelated to a system call. | ||
| 111 | - * Print a message and terminate. | ||
| 112 | - */ | ||
| 113 | -void err_quit(int fd, const char *fmt, ...) | ||
| 114 | -{ | ||
| 115 | - va_list ap; | ||
| 116 | - | ||
| 117 | - va_start(ap, fmt); | ||
| 118 | - err_doit(fd, 0, fmt, ap); | ||
| 119 | - va_end(ap); | ||
| 120 | - exit(1); | ||
| 121 | -} | ||
| 122 | - | ||
| 123 | - | ||
| 124 | -/* | ||
| 125 | - * Return a pointer to a string containing current time. | ||
| 126 | - */ | ||
| 127 | -char *err_tstamp(void) | ||
| 128 | -{ | ||
| 129 | - static char *months[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", | ||
| 130 | - "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; | ||
| 131 | - static char str[32]; | ||
| 132 | - static time_t lastt = 0; | ||
| 133 | - struct tm *tmp; | ||
| 134 | - time_t currt = st_time(); | ||
| 135 | - | ||
| 136 | - if (currt == lastt) | ||
| 137 | - return str; | ||
| 138 | - | ||
| 139 | - tmp = localtime(&currt); | ||
| 140 | - sprintf(str, "[%02d/%s/%d:%02d:%02d:%02d] ", tmp->tm_mday, | ||
| 141 | - months[tmp->tm_mon], 1900 + tmp->tm_year, tmp->tm_hour, | ||
| 142 | - tmp->tm_min, tmp->tm_sec); | ||
| 143 | - lastt = currt; | ||
| 144 | - | ||
| 145 | - return str; | ||
| 146 | -} | ||
| 147 | - | ||
| 148 | - | ||
| 149 | -/* | ||
| 150 | - * Print a message and return to caller. | ||
| 151 | - * Caller specifies "errnoflag". | ||
| 152 | - */ | ||
| 153 | -static void err_doit(int fd, int errnoflag, const char *fmt, va_list ap) | ||
| 154 | -{ | ||
| 155 | - int errno_save; | ||
| 156 | - char buf[MAXLINE]; | ||
| 157 | - | ||
| 158 | - errno_save = errno; /* value caller might want printed */ | ||
| 159 | - strcpy(buf, err_tstamp()); /* prepend a message with time stamp */ | ||
| 160 | - vsprintf(buf + strlen(buf), fmt, ap); | ||
| 161 | - if (errnoflag) | ||
| 162 | - sprintf(buf + strlen(buf), ": %s\n", strerror(errno_save)); | ||
| 163 | - else | ||
| 164 | - strcat(buf, "\n"); | ||
| 165 | - write(fd, buf, strlen(buf)); | ||
| 166 | - errno = errno_save; | ||
| 167 | -} | ||
| 168 | - |
| 1 | -/* | ||
| 2 | - * Portions created by SGI are Copyright (C) 2000 Silicon Graphics, Inc. | ||
| 3 | - * All Rights Reserved. | ||
| 4 | - * | ||
| 5 | - * Redistribution and use in source and binary forms, with or without | ||
| 6 | - * modification, are permitted provided that the following conditions | ||
| 7 | - * are met: | ||
| 8 | - * | ||
| 9 | - * 1. Redistributions of source code must retain the above copyright | ||
| 10 | - * notice, this list of conditions and the following disclaimer. | ||
| 11 | - * 2. Redistributions in binary form must reproduce the above copyright | ||
| 12 | - * notice, this list of conditions and the following disclaimer in the | ||
| 13 | - * documentation and/or other materials provided with the distribution. | ||
| 14 | - * 3. Neither the name of Silicon Graphics, Inc. nor the names of its | ||
| 15 | - * contributors may be used to endorse or promote products derived from | ||
| 16 | - * this software without specific prior written permission. | ||
| 17 | - * | ||
| 18 | - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
| 19 | - * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
| 20 | - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||
| 21 | - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||
| 22 | - * HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
| 23 | - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED | ||
| 24 | - * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | ||
| 25 | - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | ||
| 26 | - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | ||
| 27 | - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
| 28 | - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
| 29 | - */ | ||
| 30 | - | ||
| 31 | -#include <stdio.h> | ||
| 32 | -#include <stdlib.h> | ||
| 33 | -#include <unistd.h> | ||
| 34 | -#include <sys/time.h> | ||
| 35 | -#include <sys/types.h> | ||
| 36 | -#include <sys/socket.h> | ||
| 37 | -#include <netinet/in.h> | ||
| 38 | -#include <arpa/inet.h> | ||
| 39 | -#include <netdb.h> | ||
| 40 | -#include "st.h" | ||
| 41 | - | ||
| 42 | -#if !defined(NETDB_INTERNAL) && defined(h_NETDB_INTERNAL) | ||
| 43 | -#define NETDB_INTERNAL h_NETDB_INTERNAL | ||
| 44 | -#endif | ||
| 45 | - | ||
| 46 | -/* Resolution timeout (in microseconds) */ | ||
| 47 | -#define TIMEOUT (2*1000000LL) | ||
| 48 | - | ||
| 49 | -/* External function defined in the res.c file */ | ||
| 50 | -int dns_getaddr(const char *host, struct in_addr *addr, st_utime_t timeout); | ||
| 51 | - | ||
| 52 | - | ||
| 53 | -void *do_resolve(void *host) | ||
| 54 | -{ | ||
| 55 | - struct in_addr addr; | ||
| 56 | - | ||
| 57 | - /* Use dns_getaddr() instead of gethostbyname(3) to get IP address */ | ||
| 58 | - if (dns_getaddr(host, &addr, TIMEOUT) < 0) { | ||
| 59 | - fprintf(stderr, "dns_getaddr: can't resolve %s: ", (char *)host); | ||
| 60 | - if (h_errno == NETDB_INTERNAL) | ||
| 61 | - perror(""); | ||
| 62 | - else | ||
| 63 | - herror(""); | ||
| 64 | - } else | ||
| 65 | - printf("%-40s %s\n", (char *)host, inet_ntoa(addr)); | ||
| 66 | - | ||
| 67 | - return NULL; | ||
| 68 | -} | ||
| 69 | - | ||
| 70 | - | ||
| 71 | -/* | ||
| 72 | - * Asynchronous DNS host name resolution. This program creates one | ||
| 73 | - * ST thread for each host name (specified as command line arguments). | ||
| 74 | - * All threads do host name resolution concurrently. | ||
| 75 | - */ | ||
| 76 | -int main(int argc, char *argv[]) | ||
| 77 | -{ | ||
| 78 | - int i; | ||
| 79 | - | ||
| 80 | - if (argc < 2) { | ||
| 81 | - fprintf(stderr, "Usage: %s <hostname1> [<hostname2>] ...\n", argv[0]); | ||
| 82 | - exit(1); | ||
| 83 | - } | ||
| 84 | - | ||
| 85 | - if (st_init() < 0) { | ||
| 86 | - perror("st_init"); | ||
| 87 | - exit(1); | ||
| 88 | - } | ||
| 89 | - | ||
| 90 | - for (i = 1; i < argc; i++) { | ||
| 91 | - /* Create a separate thread for each host name */ | ||
| 92 | - if (st_thread_create(do_resolve, argv[i], 0, 0) == NULL) { | ||
| 93 | - perror("st_thread_create"); | ||
| 94 | - exit(1); | ||
| 95 | - } | ||
| 96 | - } | ||
| 97 | - | ||
| 98 | - st_thread_exit(NULL); | ||
| 99 | - | ||
| 100 | - /* NOTREACHED */ | ||
| 101 | - return 1; | ||
| 102 | -} | ||
| 103 | - |
trunk/research/st-1.9/examples/proxy.c
已删除
100644 → 0
| 1 | -/* | ||
| 2 | - * Portions created by SGI are Copyright (C) 2000 Silicon Graphics, Inc. | ||
| 3 | - * All Rights Reserved. | ||
| 4 | - * | ||
| 5 | - * Redistribution and use in source and binary forms, with or without | ||
| 6 | - * modification, are permitted provided that the following conditions | ||
| 7 | - * are met: | ||
| 8 | - * | ||
| 9 | - * 1. Redistributions of source code must retain the above copyright | ||
| 10 | - * notice, this list of conditions and the following disclaimer. | ||
| 11 | - * 2. Redistributions in binary form must reproduce the above copyright | ||
| 12 | - * notice, this list of conditions and the following disclaimer in the | ||
| 13 | - * documentation and/or other materials provided with the distribution. | ||
| 14 | - * 3. Neither the name of Silicon Graphics, Inc. nor the names of its | ||
| 15 | - * contributors may be used to endorse or promote products derived from | ||
| 16 | - * this software without specific prior written permission. | ||
| 17 | - * | ||
| 18 | - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
| 19 | - * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
| 20 | - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||
| 21 | - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||
| 22 | - * HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
| 23 | - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED | ||
| 24 | - * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | ||
| 25 | - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | ||
| 26 | - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | ||
| 27 | - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
| 28 | - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
| 29 | - */ | ||
| 30 | - | ||
| 31 | -#include <stdio.h> | ||
| 32 | -#include <stdlib.h> | ||
| 33 | -#include <string.h> | ||
| 34 | -#include <signal.h> | ||
| 35 | -#include <unistd.h> | ||
| 36 | -#include <fcntl.h> | ||
| 37 | -#include <sys/types.h> | ||
| 38 | -#include <sys/stat.h> | ||
| 39 | -#include <sys/socket.h> | ||
| 40 | -#include <netinet/in.h> | ||
| 41 | -#include <arpa/inet.h> | ||
| 42 | -#include <netdb.h> | ||
| 43 | -#include "st.h" | ||
| 44 | - | ||
| 45 | -#define IOBUFSIZE (16*1024) | ||
| 46 | - | ||
| 47 | -#define IOV_LEN 256 | ||
| 48 | -#define IOV_COUNT (IOBUFSIZE / IOV_LEN) | ||
| 49 | - | ||
| 50 | -#ifndef INADDR_NONE | ||
| 51 | -#define INADDR_NONE 0xffffffff | ||
| 52 | -#endif | ||
| 53 | - | ||
| 54 | -static char *prog; /* Program name */ | ||
| 55 | -static struct sockaddr_in rmt_addr; /* Remote address */ | ||
| 56 | - | ||
| 57 | -static unsigned long testing; | ||
| 58 | -#define TESTING_VERBOSE 0x1 | ||
| 59 | -#define TESTING_READV 0x2 | ||
| 60 | -#define TESTING_READ_RESID 0x4 | ||
| 61 | -#define TESTING_WRITEV 0x8 | ||
| 62 | -#define TESTING_WRITE_RESID 0x10 | ||
| 63 | - | ||
| 64 | -static void read_address(const char *str, struct sockaddr_in *sin); | ||
| 65 | -static void start_daemon(void); | ||
| 66 | -static int cpu_count(void); | ||
| 67 | -static void set_concurrency(int nproc); | ||
| 68 | -static void *handle_request(void *arg); | ||
| 69 | -static void print_sys_error(const char *msg); | ||
| 70 | - | ||
| 71 | - | ||
| 72 | -/* | ||
| 73 | - * This program acts as a generic gateway. It listens for connections | ||
| 74 | - * to a local address ('-l' option). Upon accepting a client connection, | ||
| 75 | - * it connects to the specified remote address ('-r' option) and then | ||
| 76 | - * just pumps the data through without any modification. | ||
| 77 | - */ | ||
| 78 | -int main(int argc, char *argv[]) | ||
| 79 | -{ | ||
| 80 | - extern char *optarg; | ||
| 81 | - int opt, sock, n; | ||
| 82 | - int laddr, raddr, num_procs, alt_ev, one_process; | ||
| 83 | - int serialize_accept = 0; | ||
| 84 | - struct sockaddr_in lcl_addr, cli_addr; | ||
| 85 | - st_netfd_t cli_nfd, srv_nfd; | ||
| 86 | - | ||
| 87 | - prog = argv[0]; | ||
| 88 | - num_procs = laddr = raddr = alt_ev = one_process = 0; | ||
| 89 | - | ||
| 90 | - /* Parse arguments */ | ||
| 91 | - while((opt = getopt(argc, argv, "l:r:p:Saht:X")) != EOF) { | ||
| 92 | - switch (opt) { | ||
| 93 | - case 'a': | ||
| 94 | - alt_ev = 1; | ||
| 95 | - break; | ||
| 96 | - case 'l': | ||
| 97 | - read_address(optarg, &lcl_addr); | ||
| 98 | - laddr = 1; | ||
| 99 | - break; | ||
| 100 | - case 'r': | ||
| 101 | - read_address(optarg, &rmt_addr); | ||
| 102 | - if (rmt_addr.sin_addr.s_addr == INADDR_ANY) { | ||
| 103 | - fprintf(stderr, "%s: invalid remote address: %s\n", prog, optarg); | ||
| 104 | - exit(1); | ||
| 105 | - } | ||
| 106 | - raddr = 1; | ||
| 107 | - break; | ||
| 108 | - case 'p': | ||
| 109 | - num_procs = atoi(optarg); | ||
| 110 | - if (num_procs < 1) { | ||
| 111 | - fprintf(stderr, "%s: invalid number of processes: %s\n", prog, optarg); | ||
| 112 | - exit(1); | ||
| 113 | - } | ||
| 114 | - break; | ||
| 115 | - case 'S': | ||
| 116 | - /* | ||
| 117 | - * Serialization decision is tricky on some platforms. For example, | ||
| 118 | - * Solaris 2.6 and above has kernel sockets implementation, so supposedly | ||
| 119 | - * there is no need for serialization. The ST library may be compiled | ||
| 120 | - * on one OS version, but used on another, so the need for serialization | ||
| 121 | - * should be determined at run time by the application. Since it's just | ||
| 122 | - * an example, the serialization decision is left up to user. | ||
| 123 | - * Only on platforms where the serialization is never needed on any OS | ||
| 124 | - * version st_netfd_serialize_accept() is a no-op. | ||
| 125 | - */ | ||
| 126 | - serialize_accept = 1; | ||
| 127 | - break; | ||
| 128 | - case 't': | ||
| 129 | - testing = strtoul(optarg, NULL, 0); | ||
| 130 | - break; | ||
| 131 | - case 'X': | ||
| 132 | - one_process = 1; | ||
| 133 | - break; | ||
| 134 | - case 'h': | ||
| 135 | - case '?': | ||
| 136 | - fprintf(stderr, "Usage: %s [options] -l <[host]:port> -r <host:port>\n", | ||
| 137 | - prog); | ||
| 138 | - fprintf(stderr, "options are:\n"); | ||
| 139 | - fprintf(stderr, " -p <num_processes> number of parallel processes\n"); | ||
| 140 | - fprintf(stderr, " -S serialize accepts\n"); | ||
| 141 | - fprintf(stderr, " -a use alternate event system\n"); | ||
| 142 | -#ifdef DEBUG | ||
| 143 | - fprintf(stderr, " -t mask testing/debugging mode\n"); | ||
| 144 | - fprintf(stderr, " -X one process, don't daemonize\n"); | ||
| 145 | -#endif | ||
| 146 | - exit(1); | ||
| 147 | - } | ||
| 148 | - } | ||
| 149 | - if (!laddr) { | ||
| 150 | - fprintf(stderr, "%s: local address required\n", prog); | ||
| 151 | - exit(1); | ||
| 152 | - } | ||
| 153 | - if (!raddr) { | ||
| 154 | - fprintf(stderr, "%s: remote address required\n", prog); | ||
| 155 | - exit(1); | ||
| 156 | - } | ||
| 157 | - if (num_procs == 0) | ||
| 158 | - num_procs = cpu_count(); | ||
| 159 | - | ||
| 160 | - fprintf(stderr, "%s: starting proxy daemon on %s:%d\n", prog, | ||
| 161 | - inet_ntoa(lcl_addr.sin_addr), ntohs(lcl_addr.sin_port)); | ||
| 162 | - | ||
| 163 | - /* Start the daemon */ | ||
| 164 | - if (one_process) | ||
| 165 | - num_procs = 1; | ||
| 166 | - else | ||
| 167 | - start_daemon(); | ||
| 168 | - | ||
| 169 | - if (alt_ev) | ||
| 170 | - st_set_eventsys(ST_EVENTSYS_ALT); | ||
| 171 | - | ||
| 172 | - /* Initialize the ST library */ | ||
| 173 | - if (st_init() < 0) { | ||
| 174 | - print_sys_error("st_init"); | ||
| 175 | - exit(1); | ||
| 176 | - } | ||
| 177 | - | ||
| 178 | - /* Create and bind listening socket */ | ||
| 179 | - if ((sock = socket(PF_INET, SOCK_STREAM, 0)) < 0) { | ||
| 180 | - print_sys_error("socket"); | ||
| 181 | - exit(1); | ||
| 182 | - } | ||
| 183 | - n = 1; | ||
| 184 | - if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&n, sizeof(n)) < 0) { | ||
| 185 | - print_sys_error("setsockopt"); | ||
| 186 | - exit(1); | ||
| 187 | - } | ||
| 188 | - if (bind(sock, (struct sockaddr *)&lcl_addr, sizeof(lcl_addr)) < 0) { | ||
| 189 | - print_sys_error("bind"); | ||
| 190 | - exit(1); | ||
| 191 | - } | ||
| 192 | - listen(sock, 128); | ||
| 193 | - if ((srv_nfd = st_netfd_open_socket(sock)) == NULL) { | ||
| 194 | - print_sys_error("st_netfd_open"); | ||
| 195 | - exit(1); | ||
| 196 | - } | ||
| 197 | - /* See the comment regarding serialization decision above */ | ||
| 198 | - if (num_procs > 1 && serialize_accept && st_netfd_serialize_accept(srv_nfd) | ||
| 199 | - < 0) { | ||
| 200 | - print_sys_error("st_netfd_serialize_accept"); | ||
| 201 | - exit(1); | ||
| 202 | - } | ||
| 203 | - | ||
| 204 | - /* Start server processes */ | ||
| 205 | - if (!one_process) | ||
| 206 | - set_concurrency(num_procs); | ||
| 207 | - | ||
| 208 | - for ( ; ; ) { | ||
| 209 | - n = sizeof(cli_addr); | ||
| 210 | - cli_nfd = st_accept(srv_nfd, (struct sockaddr *)&cli_addr, &n, | ||
| 211 | - ST_UTIME_NO_TIMEOUT); | ||
| 212 | - if (cli_nfd == NULL) { | ||
| 213 | - print_sys_error("st_accept"); | ||
| 214 | - exit(1); | ||
| 215 | - } | ||
| 216 | - if (st_thread_create(handle_request, cli_nfd, 0, 0) == NULL) { | ||
| 217 | - print_sys_error("st_thread_create"); | ||
| 218 | - exit(1); | ||
| 219 | - } | ||
| 220 | - } | ||
| 221 | - | ||
| 222 | - /* NOTREACHED */ | ||
| 223 | - return 1; | ||
| 224 | -} | ||
| 225 | - | ||
| 226 | - | ||
| 227 | -static void read_address(const char *str, struct sockaddr_in *sin) | ||
| 228 | -{ | ||
| 229 | - char host[128], *p; | ||
| 230 | - struct hostent *hp; | ||
| 231 | - unsigned short port; | ||
| 232 | - | ||
| 233 | - strcpy(host, str); | ||
| 234 | - if ((p = strchr(host, ':')) == NULL) { | ||
| 235 | - fprintf(stderr, "%s: invalid address: %s\n", prog, host); | ||
| 236 | - exit(1); | ||
| 237 | - } | ||
| 238 | - *p++ = '\0'; | ||
| 239 | - port = (unsigned short) atoi(p); | ||
| 240 | - if (port < 1) { | ||
| 241 | - fprintf(stderr, "%s: invalid port: %s\n", prog, p); | ||
| 242 | - exit(1); | ||
| 243 | - } | ||
| 244 | - | ||
| 245 | - memset(sin, 0, sizeof(struct sockaddr_in)); | ||
| 246 | - sin->sin_family = AF_INET; | ||
| 247 | - sin->sin_port = htons(port); | ||
| 248 | - if (host[0] == '\0') { | ||
| 249 | - sin->sin_addr.s_addr = INADDR_ANY; | ||
| 250 | - return; | ||
| 251 | - } | ||
| 252 | - sin->sin_addr.s_addr = inet_addr(host); | ||
| 253 | - if (sin->sin_addr.s_addr == INADDR_NONE) { | ||
| 254 | - /* not dotted-decimal */ | ||
| 255 | - if ((hp = gethostbyname(host)) == NULL) { | ||
| 256 | - fprintf(stderr, "%s: can't resolve address: %s\n", prog, host); | ||
| 257 | - exit(1); | ||
| 258 | - } | ||
| 259 | - memcpy(&sin->sin_addr, hp->h_addr, hp->h_length); | ||
| 260 | - } | ||
| 261 | -} | ||
| 262 | - | ||
| 263 | -#ifdef DEBUG | ||
| 264 | -static void show_iov(const struct iovec *iov, int niov) | ||
| 265 | -{ | ||
| 266 | - int i; | ||
| 267 | - size_t total; | ||
| 268 | - | ||
| 269 | - printf("iov %p has %d entries:\n", iov, niov); | ||
| 270 | - total = 0; | ||
| 271 | - for (i = 0; i < niov; i++) { | ||
| 272 | - printf("iov[%3d] iov_base=%p iov_len=0x%lx(%lu)\n", | ||
| 273 | - i, iov[i].iov_base, (unsigned long) iov[i].iov_len, | ||
| 274 | - (unsigned long) iov[i].iov_len); | ||
| 275 | - total += iov[i].iov_len; | ||
| 276 | - } | ||
| 277 | - printf("total 0x%lx(%ld)\n", (unsigned long) total, (unsigned long) total); | ||
| 278 | -} | ||
| 279 | - | ||
| 280 | -/* | ||
| 281 | - * This version is tricked out to test all the | ||
| 282 | - * st_(read|write)v?(_resid)? variants. Use the non-DEBUG version for | ||
| 283 | - * anything serious. st_(read|write) are all this function really | ||
| 284 | - * needs. | ||
| 285 | - */ | ||
| 286 | -static int pass(st_netfd_t in, st_netfd_t out) | ||
| 287 | -{ | ||
| 288 | - char buf[IOBUFSIZE]; | ||
| 289 | - struct iovec iov[IOV_COUNT]; | ||
| 290 | - int ioviter, nw, nr; | ||
| 291 | - | ||
| 292 | - if (testing & TESTING_READV) { | ||
| 293 | - for (ioviter = 0; ioviter < IOV_COUNT; ioviter++) { | ||
| 294 | - iov[ioviter].iov_base = &buf[ioviter * IOV_LEN]; | ||
| 295 | - iov[ioviter].iov_len = IOV_LEN; | ||
| 296 | - } | ||
| 297 | - if (testing & TESTING_VERBOSE) { | ||
| 298 | - printf("readv(%p)...\n", in); | ||
| 299 | - show_iov(iov, IOV_COUNT); | ||
| 300 | - } | ||
| 301 | - if (testing & TESTING_READ_RESID) { | ||
| 302 | - struct iovec *riov = iov; | ||
| 303 | - int riov_cnt = IOV_COUNT; | ||
| 304 | - if (st_readv_resid(in, &riov, &riov_cnt, ST_UTIME_NO_TIMEOUT) == 0) { | ||
| 305 | - if (testing & TESTING_VERBOSE) { | ||
| 306 | - printf("resid\n"); | ||
| 307 | - show_iov(riov, riov_cnt); | ||
| 308 | - printf("full\n"); | ||
| 309 | - show_iov(iov, IOV_COUNT); | ||
| 310 | - } | ||
| 311 | - nr = 0; | ||
| 312 | - for (ioviter = 0; ioviter < IOV_COUNT; ioviter++) | ||
| 313 | - nr += iov[ioviter].iov_len; | ||
| 314 | - nr = IOBUFSIZE - nr; | ||
| 315 | - } else | ||
| 316 | - nr = -1; | ||
| 317 | - } else | ||
| 318 | - nr = (int) st_readv(in, iov, IOV_COUNT, ST_UTIME_NO_TIMEOUT); | ||
| 319 | - } else { | ||
| 320 | - if (testing & TESTING_READ_RESID) { | ||
| 321 | - size_t resid = IOBUFSIZE; | ||
| 322 | - if (st_read_resid(in, buf, &resid, ST_UTIME_NO_TIMEOUT) == 0) | ||
| 323 | - nr = IOBUFSIZE - resid; | ||
| 324 | - else | ||
| 325 | - nr = -1; | ||
| 326 | - } else | ||
| 327 | - nr = (int) st_read(in, buf, IOBUFSIZE, ST_UTIME_NO_TIMEOUT); | ||
| 328 | - } | ||
| 329 | - if (testing & TESTING_VERBOSE) | ||
| 330 | - printf("got 0x%x(%d) E=%d\n", nr, nr, errno); | ||
| 331 | - | ||
| 332 | - if (nr <= 0) | ||
| 333 | - return 0; | ||
| 334 | - | ||
| 335 | - if (testing & TESTING_WRITEV) { | ||
| 336 | - for (nw = 0, ioviter = 0; nw < nr; | ||
| 337 | - nw += iov[ioviter].iov_len, ioviter++) { | ||
| 338 | - iov[ioviter].iov_base = &buf[nw]; | ||
| 339 | - iov[ioviter].iov_len = nr - nw; | ||
| 340 | - if (iov[ioviter].iov_len > IOV_LEN) | ||
| 341 | - iov[ioviter].iov_len = IOV_LEN; | ||
| 342 | - } | ||
| 343 | - if (testing & TESTING_VERBOSE) { | ||
| 344 | - printf("writev(%p)...\n", out); | ||
| 345 | - show_iov(iov, ioviter); | ||
| 346 | - } | ||
| 347 | - if (testing & TESTING_WRITE_RESID) { | ||
| 348 | - struct iovec *riov = iov; | ||
| 349 | - int riov_cnt = ioviter; | ||
| 350 | - if (st_writev_resid(out, &riov, &riov_cnt, ST_UTIME_NO_TIMEOUT) == 0) { | ||
| 351 | - if (testing & TESTING_VERBOSE) { | ||
| 352 | - printf("resid\n"); | ||
| 353 | - show_iov(riov, riov_cnt); | ||
| 354 | - printf("full\n"); | ||
| 355 | - show_iov(iov, ioviter); | ||
| 356 | - } | ||
| 357 | - nw = 0; | ||
| 358 | - while (--ioviter >= 0) | ||
| 359 | - nw += iov[ioviter].iov_len; | ||
| 360 | - nw = nr - nw; | ||
| 361 | - } else | ||
| 362 | - nw = -1; | ||
| 363 | - } else | ||
| 364 | - nw = st_writev(out, iov, ioviter, ST_UTIME_NO_TIMEOUT); | ||
| 365 | - } else { | ||
| 366 | - if (testing & TESTING_WRITE_RESID) { | ||
| 367 | - size_t resid = nr; | ||
| 368 | - if (st_write_resid(out, buf, &resid, ST_UTIME_NO_TIMEOUT) == 0) | ||
| 369 | - nw = nr - resid; | ||
| 370 | - else | ||
| 371 | - nw = -1; | ||
| 372 | - } else | ||
| 373 | - nw = st_write(out, buf, nr, ST_UTIME_NO_TIMEOUT); | ||
| 374 | - } | ||
| 375 | - if (testing & TESTING_VERBOSE) | ||
| 376 | - printf("put 0x%x(%d) E=%d\n", nw, nw, errno); | ||
| 377 | - | ||
| 378 | - if (nw != nr) | ||
| 379 | - return 0; | ||
| 380 | - | ||
| 381 | - return 1; | ||
| 382 | -} | ||
| 383 | -#else /* DEBUG */ | ||
| 384 | -/* | ||
| 385 | - * This version is the simple one suitable for serious use. | ||
| 386 | - */ | ||
| 387 | -static int pass(st_netfd_t in, st_netfd_t out) | ||
| 388 | -{ | ||
| 389 | - char buf[IOBUFSIZE]; | ||
| 390 | - int nw, nr; | ||
| 391 | - | ||
| 392 | - nr = (int) st_read(in, buf, IOBUFSIZE, ST_UTIME_NO_TIMEOUT); | ||
| 393 | - if (nr <= 0) | ||
| 394 | - return 0; | ||
| 395 | - | ||
| 396 | - nw = st_write(out, buf, nr, ST_UTIME_NO_TIMEOUT); | ||
| 397 | - if (nw != nr) | ||
| 398 | - return 0; | ||
| 399 | - | ||
| 400 | - return 1; | ||
| 401 | -} | ||
| 402 | -#endif | ||
| 403 | - | ||
| 404 | -static void *handle_request(void *arg) | ||
| 405 | -{ | ||
| 406 | - struct pollfd pds[2]; | ||
| 407 | - st_netfd_t cli_nfd, rmt_nfd; | ||
| 408 | - int sock; | ||
| 409 | - | ||
| 410 | - cli_nfd = (st_netfd_t) arg; | ||
| 411 | - pds[0].fd = st_netfd_fileno(cli_nfd); | ||
| 412 | - pds[0].events = POLLIN; | ||
| 413 | - | ||
| 414 | - /* Connect to remote host */ | ||
| 415 | - if ((sock = socket(PF_INET, SOCK_STREAM, 0)) < 0) { | ||
| 416 | - print_sys_error("socket"); | ||
| 417 | - goto done; | ||
| 418 | - } | ||
| 419 | - if ((rmt_nfd = st_netfd_open_socket(sock)) == NULL) { | ||
| 420 | - print_sys_error("st_netfd_open_socket"); | ||
| 421 | - close(sock); | ||
| 422 | - goto done; | ||
| 423 | - } | ||
| 424 | - if (st_connect(rmt_nfd, (struct sockaddr *)&rmt_addr, | ||
| 425 | - sizeof(rmt_addr), ST_UTIME_NO_TIMEOUT) < 0) { | ||
| 426 | - print_sys_error("st_connect"); | ||
| 427 | - st_netfd_close(rmt_nfd); | ||
| 428 | - goto done; | ||
| 429 | - } | ||
| 430 | - pds[1].fd = sock; | ||
| 431 | - pds[1].events = POLLIN; | ||
| 432 | - | ||
| 433 | - /* | ||
| 434 | - * Now just pump the data through. | ||
| 435 | - * XXX This should use one thread for each direction for true full-duplex. | ||
| 436 | - */ | ||
| 437 | - for ( ; ; ) { | ||
| 438 | - pds[0].revents = 0; | ||
| 439 | - pds[1].revents = 0; | ||
| 440 | - | ||
| 441 | - if (st_poll(pds, 2, ST_UTIME_NO_TIMEOUT) <= 0) { | ||
| 442 | - print_sys_error("st_poll"); | ||
| 443 | - break; | ||
| 444 | - } | ||
| 445 | - | ||
| 446 | - if (pds[0].revents & POLLIN) { | ||
| 447 | - if (!pass(cli_nfd, rmt_nfd)) | ||
| 448 | - break; | ||
| 449 | - } | ||
| 450 | - | ||
| 451 | - if (pds[1].revents & POLLIN) { | ||
| 452 | - if (!pass(rmt_nfd, cli_nfd)) | ||
| 453 | - break; | ||
| 454 | - } | ||
| 455 | - } | ||
| 456 | - st_netfd_close(rmt_nfd); | ||
| 457 | - | ||
| 458 | -done: | ||
| 459 | - | ||
| 460 | - st_netfd_close(cli_nfd); | ||
| 461 | - | ||
| 462 | - return NULL; | ||
| 463 | -} | ||
| 464 | - | ||
| 465 | -static void start_daemon(void) | ||
| 466 | -{ | ||
| 467 | - pid_t pid; | ||
| 468 | - | ||
| 469 | - /* Start forking */ | ||
| 470 | - if ((pid = fork()) < 0) { | ||
| 471 | - print_sys_error("fork"); | ||
| 472 | - exit(1); | ||
| 473 | - } | ||
| 474 | - if (pid > 0) | ||
| 475 | - exit(0); /* parent */ | ||
| 476 | - | ||
| 477 | - /* First child process */ | ||
| 478 | - setsid(); /* become session leader */ | ||
| 479 | - | ||
| 480 | - if ((pid = fork()) < 0) { | ||
| 481 | - print_sys_error("fork"); | ||
| 482 | - exit(1); | ||
| 483 | - } | ||
| 484 | - if (pid > 0) /* first child */ | ||
| 485 | - exit(0); | ||
| 486 | - | ||
| 487 | - chdir("/"); | ||
| 488 | - umask(022); | ||
| 489 | -} | ||
| 490 | - | ||
| 491 | -/* | ||
| 492 | - * Create separate processes ("virtual processors"). Since it's just an | ||
| 493 | - * example, there is no watchdog - the parent just exits leaving children | ||
| 494 | - * on their own. | ||
| 495 | - */ | ||
| 496 | -static void set_concurrency(int nproc) | ||
| 497 | -{ | ||
| 498 | - pid_t pid; | ||
| 499 | - int i; | ||
| 500 | - | ||
| 501 | - if (nproc < 1) | ||
| 502 | - nproc = 1; | ||
| 503 | - | ||
| 504 | - for (i = 0; i < nproc; i++) { | ||
| 505 | - if ((pid = fork()) < 0) { | ||
| 506 | - print_sys_error("fork"); | ||
| 507 | - exit(1); | ||
| 508 | - } | ||
| 509 | - /* Child returns */ | ||
| 510 | - if (pid == 0) | ||
| 511 | - return; | ||
| 512 | - } | ||
| 513 | - | ||
| 514 | - /* Parent just exits */ | ||
| 515 | - exit(0); | ||
| 516 | -} | ||
| 517 | - | ||
| 518 | -static int cpu_count(void) | ||
| 519 | -{ | ||
| 520 | - int n; | ||
| 521 | - | ||
| 522 | -#if defined (_SC_NPROCESSORS_ONLN) | ||
| 523 | - n = (int) sysconf(_SC_NPROCESSORS_ONLN); | ||
| 524 | -#elif defined (_SC_NPROC_ONLN) | ||
| 525 | - n = (int) sysconf(_SC_NPROC_ONLN); | ||
| 526 | -#elif defined (HPUX) | ||
| 527 | -#include <sys/mpctl.h> | ||
| 528 | - n = mpctl(MPC_GETNUMSPUS, 0, 0); | ||
| 529 | -#else | ||
| 530 | - n = -1; | ||
| 531 | - errno = ENOSYS; | ||
| 532 | -#endif | ||
| 533 | - | ||
| 534 | - return n; | ||
| 535 | -} | ||
| 536 | - | ||
| 537 | -static void print_sys_error(const char *msg) | ||
| 538 | -{ | ||
| 539 | - fprintf(stderr, "%s: %s: %s\n", prog, msg, strerror(errno)); | ||
| 540 | -} | ||
| 541 | - |
trunk/research/st-1.9/examples/res.c
已删除
100644 → 0
| 1 | -/* | ||
| 2 | - * Copyright (c) 1985, 1988, 1993 | ||
| 3 | - * The Regents of the University of California. All rights reserved. | ||
| 4 | - * | ||
| 5 | - * Redistribution and use in source and binary forms, with or without | ||
| 6 | - * modification, are permitted provided that the following conditions | ||
| 7 | - * are met: | ||
| 8 | - * 1. Redistributions of source code must retain the above copyright | ||
| 9 | - * notice, this list of conditions and the following disclaimer. | ||
| 10 | - * 2. Redistributions in binary form must reproduce the above copyright | ||
| 11 | - * notice, this list of conditions and the following disclaimer in the | ||
| 12 | - * documentation and/or other materials provided with the distribution. | ||
| 13 | - * 3. All advertising materials mentioning features or use of this software | ||
| 14 | - * must display the following acknowledgement: | ||
| 15 | - * This product includes software developed by the University of | ||
| 16 | - * California, Berkeley and its contributors. | ||
| 17 | - * 4. Neither the name of the University nor the names of its contributors | ||
| 18 | - * may be used to endorse or promote products derived from this software | ||
| 19 | - * without specific prior written permission. | ||
| 20 | - * | ||
| 21 | - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | ||
| 22 | - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||
| 23 | - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | ||
| 24 | - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | ||
| 25 | - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | ||
| 26 | - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | ||
| 27 | - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | ||
| 28 | - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | ||
| 29 | - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | ||
| 30 | - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | ||
| 31 | - * SUCH DAMAGE. | ||
| 32 | - * | ||
| 33 | - * Portions created by SGI are Copyright (C) 2000 Silicon Graphics, Inc. | ||
| 34 | - * All Rights Reserved. | ||
| 35 | - * | ||
| 36 | - * Redistribution and use in source and binary forms, with or without | ||
| 37 | - * modification, are permitted provided that the following conditions | ||
| 38 | - * are met: | ||
| 39 | - * | ||
| 40 | - * 1. Redistributions of source code must retain the above copyright | ||
| 41 | - * notice, this list of conditions and the following disclaimer. | ||
| 42 | - * 2. Redistributions in binary form must reproduce the above copyright | ||
| 43 | - * notice, this list of conditions and the following disclaimer in the | ||
| 44 | - * documentation and/or other materials provided with the distribution. | ||
| 45 | - * 3. Neither the name of Silicon Graphics, Inc. nor the names of its | ||
| 46 | - * contributors may be used to endorse or promote products derived from | ||
| 47 | - * this software without specific prior written permission. | ||
| 48 | - * | ||
| 49 | - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
| 50 | - * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
| 51 | - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||
| 52 | - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||
| 53 | - * HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
| 54 | - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED | ||
| 55 | - * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | ||
| 56 | - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | ||
| 57 | - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | ||
| 58 | - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
| 59 | - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
| 60 | - */ | ||
| 61 | - | ||
| 62 | -#if defined (DARWIN) | ||
| 63 | -#define BIND_8_COMPAT | ||
| 64 | -#endif | ||
| 65 | - | ||
| 66 | -#include <stdio.h> | ||
| 67 | -#include <string.h> | ||
| 68 | -#include <unistd.h> | ||
| 69 | -#include <sys/types.h> | ||
| 70 | -#include <sys/socket.h> | ||
| 71 | -#include <netinet/in.h> | ||
| 72 | -#include <arpa/nameser.h> | ||
| 73 | -#include <resolv.h> | ||
| 74 | -#include <netdb.h> | ||
| 75 | -#include <errno.h> | ||
| 76 | -#include "st.h" | ||
| 77 | - | ||
| 78 | -#define MAXPACKET 1024 | ||
| 79 | - | ||
| 80 | -#if !defined(NETDB_INTERNAL) && defined(h_NETDB_INTERNAL) | ||
| 81 | -#define NETDB_INTERNAL h_NETDB_INTERNAL | ||
| 82 | -#endif | ||
| 83 | - | ||
| 84 | -/* New in Solaris 7 */ | ||
| 85 | -#if !defined(_getshort) && defined(ns_get16) | ||
| 86 | -#define _getshort(cp) ns_get16(cp) | ||
| 87 | -#endif | ||
| 88 | - | ||
| 89 | -typedef union { | ||
| 90 | - HEADER hdr; | ||
| 91 | - u_char buf[MAXPACKET]; | ||
| 92 | -} querybuf_t; | ||
| 93 | - | ||
| 94 | - | ||
| 95 | -static int parse_answer(querybuf_t *ans, int len, struct in_addr *addr) | ||
| 96 | -{ | ||
| 97 | - char buf[MAXPACKET]; | ||
| 98 | - HEADER *ahp; | ||
| 99 | - u_char *cp, *eoa; | ||
| 100 | - int type, n; | ||
| 101 | - | ||
| 102 | - ahp = &ans->hdr; | ||
| 103 | - eoa = ans->buf + len; | ||
| 104 | - cp = ans->buf + sizeof(HEADER); | ||
| 105 | - | ||
| 106 | - while (ahp->qdcount > 0) { | ||
| 107 | - ahp->qdcount--; | ||
| 108 | - cp += dn_skipname(cp, eoa) + QFIXEDSZ; | ||
| 109 | - } | ||
| 110 | - while (ahp->ancount > 0 && cp < eoa) { | ||
| 111 | - ahp->ancount--; | ||
| 112 | - if ((n = dn_expand(ans->buf, eoa, cp, buf, sizeof(buf))) < 0) | ||
| 113 | - break; | ||
| 114 | - cp += n; | ||
| 115 | - type = _getshort(cp); | ||
| 116 | - cp += 8; | ||
| 117 | - n = _getshort(cp); | ||
| 118 | - cp += 2; | ||
| 119 | - if (type == T_CNAME) { | ||
| 120 | - cp += n; | ||
| 121 | - continue; | ||
| 122 | - } | ||
| 123 | - memcpy(addr, cp, n); | ||
| 124 | - return 0; | ||
| 125 | - } | ||
| 126 | - | ||
| 127 | - h_errno = TRY_AGAIN; | ||
| 128 | - return -1; | ||
| 129 | -} | ||
| 130 | - | ||
| 131 | - | ||
| 132 | -static int query_domain(st_netfd_t nfd, const char *name, struct in_addr *addr, | ||
| 133 | - st_utime_t timeout) | ||
| 134 | -{ | ||
| 135 | - querybuf_t qbuf; | ||
| 136 | - u_char *buf = qbuf.buf; | ||
| 137 | - HEADER *hp = &qbuf.hdr; | ||
| 138 | - int blen = sizeof(qbuf); | ||
| 139 | - int i, len, id; | ||
| 140 | - | ||
| 141 | - for (i = 0; i < _res.nscount; i++) { | ||
| 142 | - len = res_mkquery(QUERY, name, C_IN, T_A, NULL, 0, NULL, buf, blen); | ||
| 143 | - if (len <= 0) { | ||
| 144 | - h_errno = NO_RECOVERY; | ||
| 145 | - return -1; | ||
| 146 | - } | ||
| 147 | - id = hp->id; | ||
| 148 | - | ||
| 149 | - if (st_sendto(nfd, buf, len, (struct sockaddr *)&(_res.nsaddr_list[i]), | ||
| 150 | - sizeof(struct sockaddr), timeout) != len) { | ||
| 151 | - h_errno = NETDB_INTERNAL; | ||
| 152 | - /* EINTR means interrupt by other thread, NOT by a caught signal */ | ||
| 153 | - if (errno == EINTR) | ||
| 154 | - return -1; | ||
| 155 | - continue; | ||
| 156 | - } | ||
| 157 | - | ||
| 158 | - /* Wait for reply */ | ||
| 159 | - do { | ||
| 160 | - len = st_recvfrom(nfd, buf, blen, NULL, NULL, timeout); | ||
| 161 | - if (len <= 0) | ||
| 162 | - break; | ||
| 163 | - } while (id != hp->id); | ||
| 164 | - | ||
| 165 | - if (len < HFIXEDSZ) { | ||
| 166 | - h_errno = NETDB_INTERNAL; | ||
| 167 | - if (len >= 0) | ||
| 168 | - errno = EMSGSIZE; | ||
| 169 | - else if (errno == EINTR) /* see the comment above */ | ||
| 170 | - return -1; | ||
| 171 | - continue; | ||
| 172 | - } | ||
| 173 | - | ||
| 174 | - hp->ancount = ntohs(hp->ancount); | ||
| 175 | - hp->qdcount = ntohs(hp->qdcount); | ||
| 176 | - if ((hp->rcode != NOERROR) || (hp->ancount == 0)) { | ||
| 177 | - switch (hp->rcode) { | ||
| 178 | - case NXDOMAIN: | ||
| 179 | - h_errno = HOST_NOT_FOUND; | ||
| 180 | - break; | ||
| 181 | - case SERVFAIL: | ||
| 182 | - h_errno = TRY_AGAIN; | ||
| 183 | - break; | ||
| 184 | - case NOERROR: | ||
| 185 | - h_errno = NO_DATA; | ||
| 186 | - break; | ||
| 187 | - case FORMERR: | ||
| 188 | - case NOTIMP: | ||
| 189 | - case REFUSED: | ||
| 190 | - default: | ||
| 191 | - h_errno = NO_RECOVERY; | ||
| 192 | - } | ||
| 193 | - continue; | ||
| 194 | - } | ||
| 195 | - | ||
| 196 | - if (parse_answer(&qbuf, len, addr) == 0) | ||
| 197 | - return 0; | ||
| 198 | - } | ||
| 199 | - | ||
| 200 | - return -1; | ||
| 201 | -} | ||
| 202 | - | ||
| 203 | - | ||
| 204 | -#define CLOSE_AND_RETURN(ret) \ | ||
| 205 | - { \ | ||
| 206 | - n = errno; \ | ||
| 207 | - st_netfd_close(nfd); \ | ||
| 208 | - errno = n; \ | ||
| 209 | - return (ret); \ | ||
| 210 | - } | ||
| 211 | - | ||
| 212 | - | ||
| 213 | -int dns_getaddr(const char *host, struct in_addr *addr, st_utime_t timeout) | ||
| 214 | -{ | ||
| 215 | - char name[MAXDNAME], **domain; | ||
| 216 | - const char *cp; | ||
| 217 | - int s, n, maxlen, dots; | ||
| 218 | - int trailing_dot, tried_as_is; | ||
| 219 | - st_netfd_t nfd; | ||
| 220 | - | ||
| 221 | - if ((_res.options & RES_INIT) == 0 && res_init() == -1) { | ||
| 222 | - h_errno = NETDB_INTERNAL; | ||
| 223 | - return -1; | ||
| 224 | - } | ||
| 225 | - if (_res.options & RES_USEVC) { | ||
| 226 | - h_errno = NETDB_INTERNAL; | ||
| 227 | - errno = ENOSYS; | ||
| 228 | - return -1; | ||
| 229 | - } | ||
| 230 | - if (!host || *host == '\0') { | ||
| 231 | - h_errno = HOST_NOT_FOUND; | ||
| 232 | - return -1; | ||
| 233 | - } | ||
| 234 | - | ||
| 235 | - /* Create UDP socket */ | ||
| 236 | - if ((s = socket(PF_INET, SOCK_DGRAM, 0)) < 0) { | ||
| 237 | - h_errno = NETDB_INTERNAL; | ||
| 238 | - return -1; | ||
| 239 | - } | ||
| 240 | - if ((nfd = st_netfd_open_socket(s)) == NULL) { | ||
| 241 | - h_errno = NETDB_INTERNAL; | ||
| 242 | - n = errno; | ||
| 243 | - close(s); | ||
| 244 | - errno = n; | ||
| 245 | - return -1; | ||
| 246 | - } | ||
| 247 | - | ||
| 248 | - maxlen = sizeof(name) - 1; | ||
| 249 | - n = 0; | ||
| 250 | - dots = 0; | ||
| 251 | - trailing_dot = 0; | ||
| 252 | - tried_as_is = 0; | ||
| 253 | - | ||
| 254 | - for (cp = host; *cp && n < maxlen; cp++) { | ||
| 255 | - dots += (*cp == '.'); | ||
| 256 | - name[n++] = *cp; | ||
| 257 | - } | ||
| 258 | - if (name[n - 1] == '.') | ||
| 259 | - trailing_dot = 1; | ||
| 260 | - | ||
| 261 | - /* | ||
| 262 | - * If there are dots in the name already, let's just give it a try | ||
| 263 | - * 'as is'. The threshold can be set with the "ndots" option. | ||
| 264 | - */ | ||
| 265 | - if (dots >= _res.ndots) { | ||
| 266 | - if (query_domain(nfd, host, addr, timeout) == 0) | ||
| 267 | - CLOSE_AND_RETURN(0); | ||
| 268 | - if (h_errno == NETDB_INTERNAL && errno == EINTR) | ||
| 269 | - CLOSE_AND_RETURN(-1); | ||
| 270 | - tried_as_is = 1; | ||
| 271 | - } | ||
| 272 | - | ||
| 273 | - /* | ||
| 274 | - * We do at least one level of search if | ||
| 275 | - * - there is no dot and RES_DEFNAME is set, or | ||
| 276 | - * - there is at least one dot, there is no trailing dot, | ||
| 277 | - * and RES_DNSRCH is set. | ||
| 278 | - */ | ||
| 279 | - if ((!dots && (_res.options & RES_DEFNAMES)) || | ||
| 280 | - (dots && !trailing_dot && (_res.options & RES_DNSRCH))) { | ||
| 281 | - name[n++] = '.'; | ||
| 282 | - for (domain = _res.dnsrch; *domain; domain++) { | ||
| 283 | - strncpy(name + n, *domain, maxlen - n); | ||
| 284 | - if (query_domain(nfd, name, addr, timeout) == 0) | ||
| 285 | - CLOSE_AND_RETURN(0); | ||
| 286 | - if (h_errno == NETDB_INTERNAL && errno == EINTR) | ||
| 287 | - CLOSE_AND_RETURN(-1); | ||
| 288 | - if (!(_res.options & RES_DNSRCH)) | ||
| 289 | - break; | ||
| 290 | - } | ||
| 291 | - } | ||
| 292 | - | ||
| 293 | - /* | ||
| 294 | - * If we have not already tried the name "as is", do that now. | ||
| 295 | - * note that we do this regardless of how many dots were in the | ||
| 296 | - * name or whether it ends with a dot. | ||
| 297 | - */ | ||
| 298 | - if (!tried_as_is) { | ||
| 299 | - if (query_domain(nfd, host, addr, timeout) == 0) | ||
| 300 | - CLOSE_AND_RETURN(0); | ||
| 301 | - } | ||
| 302 | - | ||
| 303 | - CLOSE_AND_RETURN(-1); | ||
| 304 | -} | ||
| 305 | - |
trunk/research/st-1.9/examples/server.c
已删除
100644 → 0
| 1 | -/* | ||
| 2 | - * Portions created by SGI are Copyright (C) 2000 Silicon Graphics, Inc. | ||
| 3 | - * All Rights Reserved. | ||
| 4 | - * | ||
| 5 | - * Redistribution and use in source and binary forms, with or without | ||
| 6 | - * modification, are permitted provided that the following conditions | ||
| 7 | - * are met: | ||
| 8 | - * | ||
| 9 | - * 1. Redistributions of source code must retain the above copyright | ||
| 10 | - * notice, this list of conditions and the following disclaimer. | ||
| 11 | - * 2. Redistributions in binary form must reproduce the above copyright | ||
| 12 | - * notice, this list of conditions and the following disclaimer in the | ||
| 13 | - * documentation and/or other materials provided with the distribution. | ||
| 14 | - * 3. Neither the name of Silicon Graphics, Inc. nor the names of its | ||
| 15 | - * contributors may be used to endorse or promote products derived from | ||
| 16 | - * this software without specific prior written permission. | ||
| 17 | - * | ||
| 18 | - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
| 19 | - * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
| 20 | - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||
| 21 | - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||
| 22 | - * HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
| 23 | - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED | ||
| 24 | - * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | ||
| 25 | - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | ||
| 26 | - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | ||
| 27 | - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
| 28 | - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
| 29 | - */ | ||
| 30 | - | ||
| 31 | -#include <stdio.h> | ||
| 32 | -#include <stdlib.h> | ||
| 33 | -#include <string.h> | ||
| 34 | -#include <unistd.h> | ||
| 35 | -#include <time.h> | ||
| 36 | -#include <sys/types.h> | ||
| 37 | -#include <sys/stat.h> | ||
| 38 | -#include <sys/socket.h> | ||
| 39 | -#include <sys/wait.h> | ||
| 40 | -#include <netinet/in.h> | ||
| 41 | -#include <arpa/inet.h> | ||
| 42 | -#include <netdb.h> | ||
| 43 | -#include <fcntl.h> | ||
| 44 | -#include <signal.h> | ||
| 45 | -#include <pwd.h> | ||
| 46 | -#include "st.h" | ||
| 47 | - | ||
| 48 | - | ||
| 49 | -/****************************************************************** | ||
| 50 | - * Server configuration parameters | ||
| 51 | - */ | ||
| 52 | - | ||
| 53 | -/* Log files */ | ||
| 54 | -#define PID_FILE "pid" | ||
| 55 | -#define ERRORS_FILE "errors" | ||
| 56 | -#define ACCESS_FILE "access" | ||
| 57 | - | ||
| 58 | -/* Default server port */ | ||
| 59 | -#define SERV_PORT_DEFAULT 8000 | ||
| 60 | - | ||
| 61 | -/* Socket listen queue size */ | ||
| 62 | -#define LISTENQ_SIZE_DEFAULT 256 | ||
| 63 | - | ||
| 64 | -/* Max number of listening sockets ("hardware virtual servers") */ | ||
| 65 | -#define MAX_BIND_ADDRS 16 | ||
| 66 | - | ||
| 67 | -/* Max number of "spare" threads per process per socket */ | ||
| 68 | -#define MAX_WAIT_THREADS_DEFAULT 8 | ||
| 69 | - | ||
| 70 | -/* Number of file descriptors needed to handle one client session */ | ||
| 71 | -#define FD_PER_THREAD 2 | ||
| 72 | - | ||
| 73 | -/* Access log buffer flushing interval (in seconds) */ | ||
| 74 | -#define ACCLOG_FLUSH_INTERVAL 30 | ||
| 75 | - | ||
| 76 | -/* Request read timeout (in seconds) */ | ||
| 77 | -#define REQUEST_TIMEOUT 30 | ||
| 78 | - | ||
| 79 | - | ||
| 80 | -/****************************************************************** | ||
| 81 | - * Global data | ||
| 82 | - */ | ||
| 83 | - | ||
| 84 | -struct socket_info { | ||
| 85 | - st_netfd_t nfd; /* Listening socket */ | ||
| 86 | - char *addr; /* Bind address */ | ||
| 87 | - unsigned int port; /* Port */ | ||
| 88 | - int wait_threads; /* Number of threads waiting to accept */ | ||
| 89 | - int busy_threads; /* Number of threads processing request */ | ||
| 90 | - int rqst_count; /* Total number of processed requests */ | ||
| 91 | -} srv_socket[MAX_BIND_ADDRS]; /* Array of listening sockets */ | ||
| 92 | - | ||
| 93 | -static int sk_count = 0; /* Number of listening sockets */ | ||
| 94 | - | ||
| 95 | -static int vp_count = 0; /* Number of server processes (VPs) */ | ||
| 96 | -static pid_t *vp_pids; /* Array of VP pids */ | ||
| 97 | - | ||
| 98 | -static int my_index = -1; /* Current process index */ | ||
| 99 | -static pid_t my_pid = -1; /* Current process pid */ | ||
| 100 | - | ||
| 101 | -static st_netfd_t sig_pipe[2]; /* Signal pipe */ | ||
| 102 | - | ||
| 103 | -/* | ||
| 104 | - * Configuration flags/parameters | ||
| 105 | - */ | ||
| 106 | -static int interactive_mode = 0; | ||
| 107 | -static int serialize_accept = 0; | ||
| 108 | -static int log_access = 0; | ||
| 109 | -static char *logdir = NULL; | ||
| 110 | -static char *username = NULL; | ||
| 111 | -static int listenq_size = LISTENQ_SIZE_DEFAULT; | ||
| 112 | -static int errfd = STDERR_FILENO; | ||
| 113 | - | ||
| 114 | -/* | ||
| 115 | - * Thread throttling parameters (all numbers are per listening socket). | ||
| 116 | - * Zero values mean use default. | ||
| 117 | - */ | ||
| 118 | -static int max_threads = 0; /* Max number of threads */ | ||
| 119 | -static int max_wait_threads = 0; /* Max number of "spare" threads */ | ||
| 120 | -static int min_wait_threads = 2; /* Min number of "spare" threads */ | ||
| 121 | - | ||
| 122 | - | ||
| 123 | -/****************************************************************** | ||
| 124 | - * Useful macros | ||
| 125 | - */ | ||
| 126 | - | ||
| 127 | -#ifndef INADDR_NONE | ||
| 128 | -#define INADDR_NONE 0xffffffff | ||
| 129 | -#endif | ||
| 130 | - | ||
| 131 | -#define SEC2USEC(s) ((s)*1000000LL) | ||
| 132 | - | ||
| 133 | -#define WAIT_THREADS(i) (srv_socket[i].wait_threads) | ||
| 134 | -#define BUSY_THREADS(i) (srv_socket[i].busy_threads) | ||
| 135 | -#define TOTAL_THREADS(i) (WAIT_THREADS(i) + BUSY_THREADS(i)) | ||
| 136 | -#define RQST_COUNT(i) (srv_socket[i].rqst_count) | ||
| 137 | - | ||
| 138 | - | ||
| 139 | -/****************************************************************** | ||
| 140 | - * Forward declarations | ||
| 141 | - */ | ||
| 142 | - | ||
| 143 | -static void usage(const char *progname); | ||
| 144 | -static void parse_arguments(int argc, char *argv[]); | ||
| 145 | -static void start_daemon(void); | ||
| 146 | -static void set_thread_throttling(void); | ||
| 147 | -static void create_listeners(void); | ||
| 148 | -static void change_user(void); | ||
| 149 | -static void open_log_files(void); | ||
| 150 | -static void start_processes(void); | ||
| 151 | -static void wdog_sighandler(int signo); | ||
| 152 | -static void child_sighandler(int signo); | ||
| 153 | -static void install_sighandlers(void); | ||
| 154 | -static void start_threads(void); | ||
| 155 | -static void *process_signals(void *arg); | ||
| 156 | -static void *flush_acclog_buffer(void *arg); | ||
| 157 | -static void *handle_connections(void *arg); | ||
| 158 | -static void dump_server_info(void); | ||
| 159 | - | ||
| 160 | -static void Signal(int sig, void (*handler)(int)); | ||
| 161 | -static int cpu_count(void); | ||
| 162 | - | ||
| 163 | -extern void handle_session(long srv_socket_index, st_netfd_t cli_nfd); | ||
| 164 | -extern void load_configs(void); | ||
| 165 | -extern void logbuf_open(void); | ||
| 166 | -extern void logbuf_flush(void); | ||
| 167 | -extern void logbuf_close(void); | ||
| 168 | - | ||
| 169 | -/* Error reporting functions defined in the error.c file */ | ||
| 170 | -extern void err_sys_report(int fd, const char *fmt, ...); | ||
| 171 | -extern void err_sys_quit(int fd, const char *fmt, ...); | ||
| 172 | -extern void err_sys_dump(int fd, const char *fmt, ...); | ||
| 173 | -extern void err_report(int fd, const char *fmt, ...); | ||
| 174 | -extern void err_quit(int fd, const char *fmt, ...); | ||
| 175 | - | ||
| 176 | - | ||
| 177 | -/* | ||
| 178 | - * General server example: accept a client connection and do something. | ||
| 179 | - * This program just outputs a short HTML page, but can be easily adapted | ||
| 180 | - * to do other things. | ||
| 181 | - * | ||
| 182 | - * This server creates a constant number of processes ("virtual processors" | ||
| 183 | - * or VPs) and replaces them when they die. Each virtual processor manages | ||
| 184 | - * its own independent set of state threads (STs), the number of which varies | ||
| 185 | - * with load against the server. Each state thread listens to exactly one | ||
| 186 | - * listening socket. The initial process becomes the watchdog, waiting for | ||
| 187 | - * children (VPs) to die or for a signal requesting termination or restart. | ||
| 188 | - * Upon receiving a restart signal (SIGHUP), all VPs close and then reopen | ||
| 189 | - * log files and reload configuration. All currently active connections remain | ||
| 190 | - * active. It is assumed that new configuration affects only request | ||
| 191 | - * processing and not the general server parameters such as number of VPs, | ||
| 192 | - * thread limits, bind addresses, etc. Those are specified as command line | ||
| 193 | - * arguments, so the server has to be stopped and then started again in order | ||
| 194 | - * to change them. | ||
| 195 | - * | ||
| 196 | - * Each state thread loops processing connections from a single listening | ||
| 197 | - * socket. Only one ST runs on a VP at a time, and VPs do not share memory, | ||
| 198 | - * so no mutual exclusion locking is necessary on any data, and the entire | ||
| 199 | - * server is free to use all the static variables and non-reentrant library | ||
| 200 | - * functions it wants, greatly simplifying programming and debugging and | ||
| 201 | - * increasing performance (for example, it is safe to ++ and -- all global | ||
| 202 | - * counters or call inet_ntoa(3) without any mutexes). The current thread on | ||
| 203 | - * each VP maintains equilibrium on that VP, starting a new thread or | ||
| 204 | - * terminating itself if the number of spare threads exceeds the lower or | ||
| 205 | - * upper limit. | ||
| 206 | - * | ||
| 207 | - * All I/O operations on sockets must use the State Thread library's I/O | ||
| 208 | - * functions because only those functions prevent blocking of the entire VP | ||
| 209 | - * process and perform state thread scheduling. | ||
| 210 | - */ | ||
| 211 | -int main(int argc, char *argv[]) | ||
| 212 | -{ | ||
| 213 | - /* Parse command-line options */ | ||
| 214 | - parse_arguments(argc, argv); | ||
| 215 | - | ||
| 216 | - /* Allocate array of server pids */ | ||
| 217 | - if ((vp_pids = calloc(vp_count, sizeof(pid_t))) == NULL) | ||
| 218 | - err_sys_quit(errfd, "ERROR: calloc failed"); | ||
| 219 | - | ||
| 220 | - /* Start the daemon */ | ||
| 221 | - if (!interactive_mode) | ||
| 222 | - start_daemon(); | ||
| 223 | - | ||
| 224 | - /* Initialize the ST library */ | ||
| 225 | - if (st_init() < 0) | ||
| 226 | - err_sys_quit(errfd, "ERROR: initialization failed: st_init"); | ||
| 227 | - | ||
| 228 | - /* Set thread throttling parameters */ | ||
| 229 | - set_thread_throttling(); | ||
| 230 | - | ||
| 231 | - /* Create listening sockets */ | ||
| 232 | - create_listeners(); | ||
| 233 | - | ||
| 234 | - /* Change the user */ | ||
| 235 | - if (username) | ||
| 236 | - change_user(); | ||
| 237 | - | ||
| 238 | - /* Open log files */ | ||
| 239 | - open_log_files(); | ||
| 240 | - | ||
| 241 | - /* Start server processes (VPs) */ | ||
| 242 | - start_processes(); | ||
| 243 | - | ||
| 244 | - /* Turn time caching on */ | ||
| 245 | - st_timecache_set(1); | ||
| 246 | - | ||
| 247 | - /* Install signal handlers */ | ||
| 248 | - install_sighandlers(); | ||
| 249 | - | ||
| 250 | - /* Load configuration from config files */ | ||
| 251 | - load_configs(); | ||
| 252 | - | ||
| 253 | - /* Start all threads */ | ||
| 254 | - start_threads(); | ||
| 255 | - | ||
| 256 | - /* Become a signal processing thread */ | ||
| 257 | - process_signals(NULL); | ||
| 258 | - | ||
| 259 | - /* NOTREACHED */ | ||
| 260 | - return 1; | ||
| 261 | -} | ||
| 262 | - | ||
| 263 | - | ||
| 264 | -/******************************************************************/ | ||
| 265 | - | ||
| 266 | -static void usage(const char *progname) | ||
| 267 | -{ | ||
| 268 | - fprintf(stderr, "Usage: %s -l <log_directory> [<options>]\n\n" | ||
| 269 | - "Possible options:\n\n" | ||
| 270 | - "\t-b <host>:<port> Bind to specified address. Multiple" | ||
| 271 | - " addresses\n" | ||
| 272 | - "\t are permitted.\n" | ||
| 273 | - "\t-p <num_processes> Create specified number of processes.\n" | ||
| 274 | - "\t-t <min_thr>:<max_thr> Specify thread limits per listening" | ||
| 275 | - " socket\n" | ||
| 276 | - "\t across all processes.\n" | ||
| 277 | - "\t-u <user> Change server's user id to specified" | ||
| 278 | - " value.\n" | ||
| 279 | - "\t-q <backlog> Set max length of pending connections" | ||
| 280 | - " queue.\n" | ||
| 281 | - "\t-a Enable access logging.\n" | ||
| 282 | - "\t-i Run in interactive mode.\n" | ||
| 283 | - "\t-S Serialize all accept() calls.\n" | ||
| 284 | - "\t-h Print this message.\n", | ||
| 285 | - progname); | ||
| 286 | - exit(1); | ||
| 287 | -} | ||
| 288 | - | ||
| 289 | - | ||
| 290 | -/******************************************************************/ | ||
| 291 | - | ||
| 292 | -static void parse_arguments(int argc, char *argv[]) | ||
| 293 | -{ | ||
| 294 | - extern char *optarg; | ||
| 295 | - int opt; | ||
| 296 | - char *c; | ||
| 297 | - | ||
| 298 | - while ((opt = getopt(argc, argv, "b:p:l:t:u:q:aiSh")) != EOF) { | ||
| 299 | - switch (opt) { | ||
| 300 | - case 'b': | ||
| 301 | - if (sk_count >= MAX_BIND_ADDRS) | ||
| 302 | - err_quit(errfd, "ERROR: max number of bind addresses (%d) exceeded", | ||
| 303 | - MAX_BIND_ADDRS); | ||
| 304 | - if ((c = strdup(optarg)) == NULL) | ||
| 305 | - err_sys_quit(errfd, "ERROR: strdup"); | ||
| 306 | - srv_socket[sk_count++].addr = c; | ||
| 307 | - break; | ||
| 308 | - case 'p': | ||
| 309 | - vp_count = atoi(optarg); | ||
| 310 | - if (vp_count < 1) | ||
| 311 | - err_quit(errfd, "ERROR: invalid number of processes: %s", optarg); | ||
| 312 | - break; | ||
| 313 | - case 'l': | ||
| 314 | - logdir = optarg; | ||
| 315 | - break; | ||
| 316 | - case 't': | ||
| 317 | - max_wait_threads = (int) strtol(optarg, &c, 10); | ||
| 318 | - if (*c++ == ':') | ||
| 319 | - max_threads = atoi(c); | ||
| 320 | - if (max_wait_threads < 0 || max_threads < 0) | ||
| 321 | - err_quit(errfd, "ERROR: invalid number of threads: %s", optarg); | ||
| 322 | - break; | ||
| 323 | - case 'u': | ||
| 324 | - username = optarg; | ||
| 325 | - break; | ||
| 326 | - case 'q': | ||
| 327 | - listenq_size = atoi(optarg); | ||
| 328 | - if (listenq_size < 1) | ||
| 329 | - err_quit(errfd, "ERROR: invalid listen queue size: %s", optarg); | ||
| 330 | - break; | ||
| 331 | - case 'a': | ||
| 332 | - log_access = 1; | ||
| 333 | - break; | ||
| 334 | - case 'i': | ||
| 335 | - interactive_mode = 1; | ||
| 336 | - break; | ||
| 337 | - case 'S': | ||
| 338 | - /* | ||
| 339 | - * Serialization decision is tricky on some platforms. For example, | ||
| 340 | - * Solaris 2.6 and above has kernel sockets implementation, so supposedly | ||
| 341 | - * there is no need for serialization. The ST library may be compiled | ||
| 342 | - * on one OS version, but used on another, so the need for serialization | ||
| 343 | - * should be determined at run time by the application. Since it's just | ||
| 344 | - * an example, the serialization decision is left up to user. | ||
| 345 | - * Only on platforms where the serialization is never needed on any OS | ||
| 346 | - * version st_netfd_serialize_accept() is a no-op. | ||
| 347 | - */ | ||
| 348 | - serialize_accept = 1; | ||
| 349 | - break; | ||
| 350 | - case 'h': | ||
| 351 | - case '?': | ||
| 352 | - usage(argv[0]); | ||
| 353 | - } | ||
| 354 | - } | ||
| 355 | - | ||
| 356 | - if (logdir == NULL && !interactive_mode) { | ||
| 357 | - err_report(errfd, "ERROR: logging directory is required\n"); | ||
| 358 | - usage(argv[0]); | ||
| 359 | - } | ||
| 360 | - | ||
| 361 | - if (getuid() == 0 && username == NULL) | ||
| 362 | - err_report(errfd, "WARNING: running as super-user!"); | ||
| 363 | - | ||
| 364 | - if (vp_count == 0 && (vp_count = cpu_count()) < 1) | ||
| 365 | - vp_count = 1; | ||
| 366 | - | ||
| 367 | - if (sk_count == 0) { | ||
| 368 | - sk_count = 1; | ||
| 369 | - srv_socket[0].addr = "0.0.0.0"; | ||
| 370 | - } | ||
| 371 | -} | ||
| 372 | - | ||
| 373 | - | ||
| 374 | -/******************************************************************/ | ||
| 375 | - | ||
| 376 | -static void start_daemon(void) | ||
| 377 | -{ | ||
| 378 | - pid_t pid; | ||
| 379 | - | ||
| 380 | - /* Start forking */ | ||
| 381 | - if ((pid = fork()) < 0) | ||
| 382 | - err_sys_quit(errfd, "ERROR: fork"); | ||
| 383 | - if (pid > 0) | ||
| 384 | - exit(0); /* parent */ | ||
| 385 | - | ||
| 386 | - /* First child process */ | ||
| 387 | - setsid(); /* become session leader */ | ||
| 388 | - | ||
| 389 | - if ((pid = fork()) < 0) | ||
| 390 | - err_sys_quit(errfd, "ERROR: fork"); | ||
| 391 | - if (pid > 0) /* first child */ | ||
| 392 | - exit(0); | ||
| 393 | - | ||
| 394 | - umask(022); | ||
| 395 | - | ||
| 396 | - if (chdir(logdir) < 0) | ||
| 397 | - err_sys_quit(errfd, "ERROR: can't change directory to %s: chdir", logdir); | ||
| 398 | -} | ||
| 399 | - | ||
| 400 | - | ||
| 401 | -/****************************************************************** | ||
| 402 | - * For simplicity, the minimal size of thread pool is considered | ||
| 403 | - * as a maximum number of spare threads (max_wait_threads) that | ||
| 404 | - * will be created upon server startup. The pool size can grow up | ||
| 405 | - * to the max_threads value. Note that this is a per listening | ||
| 406 | - * socket limit. It is also possible to limit the total number of | ||
| 407 | - * threads for all sockets rather than impose a per socket limit. | ||
| 408 | - */ | ||
| 409 | - | ||
| 410 | -static void set_thread_throttling(void) | ||
| 411 | -{ | ||
| 412 | - /* | ||
| 413 | - * Calculate total values across all processes. | ||
| 414 | - * All numbers are per listening socket. | ||
| 415 | - */ | ||
| 416 | - if (max_wait_threads == 0) | ||
| 417 | - max_wait_threads = MAX_WAIT_THREADS_DEFAULT * vp_count; | ||
| 418 | - /* Assuming that each client session needs FD_PER_THREAD file descriptors */ | ||
| 419 | - if (max_threads == 0) | ||
| 420 | - max_threads = (st_getfdlimit() * vp_count) / FD_PER_THREAD / sk_count; | ||
| 421 | - if (max_wait_threads > max_threads) | ||
| 422 | - max_wait_threads = max_threads; | ||
| 423 | - | ||
| 424 | - /* | ||
| 425 | - * Now calculate per-process values. | ||
| 426 | - */ | ||
| 427 | - if (max_wait_threads % vp_count) | ||
| 428 | - max_wait_threads = max_wait_threads / vp_count + 1; | ||
| 429 | - else | ||
| 430 | - max_wait_threads = max_wait_threads / vp_count; | ||
| 431 | - if (max_threads % vp_count) | ||
| 432 | - max_threads = max_threads / vp_count + 1; | ||
| 433 | - else | ||
| 434 | - max_threads = max_threads / vp_count; | ||
| 435 | - | ||
| 436 | - if (min_wait_threads > max_wait_threads) | ||
| 437 | - min_wait_threads = max_wait_threads; | ||
| 438 | -} | ||
| 439 | - | ||
| 440 | - | ||
| 441 | -/******************************************************************/ | ||
| 442 | - | ||
| 443 | -static void create_listeners(void) | ||
| 444 | -{ | ||
| 445 | - int i, n, sock; | ||
| 446 | - char *c; | ||
| 447 | - struct sockaddr_in serv_addr; | ||
| 448 | - struct hostent *hp; | ||
| 449 | - unsigned short port; | ||
| 450 | - | ||
| 451 | - for (i = 0; i < sk_count; i++) { | ||
| 452 | - port = 0; | ||
| 453 | - if ((c = strchr(srv_socket[i].addr, ':')) != NULL) { | ||
| 454 | - *c++ = '\0'; | ||
| 455 | - port = (unsigned short) atoi(c); | ||
| 456 | - } | ||
| 457 | - if (srv_socket[i].addr[0] == '\0') | ||
| 458 | - srv_socket[i].addr = "0.0.0.0"; | ||
| 459 | - if (port == 0) | ||
| 460 | - port = SERV_PORT_DEFAULT; | ||
| 461 | - | ||
| 462 | - /* Create server socket */ | ||
| 463 | - if ((sock = socket(PF_INET, SOCK_STREAM, 0)) < 0) | ||
| 464 | - err_sys_quit(errfd, "ERROR: can't create socket: socket"); | ||
| 465 | - n = 1; | ||
| 466 | - if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&n, sizeof(n)) < 0) | ||
| 467 | - err_sys_quit(errfd, "ERROR: can't set SO_REUSEADDR: setsockopt"); | ||
| 468 | - memset(&serv_addr, 0, sizeof(serv_addr)); | ||
| 469 | - serv_addr.sin_family = AF_INET; | ||
| 470 | - serv_addr.sin_port = htons(port); | ||
| 471 | - serv_addr.sin_addr.s_addr = inet_addr(srv_socket[i].addr); | ||
| 472 | - if (serv_addr.sin_addr.s_addr == INADDR_NONE) { | ||
| 473 | - /* not dotted-decimal */ | ||
| 474 | - if ((hp = gethostbyname(srv_socket[i].addr)) == NULL) | ||
| 475 | - err_quit(errfd, "ERROR: can't resolve address: %s", | ||
| 476 | - srv_socket[i].addr); | ||
| 477 | - memcpy(&serv_addr.sin_addr, hp->h_addr, hp->h_length); | ||
| 478 | - } | ||
| 479 | - srv_socket[i].port = port; | ||
| 480 | - | ||
| 481 | - /* Do bind and listen */ | ||
| 482 | - if (bind(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) | ||
| 483 | - err_sys_quit(errfd, "ERROR: can't bind to address %s, port %hu", | ||
| 484 | - srv_socket[i].addr, port); | ||
| 485 | - if (listen(sock, listenq_size) < 0) | ||
| 486 | - err_sys_quit(errfd, "ERROR: listen"); | ||
| 487 | - | ||
| 488 | - /* Create file descriptor object from OS socket */ | ||
| 489 | - if ((srv_socket[i].nfd = st_netfd_open_socket(sock)) == NULL) | ||
| 490 | - err_sys_quit(errfd, "ERROR: st_netfd_open_socket"); | ||
| 491 | - /* | ||
| 492 | - * On some platforms (e.g. IRIX, Linux) accept() serialization is never | ||
| 493 | - * needed for any OS version. In that case st_netfd_serialize_accept() | ||
| 494 | - * is just a no-op. Also see the comment above. | ||
| 495 | - */ | ||
| 496 | - if (serialize_accept && st_netfd_serialize_accept(srv_socket[i].nfd) < 0) | ||
| 497 | - err_sys_quit(errfd, "ERROR: st_netfd_serialize_accept"); | ||
| 498 | - } | ||
| 499 | -} | ||
| 500 | - | ||
| 501 | - | ||
| 502 | -/******************************************************************/ | ||
| 503 | - | ||
| 504 | -static void change_user(void) | ||
| 505 | -{ | ||
| 506 | - struct passwd *pw; | ||
| 507 | - | ||
| 508 | - if ((pw = getpwnam(username)) == NULL) | ||
| 509 | - err_quit(errfd, "ERROR: can't find user '%s': getpwnam failed", username); | ||
| 510 | - | ||
| 511 | - if (setgid(pw->pw_gid) < 0) | ||
| 512 | - err_sys_quit(errfd, "ERROR: can't change group id: setgid"); | ||
| 513 | - if (setuid(pw->pw_uid) < 0) | ||
| 514 | - err_sys_quit(errfd, "ERROR: can't change user id: setuid"); | ||
| 515 | - | ||
| 516 | - err_report(errfd, "INFO: changed process user id to '%s'", username); | ||
| 517 | -} | ||
| 518 | - | ||
| 519 | - | ||
| 520 | -/******************************************************************/ | ||
| 521 | - | ||
| 522 | -static void open_log_files(void) | ||
| 523 | -{ | ||
| 524 | - int fd; | ||
| 525 | - char str[32]; | ||
| 526 | - | ||
| 527 | - if (interactive_mode) | ||
| 528 | - return; | ||
| 529 | - | ||
| 530 | - /* Open access log */ | ||
| 531 | - if (log_access) | ||
| 532 | - logbuf_open(); | ||
| 533 | - | ||
| 534 | - /* Open and write pid to pid file */ | ||
| 535 | - if ((fd = open(PID_FILE, O_CREAT | O_WRONLY | O_TRUNC, 0644)) < 0) | ||
| 536 | - err_sys_quit(errfd, "ERROR: can't open pid file: open"); | ||
| 537 | - sprintf(str, "%d\n", (int)getpid()); | ||
| 538 | - if (write(fd, str, strlen(str)) != strlen(str)) | ||
| 539 | - err_sys_quit(errfd, "ERROR: can't write to pid file: write"); | ||
| 540 | - close(fd); | ||
| 541 | - | ||
| 542 | - /* Open error log file */ | ||
| 543 | - if ((fd = open(ERRORS_FILE, O_CREAT | O_WRONLY | O_APPEND, 0644)) < 0) | ||
| 544 | - err_sys_quit(errfd, "ERROR: can't open error log file: open"); | ||
| 545 | - errfd = fd; | ||
| 546 | - | ||
| 547 | - err_report(errfd, "INFO: starting the server..."); | ||
| 548 | -} | ||
| 549 | - | ||
| 550 | - | ||
| 551 | -/******************************************************************/ | ||
| 552 | - | ||
| 553 | -static void start_processes(void) | ||
| 554 | -{ | ||
| 555 | - int i, status; | ||
| 556 | - pid_t pid; | ||
| 557 | - sigset_t mask, omask; | ||
| 558 | - | ||
| 559 | - if (interactive_mode) { | ||
| 560 | - my_index = 0; | ||
| 561 | - my_pid = getpid(); | ||
| 562 | - return; | ||
| 563 | - } | ||
| 564 | - | ||
| 565 | - for (i = 0; i < vp_count; i++) { | ||
| 566 | - if ((pid = fork()) < 0) { | ||
| 567 | - err_sys_report(errfd, "ERROR: can't create process: fork"); | ||
| 568 | - if (i == 0) | ||
| 569 | - exit(1); | ||
| 570 | - err_report(errfd, "WARN: started only %d processes out of %d", i, | ||
| 571 | - vp_count); | ||
| 572 | - vp_count = i; | ||
| 573 | - break; | ||
| 574 | - } | ||
| 575 | - if (pid == 0) { | ||
| 576 | - my_index = i; | ||
| 577 | - my_pid = getpid(); | ||
| 578 | - /* Child returns to continue in main() */ | ||
| 579 | - return; | ||
| 580 | - } | ||
| 581 | - vp_pids[i] = pid; | ||
| 582 | - } | ||
| 583 | - | ||
| 584 | - /* | ||
| 585 | - * Parent process becomes a "watchdog" and never returns to main(). | ||
| 586 | - */ | ||
| 587 | - | ||
| 588 | - /* Install signal handlers */ | ||
| 589 | - Signal(SIGTERM, wdog_sighandler); /* terminate */ | ||
| 590 | - Signal(SIGHUP, wdog_sighandler); /* restart */ | ||
| 591 | - Signal(SIGUSR1, wdog_sighandler); /* dump info */ | ||
| 592 | - | ||
| 593 | - /* Now go to sleep waiting for a child termination or a signal */ | ||
| 594 | - for ( ; ; ) { | ||
| 595 | - if ((pid = wait(&status)) < 0) { | ||
| 596 | - if (errno == EINTR) | ||
| 597 | - continue; | ||
| 598 | - err_sys_quit(errfd, "ERROR: watchdog: wait"); | ||
| 599 | - } | ||
| 600 | - /* Find index of the exited child */ | ||
| 601 | - for (i = 0; i < vp_count; i++) { | ||
| 602 | - if (vp_pids[i] == pid) | ||
| 603 | - break; | ||
| 604 | - } | ||
| 605 | - | ||
| 606 | - /* Block signals while printing and forking */ | ||
| 607 | - sigemptyset(&mask); | ||
| 608 | - sigaddset(&mask, SIGTERM); | ||
| 609 | - sigaddset(&mask, SIGHUP); | ||
| 610 | - sigaddset(&mask, SIGUSR1); | ||
| 611 | - sigprocmask(SIG_BLOCK, &mask, &omask); | ||
| 612 | - | ||
| 613 | - if (WIFEXITED(status)) | ||
| 614 | - err_report(errfd, "WARN: watchdog: process %d (pid %d) exited" | ||
| 615 | - " with status %d", i, pid, WEXITSTATUS(status)); | ||
| 616 | - else if (WIFSIGNALED(status)) | ||
| 617 | - err_report(errfd, "WARN: watchdog: process %d (pid %d) terminated" | ||
| 618 | - " by signal %d", i, pid, WTERMSIG(status)); | ||
| 619 | - else if (WIFSTOPPED(status)) | ||
| 620 | - err_report(errfd, "WARN: watchdog: process %d (pid %d) stopped" | ||
| 621 | - " by signal %d", i, pid, WSTOPSIG(status)); | ||
| 622 | - else | ||
| 623 | - err_report(errfd, "WARN: watchdog: process %d (pid %d) terminated:" | ||
| 624 | - " unknown termination reason", i, pid); | ||
| 625 | - | ||
| 626 | - /* Fork another VP */ | ||
| 627 | - if ((pid = fork()) < 0) { | ||
| 628 | - err_sys_report(errfd, "ERROR: watchdog: can't create process: fork"); | ||
| 629 | - } else if (pid == 0) { | ||
| 630 | - my_index = i; | ||
| 631 | - my_pid = getpid(); | ||
| 632 | - /* Child returns to continue in main() */ | ||
| 633 | - return; | ||
| 634 | - } | ||
| 635 | - vp_pids[i] = pid; | ||
| 636 | - | ||
| 637 | - /* Restore the signal mask */ | ||
| 638 | - sigprocmask(SIG_SETMASK, &omask, NULL); | ||
| 639 | - } | ||
| 640 | -} | ||
| 641 | - | ||
| 642 | - | ||
| 643 | -/******************************************************************/ | ||
| 644 | - | ||
| 645 | -static void wdog_sighandler(int signo) | ||
| 646 | -{ | ||
| 647 | - int i, err; | ||
| 648 | - | ||
| 649 | - /* Save errno */ | ||
| 650 | - err = errno; | ||
| 651 | - /* Forward the signal to all children */ | ||
| 652 | - for (i = 0; i < vp_count; i++) { | ||
| 653 | - if (vp_pids[i] > 0) | ||
| 654 | - kill(vp_pids[i], signo); | ||
| 655 | - } | ||
| 656 | - /* | ||
| 657 | - * It is safe to do pretty much everything here because process is | ||
| 658 | - * sleeping in wait() which is async-safe. | ||
| 659 | - */ | ||
| 660 | - switch (signo) { | ||
| 661 | - case SIGHUP: | ||
| 662 | - err_report(errfd, "INFO: watchdog: caught SIGHUP"); | ||
| 663 | - /* Reopen log files - needed for log rotation */ | ||
| 664 | - if (log_access) { | ||
| 665 | - logbuf_close(); | ||
| 666 | - logbuf_open(); | ||
| 667 | - } | ||
| 668 | - close(errfd); | ||
| 669 | - if ((errfd = open(ERRORS_FILE, O_CREAT | O_WRONLY | O_APPEND, 0644)) < 0) | ||
| 670 | - err_sys_quit(STDERR_FILENO, "ERROR: watchdog: open"); | ||
| 671 | - break; | ||
| 672 | - case SIGTERM: | ||
| 673 | - /* Non-graceful termination */ | ||
| 674 | - err_report(errfd, "INFO: watchdog: caught SIGTERM, terminating"); | ||
| 675 | - unlink(PID_FILE); | ||
| 676 | - exit(0); | ||
| 677 | - case SIGUSR1: | ||
| 678 | - err_report(errfd, "INFO: watchdog: caught SIGUSR1"); | ||
| 679 | - break; | ||
| 680 | - default: | ||
| 681 | - err_report(errfd, "INFO: watchdog: caught signal %d", signo); | ||
| 682 | - } | ||
| 683 | - /* Restore errno */ | ||
| 684 | - errno = err; | ||
| 685 | -} | ||
| 686 | - | ||
| 687 | - | ||
| 688 | -/******************************************************************/ | ||
| 689 | - | ||
| 690 | -static void install_sighandlers(void) | ||
| 691 | -{ | ||
| 692 | - sigset_t mask; | ||
| 693 | - int p[2]; | ||
| 694 | - | ||
| 695 | - /* Create signal pipe */ | ||
| 696 | - if (pipe(p) < 0) | ||
| 697 | - err_sys_quit(errfd, "ERROR: process %d (pid %d): can't create" | ||
| 698 | - " signal pipe: pipe", my_index, my_pid); | ||
| 699 | - if ((sig_pipe[0] = st_netfd_open(p[0])) == NULL || | ||
| 700 | - (sig_pipe[1] = st_netfd_open(p[1])) == NULL) | ||
| 701 | - err_sys_quit(errfd, "ERROR: process %d (pid %d): can't create" | ||
| 702 | - " signal pipe: st_netfd_open", my_index, my_pid); | ||
| 703 | - | ||
| 704 | - /* Install signal handlers */ | ||
| 705 | - Signal(SIGTERM, child_sighandler); /* terminate */ | ||
| 706 | - Signal(SIGHUP, child_sighandler); /* restart */ | ||
| 707 | - Signal(SIGUSR1, child_sighandler); /* dump info */ | ||
| 708 | - | ||
| 709 | - /* Unblock signals */ | ||
| 710 | - sigemptyset(&mask); | ||
| 711 | - sigaddset(&mask, SIGTERM); | ||
| 712 | - sigaddset(&mask, SIGHUP); | ||
| 713 | - sigaddset(&mask, SIGUSR1); | ||
| 714 | - sigprocmask(SIG_UNBLOCK, &mask, NULL); | ||
| 715 | -} | ||
| 716 | - | ||
| 717 | - | ||
| 718 | -/******************************************************************/ | ||
| 719 | - | ||
| 720 | -static void child_sighandler(int signo) | ||
| 721 | -{ | ||
| 722 | - int err, fd; | ||
| 723 | - | ||
| 724 | - err = errno; | ||
| 725 | - fd = st_netfd_fileno(sig_pipe[1]); | ||
| 726 | - | ||
| 727 | - /* write() is async-safe */ | ||
| 728 | - if (write(fd, &signo, sizeof(int)) != sizeof(int)) | ||
| 729 | - err_sys_quit(errfd, "ERROR: process %d (pid %d): child's signal" | ||
| 730 | - " handler: write", my_index, my_pid); | ||
| 731 | - errno = err; | ||
| 732 | -} | ||
| 733 | - | ||
| 734 | - | ||
| 735 | -/****************************************************************** | ||
| 736 | - * The "main" function of the signal processing thread. | ||
| 737 | - */ | ||
| 738 | - | ||
| 739 | -/* ARGSUSED */ | ||
| 740 | -static void *process_signals(void *arg) | ||
| 741 | -{ | ||
| 742 | - int signo; | ||
| 743 | - | ||
| 744 | - for ( ; ; ) { | ||
| 745 | - /* Read the next signal from the signal pipe */ | ||
| 746 | - if (st_read(sig_pipe[0], &signo, sizeof(int), | ||
| 747 | - ST_UTIME_NO_TIMEOUT) != sizeof(int)) | ||
| 748 | - err_sys_quit(errfd, "ERROR: process %d (pid %d): signal processor:" | ||
| 749 | - " st_read", my_index, my_pid); | ||
| 750 | - | ||
| 751 | - switch (signo) { | ||
| 752 | - case SIGHUP: | ||
| 753 | - err_report(errfd, "INFO: process %d (pid %d): caught SIGHUP," | ||
| 754 | - " reloading configuration", my_index, my_pid); | ||
| 755 | - if (interactive_mode) { | ||
| 756 | - load_configs(); | ||
| 757 | - break; | ||
| 758 | - } | ||
| 759 | - /* Reopen log files - needed for log rotation */ | ||
| 760 | - if (log_access) { | ||
| 761 | - logbuf_flush(); | ||
| 762 | - logbuf_close(); | ||
| 763 | - logbuf_open(); | ||
| 764 | - } | ||
| 765 | - close(errfd); | ||
| 766 | - if ((errfd = open(ERRORS_FILE, O_CREAT | O_WRONLY | O_APPEND, 0644)) < 0) | ||
| 767 | - err_sys_quit(STDERR_FILENO, "ERROR: process %d (pid %d): signal" | ||
| 768 | - " processor: open", my_index, my_pid); | ||
| 769 | - /* Reload configuration */ | ||
| 770 | - load_configs(); | ||
| 771 | - break; | ||
| 772 | - case SIGTERM: | ||
| 773 | - /* | ||
| 774 | - * Terminate ungracefully since it is generally not known how long | ||
| 775 | - * it will take to gracefully complete all client sessions. | ||
| 776 | - */ | ||
| 777 | - err_report(errfd, "INFO: process %d (pid %d): caught SIGTERM," | ||
| 778 | - " terminating", my_index, my_pid); | ||
| 779 | - if (log_access) | ||
| 780 | - logbuf_flush(); | ||
| 781 | - exit(0); | ||
| 782 | - case SIGUSR1: | ||
| 783 | - err_report(errfd, "INFO: process %d (pid %d): caught SIGUSR1", | ||
| 784 | - my_index, my_pid); | ||
| 785 | - /* Print server info to stderr */ | ||
| 786 | - dump_server_info(); | ||
| 787 | - break; | ||
| 788 | - default: | ||
| 789 | - err_report(errfd, "INFO: process %d (pid %d): caught signal %d", | ||
| 790 | - my_index, my_pid, signo); | ||
| 791 | - } | ||
| 792 | - } | ||
| 793 | - | ||
| 794 | - /* NOTREACHED */ | ||
| 795 | - return NULL; | ||
| 796 | -} | ||
| 797 | - | ||
| 798 | - | ||
| 799 | -/****************************************************************** | ||
| 800 | - * The "main" function of the access log flushing thread. | ||
| 801 | - */ | ||
| 802 | - | ||
| 803 | -/* ARGSUSED */ | ||
| 804 | -static void *flush_acclog_buffer(void *arg) | ||
| 805 | -{ | ||
| 806 | - for ( ; ; ) { | ||
| 807 | - st_sleep(ACCLOG_FLUSH_INTERVAL); | ||
| 808 | - logbuf_flush(); | ||
| 809 | - } | ||
| 810 | - | ||
| 811 | - /* NOTREACHED */ | ||
| 812 | - return NULL; | ||
| 813 | -} | ||
| 814 | - | ||
| 815 | - | ||
| 816 | -/******************************************************************/ | ||
| 817 | - | ||
| 818 | -static void start_threads(void) | ||
| 819 | -{ | ||
| 820 | - long i, n; | ||
| 821 | - | ||
| 822 | - /* Create access log flushing thread */ | ||
| 823 | - if (log_access && st_thread_create(flush_acclog_buffer, NULL, 0, 0) == NULL) | ||
| 824 | - err_sys_quit(errfd, "ERROR: process %d (pid %d): can't create" | ||
| 825 | - " log flushing thread", my_index, my_pid); | ||
| 826 | - | ||
| 827 | - /* Create connections handling threads */ | ||
| 828 | - for (i = 0; i < sk_count; i++) { | ||
| 829 | - err_report(errfd, "INFO: process %d (pid %d): starting %d threads" | ||
| 830 | - " on %s:%u", my_index, my_pid, max_wait_threads, | ||
| 831 | - srv_socket[i].addr, srv_socket[i].port); | ||
| 832 | - WAIT_THREADS(i) = 0; | ||
| 833 | - BUSY_THREADS(i) = 0; | ||
| 834 | - RQST_COUNT(i) = 0; | ||
| 835 | - for (n = 0; n < max_wait_threads; n++) { | ||
| 836 | - if (st_thread_create(handle_connections, (void *)i, 0, 0) != NULL) | ||
| 837 | - WAIT_THREADS(i)++; | ||
| 838 | - else | ||
| 839 | - err_sys_report(errfd, "ERROR: process %d (pid %d): can't create" | ||
| 840 | - " thread", my_index, my_pid); | ||
| 841 | - } | ||
| 842 | - if (WAIT_THREADS(i) == 0) | ||
| 843 | - exit(1); | ||
| 844 | - } | ||
| 845 | -} | ||
| 846 | - | ||
| 847 | - | ||
| 848 | -/******************************************************************/ | ||
| 849 | - | ||
| 850 | -static void *handle_connections(void *arg) | ||
| 851 | -{ | ||
| 852 | - st_netfd_t srv_nfd, cli_nfd; | ||
| 853 | - struct sockaddr_in from; | ||
| 854 | - int fromlen; | ||
| 855 | - long i = (long) arg; | ||
| 856 | - | ||
| 857 | - srv_nfd = srv_socket[i].nfd; | ||
| 858 | - fromlen = sizeof(from); | ||
| 859 | - | ||
| 860 | - while (WAIT_THREADS(i) <= max_wait_threads) { | ||
| 861 | - cli_nfd = st_accept(srv_nfd, (struct sockaddr *)&from, &fromlen, | ||
| 862 | - ST_UTIME_NO_TIMEOUT); | ||
| 863 | - if (cli_nfd == NULL) { | ||
| 864 | - err_sys_report(errfd, "ERROR: can't accept connection: st_accept"); | ||
| 865 | - continue; | ||
| 866 | - } | ||
| 867 | - /* Save peer address, so we can retrieve it later */ | ||
| 868 | - st_netfd_setspecific(cli_nfd, &from.sin_addr, NULL); | ||
| 869 | - | ||
| 870 | - WAIT_THREADS(i)--; | ||
| 871 | - BUSY_THREADS(i)++; | ||
| 872 | - if (WAIT_THREADS(i) < min_wait_threads && TOTAL_THREADS(i) < max_threads) { | ||
| 873 | - /* Create another spare thread */ | ||
| 874 | - if (st_thread_create(handle_connections, (void *)i, 0, 0) != NULL) | ||
| 875 | - WAIT_THREADS(i)++; | ||
| 876 | - else | ||
| 877 | - err_sys_report(errfd, "ERROR: process %d (pid %d): can't create" | ||
| 878 | - " thread", my_index, my_pid); | ||
| 879 | - } | ||
| 880 | - | ||
| 881 | - handle_session(i, cli_nfd); | ||
| 882 | - | ||
| 883 | - st_netfd_close(cli_nfd); | ||
| 884 | - WAIT_THREADS(i)++; | ||
| 885 | - BUSY_THREADS(i)--; | ||
| 886 | - } | ||
| 887 | - | ||
| 888 | - WAIT_THREADS(i)--; | ||
| 889 | - return NULL; | ||
| 890 | -} | ||
| 891 | - | ||
| 892 | - | ||
| 893 | -/******************************************************************/ | ||
| 894 | - | ||
| 895 | -static void dump_server_info(void) | ||
| 896 | -{ | ||
| 897 | - char *buf; | ||
| 898 | - int i, len; | ||
| 899 | - | ||
| 900 | - if ((buf = malloc(sk_count * 512)) == NULL) { | ||
| 901 | - err_sys_report(errfd, "ERROR: malloc failed"); | ||
| 902 | - return; | ||
| 903 | - } | ||
| 904 | - | ||
| 905 | - len = sprintf(buf, "\n\nProcess #%d (pid %d):\n", my_index, (int)my_pid); | ||
| 906 | - for (i = 0; i < sk_count; i++) { | ||
| 907 | - len += sprintf(buf + len, "\nListening Socket #%d:\n" | ||
| 908 | - "-------------------------\n" | ||
| 909 | - "Address %s:%u\n" | ||
| 910 | - "Thread limits (min/max) %d/%d\n" | ||
| 911 | - "Waiting threads %d\n" | ||
| 912 | - "Busy threads %d\n" | ||
| 913 | - "Requests served %d\n", | ||
| 914 | - i, srv_socket[i].addr, srv_socket[i].port, | ||
| 915 | - max_wait_threads, max_threads, | ||
| 916 | - WAIT_THREADS(i), BUSY_THREADS(i), RQST_COUNT(i)); | ||
| 917 | - } | ||
| 918 | - | ||
| 919 | - write(STDERR_FILENO, buf, len); | ||
| 920 | - free(buf); | ||
| 921 | -} | ||
| 922 | - | ||
| 923 | - | ||
| 924 | -/****************************************************************** | ||
| 925 | - * Stubs | ||
| 926 | - */ | ||
| 927 | - | ||
| 928 | -/* | ||
| 929 | - * Session handling function stub. Just dumps small HTML page. | ||
| 930 | - */ | ||
| 931 | -void handle_session(long srv_socket_index, st_netfd_t cli_nfd) | ||
| 932 | -{ | ||
| 933 | - static char resp[] = "HTTP/1.0 200 OK\r\nContent-type: text/html\r\n" | ||
| 934 | - "Connection: close\r\n\r\n<H2>It worked!</H2>\n"; | ||
| 935 | - char buf[512]; | ||
| 936 | - int n = sizeof(resp) - 1; | ||
| 937 | - struct in_addr *from = st_netfd_getspecific(cli_nfd); | ||
| 938 | - | ||
| 939 | - if (st_read(cli_nfd, buf, sizeof(buf), SEC2USEC(REQUEST_TIMEOUT)) < 0) { | ||
| 940 | - err_sys_report(errfd, "WARN: can't read request from %s: st_read", | ||
| 941 | - inet_ntoa(*from)); | ||
| 942 | - return; | ||
| 943 | - } | ||
| 944 | - if (st_write(cli_nfd, resp, n, ST_UTIME_NO_TIMEOUT) != n) { | ||
| 945 | - err_sys_report(errfd, "WARN: can't write response to %s: st_write", | ||
| 946 | - inet_ntoa(*from)); | ||
| 947 | - return; | ||
| 948 | - } | ||
| 949 | - | ||
| 950 | - RQST_COUNT(srv_socket_index)++; | ||
| 951 | -} | ||
| 952 | - | ||
| 953 | - | ||
| 954 | -/* | ||
| 955 | - * Configuration loading function stub. | ||
| 956 | - */ | ||
| 957 | -void load_configs(void) | ||
| 958 | -{ | ||
| 959 | - err_report(errfd, "INFO: process %d (pid %d): configuration loaded", | ||
| 960 | - my_index, my_pid); | ||
| 961 | -} | ||
| 962 | - | ||
| 963 | - | ||
| 964 | -/* | ||
| 965 | - * Buffered access logging methods. | ||
| 966 | - * Note that stdio functions (fopen(3), fprintf(3), fflush(3), etc.) cannot | ||
| 967 | - * be used if multiple VPs are created since these functions can flush buffer | ||
| 968 | - * at any point and thus write only partial log record to disk. | ||
| 969 | - * Also, it is completely safe for all threads of the same VP to write to | ||
| 970 | - * the same log buffer without any mutex protection (one buffer per VP, of | ||
| 971 | - * course). | ||
| 972 | - */ | ||
| 973 | -void logbuf_open(void) | ||
| 974 | -{ | ||
| 975 | - | ||
| 976 | -} | ||
| 977 | - | ||
| 978 | - | ||
| 979 | -void logbuf_flush(void) | ||
| 980 | -{ | ||
| 981 | - | ||
| 982 | -} | ||
| 983 | - | ||
| 984 | - | ||
| 985 | -void logbuf_close(void) | ||
| 986 | -{ | ||
| 987 | - | ||
| 988 | -} | ||
| 989 | - | ||
| 990 | - | ||
| 991 | -/****************************************************************** | ||
| 992 | - * Small utility functions | ||
| 993 | - */ | ||
| 994 | - | ||
| 995 | -static void Signal(int sig, void (*handler)(int)) | ||
| 996 | -{ | ||
| 997 | - struct sigaction sa; | ||
| 998 | - | ||
| 999 | - sa.sa_handler = handler; | ||
| 1000 | - sigemptyset(&sa.sa_mask); | ||
| 1001 | - sa.sa_flags = 0; | ||
| 1002 | - sigaction(sig, &sa, NULL); | ||
| 1003 | -} | ||
| 1004 | - | ||
| 1005 | -static int cpu_count(void) | ||
| 1006 | -{ | ||
| 1007 | - int n; | ||
| 1008 | - | ||
| 1009 | -#if defined (_SC_NPROCESSORS_ONLN) | ||
| 1010 | - n = (int) sysconf(_SC_NPROCESSORS_ONLN); | ||
| 1011 | -#elif defined (_SC_NPROC_ONLN) | ||
| 1012 | - n = (int) sysconf(_SC_NPROC_ONLN); | ||
| 1013 | -#elif defined (HPUX) | ||
| 1014 | -#include <sys/mpctl.h> | ||
| 1015 | - n = mpctl(MPC_GETNUMSPUS, 0, 0); | ||
| 1016 | -#else | ||
| 1017 | - n = -1; | ||
| 1018 | - errno = ENOSYS; | ||
| 1019 | -#endif | ||
| 1020 | - | ||
| 1021 | - return n; | ||
| 1022 | -} | ||
| 1023 | - | ||
| 1024 | -/******************************************************************/ | ||
| 1025 | - |
| 1 | -# | ||
| 2 | -# Portions created by SGI are Copyright (C) 2000 Silicon Graphics, Inc. | ||
| 3 | -# All Rights Reserved. | ||
| 4 | -# | ||
| 5 | -# Redistribution and use in source and binary forms, with or without | ||
| 6 | -# modification, are permitted provided that the following conditions | ||
| 7 | -# are met: | ||
| 8 | -# | ||
| 9 | -# 1. Redistributions of source code must retain the above copyright | ||
| 10 | -# notice, this list of conditions and the following disclaimer. | ||
| 11 | -# 2. Redistributions in binary form must reproduce the above copyright | ||
| 12 | -# notice, this list of conditions and the following disclaimer in the | ||
| 13 | -# documentation and/or other materials provided with the distribution. | ||
| 14 | -# 3. Neither the name of Silicon Graphics, Inc. nor the names of its | ||
| 15 | -# contributors may be used to endorse or promote products derived from | ||
| 16 | -# this software without specific prior written permission. | ||
| 17 | -# | ||
| 18 | -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
| 19 | -# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
| 20 | -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||
| 21 | -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||
| 22 | -# HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
| 23 | -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED | ||
| 24 | -# TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | ||
| 25 | -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | ||
| 26 | -# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | ||
| 27 | -# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
| 28 | -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
| 29 | - | ||
| 30 | -CC = cc | ||
| 31 | - | ||
| 32 | -SHELL = /bin/sh | ||
| 33 | -ECHO = /bin/echo | ||
| 34 | - | ||
| 35 | -DEPTH = .. | ||
| 36 | -BUILD = | ||
| 37 | -TARGETDIR = obj | ||
| 38 | - | ||
| 39 | -DEFINES = | ||
| 40 | -OTHER_FLAGS = | ||
| 41 | -CFLAGS = | ||
| 42 | - | ||
| 43 | -OBJDIR = $(DEPTH)/$(TARGETDIR) | ||
| 44 | -INCDIR = $(DEPTH)/$(TARGETDIR) | ||
| 45 | - | ||
| 46 | -LIBRESOLV = | ||
| 47 | -EXTRALIBS = | ||
| 48 | - | ||
| 49 | -SLIBRARY = $(OBJDIR)/libstx.a | ||
| 50 | -OBJS = $(OBJDIR)/dnscache.o $(OBJDIR)/dnsres.o $(OBJDIR)/lrucache.o | ||
| 51 | - | ||
| 52 | - | ||
| 53 | -CFLAGS += -Wall -I$(INCDIR) | ||
| 54 | -AR = ar | ||
| 55 | -ARFLAGS = rv | ||
| 56 | -RANLIB = ranlib | ||
| 57 | - | ||
| 58 | - | ||
| 59 | -########################## | ||
| 60 | -# Platform section. | ||
| 61 | -# | ||
| 62 | - | ||
| 63 | -ifeq (LINUX, $(findstring LINUX, $(OS))) | ||
| 64 | -LIBRESOLV = -lresolv | ||
| 65 | -endif | ||
| 66 | - | ||
| 67 | -ifeq ($(OS), SOLARIS) | ||
| 68 | -LIBRESOLV = -lresolv | ||
| 69 | -EXTRALIBS = -lsocket -lnsl | ||
| 70 | -endif | ||
| 71 | - | ||
| 72 | -# | ||
| 73 | -# End of platform section. | ||
| 74 | -########################## | ||
| 75 | - | ||
| 76 | - | ||
| 77 | -all: $(SLIBRARY) | ||
| 78 | - | ||
| 79 | -$(SLIBRARY): $(OBJS) | ||
| 80 | - $(AR) $(ARFLAGS) $@ $(OBJS) | ||
| 81 | - $(RANLIB) $@ | ||
| 82 | - | ||
| 83 | -$(OBJDIR)/%.o: %.c stx.h common.h | ||
| 84 | - $(CC) $(CFLAGS) -c $< -o $@ | ||
| 85 | - | ||
| 86 | -clean: | ||
| 87 | - rm -rf $(OBJS) $(SLIBRARY) | ||
| 88 | - | ||
| 89 | -#.DEFAULT: | ||
| 90 | -# @cd $(DEPTH); $(MAKE) $@ | ||
| 91 | - |
trunk/research/st-1.9/extensions/README
已删除
100644 → 0
| 1 | -This directory contains extensions to the core State Threads Library | ||
| 2 | -that were contributed by users. All files hereunder are not part of the | ||
| 3 | -State Threads Library itself. They are provided as-is, without warranty | ||
| 4 | -or support, and under whatever license terms their authors provided. To | ||
| 5 | -contribute your own extensions, just mail them to the project | ||
| 6 | -administrators or to one of the project's mailing lists; see | ||
| 7 | -state-threads.sourceforge.net. Please indicate the license terms under | ||
| 8 | -which the project may distribute your contribution. | ||
| 9 | - | ||
| 10 | -======================================================================== | ||
| 11 | - | ||
| 12 | -stx_fileio | ||
| 13 | ----------- | ||
| 14 | -Contributed by Jeff <jlb-st@houseofdistraction.com>, 4 Nov 2002. | ||
| 15 | - | ||
| 16 | -Provides non-blocking random access file reading capability for | ||
| 17 | -programs using the State Threads library. There is one public function: | ||
| 18 | - | ||
| 19 | -ssize_t stx_file_read(st_netfd_t fd, off_t offset, | ||
| 20 | - void *buf, size_t nbytes, st_utime_t timeout); | ||
| 21 | - | ||
| 22 | -The implementation is not optimal in that the data is copied at least once | ||
| 23 | -more than should be necessary. Its usefulness is limited to cases where | ||
| 24 | -random access to a file is required and where starvation of other threads | ||
| 25 | -is unacceptable. | ||
| 26 | - | ||
| 27 | -The particular application which motivated this implementation was a UDP | ||
| 28 | -file transfer protocol. Because the OS does very little buffering of UDP | ||
| 29 | -traffic it is important that UDP transmission threads are not starved for | ||
| 30 | -periods of time which are long relative to the interval required to | ||
| 31 | -maintain a steady send rate. | ||
| 32 | - | ||
| 33 | -Licensed under the same dual MPL/GPL as core State Threads. | ||
| 34 | - | ||
| 35 | -======================================================================== | ||
| 36 | - | ||
| 37 | -stx_dns | ||
| 38 | -------- | ||
| 39 | - | ||
| 40 | -Documentation coming. | ||
| 41 | - | ||
| 42 | -======================================================================== |
| 1 | -#ifndef _STX_COMMON_H_ | ||
| 2 | -#define _STX_COMMON_H_ | ||
| 3 | - | ||
| 4 | -#include <stddef.h> | ||
| 5 | -#include <stdlib.h> | ||
| 6 | - | ||
| 7 | - | ||
| 8 | -#define STX_BEGIN_MACRO { | ||
| 9 | -#define STX_END_MACRO } | ||
| 10 | - | ||
| 11 | - | ||
| 12 | -/***************************************** | ||
| 13 | - * Circular linked list definitions | ||
| 14 | - */ | ||
| 15 | - | ||
| 16 | -typedef struct _stx_clist { | ||
| 17 | - struct _stx_clist *next; | ||
| 18 | - struct _stx_clist *prev; | ||
| 19 | -} stx_clist_t; | ||
| 20 | - | ||
| 21 | -/* Insert element "_e" into the list, before "_l" */ | ||
| 22 | -#define STX_CLIST_INSERT_BEFORE(_e,_l) \ | ||
| 23 | - STX_BEGIN_MACRO \ | ||
| 24 | - (_e)->next = (_l); \ | ||
| 25 | - (_e)->prev = (_l)->prev; \ | ||
| 26 | - (_l)->prev->next = (_e); \ | ||
| 27 | - (_l)->prev = (_e); \ | ||
| 28 | - STX_END_MACRO | ||
| 29 | - | ||
| 30 | -/* Insert element "_e" into the list, after "_l" */ | ||
| 31 | -#define STX_CLIST_INSERT_AFTER(_e,_l) \ | ||
| 32 | - STX_BEGIN_MACRO \ | ||
| 33 | - (_e)->next = (_l)->next; \ | ||
| 34 | - (_e)->prev = (_l); \ | ||
| 35 | - (_l)->next->prev = (_e); \ | ||
| 36 | - (_l)->next = (_e); \ | ||
| 37 | - STX_END_MACRO | ||
| 38 | - | ||
| 39 | -/* Append an element "_e" to the end of the list "_l" */ | ||
| 40 | -#define STX_CLIST_APPEND_LINK(_e,_l) STX_CLIST_INSERT_BEFORE(_e,_l) | ||
| 41 | - | ||
| 42 | -/* Remove the element "_e" from it's circular list */ | ||
| 43 | -#define STX_CLIST_REMOVE_LINK(_e) \ | ||
| 44 | - STX_BEGIN_MACRO \ | ||
| 45 | - (_e)->prev->next = (_e)->next; \ | ||
| 46 | - (_e)->next->prev = (_e)->prev; \ | ||
| 47 | - STX_END_MACRO | ||
| 48 | - | ||
| 49 | -/* Return the head/tail of the list */ | ||
| 50 | -#define STX_CLIST_HEAD(_l) (_l)->next | ||
| 51 | -#define STX_CLIST_TAIL(_l) (_l)->prev | ||
| 52 | - | ||
| 53 | -/* Return non-zero if the given circular list "_l" is empty, */ | ||
| 54 | -/* zero if the circular list is not empty */ | ||
| 55 | -#define STX_CLIST_IS_EMPTY(_l) \ | ||
| 56 | - ((_l)->next == (_l)) | ||
| 57 | - | ||
| 58 | -/* Initialize a circular list */ | ||
| 59 | -#define STX_CLIST_INIT_CLIST(_l) \ | ||
| 60 | - STX_BEGIN_MACRO \ | ||
| 61 | - (_l)->next = (_l); \ | ||
| 62 | - (_l)->prev = (_l); \ | ||
| 63 | - STX_END_MACRO | ||
| 64 | - | ||
| 65 | - | ||
| 66 | -/***************************************** | ||
| 67 | - * Useful macros | ||
| 68 | - */ | ||
| 69 | - | ||
| 70 | -#ifndef offsetof | ||
| 71 | -#define offsetof(type, identifier) ((size_t)&(((type *)0)->identifier)) | ||
| 72 | -#endif | ||
| 73 | - | ||
| 74 | -#define STX_MIN(a, b) (((a) < (b)) ? (a) : (b)) | ||
| 75 | - | ||
| 76 | -#endif /* !_STX_COMMON_H_ */ | ||
| 77 | - |
| 1 | -#include "stx.h" | ||
| 2 | -#include "common.h" | ||
| 3 | - | ||
| 4 | - | ||
| 5 | -/***************************************** | ||
| 6 | - * Basic types definitions | ||
| 7 | - */ | ||
| 8 | - | ||
| 9 | -typedef struct _stx_dns_data { | ||
| 10 | - struct in_addr *addrs; | ||
| 11 | - int num_addrs; | ||
| 12 | - int cur; | ||
| 13 | - time_t expires; | ||
| 14 | -} stx_dns_data_t; | ||
| 15 | - | ||
| 16 | - | ||
| 17 | -#define MAX_HOST_ADDRS 1024 | ||
| 18 | - | ||
| 19 | -static struct in_addr addr_list[MAX_HOST_ADDRS]; | ||
| 20 | - | ||
| 21 | -stx_cache_t *_stx_dns_cache = NULL; | ||
| 22 | - | ||
| 23 | -extern int _stx_dns_ttl; | ||
| 24 | -extern int _stx_dns_getaddrlist(const char *hostname, struct in_addr *addrs, | ||
| 25 | - int *num_addrs, st_utime_t timeout); | ||
| 26 | - | ||
| 27 | - | ||
| 28 | -static unsigned long hash_hostname(const void *key) | ||
| 29 | -{ | ||
| 30 | - const char *name = (const char *)key; | ||
| 31 | - unsigned long hash = 0; | ||
| 32 | - | ||
| 33 | - while (*name) | ||
| 34 | - hash = (hash << 4) - hash + *name++; /* hash = hash * 15 + *name++ */ | ||
| 35 | - | ||
| 36 | - return hash; | ||
| 37 | -} | ||
| 38 | - | ||
| 39 | -static void cleanup_entry(void *key, void *data) | ||
| 40 | -{ | ||
| 41 | - if (key) | ||
| 42 | - free(key); | ||
| 43 | - | ||
| 44 | - if (data) { | ||
| 45 | - if (((stx_dns_data_t *)data)->addrs) | ||
| 46 | - free(((stx_dns_data_t *)data)->addrs); | ||
| 47 | - free(data); | ||
| 48 | - } | ||
| 49 | -} | ||
| 50 | - | ||
| 51 | -static int lookup_entry(const char *host, struct in_addr *addrs, | ||
| 52 | - int *num_addrs, int rotate) | ||
| 53 | -{ | ||
| 54 | - stx_cache_entry_t *entry; | ||
| 55 | - stx_dns_data_t *data; | ||
| 56 | - int n; | ||
| 57 | - | ||
| 58 | - entry = stx_cache_entry_lookup(_stx_dns_cache, host); | ||
| 59 | - if (entry) { | ||
| 60 | - data = (stx_dns_data_t *)stx_cache_entry_getdata(entry); | ||
| 61 | - if (st_time() <= data->expires) { | ||
| 62 | - if (*num_addrs == 1) { | ||
| 63 | - if (rotate) { | ||
| 64 | - *addrs = data->addrs[data->cur++]; | ||
| 65 | - if (data->cur >= data->num_addrs) | ||
| 66 | - data->cur = 0; | ||
| 67 | - } else { | ||
| 68 | - *addrs = data->addrs[0]; | ||
| 69 | - } | ||
| 70 | - } else { | ||
| 71 | - n = STX_MIN(*num_addrs, data->num_addrs); | ||
| 72 | - memcpy(addrs, data->addrs, n * sizeof(*addrs)); | ||
| 73 | - *num_addrs = n; | ||
| 74 | - } | ||
| 75 | - | ||
| 76 | - stx_cache_entry_release(_stx_dns_cache, entry); | ||
| 77 | - return 1; | ||
| 78 | - } | ||
| 79 | - | ||
| 80 | - /* | ||
| 81 | - * Cache entry expired: decrement its refcount and purge it from cache. | ||
| 82 | - */ | ||
| 83 | - stx_cache_entry_release(_stx_dns_cache, entry); | ||
| 84 | - stx_cache_entry_delete(_stx_dns_cache, entry); | ||
| 85 | - } | ||
| 86 | - | ||
| 87 | - return 0; | ||
| 88 | -} | ||
| 89 | - | ||
| 90 | -static void insert_entry(const char *host, struct in_addr *addrs, int count) | ||
| 91 | -{ | ||
| 92 | - stx_cache_entry_t *entry; | ||
| 93 | - stx_dns_data_t *data; | ||
| 94 | - char *key; | ||
| 95 | - size_t n; | ||
| 96 | - | ||
| 97 | - if (_stx_dns_ttl > 0) { | ||
| 98 | - key = strdup(host); | ||
| 99 | - data = (stx_dns_data_t *)malloc(sizeof(stx_dns_data_t)); | ||
| 100 | - n = count * sizeof(*addrs); | ||
| 101 | - if (data) { | ||
| 102 | - data->addrs = (struct in_addr *)malloc(n); | ||
| 103 | - if (data->addrs) | ||
| 104 | - memcpy(data->addrs, addrs, n); | ||
| 105 | - data->num_addrs = count; | ||
| 106 | - data->cur = 0; | ||
| 107 | - data->expires = st_time() + _stx_dns_ttl; | ||
| 108 | - } | ||
| 109 | - entry = stx_cache_entry_create(key, data, strlen(host) + 1 + | ||
| 110 | - sizeof(stx_dns_data_t) + n + | ||
| 111 | - stx_cache_entry_sizeof()); | ||
| 112 | - if (key && data && data->addrs && entry && | ||
| 113 | - stx_cache_entry_insert(_stx_dns_cache, entry) == 0) { | ||
| 114 | - stx_cache_entry_release(_stx_dns_cache, entry); | ||
| 115 | - return; | ||
| 116 | - } | ||
| 117 | - | ||
| 118 | - if (entry) | ||
| 119 | - stx_cache_entry_delete(_stx_dns_cache, entry); | ||
| 120 | - else | ||
| 121 | - cleanup_entry(key, data); | ||
| 122 | - } | ||
| 123 | -} | ||
| 124 | - | ||
| 125 | - | ||
| 126 | - | ||
| 127 | -int _stx_dns_cache_getaddrlist(const char *hostname, struct in_addr *addrs, | ||
| 128 | - int *num_addrs, st_utime_t timeout, | ||
| 129 | - int rotate) | ||
| 130 | -{ | ||
| 131 | - char host[128]; | ||
| 132 | - int n, count; | ||
| 133 | - | ||
| 134 | - if (!_stx_dns_cache) | ||
| 135 | - return _stx_dns_getaddrlist(hostname, addrs, num_addrs, timeout); | ||
| 136 | - | ||
| 137 | - for (n = 0; n < sizeof(host) - 1 && hostname[n]; n++) { | ||
| 138 | - host[n] = tolower(hostname[n]); | ||
| 139 | - } | ||
| 140 | - host[n] = '\0'; | ||
| 141 | - | ||
| 142 | - if (lookup_entry(host, addrs, num_addrs, rotate)) | ||
| 143 | - return 0; | ||
| 144 | - | ||
| 145 | - count = MAX_HOST_ADDRS; | ||
| 146 | - if (_stx_dns_getaddrlist(host, addr_list, &count, timeout) < 0) | ||
| 147 | - return -1; | ||
| 148 | - n = STX_MIN(*num_addrs, count); | ||
| 149 | - memcpy(addrs, addr_list, n * sizeof(*addrs)); | ||
| 150 | - *num_addrs = n; | ||
| 151 | - | ||
| 152 | - insert_entry(host, addr_list, count); | ||
| 153 | - return 0; | ||
| 154 | -} | ||
| 155 | - | ||
| 156 | - | ||
| 157 | -int stx_dns_cache_init(size_t max_size, size_t max_bytes, size_t hash_size) | ||
| 158 | -{ | ||
| 159 | - _stx_dns_cache = stx_cache_create(max_size, max_bytes, hash_size, | ||
| 160 | - hash_hostname, | ||
| 161 | - (long (*)(const void *, const void *))strcmp, | ||
| 162 | - cleanup_entry); | ||
| 163 | - if (!_stx_dns_cache) | ||
| 164 | - return -1; | ||
| 165 | - | ||
| 166 | - return 0; | ||
| 167 | -} | ||
| 168 | - | ||
| 169 | -void stx_dns_cache_getinfo(stx_cache_info_t *info) | ||
| 170 | -{ | ||
| 171 | - if (_stx_dns_cache) | ||
| 172 | - stx_cache_getinfo(_stx_dns_cache, info); | ||
| 173 | - else | ||
| 174 | - memset(info, 0, sizeof(stx_cache_info_t)); | ||
| 175 | -} | ||
| 176 | - | ||
| 177 | -int stx_dns_getaddrlist(const char *hostname, struct in_addr *addrs, | ||
| 178 | - int *num_addrs, st_utime_t timeout) | ||
| 179 | -{ | ||
| 180 | - return _stx_dns_cache_getaddrlist(hostname, addrs, num_addrs, timeout, 0); | ||
| 181 | -} | ||
| 182 | - | ||
| 183 | -int stx_dns_getaddr(const char *hostname, struct in_addr *addr, | ||
| 184 | - st_utime_t timeout) | ||
| 185 | -{ | ||
| 186 | - int n = 1; | ||
| 187 | - | ||
| 188 | - return _stx_dns_cache_getaddrlist(hostname, addr, &n, timeout, 1); | ||
| 189 | -} | ||
| 190 | - |
| 1 | -/* | ||
| 2 | - * Copyright (c) 1985, 1988, 1993 | ||
| 3 | - * The Regents of the University of California. All rights reserved. | ||
| 4 | - * | ||
| 5 | - * Redistribution and use in source and binary forms, with or without | ||
| 6 | - * modification, are permitted provided that the following conditions | ||
| 7 | - * are met: | ||
| 8 | - * 1. Redistributions of source code must retain the above copyright | ||
| 9 | - * notice, this list of conditions and the following disclaimer. | ||
| 10 | - * 2. Redistributions in binary form must reproduce the above copyright | ||
| 11 | - * notice, this list of conditions and the following disclaimer in the | ||
| 12 | - * documentation and/or other materials provided with the distribution. | ||
| 13 | - * 3. All advertising materials mentioning features or use of this software | ||
| 14 | - * must display the following acknowledgement: | ||
| 15 | - * This product includes software developed by the University of | ||
| 16 | - * California, Berkeley and its contributors. | ||
| 17 | - * 4. Neither the name of the University nor the names of its contributors | ||
| 18 | - * may be used to endorse or promote products derived from this software | ||
| 19 | - * without specific prior written permission. | ||
| 20 | - * | ||
| 21 | - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | ||
| 22 | - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||
| 23 | - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | ||
| 24 | - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | ||
| 25 | - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | ||
| 26 | - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | ||
| 27 | - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | ||
| 28 | - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | ||
| 29 | - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | ||
| 30 | - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | ||
| 31 | - * SUCH DAMAGE. | ||
| 32 | - * | ||
| 33 | - * Portions created by SGI are Copyright (C) 2000 Silicon Graphics, Inc. | ||
| 34 | - * All Rights Reserved. | ||
| 35 | - * | ||
| 36 | - * Redistribution and use in source and binary forms, with or without | ||
| 37 | - * modification, are permitted provided that the following conditions | ||
| 38 | - * are met: | ||
| 39 | - * | ||
| 40 | - * 1. Redistributions of source code must retain the above copyright | ||
| 41 | - * notice, this list of conditions and the following disclaimer. | ||
| 42 | - * 2. Redistributions in binary form must reproduce the above copyright | ||
| 43 | - * notice, this list of conditions and the following disclaimer in the | ||
| 44 | - * documentation and/or other materials provided with the distribution. | ||
| 45 | - * 3. Neither the name of Silicon Graphics, Inc. nor the names of its | ||
| 46 | - * contributors may be used to endorse or promote products derived from | ||
| 47 | - * this software without specific prior written permission. | ||
| 48 | - * | ||
| 49 | - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
| 50 | - * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
| 51 | - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||
| 52 | - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||
| 53 | - * HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
| 54 | - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED | ||
| 55 | - * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | ||
| 56 | - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | ||
| 57 | - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | ||
| 58 | - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
| 59 | - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
| 60 | - */ | ||
| 61 | - | ||
| 62 | -#include "stx.h" | ||
| 63 | - | ||
| 64 | -#define MAXPACKET 1024 | ||
| 65 | - | ||
| 66 | -#if !defined(NETDB_INTERNAL) && defined(h_NETDB_INTERNAL) | ||
| 67 | -#define NETDB_INTERNAL h_NETDB_INTERNAL | ||
| 68 | -#endif | ||
| 69 | - | ||
| 70 | -/* New in Solaris 7 */ | ||
| 71 | -#if !defined(_getshort) && defined(ns_get16) | ||
| 72 | -#define _getshort(cp) ns_get16(cp) | ||
| 73 | -#define _getlong(cp) ns_get32(cp) | ||
| 74 | -#endif | ||
| 75 | - | ||
| 76 | -typedef union { | ||
| 77 | - HEADER hdr; | ||
| 78 | - u_char buf[MAXPACKET]; | ||
| 79 | -} querybuf_t; | ||
| 80 | - | ||
| 81 | -int _stx_dns_ttl; | ||
| 82 | - | ||
| 83 | - | ||
| 84 | -static int parse_answer(querybuf_t *ans, int len, struct in_addr *addrs, | ||
| 85 | - int *num_addrs) | ||
| 86 | -{ | ||
| 87 | - char buf[MAXPACKET]; | ||
| 88 | - HEADER *ahp; | ||
| 89 | - u_char *cp, *eoa; | ||
| 90 | - int type, n, i; | ||
| 91 | - | ||
| 92 | - ahp = &ans->hdr; | ||
| 93 | - eoa = ans->buf + len; | ||
| 94 | - cp = ans->buf + sizeof(HEADER); | ||
| 95 | - h_errno = TRY_AGAIN; | ||
| 96 | - _stx_dns_ttl = -1; | ||
| 97 | - i = 0; | ||
| 98 | - | ||
| 99 | - while (ahp->qdcount > 0) { | ||
| 100 | - ahp->qdcount--; | ||
| 101 | - cp += dn_skipname(cp, eoa) + QFIXEDSZ; | ||
| 102 | - } | ||
| 103 | - while (ahp->ancount > 0 && cp < eoa && i < *num_addrs) { | ||
| 104 | - ahp->ancount--; | ||
| 105 | - if ((n = dn_expand(ans->buf, eoa, cp, buf, sizeof(buf))) < 0) | ||
| 106 | - return -1; | ||
| 107 | - cp += n; | ||
| 108 | - if (cp + 4 + 4 + 2 >= eoa) | ||
| 109 | - return -1; | ||
| 110 | - type = _getshort(cp); | ||
| 111 | - cp += 4; | ||
| 112 | - if (type == T_A) | ||
| 113 | - _stx_dns_ttl = _getlong(cp); | ||
| 114 | - cp += 4; | ||
| 115 | - n = _getshort(cp); | ||
| 116 | - cp += 2; | ||
| 117 | - if (type == T_A) { | ||
| 118 | - if (n > sizeof(*addrs) || cp + n > eoa) | ||
| 119 | - return -1; | ||
| 120 | - memcpy(&addrs[i++], cp, n); | ||
| 121 | - } | ||
| 122 | - cp += n; | ||
| 123 | - } | ||
| 124 | - | ||
| 125 | - *num_addrs = i; | ||
| 126 | - return 0; | ||
| 127 | -} | ||
| 128 | - | ||
| 129 | - | ||
| 130 | -static int query_domain(st_netfd_t nfd, const char *name, | ||
| 131 | - struct in_addr *addrs, int *num_addrs, | ||
| 132 | - st_utime_t timeout) | ||
| 133 | -{ | ||
| 134 | - querybuf_t qbuf; | ||
| 135 | - u_char *buf = qbuf.buf; | ||
| 136 | - HEADER *hp = &qbuf.hdr; | ||
| 137 | - int blen = sizeof(qbuf); | ||
| 138 | - int i, len, id; | ||
| 139 | - | ||
| 140 | - for (i = 0; i < _res.nscount; i++) { | ||
| 141 | - len = res_mkquery(QUERY, name, C_IN, T_A, NULL, 0, NULL, buf, blen); | ||
| 142 | - if (len <= 0) { | ||
| 143 | - h_errno = NO_RECOVERY; | ||
| 144 | - return -1; | ||
| 145 | - } | ||
| 146 | - id = hp->id; | ||
| 147 | - | ||
| 148 | - if (st_sendto(nfd, buf, len, (struct sockaddr *)&(_res.nsaddr_list[i]), | ||
| 149 | - sizeof(struct sockaddr), timeout) != len) { | ||
| 150 | - h_errno = NETDB_INTERNAL; | ||
| 151 | - /* EINTR means interrupt by other thread, NOT by a caught signal */ | ||
| 152 | - if (errno == EINTR) | ||
| 153 | - return -1; | ||
| 154 | - continue; | ||
| 155 | - } | ||
| 156 | - | ||
| 157 | - /* Wait for reply */ | ||
| 158 | - do { | ||
| 159 | - len = st_recvfrom(nfd, buf, blen, NULL, NULL, timeout); | ||
| 160 | - if (len <= 0) | ||
| 161 | - break; | ||
| 162 | - } while (id != hp->id); | ||
| 163 | - | ||
| 164 | - if (len < HFIXEDSZ) { | ||
| 165 | - h_errno = NETDB_INTERNAL; | ||
| 166 | - if (len >= 0) | ||
| 167 | - errno = EMSGSIZE; | ||
| 168 | - else if (errno == EINTR) /* see the comment above */ | ||
| 169 | - return -1; | ||
| 170 | - continue; | ||
| 171 | - } | ||
| 172 | - | ||
| 173 | - hp->ancount = ntohs(hp->ancount); | ||
| 174 | - hp->qdcount = ntohs(hp->qdcount); | ||
| 175 | - if ((hp->rcode != NOERROR) || (hp->ancount == 0)) { | ||
| 176 | - switch (hp->rcode) { | ||
| 177 | - case NXDOMAIN: | ||
| 178 | - h_errno = HOST_NOT_FOUND; | ||
| 179 | - break; | ||
| 180 | - case SERVFAIL: | ||
| 181 | - h_errno = TRY_AGAIN; | ||
| 182 | - break; | ||
| 183 | - case NOERROR: | ||
| 184 | - h_errno = NO_DATA; | ||
| 185 | - break; | ||
| 186 | - case FORMERR: | ||
| 187 | - case NOTIMP: | ||
| 188 | - case REFUSED: | ||
| 189 | - default: | ||
| 190 | - h_errno = NO_RECOVERY; | ||
| 191 | - } | ||
| 192 | - continue; | ||
| 193 | - } | ||
| 194 | - | ||
| 195 | - if (parse_answer(&qbuf, len, addrs, num_addrs) == 0) | ||
| 196 | - return 0; | ||
| 197 | - } | ||
| 198 | - | ||
| 199 | - return -1; | ||
| 200 | -} | ||
| 201 | - | ||
| 202 | - | ||
| 203 | -#define CLOSE_AND_RETURN(ret) \ | ||
| 204 | - { \ | ||
| 205 | - n = errno; \ | ||
| 206 | - st_netfd_close(nfd); \ | ||
| 207 | - errno = n; \ | ||
| 208 | - return (ret); \ | ||
| 209 | - } | ||
| 210 | - | ||
| 211 | - | ||
| 212 | -int _stx_dns_getaddrlist(const char *host, struct in_addr *addrs, | ||
| 213 | - int *num_addrs, st_utime_t timeout) | ||
| 214 | -{ | ||
| 215 | - char name[MAXDNAME], **domain; | ||
| 216 | - const char *cp; | ||
| 217 | - int s, n, maxlen, dots; | ||
| 218 | - int trailing_dot, tried_as_is; | ||
| 219 | - st_netfd_t nfd; | ||
| 220 | - | ||
| 221 | - if ((_res.options & RES_INIT) == 0 && res_init() == -1) { | ||
| 222 | - h_errno = NETDB_INTERNAL; | ||
| 223 | - return -1; | ||
| 224 | - } | ||
| 225 | - if (_res.options & RES_USEVC) { | ||
| 226 | - h_errno = NETDB_INTERNAL; | ||
| 227 | - errno = ENOSYS; | ||
| 228 | - return -1; | ||
| 229 | - } | ||
| 230 | - if (!host || *host == '\0') { | ||
| 231 | - h_errno = HOST_NOT_FOUND; | ||
| 232 | - return -1; | ||
| 233 | - } | ||
| 234 | - | ||
| 235 | - /* Create UDP socket */ | ||
| 236 | - if ((s = socket(PF_INET, SOCK_DGRAM, 0)) < 0) { | ||
| 237 | - h_errno = NETDB_INTERNAL; | ||
| 238 | - return -1; | ||
| 239 | - } | ||
| 240 | - if ((nfd = st_netfd_open_socket(s)) == NULL) { | ||
| 241 | - h_errno = NETDB_INTERNAL; | ||
| 242 | - n = errno; | ||
| 243 | - close(s); | ||
| 244 | - errno = n; | ||
| 245 | - return -1; | ||
| 246 | - } | ||
| 247 | - | ||
| 248 | - maxlen = sizeof(name) - 1; | ||
| 249 | - n = 0; | ||
| 250 | - dots = 0; | ||
| 251 | - trailing_dot = 0; | ||
| 252 | - tried_as_is = 0; | ||
| 253 | - | ||
| 254 | - for (cp = host; *cp && n < maxlen; cp++) { | ||
| 255 | - dots += (*cp == '.'); | ||
| 256 | - name[n++] = *cp; | ||
| 257 | - } | ||
| 258 | - if (name[n - 1] == '.') | ||
| 259 | - trailing_dot = 1; | ||
| 260 | - | ||
| 261 | - /* | ||
| 262 | - * If there are dots in the name already, let's just give it a try | ||
| 263 | - * 'as is'. The threshold can be set with the "ndots" option. | ||
| 264 | - */ | ||
| 265 | - if (dots >= _res.ndots) { | ||
| 266 | - if (query_domain(nfd, host, addrs, num_addrs, timeout) == 0) | ||
| 267 | - CLOSE_AND_RETURN(0); | ||
| 268 | - if (h_errno == NETDB_INTERNAL && errno == EINTR) | ||
| 269 | - CLOSE_AND_RETURN(-1); | ||
| 270 | - tried_as_is = 1; | ||
| 271 | - } | ||
| 272 | - | ||
| 273 | - /* | ||
| 274 | - * We do at least one level of search if | ||
| 275 | - * - there is no dot and RES_DEFNAME is set, or | ||
| 276 | - * - there is at least one dot, there is no trailing dot, | ||
| 277 | - * and RES_DNSRCH is set. | ||
| 278 | - */ | ||
| 279 | - if ((!dots && (_res.options & RES_DEFNAMES)) || | ||
| 280 | - (dots && !trailing_dot && (_res.options & RES_DNSRCH))) { | ||
| 281 | - name[n++] = '.'; | ||
| 282 | - for (domain = _res.dnsrch; *domain; domain++) { | ||
| 283 | - strncpy(name + n, *domain, maxlen - n); | ||
| 284 | - if (query_domain(nfd, name, addrs, num_addrs, timeout) == 0) | ||
| 285 | - CLOSE_AND_RETURN(0); | ||
| 286 | - if (h_errno == NETDB_INTERNAL && errno == EINTR) | ||
| 287 | - CLOSE_AND_RETURN(-1); | ||
| 288 | - if (!(_res.options & RES_DNSRCH)) | ||
| 289 | - break; | ||
| 290 | - } | ||
| 291 | - } | ||
| 292 | - | ||
| 293 | - /* | ||
| 294 | - * If we have not already tried the name "as is", do that now. | ||
| 295 | - * note that we do this regardless of how many dots were in the | ||
| 296 | - * name or whether it ends with a dot. | ||
| 297 | - */ | ||
| 298 | - if (!tried_as_is) { | ||
| 299 | - if (query_domain(nfd, host, addrs, num_addrs, timeout) == 0) | ||
| 300 | - CLOSE_AND_RETURN(0); | ||
| 301 | - } | ||
| 302 | - | ||
| 303 | - CLOSE_AND_RETURN(-1); | ||
| 304 | -} | ||
| 305 | - |
| 1 | -#include "stx.h" | ||
| 2 | -#include "common.h" | ||
| 3 | - | ||
| 4 | - | ||
| 5 | -/***************************************** | ||
| 6 | - * Basic types definitions | ||
| 7 | - */ | ||
| 8 | - | ||
| 9 | -struct _stx_centry { | ||
| 10 | - void *key; /* key for doing lookups */ | ||
| 11 | - void *data; /* data in the cache */ | ||
| 12 | - size_t weight; /* "weight" of this entry */ | ||
| 13 | - struct _stx_centry *next; /* next entry */ | ||
| 14 | - struct _stx_centry **pthis; | ||
| 15 | - stx_clist_t lru_link; /* for putting this entry on LRU list */ | ||
| 16 | - int ref_count; /* use count for this entry */ | ||
| 17 | - int delete_pending; /* pending delete flag */ | ||
| 18 | -}; | ||
| 19 | - | ||
| 20 | -struct _stx_cache { | ||
| 21 | - size_t max_size; /* max size of cache */ | ||
| 22 | - size_t cur_size; /* current size of cache */ | ||
| 23 | - | ||
| 24 | - size_t max_weight; /* cache capacity */ | ||
| 25 | - size_t cur_weight; /* current total "weight" of all entries */ | ||
| 26 | - | ||
| 27 | - size_t hash_size; /* size of hash table */ | ||
| 28 | - stx_cache_entry_t **table; /* hash table for this cache */ | ||
| 29 | - | ||
| 30 | - stx_clist_t lru_list; /* least-recently-used list */ | ||
| 31 | - | ||
| 32 | - /* Cache stats */ | ||
| 33 | - unsigned long hits; /* num cache hits */ | ||
| 34 | - unsigned long lookups; /* num cache lookups */ | ||
| 35 | - unsigned long inserts; /* num inserts */ | ||
| 36 | - unsigned long deletes; /* num deletes */ | ||
| 37 | - | ||
| 38 | - /* Functions */ | ||
| 39 | - unsigned long (*key_hash_fn)(const void *); | ||
| 40 | - long (*key_cmp_fn)(const void *, const void *); | ||
| 41 | - void (*cleanup_fn)(void *, void *); | ||
| 42 | -}; | ||
| 43 | - | ||
| 44 | - | ||
| 45 | -#define STX_CACHE_ENTRY_PTR(_qp) \ | ||
| 46 | - ((stx_cache_entry_t *)((char *)(_qp) - offsetof(stx_cache_entry_t, lru_link))) | ||
| 47 | - | ||
| 48 | - | ||
| 49 | -/***************************************** | ||
| 50 | - * Cache methods | ||
| 51 | - */ | ||
| 52 | - | ||
| 53 | -stx_cache_t *stx_cache_create(size_t max_size, size_t max_weight, | ||
| 54 | - size_t hash_size, | ||
| 55 | - unsigned long (*key_hash_fn)(const void *key), | ||
| 56 | - long (*key_cmp_fn)(const void *key1, | ||
| 57 | - const void *key2), | ||
| 58 | - void (*cleanup_fn)(void *key, void *data)) | ||
| 59 | -{ | ||
| 60 | - stx_cache_t *newcache; | ||
| 61 | - | ||
| 62 | - newcache = (stx_cache_t *)calloc(1, sizeof(stx_cache_t)); | ||
| 63 | - if (newcache == NULL) | ||
| 64 | - return NULL; | ||
| 65 | - newcache->table = (stx_cache_entry_t **)calloc(hash_size, | ||
| 66 | - sizeof(stx_cache_entry_t *)); | ||
| 67 | - if (newcache->table == NULL) { | ||
| 68 | - free(newcache); | ||
| 69 | - return NULL; | ||
| 70 | - } | ||
| 71 | - | ||
| 72 | - newcache->max_size = max_size; | ||
| 73 | - newcache->max_weight = max_weight; | ||
| 74 | - newcache->hash_size = hash_size; | ||
| 75 | - STX_CLIST_INIT_CLIST(&(newcache->lru_list)); | ||
| 76 | - newcache->key_hash_fn = key_hash_fn; | ||
| 77 | - newcache->key_cmp_fn = key_cmp_fn; | ||
| 78 | - newcache->cleanup_fn = cleanup_fn; | ||
| 79 | - | ||
| 80 | - return newcache; | ||
| 81 | -} | ||
| 82 | - | ||
| 83 | - | ||
| 84 | -void stx_cache_empty(stx_cache_t *cache) | ||
| 85 | -{ | ||
| 86 | - size_t i; | ||
| 87 | - stx_cache_entry_t *entry, *next_entry; | ||
| 88 | - | ||
| 89 | - for (i = 0; i < cache->hash_size; i++) { | ||
| 90 | - entry = cache->table[i]; | ||
| 91 | - while (entry) { | ||
| 92 | - next_entry = entry->next; | ||
| 93 | - stx_cache_entry_delete(cache, entry); | ||
| 94 | - entry = next_entry; | ||
| 95 | - } | ||
| 96 | - } | ||
| 97 | -} | ||
| 98 | - | ||
| 99 | - | ||
| 100 | -void stx_cache_traverse(stx_cache_t *cache, | ||
| 101 | - void (*callback)(void *key, void *data)) | ||
| 102 | -{ | ||
| 103 | - size_t i; | ||
| 104 | - stx_cache_entry_t *entry; | ||
| 105 | - | ||
| 106 | - for (i = 0; i < cache->hash_size; i++) { | ||
| 107 | - for (entry = cache->table[i]; entry; entry = entry->next) { | ||
| 108 | - if (!entry->delete_pending) | ||
| 109 | - (*callback)(entry->key, entry->data); | ||
| 110 | - } | ||
| 111 | - } | ||
| 112 | -} | ||
| 113 | - | ||
| 114 | - | ||
| 115 | -void stx_cache_traverse_lru(stx_cache_t *cache, | ||
| 116 | - void (*callback)(void *key, void *data), | ||
| 117 | - unsigned int n) | ||
| 118 | -{ | ||
| 119 | - stx_clist_t *q; | ||
| 120 | - stx_cache_entry_t *entry; | ||
| 121 | - | ||
| 122 | - for (q = STX_CLIST_HEAD(&cache->lru_list); q != &cache->lru_list && n; | ||
| 123 | - q = q->next, n--) { | ||
| 124 | - entry = STX_CACHE_ENTRY_PTR(q); | ||
| 125 | - (*callback)(entry->key, entry->data); | ||
| 126 | - } | ||
| 127 | -} | ||
| 128 | - | ||
| 129 | - | ||
| 130 | -void stx_cache_traverse_mru(stx_cache_t *cache, | ||
| 131 | - void (*callback)(void *key, void *data), | ||
| 132 | - unsigned int n) | ||
| 133 | -{ | ||
| 134 | - stx_clist_t *q; | ||
| 135 | - stx_cache_entry_t *entry; | ||
| 136 | - | ||
| 137 | - for (q = STX_CLIST_TAIL(&cache->lru_list); q != &cache->lru_list && n; | ||
| 138 | - q = q->prev, n--) { | ||
| 139 | - entry = STX_CACHE_ENTRY_PTR(q); | ||
| 140 | - (*callback)(entry->key, entry->data); | ||
| 141 | - } | ||
| 142 | -} | ||
| 143 | - | ||
| 144 | - | ||
| 145 | -size_t stx_cache_getsize(stx_cache_t *cache) | ||
| 146 | -{ | ||
| 147 | - return cache->cur_size; | ||
| 148 | -} | ||
| 149 | - | ||
| 150 | - | ||
| 151 | -size_t stx_cache_getweight(stx_cache_t *cache) | ||
| 152 | -{ | ||
| 153 | - return cache->cur_weight; | ||
| 154 | -} | ||
| 155 | - | ||
| 156 | - | ||
| 157 | -void stx_cache_getinfo(stx_cache_t *cache, stx_cache_info_t *info) | ||
| 158 | -{ | ||
| 159 | - info->max_size = cache->max_size; | ||
| 160 | - info->max_weight = cache->max_weight; | ||
| 161 | - info->hash_size = cache->hash_size; | ||
| 162 | - info->cur_size = cache->cur_size; | ||
| 163 | - info->cur_weight = cache->cur_weight; | ||
| 164 | - info->hits = cache->hits; | ||
| 165 | - info->lookups = cache->lookups; | ||
| 166 | - info->inserts = cache->inserts; | ||
| 167 | - info->deletes = cache->deletes; | ||
| 168 | -} | ||
| 169 | - | ||
| 170 | - | ||
| 171 | -/***************************************** | ||
| 172 | - * Cache entry methods | ||
| 173 | - */ | ||
| 174 | - | ||
| 175 | -stx_cache_entry_t *stx_cache_entry_create(void *key, void *data, | ||
| 176 | - size_t weight) | ||
| 177 | -{ | ||
| 178 | - stx_cache_entry_t *newentry; | ||
| 179 | - | ||
| 180 | - newentry = (stx_cache_entry_t *)calloc(1, sizeof(stx_cache_entry_t)); | ||
| 181 | - if (newentry == NULL) | ||
| 182 | - return NULL; | ||
| 183 | - | ||
| 184 | - newentry->key = key; | ||
| 185 | - newentry->data = data; | ||
| 186 | - newentry->weight = weight; | ||
| 187 | - | ||
| 188 | - return newentry; | ||
| 189 | -} | ||
| 190 | - | ||
| 191 | - | ||
| 192 | -void stx_cache_entry_delete(stx_cache_t *cache, stx_cache_entry_t *entry) | ||
| 193 | -{ | ||
| 194 | - entry->delete_pending = 1; | ||
| 195 | - | ||
| 196 | - if (entry->ref_count > 0) | ||
| 197 | - return; | ||
| 198 | - | ||
| 199 | - if (entry->pthis) { | ||
| 200 | - *entry->pthis = entry->next; | ||
| 201 | - if (entry->next) | ||
| 202 | - entry->next->pthis = entry->pthis; | ||
| 203 | - | ||
| 204 | - cache->cur_size--; | ||
| 205 | - cache->cur_weight -= entry->weight; | ||
| 206 | - cache->deletes++; | ||
| 207 | - STX_CLIST_REMOVE_LINK(&(entry->lru_link)); | ||
| 208 | - } | ||
| 209 | - | ||
| 210 | - if (cache->cleanup_fn) | ||
| 211 | - cache->cleanup_fn(entry->key, entry->data); | ||
| 212 | - | ||
| 213 | - entry->pthis = NULL; | ||
| 214 | - entry->key = NULL; | ||
| 215 | - entry->data = NULL; | ||
| 216 | - free(entry); | ||
| 217 | -} | ||
| 218 | - | ||
| 219 | - | ||
| 220 | -stx_cache_entry_t *stx_cache_entry_lookup(stx_cache_t *cache, const void *key) | ||
| 221 | -{ | ||
| 222 | - unsigned long bucket; | ||
| 223 | - stx_cache_entry_t *entry; | ||
| 224 | - | ||
| 225 | - cache->lookups++; | ||
| 226 | - bucket = cache->key_hash_fn(key) % cache->hash_size; | ||
| 227 | - for (entry = cache->table[bucket]; entry; entry = entry->next) { | ||
| 228 | - if (!entry->delete_pending && cache->key_cmp_fn(key, entry->key) == 0) | ||
| 229 | - break; | ||
| 230 | - } | ||
| 231 | - if (entry) { | ||
| 232 | - cache->hits++; | ||
| 233 | - if (entry->ref_count == 0) | ||
| 234 | - STX_CLIST_REMOVE_LINK(&(entry->lru_link)); | ||
| 235 | - entry->ref_count++; | ||
| 236 | - } | ||
| 237 | - | ||
| 238 | - return entry; | ||
| 239 | -} | ||
| 240 | - | ||
| 241 | - | ||
| 242 | -void stx_cache_entry_release(stx_cache_t *cache, stx_cache_entry_t *entry) | ||
| 243 | -{ | ||
| 244 | - if (entry->ref_count == 0) | ||
| 245 | - return; | ||
| 246 | - | ||
| 247 | - entry->ref_count--; | ||
| 248 | - | ||
| 249 | - if (entry->ref_count == 0) { | ||
| 250 | - STX_CLIST_APPEND_LINK(&(entry->lru_link), &(cache->lru_list)); | ||
| 251 | - if (entry->delete_pending) | ||
| 252 | - stx_cache_entry_delete(cache, entry); | ||
| 253 | - } | ||
| 254 | -} | ||
| 255 | - | ||
| 256 | - | ||
| 257 | -int stx_cache_entry_insert(stx_cache_t *cache, stx_cache_entry_t *entry) | ||
| 258 | -{ | ||
| 259 | - stx_cache_entry_t *old_entry; | ||
| 260 | - unsigned long bucket; | ||
| 261 | - | ||
| 262 | - /* | ||
| 263 | - * If cache capacity is exceeded, try to remove LRU entries till there is | ||
| 264 | - * enough room or LRU list is empty. | ||
| 265 | - */ | ||
| 266 | - while (cache->cur_weight + entry->weight > cache->max_weight) { | ||
| 267 | - old_entry = stx_cache_entry_getlru(cache); | ||
| 268 | - if (!old_entry) { | ||
| 269 | - /* cache capacity is exceeded and all entries are in use */ | ||
| 270 | - return -1; | ||
| 271 | - } | ||
| 272 | - stx_cache_entry_delete(cache, old_entry); | ||
| 273 | - } | ||
| 274 | - | ||
| 275 | - /* If cache size is exceeded, remove LRU entry */ | ||
| 276 | - if (cache->cur_size >= cache->max_size) { | ||
| 277 | - old_entry = stx_cache_entry_getlru(cache); | ||
| 278 | - if (!old_entry) { | ||
| 279 | - /* cache size is exceeded and all entries are in use */ | ||
| 280 | - return -1; | ||
| 281 | - } | ||
| 282 | - stx_cache_entry_delete(cache, old_entry); | ||
| 283 | - } | ||
| 284 | - | ||
| 285 | - /* Don't add duplicate entries in the cache */ | ||
| 286 | - bucket = cache->key_hash_fn(entry->key) % cache->hash_size; | ||
| 287 | - for (old_entry = cache->table[bucket]; old_entry; | ||
| 288 | - old_entry = old_entry->next) { | ||
| 289 | - if (!old_entry->delete_pending && | ||
| 290 | - cache->key_cmp_fn(entry->key, old_entry->key) == 0) | ||
| 291 | - break; | ||
| 292 | - } | ||
| 293 | - if (old_entry) | ||
| 294 | - stx_cache_entry_delete(cache, old_entry); | ||
| 295 | - | ||
| 296 | - /* Insert in the hash table */ | ||
| 297 | - entry->next = cache->table[bucket]; | ||
| 298 | - cache->table[bucket] = entry; | ||
| 299 | - entry->pthis = &cache->table[bucket]; | ||
| 300 | - if (entry->next) | ||
| 301 | - entry->next->pthis = &entry->next; | ||
| 302 | - entry->ref_count++; | ||
| 303 | - | ||
| 304 | - cache->inserts++; | ||
| 305 | - cache->cur_size++; | ||
| 306 | - cache->cur_weight += entry->weight; | ||
| 307 | - | ||
| 308 | - return 0; | ||
| 309 | -} | ||
| 310 | - | ||
| 311 | - | ||
| 312 | -stx_cache_entry_t *stx_cache_entry_getlru(stx_cache_t *cache) | ||
| 313 | -{ | ||
| 314 | - if (STX_CLIST_IS_EMPTY(&(cache->lru_list))) | ||
| 315 | - return NULL; | ||
| 316 | - | ||
| 317 | - return STX_CACHE_ENTRY_PTR(STX_CLIST_HEAD(&(cache->lru_list))); | ||
| 318 | -} | ||
| 319 | - | ||
| 320 | - | ||
| 321 | -int stx_cache_entry_sizeof(void) | ||
| 322 | -{ | ||
| 323 | - return (int)sizeof(stx_cache_entry_t); | ||
| 324 | -} | ||
| 325 | - | ||
| 326 | - | ||
| 327 | -void *stx_cache_entry_getdata(stx_cache_entry_t *entry) | ||
| 328 | -{ | ||
| 329 | - return entry->data; | ||
| 330 | -} | ||
| 331 | - | ||
| 332 | - | ||
| 333 | -void *stx_cache_entry_getkey(stx_cache_entry_t *entry) | ||
| 334 | -{ | ||
| 335 | - return entry->key; | ||
| 336 | -} | ||
| 337 | - | ||
| 338 | - | ||
| 339 | -size_t stx_cache_entry_getweight(stx_cache_entry_t *entry) | ||
| 340 | -{ | ||
| 341 | - return entry->weight; | ||
| 342 | -} | ||
| 343 | - |
-
请 注册 或 登录 后发表评论