Automatic copy-on-write semantic memory slices for use in C (and C++)
Automatic copy-on-write semantic memory slices library for use in C and C++.
# Usage
# Usage
See `include/cow.h` for documentation on each function.
See `include/cow.h` for documentation on each function.
See `include/cow.hpp` for the C++ wrapper API class.
## C API
## C API
Each function, macro, and type definition in the header will be prefixed with `cow_` or `COW_`. Internal non-prototpyed items use the namespace `_cow_` or `_COW_`.
Each function, macro, and type definition in the header will be prefixed with `cow_` or `COW_`. Internal and/or non-prototpyed items use the namespace `_cow_` or `_COW_`.
### C++ wrapper API
### C++ wrapper API
The C++ interface defines the type `Cow`, a reference-counted wrapper over `cow_t` instances that supports cloning through its subtype, `Cow::Fake`, and automatically ensures the originally created `cow_t` is not destroyed until all its clones are, as well as the namespace `_cow_util` which contains memory accessor helpers `Span<T>` and `Slice<T>` (aka `Span<T>::Slice`).
The C++ interface defines the type `Cow`, a reference-counted wrapper over `cow_t` instances that supports cloning through its subtype, `Cow::Fake`, and automatically ensures the originally created `cow_t` is not destroyed until all its clones are, as well as the namespace `_cow_util` which contains memory accessor helpers `Span<T>` and `Slice<T>` (aka `Span<T>::Slice`).
@ -16,11 +17,11 @@ There are also the following:
## Building
## Building
Run `make` to build to build the `release` (optimised) target of the library.
Run `make` to build to build the `release` (optimised) target of the library.
It will create four files: `libcow-release.a`, `libcow-release.so`, `libcow.a`, and `libcow.so`.
It will create four files: `libcow-release.a`, `libcow-release.so`, `libcow.a`, and `libcow.so` (wish `SONAME` versioned symlinks).
The latter two are just symlinks to the former two.
The latter two are hardlinked to the former two.
Run `make debug` to build the debug target, which disables optimisations and includes trace messages.
Run `make debug` to build the debug target, which disables optimisations and includes trace messages.
It will create two files: `libcow-debug.a` and `libcow-debug.so`.
It will create two files: `libcow-debug.a` and `libcow-debug.so`. The debug target `.so` does not include a `SONAME`, nor does it produce the versioning symlinks (unless you manually set `LDFLAGS="-Wl,-soname,libcow.so.<version>"` and create the symlinks afterwards.)
Each target compiles both a static and dynamic library. You may need to run `make clean` before switching build targets.
Each target compiles both a static and dynamic library. You may need to run `make clean` before switching build targets.
To build both targets, run `make all`.
To build both targets, run `make all`.
@ -32,29 +33,29 @@ Run `sudo make uninstall` to remove the libraries and header files.
By default, the install target is `/usr/local/`. Set the `PREFIX` variable when running `make install` / `make uninstall` to specify a different path.
By default, the install target is `/usr/local/`. Set the `PREFIX` variable when running `make install` / `make uninstall` to specify a different path.
### Full build and installation
## Installing
To build and install with the default configuration.
```shell
```shell
$ make && sudo make install
$ make && sudo make install
```
```
Will build with the default optimisation configuration and install the following files/directories:
Will build with the default optimisations enabled and install the following files/directories:
* The `release` target specifies `-march=native` by default. This may be undesirable, if so, set `TARGET_CPU=""` when running `make`.
* The `release` target specifies `-march=native` by default. This may be undesirable, if so, set `TARGET_CPU=""` when running `make`.
* Many optimisation flags for the `release` configuration are specific to GCC (with graphite enabled by default), if builds on other compilers (or non-graphite enabled GCC builds) complain, either set the `OPT_FLAGS` env var or remove the problem flags from the Makefile.
* Many optimisation flags for the `release` configuration are specific to GCC (with graphite enabled by default), if builds on other compilers (or non-graphite enabled GCC builds) complain, either set the `OPT_FLAGS` env var or remove the problem flags from the Makefile.
* `release` builds are stripped by default. run `make STRIP=: release` to prevent stripping.
* `release` builds are stripped by default. run `make STRIP=: release` to prevent stripping.
* The targets are all built with `-fno-strict-aliasing`, but functions in the header file are still annotated with `restrict` needed. This is just to inform users that the function will assume the pointer is not aliased. (When included in C++, where `restrict` is not a keyword, we temporarily define it to be `__restrict__`, which is the GCC equivalent for C++).
* The targets are all built with `-fno-strict-aliasing`, but functions in the header file are still annotated with `restrict` needed. This is just to inform users that the function will assume the pointer is not aliased. (When included in C++, where `restrict` is not a keyword, we temporarily define it to be `__restrict__`, which is the GCC equivalent for C++).
* The `debug` target `.so` does not include a `SONAME`, nor does it produce the output symlinks expected of a `SONAME`. The `release` target does. The version is specified in the Makefile.
## Using
## Using
Link to either `libcow.a` or `libcow.so` (or the debug target libraries), and include the header `include/cow.h` to your project to use this library.
Link to either `libcow.a` or `libcow.so` (or the debug target libraries), and include the header `include/cow.h` to your project to use this library.
The header should work in C++ projects as well.
The header should work in C++ projects as well, but there is a C++-specific wrapper API in `include/cow.hpp` which you can use instead for automatic handling of resources (*see above*).
# Requirements
# Requirements
Relying on the `memfd_create()` syscall, Linux >=3.17 and glibc >=2.27 (or equivalent) are required for build.
Relying on the `memfd_create()` syscall, Linux >=3.17 and glibc >=2.27 (or equivalent) are required for build.
@ -99,6 +100,52 @@ Fake: Hello fake!
```
```
Notice the first read of `fake` contains the data written to `origin`. And that the write of `Hello fake!` caused only `fake` to be updated, not `origin`.
Notice the first read of `fake` contains the data written to `origin`. And that the write of `Hello fake!` caused only `fake` to be updated, not `origin`.
## C++ API example
``` c++
#include<cow.hpp>
#include<cstring>
#include<cstdio>
void write_cow(Cow& to, const char* string)
{
strncpy(to.area_as<char>(), string, to.size()-1);
}
void read_cow(const Cow& from)
{
printf("Cow says: %s\n", from.area_as<char>());
}
int main()
{
Cow area(1024);
write_cow(area, "Initial state");
Cow::Fake clone = area;
read_cow(clone);
write_cow(clone, "Cloned state");
read_cow(clone);
read_cow(area);
return 0;
}
```
Will print:
``` shell
$ ./test
Cow says: Initial state
Cow says: Cloned state
Cow says: Initial state
```
The `Cow` class and its subclass `Cow::Fake` handles freeing resources automatically. Alternatively, there is the `Area` class which can act as both (see `cow/area.hpp`).
## What is happening here?
## What is happening here?
The cloned slice, `fake`, which is created from `origin` with the `cow_clone()` function will contain all the information within `origin`.
The cloned slice, `fake`, which is created from `origin` with the `cow_clone()` function will contain all the information within `origin`.
The cloned slice can be written to, however, those writes will only be visible to that specific instance of `cow_t`, even if that `cow_t*` is again `cow_clone()`d.
The cloned slice can be written to, however, those writes will only be visible to that specific instance of `cow_t`, even if that `cow_t*` is again `cow_clone()`d.
/// Writes to this instance pointer (`cow_ptr()`) are written to the allocated memory.
/// Writes to this instance pointer (`cow_ptr()`) are written to the allocated memory.
/// Writes to any cloned instances do not propagate to this instance.
/// Writes to any cloned instances do not propagate to this instance.
cow_t*cow_create(size_tsize);
cow_t*cow_create(size_tsize);
/// Create a new copy-on-write area of `size` bytes from file `fd`.
/// Writes to this instance pointer (`cow_ptr()`) are written to the allocated memory.
/// Writes to any cloned instances do not propagate to this instance.
///
/// The resulting object does not own `fd`, but does own a duplicate of it.
cow_t*cow_create_fd(intfd,size_tsize);
/// Free a cow area. This should be called on all clones before the parent of those clones is freed.
/// Free a cow area. This should be called on all clones before the parent of those clones is freed.
voidcow_free(cow_t*restrictcow);
voidcow_free(cow_t*restrictcow);
/// Create a clone of this instance. Any writes to the returned pointer will not be propagated to the input one.
/// Create a clone of this instance. Any writes to the returned pointer will not be propagated to the input one.
@ -31,7 +58,7 @@ static inline
#ifdef _COW_NO_ASSUME_ABI
#ifdef _COW_NO_ASSUME_ABI
#define _cow_size_unsafe(v) cow_size(v)
#define _cow_size_unsafe(v) cow_size(v)
#else
#else
// XXX: This macro is *VERY* ABI sensitive. This shouldn't be used if the ABI has changed since the build of libcow's `cow_t.h` passed its static assertions in *both* the C and C++ implementations.
// XXX: This macro is *VERY* ABI sensitive. This shouldn't be used if the ABI has changed since the build of libcow's `cow_t.h` passed its stati assertions in *both* the C and C++ implementations.
// The C++ API uses this by default for its `Cow::size()` function.
// The C++ API uses this by default for its `Cow::size()` function.
#define _cow_size_unsafe(v) (*(((size_t*)(v))+1))
#define _cow_size_unsafe(v) (*(((size_t*)(v))+1))
__attribute__((deprecated("size() is safer and offers better codegen.")))
__attribute__((deprecated("size() is safer and offers better codegen.")))
TRACE("WARNING: attempted to free poisoned object at %p",(constvoid*)cow);
return;
}
TRACE("unmapping %s cow of %lu size from %p (fd %d, real fd %d)",cow_is_fake(cow)?"fake":"and closing fd of origin",cow->size,cow->origin,cow->fd,cow_real_fd(cow));
TRACE("unmapping %s cow of %lu size from %p (fd %d, real fd %d)",cow_is_fake(cow)?"fake":"and closing fd of origin",cow->size,cow->origin,cow->fd,cow_real_fd(cow));