-
Notifications
You must be signed in to change notification settings - Fork 907
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
Proposal: introduce kj::CopyOnWrite<T>, kj::AtomicCopyOnWrite<T> #1934
Comments
/cc @mikea ... this may have some interesting intersection with some of your other in-progress smart ptr proposals |
I wonder if this could just be an inherent feature of In any case I have nothing against the idea in theory, but to merge this I'd want there to be at least one real-world use queued up immediately where the utility clearly makes the code better. It sounds like you've identified some already. |
Yep, currently in the workerd codebase we end up copying |
Briefly discussed with @mikea ... I definitely think this can be worked into the // Modify the Rc/Arc in place:
struct Foo { int x = 0 };
kj::Rc<Foo> foo = kj::refcounted<Foo>();
kj::Rc<Foo> ref = foo.addRef(); // ref gets sent off somewhere else
foo.write()->x = 1; // copies only if refcount > 1 and replaces the inner Foo in-place, otherwise modify in place
// As opposed to...
kj::Rc<Foo> foo = kj::refcounted<Foo>();
kj::Rc<Foo> ref = foo.addRef(); // ref gets sent off somewhere else
kj::Rc<Foo> foo2 = foo.clone(); // aways copies, even if refcount is 1
foo2->x = 1; The second option feels more aligned with The one significant drawback to this approach, however, is that the copy-on-write semantics could be easily missed, which could introduce some subtle bugs. For instance, if someone forgot to use this additional API when given a // I want copy-on-write semantics for edits to Foo
kj::Rc<Foo> foo = ...
foo->x = 1; // oops should have called clone() or write() ...
// vs...
kj::CopyOnWrite<Foo> foo = ...
foo.write()->x = 1; // no choice but to call write ... no way to accidentally oops |
Maybe |
kj::Rc<const Foo> foo = kj::refcounted<const Foo>();
kj::Rc<const Foo> ref = foo.addRef(); // Send ref off to something else...
kj::Rc<Foo> mutFoo = foo.makeWritable();
// Tho it does feel a bit weird for kj::Rc<T> to also have this "makeWritable"
// maybe instead....?
kj::Rc<Foo> mutFoo = kj::copyOnWrite(kj::mv(foo));
// Then to get another copy-on-write Rc...
kj::Rc<const Foo> constFoo = mutFoo.makeConst(); // consumes mutFoo |
You can use SFINAE to make sure it's only there if Maybe |
ok, I think this approach works. I'll work up a PR based on the |
There are a range of cases where Copy-on-write semantics would be helpful and could help performance (e.g. avoiding copies on iterators, for instance, when iterating over a set of headers .. see api/http.c++ in the workerd codebase). It would be useful for kj to have a simple mechanism built-in.
Example use:
The text was updated successfully, but these errors were encountered: