04:56 | <Domenic> | James M Snell: BTW I think it would be really cool if there were some sort of language feature (?) where we could give a BufferSource to platform APIs and say "please transfer this to yourself, I won't use it" and then they wouldn't need to do copies. It's hard to imagine how it would work though... there's nothing straightforward, like there is in languages with move semantics that parameters can declare themselves as having. |
04:56 | <Domenic> | I guess if we were starting from scratch maybe we'd design every BufferSource-taking API as transferring, and ask you to make a copy if you plan to use it later? |
04:58 | <Domenic> | But something like new Response(body.take()) or new Response(take body) or something would be neat, where it's a call-site opt-in. I guess maybe new Response(body.transfer()) (with https://github.com/tc39/proposal-arraybuffer-transfer) is possible for a highly-integrated platform + JS engine to recognize and optimize, but that's a hard lift. |
05:10 | <James M Snell> | Yeah definitely difficult. For apis like the Request and Response constructors we could probably get away with a new option that communicates the intent, e.g. `new Response(buf, { transfer: true })`... but that's difficult to do consistently for all apis. |
05:12 | <James M Snell> | I certainly wouldn't mind a language level take/move type construct tho |
05:15 | <Domenic> | Yeah and it feels unfortunate to have to program it in one-off for each API. Then you have to litigate whether that API's really in the fast-path or not, and that'll have different answers for web vs. server... this feels like an area where the language can come in and just say "now there's a global fast thing available" because it doesn't have to judge. |
05:16 | <bakkot> | I wonder how hard the new Response(body.transfer()) pattern actually is to optimize |
05:17 | <Domenic> | I guess the most feasible path would be trying to hack on an engine to see how optimizable new Response(body.transfer()) can be, and then whether that hack can be generalized, or if generalizing it would benefit from some annotation (ideally in IDL to do code-gen for all BufferSource cases) |
05:17 | <Domenic> | I think the difficulty is not as much technical as "codebase-organizational" in that the teams that implement new Response() are pretty far from the teams that implement the JS engine, and want to operate without too much interaction and intertwingling of code. |
05:18 | <bakkot> | I'm imagining an internal bit on array buffers which is like "this is not aliased anywhere", which could be checked by platform APIs |
05:19 | <bakkot> | So the JS engine people could be responsible for figuring out how to set that bit, and the platform people would just need to check it |
05:19 | <bakkot> | Don't actually know if that's at all feasible though. |
05:19 | <Domenic> | Yeah if you could pull that off it seems pretty reasonable. |
08:45 | <Luca Casonato> | But something like |
08:47 | <Luca Casonato> | I wonder how hard the |
08:47 | <Luca Casonato> | It doesn’t seem very difficult |
08:49 | <Luca Casonato> | Oh actually I think I misunderstood - the idea for the optimization is to prevent the clone in new Response because they passed in buffer is “fresh” and not anymore accessible by any other JS? This would be much more difficult, maybe impossible at our current integration level with V8 |
08:52 | <Andreu Botella> | Would it be that difficult? You could use the BackingStore as the "owned buffer". |
08:53 | <Andreu Botella> | The issue that we've been thinking about a lot for Deno is that there are a number of async APIs that take buffers, and those can lead to data races |
15:40 | <shu> | yes, that would be difficult for an API to tell its argument is in fact not aliased without some pretty deep VM support exposed at the API boundary, if there's no language equivalent to, like, rvalue references |
15:41 | <shu> | dynamic rvalue references also does not seem like a thing that anyone would want to implement |
15:41 | <shu> | well, i guess it doesn't have to be dynamic |
15:42 | <littledan> | can we just transition to refcounting? that makes alias detection easier. |
15:42 | <shu> | i hear refcounting is also faster |
15:42 | <littledan> | totally |
15:59 | <bakkot> | shu: hard to do precisely, but what about a conservative thing where it can say "not aliased" or "maybe aliased" and be precise enough for new Response(body.transfer()) to be analyzed as "not aliased" |
16:14 | <littledan> | this is the kind of thing Swift uses to omit the refcounting operations. It just composes so beautifully. |
16:15 | <littledan> | Stage 1? 🥺 |
16:17 | <shu> | bakkot: where do we track this bit, for every single argument? |
16:26 | <bakkot> | just for array buffers |
16:27 | <shu> | the "may aliased" analysis is a static one, no? |
16:28 | <shu> | like, once things are compiled to bytecode, how do i know if a virtual register store (in a register VM) or a stack push (in a stack VM) is an actual binding assignment or a temporary? |
16:31 | <bakkot> | the register store / stack push isn't aliasing in itself, presumably - it only becomes aliased once you read it off the stack (or dup etc). but I suppose updating all those ops so that they'd know to set the bit is expensive |
16:43 | <shu> | indeed, my point is i suspect to get that bit for ArrayBuffers you need to implement that bit for all data flow |
16:43 | <shu> | which is a tall ask |
17:23 | <bakkot> | yeah fair enough |
17:33 | <tolmasky> | Another nitpick about the spec that makes certain tooling difficult: sometimes camel case intrinsics are simply lowercased for their sections (such as sec-asycngeneratorfunction), but other times they are dash cased (such as sec-async-function) |
17:34 | <Mathieu Hofman> | I'm still of the opinion that Copy-on-Write is the least intrusive change for developers, and would provide performance improvements for existing programs that use array buffers. The current paradigm is that if you want to make sure the receiver of an arraybuffer doesn't change the data, or if as the receiver you want to use the data asynchronously insulating yourself from later changes by the caller, you simply do I can understand such a level of indirection is a complication for implementations, and the write guards would make optimizations more difficult, but I am wondering if we're weighting the potential improvements for users correctly related to the implementation's concerns. |
17:59 | <shu> | Mathieu Hofman: my characterization is "security risk" and not just "complication" |
17:59 | <shu> | like, a direct security risk instead of security risk arising from more complexity |
17:59 | <shu> | and in that light, i stand by that increasing likelihood of browser users not being exploited is the correct tradeoff |
18:00 | <shu> | V8 has CoW optimization for regular arrays, for instance, which aren't used as an attack vector as much |
18:01 | <shu> | i would also contend transfer of ownership is not at all a foreign concept in JS, because detaching obviously exists? |
18:01 | <Mathieu Hofman> | I don't understand what is fundamentally a security risk about copy on write? I understand it's possible for optimizations to be buggy and miss the update in backing memory, but that stems from an implementation complexity |
18:02 | <Mathieu Hofman> | detaching doesn't exist in JS itself, it only happens when integrating with host APIs |
18:02 | <shu> | Mathieu Hofman: it is as i described here: https://github.com/tc39/proposal-arraybuffer-transfer#if-performance-is-the-goal-why-add-new-methods-instead-of-implementing-copy-on-write-cow-as-a-transparent-optimization |
18:05 | <Mathieu Hofman> | I read that, and understood the it as an implementation choice, which is impacting the usability by end users. |
18:05 | <shu> | if your rebuttal is "bugs are an implementation problem", the response is that there are bugs we have seen crop up over and over to an extent that we consider other mitigations necessary than just "be really correct" |
18:05 | <shu> | because, you know, engineers aren't perfect |
18:06 | <shu> | i can't help but feel that reducing that motivation to "implementation choices should never impact usability of end users" is naive |
18:07 | <Mathieu Hofman> | I think I was just asking if there was a way to estimate the performance improvement CoW would bring to existing programs, so that it can be weighted against the implementation complexity it'd require |
18:07 | <shu> | i don't know how one would estimate that except by doing the implementation |
18:07 | <Mathieu Hofman> | I'm not asking to strictly put the need of end users above the needs of implementation |
18:10 | <shu> | should the risk calculus change in the future (like, we figure out another mitigation that is even better and less constraining of APIs), having a transfer method certainly doesn't preclude CoW optimizations |
18:11 | <Mathieu Hofman> | fair |
18:12 | <shu> | for this point, i contend it doesn't serve us well to not consider hosts in language design, and further, transferables are a well established part of the ecosystem |
18:14 | <Mathieu Hofman> | but I don't see how transfer solves all the use cases. As the discussion goes above, as the receiver of an array buffer, how can I guarantee I have exclusive access to the buffer, besides myself calling .transfer() , which would be a breaking change for existing APIs, and hard to express would happen in the API contract (besides as mentioned, an explicit transfer option). As I said, transfer is not a concept core to the JS language |
18:16 | <Mathieu Hofman> | What other parts of the ecosystem besides structured cloning have a concept of transfer of ownership ? |
18:21 | <Mathieu Hofman> | I am wondering if we should also add a way to make the target buffer "read-only" when transferring, and a way to test whether the AB is read-only. |
18:24 | <Mathieu Hofman> | I believe Jack Works started exploring that in https://github.com/tc39/proposal-limited-arraybuffer |
18:46 | <jmdyck> | Another nitpick about the spec that makes certain tooling difficult: sometimes camel case intrinsics are simply lowercased for their sections (such as sec-asycngeneratorfunction), but other times they are dash cased (such as sec-async-function) id attribute of <emu-clause> elements. It's true that the mapping from section-title to section-id isn't entirely consistent, but how does that make tooling difficult? Can't the tool just get the title-to-id mapping (e.g. from a biblio file) and remember it? Also, your example is odd, because there's no section with id="sec-async-function" . |
18:47 | <tolmasky> | sec-async-function-.* |
18:49 | <tolmasky> | I go intrinsics table -> sec-${transform(intrinsic_name)-* to grab all the methods/etc., the actual selector is now sec-${intrinsic_name.toLowercase() or sec-get-${intrinsic_name.toLowerCase()} or sec-${dashcase(intrinsic_name)} or sec-get-${dashcase(intrinsic_name) |
18:50 | <tolmasky> | Unfortunately the biblio seems to be missing certain things, I am just trying to get every intrinsic (not just top level ones, so like, Array.whatever.whatever), along with its parameters, etc. in a structured format) |
18:50 | <tolmasky> | the above seems to grab everything |
18:51 | <jmdyck> | The table is only the "well known" intrinsics, not every intrinsic. |
18:52 | <tolmasky> | right, thats why I then climb the rest of the spec |
18:53 | <tolmasky> | not sure if that is what you mean, or just wanted me to correct my statement to say "I want to grab all the well known intrinsics", either way, sure |
18:55 | <jmdyck> | okay, so if you're crawling the rest of the spec anyway, you could pick up the id for each intrinsic in the process. I.e., you don't really need the well-known intrinsics table? |
18:59 | <tolmasky> | I climb the spec to find all the intrinsics, then i want to find their definitions, hence needing to create the id. so, if I want to find what the parameters of AsyncFunction, then I have to go to sec-asyncfunction-constructor or whatever, hence the need to generate these things. I already find them all, I was just commenting that I noticed this. It's not a super big deal and would just remove 2 lines. If there's opposition to standardizing around lowercasing or dashcasing its certainly not preventing me from doing anything |
18:59 | <shu> | I am wondering if we should also add a way to make the target buffer "read-only" when transferring, and a way to test whether the AB is read-only. |
19:00 | <shu> | i'm open to the idea of RO buffers |
19:00 | <shu> | What other parts of the ecosystem besides structured cloning have a concept of transfer of ownership ? |
19:04 | <jmdyck> | tolmasky: I don't understand how you can find all the intrinsics without finding their definitions. |
19:07 | <tolmasky> | I'm putting the code online so I can show you, one sec |
19:09 | <shu> | but I don't see how transfer solves all the use cases. As the discussion goes above, as the receiver of an array buffer, how can I guarantee I have exclusive access to the buffer, besides myself calling |
19:10 | <shu> | having a language level affordance like take doesn't really answer the question of the API design, like the fast-path slow-path thing |
19:10 | <shu> | do you have overloads for all existing buffersource-taking APIs? do all future APIs need to have both overloads? those still need to be answered |
19:12 | <shu> | CoW comes at it at a different angle, but is not ideal in its own way since you can still end up with a copy, and i think is an independent optimization that shouldn't be depended to motivate move semantics in either direction |
19:19 | <Justin Ridgewell> | Here's an updated gist that we discussed in module meeting: https://gist.github.com/jridgewell/fa9754e38299d1f067e8aa8ac9ae336e |
19:20 | <Justin Ridgewell> | The way that I think about all features we add is "how would I write this today", and this is how I would write module keyword today |
19:29 | <Justin Ridgewell> | If we were to switch to as ‘foo’ , it solves my main concern (how do we do runtime reflections using a single syntax, eg as ‘text’ or as ‘buffer’ |
19:32 | <Justin Ridgewell> | But we’re likely to see conflicts, eg next’s as ‘image’ would include the intrinsic width/height of the original image, and an asset url usable in an <img src> . I imagine other frameworks might want the aspect ratio, or another optional value. Maybe they just want a data url (AMP needed that) |
20:27 | <Mathieu Hofman> | what's the argument for making it the same API? Object.freeze which if replicated would allow anyone with access to the ArrayBuffer to make read-only in place, potentially violating the expectations of the sharer. However since this is also a concern I have for transfer as well, aka if just holding an arraybuffer should give you the authority to detach it, maybe that ship has sailed? |
20:28 | <Mathieu Hofman> | just structured clone afaik, but that's a pretty core part of the ecosystem |
20:33 | <Mathieu Hofman> | I think in general JS has a concept of access through possession of object references, but in my opinion, the language itself does not have a built-in notion of ownership, and by extension of transfer of such ownership. We may introduce some notion of ownership with resource management, but that'd be somewhat tied with the concept of scope. The semantics of that proposal make it unlikely for these managed resources to be shared further than the owner, and I don't think transfer of ownership will mix. |
20:34 | <tolmasky> | jmdyck: Here: https://runkit.com/tolmasky/extracting-intrinsic-objects-from-ecmascript-specification -- I am not aware of an easier way of doing this. I found it pretty difficult to navigate biblio successfully. (shouldn't really be necessary to make your way through this, but here is parseSignature if it matters: https://runkit-packages.com/14.x.x/1670358356466/@reified/ecma262/tools/update-intrinsics/parse-signature.js ) |
20:36 | <tolmasky> | Anyways, the only reason I asked about the consistency of the IDs is that it would allow me to avoid testing so many permutations (line 33). But again, it's not actually a big deal, I was moreso curious if there was a reason for it, and if not, maybe I would in my free time do a PR making them consistent |
20:37 | <Mathieu Hofman> | The same way programmers share object references and can spread it to capture the (shallow) state of that object, I see CoW as optimizing the same pattern for byte arrays, where you shared the data, and expressing that you're interested in capturing the state of that data. |
20:40 | <Mathieu Hofman> | Which is why I suggested readonly array buffers as an alternative, where you explicitly create an immutable copy, which can be relied upon. If Records and Tuples catch on, this would roughly be the equivalent for bytes (which raises the question whether those have any links) |
20:43 | <Mathieu Hofman> | We could probably argue that "read-only" array buffers may need to be non-transferable, so that they are really immutable, aka they will keep working. |
20:46 | <bakkot> | tolmasky: no reason for the inconsistency in IDs, just different authors writing different parts and not doing exactly the same things |
20:46 | <bakkot> | wouldn't say no to a PR making them consistent if you feel like doing it, though mind that we'd need oldids on everything to keep old links working |
20:46 | <tolmasky> | If I did a PR to make them consistent, would that be useful, or are we afraid of breaking links or something |
20:47 | <tolmasky> | kind of makes no difference to me since we want to run this on every iteration of the spec, so we'll need this code forever either way, but just curious |
20:50 | <bakkot> | if you put oldids links won't break, so no particular risk |
20:50 | <bakkot> | I don't know if anyone else has ever been affected about the inconsistency, though, so I don't know if it's necessarily useful per se |
22:30 | <shu> | Mathieu Hofman: as for web specificity, i point you to the other champions of the proposal coming from node |
22:30 | <shu> | structured clone may be have originated from the web, but it is a core part of the ecosystem |
22:31 | <shu> | CoW and RO TAs are orthogonal and i am supportive of exploring them independently |
22:31 | <shu> | "The semantics of that proposal make it unlikely for these managed resources to be shared further than the owner, and I don't think transfer of ownership will mix." <- i don't understand this |
22:32 | <shu> | oh "that proposal" is referring to resource management, i see |
22:32 | <shu> | i mean i also disagree? DisposableStacks have a move? |
22:33 | <shu> | i agree that there's no built-in notion of ownership in the formal sense, but there's certainly a sense of "i'm going to neuter the object" |
22:34 | <Mathieu Hofman> | I think the use case for move is mainly for moving a set of resource ownership from a constructor scope to the constructed object. I don't see it as aimed to move ownership to a 3rd party |
22:34 | <shu> | it's the same use case as the buffer one, no? |
22:34 | <shu> | i mean the buffer reader isn't an unknown 3p |
22:35 | <shu> | that is -- if the disagreement is with how widely applicable the notion of "ownership" is in the transfer proposal, i contend it is exactly as widely applicable as DisposableStack's |
22:36 | <shu> | like this isn't designed to enable native APIs to take ownership, that does require more work |
22:38 | <Mathieu Hofman> | I dunno about the reader API specifically, but having written a decent amount of code dealing with node streams, it never seemed clear to me what the expected semantics of passing array buffers around were. It seems that the receiver often assumes the producer will no longer mutate the buffer, but that is far from being enforced / checked or even explicit. |
22:39 | <Mathieu Hofman> | And I think maybe that's where read-only stream would at least make those semantics more explicit. |
22:40 | <shu> | i confess i don't know what the immediate disagreement is |
22:41 | <Mathieu Hofman> | Regarding structured cloned I guess we'll have to disagree how relevant it is to the ecosystem. One particular web API used in node for roughly the same use case (worker rpc) does not make it a strong precedent in my mind for a widely used pattern. |
22:42 | <shu> | well, what's your position on the proposal? the closest i can make out is perhaps that you think it is mutually exclusive with CoW optimizations and RO buffers |
22:45 | <Mathieu Hofman> | I think all I'm trying to say is that I don't believe transfer sufficiently solves all the use cases related to the expression of "ownership intent" for array buffers, and that defensive code does need more tools. Currently defensive code uses `slice` but if we can't optimize that, I'd like the transfer proposal to try and tackle more fully the ergonomics of passing array buffers around. |
22:45 | <shu> | i see |
22:45 | <shu> | thank you, noted, and i will ponder it. i would love some dynamic notion of ownership |
22:45 | <Mathieu Hofman> | I think I agree that CoW would be an orthogonal optimization. |
22:46 | <shu> | i think ownership is very important for shared memory work as well |
22:50 | <Domenic> | ReadableStream as implemented in the web/Node/Deno/etc. has transfer semantics |
22:51 | <littledan> | Yeah I am confused by this whole discussion |
22:57 | <Domenic> | Let me try to generalize my original "proposals" into a problem statement. There are a lot of BufferSource-accepting APIs that cannot deal with mutations after accepting the value. They have two choices today: clone or transfer. Transfer is a bit of a risky choice for API authors. If we could somehow make it the caller's choice which semantics are done, that would lead to a lot more efficiently. And, opting in to the efficient "don't copy" path should be automatic, or at least extremely easy to opt in to, for API authors. In particular, it should be something doable from any code generator like Web IDL, so that every BufferSource-accepting API can generate a prelude that handles this. |
23:00 | <littledan> | Oh yeah the Wasm JS API has some unfortunate cloning, for example. And I was always confused why it wasn’t transfer. |
23:00 | <Kris Kowal> | I for one am also on team zero-copy, by whatever means necessary. |
23:00 | <Mathieu Hofman> | So a more relevant example would be `WritableStreamDefaultWriter.write()`? It's not clear what would happen if you pass a chunk to that API and mutate the array buffer before the promise resolves. |
23:01 | <Domenic> | Here is a strawperson with lots of objectionable properties, but hopefully it communicates the idea. There is a new argument-passing syntax, f(take arg) . This sets a bit on arg and also puts arg into a TDZ-like state, so the caller can't use it. API authors can then do something like function f(arg) { if (!isBitSet(arg) { arg = arg.slice(); } ... } . |
23:02 | <Domenic> | So a more relevant example would be `WritableStreamDefaultWriter.write()`? It's not clear what would happen if you pass a chunk to that API and mutate the array buffer before the promise resolves. |
23:02 | <Kris Kowal> | This is also a description of linear types, fwiw. |
23:03 | <Mathieu Hofman> | Wouldn't that `take` only impact the binding? What if the array buffer had been assigned to another variable? |
23:03 | <Domenic> | Yes, that doesn't really work |
23:04 | <Domenic> | I guess you want take to do something more directly related to ArrayBuffer transferring |
23:05 | <Mathieu Hofman> | It seems what you want is transferring the buffer temporarily and transferring it back. Or somehow put it in an "exclusive use" mode |
23:05 | <Domenic> | Such that
which is weird, but whatever. |
23:05 | <Domenic> | I don't think transferring it back is necessary in a majority of cases. But it would be a fun bonus. |
23:05 | <Domenic> | Presumably this is a well-researched area in programming language design. |
23:05 | <Domenic> | So I feel pretty silly just spouting strawpeople around. |
23:06 | <Mathieu Hofman> | Is it for reference types though? I can see this kind of flow analysis with value types. |
23:06 | <littledan> | I think this is the ancient linear types vs copy on write dilemma. Rust and Swift take opposite defaults here. |
23:07 | <littledan> | I don’t know how this stuff can be added to languages later—that is the real research problem here |
23:07 | <Domenic> | It feels like if you scoped it to ArrayBuffers and made some compromises, it might be doable? |
23:07 | <Domenic> | (e.g., no transferring back) |
23:08 | <littledan> | ? I am willing to believe it but I can’t picture it yet |
23:08 | <Andreu Botella> | In the case of Deno, there are APIs like async Deno.writeFile(path, buffer) which don't currently clone or detach the buffer, and therefore have latent memory soundness or data race bugs |
23:08 | <Andreu Botella> | I don't think such APIs can be changed to detach the buffer without breakage |
23:08 | <littledan> | In Swift and Rust, this stuff is really core to how variables work in the first place |
23:09 | <littledan> | Swift is actually working on adding linear types now to help you avoid unintentional copying |
23:10 | <littledan> | But it is a huge change to the model of everything |
23:31 | <Kris Kowal> | (e.g., no transferring back) |
23:32 | <Kris Kowal> | It makes for programs that can be represented with wire diagrams, and made parallel trivially, which is nifty. But, would weird to bolt post-TDZ onto JavaScript in its old age. |
23:33 | <Kris Kowal> | But doing that kind of stuff with revokable object APIs as a matter of analogy to linear types seems like fair game to me. |
23:35 | <Kris Kowal> | But as littledan said and Mathieu Hofman is arguing, this is indeed the age old conversation about linear types versus CoW and both can be made to work. |
23:37 | <Kris Kowal> | I think I’m more sympathetic at the moment to the “whatever WebGL needs” is the fixed point in this design, regardless of my preference for CoW just in terms of developer ergonomics. |
23:37 | <Kris Kowal> | So…does WebGL require transfer and detach semantics for TypedArrays? |
23:39 | <Mathieu Hofman> | I think we should take the example of piping in user land from a readable stream to a writable stream, and make that work with a single reusable buffer using zero copies besides into and out of that buffer, while guaranteeing the userland cannot do the wrong thing (aka ask the reader to write in the buffer before the writer is done reading out of the buffer) |
23:40 | <Kris Kowal> | That would be outrageously cool. |
23:42 | <Mathieu Hofman> | What kind of "exclusive use" mode do we need to add to array buffer to make this possible? Then I'd still ask to compare that added complexity with the equivalent flow if the program could assume copy on write to similarly perform "zero copy" piping. |
23:42 | <Kris Kowal> | Concretely, how do we employ TypedArrays for such a thing? Wouldn’t that require Read/Write/Opaque views into encapsulated buffers? |
23:43 | <Mathieu Hofman> | I don't believe typed arrays help here if you can get at an underlying mutable array buffer |
23:43 | <Kris Kowal> | That’s my intuition. |
23:46 | <Kris Kowal> | So, your notion is to add a level of indirection between ArrayBuffer and its underlying storage, then guarding it with either RO/WO/O modes or CoW with a userspace write barrier. |
23:47 | <Kris Kowal> | Strike prior, implementations already have this detachment concept, so that level of indirection clearly exists. |
23:47 | <Kris Kowal> | I will now walk away slowly. |
23:50 | <Mathieu Hofman> | Right there is already a detachment guard, maybe we'd introduce a way to lock the ab into read-only mode, preventing any writes, then release it? |
23:51 | <Mathieu Hofman> | That'd allow the receiver to claim the lock and release it once done, guaranteeing no mutation can occur on the AB while being read. |
23:52 | <Mathieu Hofman> | I dunno, I haven't thought this through, just spinning ideas here |
23:53 | <Kris Kowal> | I’m imagining const ab2 = ab1.take() (per Domenic’s take aparatus) would be the key. That would permanently detach ab1 , leavingab2 for the receiver to read/write. |
23:53 | <Kris Kowal> | The trouble… |
23:53 | <Mathieu Hofman> | I don't think you need to detach or even duplicate the buffer |
23:54 | <Kris Kowal> | is that interferes with the sender if they went on to read or write ab1 . CoW would make that safe. |
23:56 | <Kris Kowal> | But I think I read coherent murmurings about CoW being a pain to implement because the old view retains the underlying buffer possibly a long time. |
23:56 | <Mathieu Hofman> | Correct, it's still not backwards compatible and requires the sender to respect these semantics. I don't think anything besides CoW would be non-breaking. At first I thought that we could have the sender put the buffer in RO mode, but there is no way to guarantee it wouldn't keep a copy of the capability to release the lock. |
23:58 | <Kris Kowal> | It’d be a shame if CoW effectively forced copies to be made behind the scenes depending on GC timing. |
23:59 | <Kris Kowal> | Then zero-alloc becomes a puppet show. Did I or did I not dispose of my reference to the buffer in a timely enough fashion? Is it even possible to guarantee? |