Basic proof-of-concept for copy-on-write memory allocating and handling. This is intended for a game to be able to perform background saving without having to use immutable data structures.
On initialization, a signal handler is installed, which will handle page faults by remapping them from one region of memory to another. This is a cow_handler.
A cow_handler creates and dispatches most work to a cow_block. A cow_block is a single chunk of memory which is backed by 2 anonymous memory mapped files (original and shadow). A cow_block is expected to own a large amount of memory which is then carved up further by something such as a slab allocator. This isn't implemented in this PoC.
During typical usage, the memory in a cow_block is read-write. When a snapshot of memory is requested, each cow_block is put in to COW mode, which will make the memory read-only, create a second anonymous memory mapped file (which is not initially populated), and return a second read-only view of the original memory mapped file at a different base address, the view.
Attempting to write to a read-only cow_block memory region will copy the contents of the page in the original file to the shadow file, and re-map the address to point to it.
Anything attempting to access the view must go through a translation layer, to map the pointer from original space to view space. This can be done either through the cow_handler (slow), or asking the cow_block (fast).
When copy-on-write is disabled for a cow_block, original is marked read-write, data is copied from shadowback tooriginal, and both shadowandview` are released.
- COWInitialize - sets up the signal handler
- COWNewBlock - allocates and returns a new
cow_blockof a given size - COWFindBlock - looks up a
cow_blockgiven a pointer inoriginalspace - COWBlockPointer - maps a pointer from
originalspace toviewspace, given a specificcow_block - COWPointer -
COWFindBlock(COWBlockPointer), convenience function which should probably be avoided due to the overhead of finding the block - COWEnable - puts a
cow_blockin to COW mode, allocating the backing resources required to manage its lifecycle. Returns the baseviewpointer. - COWDisable - takes a
cow_blockout of COW mode, copying all pages back fromshadowtooriginal, and releasing any resources. - COWDumpBlock - dump a blocks data to stdout for debugging
- I have no idea what happens if multiple mutation threads trigger a fault at the same time, some mutexes might be a good idea.
- There's currently no way to delete a block.
- Remapping a page is two distinct release/create operations. In theory, one could fail if the operating system decides to use the memory during that window. It may be possible to recover by falling back to a synchronous saving in such a situation, I haven't thought it through.
- There's no Windows port, although all the pieces exist (Vectored exception handling, CreateFileMapping, MapViewOfFileEx). I just don't have a C dev environment setup for Windows.
Good luck. Have fun.