APRDesign.html 16 KB

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