Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

take operation accepts mutable loaned reference for types which supports it #718

Open
wants to merge 43 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
f613aec
separate LoanedCTypeMut trait
milyin Sep 23, 2024
e7dee54
validate equivalece in transmute
milyin Sep 23, 2024
c023e9b
mutable loaned type wraps rust owned type for equal size types
milyin Sep 24, 2024
cdc06c8
loan_mut added
milyin Sep 24, 2024
2d54b53
Merge branch 'loan_mut' into take_loan_mut2
milyin Sep 24, 2024
0e02579
move, loan, etc operations documented
milyin Sep 24, 2024
8a229e4
take functions added
milyin Sep 27, 2024
531f520
Merge branch 'main' into take_loan_mut2
milyin Oct 21, 2024
18d1161
syntax error fixes
milyin Oct 21, 2024
56b1f4d
build errors fixed
milyin Oct 22, 2024
28c863e
unsafe transmute for loaned mut - unifinished
milyin Oct 23, 2024
89db48c
build fixes
milyin Oct 23, 2024
ee774c7
Merge branch 'main' into take_loan_mut2
milyin Oct 23, 2024
ebb4e48
header regenerated
milyin Oct 23, 2024
41fca7e
build fix
milyin Oct 23, 2024
0ea6646
clippy fix
milyin Oct 23, 2024
b264590
clippy fix
milyin Oct 23, 2024
12d0937
doc corrected
milyin Oct 23, 2024
d89df38
generic for take loaned
milyin Oct 24, 2024
6eb5e62
missing loan_muts added, unnecessry unsafe removed, test for loan_mut…
milyin Oct 24, 2024
5fed212
z_check is not internal anymore
milyin Oct 24, 2024
29cde90
clang fmt
milyin Oct 24, 2024
75b9d20
internal check in examples renamed
milyin Oct 24, 2024
c43af34
clang format
milyin Oct 24, 2024
fe67f27
clang format
milyin Oct 24, 2024
fd04bef
take_loaned kept only for calllback parameters
milyin Oct 25, 2024
608e436
clang format
milyin Oct 25, 2024
482491e
cargo fmt
milyin Oct 25, 2024
9e3a063
doc comments corrected
milyin Oct 25, 2024
a87992f
internal local names no need to have 'z..' prefix
milyin Oct 25, 2024
097ac4a
use loaned_mut on Option only where required
milyin Oct 26, 2024
792aa7e
transmute rs corrected
milyin Oct 26, 2024
e8e4568
unised trait removed
milyin Oct 26, 2024
f55a58f
unused unsafe removed
milyin Oct 26, 2024
e70a107
removed unused loan_mut
milyin Oct 27, 2024
5b70018
doc corrected
milyin Oct 27, 2024
8022fc9
Merge branch 'main' into take_loan_mut2
milyin Nov 7, 2024
f214cb4
doc updated
milyin Nov 8, 2024
8119967
Merge branch 'main' into take_loan_mut2
milyin Nov 8, 2024
4eb098d
doc updated
milyin Nov 8, 2024
f709ed7
doc updated
milyin Nov 8, 2024
ae81556
doc updated, renamed to take_from_loaned
milyin Nov 8, 2024
c14a441
errors in doc fixed
milyin Nov 8, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ impl crate::transmute::TakeCType for {moved_type_name} {{

impl Drop for {type_name} {{
fn drop(&mut self) {{
use crate::transmute::RustTypeRef;
use crate::transmute::RustTypeMut;
std::mem::take(self.as_rust_type_mut());
}}
}}
Expand Down Expand Up @@ -988,7 +988,7 @@ pub fn create_generics_header(path_in: &str, path_out: &str) {
)
.unwrap();

// Collect all function signatures to be wrappeb by macros and verify that all necessary functions are present for each entity
// Collect all function signatures to be wrapped by macros and verify that all necessary functions are present for each entity
let (move_funcs, take_funcs) = make_move_take_signatures(path_in);
let loan_funcs = find_loan_functions(path_in);
let loan_mut_funcs = find_loan_mut_functions(path_in);
Expand Down
5 changes: 5 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,12 @@ zenoh-c API documentation is available on [Read the Docs](https://zenoh-c.readth
-------------------------------
## How to build it

1. generate headers with all API functions/structures enabled
2. run doxygen to parse headers
3. generate documentation

```bash
cargo check --all-features
cd docs
doxygen
sphinx-build -b html . _build/html
Expand Down
145 changes: 145 additions & 0 deletions docs/concepts.rst
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,151 @@ Types named `z_xxx_t` are copyable, and can be passed by value. Some of them are
z_timestamp_new(&ts, z_loan(session));
z_timestamp_t ts1 = ts;

Common operations
=================

The transition between "owned", "loaned" and "moved" structures above is performed by corresponding functions.
The following operations are available: move, loan, mutable loan, take, and drop. They are performed for
"xxx" entities by functions `z_xxx_move`, `z_xxx_loan`, `z_xxx_loan_mut`, `z_xxx_take`, `z_xxx_take_from_loaned`
(for certain types), and `z_xxx_drop`.
The generic macros `z_move`, `z_loan`, `z_loan_mut`, `z_take`, and `z_drop` are also provided.

Loan operation
--------------

Function `z_xxx_loan` accepts `const z_owned_xxx_t*` and returns a pointer `const z_loaned_xxx_t*` which gives read-only
access to the `z_owned_xxx_t` entity.

The `z_loan` macro accepts a variable of `z_owned_xxx_t` type and calls the corresponding `z_xxx_loan` function.

Mutable loan operation
----------------------

The function `z_xxx_loan_mut` accepts `z_owned_xxx_t*` and
returns a pointer `z_xxx_loaned_t*` which allows reading and modifying the `z_owned_xxx_t` entity.

The `z_loan_mut` macro accepts a variable of `z_owned_xxx_t` type and calls the corresponding `z_xxx_loan_mut` function.

Move operation
--------------

The function `z_xxx_move` accepts `z_owned_xxx_t*` and
returns a pointer `z_moved_xxx_t*` which only allows taking
ownership of the `z_owned_xxx_t`. The agreement is that the function which accepts a `z_moved_xxx_t*` parameter
is obliged to take ownership of it (see "take" operation).

The `z_move` macro accepts a variable of `z_owned_xxx_t` type and calls the corresponding `z_xxx_move` function.

Take operation
--------------

Functions `z_xxx_take` accept pointers to uninitialized `z_owned_xxx_t` destination structures and
`z_moved_xxx_t*` source pointers.

These functions move data from the source `z_owned_xxx_t` structure into the destination one. The source
structure is set to an empty "gravestone" state, like after a drop operation.

The `z_take` macro accepts `z_moved_xxx_t*` pointer and calls the corresponding
`z_xxx_take` functions.

Take from mutably loaned object operation
-----------------------------------------

Functions `z_xxx_take_from_loaned` accept pointers to uninitialized `z_owned_xxx_t` destination structures and
`z_loaned_xxx_t*` source pointers.

These functions move data from the source `z_loaned_xxx_t` structure into the destination one. The source
structure is set to "valid but unspecified" state: it **have** to be dropped, no other operation on it is safe,
unless if it's explicitly specified. See also section "Comparison with C++ move semantics".

The `z_take_from_loaned` macro accepts `z_loaned_xxx_t*` pointer and calls the corresponding
`z_xxx_take_from_loaned` functions.

Drop operation
--------------

Function `z_xxx_drop` accepts `z_moved_xxx_t*` pointer. It frees all resources hold by corresponding
`z_owned_xxx_t` object and sets this object to gravestone state, safe to double drop.

`z_drop` macro accepts `z_moved_xxx_t*` and calls corresponding `z_xxx_drop` function

Comparison with C++ move semantics
==================================

The behavior of `z_move` is similar to C++ `std::move`, it converts normal reference to a "rvalue reference" which is intended to be consumed by the function.
The difference is that the C++ automatically destructs the object. So the move semantics in C++ means taking the heavy data from rvalue-referenced object and
leaving it in some valid state which is later destroyed by it's destructor.

There is no automatic destructors in C, so for the same logic we would need to require developer to call destructor (`z_drop`) even after `z_move` operation.
This is inconvenient, so for move operation our requirement is more strict than for C++: if function expects `z_moved_xxx_t*` it
should left the object on passed pointer in "gravestone" state, i.e. state which doesn't hold any external resources and so safe to be forgotten.

Unfortunately this strict `z_move` semantic is not enough in the situations below:

First problem is that arguments of callbacks are "mutable loaned" references (e.g. `z_loaned_sample_t*`). It would be more logical to make them "moved" references to give
ownership to the callback function. But in this case the callback function would be obliged to take the ownership and drop the object after use even if
he needs only to read the object.

But on the other hand sometimes it's necessary to take ownership of the object passed to callback for further processing. Therefore the take
operation from mutable reference is required.

The second problem is that the C++ API doesn't use the zenog-c moved/loaned syntax sugar, as C++ has its own move semantics. The C++ `std::move` can be called
on any non-const reference, so we need to support this behavior. Detailed explanation is in note below, it's ok to skip it.

.. note::

Zenoh C++ API bypasses zenoh-c protection by simply making `reinterpret_cast` from `z_loaned_xxx_t*` to `z_owned_xxx_t*` and back when necessary. This means that
if the move constructor in C++ accepts e.g. C++ object `Reply&&`, it can't be sure if this reference points to `z_owned_reply_t` with valid gravestone state (internally in Rust this
corresponds to `Option<Reply>` and gravestone state is `None`) or is it `z_loaned_reply_t*` received from inside zenoh-c, which points just to `Reply`, not option-wrapped.
(It's important to notice that `Reply` and `Option<Reply>` have same size and layout in memory due to Null-Pointer Optimization, so it's safe to treat
`Option<Reply>` just as `Reply`, but not in other direction).

To resolve this the `z_take_from_loaned` operation is introduced for `z_loaned_xxx_t*`. It behaves similarly to `z_take` for `z_moved_xxx_t*` but doesn't provide
guarantee that the object is left in gravestone state. Instead it just leaves the object in some probably unusable but safe to drop state.

Zenoh guarantees that it never uses this operation inside its code. I.e. it's always safe to pass object to function with `z_loan_mut` and continue using it after return.
It's recommended to follow this rule in user code too and use `z_take_from_loaned` only in exceptional cases.

Examples:

`z_move` and `z_take` usage:

.. code-block:: c

void consume_string(z_moved_string_t* ps) {
z_owned_string_t s;
z_take(&s, ps);
printf("%.*s\n", z_string_len(z_loan(s)), z_string_data(z_loan(s)));
z_drop(s);
}
...
z_owned_string_t s;
z_string_copy_from_str(&s, "Hello, world!");
consume_string(z_move(s));
// no need to drop s here, passing it by z_move promises that it's dropped inside consume_string

`z_loan_mut` and `z_take_from_loaned` usage *(Notice that this example if fictional: actually take from loaned is implemented only
for types used in callbacks at this moment: `z_loaned_sample_t*`, `z_loaned_reply_t*`, `z_loaned_hello_t*`, `z_loaned_query_t*`)*:

.. code-block:: c

void may_consume_string(z_loaned_string_t* ps) {
if (z_string_len(ps) < 42) {
// process it in place
} else {
z_owned_string_t s;
z_string_take_from_loaned(&s, ps);
// save s for further processing
}
}
...
z_owned_string_t s;
z_string_copy_from_str(&s, "Hello, world!");
may_consume_string(z_loan_mut(s));
// can't make any assumptions about s here, but still obliged to drop it
z_drop(s);


Name Prefixes `z_`, `zc_`, `ze_`
================================

Expand Down
Loading