1. Design notes
The porting layer design adheres to concepts of APR (Apache Portable Runtime libraries). These particularly include:
- explicit memory management based on pools;
- consistent error reporting approach via returned status value;
- incomplete types to enforce platform abstraction.
Detailed APIs mainly follow APR standard, but there may not be strict correspondence in signatures. Pure C should be used for implementation.
Using APR binaries directly
We define our own headers (aligned with APR), but do not wrap APR functions in code (no delegating calls).
Instead, we map our signatures to APR functions directly, where it is possible, - via a set of defines, like this:
#define port_allocator_create(allocator) apr_allocator_create(allocator)
#define port_allocator_destroy(allocator) apr_allocator_destroy(allocator)
So, there is no runtime performance penalty at all.
Why don't we use APR headers directly?
Because, current approach has several advantages which we consider significant:
- Flexibility to switch portlib implementation at any moment - to j9 or any other, including our own. In other words, we hide internal portlib issues from API customers.
- We have a number of extensions (currently there are over 20 functions in portlib, which are not in APR). So we have consistent naming of all portlib API
- Easy to bugfix or tune particular functions. We already discovered cases, when certain APR functions have flaws or do not suite VM needs properly - and we can replace single functions very easily. The same is applicable for VAV.
- When (if) the time comes for dynamic linking of porting layer, or for multi-VM support, our headers are more suitable for organizing vtables than APR ones (which somewhere use defines instead of real functions).
- APR has its own build organization, completely different from DRL. Namely, there are different sets of headers for each platform, with specific defines. We avoid extra inconveniences by introducing common headers.
- Clearly identified subset of APR functionality, which is actually used - will be helpful for further VM porting
Of course, we also realize drawbacks of it:
- Need for duplication of headers and documentation (though we may simply use redirection links to APR docs).
- Potential compatibility issues with future APR development (e.g. on extension boundaries).
- Some space for confusion in open community, especially for those experienced in APR applications.
2. Directory structure
Porting functionality is split to several groups (atomics, file I/O, mempools, etc). Each group has a base directory, which contains subdirectories with actual code. These subdirectories are named after the platforms and/or architectures they are compiled on. If there is a strong dependency on both OS and machine architecture, the subdirectory name should combine both. For example:
Port
|
-> file_io
| |
| -> linux
| |
| -> windows
|
-> atomics
|
-> linux_em64t
|
-> linux_ia32
|
-> windows
Public headers are kept in a separate base directory named include. Implementation headers may be kept in source directories?
3. Build system
Currently we reuse VM build system based on MakeCommon.
TODO - document platform defines and macros.
4. APR issues
- Non-buffered file I/O on Windows has bad support for
apr_file_eof();
- On Windows,
apr_dso_load()
fails on NULL path -
which is meant for obtaining a handle for the calling module itself;