From e8ec7aa8e38a93f5b034ac74cebce5de23710317 Mon Sep 17 00:00:00 2001
From: hongbotian The Apache Portable Run-time libraries have been designed to provide a common
+interface to low level routines across any platform. The original goal of APR
+was to combine all code in Apache to one common code base. This is not the
+correct approach however, so the goal of APR has changed. There are places
+where common code is not a good thing. For example, how to map requests
+to either threads or processes should be platform specific. APR's place
+is now to combine any code that can be safely combined without sacrificing
+performance. To this end we have created a set of operations that are required for cross
+platform development. There may be other types that are desired and those
+will be implemented in the future. This document will discuss the structure of APR, and how best to contribute
+code to the effort. APR on Windows and Netware is different from APR on all other systems,
+because those platforms don't use autoconf. On Unix, apr_private.h (private to
+APR) and apr.h (public, used by applications that use APR) are generated by
+autoconf from acconfig.h and apr.h.in respectively. On Windows (and Netware),
+apr_private.h and apr.h are created from apr_private.hw (apr_private.hwn)
+and apr.hw (apr.hwn) respectively.
+ If you add code to acconfig.h or tests to configure.in or aclocal.m4,
+ please give some thought to whether or not Windows and Netware need
+ these additions as well. A general rule of thumb, is that if it is
+ a feature macro, such as APR_HAS_THREADS, Windows and Netware need it.
+ In other words, if the definition is going to be used in a public APR
+ header file, such as apr_general.h, Windows needs it.
+
+ The only time it is safe to add a macro or test without also adding
+ the macro to apr*.h[n]w, is if the macro tells APR how to build. For
+ example, a test for a header file does not need to be added to Windows.
+ One of the goals of APR is to provide a common set of features across all
+platforms. This is an admirable goal, it is also not realistic. We cannot
+expect to be able to implement ALL features on ALL platforms. So we are
+going to do the next best thing. Provide a common interface to ALL APR
+features on MOST platforms. APR developers should create FEATURE MACROS for any feature that is not
+available on ALL platforms. This should be a simple definition which has
+the form: This macro should evaluate to true if APR has this feature on this platform.
+For example, Linux and Windows have mmap'ed files, and APR is providing an
+interface for mmapp'ing a file. On both Linux and Windows, APR_HAS_MMAP
+should evaluate to one, and the ap_mmap_* functions should map files into
+memory and return the appropriate status codes. If your OS of choice does not have mmap'ed files, APR_HAS_MMAP should
+evaluate to zero, and all ap_mmap_* functions should not be defined. The
+second step is a precaution that will allow us to break at compile time if a
+programmer tries to use unsupported functions. The base types in APR Each type has a base directory. Inside this base directory, are
+subdirectories, which contain the actual code. These subdirectories are named
+after the platforms the are compiled on. Unix is also used as a common
+directory. If the code you are writing is POSIX based, you should look at the
+code in the unix directory. A good rule of thumb, is that if more than half
+your code needs to be ifdef'ed out, and the structures required for your code
+are substantively different from the POSIX code, you should create a new
+directory. Currently, the APR code is written for Unix, BeOS, Windows, and OS/2. An
+example of the directory structure is the file I/O directory: Obviously, BeOS does not have a directory. This is because BeOS is currently
+using the Unix directory for it's file_io. There are a few special top level directories. These are test and include.
+Test is a directory which stores all test programs. It is expected
+that if a new type is developed, there will also be a new test program, to
+help people port this new type to different platforms. A small document
+describing how to create new tests that integrate with the test suite can be
+found in the test/ directory. Include is a directory which stores all
+required APR header files for external use. The current design of APR requires that most APR types be incomplete.
+It is not possible to write flexible portable code if programs can access
+the internals of APR types. This is because different platforms are
+likely to define different native types. There are only two execptions to
+this rule:Design of APR
+
+APR On Windows and Netware
+
+APR Features
+
+APR_HAS_FEATURE
+
+APR types
+
+
+
+
+
+ Shared library routines
+
+ Memory-mapped files
+
+ Polling I/O
+
+ Time
+
+ Users and groups
+
+ Process and thread locks (critical sections)
+
+ Shared memory
+
+ File I/O, including pipes
+
+ Atomic integer operations
+
+ String handling routines
+
+ Pool-based memory allocation
+
+ Reading passwords from the terminal
+
+ Tables and hashes
+
+ Network I/O
+
+ Threads and processes
+
+ Any APR type which doesn't have any other place to belong. This
+ should be used sparingly.
+
+ Functions meant to be used across multiple APR types. This area
+ is for internal functions only. If a function is exposed, it should
+ not be put here.
+Directory Structure
+
+
+apr
+ |
+ -> file_io
+ |
+ -> unix The Unix and common base code
+ |
+ -> win32 The Windows code
+ |
+ -> os2 The OS/2 code
+
+
+Creating an APR Type
+
+
+
For this reason, each platform defines a structure in their own directories. +Those structures are then typedef'ed in an external header file. For example +in file_io/unix/fileio.h:
+ ++ struct ap_file_t { + apr_pool_t *cntxt; + int filedes; + FILE *filehand; + ... + } ++ +
In include/apr_file_io.h:
+ + typedef struct ap_file_t ap_file_t; + + +This will cause a compiler error if somebody tries to access the filedes +field in this structure. Windows does not have a filedes field, so obviously, +it is important that programs not be able to access these.
+ +You may notice the apr_pool_t field. Most APR types have this field. This +type is used to allocate memory within APR. Because every APR type has a pool, +any APR function can allocate memory if it needs to. This is very important +and it is one of the reasons that APR works. If you create a new type, you +must add a pool to it. If you do not, then all functions that operate on that +type will need a pool argument.
+ +When creating a new function, please try to adhere to these rules.
+ +Whenever a new function is added to APR, it MUST be documented. New +functions will not be committed unless there are docs to go along with them. +The documentation should be a comment block above the function in the header +file.
+ +The format for the comment block is:
+ ++ /** + * Brief description of the function + * @param parma_1_name explanation + * @param parma_2_name explanation + * @param parma_n_name explanation + * @tip Any extra information people should know. + * @deffunc function prototype if required + */ ++ +
For an actual example, look at any file in the include directory. The +reason the docs are in the header files is to ensure that the docs always +reflect the current code. If you change paramters or return values for a +function, please be sure to update the documentation.
+ +Most APR functions should return an ap_status_t type. The only time an +APR function does not return an ap_status_t is if it absolutely CAN NOT +fail. Examples of this would be filling out an array when you know you are +not beyond the array's range. If it cannot fail on your platform, but it +could conceivably fail on another platform, it should return an ap_status_t. +Unless you are sure, return an ap_status_t.
+ + + This includes functions that return TRUE/FALSE values. How that + is handled is discussed below + + +All platforms return errno values unchanged. Each platform can also have +one system error type, which can be returned after an offset is added. +There are five types of error values in APR, each with it's own offset.
+ + ++ Name Purpose +0) This is 0 for all platforms and isn't really defined + anywhere, but it is the offset for errno values. + (This has no name because it isn't actually defined, + but for completeness we are discussing it here). + +1) APR_OS_START_ERROR This is platform dependent, and is the offset at which + APR errors start to be defined. Error values are + defined as anything which caused the APR function to + fail. APR errors in this range should be named + APR_E* (i.e. APR_ENOSOCKET) + +2) APR_OS_START_STATUS This is platform dependent, and is the offset at which + APR status values start. Status values do not indicate + success or failure, and should be returned if + APR_SUCCESS does not make sense. APR status codes in + this range should be name APR_* (i.e. APR_DETACH) + +4) APR_OS_START_USEERR This is platform dependent, and is the offset at which + APR apps can begin to add their own error codes. + +3) APR_OS_START_SYSERR This is platform dependent, and is the offset at which + system error values begin. ++ +The difference in naming between APR_OS_START_ERROR and +APR_OS_START_STATUS mentioned above allows programmers to easily determine if +the error code indicates an error condition or a status codition. + +
If your function has multiple return codes that all indicate success, but
+with different results, or if your function can only return PASS/FAIL, you
+should still return an apr_status_t. In the first case, define one
+APR status code for each return value, an example of this is
+apr_proc_wait
, which can only return APR_CHILDDONE,
+APR_CHILDNOTDONE, or an error code. In the second case, please return
+APR_SUCCESS for PASS, and define a new APR status code for failure, an
+example of this is apr_compare_users
, which can only return
+APR_SUCCESS, APR_EMISMATCH, or an error code.
All of these definitions can be found in apr_errno.h for all platforms. When +an error occurs in an APR function, the function must return an error code. +If the error occurred in a system call and that system call uses errno to +report an error, then the code is returned unchanged. For example:
+ ++ if (open(fname, oflags, 0777) < 0) + return errno; ++ +
The next place an error can occur is a system call that uses some error value +other than the primary error value on a platform. This can also be handled +by APR applications. For example:
+ ++ if (CreateFile(fname, oflags, sharemod, NULL, + createflags, attributes, 0) == INVALID_HANDLE_VALUE + return (GetLAstError() + APR_OS_START_SYSERR); ++ +
These two examples implement the same function for two different platforms. +Obviously even if the underlying problem is the same on both platforms, this +will result in two different error codes being returned. This is OKAY, and +is correct for APR. APR relies on the fact that most of the time an error +occurs, the program logs the error and continues, it does not try to +programatically solve the problem. This does not mean we have not provided +support for programmatically solving the problem, it just isn't the default +case. We'll get to how this problem is solved in a little while.
+ +If the error occurs in an APR function but it is not due to a system call, +but it is actually an APR error or just a status code from APR, then the +appropriate code should be returned. These codes are defined in apr_errno.h +and should be self explanatory.
+ +No APR code should ever return a code between APR_OS_START_USEERR and +APR_OS_START_SYSERR, those codes are reserved for APR applications.
+ +To programmatically correct an error in a running application, the error +codes need to be consistent across platforms. This should make sense. APR +has provided macros to test for status code equivalency. For example, to +determine if the code that you received from the APR function means EOF, you +would use the macro APR_STATUS_IS_EOF().
+ +Why did APR take this approach? There are two ways to deal with error +codes portably.
+ +The problem with option number one is that it takes time to convert error +codes to a common code, and most of the time programs want to just output +an error string. If we convert all errors to a common subset, we have four +steps to output an error string:
+ +The seocnd problem with option 1, is that it is a lossy conversion. For +example, Windows and OS/2 have a couple hundred error codes, but POSIX errno +only defines about 50 errno values. This means that if we convert to a +canonical error value immediately, there is no way for the programmer to +get the actual system error.
+ ++ make syscall that fails + convert to common error code step 1 + return common error code + check for success + call error output function step 2 + convert back to system error step 3 + output error string step 4 ++ +
By keeping the errors platform specific, we can output error strings in two +steps.
+ ++ make syscall that fails + return error code + check for success + call error output function step 1 + output error string step 2 ++ +
Less often, programs change their execution based on what error was returned. +This is no more expensive using option 2 than it is using option 1, but we +put the onus of converting the error code on the programmer themselves. +For example, using option 1:
+ ++ make syscall that fails + convert to common error code + return common error code + decide execution based on common error code ++ +
Using option 2:
+ ++ make syscall that fails + return error code + convert to common error code (using ap_canonical_error) + decide execution based on common error code ++ +
Finally, there is one more operation on error codes. You can get a string +that explains in human readable form what has happened. To do this using +APR, call ap_strerror().
+ -- cgit