1<HTML> 2<HEAD><TITLE>APR Design Document</TITLE></HEAD> 3<BODY> 4<h1>Design of APR</h1> 5 6<p>The Apache Portable Run-time libraries have been designed to provide a common 7interface to low level routines across any platform. The original goal of APR 8was to combine all code in Apache to one common code base. This is not the 9correct approach however, so the goal of APR has changed. There are places 10where common code is not a good thing. For example, how to map requests 11to either threads or processes should be platform specific. APR's place 12is now to combine any code that can be safely combined without sacrificing 13performance.</p> 14 15<p>To this end we have created a set of operations that are required for cross 16platform development. There may be other types that are desired and those 17will be implemented in the future.</p> 18 19<p>This document will discuss the structure of APR, and how best to contribute 20code to the effort.</p> 21 22<h2>APR On Windows and Netware</h2> 23 24<p>APR on Windows and Netware is different from APR on all other systems, 25because those platforms don't use autoconf. On Unix, apr_private.h (private to 26APR) and apr.h (public, used by applications that use APR) are generated by 27autoconf from acconfig.h and apr.h.in respectively. On Windows (and Netware), 28apr_private.h and apr.h are created from apr_private.hw (apr_private.hwn) 29and apr.hw (apr.hwn) respectively.</p> 30 31<p> <strong> 32 If you add code to acconfig.h or tests to configure.in or aclocal.m4, 33 please give some thought to whether or not Windows and Netware need 34 these additions as well. A general rule of thumb, is that if it is 35 a feature macro, such as APR_HAS_THREADS, Windows and Netware need it. 36 In other words, if the definition is going to be used in a public APR 37 header file, such as apr_general.h, Windows needs it. 38 39 The only time it is safe to add a macro or test without also adding 40 the macro to apr*.h[n]w, is if the macro tells APR how to build. For 41 example, a test for a header file does not need to be added to Windows. 42</strong></p> 43 44<h2>APR Features</h2> 45 46<p>One of the goals of APR is to provide a common set of features across all 47platforms. This is an admirable goal, it is also not realistic. We cannot 48expect to be able to implement ALL features on ALL platforms. So we are 49going to do the next best thing. Provide a common interface to ALL APR 50features on MOST platforms.</p> 51 52<p>APR developers should create FEATURE MACROS for any feature that is not 53available on ALL platforms. This should be a simple definition which has 54the form:</p> 55 56<code>APR_HAS_FEATURE</code> 57 58<p>This macro should evaluate to true if APR has this feature on this platform. 59For example, Linux and Windows have mmap'ed files, and APR is providing an 60interface for mmapp'ing a file. On both Linux and Windows, APR_HAS_MMAP 61should evaluate to one, and the ap_mmap_* functions should map files into 62memory and return the appropriate status codes.</p> 63 64<p>If your OS of choice does not have mmap'ed files, APR_HAS_MMAP should 65evaluate to zero, and all ap_mmap_* functions should not be defined. The 66second step is a precaution that will allow us to break at compile time if a 67programmer tries to use unsupported functions.</p> 68 69<h2>APR types</h2> 70 71<p>The base types in APR</p> 72 73<ul> 74<li>dso<br> 75 Shared library routines 76<li>mmap<br> 77 Memory-mapped files 78<li>poll<br> 79 Polling I/O 80<li>time<br> 81 Time 82<li>user<br> 83 Users and groups 84<li>locks<br> 85 Process and thread locks (critical sections) 86<li>shmem<br> 87 Shared memory 88<li>file_io<br> 89 File I/O, including pipes 90<li>atomic<br> 91 Atomic integer operations 92<li>strings<br> 93 String handling routines 94<li>memory<br> 95 Pool-based memory allocation 96<li>passwd<br> 97 Reading passwords from the terminal 98<li>tables<br> 99 Tables and hashes 100<li>network_io<br> 101 Network I/O 102<li>threadproc<br> 103 Threads and processes 104<li>misc<br> 105 Any APR type which doesn't have any other place to belong. This 106 should be used sparingly. 107<li>support<br> 108 Functions meant to be used across multiple APR types. This area 109 is for internal functions only. If a function is exposed, it should 110 not be put here. 111</ul> 112 113<h2>Directory Structure</h2> 114 115<p>Each type has a base directory. Inside this base directory, are 116subdirectories, which contain the actual code. These subdirectories are named 117after the platforms the are compiled on. Unix is also used as a common 118directory. If the code you are writing is POSIX based, you should look at the 119code in the unix directory. A good rule of thumb, is that if more than half 120your code needs to be ifdef'ed out, and the structures required for your code 121are substantively different from the POSIX code, you should create a new 122directory.</p> 123 124<p>Currently, the APR code is written for Unix, BeOS, Windows, and OS/2. An 125example of the directory structure is the file I/O directory:</p> 126 127<pre> 128apr 129 | 130 -> file_io 131 | 132 -> unix The Unix and common base code 133 | 134 -> win32 The Windows code 135 | 136 -> os2 The OS/2 code 137</pre> 138 139<p>Obviously, BeOS does not have a directory. This is because BeOS is currently 140using the Unix directory for it's file_io.</p> 141 142<p>There are a few special top level directories. These are test and include. 143Test is a directory which stores all test programs. It is expected 144that if a new type is developed, there will also be a new test program, to 145help people port this new type to different platforms. A small document 146describing how to create new tests that integrate with the test suite can be 147found in the test/ directory. Include is a directory which stores all 148required APR header files for external use.</p> 149 150<h2>Creating an APR Type</h2> 151 152<p>The current design of APR requires that most APR types be incomplete. 153It is not possible to write flexible portable code if programs can access 154the internals of APR types. This is because different platforms are 155likely to define different native types. There are only two exceptions to 156this rule:</p> 157 158<ul> 159<li>The first exception to this rule is if the type can only reasonably be 160implemented one way. For example, time is a complete type because there 161is only one reasonable time implementation. 162 163<li>The second exception to the incomplete type rule can be found in 164apr_portable.h. This file defines the native types for each platform. 165Using these types, it is possible to extract native types for any APR type.</p> 166</ul> 167 168<p>For this reason, each platform defines a structure in their own directories. 169Those structures are then typedef'ed in an external header file. For example 170in file_io/unix/fileio.h:</p> 171 172<pre> 173 struct ap_file_t { 174 apr_pool_t *cntxt; 175 int filedes; 176 FILE *filehand; 177 ... 178 } 179</pre> 180 181<p>In include/apr_file_io.h:</p> 182 </pre> 183 typedef struct ap_file_t ap_file_t; 184 </pre> 185 186<p> This will cause a compiler error if somebody tries to access the filedes 187field in this structure. Windows does not have a filedes field, so obviously, 188it is important that programs not be able to access these.</p> 189 190<p>You may notice the apr_pool_t field. Most APR types have this field. This 191type is used to allocate memory within APR. Because every APR type has a pool, 192any APR function can allocate memory if it needs to. This is very important 193and it is one of the reasons that APR works. If you create a new type, you 194must add a pool to it. If you do not, then all functions that operate on that 195type will need a pool argument.</p> 196 197<h2>New Function</h2> 198 199<p>When creating a new function, please try to adhere to these rules.</p> 200 201<ul> 202<li> Result arguments should be the first arguments. 203<li> If a function needs a pool, it should be the last argument. 204<li> These rules are flexible, especially if it makes the code easier 205 to understand because it mimics a standard function. 206</ul> 207 208<h2>Documentation</h2> 209 210<p>Whenever a new function is added to APR, it MUST be documented. New 211functions will not be committed unless there are docs to go along with them. 212The documentation should be a comment block above the function in the header 213file.</p> 214 215<p>The format for the comment block is:</p> 216 217<pre> 218 /** 219 * Brief description of the function 220 * @param parma_1_name explanation 221 * @param parma_2_name explanation 222 * @param parma_n_name explanation 223 * @tip Any extra information people should know. 224 * @deffunc function prototype if required 225 */ 226</pre> 227 228<p>For an actual example, look at any file in the include directory. The 229reason the docs are in the header files is to ensure that the docs always 230reflect the current code. If you change parameters or return values for a 231function, please be sure to update the documentation.</p> 232 233<h2>APR Error reporting</h2> 234 235<p>Most APR functions should return an ap_status_t type. The only time an 236APR function does not return an ap_status_t is if it absolutely CAN NOT 237fail. Examples of this would be filling out an array when you know you are 238not beyond the array's range. If it cannot fail on your platform, but it 239could conceivably fail on another platform, it should return an ap_status_t. 240Unless you are sure, return an ap_status_t.</p> 241 242<strong> 243 This includes functions that return TRUE/FALSE values. How that 244 is handled is discussed below 245</strong> 246 247<p>All platforms return errno values unchanged. Each platform can also have 248one system error type, which can be returned after an offset is added. 249There are five types of error values in APR, each with its own offset.</p> 250 251<!-- This should be turned into a table, but I am lazy today --> 252<pre> 253 Name Purpose 2540) This is 0 for all platforms and isn't really defined 255 anywhere, but it is the offset for errno values. 256 (This has no name because it isn't actually defined, 257 but for completeness we are discussing it here). 258 2591) APR_OS_START_ERROR This is platform dependent, and is the offset at which 260 APR errors start to be defined. Error values are 261 defined as anything which caused the APR function to 262 fail. APR errors in this range should be named 263 APR_E* (i.e. APR_ENOSOCKET) 264 2652) APR_OS_START_STATUS This is platform dependent, and is the offset at which 266 APR status values start. Status values do not indicate 267 success or failure, and should be returned if 268 APR_SUCCESS does not make sense. APR status codes in 269 this range should be name APR_* (i.e. APR_DETACH) 270 2714) APR_OS_START_USEERR This is platform dependent, and is the offset at which 272 APR apps can begin to add their own error codes. 273 2743) APR_OS_START_SYSERR This is platform dependent, and is the offset at which 275 system error values begin. 276</pre> 277 278<strong>The difference in naming between APR_OS_START_ERROR and 279APR_OS_START_STATUS mentioned above allows programmers to easily determine if 280the error code indicates an error condition or a status condition.</strong> 281 282<p>If your function has multiple return codes that all indicate success, but 283with different results, or if your function can only return PASS/FAIL, you 284should still return an apr_status_t. In the first case, define one 285APR status code for each return value, an example of this is 286<code>apr_proc_wait</code>, which can only return APR_CHILDDONE, 287APR_CHILDNOTDONE, or an error code. In the second case, please return 288APR_SUCCESS for PASS, and define a new APR status code for failure, an 289example of this is <code>apr_compare_users</code>, which can only return 290APR_SUCCESS, APR_EMISMATCH, or an error code.</p> 291 292<p>All of these definitions can be found in apr_errno.h for all platforms. When 293an error occurs in an APR function, the function must return an error code. 294If the error occurred in a system call and that system call uses errno to 295report an error, then the code is returned unchanged. For example: </p> 296 297<pre> 298 if (open(fname, oflags, 0777) < 0) 299 return errno; 300</pre> 301 302<p>The next place an error can occur is a system call that uses some error value 303other than the primary error value on a platform. This can also be handled 304by APR applications. For example:</p> 305 306<pre> 307 if (CreateFile(fname, oflags, sharemod, NULL, 308 createflags, attributes, 0) == INVALID_HANDLE_VALUE 309 return (GetLAstError() + APR_OS_START_SYSERR); 310</pre> 311 312<p>These two examples implement the same function for two different platforms. 313Obviously even if the underlying problem is the same on both platforms, this 314will result in two different error codes being returned. This is OKAY, and 315is correct for APR. APR relies on the fact that most of the time an error 316occurs, the program logs the error and continues, it does not try to 317programatically solve the problem. This does not mean we have not provided 318support for programmatically solving the problem, it just isn't the default 319case. We'll get to how this problem is solved in a little while.</p> 320 321<p>If the error occurs in an APR function but it is not due to a system call, 322but it is actually an APR error or just a status code from APR, then the 323appropriate code should be returned. These codes are defined in apr_errno.h 324and should be self explanatory.</p> 325 326<p>No APR code should ever return a code between APR_OS_START_USEERR and 327APR_OS_START_SYSERR, those codes are reserved for APR applications.</p> 328 329<p>To programmatically correct an error in a running application, the error 330codes need to be consistent across platforms. This should make sense. APR 331has provided macros to test for status code equivalency. For example, to 332determine if the code that you received from the APR function means EOF, you 333would use the macro APR_STATUS_IS_EOF().</p> 334 335<p>Why did APR take this approach? There are two ways to deal with error 336codes portably.</p> 337 338<ol type=1> 339<li> Return the same error code across all platforms. 340<li> Return platform specific error codes and convert them when necessary. 341</ol> 342 343<p>The problem with option number one is that it takes time to convert error 344codes to a common code, and most of the time programs want to just output 345an error string. If we convert all errors to a common subset, we have four 346steps to output an error string:</p> 347 348<p>The second problem with option 1, is that it is a lossy conversion. For 349example, Windows and OS/2 have a couple hundred error codes, but POSIX errno 350only defines about 50 errno values. This means that if we convert to a 351canonical error value immediately, there is no way for the programmer to 352get the actual system error.</p> 353 354<pre> 355 make syscall that fails 356 convert to common error code step 1 357 return common error code 358 check for success 359 call error output function step 2 360 convert back to system error step 3 361 output error string step 4 362</pre> 363 364<p>By keeping the errors platform specific, we can output error strings in two 365steps.</p> 366 367<pre> 368 make syscall that fails 369 return error code 370 check for success 371 call error output function step 1 372 output error string step 2 373</pre> 374 375<p>Less often, programs change their execution based on what error was returned. 376This is no more expensive using option 2 than it is using option 1, but we 377put the onus of converting the error code on the programmer themselves. 378For example, using option 1:</p> 379 380<pre> 381 make syscall that fails 382 convert to common error code 383 return common error code 384 decide execution based on common error code 385</pre> 386 387<p>Using option 2:</p> 388 389<pre> 390 make syscall that fails 391 return error code 392 convert to common error code (using ap_canonical_error) 393 decide execution based on common error code 394</pre> 395 396<p>Finally, there is one more operation on error codes. You can get a string 397that explains in human readable form what has happened. To do this using 398APR, call ap_strerror().</p> 399 400