libapr(apache portable runtime) programming tutorial INOUE Seiichiro 2005/8/29 This is a tutorial on how to use libapr(apache portable runtime). Tutorial Availability

Please look for updates on . libapr skeleton code

I believe it is a good idea to write 'skeleton code' at first, when you start to learn a new library or a new framework programming. 'skeleton code' is the smallest source code, but you can compile and execute it (although it usually do nothing useful). Fortunately, libapr's skeleton code is much simpler than other modern frameworks. Let's take a look at apr-skeleton.c. We call apr_initialize() at the initialization, and call apr_terminate() at the finalization. That's all. As you can imagine, the code does nothing. libapr is not a framework library. Accordingly, libapr doesn't help you to design the whole structure of the source code. There are pros and cons. Pros is that it is easy to use libapr with the other existing code. Cons is that you need to design the whole structure of the code by yourself when you use libapr. Here, we have some libapr programming styles and rules: naming rule is very simple and clear. opaque data types are commonly used (a.k.a. incomplete types) most of return types are apr_status_t. As a result, result-arguments are commonly used. memory pool rules We are able to see these styles in the following code. /* excerpted from */ apr_status_t rv; apr_pool_t *mp; rv = apr_pool_create(&mp, NULL); I will describe the meaning of the code. Here, please take a look at only style. You can see apr_ prefix. The apr_ prefix indicates that the symbol is in libapr naming scope. You can see _t suffix. It indicates that the symbol is a type name. apr_pool_t is opaque type. It means the type's structure is not public. By OO(Object Oriented) terminology, all member variables are private. You can't touch them directly. Furthermore, you can't see them in public header files. All you can do for the type is to call APIs such as apr_foo_bar() functions. Most importantly, you can't allocate their instance memories directly. All you can do is to call construct APIs. Only libapr knows how to construct and destruct the objects. As you see, apr_pool_create()'s return type is apr_status_t. apr_status_t is either status code or error code. apr_status_t is a commonly used as return types of most APIs. Accordingly, we can get results from functions by arguments. Such arguments are called result-argument. There are many result-arguments in the libapr world. In general, if you see apr_foo_t type, you will see apr_foo_bar() functions, which are related to apr_foo_t type. The following code is a typical pseudo code. /* pseudo code of libapr. error checks omitted */ apr_status_t rv; apr_foo_t *foo; rv = apr_foo_create(&foo, args...);/* create a @foo object by @args */ rv = apr_foo_do_something(foo, args...); /* do something with @foo */ apr_foo_destroy(foo); /* destroy the @foo object. Sometimes, this is done implicitly by destroying related memory pool. Please see below */ memory pool (apr_pool_t)

Most of libapr APIs are dependent on memory pool. By memory pool, you can easily manage a set of memory chunks. Imagine the case without memory pool system, where you allocate several memory chunks. You have to free each of them. If you have ten memory chunks, you have to free ten times, otherwise you would suffer from memory leak bugs. Memory pool solves this issue. After you allocate one memory pool, you can allocate multiple memory chunks from the pool. To free them, all you have to do is to destroy the memory pool. By which, you can free all the memory chunks. There are two good points. First, as stated above, it is defensive against memory leak bugs. Second, allocation costs of memory chunks become relatively lower. In a sense, memory pool forces you to obey a session-oriented programming. A memory pool is a kind of a session context, that is, a set of objects that have the same lifetimes. You can control a set of objects within a session context. At the beginning of a session you create a memory pool. Then, you create objects in the memory pool during the session. Note that you don't need to care about their lifetimes. Finally, at the end of the session all you have to do is to destroy the memory pool. REMARK: In general, objects lifetime control is the most difficult part in programming. Thus, there are many other techniques for it, such as smart pointer, GC(garbage collection) and so on. Note that it is a bit hard to use such techniques at the same time. Since memory pool is one of such techniques, you have to be careful about the mixture. REMARK: In the future, memory pool would become less important than now in libapr. Please refer to . There are three basic APIs as follows: /* excerpted from apr_pools.h */ APR_DECLARE(apr_status_t) apr_pool_create(apr_pool_t **newpool, apr_pool_t *parent); APR_DECLARE(void *) apr_palloc(apr_pool_t *p, apr_size_t size); APR_DECLARE(void) apr_pool_destroy(apr_pool_t *p); We create a memory pool by apr_pool_create(). The memory pool is alive until you call apr_pool_destroy(). The first argument of apr_pool_create() is a result argument. A newly created memory pool object, apr_pool_t, is returned by this API call. We call apr_palloc() to get a memory chunk by specifing the chunk's size. Please take a look at to know the usage. /* excerpted from */ apr_pool_t *mp; /* create a memory pool. */ apr_pool_create(&mp, NULL); /* allocate memory chunks from the memory pool */ char *buf1; buf1 = apr_palloc(mp, MEM_ALLOC_SIZE); In a nutshell, we can use apr_palloc() like malloc(3). We can also call apr_pcalloc(). As you can guess, apr_pcalloc() is similar to calloc(3). apr_pcalloc() returns a zero-cleard memory chunk. If you use malloc(3)/calloc(3), you need to call free(3) for the allocated memories. In contrast, you don't need to free each memory chunks in memory pool. You just call apr_pool_destroy() for the memory pool and it frees all the memory chunks. REMARK: There is no limitation about memory chunk size that you can allocate by apr_palloc(). Nevertheless, it isn't a good idea to allocate large size memory chunk in memory pool. That is because memory pool is essentially designed for smaller chunks. Actually, the initial size of memory pool is 8 kilo bytes. If you need a large size memory chunk, e.g. over several mega bytes, you shouldn't use memory pool. REMARK: By default, memory pool manager never returns allocated memory back to the system. If a program runs for a long time, it would have problem. I recommend you to specify the upper limit as follows: /* sample code to set the upper limit to make memory pool manager release the memory back to the system */ #define YOUR_POOL_MAX_FREE_SIZE 32 /* apr_pool max free list size */ apr_pool_t *mp; apr_pool_create(&mp, NULL); apr_allocator_t* pa = apr_pool_allocator_get(mp); if (pa) { apr_allocator_max_free_set(pa, YOUR_POOL_MAX_FREE_SIZE); } There are two more APIs you have to know. One is apr_pool_clear(), and the other is apr_pool_cleanup_register(). apr_pool_clear() is similar to apr_pool_destroy(), but the memory pool is still reusable. A typical code is as follows: /* sample code about apr_pool_clear() */ apr_pool_t *mp; apr_pool_create(&mp, NULL); for (i = 0; i < n; ++i) { do_operation(..., mp); apr_pool_clear(mp); } apr_pool_destroy(mp); The memory pool is used in do_operation(), that is, several memory chunks are allocated. If you don't need the memory chunks out of do_operation(), you can call apr_pool_clear(). You are able to reduce the amount of memory usage. If you are familiar with local stack memory system, you can think of memory pool as local stack memory. Calling apr_palloc() is similar to moving SP(stack pointer), and calling apr_pool_clear() is similar to rewinding SP. Both are very light operations. By apr_pool_cleanup_register(), we can have hook functions on memory pool clear/destroy. You have a callback function that is called whenever the memory pool is cleared or destroyed. In the callback functions, you can implement any finalization code depending on the memory pool. The last topic about memory pool is sub pool. Each memory pool is able to have a parent memory pool. Accordingly, memory pools construct trees. The second argument of apr_pool_create() is a parent memory pool. When you pass NULL as the parent memory pool, the newly created memory pool becomes a root memory pool. You can create sub memory pools under the root memory pool. Whene you call apr_pool_destroy() for a memory pool in the tree, the child memory pools are also destroyed. When you call apr_pool_clear() for the memory pool, the memory pool is alive but the child memory pools are destroyed. Whenever a child memory pool is destroyed, the cleanup functions for it mentioned above are called. REMARK: It is a typical bug that you pass NULL as pool cleanup callback function. Instead, you must pass apr_pool_cleanup_null as follows: /* pseudo code about memory pool typical bug */ /* apr_pool_cleanup_register(mp, ANY_CONTEXT_OF_YOUR_CODE, ANY_CALLBACK_OF_YOUR_CODE, NULL); THIS IS A BUG */ /* FIXED */ apr_pool_cleanup_register(mp, ANY_CONTEXT_OF_YOUR_CODE, ANY_CALLBACK_OF_YOUR_CODE, apr_pool_cleanup_null); Error status (apr_status_t)

Most of libapr functions return apr_status_t value. apr_status_t value is each of APR_SUCCESS or the others. APR_SUCCESS just indicates success. A typical code looks as follows: /* pseudo code about apr_status_t check */ apr_status_t rv; rv = apr_pool_create(&mp, NULL); if (rv != APR_SUCCESS) { ERROR-HANDLING; } libapr defines some error statuses such as APR_EINVAL, and some error-check macros such as APR_STATUS_IS_ENOMEM((). Some of them are rather useful on handling portability issues. A typical one is APR_STATUS_IS_EAGAIN() macro. Historically, there are two error numbers that have the same meaning, EAGAIN and EWOULDBLOCK. APR_STATUS_IS_EAGAIN() macro takes care of the issue. Nevertheless, it is almost impossible to deal with all error cases independently from many OSes. libapr doesn't reinvent the wheel on error handling. All libapr does is very simple. In success, return APR_SUCCESS In libapr layer's error, return APR_FOO error code In very common OS error, return APR_FOO error code In most of OS dependent error, return OS error number with the offset I recommend you to follow the simple rules. To compare return value with APR_SUCCESS If you need to know more error details, to compare the values with the other error code One API that you had better know is apr_strerror(). You can show the error description as follows: /* pseudo code about apr_strerror() */ apr_status_t rv; rv = apr_foo_bar(); if (rv != APR_SUCCESS) { char errbuf[256]; apr_strerror(rv, buf, sizeof(buf)); puts(errbuf); /* show the error description */ } file handling

When we handle files, we have to call apr_file_open() at first. Here is the prototype declaration. /* excerpted from apr_file_io.h */ APR_DECLARE(apr_status_t) apr_file_open(apr_file_t **newf, const char *fname, apr_int32_t flag, apr_fileperms_t perm, apr_pool_t *pool); The first argument type is apr_file_t**, which is result argument. Namely, you can create an apr_file_t object by calling apr_file_open(). The second argument is file name path. The third argument is a bit-wised flag. The bit-flags are defined in apr_file_io.h. The fourth argument is file access permission, which has effects on a newly created file. The value is bit-wised flag. The bit-flags are defined in apr_file_info.h. For example, if you want to create a file that has an access permission 0600, i.e. read-write access only by the file owner, you have to specify APR_UREAD|APR_UWRITE. In usual cases, you can use APR_OS_DEFAULT as file permission. The fifth final argument is a memory pool to use. Needless to say, you need to create the memory pool by apr_pool_create(). After we open file, we can handle file by other APIs. We can find them in apr_file_io.h. The basic APIs are apr_file_read() and apr_file_write(). As you expect, apr_file_read() allows us to read something from file, and apr_file_write() allows us to write something to file. Here is the prototype declarations, /* excerpted from apr_file_io.h */ APR_DECLARE(apr_status_t) apr_file_read(apr_file_t *thefile, void *buf, apr_size_t *nbytes); APR_DECLARE(apr_status_t) apr_file_write(apr_file_t *thefile, const void *buf, apr_size_t *nbytes); The third argument of both functions is value-result argument. It means, by which, we specify the input value's length on entry and get the output result's length on exit. In particular, apr_file_read() returns the length of read bytes and apr_file_write() returns the length of written bytes. Here is a sample code. /* pseudo code about apr_file_write() */ strcpy(outbuf, "123456789"); apr_size_t outlen = strlen(outbuf); rv = apr_file_write(fp, outbuf, &outlen); printf("apr_file_write() rv = %d, nbytes = %d\n", rv, outlen); In this case, before calling apr_file_write(), 'outlen' variable's value is 9. Passing in &outlen to apr_file_write() tells the API the writable length is 9. After returning from apr_file_write(), 'outlen' variable's value becomes actual written length. Usually, it is 9 if the file is a local file. Theoretically, it could become a smaller value (e.g. by disk full). We have to call apr_file_close() to close the file. Rather than that, we can implicitly close the file by destroying the memory pool that is passed to apr_file_open(). I prefer explicit closing to such an implicit way. It's just my opinion. REMARK: There are some source code compatibility issues among libapr versions. The third argument of apr_file_open() has APR_FOPEN_ prefix after libapr-1.1.0, althouth it formerly didn't. We should use APR_FOPEN_CREATE instead of APR_CREATE. Please see apr_file_io.h of your using version. Similarly, the fourth argument of apr_file_open() has APR_FPROT_ prefixes after libapr-1.1.0. REMARK: There is a portability issue about file path separator. Unix(POSIX) uses slash('/'), and MS-Windows uses backslash('\') as separator. If you write an application program for both Unix and MS-Windows, I recommend you to canonicalize file pathes to use slash('/') as separators, because MS-Windows accepts it. REMARK: Be careful about apr_file_gets() usage. Calling apr_file_gets() without APR_BUFFERED severely hits performance. This is because that apr_file_gets() internally calls apr_file_read() per one byte. Remember you must open file with APR_BUFFERED flag when you use apr_file_gets(). I recommend you to specify APR_BUFFERED flag except the following cases: When you mmap the file (it causes an error to mmap APR_BUFFERED file) No read/write (e.g. a file only for lock purpose) You are sure that you reads/writes with a buffer big enough REMARK: While you open file with APR_BUFFERED flag and if you call apr_file_trunc() for the file, you must call apr_file_flush() before apr_file_trunc(). Otherwise, the file becomes broken. REMARK: When you open file with APR_BUFFERED flag and the file is shared by multiple threads, APR_XTHREAD flag is also required for the file. Unfortunately, APR_XTHREAD flag on Windows has a side effect. My experiences tell me not to use APR_XTHREAD flag on Windows. We can get file information such as size, timestamp, owner, access permission, and so on. Such the information are in apr_finfo_t structure, which we find in apr_file_info.h. There are two related APIs as follows: /* excerpted from apr_file_io.h */ APR_DECLARE(apr_status_t) apr_file_info_get(apr_finfo_t *finfo, apr_int32_t wanted, apr_file_t *thefile); /* excerpted from apr_file_info.h */ APR_DECLARE(apr_status_t) apr_stat(apr_finfo_t *finfo, const char *fname, apr_int32_t wanted, apr_pool_t *pool); apr_file_info_get() requires apr_file_t object, and apr_stat() requires file name. If we have already opened file and have apr_file_t object, it is better to use apr_file_info_get(). Otherwise, we have to call apr_stat(). Unlike other many types, apr_finfo_t is complete type. Rather than calling API to create object, we have to allocate the apr_finfo_t memory explicitly. Typically, it's allocated in local stack, because what we want to know might be some of the attributes such as file size or timestamp. Note that some memories, e.g. apr_finfo_t::fname, are allocated in the memory pool. This would cause a serious bug. Be careful. Please take a look at about the usage. There are some file handling APIs that works based on file names. For example, apr_file_remove() and apr_file_copy(). You can find them in apr_file_io.h and apr_file_info.h. REMARK: Some APIs have 'wanted' argument that we specify bit-wised flag to get file attributes. The APIs are apr_dir_read(), apr_stat(), apr_lstat(), and apr_file_info_get(). Note that the value of 'wanted' argument could be beyond the OS file system support. and in such a case, the API returns APR_INCOMPLETE. file lock

When we intend to lock file among processes, we can use apr_file_lock(). Historically, there are several confusions about file lock APIs on Unix. Thus, it is very useful that libapr provides one simple API. /* excerpted from apr_file_io.h */ APR_DECLARE(apr_status_t) apr_file_lock(apr_file_t *thefile, int type); APR_DECLARE(apr_status_t) apr_file_unlock(apr_file_t *thefile); apr_file_lock() has two arguments. One is apr_file_t object. The other is flag, by which we specify the lock type. The lock type is either APR_FLOCK_SHARED or APR_FLOCK_EXCLUSIVE. We can use the former as a readable lock, and the latter as a writable lock. To unlock the file, we call apr_file_unlock(). Or, calling apr_file_close() implicitly does unlock. Take a look at about the usage. Additionally, we can use a bitwised-or flag APR_FLOCK_NONBLOCK. Without APR_FLOCK_NONBLOCK flag, calling apr_file_lock() would block. With APR_FLOCK_NONBLOCK, if apr_file_lock() can't lock file, it returns an error code, APR_EAGAIN. We should compare apr_file_lock() return value with APR_SUCCESS. If the value is APR_SUCCESS, the file was successfully locked. Othewrise, we failed to lock the file. file-system directory handling

When we handle file-system directories, we have to call apr_dir_open() at first. By apr_dir_open(), we have an apr_dir_t object. All we can do with apr_dir_t is to scan the directory. The API is apr_dir_read(). Finally, we call apr_dir_close() to close the directory. The prototype declarations are as follows: /* excerpted from apr_file_info.h */ APR_DECLARE(apr_status_t) apr_dir_open(apr_dir_t **new_dir, const char *dirname, apr_pool_t *pool); APR_DECLARE(apr_status_t) apr_dir_read(apr_finfo_t *finfo, apr_int32_t wanted, apr_dir_t *thedir); APR_DECLARE(apr_status_t) apr_dir_close(apr_dir_t *thedir); The first argument of apr_dir_open() is result argument. By which, we can get a newly created apr_dir_t object. The second argument is directory name. The third argument is memory pool to use. The first argument of apr_dir_read() is result argument. As mentioned earlier, apr_finfo_t is complete type. Thus, we have to allocate its memory explicitly. apr_dir_read() returns an entry of the directory by apr_finfo_t. The entry is either file or directory. The second argument is bit-wised flag. The flags are defined in apr_file_info.h with APR_FINFO_ prefix, such as APR_FINFO_SIZE, APR_FINFO_TYPE and APR_FINFO_NAME. The third argument is apr_dir_t object to scan. Here is a sample code: /* pseudo code about apr_dir_read() */ /* no error checks */ apr_pool_t *mp; apr_pool_create(&mp, NULL); const char *dirpath = "/home";/* directory path to scan */ apr_dir_t *dir; apr_dir_open(&dir, dirpath, mp);/* create the apr_dir_t object */ apr_finfo_t dirent; apr_dir_read(&dirent, APR_FINFO_DIRENT, dir);/* the apr_finfo_t object filled */ /* dirent is the first entry of the directory. * the entry is either file or directory. */ apr_dir_close(dir); In the sample code above we call apr_dir_read() only once, but we usually call apr_dir_read() multiple times to scan all files under the directory. To scan all files, we just keep calling apr_dir_read() while it returns APR_SUCCESS. Take a look at about precise usage. /* pseudo code about apr_dir_read() loop. error checks omitted */ /* typical while loop of apr_dir_read() */ apr_dir_open(&dir, dirpath, mp); while ((apr_dir_read(&dirent, APR_FINFO_NAME, dir)) == APR_SUCCESS) { printf("file name is %s\n", dirent.name); } apr_dir_close(dir); As you can imagine, apr_dir_t object has the current position. Calling apr_dir_read() makes the position move forward. We can rewind the (internal)position by apr_dir_rewind(). All the operations we can do with the position are just these two, moving forward and rewinding. As you see in , if you scan directories recursively, you need to call apr_dir_open() recursively. REMARK: On Unix, apr_dir_read() returns apr_finfo_t object whose apr_file_t::fname is NULL character string handling

I assume you are familiar with C language's string APIs, such as strlen(3) and strcpy(3). libapr provides some string APIs. They are almost same as the common(ANSI C) APIs. Why does libapr provide a yet another string APIs? The benefit of libapr's string APIs is related to memory pool. In a common C string handling, we have to write much memory management code. The following code is an example. /* ANSI C string example (a bit naive code) */ /* we concatenate three strings, s1, s2, s3 */ int len1 = strlen(s1); int len2 = strlen(s2); int len3 = strlen(s3); int total_len = len1 + len2 + len3; char *cat_str = malloc(total_len + 1); strcpy(cat_str, s1); strcat(cat_str, s2); strcat(cat_str, s3); /* later, we have to free the allocated memory */ free(cat_str); The same thing is written with libapr as follows: /* pseudo code about libapr string APIs */ apr_pool_t *mp; apr_pool_create(&mp, NULL); /* apr_pstrcat() takes care of both memory allocation and string concatenation. * If the concatenated string is read-only, we should use 'const char*' type. */ const char *cat_str = apr_pstrcat(mp, s1, s2, s3, NULL); /* later, all we have to do is to destroy the memory pool to free all the memory */ apr_pool_destroy(mp); Like apr_pstrcat(), apr_psprintf() allows you to write much simpler code. You can find other string APIs in apr_strings.h. time handling

libapr's time APIs are almost based on POSIX scheme. The value of apr_time_t is the elapsed time since UNIX epoch time(1970/1/1). The big differences are that apr_time_t is 64bit(long long) number and it represents microseconds. The most useful API is apr_time_now(). As you can guess, it returns the current time. You can find the protype declaration in apr_time.h. /* excerpted from apr_time.h */ APR_DECLARE(apr_time_t) apr_time_now(void); In many cases, we need to convert apr_time_t value to the other formats. There are mainly two formats. - apr_time_exp_t (time structure) - string formats (e.g. rfc822) We use the following APIs to convert apr_time_t to apr_time_exp_t structure. /* excerpted from apr_time.h */ APR_DECLARE(apr_status_t) apr_time_exp_gmt(apr_time_exp_t *result, apr_time_t input); APR_DECLARE(apr_status_t) apr_time_exp_lt(apr_time_exp_t *result, apr_time_t input); The apr_time_exp_gmt() returns the result in GMT timezone, and apr_time_exp_lt() returns the result in local timezone. The first argument of both APIs is result argument. We can do the opposite conversion. We use the following API to convert apr_time_exp_t structure to apr_time_t value. /* excerpted from apr_time.h */ APR_DECLARE(apr_status_t) apr_time_exp_get(apr_time_t *result, apr_time_exp_t *input); There are some APIs to convert apr_time_t to various string formats as follows: /* excerpted from apr_time.h */ APR_DECLARE(apr_status_t) apr_rfc822_date(char *date_str, apr_time_t t); APR_DECLARE(apr_status_t) apr_ctime(char *date_str, apr_time_t t); APR_DECLARE(apr_status_t) apr_strftime(char *s, apr_size_t *retsize, apr_size_t max, const char *format, apr_time_exp_t *tm); On the other hand, if we convert such string formats to apr_time_t value, we have to call apr-util APIs, which are defined in apr_date.h. Please take a look at about the usage of time APIs. REMARK: As stated above, apr_time_t is long long type (64bit). Note that the following sample code causes overflow. /* BUGGY sample. This overflows */ const apr_time_t ONE_HOUR = 1000 * 1000 * 60 * 60; We can fix the bug by explicit type cast, but I recommend you to use implicit type cast which libapr provides. The following code shows it. /* two examples to get around overflow above */ const apr_time_t ONE_HOUR = APR_TIME_C(1000) * 1000 * 60 * 60; or const apr_time_t ONE_HOUR = APR_USEC_PER_SEC * 60 * 60; REMARK: Sometimes, often in debugging, we want to print out time values. Unfortunately, Unix and Windows have different printf(3) format specifier for 64bit value. On Unix it is "%lld" and on Windows it is "%I64d". For such portability issues, libapr provides format specifiers, e.g. APR_INT64_T_FMT. There is APR_TIME_T_FMT in apr_time.h. We can write portable code with such format specifiers. /* On Unix, APR_INT64_T_FMT is defined in apr.h */ #define APR_INT64_T_FMT "lld" /* On Windows, APR_INT64_T_FMT is defined in apr.h */ #define APR_INT64_T_FMT "I64d" /* excerpted from apr_time.h */ #define APR_TIME_T_FMT APR_INT64_T_FMT /* We can use APR_TIME_T_FMT as follows */ printf("The current time: %" APR_TIME_T_FMT "[us]\n", apr_time_now()); command line options

For CLI(command line interface) tools, command line options are popular. libapr provides APIs to handle command line options easily. Here is an excerpted code from . /* excerpted from */ static const apr_getopt_option_t opt_option[] = { /* long-option, short-option, has-arg flag, description */ { "in", 'i', TRUE, "input file" }, /* -i name or --in name */ { "out", 'o', TRUE, "output file" }, /* -o name or --out name */ { "help", 'h', FALSE, "show help" }, /* -h or --help */ { NULL, 0, 0, NULL }, /* end (a.k.a. sentinel) */ }; At first, we should supply an array of apr_getopt_option_t elements. We call it option-list. Each element has four variables, i.e. long option, short option, flag of the trailing argument, and description. A long option is specified as '--help'. A short option is specified as '-h'. Short options are mandatory and long options are optional. We can set a long option to NULL. The third variable is flag of the existence of trailing argument. If a command line option works as '--in filename', i.e. the trailing argument is required, we have to set the flag to TRUE. If you run the program without the required argument, e.g. './a.out -in', it causes an error. The option-list must have a sentinel element as described above. To parse actual command line options based on the option-list, we have to call apr_getopt_init() at first. That initializes apr_getopt_t object. Next, we keep calling apr_getopt_long() while it returns APR_SUCCESS. Here is an excerpted code from . /* excerpted from */ /* initialize apr_getopt_t */ apr_getopt_t *opt; apr_getopt_init(&opt, mp, argc, argv); /* parse the all options based on opt_option[] */ while ((rv = apr_getopt_long(opt, opt_option, &optch, &optarg)) == APR_SUCCESS) { switch (optch) { case 'i': ...OMIT During the loop, apr_getopt_long() processes the actual command line option one by one. If the option found is in the option-list, apr_getopt_long() returns APR_SUCCESS and set optch's value. When the option has the trailing argument, apr_getopt_long() parses it and set optarg's value. I show you an example. Let's think about the case that you run the program as './getopt-sample -h -i foo.txt'. At the first loop, apr_getopt_long() finds 'h' in the option-list. Then, apr_getopt_long() returns APR_SUCCESS and set optch to 'h'. At the next loop, apr_getopt_long() finds 'i' in the option-list and 'i' requiring the trailing argument, so it parses the trailing argument, 'foo.txt'. Thus, apr_getopt_long() returns APR_SUCCESS, and set optch to 'i' and optarg to "foo.txt". At the next loop, apr_getopt_long() finds no more options, so returns APR_EOF. memory map(mmap)

mmap is memory map. What it does is to map files into memory. mmap is mainly used as the following purposes. - read/write files with less overhead - allocate a bigger memory space (depends on OS) - shared memory between processes Think about the case that we read all contents from a file. In such a case, we might prepare a buffer and keep reading until the end of the file. It looks as follows: /* naive code to read all contents of a file */ apr_file_t *fp; apr_file_open(&fp, filename, APR_READ, APR_OS_DEFAULT, mp); while (1) { char buf[1024]; apr_size_t len = sizeof(buf); rv = apr_file_read(fp, buf, &len); if (rv != APR_SUCCESS) { break; } /* scan buf */ } apr_file_close(fp); We can do the same thing with apr_mmap_t as follows: /* excerpted from , but I omitted error checks */ apr_file_open(&fp, filename, APR_READ, APR_OS_DEFAULT, mp); apr_finfo_t finfo; apr_file_info_get(&finfo, APR_FINFO_SIZE, fp); apr_mmap_t *mmap; apr_mmap_create(&mmap, fp, 0, finfo.size, APR_MMAP_READ, mp); /* scan mmap->mm */ apr_mmap_delete(mmap); If the file is big enough, mmap based code can be faster. More importantly, mmap can be against memory fragmentation. Most of dynamic memory allocation systems have memory fragmentation problem, but mmap is out of such user-space memory allocation managements. Unfortunatelly, on some OSes, mmap might be buggy or slow. We can use mmap to modify files. We open the file with APR_WRITE, and we have to mmap the file with APR_MMAP_WRITE flag. REMARK: You can't mmap the file that is opened with APR_BUFFERED flag. The following code returns APR_EBADF. /* BUGGY mmap sample */ apr_file_t *fp; apr_mmap_t *mm; apr_file_open(&fp, fname, APR_READ|APR_BUFFERED, APR_OS_DEFAULT, mp); rv = apr_mmap_create(&mm, fp, 0, finfo.size, APR_MMAP_READ, mp);/* BUG: rv==APR_EBADF */ DSO(Dynamic Symbol Object)

You might know so(shared object) or dll(dynamic link library). Essentially, DSO is dependent on so/dll. Now, we call so/dll 'dynamic library' to distinguish from dso. To understand dso, we should know how link and load work. In general, when we use dynamic libraries, we link them with your program at a build time. ld(1) takes care of it on Unix. ld(1) is known as link editor. Since ld(1) is usually called by gcc(1) implicitly, you wouldn't be aware of ld(1). We sometimes happen to see link error messages at a compile time. It means ld(1) can't resolve the symbols that you're using in your program. At a runtime, ld.so(8) loads the dynamic libraries. If it failed, you would see load error messages. A summary is that link is done at a compile(build) time, and load is done at a runtime. If you want to know more, please read manuals of ld(1) and ld.so(8). By dso, both link and load are done at a runtime. Why do we use such a mechanism? The reason is that we can make plugin architecture. All we have to do during a build time is to define the interfaces(symbol names and how we call them) of loadable modules. The program loads the modules at a runtime and use them through the interfaces. Loadable modules can be developed by independent programmers. This can make the system very flexible and extensible. Let's take a look at . You can find two strings "libm.so" and "pow" in . In fact, pow(3) is a trivial function and it doesn't worth calling as dso, but it is just an example code. At first, we call apr_dso_load() and to pass the library file name, "libm.so". /* excerpted from , but I omitted error checks */ const char fname[] = "libm.so"; apr_dso_handle_t *dso_h; apr_dso_load(&dso_h, fname, mp); As you can imagine, if you want your program to have plugins, you need a way to indicate the plugin file names. We can find such a system, apache modules. Their module names are specified in "httpd.conf" file. apr_dso_load() happens to fail. The cause is usually that it can't find the dynamic library file. The search path for dynamic library files at a runtime is dependent on OS. On GNU/Linux, it depends on LD_LIBRARY_PATH environment variable. On MS-Windows, it depends on PATH environment variable. After apr_dso_load() returns successfully, we can call apr_dso_sym(). The prototype declaration is as follows: /* excerpted from apr_dso.h */ APR_DECLARE(apr_status_t) apr_dso_sym(apr_dso_handle_sym_t *ressym, apr_dso_handle_t *handle, const char *symname); By apr_dso_sym(), we can get a symbol object by a symbol name. The first argument is result argument. The second argument is dso handle, which we can get by apr_dso_open() above. The third argument is symbol name. The following code is excerpted from . The symbol name is "pow" and we get a function pointer as a symbol object. Since we know pow(3)'s interface, we can have typedef'd pow_fn_t. /* excerpted from , but I omitted error checks */ typedef double (*pow_fn_t)(double x, double y); pow_fn_t pow_fn; /* seek pow(3) function from libm.so */ apr_dso_sym((apr_dso_handle_sym_t*)&pow_fn, dso_h, "pow"); /* call pow(3) */ printf("%d ^ %d = %f\n", 2, 2, pow_fn(2, 2)); If your program has plugins, you have to define the symbol names and their interfaces. Then, plugin developers must follow them. Finally, we call apr_dso_unload() to release the loadable module. It could reduce memory consumption. Network programming

libapr provides APIs to support network programming. The APIs are based on traditional socket programming scheme. If you are familiar with bind(2), listen(2), accept(2) and connect(2), you can easily understand it. I show you how to use the APIs with three categories, server side progamming, client side progaramming, and multiplex process programming. server side programming

A typical server process opens a listen port, and listen to the port for any client process to connect. Then, it accepts the client's connection, and communicate with the client using their network protocol. Although the network protocol depends on your application, the basic structure of server code is almost same. At first, we have to create a socket address object, apr_sockaddr_t. In traditional socket programming, socket address structure would cause confusion. In contrast, libapr's socket address structure is simple. It can hide chaos among various platforms and IPv4/IPv6 stacks. We can create the object by apr_sockaddr_info_get(). The prototype declaration is as follows: /* excerpted from apr_network_io.h */ APR_DECLARE(apr_status_t) apr_sockaddr_info_get(apr_sockaddr_t **sa, const char *hostname, apr_int32_t family, apr_port_t port, apr_int32_t flags, apr_pool_t *p); The first argument is result argument. The second argument is a hostname, or an IP address. I'll describe it later. The third argument is address family. It is usually APR_INET. If you intend to use IPv6, please specify APR_INET6. The fourth argument is a port number. Server side program should specify the port number to listen. For example, if you're creating a Web server, you might have to specify number 80. As you will see, client side program specifies the port number of the remote server. So, if you're creating a Web browser, you might have to specify number 80, too. Note that you can set the port number to zero. If so, the system selects a port number, which is available. The fifth argument is flags. In most cases, it is 0. The final argument is memory pool to use. We are back to the second argument. As you will see in client side programming below, client program generally specifies the server(remote) hostname or IP address. In contrast, server program should specifies its local address, and it binds the socket address object to a socket by apr_socket_bind(). What value is allowed as a local address? One choice is a solid address or hostname. Namely, if you are creating a yahoo server, you can specify "www.yahoo.com" or "66.94.230.38". In general, such values are supplied by configuration files. Second choice is loopback address, i.e. "127.0.0.1" or "localhost". It works and it's valid. However, in such a case, only a client process running on the same host can connect to the server. If your purpose is to allow only local processes to connect to your server, specifying loopback address is a right choice. The other choice is to specify NULL or APR_ANYADDR(="0.0.0.0"). They imply that the server will bind all network interfaces to a socket. Accordingly, in such a case, any client process can connect to the server via a solid address or loopback address. Internally, NULL is better than APR_ANYADDR. As a result, it's usually good to specify NULL as the second argument of apr_sockaddr_info_get(). There is one exception. When the server host is multihome host, i.e. it has multiple solid IP addresses, and you don't want some IP addresses available from remote hosts, you shouldn't specify NULL. You must bind solid IP addresses to the listening socket. Next, we create a socket object, apr_socket_t. In traditional socket progamming, socket type is just integer, and it acts as a file descriptor. apr_socket_t is opaque type and it hides such the OS dependencies. We can create socket object by apr_socket_create(). The prototype declaration is as follows: /* excerpted from apr_network_io.h */ APR_DECLARE(apr_status_t) apr_socket_create(apr_socket_t **new_sock, int family, int type, int protocol, apr_pool_t *cont); The first argument is result argument. The second argument is address family. It is same as one of apr_sockaddr_info_get()'s third argument. Later, we make a relation between socket address object and socket object. In which, if they have different address family, it fails. The third and fourth arguments are socket type and protocol type. In general, all you have to know is two combinations. One is SOCK_STREAM as type and APR_PROTO_TCP as protocol. The other is SOCK_DGRAM as type and APR_PROTO_UDP as protocol. The final argument is memory pool to use. Now, let's take a look at . The following is a typical code of server side to create a TCP listening socket. /* excerpted from , but I omitted error checks */ apr_sockaddr_t *sa; apr_socket_t *s; apr_sockaddr_info_get(&sa, NULL, APR_INET, DEF_LISTEN_PORT, 0, mp); apr_socket_create(&s, sa->family, SOCK_STREAM, mp); apr_socket_bind(s, sa); apr_socket_listen(s, DEF_SOCKET_BACKLOG); There are two application dependent constant numbers, DEF_LISTEN_PORT and DEF_SOCKET_BACKLOG. Don't care about them. You can find two new APIs, apr_socket_bind() and apr_socket_listen(). By calling apr_socket_bind(), we can make a relation between socket address object(apr_sockaddr_t) and socket object(apr_socket_t). We call it binding the address to the socket. Then, by calling apr_socket_listen(), we change the socket's status to listening. It means we allow the socket to accept connections from remote clients. The second argument of apr_socket_listen() is length of the internal queue. The queue is a waiting queue of remote clients. They wait in the queue in (OS)kernel until the application accepts them. The length is historically called backlog. If you are not sure about proper value, I suggest you to use 'SOMAXCONN', which is a system default max value. Next, we have to handle new connections from remote clients. At first, we have to call apr_socket_accept(). /* excerpted from , but I omit error checks */ apr_socket_t *ns;/* accepted socket */ apr_socket_accept(&ns, s, mp); The second argument of apr_socket_accept() is the listening socket that we create above. Here, we get another socket object, named 'ns', accepted socket. From socket object creation's perspective, apr_socket_accept() is similar to apr_socket_create(). Namely, apr_socket_accept() also creates a new socket object. The first argument is result argument. Note that the newly created socket object is completely different from the listening socket object, which is passed as second argument to apr_socket_accept(). After returing from apr_socket_accept(), the listening socket is still just listening socket. It means we can continue to call apr_socket_accept(), and when the other remote client connects to the server, we're going to have a yet another socket object from apr_socket_accept(). After apr_socket_accept(), we have to handle two sockets independently. In general, we have the listening socket keep listening, and we have the accepted socket talk network protocol with the remote client. In , we send a simple HTTP request and receive the response. To send and receive using socket, we call apr_socket_send() and apr_socket_recv(). Their prototype declarations are as follows: /* excerpted from apr_network_io.h */ APR_DECLARE(apr_status_t) apr_socket_send(apr_socket_t *sock, const char *buf, apr_size_t *len); APR_DECLARE(apr_status_t) apr_socket_recv(apr_socket_t *sock, char *buf, apr_size_t *len); They are similar to apr_file_read() and apr_file_write() described above. The first and second argumnts are needless to explain. The third argument of both is value-result argument. By which, we specify the input buffer's length on entry and get the output result's length on exit. apr_socket_send() returns the length of sent bytes. apr_socket_recv() returns the length of received bytes. We will discuss about return values of those APIs later. client side programming

Comparing to server side programming, client side programming looks simpler. A new API to note is only one, apr_socket_connect(). /* excerpted from apr_network_io.h */ APR_DECLARE(apr_status_t) apr_socket_connect(apr_socket_t *sock, apr_sockaddr_t *sa); This is similar to apr_socket_bind() on those arguments list. Like apr_socket_bind(), apr_socket_connect() makes a relation between socket object and socket address object. The difference is that the socket address object is related to the remote server address. Let's take a look at . The following is a typical code of client side program. /* excerpted from , but I omit error checks */ apr_sockaddr_t *sa; apr_socket_t *s; apr_sockaddr_info_get(&sa, DEF_REMOTE_HOST, APR_INET, DEF_REMOTE_PORT, 0, mp); apr_socket_create(&s, sa->family, SOCK_STREAM, mp); apr_socket_connect(s, sa); The second and fourth arguments of apr_sockaddr_info_get() are remote server's IP address(or hostname) and port number. The others are needless to explain, because they are same as server side. apr_socket_create() is also needless to explain, because it's also same as server side. By calling apr_socket_connect(), we begin to connect to the remote server. I'll explain return values of these APIs. After connecting to the server, the client program talks network protocol with the server through the socket. As you can see in , the code is same as server side. We can use apr_socket_send() and apr_socket_recv(). multiplex processing (poll)

Go back to again. It has a loop to keep calling apr_socket_accept(), so that the server process can accept multiple clients. However, it doesn't call apr_socket_accept() until it completes the session between one client. Obviously, when multiple clients connect to the server simultaneously, the server handles only one client at a moment. Such a server is normally useless. We need concurrent server. In general, there are three programming models to develop concurrent server; multi-process model, multi-threaded model, and multiplexing model. Here, we focus on the final one. Historically, multiplexing model relies on select(2) or poll(2) APIs, and the code is often based on event-driven style. The essential idea is that we check multiple sockets whether they are ready to read or write. Then, when we know some of the sockets are ready to read/write, we do actual read/write with them. In addition, we need non-blocking sockets for multiplex processing. I'll describe how to use non-blocking sockets in the next section. Let's take a look at . The creation of the listening socket is almost same as . The different is that we make it non-blocking. Then, we create a pollset obejct by apr_pollset_create(). pollset is a set of sockets to watch. The prototype declaration is as follows: /* excerted from apr_poll.h */ APR_DECLARE(apr_status_t) apr_pollset_create(apr_pollset_t **pollset, apr_uint32_t size, apr_pool_t *p, apr_uint32_t flags); The first argument is result argument. The second argument is size of pollset. Unfortunately, pollset is not a dynamic-sized set. We have to specify the size of set at creation. The third argument is memory pool to use. The fourth argument is flag, but it's a reserved argument now. After creating pollset object, we can add sockets to the pollset by apr_pollset_add(). In the following example, we add the first and second sockets to the pollset to know whether the socket is ready to read. Then we remove a socket from the pollset because we are not interested in its readability. Finally, we add the third socket to know whether it is ready to write. /* excerpted from */ /* we watch @lsock(listening socket) to know whether it is ready to read(APR_POLLIN) */ apr_pollfd_t pfd = { mp, APR_POLL_SOCKET, APR_POLLIN, 0, { NULL }, NULL }; pfd.desc.s = lsock; apr_pollset_add(pollset, &pfd); /* we watch @ns(connected socket) to know whether it is ready to read(APR_POLLIN) */ apr_pollfd_t pfd = { mp, APR_POLL_SOCKET, APR_POLLIN, 0, { NULL }, serv_ctx }; pfd.desc.s = ns; apr_pollset_add(pollset, &pfd); /* we are not interested in the socket's readability, so remove it from the pollset */ apr_pollfd_t pfd = { serv_ctx->mp, APR_POLL_SOCKET, APR_POLLIN, 0, { NULL }, serv_ctx }; pfd.desc.s = sock; apr_pollset_remove(pollset, &pfd); /* we watch @sock(connected socket) to know whether it is ready to write(APR_POLLOUT) */ pfd.reqevents = APR_POLLOUT; apr_pollset_add(pollset, &pfd); We need to look at apr_pollfd_t's definition. It is in apr_poll.h as follows: /* excerpted from apr_poll.h */ /** Union of either an APR file or socket. */ typedef union { apr_file_t *f; /**< file */ apr_socket_t *s; /**< socket */ } apr_descriptor; /** Poll descriptor set. */ struct apr_pollfd_t { apr_pool_t *p; /**< associated pool */ apr_datatype_e desc_type; /**< descriptor type */ apr_int16_t reqevents; /**< requested events */ apr_int16_t rtnevents; /**< returned events */ apr_descriptor desc; /**< @see apr_descriptor */ void *client_data; /**< allows app to associate context */ }; pollset is designed to work for files as same as for sockets, but we ignore files in this document. We set apr_pollfd_t::desc_type to APR_POLL_SOCKET, and we specify socket object as apr_pollfd_t::desc. apr_pollfd_t::reqevents and apr_pollfd_t::rtnevents have a relation, the former is input and the latter is output. The values are bit-wised flag of APR_POLLIN, APR_POLLOUT, etc. The following sample shows it. /* if you want to know whether the socket is ready to read or write, you can specify the bit-wised flag as follows */ apr_pollfd_t pfd = { mp, APR_POLL_SOCKET, APR_POLLIN|APR_POLLOUT, 0, { sock }, app_ctx }; apr_pollset_add(pollset, &pfd); apr_pollfd_t::rtnevents doesn't have any meaning when the apr_pollfd_t object is used for apr_pollset_add() or apr_pollset_remove(). It does have meaning when the object is returned from apr_pollset_poll(). apr_pollset_poll() prototype declaration is as follows: /* excerpted from apr_poll.h */ APR_DECLARE(apr_status_t) apr_pollset_poll(apr_pollset_t *pollset, apr_interval_time_t timeout, apr_int32_t *num, const apr_pollfd_t **descriptors); The first argument is that we pass our pollset object. The second argument is timeout value. If the timeout is zero, it implies no timeout. The third and fourth argument are result arguments. By these result arguments, we get the number of active sockets, which are ready to read or write, and we get an array of the active socket objects. The array is apr_pollfd_t's array. We can know whether the sockets are ready to read/write by checking apr_pollfd_t::rtnevents's values. Here is a pseudo code. /* pseudo code to show how apr_pollset_poll() works */ const apr_pollfd_t *ret_pfd; /* returned pollset */ rv = apr_pollset_poll(pollset, DEF_POLL_TIMEOUT, &num, &ret_pfd); if (rv == APR_SUCCESS) { /* num and ret_pfd are result-arguments. * ret_pfd is an array of apr_pollfd_t, and the array size is num */ for (int i = 0; i < num; i++) { if (ret_pfd[i].rtnevents & APR_POLLIN) { printf("socket %p is ready to read\n", ret_pfd[i].desc.s); } else if (ret_pfd[i].rtnevents & APR_POLLOUT) { printf("socket %p is ready to write\n", ret_pfd[i].desc.s); } } } real network programming

We have glanced network APIs in the previous sections. It doesn't seem so difficult. Nevertheless, real network programming is not so easy. In particular, it is hard to make your software efficient, secure and robust to various network errors. Here, I'll show you some hints for real programming. blocking vs. non-blocking socket

There are mainly three modes with sockets, blocking-forever, blocking-with-timeout, and non-blocking. The mode is controlled by two APIs, apr_socket_opt_set() and apr_socket_timeout_set(). We have a portability issue on this. The table below shows it. APR_SO_NONBLOCK | timeout value to apr_socket_timeout_set()| mode @ off(==0) | t == 0 | non-blocking @ off(==0) | t < 0 | blocking-forever @ off(==0) | t > 0 | blocking-with-timeout @ on(==1) | t == 0 | non-blocking @ on(==1) | t < 0 | blocking-forever @ on(==1) | t > 0 | blocking-with-timeout
Unix
APR_SO_NONBLOCK | timeout value to apr_socket_timeout_set()| mode @ off(==0) | t == 0 | blocking-forever@ off(==0) | t < 0 | blocking-forever@ off(==0) | t > 0 | blocking-with-timeout @ on(==1) | t == 0 | non-blocking@ on(==1) | t < 0 | non-blocking@ on(==1) | t > 0 | non-blocking
Windows
The default mode is APR_SO_NONBLOCK==0(off) and APR_SO_TIMEOUT==-1. Namely, default socket is blocking-forever on both Unix and Windows. Conclusion (my recommendation): [a] When you want a non-blocking socket, set it to 'APR_SO_NONBLOCK==1(on) and timeout==0'. [b] When you want a blocking-with-timeout socket, set it to 'APR_SO_NONBLOCK==0(off) and timeout>0'. Note that you must keep the order of calling the APIs. You must call apr_socket_opt_set(sock, APR_SO_NONBLOCK, 1) and then call apr_socket_timeout_set(sock, timeout). Otherwise, on Unix the socket becomes blocking-forever. [c] When you want a blocking-forever socket, set it to 'APR_SO_NONBLOCK==0(off) and timeout<0'. In my opinion, we merely need blocking-forever sockets for real application. non-blocking apr_socket_accept()

We can control blocking/non-blocking mode for apr_socket_recv() and apr_socket_send() as I described above. How about other APIs? We can ignore apr_socket_bind() and apr_socket_listen(). Because they never block. Simply, these APIs are always non-blocking mode. We have two more APIs to consider, apr_socket_accept() and apr_socket_connect(). Here, we consider apr_socket_accept(). It is almost same as apr_socket_recv()/apr_socket_send(). The mode follows the table in the previous section. Unlike apr_socket_recv()/apr_socket_send(), blocking-forever socket is useful for apr_socket_accept(). It is because that the program might be a server process and might have nothing to do until any client connects to. If we write a mulplexing model code, we need non-blocking socket for listening socket. We check listening socket whether it is ready to read. Readiness to read indicates that any client has connected to the socket. After we know the readiness to read, we just call apr_socket_accept(). Please take a look at about this pattern. Note that you must not assume the newly connected socket, which is returned from apr_socket_accept(), inherits the mode from the listening socket. You should specify the mode for the connected socket explicitly. Please look at the following example. /* excerpted from , but I omitted error checks */ apr_socket_accept(&ns, lsock, mp); ...SNIP... /* non-blocking socket. We can't expect that @ns inherits non-blocking mode from @lsock */ apr_socket_opt_set(ns, APR_SO_NONBLOCK, 1); apr_socket_timeout_set(ns, 0); non-blocking apr_socket_connect()

apr_socket_connect() is a bit different from other APIs on blocking/non-blocking mode. It has three modes, blocking-with-system-timeout, blocking-with-timeout, and non-blocking. Unlike other APIs, apr_socket_connect() never blocks forever. The default mode is blocking-with-system-timeout. The timeout value depends on OS, and it is relatively longer, e.g. over one minute. In my opinion, it is not good to use blocking-with-system-timeout mode for real applications, because it is uncontrollable. We have to set either blocking-with-timeout or non-blocking to the mode. To make blocking-with-timeout sockets, we have to set it to 'APR_SO_NONBLOCK==1(on) and timeout>0'. As you see, this is not same as above. On Unix, we have no problem to specify 'APR_SO_NONBLOCK==0(off) and timeout>0'. Unfortunatelly, we have a problem on Windows. Setting the mode to 'APR_SO_NONBLOCK==0(off) and timeout>0' causes blocking-with-system-timeout sockets on Windows. Conclusion: If we want blocking-with-timeout socket without portability issues, we should write code as follows: /* pseudo code: blocking-with-timeout apr_socket_connect() */ apr_socket_opt_set(sock, APR_SO_NONBLOCK, 1); apr_socket_timeout_set(sock, positive_timeout); apr_socket_connect(sock, sa); /* still blocking-with-timeout for other operations, apr_socket_send()/apr_socket_recv() */ apr_socket_opt_set(sock, APR_SO_NONBLOCK, 0); apr_socket_timeout_set(sock, positive_timeout); apr_socket_send(sock, ...); apr_socket_recv(sock, ...); Blocking-with-timeout apr_socket_connect() returns APR_SUCCESS if the connection has been established successfully. Otherwise, it returns an error value. For example, APR_TIMEUP, APR_ECONNREFUSED, or APR_EHOSTUNREACH. If error value is APR_ECONNREFUSED, the server process's listen(2) backlog is beyond the limit. Please see apr_socket_listen() description above. For mulplexing model programming, we need non-blocking sockets with apr_socket_connect(). To make non-blocking socket for apr_socket_connect(), set it to 'APR_SO_NONBLOCK==1(on) and timeout==0' as usual. Non-blocking apr_socket_connect() returns APR_EINPROGRESS unless the connection is established. In general, apr_socket_connect() can't establish the connection immediately. Accordingly, non-blocking apr_socket_connect() usually returns APR_EINPROGRESS except that the connection is established between local processes, which run on the same host. In that case, apr_socket_connect() could return APR_SUCCESS even if it is non-blocking. For multiplex processing, we want to know when the connection is established. If we know that, we can call apr_socket_connect() and expect it to return APR_SUCCESS. Unfortunatelly, there is a portability issue on checking readiness to call apr_socket_connect(). My suggestion is following logic. I assume that most of applications will send some bytes after apr_socket_connect() has established the connection. /* pseudo code: check readiness for non-blocking apr_socket_connect() by apr_pollset_poll() */ /* XXX this is not the best way, but works on almost all platforms */ /* set non-blocking to the socket */ apr_socket_opt_set(sock, APR_SO_NONBLOCK, 1); apr_socket_timeout_set(sock, 0); rv = apr_socket_connect(sock, sa); /* this returns immediately */ if (rv == APR_SUCCESS) { /* special case: the server might run on the same host */ /* ready to write */ } else if (APR_STATUS_IS_EINPROGRESS(rv)) { /* usual case */ /* add the socket to pollset to check APR_POLLOUT(writable) */ pfd.reqevents = APR_POLLOUT; apr_pollset_add(pollset, &pfd); /* we should specify timeout to apr_pollset_poll(). */ apr_pollset_poll(pollset, positive_timeout, &num, &ret_pfd); /* go to [1] or [2] based on pollset's results */ } /* [1] when the socket is ready to write */ call apr_socket_send(). /* you don't need to call apr_socket_connect(), but you can call it (no side-effects) */ /* [2] when the socket is not ready to write by the timeout * we guess the socket can't establish the connection */ apr_socket_close(sock); Detection of the remote host closed the socket

Detecting whether socket is unavailable is important for real applications. In general, there are three scenarios when we lost TCP session. The remote process has closed socket, the remote process has crashed(terminated), or the remote host has crashed. Except the last case, we maybe receive FIN packet and we know we've lost out TCP session. From API perspective, we can know it by that apr_socket_recv() returns APR_EOF. The following example shows how to detect whether the remote process has closed the session. /* pseudo code to detect we lost TCP session, i.e. the remote process has closed the socket or process terminated */ apr_status_t rv; rv = apr_socket_recv(sock, ..., &len); /* we assume @sock is blocking */ if (APR_STATUS_IS_TIMEUP(rv)) { /* we might be here, if @sock is blocking-with-timeout */ } else if (APR_STATUS_IS_EOF(rv) || len == 0) { /* we lost TCP session. * XXX On Windows, rv would equal to APR_SUCCESS and len==0 in this case. So, we should check @len in addition to APR_EOF check */ } When we use non-blocking socket, we can know we lost TCP session by that socket is readable and apr_socket_recv() returns APR_EOF. /* pseudo code to detect we lost TCP session. We use non-blocking socket */ apr_status_t rv; rv = apr_pollset_poll(pollset, DEF_POLL_TIMEOUT, &num, &ret_pfd); if (rv == APR_SUCCESS) { for (int i = 0; i < num; i++) { if (ret_pfd[i].rtnevents & APR_POLLIN) { rv = apr_socket_recv(ret_pfd[i].desc.s, ..., &len); if (APR_STATUS_IS_EAGAIN(rv)) { /* we have no data to read. we should keep polling the socket */ } else if (APR_STATUS_IS_EOF(rv) || len == 0) { /* we lost TCP session. * XXX On Windows, rv would equal to APR_SUCCESS and len==0 in this case. So, we should check @len in addition to APR_EOF check */ } else { /* we got data */ } } } } IP address from hostname

We call apr_sockaddr_info_get() with the first argument, a target hostname, and we can get apr_sockaddr_t objet. Then, we call apr_sockaddr_ip_get() with the apr_sockaddr_t object. The following prototype declaration is excerpted from apr_network_io.h. /* excerpted from apr_network_io.h */ APR_DECLARE(apr_status_t) apr_sockaddr_ip_get(char **addr, apr_sockaddr_t *sockaddr); Hostname from IP address

We call apr_sockaddr_info_get() with the first argument, a target IP address, and we can get apr_sockaddr_t object. Then, we call apr_getnameinfo() with the apr_sockaddr_t object. Note that apr_sockaddr_t::hostname will be implicitly modified by apr_getnameinfo(). /* excerpted from apr_network_io.h */ APR_DECLARE(apr_status_t) apr_getnameinfo(char **hostname, apr_sockaddr_t *sa, apr_int32_t flags); Process handling

Process is a running instance of a program. Generating child processes means that we execute the other programs from the current process. It is known as 'fork and exec' on traditional Unix. It is a very powerful scheme on Unix, but it has some drawbacks. First, it is not so portable when we think about OS other than Unix. Second, the code wouldn't become straightforward. libapr wraps it and hides portability issues. Generating child proccess is useful when we need outputs from other programs. Or, we can easily write an application launcher program. Let's take a look at . At first, we create a process attribute object by apr_procattr_create(): /* excerpted from apr_thread_proc.h */ APR_DECLARE(apr_status_t) apr_procattr_create(apr_procattr_t **new_attr, apr_pool_t *cont); The first argument is result argument and the second argument is memory pool to use. apr_procattr_t is opaque structure, and it has some setter APIs, which are declared in apr_thread_proc.h. The simplest API is apr_procattr_dir_set(). By which, we can set the current directory of the child process. Some setter APIs are related to file descriptors. I will describe them in the next 'pipe' section. Here, I focus on the following two APIs: /* excerpted from apr_thread_proc.h */ APR_DECLARE(apr_status_t) apr_threadattr_detach_set(apr_threadattr_t *attr, apr_int32_t on); typedef enum { APR_SHELLCMD, /**< use the shell to invoke the program */ APR_PROGRAM, /**< invoke the program directly, no copied env */ APR_PROGRAM_ENV, /**< invoke the program, replicating our environment */ APR_PROGRAM_PATH, /**< find program on PATH, use our environment */ APR_SHELLCMD_ENV /**< use the shell to invoke the program, * replicating our environment */ } apr_cmdtype_e; APR_DECLARE(apr_status_t) apr_procattr_cmdtype_set(apr_procattr_t *attr, apr_cmdtype_e cmd); By apr_threadattr_detach_set(), we can make child process detached. What is detached process? Unfortunately, the meaning is different on platform(OS). On Unix, detached process implies daemon process. It means process is detached from the control terminal(a.k.a. tty). On the other hand, on MS-Windows, detached process implies process without the console window. The default status is non-detachable. When do we need detached process? Unfortunately, the code should depends on OS. On MS-Windows, decision is easy. We set detached flag, only if the child process is a command line application and we don't want to see the console window. On Unix, decision is not so easy. In a nutshell, I recommend you to set detached flag, if child process is a server process. apr_cmdtype_e is a little bit hard to understand. I'll describe their differences. One difference is whether using shell or not. APR_SHELLCMD and APR_SHELLCMD_ENV are using shell. They internally execute a shell process and make the shell launch a new process. If you are familiar with Unix API, you can imagine system(3) library routine. system(3) is sometimes mentioned as an easier alternative of 'fork and exec'. As stated above 'fork and exec' scheme is not easy, so that system(3) is sometimes useful for rapid programmings. In libapr scheme, do we need a shell to execute child processes? My opinion is No. Using shell seems to have a clear advantage, shell expansion. We can run command such as 'find ~/ -print' or 'ls *.txt'. It is useful in very limited cases. However, as you can easily imagine, the power causes security holes. My suggestion is that only if you know what you're doing, you can use APR_SHELLCMD or APR_SHELLCMD_ENV. Otherwise, don't use them. The next difference is related to environment variables. APR_PROGRAM_ENV and APR_SHELLCMD_ENV are different from others. There are two ways to pass environment variables to child process. One is using APR_PROGRAM_ENV or APR_SHELLCMD_ENV. The other is specifying the argument of apr_proc_create(), which I'll describe later. The former indicates inheritance from the parent process. By APR_PROGRAM_ENV or APR_SHELLCMD_ENV, child process receives the copy of environment variables from the parent process. It is a copy, not a share. Thus, if the child process overrides any of values, it doesn't make any effects on the parent process. The final difference is related to PATH environment variable. APR_PROGRAM_PATH is unique at this point. Only with APR_PROGRAM_PATH, we can use a program name instead of a program path. For example, we can launch child process by 'ls' or 'emacs'. Internally, libapr searches the exact path of the program in PATH environment variable's value. Otherwise, we have to specify the exact pathes, such as '/bin/ls' or '/usr/bin/emacs'. You think it's useful? Sometimes, yes, but my opinion is that we shouldn't rely on it, because it causes security risks and unexpected results. Please use any detection tools, such as autoconf, or let end-users input exact pathes of commands to run. To launch a child process, we call apr_proc_create(). The following declaration is excerpted from apr_thread_proc.h: /* excerpted from apr_thread_proc.h */ APR_DECLARE(apr_status_t) apr_proc_create(apr_proc_t *new_proc, const char *progname, const char * const *args, const char * const *env, apr_procattr_t *attr, apr_pool_t *pool); The first argument is result argument. It is a little bit different from the other APIs. apr_proc_t is complete type. It is our responsibility to allocate apr_proc_t object memory. The second argument is a program name to run. As stated above, it should be an absolute path of the command. The third argument is argument list passed to the child process. Note that the first element of the array is the program path, and the final element is NULL(sentinel). The typical code becomes as follows: /* pseudo code of args to apr_proc_create() */ int argc = 0; const char* argv[32]; /* 32 is a magic number. enough size for the number of arguments list */ argv[argc++] = progname; /* program path of the command to run */ argv[argc++] = "-i"; argv[argc++] = "foo"; argv[argc++] = "--longopt"; argv[argc++] = "bar"; argv[argc++] = NULL; /* The final element should be NULL as sentinel */ The fourth argument is environment variable value list passed to child process. Again, note that the final element of the array should be NULL. The fifth argument is apr_procattr_t, which we have already created by apr_procattr_create(). The last argument is memory pool to use. Parent process should take care of the child process's termination. It is known as wait(2) system call in traditional Unix scheme. If parent process ignore the termination of a child process, the child becomes zombie. Although zombie doesn't eat human brains, it consumes OS resources, e.g. system memory. To take care of zombie, we have to call apr_proc_wait(). The prototype declaration is as follows: /* excerpted from apr_thread_proc.h */ APR_DECLARE(apr_status_t) apr_proc_wait(apr_proc_t *proc, int *exitcode, apr_exit_why_e *exitwhy, apr_wait_how_e waithow); The first argument is apr_proc_t object that we have had by apr_proc_create(). The second and third arguments are result arguments. The fourth argument is a parameter to indicate how to wait. The value is one of APR_WAIT or APR_NOWAIT. By specifying APR_WAIT, apr_proc_wait() blocks until the child process terminates. APR_NOWAIT indicates non-blocking. Please take a look at about the usage. REMARK: If the parent process dies without taking care of zombies, zombies disappear. The ancestor process takes care of zombies. pipe

pipe is one of the inter-process communications. pipe is very useful between parent process and child process. Parent process can send a byte stream to the child process through a pipe. The child process can read them from its standard input. Similarly, parent process can receive a byte stream from the child process through a pipe. The child process writes them to its standard output or its standard error output. Most importantly, child process program doesn't care about pipe. It just reads/writes through its standard intput/output/error. On the other hand, from the parent's perspective, pipe is seen as a file object. Accordingly, parent process just calls apr_file_read() or apr_file_write() to send to/receive data from its pipe. To handle pipe, we call apr_procattr_io_set() to set process attribute object. /* excerpted from apr_thread_proc.h */ APR_DECLARE(apr_status_t) apr_procattr_io_set(apr_procattr_t *attr, apr_int32_t in, apr_int32_t out, apr_int32_t err); apr_procattr_io_set() has four arguments. The first is an apr_procattr_t object to set. The other arguments are related to standard intput/output/error. The following code is excerpted from : /* excerpted from pipe-sample.h, but I omitted error checks */ apr_procattr_t *pattr; apr_procattr_create(&pattr, mp); apr_procattr_io_set(pattr, APR_NO_PIPE, APR_FULL_BLOCK, APR_NO_PIPE); This means the parent process cares only the child process's standard output. The parent will receive a byte stream from the child's standard output. As you see in , the parent process calls apr_file_read() to read data from the child process. The data are passed through pipe between two processes. In addition, specifying APR_FULL_BLOCK indicates the parent will block until the child writes something to its standard output or child's termination. multiple threads

Thread is sometimes called light-weight process. In general, process is a virtualization of CPU and memory. In contrast, thread is a virtualization of only CPU. From a C programmer's eyes, CPU represents both PC(Program Counter) and SP(Stack Pointer). We can think each thread has its own PC and SP. Having independent PC implies that multiple threads run simultaneously. Having independent SP implies that each thread has independent stack memory. Rather than that, each thread doesn't have own memory space(address space) unlike process. All threads in the same process share one memory space. In other words, threads share all objects except ones in stack, a.k.a. local objects. To create a new thread, we can create a thread attribute object by apr_threadattr_create() if we need it. We can use NULL instead of creating apr_threadattr_t object. The prototype declaration is as follows: /* excerpted from apr_thread_proc.h */ APR_DECLARE(apr_status_t) apr_threadattr_create(apr_threadattr_t **new_attr, apr_pool_t *cont); apr_threadattr_t is opaque structure. It has some setter APIs. If we need undefault behaviours, we can set thread attributes. Then, we just call apr_thread_create() so that we can create a new thread. /* excerpted from apr_thread_proc.h */ APR_DECLARE(apr_status_t) apr_thread_create(apr_thread_t **new_thread, apr_threadattr_t *attr, apr_thread_start_t func, void *data, apr_pool_t *cont); The first argument is result argument, by which we can get apr_thread_t object. The second argument is thread attribute object mentioned above. As stated above, NULL is OK. The third argument is function pointer, from which a new thread starts to run. It's called thread entry point. The fourth argument is arbitrary context object passed to the thread entry point. The last argument is memory pool to use. Thread entry point, apr_thread_start_t, looks as follows: /* excerpted from */ void* APR_THREAD_FUNC doit(apr_thread_t *thd, void *data); For portability, APR_THREAD_FUNC macro is required. This is a kind of callback function, which is called by system. Please take a look at and execute it in order to know how threads work. The first argument of thread entry point is thread object. This is one that is returned from apr_thread_create() as its result argument. The second argument of thread entry point is context object that is specified by the fourth argument of apr_thread_create(). Return type is void pointer. In pthread(POSIX thread) scheme, thread entry point function can return its status code as return value. In the current libapr scheme, return value has no meaning. So, it's OK to just return NULL. Instead, we call apr_thread_exit() to return status code. Similar to process, thread has an attribute called detachable. If thread is detached, the main thread can't control the sub thread, especially its termination. On the other hand, if thread is not detached, the main thread should take care of its termination. I'll describe it later. The default attribute is detached. When thread entry point exits, the thread terminates on Unix. However, it doesn't terminate on Windows. For portability, we have to call apr_thread_exit(). Main thread should call apr_thread_join() to take care of sub thread's termination. Imagine generating a new thread is similar to splitting a running context(virtualized CPU) and apr_thread_join() makes one running context from the splitted ones. By calling apr_thread_join(), we can get status code from terminated thread, which has called apr_thread_exit(). Thread and memory pool is difficult to control properly. I call a memory pool 'thread-mp', which is passed to apr_thread_create(). There are some caveats. Most importantly, apr_thread_exit() destroys the child memory pools of 'thread-mp'. It causes a typical bug that we destroy a child memory pool after destroying its parent memory pool. This kind of bug can cause a prcoess crash and it is fairly very hard to find. As mentioned earlier, if thread is detached, we don't need to call apr_thread_join() for it. In other words, it means we can't know when sub thread terminates. It also means we can't know when we can destroy 'thread-mp'. My recommendation for workaround is that you should use non-detached thread, and should call apr_thread_join() to know whether you can destory 'thread-mp'. REMARK: Main thread is sometimes called parent thread, but some people don't like such parent/child naming. Because threads don't make parent-child relationship unlike process. Even if it seems that parent thread generates a child thread, they are completely equal, so that we should consider one thread is splitted to two threads. Nevertheless, we often need to destinguish two threads for explanation. Here, I use the terms, main thread and sub thread. mutex lock

Mutex is abbreviation of 'mutual exclusion'. As stated above, mulitiple threads run in the same memory address space. It means all objects are shared except ones on stack memory. If multiple threads modify the same object simultaneoulsy, the result is undetermined. Because one line in C program is not always an atomic operation of CPU. The simplest solution to get around is mutex lock. At first, we call apr_thread_mutex_create(). Mutex lock has three basic operations: /* excerpted from apr_thread_proc.h */ APR_DECLARE(apr_status_t) apr_thread_mutex_lock(apr_thread_mutex_t *mutex); APR_DECLARE(apr_status_t) apr_thread_mutex_trylock(apr_thread_mutex_t *mutex); APR_DECLARE(apr_status_t) apr_thread_mutex_unlock(apr_thread_mutex_t *mutex); lock and unlock operations are always used as pair. trylock operation is alternative to lock. Code looks as follows: /* pseudo code: mutex lock's typical code */ /* case one */ apr_thread_mutex_lock(mutex);/* sleep until the current thread acquires the mutex lock */ do something on shared objects... /* during this, the other threads can't acquire the mutex lock. */ apr_thread_mutex_unlock(mutex);/* we should unlock after lock as soon as possible */ /* case two */ apr_status_t rv = apr_thread_mutex_trylock(mutex); if (APR_STATUS_IS_EBUSY(rv)) { go through/* we don't need to unlock, because we didn't acquire the lock */ } else { do something on shared objects... apr_thread_mutex_unlock(mutex);/* we should unlock */ } Imagine the case multiple threads are running. They run independently, and they reach the same point simultaneously. They call apr_thread_mutex_lock() simultaneously to acquire the mutex lock. Only one thread can acquire it. We can't control which thread is chosen, because it depends on the system. Anyway, only one thread becomes the winner. It acquires the mutex lock and returns from apr_thread_mutex_lock(). The other threads still sleep in apr_thread_mutex_lock(). The winner thread calls apr_thread_mutex_unlock() later. Then, another only one thread is waken, chosen from the sleeping threads. This second winner returns from apr_thread_mutex_lock(), although the others still sleep. When the second winner calls apr_thread_mutex_unlock(), another thread, the third winner, is chosen from the sleeping threads. Similarly, it goes like this. apr_thread_mutex_trylock() never blocks. It returns APR_SUCCESS or APR_EBUSY. It returns APR_SUCCESS when it has acquired the mutex lock. Otherwise, it returns APR_EBUSY. APR_EBUSY indicates another thread is acquiring the mutex lock. In general, the time between lock and unlock is shorter, the better. It performs faster and it helps you to get around deadlock bugs which I describe later. We can create two kinds of mutex lock, APR_THREAD_MUTEX_NESTED or APR_THREAD_MUTEX_UNNESTED. The former, nested lock, allows recursive locks by the same thread. The latter, unnested lock, doesn't allow it. Nested locks are a little bit inefficient than unnested locks. So, if you don't need recursive locks, you should create APR_THREAD_MUTEX_UNNESTED locks. There is APR_THREAD_MUTEX_DEFAULT defined in apr_thread_mutex.h. In my opinion, you shouldn't rely on it. Because you must be aware that the mutex lock is recursive or not. There is a well-known bug called deadlock in multi-threaded programming. The following example has deadlock bug. /* pseudo code: deadlock bug sample code */ apr_thread_mutex_t *mutex1; apr_thread_mutex_t *mutex2; /* portion-X */ apr_thread_mutex_lock(mutex1); apr_thread_mutex_lock(mutex2); do something on shared objects... apr_thread_mutex_unlock(mutex2); apr_thread_mutex_unlock(mutex1); /* portion-Y */ apr_thread_mutex_lock(mutex2); apr_thread_mutex_lock(mutex1); do something on shared objects... apr_thread_mutex_unlock(mutex1); apr_thread_mutex_unlock(mutex2); What can happen? Think about two threads running concurrently. One thread named thread-a runs at portion-X. The other thread named thread-b runs at portion-Y. Imagine thread-a has acquired mutex1. Then, two threads are competitors on acquiring mutex2. If thread-a wins, you're lucky and there is no problem. In contrast, if thread-b wins, you have a problem. thread-a tries to acquire mutex2 in portion-X and thread-b tries to acquire mutex1 in portion-Y. Then, what happens next? Nothing happens forever. Each thread will sleep to wait mutex lock. Unfortunately, the mutex locks are never unlocked. It seems easy to fix. It is sometimes so, but it isn't sometimes. Because the bug can be unreproducable. There is no silver bullet to avoid deadlock bugs, but we can have one principle to get around deadlocks. It is caleld 'layered approach'. It said that we should always lock multiple mutex locks in the same order. For example, if we have to lock mutex1 and mutex2 at once, we must decide the locking order and must always keep it. If we decided mutex1 is upper on mutex2, we must always lock mutex1 before mutex2. condition variable

We often have the case that we require one thread to wait until the other thread completes something. We can write such a code with condition variable. One thread has a role to wake up the other thread. The other thread has a role to wait(sleep) until it's notified(signalled). Sometimes, the former is called producer and the latter is called consumer. Here, I call them so. At first, we create a condition variable object. We just call apr_thread_cond_create() as follows: /* excerpted from thread- */ typedef struct { /* condition variable should be used with a mutex variable */ apr_thread_mutex_t *mutex; apr_thread_cond_t *cond; /* shared context depends on application */ int input_num; } my_production_t; apr_thread_mutex_create(&prod.mutex, APR_THREAD_MUTEX_UNNESTED, mp); apr_thread_cond_create(&prod.cond, mp); As you can see, I create apr_thread_mutex_t object, too. That's because condition variable requires apr_thread_mutex_t. The relation between two objects is not explicit in their creations, but it will be clear when we call apr_thread_cond_wait() later. Let's take a look at thread-. In which, the main thread works as a producer and the sub thread works as a consumer. The basic structure of the producer's code looks as follows: /* producer thread's basic code */ apr_thread_mutex_lock(prod->mutex); apr_thread_cond_signal(prod->cond); apr_thread_mutex_unlock(prod->mutex); By calling apr_thread_cond_signal(), producer thread can wake up consumer thread. The call must be protected by the associated mutex lock, because the condition variable object is shared between producer and consumer. Calling apr_thread_cond_signal() never block. Even if there are multiple consumers waiting, only one consumer is waken up. If we want to wake all of them, we use a call to apr_thread_cond_broadcast(). The basic structure of the consumer's code looks as follows: /* consumer thread's basic code */ apr_thread_mutex_lock(prod->mutex); apr_thread_cond_wait(prod->cond, prod->mutex); apr_thread_mutex_unlock(prod->mutex); By calling apr_thread_cond_wait(), the consumer thread blocks. It sleeps until producer thread wakes it up. If we want a timeout for the wait, we can use apr_thread_cond_timedwait() instead. As same as producer, calling apr_thread_cond_wait() must be protected by the associated mutex lock. Moreover, apr_thread_cond_wait() requires the mutex lock as the function's second argument. The reason is that apr_thread_cond_wait() internaly releases(unlocks) the associted mutex lock, then sleeps. After waken up, it internally acquires the mutex lock, again. At first glance, both producer's code and consumer's code are protected by the same mutex lock, so that you would think they don't work properly. However, it works by such internal unlock of apr_thread_cond_wait(). If producer calls apr_thread_cond_signal() while no consumer exists, what happens? Unfortunately, there is a portability issue. Think about the case that a producer thread calls apr_thread_cond_signal() when no consumer thread waits for the condition variable. Then, a consumer thread calls apr_thread_cond_wait() for the condition variable. On Unix the consumer sleeps, but on Windows the consumer thread doesn't sleep. To get around this cross-platform problem, we generally must have a flag variable. Please take a look at thread- about it. my_production_t::input_num works as such a flag, although it is also an output production shared between two threads. Without such a flag, apr_thread_cond_wait() would sleep forever. Container APIs

libapr provides some container(a.k.a. collection) APIs. dynamic array

Array is the most general container type in C language. However, array size is noramally fixed. It's not flexible. libapr provides dynamic-sized array. The APIs are declared in apr_tables.h. As container type, dynamic array has the following features. append|efficient(API support)@ insert/prepend|inefficient(no API support)@ delete|inefficient(no API support)@ delete(only the first element)|efficient(API support)@ search(lookup)|inefficient, but depends(no API support)@ iteration|efficient(no API support)
array
'No API support' above means that you have to touch apr_array_header_t::elts directly. This is not a good attitude as a progammer, but we should accept it as a pragmatic programmer. The dynamic array type is apr_array_header_t. The object is created by apr_array_make() as follows: /* excerpted from */ apr_array_header_t *arr; arr = apr_array_make(mp, ARRAY_INIT_SZ, sizeof(const char*)); The first argument is memory pool to use. Both array itself and elements are allocated in the memory pool. The second argument is an initial size of the array. The third argument is the size of element. The third argument is important for code readers, because it tells them what the container's type objects are. Unlike STL, apr_array_header_t is not type-safe. Therefore, it is important to declare the type clearly. In the sample above, we know that the array is an array of string pointers(const char*). REMARK: If you're familiar with the other libapr's APIs, you would feel weird because it doesn't use a result argument for a newly created object. Don't care. We have to call apr_array_push() to append a new elememnt to the array. /* excerpted from */ *(const char**)apr_array_push(arr) = "123"; Iteration is important interface for array. We can do as follows: /* excerpted from */ apr_array_header_t *arr; int i; for (i = 0; i < arr->nelts; i++) { const char *s = ((const char**)arr->elts)[i]; printf("%d: %s\n", i, s); } I have to say that the APIs above are not so intuitive. Fortunately they are very small interfaces, so you become familiar with them soon. Here, I show you generic form of array APIs. /* generic form of apr_array_header_t usages. 'T' is a type name. */ apr_array_header_t *arr = apr_array_make(mp, ARRAY_INIT_SZ, sizeof(T));/* array of T objects */ /* add an element */ T elem; *(T*)apr_array_push(arr) = elem; /* iteration */ for (i = 0; i < arr->nelts; i++) { T elem = ((T*)arr->elts)[i]; } Let's take a look at . In , the array is an array of pointer. The array elements are allocated in the memory pool passed to apr_array_make(), but only pointer's memories are allocated. The objects that the pointers refer to are not allocated. So, the following sample code has a bug, because the strings in the array are not persistent. /* BUGGY sample of apr_array_header_t usage */ void func(apr_array_header_t *str_arr) { char str[32]; strcpy(str, "foobar"); *(const char**)apr_array_push(str_arr) = str;/* BUG, if function caller uses this array */ return; } In most cases, the array itself and the elements are same lifetime, so that it might be better to allocate the objects in the same memory pool as follows: /* fix example of above bug */ void func(apr_array_header_t *str_arr) { const char *str = apr_pstrdup(str_arr->pool, "foobar"); *(const char**)apr_array_push(str_arr) = str; } As you have seen, apr_array_header_t works with arbitrary type. Obviously, string array becomes more powerful than others. The reason is apr_array_pstrcat(). It works with string array, i.e. 'T is char*' or 'T is const char*' in the generic form above. apr_array_pstrcat() concatenates all the elements in the array and make a new string. The following example shows how to make one string from string chunks. /* pseudo code to make one string from string chunks */ apr_array_header_t *html_arr = apr_array_make(mp, ARRAY_INIT_SZ, sizeof(const char*)); *(const char**)apr_array_push(html_arr) = "<html>"; *(const char**)apr_array_push(html_arr) = "<head><title>foo</title></head>"; *(const char**)apr_array_push(html_arr) = "<body><ul>"; for (i = 0; i < NUM_LIST; i++) { *(const char**)apr_array_push(html_arr) = apr_psprintf(mp, "<li>%d</li>", i); } *(const char**)apr_array_push(html_arr) = "</ul></body>"; *(const char**)apr_array_push(html_arr) = "</html>"; const char *html_str = apr_array_pstrcat(mp, html_arr, '\0'); You might intend to sort the elements in the array. I mentioned that search on array isn't so effiecient above, but it can be efficient by bsearch(3) if it's sorted. I show you how to use qsort(3) with array. /* qsort(3) sample with array */ /* we assume arr is an array of string */ qsort(arr->elts, arr->nelts, arr->elt_size, cmp_string); /* compare function for qsort(3) */ static int cmp_string(const void *v1, const void *v2) { const char *s1 = *(const char**)v1; const char *s2 = *(const char**)v2; return strcmp(s1, s2); } There is one more API that I have to point out. It is apr_array_pop(). By apr_array_pop(), we can use array as stack, which is known as first-in, last-out container. Please take a look at about the usage. table

Table is container of key-value pairs. It assumes key is character string. Value is allowed to be any type, but it is almost assumed to be character string. In the following description, we assume both key and value are character strings. As container type, table has the following features. append|efficient(API support)@ insert/prepend|inefficient(API support)@ delete|inefficient(API support)@ search(lookup)|almost efficient(API support)@ iteration|efficient(API support)
table
As you see, the features are almost same as array. This is natural, because table internally relies on array. Table has additional better features, though. First, table has better API supports. Second, table is more efficient for search(lookup). Table uses internal checksums for efficient search. Although it is not so efficient as hash table described later, it is better enough. Table type is apr_table_t. We call apr_table_make() to create a new object as follows: /* excerpted from */ apr_table_t *tab; tab = apr_table_make(mp, TABLE_INIT_SZ); The first argument is memory pool to use. Like apr_array_header_t, both table itself and elements are allocated in the memory pool. The second argument is an initial size of table. As same as dynamic array, table size is dynamic. So, the initial size is just a hint. There are four API to append elements to table as follows: /* excerpted from apr_tables.h */ APR_DECLARE(void) apr_table_set(apr_table_t *t, const char *key, const char *val); APR_DECLARE(void) apr_table_setn(apr_table_t *t, const char *key, const char *val); APR_DECLARE(void) apr_table_add(apr_table_t *t, const char *key, const char *val); APR_DECLARE(void) apr_table_addn(apr_table_t *t, const char *key, const char *val); The difference between 'set' and 'add' is that we allow multiple values for the same key or not. By 'set' calls, we don't allow multiple values for the same key. So, if another value already exists for the key, both apr_table_set() and apr_table_setn() over-write the old value. In contrast, both apr_table_add() and apr_table_addn() allow multiple values for the same key. Thus, the old values are still left. The difference between 'with-n' and 'without-n' is that key and value's memories are duplicated or not. By 'without-n' calls, APIs duplicate both key and value string's memory. By 'with-n' calls, APIs don't duplicate them. 'With-n' APIs are more efficient, so when you know the string duplication is not necessary, you should use apr_table_setn() or apr_table_addn(). I show such examples: /* examples we can call apr_table_setn() or apr_table_addn() */ apr_table_setn(tab, "foo", "bar"); /* string literals don't require duplication */ apr_table_setn(tab, apr_pstrdup(mp, "foo"), apr_pstrdup(mp, "bar")); /* since the strings are already allocated in the same memory pool, we don't need double duplications */ To search value by key, we call apr_table_get(). The prototype declaration is as follows: /* excerpted from apr_tables.h */ APR_DECLARE(const char *) apr_table_get(const apr_table_t *t, const char *key); If the key exists in the table, the associated value is returned. Otherwise, the return value is NULL. As mentioned earlier, 'add' APIs allow multiple values for a key. However, apr_table_get() can't return multiple values. It just returns the first value. Allowing multiple values does make sense when we iterate over the table, which I'll describe later. REMARK: Table key is case-insensitive. Historically, table was developed for HTTP headers. apr_table_get()'s return value is just a pointer. I mean it doesn't duplicate the value string. So, the following sample code is buggy code. /* BUGGY sample */ apr_table_setn(tab, "mykey", strdup("myval"));/* apr_table_setn() doesn't duplicate the strings */ const char *v = apr_table_get(tab, "mykey"); assert(strcmp(v, "myval") == 0); /* v's value should be "myval" */ /* v is a string allocated by strdup(3), so calling free(v) is a proper operation. * However, since the element still exists in the table, free(v) leaves an invalid memory in the table */ free(v); On the other hand, when you remove an element from the table, you must free the string memory to get around memory leak. /* The similar situation above */ const char *v = apr_table_get(tab, "mykey"); apr_table_unset(tab, "mykey"); free(v);/* XXX If we forget this, it causes memory leak... */ A simple solution is to use memory pool. If all keys and values strings are allocated in the same memory pool of the table, all you have to do is to call apr_pool_destroy(). There are two ways to do iteration over table. One is to use callback function. The other is to use internal apr_array_header_t directly. To use callback function for iteration, we call apr_table_do(). The prototype declaration and usage are as follows: /* excerpted from apr_tables.h */ APR_DECLARE_NONSTD(int) apr_table_do(apr_table_do_callback_fn_t *comp, void *rec, const apr_table_t *t, ...); /* excerpted from */ apr_table_do(tab_cb, "label1", tab, NULL); apr_table_do(tab_cb, "label2", tab, "key1", "key2", NULL);/* iteration with filters */ /* table iteration callback */ static int tab_cb(void *data, const char *key, const char *value) { const char *label = data; printf("callback[%s]: %s %s\n", label, key, value); return TRUE;/* TRUE:continue iteration. FALSE:stop iteration */ } The first argument of apr_table_do() is callback function. The second argument is context object passed to callback function. The third argument is table object. The remained arguments work as filters for keys. The other iteration style is to use apr_array_header_t directly. This seems a bad attitude, because it depends on the internal implementaion. It might be bad, but you can be pragmatic. Table internally has apr_table_entry_t object. If you understand libapr's dynamic array described above, you can understand the following example. /* excerpted from */ const apr_array_header_t *tarr = apr_table_elts(tab); const apr_table_entry_t *telts = (const apr_table_entry_t*)tarr->elts; int i; for (i = 0; i < tarr->nelts; i++) { printf("key = %s, val = %s\n", telts[i].key, telts[i].val); } hash table

Hash table is a yet another container type of key and value pairs. As container type, hash table has the following features. append/insert/prepend|almost efficient(API support)@ delete|almost inefficient(API support)@ search(lookup)|efficient(API support)@ iteration|almost inefficient(API support)
hash table
Obviously, search(lookup) is efficient and scalable. The other operations are not so bad. As a result, if we require a container with many read(lookup) operations and less write(insert/append/prepend/delete) operations, hash table can be the best choice. Hash table type is apr_hash_t. We call apr_hash_make() to create a new object. The argument is only one, memory pool to use. /* excerpted from apr_hash.h */ APR_DECLARE(apr_hash_t *) apr_hash_make(apr_pool_t *pool); Hash table has two basic operations, 'set' and 'get'. Their prototype declarations are as follows: /* excerpted from apr_hash.h */ APR_DECLARE(void) apr_hash_set(apr_hash_t *ht, const void *key, apr_ssize_t klen, const void *val); APR_DECLARE(void *) apr_hash_get(apr_hash_t *ht, const void *key, apr_ssize_t klen); key is normally a character string, but it allows any type. 'apr_ssize_t klen' is length of the key. val is a value, which is sometimes a character string, but any type is allowed as same as key. Both key and val are just pointers. Later, I'll describe cautions about memory managements of them. If key is a character string, we can set klen to APR_HASH_KEY_STRING. Internally, it just calls strlen(3) for key. For comparison of keys, hash table does memory comparison of values of keys rather than comparison of pointer values. More precisely, it calls memcmp(3) for keys with their length by klen. When keys are character strings, everything is straightforward. The following sample code works as you expect. /* works as you expect */ apr_hash_t *ht = apr_hash_make(mp); const char *k1 = apr_pstrdup(mp, "foo"); const char *v1 = apr_pstrdup(mp, "bar"); apr_hash_set(ht, k1, APR_HASH_KEY_STRING, v1); /* "foo"=>"bar" */ const char *k2 = apr_pstrdup(mp, "foo"); assert(k1 != k2); /* key pointers are different */ assert(strcmp(k1, k2) == 0); /* key strings are same. hash table does the almost same comparison */ const char *v2 = apr_hash_get(ht, k2, APR_HASH_KEY_STRING); assert(v1 == v2); /* hash table keeps the pointers. This equation is true */ assert(strcmp(v1, v2) == 0); /* needless to say, this is also true */ If keys are not character strings, it wouldn't work as you expect. See the following example. /* Bit weird code. This doesn't work as you expect */ typedef struct _key_t { int ki; const char *ks; } key_t; key_t k1 = { 0, apr_pstrdup(mp, "foo") }; key_t k2 = { 0, apr_pstrdup(mp, "foo") }; const char *v = apr_pstrdup(mp, "bar"); apr_hash_set(ht, &k1, sizeof(key_t), v);/* this is legal */ const char *v1 = apr_hash_get(ht, &k1, sizeof(key_t)); assert(v1 == v); /* works as you expect */ const char *v2 = apr_hash_get(ht, &k2, sizeof(key_t)); /* Do you think v2 equals to v ? */ /* No. hash table calls memcmp(&k1, &k2, sizeof(key_t) internally. By this comparison, k1::ks doesn't equal to k2:ks, so that v2 doesn't equal to v1. */ In my experience, types other than character strings are rarely used for keys. So, don't care so much. In the examples above, I did apr_pstrdup() for both keys and values. Hash table keeps only pointers, so we have to take care of string memories by ourselves. The following sample is a typical buggy code. /* BUGGY code: an invalid entry exists in hash table */ void func(apr_hash_t *ht) { char key[32]; strcpy(key, "foo"); apr_hash_set(ht, key, APR_HASH_KEY_STRING, "bar");/* key is going to be invalid out of this function */ return; } When we delete an entry, we just pass NULL to 'val' as follows: /* excerpted from . Delete an entry from hash table */ apr_hash_set(ht, "to-del", APR_HASH_KEY_STRING, NULL); In some cases, key string and value object may not be allocated in memory pool. We must be careful about their memory managements. Note that we should take care of thier memory after deleting the entry from hash table as follows: /* A case value objects require own memory management */ typedef struct _ht_obj_t { /* value object type */ const char *key; int val; } ht_obj_t; /* create an object value */ ht_obj_t *obj = malloc(sizeof(ht_obj_t)); obj->key = strdup("foo"); obj->val = 0; apr_hash_set(ht, obj->key, APR_HASH_KEY_STRING, obj); /* [1] typical memory leak bug in deleting entry */ { apr_hash_set(ht, "foo", APR_HASH_KEY_STRING, NULL); /* if we forget to free @obj related to "foo", we have memory leak bug */ } /* [2] workaround of memory leak */ { ht_obj_t *obj = apr_hash_get(ht, "foo", APR_HASH_KEY_STRING); apr_hash_set(ht, "foo", APR_HASH_KEY_STRING, NULL); destroy_obj(obj);/* assuming this takes care of free(obj->key) and free(obj) */ } If we add a new entry to hash table and the same key already exists, what happen? The answer is the old value is overwritten(it's intuitive). However, the old key isn't overwritten, neither. This sometimes causes a bug. The following code shows it. /* BUGGY code (very confusable) */ const char *old_key = strdup("foo"); const char *old_val = strdup("bar"); apr_hash_set(ht, old_key, APR_HASH_KEY_STRING, old_val); /* overwrite with a new key, but the same key */ const char *new_key = apr_pstrdup(mp, "foo"); const char *new_val = apr_pstrdup(mp, "BAR"); apr_hash_set(ht, new_key, APR_HASH_KEY_STRING, new_val);/* this overwrites old entry as you expect except that @old_key is still alive... */ const char *v = apr_hash_get(ht, "foo", APR_HASH_KEY_STRING); assert(v == new_val && v != old_val); /* We find the new value in hash table */ /* We would think both old key and old val have no reference... */ free(key); /* BUGGY... */ free(val); /* What happened? * The current entry in hash table consists of old_key and new_val, so that it causes disaster. */ The iteration code on hash table looks as follows: /* excerpted from */ apr_hash_index_t *hi; for (hi = apr_hash_first(NULL, ht); hi; hi = apr_hash_next(hi)) { const char *k; const char *v; apr_hash_this(hi, (const void**)&k, NULL, (void**)&v); printf("ht iteration: key=%s, val=%s\n", k, v); } REMARK: The latest libapr has freelist in hash table, but the old version didn't. So, it caused memory leak to keep adding and deleting various entries with unique keys. If you use an older libapr, I recommend you to upgrade a newer version(after apache-2.0.54). If upgrading is difficult(e.g. you're developing apache modules and you can't easily upgrade the apache), I suggest you to update only apr_hash.c.. ring (cyclic doubly linked list)

Ring is cyclic doubly linked list. As container type, linked list has the following features. append/insert/prepend|almost efficient(API support)@ delete|almost efficient(API support)@ search(lookup)|depends(API support)@ iteration|almost efficient(API support)
ring
Linked list is good at modifications, such as append, insert, prepend, delete. Such operations usually work better than other container types. Moreover, other operations, such as search and iteration, usually work almost efficiently. Since we can easily make sorted linked list, search operation works much faster. I think linked list is often a good choice for you. apr_ring is a little bit different from the other container types mentioned above. apr_ring is not a container type by itself. It helps you to make linked list data structure. At first, we have to define our own types, element type and its container type. In the following example, my_elem_t is an element type name, and my_ring_t is a container type name. Putting APR_RING_ENTRY() macro in a structure type means that the type becomes element of ring container. APR_RING_HEAD() defines our own ring container type. /* excerpted from */ /* type definitions sample of ring */ typedef struct _my_elem_t { APR_RING_ENTRY(_my_elem_t) link; int num; const char *str; } my_elem_t; typedef struct _my_ring_t my_ring_t; APR_RING_HEAD(_my_ring_t, _my_elem_t); Next, we create a ring container object. The type is complete, so we can create an object explicitly. Note that we shouldn't rely on my_ring_t's internals, and we must call only APIs to use it. After we create a ring container object, we initialize it by APR_RING_INIT(). APR_RING_INIT() requires three arguments, the ring container object, element type name, and the name of APR_RING_ENTRY() in the element type. /* excerpted from */ my_ring_t *ring; ring = apr_palloc(mp, sizeof(my_ring_t)); APR_RING_INIT(ring, _my_elem_t, link); Now, we are ready to use ring. To append an element to ring, we call APR_RING_INSERT_TAIL(). To prepend, we call APR_RING_INSERT_HEAD(). To insert, we call APR_RING_INSERT_BEFORE() or APR_RING_INSERT_AFTER(). Please take a look at about the usage. We can handle a set of elements simultaneously. To insert elements to ring, we call APR_RING_SPLICE_AFTER() or APR_RING_SPLICE_BEFORE(). We can easily merge two rings to one ring. To do so, we call APR_RING_CONCAT(). To remove elements from ring, we call APR_RING_UNSPLICE(). We can do iteration over ring. The following sample code shows the most basic iteration. /* excerpted from */ const my_elem_t *ep; for (ep = APR_RING_FIRST(ring); ep != APR_RING_SENTINEL(ring, _my_elem_t, link); ep = APR_RING_NEXT(ep, link)) { printf("elem: num=%d, str=%s\n", ep->num, ep->str); } As ring is doubly linked list, we can do reverse iteration, too. Furthermore, ring is cyclic linked list, so we can continue the iteration beyond the end of list. Please take a look at about the usage. REMARK: As you can see, all ring APIs are implemented as macros. REMARK: If the ring object's lifetime is long and you allocate the elements in memory pool, it is a good idea to use freelist. You can easily implement freelist by ring. By freelist, you can avoid memory leak. In addition, adding a new element becomes faster. Tutorial Copyright and Permissions Notice

The libapr programming tutorial is Copyright (C) 2005 INOUE Seiichiro <inoue&ariel-networks.com> Permission is granted to make and distribute verbatim copies of this manual provided the copyright notice and this permission notice are preserved on all copies. Permission is granted to copy and distribute modified versions of this document under the conditions for verbatim copying, provided that this copyright notice is included exactly as in the original, and that the entire resulting derived work is distributed under the terms of a permission notice identical to this one. Permission is granted to copy and distribute translations of this document into another language, under the above conditions for modified versions. If you are intending to incorporate this document into a published work, please contact the maintainer, and we will make an effort to ensure that you have the most up to date information available. There is no guarantee that this document lives up to its intended purpose. This is simply provided as a free resource. As such, the authors and maintainers of the information provided within can not make any guarantee that the information is even accurate.