Topic: D1031R1 draft 1 LLFIO with proposed C++ object
Author: Niall Douglas <nialldouglas14@gmail.com>
Date: Wed, 1 Aug 2018 11:01:22 -0700 (PDT)
Raw View
------=_Part_17_462843701.1533146482841
Content-Type: multipart/alternative;
boundary="----=_Part_18_1402463751.1533146482843"
------=_Part_18_1402463751.1533146482843
Content-Type: text/plain; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable
So assuming people were put off by the length of the paper, below is the=20
section I'd really like feedback upon.
Niall
3 Impact on the Standard
Listed at the end of this section are the in-flight WG21 papers this=20
proposal is dependent upon, and which would need to enter the standard=20
before this library can be considered. However a bigger issue involves the=
=20
potential changes to the C++ object model, plus new contracts with which to=
=20
indicate the limited side effects of i/o functions in order to enable=20
improved optimisation.=20
3.1 Potential changes to the C++ object model
Firstly I wish to make it clear that there is the viable option of simply=
=20
making no changes at all to the object model, and any placement of objects=
=20
into mapped memory is declared to be undefined behaviour, as it is at=20
present5=20
<file:///C:/Users/ned/Documents/boostish/wg21/P1031_file_io6.html#fn5x0> .=
=20
However I also think that an opportunity missed. Via memory maps, the=20
proposed low level file i/o library herein enables object lifetime to=20
exceed that of the program. It also enables an object to appear in multiple=
=20
instances of concurrent C++ program execution, or indeed at multiple=20
addresses within a C++ program. And finally one can mark individual pages=
=20
of memory with different access permissions than read-only or read-write=20
(e.g. no access i.e. unreachable), or kick individual pages out to swap, or=
=20
throw away their contents. In other words, individual pages of memory can=
=20
become unreachable with the objects within that storage still being alive.=
=20
All four things are problematic for the current C++ standard=E2=80=99s obje=
ct=20
model, not least that objects cannot currently outlive the program; that=20
there is no awareness of it being possible for multiple concurrent C++=20
program instances to exist in the current standard; that objects not marked=
=20
with [[no_unique_address]] and which are not bits in a bitfield must=20
currently always have a single, unique address in the process; that all=20
alive objects are equally reachable from anywhere in the program; and=20
finally that objects are always available at their unique address, and are=
=20
not also elsewhere.=20
Furthermore, there are reordering constraints peculiar to mapped memory if=
=20
a modification to an object is to persist at all, or indeed in a form which=
=20
is usable. This is because in most implementations of C++, atomic=20
reordering constraints only affect apparent ordering to CPU cores within=20
the same symmetric multiprocessing cluster, and may have no effect on the a=
ctual=20
ordering of when modifications are sent to main memory and/or the storage=
=20
device. Thus, if a C++ program modifies an object, and then modifies it=20
again, some of the changes of the latter modification may land in main=20
memory before some of the changes of the earlier modification, irrespective=
=20
of any atomics or fences or anything else which a C++ program currently can=
=20
legally do. This has obvious implications for the integrity of that object=
=20
across a program restart.=20
I therefore suggest some changes to the C++ object model, listed below.=20
3.1.1 The relevant parts of the current standard
There are presently four kinds of storage duration. As [basic.stc]=20
describes it:=20
The storage duration is the property of an object that defines the minimum=
=20
potential lifetime of the storage containing the object. The storage=20
duration is determined by the construct used to create the object and is=20
one of the following:=20
1. static storage duration=20
2. thread storage duration=20
3. automatic storage duration=20
4. dynamic storage duration
[basic.life] describes in some detail the lifetime model, and it=20
essentially reduces down to one of these possible states for each object=20
(and apologies for the over-simplification for purposes of terse=20
exposition):=20
1. Unconstructed (from P0593: and considered unreachable by the compiler=
=20
during analysis), but storage is of the right size and alignment, and=20
pointers and glvalues to the storage of the object type are permitted to=
be=20
used under limited circumstances.=20
2. Unalive, in the process of being constructed, for types with=20
non-trivial constructors only.=20
3. Alive, fully constructed.=20
4. Unalive, in the process of being destructed, for types with=20
non-trivial destructors only.
You will note that due to their triviality, memory storing trivial types=20
such as std::byte can either be unconstructed, or fully constructed, and no=
=20
lifetime state exists apart from those two. It is important to understand=
=20
that currently speaking, using allocated memory in which nothing has been=
=20
constructed is undefined behaviour, so the common method of reinterpret=20
casting the return from malloc() to a pointer of the type you want is=20
presently undefined behaviour.=20
This is being dealt with in [P0593=20
<file:///C:/Users/ned/Documents/boostish/wg21/P1031_file_io.html#XP0593>] I=
mplicit=20
creation of objects for low-level object manipulation. Amongst its many=20
recommendations, it makes unconstructed memory unreachable by default. You=
=20
can make it reachable by (i) constructing objects into it (ii) via the=20
proposed std::bless() which tells the compiler that a region of unreachable=
=20
memory (i.e. unconstructed) is now reachable (and contains alive trivial=20
objects of some form). You can restore it to being unreachable by calling=
=20
the destructor of the objects, even if those objects are completely=20
trivial. P0593 doesn=E2=80=99t mention it explicitly (yet!), but given std:=
:launder=E2=80=99s=20
special casing for std::byte, it seems likely that calling the destructor=
=20
for std::byte on a region with unknown trivially destructible types of=20
objects in it will always mark it unreachable. And finally, P0593 names=20
various functions as always returning memory which is reachable i.e.=20
containing an array of alive objects of some unspecified trivial type.=20
This new concept of reachable vs unreachable memory is an important one,=20
and it is leaned upon heavily in the proposed extensions.=20
3.1.2 A new storage duration: mapped
As always, naming is very hard, but mapped storage duration seems a=20
reasonable name for memory which can be shared between concurrently running=
=20
processes, or between multiple start-stop cycles of the same program (note=
=20
that a program using an object from mapped storage which it itself did not=
=20
store there is undefined behaviour).=20
The storage for objects with mapped storage duration shall last for the=20
duration of the filesystem entity (see TS definitions below for precise=20
meaning) referred to by the section_handle instance which represents the=20
storage. section_handle instances may refer to filesystem entities whose=20
lifetimes automatically end when the program ends (=E2=80=98anonymous inode=
s=E2=80=99), or=20
which may persist for an indeterminate period, including across subsequent=
=20
executions of the C++ program.=20
Mapped storage has the most similarity to dynamic storage, but with the=20
following differences:=20
- It has allocation and alignment granularities with architecture=20
specific coarse sizes (=E2=80=98memory page=E2=80=99). 4Kb/2Mb/1Gb page =
sizes are common.=20
- New allocations are guaranteed to be all bits zero on creation.=20
- It has map on first read semantics. This means that the first read=20
from a memory page can take hundreds of CPU cycles, and a TLB shootdown=
=20
causes an interrupt for other CPUs.=20
- It has allocate on first write semantics. This means that the first=20
write to a memory page can take thousands or even hundreds of thousands =
of=20
CPU cycles, plus a TLB shootdown.=20
- Usually, but not always, mapped storage is a memory cache of=20
equivalent storage a high latency storage device. Hence they can be=20
individually pushed to storage, their contents thrown away, deallocated,=
=20
given different access permission or caching strategies, and lots of oth=
er=20
interesting (i.e. potentially game changing for large STL containers)=20
operations. Individual pages can be:=20
- Read-only, read-write, or copy-on-write. These are self describing,=
=20
and these are hard characteristics: violating them means program fail=
ure.=20
- Committed or uncommitted. This indicates whether the page counts=20
towards the resources used by the C++ program. Uncommitted memory is=
=20
inaccessible, and acts as a placeholder for later use (i.e. it is res=
erved=20
address space, useful for expanding large arrays without content copy=
ing).=20
- Dirty or clean. This indicates whether the page contains data not=
=20
yet mirrored onto its backing storage.=20
- Allocated or unallocated. This indicates whether storage backing=20
the page has been allocated on the storage device. If not, the first =
write=20
to a clean page may be very expensive as the page may need to be copi=
ed by=20
the kernel and/or space allocated for it on the backing storage devic=
e.
=20
The storage represented by a section_handle instance can be mapped into=20
(i.e. made available to) a C++ program by creating a map_handle sourcing=20
the storage from a section_handle instance. The storage mapped by the low=
=20
level map_handle shall represent unconstructed and unreachable memory, and=
=20
will require the use of std::bless() or map_view to make it reachable=20
(alternatively, use the convenience class mapped on a section_handle instan=
ce=20
which bundles the aforementioned low level operations on your behalf).=20
Destroying a map_view does not make the storage unreachable. Decommitting=
=20
individual pages does, as does destroying a mapped or map_handle.=20
3.1.3 A new lifetime stage: unreachable
I propose adding a new separate lifetime status for objects: unreachable.=
=20
Under P0593, unconstructed objects are unreachable, but there is no=20
possibility for a partially constructed, alive or partially destructed=20
object to be unreachable. I propose that this new status ought to be added=
=20
such that objects can now have the following lifetime states:=20
1. Unconstructed, always unreachable.=20
2. Unalive, in the process of being constructed, for types with=20
non-trivial constructors only. Always reachable.=20
3. Alive, fully constructed, reachable. Has a single, unique address in=
=20
memory (unless marked with [[no_unique_address]]).=20
4. Alive, fully constructed, unreachable. Does NOT have a single, unique=
=20
address in memory (it may have many, or none). May change whilst=20
unreachable (i.e. reload it after marking it reachable at some address).=
=20
5. Unalive, in the process of being destructed, for types with=20
non-trivial destructors only. Always reachable.
P0593 proposed these functions to mark regions as reachable:=20
// Requires: [start, (char*)start + length) denotes a region of allocated=
=20
// storage that is a subset of the region of storage reachable through=20
start.=20
// Effects: implicitly creates objects within the denoted region.=20
void std::bless(void *start, size_t length);=20
=20
// Effects: create an object of implicit lifetype type T in the storage=20
// pointed to by T, while preserving the object representation.=20
template<typename T> T *std::bless(void *p);
Thus the obvious corrollary for marking regions as unreachable:=20
// Requires: [start, (char*)start + length) denotes a region of allocated=
=20
// storage that is a subset of the region of storage reachable through=20
start.=20
// Effects: implicitly uncreates objects within the denoted region.=20
void std::unbless(void *start, size_t length);=20
=20
// Effects: uncreate an object of implicit lifetype type T in the storage=
=20
// pointed to by T, while preserving the object representation.=20
template<typename T> void *std::unbless(T *p);=20
A gain of this new unreachable lifetime status is that it neatly solves the=
=20
problem of objects appearing at multiple addresses in a running program. We=
=20
can now say that only one of those addresses can be reachable for an alive=
=20
object at a time. If a program wishes to change an object=E2=80=99s current=
=20
reachable address, they unbless the old location, and bless the new=20
location.=20
It should be emphasised that similarly to blessing, unblessing of trivial=
=20
types causes no code emission. It simply tells the compiler what is now=20
unreachable.=20
Finally, it may seem that unreachability is a bit of a big sledgehammer to=
=20
throw at this problem. However, there are a number of other problems=20
elsewhere in C++ where having unreachable but alive objects would be very=
=20
useful =E2=80=93 thread local storage on a million CPU core compute resourc=
e is an=20
excellent example6=20
<file:///C:/Users/ned/Documents/boostish/wg21/P1031_file_io7.html#fn6x0> .=
=20
That said, this is a big change to lifetime, and if strong arguments are=20
made against it then I am happy to propose something more conservative.=20
3.1.4 Blessing and unblessing polymorphic objects
Currently P0593 makes no mention of polymorphic objects i.e. ones with=20
vptrs in many implementations. I can see lots of reasons why it would be=20
useful to bless and unbless polymorphic objects i.e. update the vptr to the=
=20
correct one for the currently running program, or clear the vptr to the=20
system null pointer.=20
Imagine, for example, a polymorphic object with mapped storage duration=20
whose constructing program instance terminates. Upon restart of the=20
program, the storage is mapped back into the program, but now the=20
polymorphic object has the wrong vptr! So we need to write the correct vptr=
=20
for that object, after which all works as expected7=20
<file:///C:/Users/ned/Documents/boostish/wg21/P1031_file_io8.html#fn7x0> .=
=20
This also applies to shared mapped memory. Extending the C++ object model=
=20
to handle memory being modifiable by concurrently running C++ programs=20
would be very hard, so I propose that we don=E2=80=99t do that. Rather we s=
ay that=20
objects in mapped storage can only be reachable in exactly one or none=20
running C++ programs at a time i.e. at exactly one or no address at any one=
=20
time, otherwise it is undefined behaviour. Therefore, if process A wants to=
=20
make available an object to process B, it unblesses it and tells process B=
=20
that the object is now available for use. Process B blesses the object into=
=20
reachable, and uses it. When done, it unblesses it once more. Now process A=
=20
can bless it and use it again, if so desired.=20
Implied in this is that blessing and unblessing becomes able to rewrite the=
=20
vptr (or whatever the C++ implementation uses to implement polymorphism).=
=20
To be truthful, I am torn on this. On the one hand, if you wish to be able=
=20
to bless and unbless polymorphic objects, rewriting the vptr is=20
unavoidable, and the frequency of relocating polymorphic objects in current=
=20
C++ is low. On the other hand, future C++=E2=80=99s may do a lot more remap=
ping of=20
memory during large array expansion in order to skip doing a memory copy,=
=20
and in this circumstance every single polymorphic object in a few million=
=20
item array would need their vptr=E2=80=99s needlessly rewritten, which is=
=20
unnecessary work.=20
I=E2=80=99m going to err on the side of caution, and propose that the polym=
orphic=20
editions of bless and unbless shall be:=20
// Effects: create an object of implicit lifetype type T in the storage=20
// pointed to by T, while preserving the object representation=20
// apart from any necessary changes to make any polymorphic=20
// objects within and including T ready for use.=20
template<typename T> T *std::revive(void *p);=20
=20
// Effects: uncreate an object of implicit lifetype type T in the storage=
=20
// pointed to by T, while preserving the object representation=20
// apart from any necessary changes to make any polymorphic=20
// functions within the objects within and including T unusable.=
=20
template<typename T> void *std::stun(T *p);
Note that stun() and revive() not just change the vptrs of the type you=20
pass them, but also any vptrs of any nested types within that type. They=20
also perform blessing and unblessing, so you do not have to do that=20
separately. Calling these functions on non-polymorphic types is permitted.=
=20
3.2 New attributes [[no_side_effects]] and [[no_visible_side_effects]], and=
=20
new contract syntax for specifying lack of side effects
It is highly important for the future efficiency of any iostreams v2 that=
=20
the low level i/o functions do not cause the compiler to dump and reload=20
all state for every i/o, as they must do at present. The i/o functions=20
proposed do not modify their handle on most platforms, and on those=20
platforms we can guarantee to the compiler that there are either no side=20
effects or no visible side effects except for those objects modified by=20
parameters.=20
Reusing bless() and unbless(), I propose the following contracts syntax for=
=20
the io_handle functions so we can tell the compiler what side effects the=
=20
i/o functions have:=20
constexpr! void ensure_blessed(buffers_type buffers)=20
{=20
for(auto &buffer : buffers)=20
{=20
bless(buffer.data(), buffer.size());=20
}=20
}=20
=20
// This function has no side effects visible to the caller except=20
// for the objects it creates in the scatter buffer list.=20
virtual buffers_type io_handle::read(io_request<buffers_type> reqs,=20
deadline d =3D deadline()) throws(
file_io_error)=20
[[no_side_effects]]=20
[[ensures: ensure_blessed(reqs.buffers)]]=20
[[ensures: ensure_blessed(return)]];
The key thing to note here is if the caller never accesses any of the=20
scatter buffers returned by the function, the read can be optimised out=20
entirely due to the [[no_side_effects]] attribute. Obviously the compiler=
=20
also does not dump and reload any state around this scatter read apart from=
=20
buffers supplied and returned, despite it calling a kernel system call,=20
because we are giving a hard guarantee to the compiler that it is safe to=
=20
assume no side effects apart from modifying the scatter buffers.=20
Gather write is less exciting, but straightforward:=20
virtual const_buffers_type io_handle::write(io_request<const_buffers_type>=
=20
reqs,=20
deadline d =3D deadline())=20
throws(file_io_error) [[no_visible_side_effects
]];=20
Here we are telling the compiler that there are side effects, just not ones=
=20
visible to the caller. Hence the compiler cannot optimise out calling this=
=20
function, but it can skip dumping and reloading state despite the kernel=20
system call made by the function.=20
3.2.1 I/O write reordering barriers
C and C++ allow the programmer to constrain memory read and write=20
operations, specifically to constrain the extent of reordering that the=20
compiler and CPU are permitted to do. One can use atomics with a memory=20
order specified, or one can call a fence function which applies a memory=20
reordering constraint for the current thread of execution either at just=20
the compiler level, or also at the CPU level whereby the CPU is told what=
=20
ordering its symmetric multiprocessing cores needs to manifest in visible=
=20
side effects.=20
File i/o is no different: concurrent users of the same file or directory or=
=20
file system will experience races unless they take special measures to=20
coordinate amongst themselves.=20
Given that I have proposed above that C++ standardises mapped storage=20
duration into the language itself, it might seem self evident that one=20
would also need to propose a standardised mechanism for indicating=20
reordering constraints on i/o, which are separate to those for threads. My=
=20
current advice is that we should not do this =E2=80=93 yet.=20
The first reason why is that persistent memory is just around the corner,=
=20
and I think it would be unwise to standardise without the industry gaining=
=20
plenty of empirical experience first. So kick that decision down a few=20
standard releases.=20
Secondly, the proposed library herein does expose whatever acquire-release=
=20
semantics is implemented by the host operating system for file i/o, plus=20
the advisory locking infrastructure implemented by the host, plus the=20
ability to enable write-through caching semantics. It also provides=20
barrier(), though one should not write code which relies upon it working,=
=20
as it frequently does not.=20
Note that if the file is opened in non-volatile RAM storage mode, barrier()=
calls=20
the appropriate architecture-specific assembler instructions to correctly=
=20
barrier writes to main memory, so in this sense there is library, if not=20
language, support for persistent memory. The obvious sticker is that barrie=
r()=20
is a virtual function with significant preamble and epilogue, so for=20
flushing a single cache line it is extremely inefficient relative to direct=
=20
language support for this.=20
Nevertheless, one can write standards conforming code with the proposed=20
library which performs better on persistent memory than on traditional=20
storage, and my advice is that this is good enough for the next few years.=
=20
--=20
You received this message because you are subscribed to the Google Groups "=
ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an e=
mail to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp=
..org/d/msgid/std-proposals/b214ce98-2aaa-4f7f-a04a-77687422111a%40isocpp.or=
g.
------=_Part_18_1402463751.1533146482843
Content-Type: text/html; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable
<div dir=3D"ltr">So assuming people were put off by the length of the paper=
, below is the section I'd really like feedback upon.<div><br></div><di=
v>Niall</div><div><br></div><div><br></div><div><h3 class=3D"sectionHead"><=
span class=3D"titlemark">3 </span><a id=3D"x1-150003"></a>Impact on the S=
tandard</h3><!--l. 640--><p class=3D"noindent">Listed at the end of this se=
ction are the in-flight WG21 papers this proposal is dependent upon, and wh=
ich would need to enter the standard before this library can be considered.=
However a bigger issue involves the potential changes to the C++ object mo=
del, plus new contracts with which to indicate the limited side effects of =
i/o functions in order to enable improved optimisation. <!--l. 643--></p><p=
class=3D"noindent"><br></p><p class=3D"noindent"></p><h4 class=3D"subsecti=
onHead"><span class=3D"titlemark">3.1 </span><a id=3D"x1-160003.1"></a>Po=
tential changes to the C++ object model</h4><!--l. 645--><p class=3D"noinde=
nt">Firstly I wish to make it clear that there is the viable option of simp=
ly making no changes at all to the object model, and any placement of objec=
ts into mapped memory is declared to be undefined behaviour, as it is at pr=
esent<span class=3D"footnote-mark"><a href=3D"file:///C:/Users/ned/Document=
s/boostish/wg21/P1031_file_io6.html#fn5x0"><sup class=3D"textsuperscript">5=
</sup></a></span><a id=3D"x1-16001f5"></a> . <!--l. 647--></p><p class=3D"n=
oindent"><br></p><p class=3D"noindent">However I also think that an opportu=
nity missed. Via memory maps, the proposed low level file i/o library herei=
n enables object lifetime to exceed that of the program. It also enables an=
object to appear in multiple instances of concurrent C++ program execution=
, or indeed at multiple addresses within a C++ program. And finally one can=
mark individual pages of memory with different access permissions than rea=
d-only or read-write (e.g. no access i.e. unreachable), or kick individual =
pages out to swap, or throw away their contents. In other words, individual=
pages of memory can become <span class=3D"ecti-1095">unreachable </span>wi=
th the objects within that storage still being <span class=3D"ecti-1095">al=
ive</span>. <!--l. 649--></p><p class=3D"noindent"><br></p><p class=3D"noin=
dent">All four things are problematic for the current C++ standard=E2=80=99=
s object model, not least that objects cannot currently outlive the program=
; that there is no awareness of it being possible for multiple concurrent C=
++ program instances to exist in the current standard; that objects not mar=
ked with <span class=3D"obeylines-h">[[no_unique_address]]</span> and which=
are not bits in a bitfield must currently always have a single, =
=
=
unique address in the process; that all alive objects are=
equally reachable from anywhere in the program; and finally that objects a=
re always available at their unique address, and are not also elsewhere. <!=
--l. 651--></p><p class=3D"noindent"><br></p><p class=3D"noindent">Furtherm=
ore, there are reordering constraints peculiar to mapped memory if a modifi=
cation to an object is to persist at all, or indeed in a form which is usab=
le. This is because in most implementations of C++, atomic reordering const=
raints only affect <span class=3D"ecti-1095">apparent </span>ordering to CP=
U cores within the same symmetric multiprocessing cluster, and may have no =
effect on the <span class=3D"ecti-1095">actual </span>ordering of when modi=
fications are sent to main memory and/or the storage device. Thus, if a C++=
program modifies an object, and then modifies it again, some of the change=
s of the latter modification may land in main memory before some of the cha=
nges of the earlier modification, irrespective of any atomics or fences or =
anything else which a C++ program currently can legally do. This has obviou=
s implications for the integrity of that object across a program restart. <=
!--l. 653--></p><p class=3D"noindent"><br></p><p class=3D"noindent">I there=
fore suggest some changes to the C++ object model, listed below. </p><p cla=
ss=3D"noindent"><br></p><h5 class=3D"subsubsectionHead"><span class=3D"titl=
emark">3.1.1 </span><a id=3D"x1-170003.1.1"></a>The relevant parts of the=
current standard</h5><!--l. 657--><p class=3D"noindent">There are presentl=
y four kinds of storage duration. As <span class=3D"obeylines-h">[basic.stc=
]</span> describes it: </p><p class=3D"noindent"><br></p><div class=3D=
"quote"><!--l. 667--><p class=3D"noindent"><span class=3D"ecti-1095">The st=
orage duration is the property of an object that defines the minimum potent=
ial lifetime of</span> <span class=3D"ecti-1095">the storage containin=
g the object. The storage duration is determined by the construct used to</=
span> <span class=3D"ecti-1095">create the object and is one of the fo=
llowing:</span> </p><ol class=3D"enumerate1"><li class=3D"enumerate" i=
d=3D"x1-17002x1"><span class=3D"ecti-1095">static storage duration</span> =
</li><li class=3D"enumerate" id=3D"x1-17004x2"><span class=3D"ecti-1095=
">thread storage duration</span> </li><li class=3D"enumerate" id=3D"x1=
-17006x3"><span class=3D"ecti-1095">automatic storage duration</span> =
</li><li class=3D"enumerate" id=3D"x1-17008x4"><span class=3D"ecti-1095">dy=
namic storage duration</span></li></ol></div><!--l. 670--><p class=3D"noind=
ent"><span class=3D"obeylines-h">[basic.life]</span> describes in some deta=
il the lifetime model, and it essentially reduces down to one of these poss=
ible states for each object (and apologies for the over-simplification for =
purposes of terse exposition): <!--l. 672--></p><p class=3D"noindent"></p><=
ol class=3D"enumerate1"><li class=3D"enumerate" id=3D"x1-17010x1">Unconstru=
cted <span class=3D"ecti-1095">(from P0593: and considered unreachable by t=
he compiler during analysis)</span>, but =
=
st=
orage is of the right size and alignment, and pointers and glvalues to the =
storage of the object type are permitted to be used under limited circu=
mstances. </li><li class=3D"enumerate" id=3D"x1-17012x2">Unalive, in t=
he process of being constructed, for types with non-trivial constructors on=
ly. </li><li class=3D"enumerate" id=3D"x1-17014x3">Alive, fully constr=
ucted. </li><li class=3D"enumerate" id=3D"x1-17016x4">Unalive, in the =
process of being destructed, for types with non-trivial destructors only.</=
li></ol><!--l. 679--><p class=3D"noindent">You will note that due to their =
triviality, memory storing trivial types such as <span class=3D"fvmr8t-x-x-=
93">std::byte </span>can either be unconstructed, or fully constructed, and=
no lifetime state exists apart from those two. It is important to understa=
nd that currently speaking, using allocated memory in which nothing has bee=
n constructed is undefined behaviour, so the common method of reinterpret c=
asting the return from <span class=3D"fvmr8t-x-x-93">malloc() </span>to a p=
ointer of the type you want is presently undefined behaviour. <!--l. 681-->=
</p><p class=3D"noindent"><br></p><p class=3D"noindent">This is being dealt=
with in <span class=3D"cite">[<a href=3D"file:///C:/Users/ned/Documents/bo=
ostish/wg21/P1031_file_io.html#XP0593">P0593</a>]</span> <span class=3D"ect=
i-1095">Implicit creation of objects for low-level object manipulation</spa=
n>. Amongst its many recommendations, it makes unconstructed memory <span c=
lass=3D"ecti-1095">unreachable </span>by default. You can make it reachable=
by (i) constructing objects into it (ii) via the proposed <span class=3D"f=
vmr8t-x-x-93">std::bless() </span>which tells the compiler that a region of=
unreachable memory (i.e. unconstructed) is now reachable (and contains ali=
ve trivial objects of some form). You can restore it to being unreachable b=
y calling the destructor of the objects, even if those objects are complete=
ly trivial. P0593 doesn=E2=80=99t mention it explicitly (yet!), but given <=
span class=3D"fvmr8t-x-x-93">std::launder</span>=E2=80=99s special casing f=
or <span class=3D"fvmr8t-x-x-93">std::byte</span>, it seems likely that cal=
ling the destructor for <span class=3D"fvmr8t-x-x-93">std::byte </span>on a=
region with unknown trivially destructible types of objects in it will alw=
ays mark it unreachable. And finally, P0593 names various functions as alwa=
ys returning memory which is reachable i.e. containing an array of alive ob=
jects of some unspecified trivial type. <!--l. 683--></p><p class=3D"noinde=
nt"><br></p><p class=3D"noindent">This new concept of reachable vs unreacha=
ble memory is an important one, and it is leaned upon heavily in the propos=
ed extensions. <!--l. 685--></p><p class=3D"noindent"><br></p><p class=3D"n=
oindent"></p><h5 class=3D"subsubsectionHead"><span class=3D"titlemark">3.1.=
2 </span><a id=3D"x1-180003.1.2"></a>A new storage duration: <span class=
=3D"ecti-1095">mapped</span></h5><!--l. 687--><p class=3D"noindent">As alwa=
ys, naming is very hard, but <span class=3D"ecti-1095">mapped storage durat=
ion </span>seems a reasonable name for memory which can be shared between c=
oncurrently running processes, or between multiple start-stop cycles of the=
same program (note that a program using an object from mapped storage whic=
h it itself did not store there is undefined behaviour). <!--l. 689--></p><=
p class=3D"noindent"><br></p><p class=3D"noindent">The storage for objects =
with mapped storage duration shall last for the duration of the filesystem =
entity (see TS definitions below for precise meaning) referred to by the <s=
pan class=3D"fvmr8t-x-x-93">section_handle </span>instance which represents=
the storage. <span class=3D"fvmr8t-x-x-93">section_handle </span>instances=
may refer to filesystem entities whose lifetimes automatically end when th=
e program ends (=E2=80=98anonymous inodes=E2=80=99), or which may persist f=
or an indeterminate period, including across subsequent executions of the C=
++ program. =
=
<!--l. 691--></p><p class=3D"noinde=
nt"><br></p><p class=3D"noindent">Mapped storage has the most similarity to=
dynamic storage, but with the following differences: </p><ul class=3D=
"itemize1"><li class=3D"itemize">It has allocation and alignment granularit=
ies with architecture specific coarse sizes (=E2=80=98memory page=E2=80=
=99). 4Kb/2Mb/1Gb page sizes are common. </li><li class=3D"itemize">Ne=
w allocations are guaranteed to be all bits zero on creation. </li><li=
class=3D"itemize">It has map on first read semantics. This means that the =
first read from a memory page can take hundreds of CPU cycles, and a TL=
B shootdown causes an interrupt for other CPUs. </li><li class=3D"item=
ize">It has allocate on first write semantics. This means that the first wr=
ite to a memory page can take thousands or even hundreds of thousands o=
f CPU cycles, plus a TLB shootdown. </li><li class=3D"itemize">Usually=
, but not always, mapped storage is a memory cache of equivalent storage a =
high latency storage device. Hence they can be individually pushed to s=
torage, their contents thrown away, deallocated, given different access=
permission or caching strategies, and lots of other interesting (i.e. =
potentially game changing for large STL containers) operations. Individual =
pages can be: <ul class=3D"itemize2"><li class=3D"itemize">Re=
ad-only, read-write, or copy-on-write. These are self describing, and these=
are hard characteristics: violating them means program failure. =
</li><li class=3D"itemize">Committed or uncommitted. This indi=
cates whether the page counts towards the resources used by =
the C++ program. Uncommitted memory is inaccessible, and acts as a=
placeholder for later use (i.e. it is reserved address space, useful for e=
xpanding large arrays without content copying). </li><li=
class=3D"itemize">Dirty or clean. This indicates whether the page contains=
data not yet mirrored onto its backing storage. </li><l=
i class=3D"itemize">Allocated or unallocated. This indicates whether storag=
e backing the page has been allocated on the storage device. If no=
t, the first write to a clean page may be very expensive as the pa=
ge may need to be copied by the kernel and/or space allocated for it =
on the backing storage device.</li></ul></li></ul><!--l. 707--><p class=
=3D"noindent">The storage represented by a <span class=3D"fvmr8t-x-x-93">se=
ction_handle </span>instance can be <span class=3D"ecti-1095">mapped </span=
>into (i.e. made available to) a C++ program by creating a <span class=3D"f=
vmr8t-x-x-93">map_handle </span>sourcing the storage from a <span class=3D"=
fvmr8t-x-x-93">section_handle </span>instance. The storage mapped by the lo=
w level <span class=3D"fvmr8t-x-x-93">map_handle </span>shall represent unc=
onstructed and unreachable memory, and will =
=
req=
uire the use of <span class=3D"fvmr8t-x-x-93">std::bless() </span>or <span =
class=3D"fvmr8t-x-x-93">map_view<t> </t></span>to make it reachable (altern=
atively, use the convenience class <span class=3D"fvmr8t-x-x-93">mapped<t> =
</t></span>on a <span class=3D"fvmr8t-x-x-93">section_handle </span>instanc=
e which bundles the aforementioned low level operations on your behalf). <!=
--l. 709--></p><p class=3D"noindent"><br></p><p class=3D"noindent">Destroyi=
ng a <span class=3D"fvmr8t-x-x-93">map_view<t> </t></span>does not make the=
storage unreachable. Decommitting individual pages does, as does destroyin=
g a <span class=3D"fvmr8t-x-x-93">mapped<t> </t></span>or <span class=3D"fv=
mr8t-x-x-93">map_handle</span>. <!--l. 711--></p><p class=3D"noindent"><br>=
</p><p class=3D"noindent"></p><h5 class=3D"subsubsectionHead"><span class=
=3D"titlemark">3.1.3 </span><a id=3D"x1-190003.1.3"></a>A new lifetime st=
age: <span class=3D"ecti-1095">unreachable</span></h5><!--l. 713--><p class=
=3D"noindent">I propose adding a new separate lifetime status for objects: =
<span class=3D"ecti-1095">unreachable</span>. Under P0593, unconstructed ob=
jects are unreachable, but there is no possibility for a partially construc=
ted, alive or partially destructed object to be unreachable. I propose that=
this new status ought to be added such that objects can now have the follo=
wing lifetime states: <!--l. 715--></p><p class=3D"noindent"></p><ol class=
=3D"enumerate1"><li class=3D"enumerate" id=3D"x1-19002x1">Unconstructed, al=
ways unreachable. </li><li class=3D"enumerate" id=3D"x1-19004x2">Unali=
ve, in the process of being constructed, for types with non-trivial constru=
ctors only. Always reachable. </li><li class=3D"enumerate" id=3D"x=
1-19006x3">Alive, fully constructed, reachable. Has a single, unique addres=
s in memory (unless marked with <span class=3D"obeylines-h">[[no_unique=
_address]]</span>). </li><li class=3D"enumerate" id=3D"x1-19008x4">Ali=
ve, fully constructed, unreachable. Does NOT have a single, unique address =
in memory (it may have many, or none). May change whilst unreachable (i=
..e. reload it after marking it reachable at some address). </li><l=
i class=3D"enumerate" id=3D"x1-19010x5">Unalive, in the process of being de=
structed, for types with non-trivial destructors only. Always reachable=
..</li></ol><!--l. 723--><p class=3D"noindent">P0593 proposed these function=
s to mark regions as reachable: <!--l. 725--></p><p class=3D"noindent"><br>=
</p><div class=3D"lstlisting" id=3D"listing-10"><div class=3D"prettyprint" =
style=3D"background-color: rgb(250, 250, 250); border-color: rgb(187, 187, =
187); border-style: solid; border-width: 1px; word-wrap: break-word;"><code=
class=3D"prettyprint"><div class=3D"subprettyprint"><span style=3D"color: =
#800;" class=3D"styled-by-prettify">// Requires: [start, (char*)start + len=
gth) denotes a region of allocated </span><span style=3D"color: #000;" clas=
s=3D"styled-by-prettify"><br></span><span style=3D"color: #800;" class=3D"s=
tyled-by-prettify">// storage that is a subset of the region of storage rea=
chable through start. </span><span style=3D"color: #000;" class=3D"styled-b=
y-prettify"><br></span><span style=3D"color: #800;" class=3D"styled-by-pret=
tify">// Effects: implicitly creates objects within the denoted region. </s=
pan><span style=3D"color: #000;" class=3D"styled-by-prettify"><br></span><s=
pan style=3D"color: #008;" class=3D"styled-by-prettify">void</span><span st=
yle=3D"color: #000;" class=3D"styled-by-prettify"> std</span><span style=3D=
"color: #660;" class=3D"styled-by-prettify">::</span><span style=3D"color: =
#000;" class=3D"styled-by-prettify">bless</span><span style=3D"color: #660;=
" class=3D"styled-by-prettify">(</span><span style=3D"color: #008;" class=
=3D"styled-by-prettify">void</span><span style=3D"color: #000;" class=3D"st=
yled-by-prettify"> </span><span style=3D"color: #660;" class=3D"styled-by-p=
rettify">*</span><span style=3D"color: #000;" class=3D"styled-by-prettify">=
start</span><span style=3D"color: #660;" class=3D"styled-by-prettify">,</sp=
an><span style=3D"color: #000;" class=3D"styled-by-prettify"> size_t length=
</span><span style=3D"color: #660;" class=3D"styled-by-prettify">);</span><=
span style=3D"color: #000;" class=3D"styled-by-prettify"> <br>=C2=A0<br></s=
pan><span style=3D"color: #800;" class=3D"styled-by-prettify">// Effects: c=
reate an object of implicit lifetype type T in the storage </span><span sty=
le=3D"color: #000;" class=3D"styled-by-prettify"><br></span><span style=3D"=
color: #800;" class=3D"styled-by-prettify">// =C2=A0 =C2=A0 =C2=A0 =C2=A0 =
=C2=A0pointed to by T, while preserving the object representation. </span><=
span style=3D"color: #000;" class=3D"styled-by-prettify"><br></span><span s=
tyle=3D"color: #008;" class=3D"styled-by-prettify">template</span><span sty=
le=3D"color: #660;" class=3D"styled-by-prettify"><</span><span style=3D"=
color: #008;" class=3D"styled-by-prettify">typename</span><span style=3D"co=
lor: #000;" class=3D"styled-by-prettify"> T</span><span style=3D"color: #66=
0;" class=3D"styled-by-prettify">></span><span style=3D"color: #000;" cl=
ass=3D"styled-by-prettify"> T </span><span style=3D"color: #660;" class=3D"=
styled-by-prettify">*</span><span style=3D"color: #000;" class=3D"styled-by=
-prettify">std</span><span style=3D"color: #660;" class=3D"styled-by-pretti=
fy">::</span><span style=3D"color: #000;" class=3D"styled-by-prettify">bles=
s</span><span style=3D"color: #660;" class=3D"styled-by-prettify">(</span><=
span style=3D"color: #008;" class=3D"styled-by-prettify">void</span><span s=
tyle=3D"color: #000;" class=3D"styled-by-prettify"> </span><span style=3D"c=
olor: #660;" class=3D"styled-by-prettify">*</span><span style=3D"color: #00=
0;" class=3D"styled-by-prettify">p</span><span style=3D"color: #660;" class=
=3D"styled-by-prettify">);</span></div></code></div><span class=3D"fvmr8t-x=
-x-76"><br></span> </div><div class=3D"lstlisting" id=3D"listing-10"><span =
class=3D"fvmr8t-x-x-76"><br></span></div><!--l. 736--><p class=3D"noindent"=
>Thus the obvious corrollary for marking regions as unreachable: <!--l. 738=
--></p><p class=3D"noindent"><br></p><div class=3D"prettyprint" style=3D"ba=
ckground-color: rgb(250, 250, 250); border-color: rgb(187, 187, 187); borde=
r-style: solid; border-width: 1px; word-wrap: break-word;"><code class=3D"p=
rettyprint"><div class=3D"subprettyprint"><span style=3D"color: #800;" clas=
s=3D"styled-by-prettify">// Requires: [start, (char*)start + length) denote=
s a region of allocated </span><span style=3D"color: #000;" class=3D"styled=
-by-prettify"><br></span><span style=3D"color: #800;" class=3D"styled-by-pr=
ettify">// storage that is a subset of the region of storage reachable thro=
ugh start. </span><span style=3D"color: #000;" class=3D"styled-by-prettify"=
><br></span><span style=3D"color: #800;" class=3D"styled-by-prettify">// Ef=
fects: implicitly uncreates objects within the denoted region. </span><span=
style=3D"color: #000;" class=3D"styled-by-prettify"><br></span><span style=
=3D"color: #008;" class=3D"styled-by-prettify">void</span><span style=3D"co=
lor: #000;" class=3D"styled-by-prettify"> std</span><span style=3D"color: #=
660;" class=3D"styled-by-prettify">::</span><span style=3D"color: #000;" cl=
ass=3D"styled-by-prettify">unbless</span><span style=3D"color: #660;" class=
=3D"styled-by-prettify">(</span><span style=3D"color: #008;" class=3D"style=
d-by-prettify">void</span><span style=3D"color: #000;" class=3D"styled-by-p=
rettify"> </span><span style=3D"color: #660;" class=3D"styled-by-prettify">=
*</span><span style=3D"color: #000;" class=3D"styled-by-prettify">start</sp=
an><span style=3D"color: #660;" class=3D"styled-by-prettify">,</span><span =
style=3D"color: #000;" class=3D"styled-by-prettify"> size_t length</span><s=
pan style=3D"color: #660;" class=3D"styled-by-prettify">);</span><span styl=
e=3D"color: #000;" class=3D"styled-by-prettify"> <br>=C2=A0<br></span><span=
style=3D"color: #800;" class=3D"styled-by-prettify">// Effects: uncreate a=
n object of implicit lifetype type T in the storage </span><span style=3D"c=
olor: #000;" class=3D"styled-by-prettify"><br></span><span style=3D"color: =
#800;" class=3D"styled-by-prettify">// =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0po=
inted to by T, while preserving the object representation. </span><span sty=
le=3D"color: #000;" class=3D"styled-by-prettify"><br></span><span style=3D"=
color: #008;" class=3D"styled-by-prettify">template</span><span style=3D"co=
lor: #660;" class=3D"styled-by-prettify"><</span><span style=3D"color: #=
008;" class=3D"styled-by-prettify">typename</span><span style=3D"color: #00=
0;" class=3D"styled-by-prettify"> T</span><span style=3D"color: #660;" clas=
s=3D"styled-by-prettify">></span><span style=3D"color: #000;" class=3D"s=
tyled-by-prettify"> </span><span style=3D"color: #008;" class=3D"styled-by-=
prettify">void</span><span style=3D"color: #000;" class=3D"styled-by-pretti=
fy"> </span><span style=3D"color: #660;" class=3D"styled-by-prettify">*</sp=
an><span style=3D"color: #000;" class=3D"styled-by-prettify">std</span><spa=
n style=3D"color: #660;" class=3D"styled-by-prettify">::</span><span style=
=3D"color: #000;" class=3D"styled-by-prettify">unbless</span><span style=3D=
"color: #660;" class=3D"styled-by-prettify">(</span><span style=3D"color: #=
000;" class=3D"styled-by-prettify">T </span><span style=3D"color: #660;" cl=
ass=3D"styled-by-prettify">*</span><span style=3D"color: #000;" class=3D"st=
yled-by-prettify">p</span><span style=3D"color: #660;" class=3D"styled-by-p=
rettify">);</span><span style=3D"color: #000;" class=3D"styled-by-prettify"=
> <br></span></div></code></div><div class=3D"lstlisting" id=3D"listing-11"=
><br><span class=3D"fvmr8t-x-x-76"><br></span></div><!--l. 749--><p class=
=3D"noindent">A gain of this new unreachable lifetime status is that it nea=
tly solves the problem of objects appearing at multiple addresses in a runn=
ing program. We can now say that only one of those addresses can be reachab=
le for an alive object at a time. If a program wishes to change an object=
=E2=80=99s current reachable address, they unbless the old location, and bl=
ess the new location. <!--l. 751--></p><p class=3D"noindent"><br></p><p cla=
ss=3D"noindent">It should be emphasised that similarly to blessing, unbless=
ing of trivial types causes no code emission. It simply tells the compiler =
what is now unreachable. <!--l. 753--></p><p class=3D"noindent"><br></p><p =
class=3D"noindent">Finally, it may seem that unreachability is a bit of a b=
ig sledgehammer to throw at this problem. However, there are a number of ot=
her problems elsewhere in C++ where having unreachable but alive objects wo=
uld be very useful =E2=80=93 thread local storage on a million CPU core com=
pute resource is an excellent example<span class=3D"footnote-mark"><a href=
=3D"file:///C:/Users/ned/Documents/boostish/wg21/P1031_file_io7.html#fn6x0"=
><sup class=3D"textsuperscript">6</sup></a></span><a id=3D"x1-19027f6"></a>=
. That said, this is a big change to lifetime, and if strong arguments are=
made against it then I am happy to propose something more conservative. </=
p><p class=3D"noindent"><br></p><h5 class=3D"subsubsectionHead"><span class=
=3D"titlemark">3.1.4 </span><a id=3D"x1-200003.1.4"></a>Blessing and unbl=
essing polymorphic objects</h5><!--l. 757--><p class=3D"noindent">Currently=
P0593 makes no mention of polymorphic objects i.e. ones with vptrs in many=
implementations. I can see lots of reasons why it would be useful to bless=
and unbless polymorphic objects i.e. update the vptr to the correct one fo=
r the currently running program, or clear the vptr to the system null point=
er. <!--l. 759--></p><p class=3D"noindent"><br></p><p class=3D"noindent">Im=
agine, for example, a polymorphic object with mapped storage duration whose=
constructing program instance terminates. Upon restart of the program, the=
storage is mapped back into the program, but now the polymorphic object ha=
s the wrong vptr! So we need to write the correct vptr for that object, aft=
er which all works as expected<span class=3D"footnote-mark"><a href=3D"file=
:///C:/Users/ned/Documents/boostish/wg21/P1031_file_io8.html#fn7x0"><sup cl=
ass=3D"textsuperscript">7</sup></a></span><a id=3D"x1-20001f7"></a> . <!--l=
.. 761--></p><p class=3D"noindent"><br></p><p class=3D"noindent">This also a=
pplies to shared mapped memory. Extending the C++ object model to handle me=
mory being modifiable by concurrently running C++ programs would be very ha=
rd, so I propose that we don=E2=80=99t do that. Rather we say that objects =
in mapped storage can only be reachable in exactly one or none running C++ =
programs at a time i.e. at exactly one or no address at any one time, other=
wise it is undefined =
=
behaviour. Therefore, if p=
rocess A wants to make available an object to process B, it <span class=3D"=
ecti-1095">unblesses </span>it and tells process B that the object is now a=
vailable for use. Process B blesses the object into reachable, and uses it.=
When done, it unblesses it once more. Now process A can bless it and use i=
t again, if so desired. <!--l. 763--></p><p class=3D"noindent"><br></p><p c=
lass=3D"noindent">Implied in this is that blessing and unblessing becomes a=
ble to rewrite the vptr (or whatever the C++ implementation uses to impleme=
nt polymorphism). To be truthful, I am torn on this. On the one hand, if yo=
u wish to be able to bless and unbless polymorphic objects, rewriting the v=
ptr is unavoidable, and the frequency of relocating polymorphic objects in =
current C++ is low. On the other hand, future C++=E2=80=99s may do a lot mo=
re remapping of memory during large array expansion in order to skip doing =
a memory copy, and in this circumstance every single polymorphic object in =
a few million item array would need their vptr=E2=80=99s needlessly rewritt=
en, which is unnecessary work. <!--l. 765--></p><p class=3D"noindent"><br><=
/p><p class=3D"noindent">I=E2=80=99m going to err on the side of caution, a=
nd propose that the polymorphic editions of bless and unbless shall be: <!-=
-l. 767--></p><p class=3D"noindent"><br></p><div class=3D"lstlisting" id=3D=
"listing-12"><div class=3D"prettyprint" style=3D"background-color: rgb(250,=
250, 250); border-color: rgb(187, 187, 187); border-style: solid; border-w=
idth: 1px; word-wrap: break-word;"><code class=3D"prettyprint"><div class=
=3D"subprettyprint"><span style=3D"color: #800;" class=3D"styled-by-prettif=
y">// Effects: create an object of implicit lifetype type T in the storage =
</span><span style=3D"color: #000;" class=3D"styled-by-prettify"><br></span=
><span style=3D"color: #800;" class=3D"styled-by-prettify">// =C2=A0 =C2=A0=
=C2=A0 =C2=A0 =C2=A0pointed to by T, while preserving the object represent=
ation </span><span style=3D"color: #000;" class=3D"styled-by-prettify"><br>=
</span><span style=3D"color: #800;" class=3D"styled-by-prettify">// =C2=A0 =
=C2=A0 =C2=A0 =C2=A0 =C2=A0apart from any necessary changes to make any pol=
ymorphic </span><span style=3D"color: #000;" class=3D"styled-by-prettify"><=
br></span><span style=3D"color: #800;" class=3D"styled-by-prettify">// =C2=
=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0objects within and including T ready for use=
.. </span><span style=3D"color: #000;" class=3D"styled-by-prettify"><br></sp=
an><span style=3D"color: #008;" class=3D"styled-by-prettify">template</span=
><span style=3D"color: #660;" class=3D"styled-by-prettify"><</span><span=
style=3D"color: #008;" class=3D"styled-by-prettify">typename</span><span s=
tyle=3D"color: #000;" class=3D"styled-by-prettify"> T</span><span style=3D"=
color: #660;" class=3D"styled-by-prettify">></span><span style=3D"color:=
#000;" class=3D"styled-by-prettify"> T </span><span style=3D"color: #660;"=
class=3D"styled-by-prettify">*</span><span style=3D"color: #000;" class=3D=
"styled-by-prettify">std</span><span style=3D"color: #660;" class=3D"styled=
-by-prettify">::</span><span style=3D"color: #000;" class=3D"styled-by-pret=
tify">revive</span><span style=3D"color: #660;" class=3D"styled-by-prettify=
">(</span><span style=3D"color: #008;" class=3D"styled-by-prettify">void</s=
pan><span style=3D"color: #000;" class=3D"styled-by-prettify"> </span><span=
style=3D"color: #660;" class=3D"styled-by-prettify">*</span><span style=3D=
"color: #000;" class=3D"styled-by-prettify">p</span><span style=3D"color: #=
660;" class=3D"styled-by-prettify">);</span><span style=3D"color: #000;" cl=
ass=3D"styled-by-prettify"> <br>=C2=A0<br></span><span style=3D"color: #800=
;" class=3D"styled-by-prettify">// Effects: uncreate an object of implicit =
lifetype type T in the storage </span><span style=3D"color: #000;" class=3D=
"styled-by-prettify"><br></span><span style=3D"color: #800;" class=3D"style=
d-by-prettify">// =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0pointed to by T, while =
preserving the object representation </span><span style=3D"color: #000;" cl=
ass=3D"styled-by-prettify"><br></span><span style=3D"color: #800;" class=3D=
"styled-by-prettify">// =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0apart from any ne=
cessary changes to make any polymorphic </span><span style=3D"color: #000;"=
class=3D"styled-by-prettify"><br></span><span style=3D"color: #800;" class=
=3D"styled-by-prettify">// =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0functions with=
in the objects within and including T unusable. </span><span style=3D"color=
: #000;" class=3D"styled-by-prettify"><br></span><span style=3D"color: #008=
;" class=3D"styled-by-prettify">template</span><span style=3D"color: #660;"=
class=3D"styled-by-prettify"><</span><span style=3D"color: #008;" class=
=3D"styled-by-prettify">typename</span><span style=3D"color: #000;" class=
=3D"styled-by-prettify"> T</span><span style=3D"color: #660;" class=3D"styl=
ed-by-prettify">></span><span style=3D"color: #000;" class=3D"styled-by-=
prettify"> </span><span style=3D"color: #008;" class=3D"styled-by-prettify"=
>void</span><span style=3D"color: #000;" class=3D"styled-by-prettify"> </sp=
an><span style=3D"color: #660;" class=3D"styled-by-prettify">*</span><span =
style=3D"color: #000;" class=3D"styled-by-prettify">std</span><span style=
=3D"color: #660;" class=3D"styled-by-prettify">::</span><span style=3D"colo=
r: #000;" class=3D"styled-by-prettify">stun</span><span style=3D"color: #66=
0;" class=3D"styled-by-prettify">(</span><span style=3D"color: #000;" class=
=3D"styled-by-prettify">T </span><span style=3D"color: #660;" class=3D"styl=
ed-by-prettify">*</span><span style=3D"color: #000;" class=3D"styled-by-pre=
ttify">p</span><span style=3D"color: #660;" class=3D"styled-by-prettify">);=
</span></div></code></div><span class=3D"fvmr8t-x-x-76"><br></span> </div><=
div class=3D"lstlisting" id=3D"listing-12"><span class=3D"fvmr8t-x-x-76"><b=
r></span></div><!--l. 781--><p class=3D"noindent">Note that <span class=3D"=
fvmr8t-x-x-93">stun() </span>and <span class=3D"fvmr8t-x-x-93">revive() </s=
pan>not just change the vptrs of the type you pass them, but also any vptrs=
of any nested types within that type. They also perform blessing and unble=
ssing, so you do not have to do that separately. Calling these functions on=
non-polymorphic types is permitted. </p><p class=3D"noindent"><br></p><h4 =
class=3D"subsectionHead"><span class=3D"titlemark">3.2 </span><a id=3D"x1=
-210003.2"></a>New attributes <span class=3D"fvmr8t-x-x-93">[[no_side_effec=
ts]] </span>and <span class=3D"fvmr8t-x-x-93">[[no_visible_side_effects]]</=
span>, and new contract syntax for specifying lack of side effects</h4><!--=
l. 786--><p class=3D"noindent">It is highly important for the future effici=
ency of any iostreams v2 that the low level i/o functions do not cause the =
compiler to dump and reload all state for every i/o, as they must do at pre=
sent. The i/o functions proposed do not modify their handle on most platfor=
ms, and on those platforms we can guarantee to the compiler that there are =
either no side effects or no visible side effects except for those objects =
modified by parameters. <!--l. 788--></p><p class=3D"noindent"><br></p><p c=
lass=3D"noindent">Reusing <span class=3D"fvmr8t-x-x-93">bless() </span>and =
<span class=3D"fvmr8t-x-x-93">unbless()</span>, I propose the following con=
tracts syntax for the <span class=3D"fvmr8t-x-x-93">io_handle </span>functi=
ons so we can tell the compiler what side effects the i/o functions have: <=
!--l. 790--></p><p class=3D"noindent"><br></p><div class=3D"lstlisting" id=
=3D"listing-13"><div class=3D"prettyprint" style=3D"background-color: rgb(2=
50, 250, 250); border-color: rgb(187, 187, 187); border-style: solid; borde=
r-width: 1px; word-wrap: break-word;"><code class=3D"prettyprint"><div clas=
s=3D"subprettyprint"><span style=3D"color: #008;" class=3D"styled-by-pretti=
fy">constexpr</span><span style=3D"color: #660;" class=3D"styled-by-prettif=
y">!</span><span style=3D"color: #000;" class=3D"styled-by-prettify"> </spa=
n><span style=3D"color: #008;" class=3D"styled-by-prettify">void</span><spa=
n style=3D"color: #000;" class=3D"styled-by-prettify"> ensure_blessed</span=
><span style=3D"color: #660;" class=3D"styled-by-prettify">(</span><span st=
yle=3D"color: #000;" class=3D"styled-by-prettify">buffers_type buffers</spa=
n><span style=3D"color: #660;" class=3D"styled-by-prettify">)</span><span s=
tyle=3D"color: #000;" class=3D"styled-by-prettify"> <br></span><span style=
=3D"color: #660;" class=3D"styled-by-prettify">{</span><span style=3D"color=
: #000;" class=3D"styled-by-prettify"> <br>=C2=A0 </span><span style=3D"col=
or: #008;" class=3D"styled-by-prettify">for</span><span style=3D"color: #66=
0;" class=3D"styled-by-prettify">(</span><span style=3D"color: #008;" class=
=3D"styled-by-prettify">auto</span><span style=3D"color: #000;" class=3D"st=
yled-by-prettify"> </span><span style=3D"color: #660;" class=3D"styled-by-p=
rettify">&</span><span style=3D"color: #000;" class=3D"styled-by-pretti=
fy">buffer </span><span style=3D"color: #660;" class=3D"styled-by-prettify"=
>:</span><span style=3D"color: #000;" class=3D"styled-by-prettify"> buffers=
</span><span style=3D"color: #660;" class=3D"styled-by-prettify">)</span><s=
pan style=3D"color: #000;" class=3D"styled-by-prettify"> <br>=C2=A0 </span>=
<span style=3D"color: #660;" class=3D"styled-by-prettify">{</span><span sty=
le=3D"color: #000;" class=3D"styled-by-prettify"> <br>=C2=A0 =C2=A0 bless</=
span><span style=3D"color: #660;" class=3D"styled-by-prettify">(</span><spa=
n style=3D"color: #000;" class=3D"styled-by-prettify">buffer</span><span st=
yle=3D"color: #660;" class=3D"styled-by-prettify">.</span><span style=3D"co=
lor: #000;" class=3D"styled-by-prettify">data</span><span style=3D"color: #=
660;" class=3D"styled-by-prettify">(),</span><span style=3D"color: #000;" c=
lass=3D"styled-by-prettify"> buffer</span><span style=3D"color: #660;" clas=
s=3D"styled-by-prettify">.</span><span style=3D"color: #000;" class=3D"styl=
ed-by-prettify">size</span><span style=3D"color: #660;" class=3D"styled-by-=
prettify">());</span><span style=3D"color: #000;" class=3D"styled-by-pretti=
fy"> <br>=C2=A0 </span><span style=3D"color: #660;" class=3D"styled-by-pret=
tify">}</span><span style=3D"color: #000;" class=3D"styled-by-prettify"> <b=
r></span><span style=3D"color: #660;" class=3D"styled-by-prettify">}</span>=
<span style=3D"color: #000;" class=3D"styled-by-prettify"> <br>=C2=A0<br></=
span><span style=3D"color: #800;" class=3D"styled-by-prettify">// This func=
tion has no side effects visible to the caller except </span><span style=3D=
"color: #000;" class=3D"styled-by-prettify"><br></span><span style=3D"color=
: #800;" class=3D"styled-by-prettify">// for the objects it creates in the =
scatter buffer list. </span><span style=3D"color: #000;" class=3D"styled-by=
-prettify"><br></span><span style=3D"color: #008;" class=3D"styled-by-prett=
ify">virtual</span><span style=3D"color: #000;" class=3D"styled-by-prettify=
"> buffers_type io_handle</span><span style=3D"color: #660;" class=3D"style=
d-by-prettify">::</span><span style=3D"color: #000;" class=3D"styled-by-pre=
ttify">read</span><span style=3D"color: #660;" class=3D"styled-by-prettify"=
>(</span><span style=3D"color: #000;" class=3D"styled-by-prettify">io_reque=
st</span><span style=3D"color: #080;" class=3D"styled-by-prettify"><buff=
ers_type></span><span style=3D"color: #000;" class=3D"styled-by-prettify=
"> reqs</span><span style=3D"color: #660;" class=3D"styled-by-prettify">,</=
span><span style=3D"color: #000;" class=3D"styled-by-prettify"> <br>=C2=A0 =
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=
=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0deadline d </span><span=
style=3D"color: #660;" class=3D"styled-by-prettify">=3D</span><span style=
=3D"color: #000;" class=3D"styled-by-prettify"> deadline</span><span style=
=3D"color: #660;" class=3D"styled-by-prettify">())</span><span style=3D"col=
or: #000;" class=3D"styled-by-prettify"> </span><span style=3D"color: #008;=
" class=3D"styled-by-prettify">throws</span><span style=3D"color: #660;" cl=
ass=3D"styled-by-prettify">(</span><span style=3D"color: #000;" class=3D"st=
yled-by-prettify">file_io_error</span><span style=3D"color: #660;" class=3D=
"styled-by-prettify">)</span><span style=3D"color: #000;" class=3D"styled-b=
y-prettify"> <br>=C2=A0 =C2=A0 </span><span style=3D"color: #660;" class=3D=
"styled-by-prettify">[[</span><span style=3D"color: #000;" class=3D"styled-=
by-prettify">no_side_effects</span><span style=3D"color: #660;" class=3D"st=
yled-by-prettify">]]</span><span style=3D"color: #000;" class=3D"styled-by-=
prettify"> <br>=C2=A0 =C2=A0 </span><span style=3D"color: #660;" class=3D"s=
tyled-by-prettify">[[</span><span style=3D"color: #000;" class=3D"styled-by=
-prettify">ensures</span><span style=3D"color: #660;" class=3D"styled-by-pr=
ettify">:</span><span style=3D"color: #000;" class=3D"styled-by-prettify"> =
ensure_blessed</span><span style=3D"color: #660;" class=3D"styled-by-pretti=
fy">(</span><span style=3D"color: #000;" class=3D"styled-by-prettify">reqs<=
/span><span style=3D"color: #660;" class=3D"styled-by-prettify">.</span><sp=
an style=3D"color: #000;" class=3D"styled-by-prettify">buffers</span><span =
style=3D"color: #660;" class=3D"styled-by-prettify">)]]</span><span style=
=3D"color: #000;" class=3D"styled-by-prettify"> <br>=C2=A0 =C2=A0 </span><s=
pan style=3D"color: #660;" class=3D"styled-by-prettify">[[</span><span styl=
e=3D"color: #000;" class=3D"styled-by-prettify">ensures</span><span style=
=3D"color: #660;" class=3D"styled-by-prettify">:</span><span style=3D"color=
: #000;" class=3D"styled-by-prettify"> ensure_blessed</span><span style=3D"=
color: #660;" class=3D"styled-by-prettify">(</span><span style=3D"color: #0=
08;" class=3D"styled-by-prettify">return</span><span style=3D"color: #660;"=
class=3D"styled-by-prettify">)]];</span></div></code></div><span class=3D"=
fvmr8t-x-x-76"><br></span> </div><div class=3D"lstlisting" id=3D"listing-13=
"><span class=3D"fvmr8t-x-x-76"><br></span></div><!--l. 808--><p class=3D"n=
oindent">The key thing to note here is if the caller never accesses any of =
the scatter buffers returned by the function, =
=
t=
he read can be optimised out entirely due to the <span class=3D"fvmr8t-x-x-=
93">[[no_side_effects]] </span>attribute. Obviously the compiler also does =
not dump and reload any state around this scatter read apart from buffers s=
upplied and returned, despite it calling a kernel system call, because we a=
re giving a hard guarantee to the compiler that it is safe to assume no sid=
e effects apart from modifying the scatter buffers. <!--l. 811--></p><p cla=
ss=3D"noindent"><br></p><p class=3D"noindent">Gather write is less exciting=
, but straightforward: <!--l. 813--></p><p class=3D"noindent"><br></p><div =
class=3D"prettyprint" style=3D"background-color: rgb(250, 250, 250); border=
-color: rgb(187, 187, 187); border-style: solid; border-width: 1px; word-wr=
ap: break-word;"><code class=3D"prettyprint"><div class=3D"subprettyprint">=
<span style=3D"color: #008;" class=3D"styled-by-prettify">virtual</span><sp=
an style=3D"color: #000;" class=3D"styled-by-prettify"> const_buffers_type =
io_handle</span><span style=3D"color: #660;" class=3D"styled-by-prettify">:=
:</span><span style=3D"color: #000;" class=3D"styled-by-prettify">write</sp=
an><span style=3D"color: #660;" class=3D"styled-by-prettify">(</span><span =
style=3D"color: #000;" class=3D"styled-by-prettify">io_request</span><span =
style=3D"color: #080;" class=3D"styled-by-prettify"><const_buffers_type&=
gt;</span><span style=3D"color: #000;" class=3D"styled-by-prettify"> reqs</=
span><span style=3D"color: #660;" class=3D"styled-by-prettify">,</span><spa=
n style=3D"color: #000;" class=3D"styled-by-prettify"> <br>=C2=A0 =C2=A0 =
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=
=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 deadline=
d </span><span style=3D"color: #660;" class=3D"styled-by-prettify">=3D</sp=
an><span style=3D"color: #000;" class=3D"styled-by-prettify"> deadline</spa=
n><span style=3D"color: #660;" class=3D"styled-by-prettify">())</span><span=
style=3D"color: #000;" class=3D"styled-by-prettify"> <br>=C2=A0 =C2=A0 =C2=
=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =
=C2=A0</span><span style=3D"color: #008;" class=3D"styled-by-prettify">thro=
ws</span><span style=3D"color: #660;" class=3D"styled-by-prettify">(</span>=
<span style=3D"color: #000;" class=3D"styled-by-prettify">file_io_error</sp=
an><span style=3D"color: #660;" class=3D"styled-by-prettify">)</span><span =
style=3D"color: #000;" class=3D"styled-by-prettify"> </span><span style=3D"=
color: #660;" class=3D"styled-by-prettify">[[</span><span style=3D"color: #=
000;" class=3D"styled-by-prettify">no_visible_side_effects</span><span styl=
e=3D"color: #660;" class=3D"styled-by-prettify">]];</span><span style=3D"co=
lor: #000;" class=3D"styled-by-prettify"> <br></span></div></code></div><di=
v class=3D"lstlisting" id=3D"listing-14"><br><span class=3D"fvmr8t-x-x-76">=
<br></span></div><!--l. 819--><p class=3D"noindent">Here we are telling the=
compiler that there are side effects, just not ones visible to the caller.=
Hence the compiler cannot optimise out calling this function, but it can s=
kip dumping and reloading state despite the kernel system call made by the =
function. <!--l. 821--></p><p class=3D"noindent"><br></p><p class=3D"noinde=
nt"></p><h5 class=3D"subsubsectionHead"><span class=3D"titlemark">3.2.1 <=
/span><a id=3D"x1-220003.2.1"></a>I/O write reordering barriers</h5><!--l. =
823--><p class=3D"noindent">C and C++ allow the programmer to constrain mem=
ory read and write operations, specifically to constrain the extent of reor=
dering that the compiler and CPU are permitted to do. One can use atomics w=
ith a memory order specified, or one can call a fence function which applie=
s a memory reordering constraint for the current thread of execution either=
at just the compiler level, or also at the CPU level whereby the CPU is to=
ld what ordering its symmetric multiprocessing cores needs to manifest in v=
isible side effects. <!--l. 825--></p><p class=3D"noindent"><br></p><p clas=
s=3D"noindent">File i/o is no different: concurrent users of the same file =
or directory or file system will experience races unless they take special =
measures to coordinate amongst themselves. <!--l. 827--></p><p class=3D"noi=
ndent"><br></p><p class=3D"noindent">Given that I have proposed above that =
C++ standardises mapped storage duration into the language itself, it might=
seem self evident that one would also need to propose a standardised mecha=
nism for indicating reordering constraints on i/o, which are separate to th=
ose for threads. My current advice is that we should <span class=3D"ecbx-10=
95">not </span>do this =E2=80=93 yet. <!--l. 829--></p><p class=3D"noindent=
"><br></p><p class=3D"noindent">The first reason why is that persistent mem=
ory is just around the corner, and I think it would be unwise to standardis=
e without the industry gaining plenty of empirical experience first. So kic=
k that decision down a few standard releases. <!--l. 831--></p><p class=3D"=
noindent">Secondly, the proposed library herein does expose whatever acquir=
e-release semantics is implemented by the host operating system for file i/=
o, plus the advisory locking infrastructure implemented by the host, plus t=
he ability to enable write-through caching semantics. It also provides <spa=
n class=3D"fvmr8t-x-x-93">barrier()</span>, though one should not write cod=
e which relies upon it working, as it frequently does not. <!--l. 833--></p=
><p class=3D"noindent"><br></p><p class=3D"noindent">Note that if the file =
is opened in non-volatile RAM storage mode, <span class=3D"fvmr8t-x-x-93">b=
arrier() </span>calls the appropriate architecture-specific assembler instr=
uctions to correctly barrier writes to main memory, so in this sense =
=
=
there is library, if not language, support for persis=
tent memory. The obvious sticker is that <span class=3D"fvmr8t-x-x-93">barr=
ier() </span>is a virtual function with significant preamble and epilogue, =
so for flushing a single cache line it is extremely inefficient relative to=
direct language support for this. <!--l. 835--></p><p class=3D"noindent">N=
evertheless, one can write standards conforming code with the proposed libr=
ary which performs better on persistent memory than on traditional storage,=
and my advice is that this is good enough for the next few years.=C2=A0</p=
></div></div>
<p></p>
-- <br />
You received this message because you are subscribed to the Google Groups &=
quot;ISO C++ Standard - Future Proposals" group.<br />
To unsubscribe from this group and stop receiving emails from it, send an e=
mail to <a href=3D"mailto:std-proposals+unsubscribe@isocpp.org">std-proposa=
ls+unsubscribe@isocpp.org</a>.<br />
To post to this group, send email to <a href=3D"mailto:std-proposals@isocpp=
..org">std-proposals@isocpp.org</a>.<br />
To view this discussion on the web visit <a href=3D"https://groups.google.c=
om/a/isocpp.org/d/msgid/std-proposals/b214ce98-2aaa-4f7f-a04a-77687422111a%=
40isocpp.org?utm_medium=3Demail&utm_source=3Dfooter">https://groups.google.=
com/a/isocpp.org/d/msgid/std-proposals/b214ce98-2aaa-4f7f-a04a-77687422111a=
%40isocpp.org</a>.<br />
------=_Part_18_1402463751.1533146482843--
------=_Part_17_462843701.1533146482841--
.
Author: Alexander Zaitsev <zamazan4ik@gmail.com>
Date: Wed, 1 Aug 2018 15:38:35 -0700 (PDT)
Raw View
------=_Part_156_1291085310.1533163115986
Content-Type: multipart/alternative;
boundary="----=_Part_157_1675232168.1533163115986"
------=_Part_157_1675232168.1533163115986
Content-Type: text/plain; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable
=D0=B2=D1=82=D0=BE=D1=80=D0=BD=D0=B8=D0=BA, 31 =D0=B8=D1=8E=D0=BB=D1=8F 201=
8 =D0=B3., 22:08:27 UTC+3 =D0=BF=D0=BE=D0=BB=D1=8C=D0=B7=D0=BE=D0=B2=D0=B0=
=D1=82=D0=B5=D0=BB=D1=8C Niall Douglas =D0=BD=D0=B0=D0=BF=D0=B8=D1=81=D0=B0=
=D0=BB:
>
> So after many weeks of pondering and writing this outside of work each=20
> day, please find attached an early draft of revision 1 of P1031 destined=
=20
> for San Diego. Changes since R0:
>
> - Wrote partial draft TS wording for deadline, handle, io_handle,=20
> mapped, mapped_view, native_handle_type and file_io_error.
> - Added impact on the standard regarding potential changes to the C++=
=20
> object model.
> - Added impact on the standard regarding the proposed=20
> [[no_side_effects]] et al contracts attributes.
> =20
> Note that the partial draft TS wording assumes Deterministic Exceptions +=
=20
> SG14 standard error object + in-progress _Fails calling convention=20
> implementation of P0709.
>
> I am very aware that modifying the C++ object and lifetime model is a hug=
e=20
> ask of WG21. This is why memory maps are pure UB in current C++, despite=
=20
> the fact that you can't implement dynamic memory allocation on the major=
=20
> platforms without something like them.
>
> Please before you blow holes in my proposed changes, can you think of a=
=20
> better alternative beforehand? I suspect that most "better alternatives"=
=20
> have a very good technical reason why I couldn't choose them, but it woul=
d=20
> do no harm to ask clever "stupid questions" at this stage in this=20
> proposal's lifecycle. Better out than in.
>
> My thanks in advance, especially for feedback on such a profound and=20
> complex part of the C++ standard.
>
> Niall
>
>
Great proposal. One suggestion - possibly will be better to divide your=20
proposal into several proposals. My own wish - move contract=20
[[no_side_effects]] into another proposal. Because [[no_side_effects]] can=
=20
help in another places for code optimization.=20
--=20
You received this message because you are subscribed to the Google Groups "=
ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an e=
mail to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp=
..org/d/msgid/std-proposals/fd17e3b3-00c9-4f2d-80b3-0f4f9490f388%40isocpp.or=
g.
------=_Part_157_1675232168.1533163115986
Content-Type: text/html; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable
<div dir=3D"ltr">=D0=B2=D1=82=D0=BE=D1=80=D0=BD=D0=B8=D0=BA, 31 =D0=B8=D1=
=8E=D0=BB=D1=8F 2018 =D0=B3., 22:08:27 UTC+3 =D0=BF=D0=BE=D0=BB=D1=8C=D0=B7=
=D0=BE=D0=B2=D0=B0=D1=82=D0=B5=D0=BB=D1=8C Niall Douglas =D0=BD=D0=B0=D0=BF=
=D0=B8=D1=81=D0=B0=D0=BB:<blockquote class=3D"gmail_quote" style=3D"margin:=
0;margin-left: 0.8ex;border-left: 1px #ccc solid;padding-left: 1ex;"><div =
dir=3D"ltr">So after many weeks of pondering and writing this outside of wo=
rk each day, please find attached an early draft of revision 1 of P1031 des=
tined for San Diego. Changes since R0:<div><ul><li>Wrote partial draft TS w=
ording for deadline, handle, io_handle, mapped, mapped_view, native_handle_=
type and file_io_error.</li><li>Added impact on the standard regarding pote=
ntial changes to the C++ object model.</li><li>Added impact on the standard=
regarding the proposed [[no_side_effects]] et al contracts attributes.<br>=
</li></ul></div><div>Note that the partial draft TS wording assumes Determi=
nistic Exceptions + SG14 standard error object + in-progress _Fails calling=
convention implementation of P0709.</div><div><br></div><div>I am very awa=
re that modifying the C++ object and lifetime model is a huge ask of WG21. =
This is why memory maps are pure UB in current C++, despite the fact that y=
ou can't implement dynamic memory allocation on the major platforms wit=
hout something like them.</div><div><br></div><div>Please before you blow h=
oles in my proposed changes, can you think of a better alternative beforeha=
nd? I suspect that most "better alternatives" have a very good te=
chnical reason why I couldn't choose them, but it would do no harm to a=
sk clever "stupid questions" at this stage in this proposal's=
lifecycle. Better out than in.</div><div><br></div><div>My thanks in advan=
ce, especially for feedback on such a profound and complex part of the C++ =
standard.</div><div><br></div><div>Niall</div><div><br></div></div></blockq=
uote><div><br></div><div>Great proposal. One suggestion - possibly will be =
better to divide your proposal into several proposals. My own wish - move c=
ontract [[no_side_effects]] into another proposal. Because [[no_side_effect=
s]] can help in another places for code optimization.=C2=A0</div></div>
<p></p>
-- <br />
You received this message because you are subscribed to the Google Groups &=
quot;ISO C++ Standard - Future Proposals" group.<br />
To unsubscribe from this group and stop receiving emails from it, send an e=
mail to <a href=3D"mailto:std-proposals+unsubscribe@isocpp.org">std-proposa=
ls+unsubscribe@isocpp.org</a>.<br />
To post to this group, send email to <a href=3D"mailto:std-proposals@isocpp=
..org">std-proposals@isocpp.org</a>.<br />
To view this discussion on the web visit <a href=3D"https://groups.google.c=
om/a/isocpp.org/d/msgid/std-proposals/fd17e3b3-00c9-4f2d-80b3-0f4f9490f388%=
40isocpp.org?utm_medium=3Demail&utm_source=3Dfooter">https://groups.google.=
com/a/isocpp.org/d/msgid/std-proposals/fd17e3b3-00c9-4f2d-80b3-0f4f9490f388=
%40isocpp.org</a>.<br />
------=_Part_157_1675232168.1533163115986--
------=_Part_156_1291085310.1533163115986--
.
Author: Nicol Bolas <jmckesson@gmail.com>
Date: Wed, 1 Aug 2018 18:31:42 -0700 (PDT)
Raw View
------=_Part_46_53266908.1533173502709
Content-Type: multipart/alternative;
boundary="----=_Part_47_1180964524.1533173502710"
------=_Part_47_1180964524.1533173502710
Content-Type: text/plain; charset="UTF-8"
*On Creation, Destruction, and Remapping:*
So you have this operation you want to be able to do. You want to take
objects in one address, make them disappear from that address, and then
make them appear in a (potentially) different address. And you want to
change the C++ object model to make this work.
Here's a question: do you *need* to change the object model in order to get
this effect?
If we limit our usage of mappable types to P0593's implicit lifetime types,
then I don't think we to affect the object model at all. Implicit lifetime
types are objects which are nothing more than a set of bits, where their
value representation is their logical functionality. Two objects of such
types with the same value representation have the same value and are
distinct only in that they have different addresses.
With such a limitation in place, you could define the mapped memory
operation as a funny sort of memcpy. It doesn't move the object. Unmapping
the memory range terminates the lifetime of all objects in that memory. The
*values* are preserved, but not the objects. You then map the memory at
potentially new addresses.
Now, you still need a way to create new objects in those addresses with
their value representations intact (see below for why non-template `bless`
can't do this). But such a feature does not require substantial changes to
the object model. It's just a new way to create an object.
To me, the principle advantage of significant changes to the object model
here would be to allow a larger variety of objects to work. So now we have
the next question: exactly which types of objects are you trying to
support? What is the minimum set of features you need to make this
worthwhile? That will help you define what changes you need to the object
model, if any.
For example, if you want to expand from Implicit Lifetime types to
Trivially Relocatable types, then the object model need not be changed
(save for the changes needed to make Trivially Relocatable work). You can
still define it as destroying the old object and manifesting a new one.
However, because such types are more than just their values, this is of
limited value. `unique_ptr<T>` is the quintessential Trivially Relocatable
example. But would it be a good thing to stick in a memory mapped object? I
don't think so. If the object it points to is not in the same memory map,
then it is not reasonable to persist the object across executions, since
that object may well be long dead.
And if `unique_ptr<T>` does point to an object in the same map, then its
address will have to be updated before you can actually use it. That means
you can't just cast and go; you have to do some work between mapping the
memory and using it. Given that... what's the point of storing it in a
memory map directly at all? Because it's part of some other object you do
want to store there?
Somethign similar is true of other popular Trivially Relocatable types.
`vector`, `fstream`, `any`, etc, all of them *could* be stored there, but
the utility of doing so is... dubious.
Your proposal suggests allowing polymorphic types. Now with these, the
destroy/recreate trick becomes tricky. The manifesting operation is based
on the value being undisturbed. But you *need* the value to be disturbed,
since the location of the vtable pointer may need to be fixed up.
Which brings me to my own personal experience. See, I've been a part of a
team that actually implemented a "memcpy"-style serialization system that
*could* handle polymorphic types. This system required two phases:
serialization and deserialization.
Now admittedly, our system did the serialization part through copying (the
host architecture and the destination architectures were not the same. And
I *mean* not the same; in one remarkable case, the compiler for the
deserialization part used a *different order* for the base classes than the
serialization compiler). But part of what it did was to put something
special in the vtable pointer slot: an index that represented which type it
was. The deserialization code didn't copy anything; it just fixed up vtable
pointers in-situ by reading the type index.
This is important because one of the things this allowed us to do was to
deserialize without knowing at compile time what each type was. We didn't
have to know the dynamic type from outside of the system. We were able to
use the base class type, and the system would know how that the pointer
actually pointed to a more derived class and therefore adjust the vtable
accordingly.
That's really important, since most polymorphic type usage is based on base
class types. If you have a polymorphic object in mapped memory, the code
that wants to retrieve the address probably doesn't know what its dynamic
type is; it knows the base class type it wants.
And that's where things get tricky. Because now, we're not talking about
some kind of memory barrier. You're talking about having to perform a
process on *each* object in a region of memory before the unmap process can
be considered complete. Then you have to perform a counter-process on each
object afterwards to fully map it again. And what's worse is that C++...
makes this *impossible* to implement generally.
We could do it in our system only because our system had a limited, fixed
set of types to work with. C++ does not. Loading DLL/SOs adds more type
indices. And while type indices will be the same for an execution of the
code, it will be able to change between executions. But you need to be able
to store a value in the mapped memory that contains something that is
persistent across executions.
C++ doesn't have a type identifier like that. So the only way to make this
work is if you know the *dynamic* type of the object when you unpack it.
And as someone who has used persistence systems of this sort, you generally
don't know. And you generally don't *want* to know; if you wanted to know,
you wouldn't be using a dynamic type ;)
So I don't think that polymorphic type support would be useful if it were
done in an implementable fashion. However, even if it was implementable, it
could still work based on having an explicit location in code where the
lifetime of the old object ends (packing the polymorphic object; replacing
the vtable pointer with an index) and the lifetime of the new begins
(unpacking it; replacing the index with a valid vtable pointer). Obviously,
we would need to create functions to explicitly do so on a per-object basis
(as your proposal outlines), but this wouldn't need to affect the object
model. Your "stun" function could be said to terminate the object's
lifetime, while your "revive" function creates it.
So even in this case, the object model itself wouldn't need to be changed.
So what use cases are you trying to support where you *need* to change the
object model?
*On Mapped Storage Duration:*
I think "Mapped storage duration" is too on-point in terms of where the
memory comes from, rather than how C++ thinks of it.
The four storage durations are based on how C++ interacts with them.
Automatic storage duration objects have storage durations determined
entirely by their objects' scopes. Static storage duration objects have
their storage duration be the lifetime of the program. Thread-local storage
duration objects have their storage duration be the lifetime of the thread.
And dynamic duration objects have their storage duration determined
dynamically: by direct manipulation of the code.
But I think there is a deeper misunderstanding here. You are confusing
properties of "storage" with the properties of "storage duration". The
former is a thing that an object needs to have in order to exist. The
latter is a property of an object, determined by the means used to
construct it, and it's purpose is to govern the (default) lifetime of that
object.
For example, you can create an automatic variable with automatic storage
duration. You can then reuse that variable's storage to create objects with
dynamic storage duration. By reusing it, you will have ended the lifetime
of the automatic variable. You would then have an object with dynamic
storage duration whose storage happens to be on the stack.
Which is why "stack storage duration" would be a misnomer, much like
"mapped storage duration".
Of course, C++ has a bunch of rules about what happens when you do this and
then try to use that variable's name later on. As well as what happens when
that variable goes out of scope, and what you need to do to make leaving
scope not invoke UB (namely, create an object of that type *back* in that
storage).
What you seem to want is simply a wider form of dynamic storage duration.
You're not changing the meaning of the storage duration; you're simply
adding ways to create/destroy such objects.
*On Bless:*
P0593 proposed these functions to mark regions as reachable:
>
Your notion of "reachability" has nothing to do with what P0593 is doing.
`bless` does not make objects "reachable"; that proposal only uses the word
"reachable" once, and that is merely a description about the relationship
between a pointer and a size (which really ought to be a
`std::span<std::byte>`).
And your definition of "reachable" here does not map to what P0593 is
doing. `std::bless` and equivalent functions declare that a piece of memory
*creates* implicit objects. When you call `bless` on a range of memory,
objects will be created to make your code work. What your "reachable"
behavior seems to want is for them to already exist.
This may seem like a trivial difference, but it isn't. P0593 points it out
<http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0593r2.html#type-punning>:
when you create objects, the contents of the storage are rendered
unspecified. And this is just as true for objects implicitly created by
`std::bless`.
You explicitly don't want this. You want the objects to already exist, with
their values intact. Non-template `bless` can't do that; `bless<T>` can,
but only for a single complete object of a specified type. And even then,
it is still *creating* the object, not making it "reachable". It wasn't
there until `bless<T>` was called.
*On Focus:*
Your section 3.2 has nothing to do with the C++ object model or the needs
of mapping memory. That's not to say that they aren't useful, but the
presence of side effects is orthogonal to memory mapping or the object
model.
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/c05b4e09-4c87-4d06-9061-4c101b37fa2f%40isocpp.org.
------=_Part_47_1180964524.1533173502710
Content-Type: text/html; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable
<div dir=3D"ltr"><div></div><div><br><div><u>On Creation, Destruction, and =
Remapping:</u></div><div><br></div><div>So you have this operation you want=
to be able to do. You want to take=20
objects in one address, make them disappear from that address, and then=20
make them appear in a (potentially) different address. And you want to chan=
ge the C++ object model to make this work.</div><div><br></div><div>Here=
9;s a question: do you <i>need</i> to change the object model in order to g=
et this effect?<br></div><div><br></div><div>If
we limit our usage of mappable types to P0593's implicit lifetime=20
types, then I don't think we to affect the object model at all. Implici=
t lifetime types are objects=20
which are nothing more than a set of bits, where their value=20
representation is their logical functionality. Two objects of such types
with the same value representation have the same value and are distinct
only in that they have different addresses.<br></div><div><br></div>With
such a limitation in place, you could define the mapped memory operation a=
s a=20
funny sort of memcpy. It doesn't move the object. Unmapping the memory =
range terminates the lifetime of all objects in that memory. The <i>values<=
/i> are preserved, but not the objects. You then map the memory at potentia=
lly new addresses.</div><div><br></div><div>Now, you still need a
way to create new objects in those addresses with their value representati=
ons intact (see below for why non-template `bless` can't do this). But =
such a feature does not require substantial changes to the object model. It=
's just a new way to create an object.<br><br><div>To me, the principle=
advantage of significant changes to the object model here would be to allo=
w a=20
larger variety of objects to work. So now we have the next question: exactl=
y which types of objects are you trying to support? What is the minimum set=
of features you need to make this worthwhile? That will help you define=20
what changes you need to the object model, if any.</div><div><br></div>For=
=20
example, if you want to expand from Implicit Lifetime types to Trivially
Relocatable types, then the object model need not be changed (save for=20
the changes needed to make Trivially Relocatable work). You can still=20
define it as destroying the old object and manifesting a new one.</div><div=
><br></div><div>However, because such types are more than just their values=
, this is of limited value. `unique_ptr<T>` is the quintessential Tri=
vially Relocatable example. But would it be a good thing to stick in a memo=
ry mapped object? I don't think so. If the object it points to is not i=
n the same memory map, then it is not reasonable to persist the object acro=
ss executions, since that object may well be long dead.</div><div><br></div=
><div>And if `unique_ptr<T>` does point to an object in the same map,=
then its address will have to be updated before you can actually use it. T=
hat means you can't just cast and go; you have to do some work between =
mapping the memory and using it. Given that... what's the point of stor=
ing it in a memory map directly at all? Because it's part of some other=
object you do want to store there?</div><div><br></div><div>Somethign simi=
lar is true of other popular Trivially Relocatable types. `vector`, `fstrea=
m`, `any`, etc, all of them <i>could</i> be stored there, but the utility o=
f doing so is... dubious.<br></div><div><br></div><div>Your proposal sugges=
ts allowing polymorphic types. Now with these, the destroy/recreate trick b=
ecomes tricky. The manifesting operation is based on the value being undist=
urbed. But you <i>need</i> the value to be disturbed, since the location of=
the vtable pointer may need to be fixed up.</div><div><br></div><div>Which=
brings me to my own personal experience. See, I've been a part of a te=
am that actually implemented a "memcpy"-style serialization syste=
m that <i>could</i> handle polymorphic types. This system required two phas=
es: serialization and deserialization.</div><div><br></div><div>Now admitte=
dly, our system did the serialization part through copying (the host archit=
ecture and the destination architectures were not the same. And I <i>mean</=
i> not the same; in one remarkable case, the compiler for the deserializati=
on part used a <i>different order</i> for the base classes than the seriali=
zation compiler). But part of what it did was to put something special in t=
he vtable pointer slot: an index that represented which type it was. The de=
serialization code didn't copy anything; it just fixed up vtable pointe=
rs in-situ by reading the type index.</div><div><br></div><div>This is impo=
rtant because one of the things this allowed us to do was to deserialize wi=
thout knowing at compile time what each type was. We didn't have to kno=
w the dynamic type from outside of the system. We were able to use the base=
class type, and the system would know how that the pointer actually pointe=
d to a more derived class and therefore adjust the vtable accordingly.</div=
><div><br></div><div>That's really important, since most polymorphic ty=
pe usage is based on base class types. If you have a polymorphic object in =
mapped memory, the code that wants to retrieve the address probably doesn&#=
39;t know what its dynamic type is; it knows the base class type it wants.<=
/div><div><br></div><div>And that's where things get tricky. Because no=
w, we're not talking about some kind of memory barrier. You're talk=
ing about having to perform a process on <i>each</i> object in a region of =
memory before the unmap process can be considered complete. Then you have t=
o perform a counter-process on each object afterwards to fully map it again=
.. And what's worse is that C++... makes this <i>impossible</i> to imple=
ment generally.</div><div><br></div><div>We could do it in our system only =
because our system had a limited, fixed set of types to work with. C++ does=
not. Loading DLL/SOs adds more type indices. And while type indices will b=
e the same for an execution of the code, it will be able to change between =
executions. But you need to be able to store a value in the mapped memory t=
hat contains something that is persistent across executions.</div><div><br>=
</div><div>C++ doesn't have a type identifier like that. So the only wa=
y to make this work is if you know the <i>dynamic</i> type of the object wh=
en you unpack it. And as someone who has used persistence systems of this s=
ort, you generally don't know. And you generally don't <i>want</i> =
to know; if you wanted to know, you wouldn't be using a dynamic type ;)=
<br></div><div><br></div><div>So I don't think that polymorphic type su=
pport would be useful if it were done in an implementable fashion. However,=
even if it was implementable, it could still work based on having an expli=
cit location in code where the lifetime of the old object ends (packing the=
polymorphic object; replacing the vtable pointer with an index) and the li=
fetime of the new begins (unpacking it; replacing the index with a valid vt=
able pointer). Obviously, we would need to create functions to explicitly d=
o so on a per-object basis (as your proposal outlines), but this wouldn'=
;t need to affect the object model. Your "stun" function could be=
said to terminate the object's lifetime, while your "revive"=
function creates it.<br></div><div><br></div><div>So even in this case, th=
e object model itself wouldn't need to be changed. So what use cases ar=
e you trying to support where you <i>need</i> to change the object model?<b=
r></div><div><br></div><div><u>On Mapped Storage Duration:</u><br></div><di=
v><br></div><div>I think "Mapped storage duration" is too on-poin=
t in terms of where the memory comes from, rather than how C++ thinks of it=
..<br></div><div><br></div><div>The four storage durations are based on how =
C++ interacts with them. Automatic storage duration objects have storage du=
rations determined entirely by their objects' scopes. Static storage du=
ration objects have their storage duration be the lifetime of the program. =
Thread-local storage duration objects have their storage duration be the li=
fetime of the thread. And dynamic duration objects have their storage durat=
ion determined dynamically: by direct manipulation of the code.<br></div><b=
r><div>But I think there is a deeper misunderstanding here. You are confusi=
ng properties of "storage" with the properties of "storage d=
uration". The former is a thing that an object needs to have in order =
to exist. The latter is a property of an object, determined by the means us=
ed to construct it, and it's purpose is to govern the (default) lifetim=
e of that object.<br></div><div><br></div><div>For example, you can create =
an automatic variable with automatic storage duration. You can then reuse t=
hat variable's storage to create objects with dynamic storage duration.=
By reusing it, you will have ended the lifetime of the automatic variable.=
You would then have an object with dynamic storage duration whose storage =
happens to be on the stack.</div><div><br></div><div>Which is why "sta=
ck storage duration" would be a misnomer, much like "mapped stora=
ge duration".<br></div><div><br></div><div>Of course, C++ has a bunch =
of rules about what happens when you do this and then try to use that varia=
ble's name later on. As well as what happens when that variable goes ou=
t of scope, and what you need to do to make leaving scope not invoke UB (na=
mely, create an object of that type <i>back</i> in that storage).</div><div=
><br></div><div>What you seem to want is simply a wider form of dynamic sto=
rage duration. You're not changing the meaning of the storage duration;=
you're simply adding ways to create/destroy such objects.</div><div><b=
r></div><div><u>On Bless:</u></div><div><br></div><blockquote class=3D"gmai=
l_quote" style=3D"margin: 0px 0px 0px 0.8ex; border-left: 1px solid rgb(204=
, 204, 204); padding-left: 1ex;"><div>P0593 proposed these functions to mar=
k regions as reachable:</div></blockquote><div><br></div><div></div><div>Yo=
ur notion of "reachability" has nothing to do with what P0593 is =
doing. `bless` does not make objects "reachable"; that proposal o=
nly uses the word "reachable" once, and that is merely a descript=
ion about the relationship between a pointer and a size (which really ought=
to be a `std::span<std::byte>`).</div><div><br></div><div>And your d=
efinition of "reachable" here does not map to what P0593 is doing=
.. `std::bless` and equivalent functions declare that a piece of memory <i>c=
reates</i> implicit objects. When you call `bless` on a range of memory, ob=
jects will be created to make your code work. What your "reachable&quo=
t; behavior seems to want is for them to already exist.</div><div><br></div=
><div>This may seem like a trivial difference, but it isn't. <a href=3D=
"http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0593r2.html#type-=
punning">P0593 points it out</a>: when you create objects, the contents of =
the storage are rendered unspecified. And this is just as true for objects =
implicitly created by `std::bless`.</div><div><br></div><div>You explicitly=
don't want this. You want the objects to already exist, with their val=
ues intact. Non-template `bless` can't do that; `bless<T>` can, b=
ut only for a single complete object of a specified type. And even then, it=
is still <i>creating</i> the object, not making it "reachable". =
It wasn't there until `bless<T>` was called.<br></div><div><br></=
div><div></div><div><u>On Focus:</u></div><div><br></div><div>Your section =
3.2 has nothing to do with the C++ object model or the needs of mapping mem=
ory. That's not to say that they aren't useful, but the presence of=
side effects is orthogonal to memory mapping or the object model.<br></div=
></div>
<p></p>
-- <br />
You received this message because you are subscribed to the Google Groups &=
quot;ISO C++ Standard - Future Proposals" group.<br />
To unsubscribe from this group and stop receiving emails from it, send an e=
mail to <a href=3D"mailto:std-proposals+unsubscribe@isocpp.org">std-proposa=
ls+unsubscribe@isocpp.org</a>.<br />
To post to this group, send email to <a href=3D"mailto:std-proposals@isocpp=
..org">std-proposals@isocpp.org</a>.<br />
To view this discussion on the web visit <a href=3D"https://groups.google.c=
om/a/isocpp.org/d/msgid/std-proposals/c05b4e09-4c87-4d06-9061-4c101b37fa2f%=
40isocpp.org?utm_medium=3Demail&utm_source=3Dfooter">https://groups.google.=
com/a/isocpp.org/d/msgid/std-proposals/c05b4e09-4c87-4d06-9061-4c101b37fa2f=
%40isocpp.org</a>.<br />
------=_Part_47_1180964524.1533173502710--
------=_Part_46_53266908.1533173502709--
.
Author: Arthur O'Dwyer <arthur.j.odwyer@gmail.com>
Date: Wed, 1 Aug 2018 18:50:41 -0700 (PDT)
Raw View
------=_Part_47_1882706862.1533174641464
Content-Type: multipart/alternative;
boundary="----=_Part_48_1775096101.1533174641465"
------=_Part_48_1775096101.1533174641465
Content-Type: text/plain; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable
On Wednesday, August 1, 2018 at 11:01:23 AM UTC-7, Niall Douglas wrote:
>
> So assuming people were put off by the length of the paper
>
That's a good assumption. :)
This is being dealt with in [P0593] Implicit creation of objects for=20
> low-level object manipulation. Amongst its many recommendations, it makes=
=20
> unconstructed memory unreachable by default. You can make it reachable by=
=20
> (i) constructing objects into it (ii) via the proposed std::bless() which=
=20
> tells the compiler that a region of unreachable memory (i.e. unconstructe=
d)=20
> is now reachable (and contains alive trivial objects of some form).
>
Nit: for "trivial", read "implicit-lifetime", right?
[...]
> Mapped storage has the most similarity to dynamic storage, but with the=
=20
> following differences:=20
>
> - It has allocation and alignment granularities with architecture=20
> specific coarse sizes (=E2=80=98memory page=E2=80=99). 4Kb/2Mb/1Gb pag=
e sizes are common.=20
> - New allocations are guaranteed to be all bits zero on creation.=20
> - It has map on first read semantics. This means that the first read=
=20
> from a memory page can take hundreds of CPU cycles, and a TLB shootdow=
n=20
> causes an interrupt for other CPUs.=20
> - It has allocate on first write semantics. This means that the first=
=20
> write to a memory page can take thousands or even hundreds of thousand=
s of=20
> CPU cycles, plus a TLB shootdown.=20
> - Usually, but not always, mapped storage is a memory cache of=20
> equivalent storage a high latency storage device. Hence they can be=20
> individually pushed to storage, their contents thrown away, deallocate=
d,=20
> given different access permission or caching strategies, and lots of o=
ther=20
> interesting (i.e. potentially game changing for large STL containers)=
=20
> operations. Individual pages can be:=20
> - Read-only, read-write, or copy-on-write. These are self=20
> describing, and these are hard characteristics: violating them mean=
s=20
> program failure.=20
> - Committed or uncommitted. This indicates whether the page counts=
=20
> towards the resources used by the C++ program. Uncommitted memory i=
s=20
> inaccessible, and acts as a placeholder for later use (i.e. it is r=
eserved=20
> address space, useful for expanding large arrays without content co=
pying).=20
> - Dirty or clean. This indicates whether the page contains data not=
=20
> yet mirrored onto its backing storage.=20
> - Allocated or unallocated. This indicates whether storage backing=
=20
> the page has been allocated on the storage device. If not, the firs=
t write=20
> to a clean page may be very expensive as the page may need to be co=
pied by=20
> the kernel and/or space allocated for it on the backing storage dev=
ice.
> =20
> The storage represented by a section_handle instance can be mapped into=
=20
> (i.e. made available to) a C++ program by creating a map_handle sourcing=
=20
> the storage from a section_handle instance. The storage mapped by the low=
=20
> level map_handle shall represent unconstructed and unreachable memory,=20
> and will require the use of std::bless() or map_view to make it reachable=
=20
> (alternatively, use the convenience class mapped on a section_handle inst=
ance=20
> which bundles the aforementioned low level operations on your behalf).
>
My hot take is that this is waaay too low-level and implementation-detaily=
=20
for a WG21 proposal. C++ programs have to be able to run on hardware that=
=20
might not even have the concept of a "TLB". The stuff in this bulleted=20
list is perfectly reasonable internal documentation for an *implementation*=
,=20
but it seems like the sort of thing that ought to be abstracted away from=
=20
everyday C++ programmers as much as possible.
I would want the trajectory to be:
- implement this stuff
- implement something higher-level and suitably abstract on top of this=20
stuff
- consider how to standardize that abstract interface
- don't bother to mention the low-level details in that proposal (but have=
=20
the reference implementation available for vendors to look at)
I'm sure I'm ill-informed and there will be apparently good reasons not to=
=20
take this trajectory, but it's still what I would *want* to see as an=20
end-user.
(And yes, I wish the Networking TS would have taken the same trajectory.)
3.1.3 A new lifetime stage: unreachable
>
> I propose adding a new separate lifetime status for objects: unreachable.=
=20
> Under P0593, unconstructed objects are unreachable, but there is no=20
> possibility for a partially constructed, alive or partially destructed=20
> object to be unreachable. I propose that this new status ought to be adde=
d=20
> [...]
>
The paper may answer this question, but: Is this related at all to the=20
existing garbage-collection hooks,=20
http://eel.is/c++draft/util.dynamic.safety, which already define=20
terminology such as "declare reachable"?
P0593 proposed these functions to mark regions as reachable:=20
>
>
> // Requires: [start, (char*)start + length) denotes a region of allocated=
=20
> // storage that is a subset of the region of storage reachable through=20
> start.=20
> // Effects: implicitly creates objects within the denoted region.=20
> void std::bless(void *start, size_t length);=20
> =20
> // Effects: create an object of implicit lifetype type T in the storage=
=20
> // pointed to by T, while preserving the object representation.=
=20
> template<typename T> T *std::bless(void *p);
>
Hooray! For purposes of P1144 relocatability, btw, I am hoping that the=20
words "implicit-lifetime" will be removed from the above comments. My=20
understanding of "implicit-lifetime" is that implicit-lifetime types are=20
precisely those types which it would be safe to access even *without* a=20
call to std::bless. However, I am out of the loop.
Thus the obvious corrollary for marking regions as unreachable:
>
>
> // Requires: [start, (char*)start + length) denotes a region of allocated=
=20
> // storage that is a subset of the region of storage reachable through=20
> start.=20
> // Effects: implicitly uncreates objects within the denoted region.=20
> void std::unbless(void *start, size_t length);=20
> =20
> // Effects: uncreate an object of implicit lifetype type T in the storage=
=20
> // pointed to by T, while preserving the object representation.=
=20
> template<typename T> void *std::unbless(T *p);=20
>
Nit: The template version should return `void` not `void*`, right?
I also don't fully understand the desired semantics of the non-template=20
version of either `bless` or `unbless`.
=20
> It should be emphasised that similarly to blessing, unblessing of trivial=
=20
> types causes no code emission. It simply tells the compiler what is now=
=20
> unreachable.
>
+1.
[...]
> I=E2=80=99m going to err on the side of caution, and propose that the pol=
ymorphic=20
> editions of bless and unbless shall be:
>
>
> // Effects: create an object of implicit lifetype type T in the storage=
=20
> // pointed to by T, while preserving the object representation=
=20
> // apart from any necessary changes to make any polymorphic=20
> // objects within and including T ready for use.=20
> template<typename T> T *std::revive(void *p);=20
> =20
> // Effects: uncreate an object of implicit lifetype type T in the storage=
=20
> // pointed to by T, while preserving the object representation=
=20
> // apart from any necessary changes to make any polymorphic=20
> // functions within the objects within and including T unusable.=
=20
> template<typename T> void *std::stun(T *p);
>
These names are clever (really! I like them!) but they don't convey the=20
semantics at all. "Revive" looks like it should be concerned with=20
resurrecting a previously killed object, not just blessing a non-object in=
=20
a slightly different way. This will of course get bikeshedded anyway, but=
=20
it strikes me that it might at least be *fun* to look at other words with=
=20
religious overtones <https://en.wikipedia.org/wiki/Dar=C5=9Bana>. Back in=
=20
reality, though, what's wrong with "polymorphic_bless"?
And I don't understand why you need "stun / polymorphic_unbless" at all.=20
What does it even mean to make a polymorphic object "unusable"? How is that=
=20
different from just doing nothing at all?
And if "revive / polymorphic_bless" is not a no-op but actually *does* have=
=20
an observable effect on memory, that's a big deal; you should emphasize=20
that somehow.
Sorry, gotta run now; possibly more later.
=E2=80=93Arthur
--=20
You received this message because you are subscribed to the Google Groups "=
ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an e=
mail to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp=
..org/d/msgid/std-proposals/f4eff39b-649f-4d8a-a087-1734c2a304c0%40isocpp.or=
g.
------=_Part_48_1775096101.1533174641465
Content-Type: text/html; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable
<div dir=3D"ltr">On Wednesday, August 1, 2018 at 11:01:23 AM UTC-7, Niall D=
ouglas wrote:<blockquote class=3D"gmail_quote" style=3D"margin: 0;margin-le=
ft: 0.8ex;border-left: 1px #ccc solid;padding-left: 1ex;"><div dir=3D"ltr">=
So assuming people were put off by the length of the paper</div></blockquot=
e><div><br></div><div>That's a good assumption. :)</div><div><br></div>=
<blockquote class=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.8ex;bor=
der-left: 1px #ccc solid;padding-left: 1ex;"><div dir=3D"ltr"><div><p>This =
is being dealt with in <span>[<a>P0593</a>]</span> <span>Implicit creation =
of objects for low-level object manipulation</span>. Amongst its many recom=
mendations, it makes unconstructed memory <span>unreachable </span>by defau=
lt. You can make it reachable by (i) constructing objects into it (ii) via =
the proposed <span>std::bless() </span>which tells the compiler that a regi=
on of unreachable memory (i.e. unconstructed) is now reachable (and contain=
s alive trivial objects of some form).</p></div></div></blockquote><div><br=
></div><div>Nit: for "trivial", read "implicit-lifetime"=
;, right?</div><div><br></div><div>[...]</div><blockquote class=3D"gmail_qu=
ote" style=3D"margin: 0;margin-left: 0.8ex;border-left: 1px #ccc solid;padd=
ing-left: 1ex;"><div dir=3D"ltr"><div><p>Mapped storage has the most simila=
rity to dynamic storage, but with the following differences: </p><ul><=
li>It has allocation and alignment granularities with architecture specific=
coarse sizes (=E2=80=98memory page=E2=80=99). 4Kb/2Mb/1Gb page sizes a=
re common. </li><li>New allocations are guaranteed to be all bits zero=
on creation. </li><li>It has map on first read semantics. This means =
that the first read from a memory page can take hundreds of CPU cycles,=
and a TLB shootdown causes an interrupt for other CPUs. </li><li>It h=
as allocate on first write semantics. This means that the first write to a =
memory page can take thousands or even hundreds of thousands of CPU cyc=
les, plus a TLB shootdown. </li><li>Usually, but not always, mapped st=
orage is a memory cache of equivalent storage a high latency storage de=
vice. Hence they can be individually pushed to storage, their contents thro=
wn away, deallocated, given different access permission or caching stra=
tegies, and lots of other interesting (i.e. potentially game changing f=
or large STL containers) operations. Individual pages can be: =
<ul><li>Read-only, read-write, or copy-on-write. These are self describing=
, and these are hard characteristics: violating them means program=
failure. </li><li>Committed or uncommitted. This indicates =
whether the page counts towards the resources used by the C++=
program. Uncommitted memory is inaccessible, and acts as a placeh=
older for later use (i.e. it is reserved address space, useful for expandin=
g large arrays without content copying). </li><li>Dirty =
or clean. This indicates whether the page contains data not yet mirrored on=
to its backing storage. </li><li>Allocated or unallocate=
d. This indicates whether storage backing the page has been alloca=
ted on the storage device. If not, the first write to a clean page may be v=
ery expensive as the page may need to be copied by the kernel and/=
or space allocated for it on the backing storage device.</li></ul>=
</li></ul><p>The storage represented by a <span>section_handle </span>insta=
nce can be <span>mapped </span>into (i.e. made available to) a C++ program =
by creating a <span>map_handle </span>sourcing the storage from a <span>sec=
tion_handle </span>instance. The storage mapped by the low level <span>map_=
handle </span>shall represent unconstructed and unreachable memory, and wil=
l =
=
require the use of <span>std::bless() </span>=
or <span>map_view </span>to make it reachable (alternatively, use the conve=
nience class <span>mapped </span>on a <span>section_handle </span>instance =
which bundles the aforementioned low level operations on your behalf).</p><=
/div></div></blockquote><div><br></div><div>My hot take is that this is waa=
ay too low-level and implementation-detaily for a WG21 proposal. C++ progra=
ms have to be able to run on hardware that might not even have the concept =
of a "TLB". =C2=A0The stuff in this bulleted list is perfectly re=
asonable internal documentation for an <i>implementation</i>, but it seems =
like the sort of thing that ought to be abstracted away from everyday C++ p=
rogrammers as much as possible.</div><div>I would want the trajectory to be=
:</div><div>- implement this stuff</div><div>- implement something higher-l=
evel and suitably abstract on top of this stuff</div><div>- consider how to=
standardize that abstract interface</div><div>- don't bother to mentio=
n the low-level details in that proposal (but have the reference implementa=
tion available for vendors to look at)</div><div><br></div><div>I'm sur=
e I'm ill-informed and there will be apparently good reasons not to tak=
e this trajectory, but it's still what I would <i>want</i> to see as an=
end-user.</div><div>(And yes, I wish the Networking TS would have taken th=
e same trajectory.)</div><div><br></div><div><br></div><blockquote class=3D=
"gmail_quote" style=3D"margin: 0;margin-left: 0.8ex;border-left: 1px #ccc s=
olid;padding-left: 1ex;"><div dir=3D"ltr"><p><span style=3D"font-size: 16px=
;">3.1.3 </span><a style=3D"font-size: 16px;"></a><span style=3D"font-siz=
e: 16px;">A new lifetime stage: </span><span style=3D"font-size: 16px;">unr=
eachable</span><br></p><p>I propose adding a new separate lifetime status f=
or objects: <span>unreachable</span>. Under P0593, unconstructed objects ar=
e unreachable, but there is no possibility for a partially constructed, ali=
ve or partially destructed object to be unreachable. I propose that this ne=
w status ought to be added [...]</p></div></blockquote><div><br></div><div>=
The paper may answer this question, but: Is this related at all to the exis=
ting garbage-collection hooks,=C2=A0<a href=3D"http://eel.is/c++draft/util.=
dynamic.safety">http://eel.is/c++draft/util.dynamic.safety</a>, which alrea=
dy define terminology such as "declare reachable"?</div><div><br>=
</div><blockquote class=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.8=
ex;border-left: 1px #ccc solid;padding-left: 1ex;"><div dir=3D"ltr"><div><p=
>P0593 proposed these functions to mark regions as reachable: </p><p><br></=
p><div><div style=3D"background-color:rgb(250,250,250);border-color:rgb(187=
,187,187);border-style:solid;border-width:1px;word-wrap:break-word"><code><=
div><span style=3D"color:#800">// Requires: [start, (char*)start + length) =
denotes a region of allocated </span><span style=3D"color:#000"><br></span>=
<span style=3D"color:#800">// storage that is a subset of the region of sto=
rage reachable through start. </span><span style=3D"color:#000"><br></span>=
<span style=3D"color:#800">// Effects: implicitly creates objects within th=
e denoted region. </span><span style=3D"color:#000"><br></span><span style=
=3D"color:#008">void</span><span style=3D"color:#000"> std</span><span styl=
e=3D"color:#660">::</span><span style=3D"color:#000">bless</span><span styl=
e=3D"color:#660">(</span><span style=3D"color:#008">void</span><span style=
=3D"color:#000"> </span><span style=3D"color:#660">*</span><span style=3D"c=
olor:#000">start</span><span style=3D"color:#660">,</span><span style=3D"co=
lor:#000"> size_t length</span><span style=3D"color:#660">);</span><span st=
yle=3D"color:#000"> <br>=C2=A0<br></span><span style=3D"color:#800">// Effe=
cts: create an object of implicit lifetype type T in the storage </span><sp=
an style=3D"color:#000"><br></span><span style=3D"color:#800">// =C2=A0 =C2=
=A0 =C2=A0 =C2=A0 =C2=A0pointed to by T, while preserving the object repres=
entation. </span><span style=3D"color:#000"><br></span><span style=3D"color=
:#008">template</span><span style=3D"color:#660"><</span><span style=3D"=
color:#008">typename</span><span style=3D"color:#000"> T</span><span style=
=3D"color:#660">></span><span style=3D"color:#000"> T </span><span style=
=3D"color:#660">*</span><span style=3D"color:#000">std</span><span style=3D=
"color:#660">::</span><span style=3D"color:#000">bless</span><span style=3D=
"color:#660">(</span><span style=3D"color:#008">void</span><span style=3D"c=
olor:#000"> </span><span style=3D"color:#660">*</span><span style=3D"color:=
#000">p</span><span style=3D"color:#660">);</span></div></code></div></div>=
</div></div></blockquote><div><br></div><div>Hooray! =C2=A0For purposes of =
P1144 relocatability, btw, I am hoping that the words "implicit-lifeti=
me" will be removed from the above comments. =C2=A0My understanding of=
"implicit-lifetime" is that implicit-lifetime types are precisel=
y those types which it would be safe to access even <i><b>without</b></i> a=
call to std::bless. =C2=A0However, I am out of the loop.</div><div><br></d=
iv><div><br></div><blockquote class=3D"gmail_quote" style=3D"margin: 0;marg=
in-left: 0.8ex;border-left: 1px #ccc solid;padding-left: 1ex;"><div dir=3D"=
ltr"><div><span></span> </div><div>Thus the obvious corrollary for marking =
regions as unreachable:<br></div><p><br></p><div style=3D"background-color:=
rgb(250,250,250);border-color:rgb(187,187,187);border-style:solid;border-wi=
dth:1px;word-wrap:break-word"><code><div><span style=3D"color:#800">// Requ=
ires: [start, (char*)start + length) denotes a region of allocated </span><=
span style=3D"color:#000"><br></span><span style=3D"color:#800">// storage =
that is a subset of the region of storage reachable through start. </span><=
span style=3D"color:#000"><br></span><span style=3D"color:#800">// Effects:=
implicitly uncreates objects within the denoted region. </span><span style=
=3D"color:#000"><br></span><span style=3D"color:#008">void</span><span styl=
e=3D"color:#000"> std</span><span style=3D"color:#660">::</span><span style=
=3D"color:#000">unbless</span><span style=3D"color:#660">(</span><span styl=
e=3D"color:#008">void</span><span style=3D"color:#000"> </span><span style=
=3D"color:#660">*</span><span style=3D"color:#000">start</span><span style=
=3D"color:#660">,</span><span style=3D"color:#000"> size_t length</span><sp=
an style=3D"color:#660">);</span><span style=3D"color:#000"> <br>=C2=A0<br>=
</span><span style=3D"color:#800">// Effects: uncreate an object of implici=
t lifetype type T in the storage </span><span style=3D"color:#000"><br></sp=
an><span style=3D"color:#800">// =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0pointed =
to by T, while preserving the object representation. </span><span style=3D"=
color:#000"><br></span><span style=3D"color:#008">template</span><span styl=
e=3D"color:#660"><</span><span style=3D"color:#008">typename</span><span=
style=3D"color:#000"> T</span><span style=3D"color:#660">></span><span =
style=3D"color:#000"> </span><span style=3D"color:#008">void</span><span st=
yle=3D"color:#000"> </span><span style=3D"color:#660">*</span><span style=
=3D"color:#000">std</span><span style=3D"color:#660">::</span><span style=
=3D"color:#000">unbless</span><span style=3D"color:#660">(</span><span styl=
e=3D"color:#000">T </span><span style=3D"color:#660">*</span><span style=3D=
"color:#000">p</span><span style=3D"color:#660">);</span><span style=3D"col=
or:#000">=C2=A0</span></div></code></div></div></blockquote><div><br></div>=
<div>Nit: The template version should return `void` not `void*`, right?</di=
v><div>I also don't fully understand the desired semantics of the non-t=
emplate version of either `bless` or `unbless`.</div><div>=C2=A0</div><bloc=
kquote class=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.8ex;border-l=
eft: 1px #ccc solid;padding-left: 1ex;"><div dir=3D"ltr"><div>It should be =
emphasised that similarly to blessing, unblessing of trivial types causes n=
o code emission. It simply tells the compiler what is now unreachable.<br><=
/div></div></blockquote><div><br></div><div>+1.</div><div><br></div><div>[.=
...]</div><blockquote class=3D"gmail_quote" style=3D"margin: 0;margin-left: =
0.8ex;border-left: 1px #ccc solid;padding-left: 1ex;"><div dir=3D"ltr"><div=
></div><p>I=E2=80=99m going to err on the side of caution, and propose that=
the polymorphic editions of bless and unbless shall be:<br></p><p><br></p>=
<div><div style=3D"background-color:rgb(250,250,250);border-color:rgb(187,1=
87,187);border-style:solid;border-width:1px;word-wrap:break-word"><code><di=
v><span style=3D"color:#800">// Effects: create an object of implicit lifet=
ype type T in the storage </span><span style=3D"color:#000"><br></span><spa=
n style=3D"color:#800">// =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0pointed to by T=
, while preserving the object representation </span><span style=3D"color:#0=
00"><br></span><span style=3D"color:#800">// =C2=A0 =C2=A0 =C2=A0 =C2=A0 =
=C2=A0apart from any necessary changes to make any polymorphic </span><span=
style=3D"color:#000"><br></span><span style=3D"color:#800">// =C2=A0 =C2=
=A0 =C2=A0 =C2=A0 =C2=A0objects within and including T ready for use. </spa=
n><span style=3D"color:#000"><br></span><span style=3D"color:#008">template=
</span><span style=3D"color:#660"><</span><span style=3D"color:#008">typ=
ename</span><span style=3D"color:#000"> T</span><span style=3D"color:#660">=
></span><span style=3D"color:#000"> T </span><span style=3D"color:#660">=
*</span><span style=3D"color:#000">std</span><span style=3D"color:#660">::<=
/span><span style=3D"color:#000">revive</span><span style=3D"color:#660">(<=
/span><span style=3D"color:#008">void</span><span style=3D"color:#000"> </s=
pan><span style=3D"color:#660">*</span><span style=3D"color:#000">p</span><=
span style=3D"color:#660">);</span><span style=3D"color:#000"> <br>=C2=A0<b=
r></span><span style=3D"color:#800">// Effects: uncreate an object of impli=
cit lifetype type T in the storage </span><span style=3D"color:#000"><br></=
span><span style=3D"color:#800">// =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0pointe=
d to by T, while preserving the object representation </span><span style=3D=
"color:#000"><br></span><span style=3D"color:#800">// =C2=A0 =C2=A0 =C2=A0 =
=C2=A0 =C2=A0apart from any necessary changes to make any polymorphic </spa=
n><span style=3D"color:#000"><br></span><span style=3D"color:#800">// =C2=
=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0functions within the objects within and incl=
uding T unusable. </span><span style=3D"color:#000"><br></span><span style=
=3D"color:#008">template</span><span style=3D"color:#660"><</span><span =
style=3D"color:#008">typename</span><span style=3D"color:#000"> T</span><sp=
an style=3D"color:#660">></span><span style=3D"color:#000"> </span><span=
style=3D"color:#008">void</span><span style=3D"color:#000"> </span><span s=
tyle=3D"color:#660">*</span><span style=3D"color:#000">std</span><span styl=
e=3D"color:#660">::</span><span style=3D"color:#000">stun</span><span style=
=3D"color:#660">(</span><span style=3D"color:#000">T </span><span style=3D"=
color:#660">*</span><span style=3D"color:#000">p</span><span style=3D"color=
:#660">);</span></div></code></div></div></div></blockquote><div><br></div>=
<div>These names are clever (really! I like them!) but they don't conve=
y the semantics at all. "Revive" looks like it should be concerne=
d with resurrecting a previously killed object, not just blessing a non-obj=
ect in a slightly different way. =C2=A0This will of course get bikeshedded =
anyway, but it strikes me that it might at least be <i>fun</i> to look at <=
a href=3D"https://en.wikipedia.org/wiki/Dar=C5=9Bana">other words with reli=
gious overtones</a>. =C2=A0Back in reality, though, what's wrong with &=
quot;polymorphic_bless"?</div><div><br></div><div>And I don't unde=
rstand why you need "stun / polymorphic_unbless" at all. What doe=
s it even mean to make a polymorphic object "unusable"? How is th=
at different from just doing nothing at all?</div><div><br></div><div>And i=
f "revive / polymorphic_bless" is not a no-op but actually <i>doe=
s</i> have an observable effect on memory, that's a big deal; you shoul=
d emphasize that somehow.</div><div><br></div><div>Sorry, gotta run now; po=
ssibly more later.</div><div>=E2=80=93Arthur</div></div>
<p></p>
-- <br />
You received this message because you are subscribed to the Google Groups &=
quot;ISO C++ Standard - Future Proposals" group.<br />
To unsubscribe from this group and stop receiving emails from it, send an e=
mail to <a href=3D"mailto:std-proposals+unsubscribe@isocpp.org">std-proposa=
ls+unsubscribe@isocpp.org</a>.<br />
To post to this group, send email to <a href=3D"mailto:std-proposals@isocpp=
..org">std-proposals@isocpp.org</a>.<br />
To view this discussion on the web visit <a href=3D"https://groups.google.c=
om/a/isocpp.org/d/msgid/std-proposals/f4eff39b-649f-4d8a-a087-1734c2a304c0%=
40isocpp.org?utm_medium=3Demail&utm_source=3Dfooter">https://groups.google.=
com/a/isocpp.org/d/msgid/std-proposals/f4eff39b-649f-4d8a-a087-1734c2a304c0=
%40isocpp.org</a>.<br />
------=_Part_48_1775096101.1533174641465--
------=_Part_47_1882706862.1533174641464--
.
Author: Arthur O'Dwyer <arthur.j.odwyer@gmail.com>
Date: Wed, 1 Aug 2018 20:20:20 -0700 (PDT)
Raw View
------=_Part_58_873843617.1533180020518
Content-Type: multipart/alternative;
boundary="----=_Part_59_877306850.1533180020519"
------=_Part_59_877306850.1533180020519
Content-Type: text/plain; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable
On Wednesday, August 1, 2018 at 6:31:42 PM UTC-7, Nicol Bolas wrote:
>
>
> For example, if you want to expand from Implicit Lifetime types to=20
> Trivially Relocatable types, then the object model need not be changed=20
> (save for the changes needed to make Trivially Relocatable work). You can=
=20
> still define it as destroying the old object and manifesting a new one.
>
> However, because such types are more than just their values, this is of=
=20
> limited value. `unique_ptr<T>` is the quintessential Trivially Relocatabl=
e=20
> example. But would it be a good thing to stick in a memory mapped object?=
I=20
> don't think so. If the object it points to is not in the same memory map,=
=20
> then it is not reasonable to persist the object across executions, since=
=20
> that object may well be long dead.
>
> And if `unique_ptr<T>` does point to an object in the same map, then its=
=20
> address will have to be updated before you can actually use it. That mean=
s=20
> you can't just cast and go; you have to do some work between mapping the=
=20
> memory and using it. Given that... what's the point of storing it in a=20
> memory map directly at all? Because it's part of some other object you do=
=20
> want to store there?
>
+1 to all of the above. "Relocatable" in the sense of "moveable-around=20
within the same process" is almost entirely orthogonal to=20
"position-independent" in the sense of "positionable-arbitrarily within the=
=20
same process", which in turn is just a small part of "shareable" in the=20
sense of "positionable-arbitrarily simultaneously in multiple processes=20
with potentially different code segments."
Another nail in the coffin of this contrived unique_ptr example:=20
unique_ptr<T> points to a thing that you're going to `delete` later, which=
=20
depends on the state of the global new/delete heap in the current process =
=E2=80=94=20
there's *no way* that's going to work if the current process isn't the one=
=20
who new'ed the object, right?
I hope that `std::bless` (assuming it works for non-implicit-lifetime=20
types) is good enough to solve the object-lifetime issues raised by=20
"trivially relocatable." Nicol, you seem to have a better handle than I do=
=20
on what exactly these issues are; could I trouble you (elsethread or via=20
email) for feedback on P1144?
=20
> Somethign similar is true of other popular Trivially Relocatable types.=
=20
> `vector`, `fstream`, `any`, etc, all of them *could* be stored there, but=
=20
> the utility of doing so is... dubious.
>
Nit: Unless I am mistaken, `fstream` cannot possibly be trivially=20
relocatable for several reasons. It contains a pointer-to-self (to its=20
streambuf), and it has a virtual destructor (which could do just about=20
anything, and therefore cannot be assumed to do the Right Thing w.r.t.=20
coordinating with the move-constructor). Please correct me if I'm wrong.
(Dissimilarly: `any` is not trivially relocatable on any mainstream=20
implementation, but it can easily be *made* trivially relocatable. I make=
=20
it so under an #ifdef=20
<https://github.com/Quuxplusone/libcxx/blob/trivially-relocatable/include/a=
ny#L142-L146>=20
in my libc++ fork.)
Your proposal suggests allowing polymorphic types. Now with these, the=20
> destroy/recreate trick becomes tricky. [...]
>
I would like to be able to use a single mental model to explain the=20
behavior of both
struct A {
virtual void foo();
};
and
struct B {
int (*f_)();
int foo() { return (*f_)(); }
};
What arguments can you provide to convince me that polymorphic `A` should=
=20
be special-cased in some way that does not equally apply to non-polymorphic=
=20
`B`?
=E2=80=93Arthur
--=20
You received this message because you are subscribed to the Google Groups "=
ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an e=
mail to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp=
..org/d/msgid/std-proposals/979722b6-de69-44ff-a0c0-faa4882c467e%40isocpp.or=
g.
------=_Part_59_877306850.1533180020519
Content-Type: text/html; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable
<div dir=3D"ltr">On Wednesday, August 1, 2018 at 6:31:42 PM UTC-7, Nicol Bo=
las wrote:<blockquote class=3D"gmail_quote" style=3D"margin: 0;margin-left:=
0.8ex;border-left: 1px #ccc solid;padding-left: 1ex;"><div dir=3D"ltr"><di=
v></div><div><div><br></div></div><div>For=20
example, if you want to expand from Implicit Lifetime types to Trivially
Relocatable types, then the object model need not be changed (save for=20
the changes needed to make Trivially Relocatable work). You can still=20
define it as destroying the old object and manifesting a new one.</div><div=
><br></div><div>However, because such types are more than just their values=
, this is of limited value. `unique_ptr<T>` is the quintessential Tri=
vially Relocatable example. But would it be a good thing to stick in a memo=
ry mapped object? I don't think so. If the object it points to is not i=
n the same memory map, then it is not reasonable to persist the object acro=
ss executions, since that object may well be long dead.</div><div><br></div=
><div>And if `unique_ptr<T>` does point to an object in the same map,=
then its address will have to be updated before you can actually use it. T=
hat means you can't just cast and go; you have to do some work between =
mapping the memory and using it. Given that... what's the point of stor=
ing it in a memory map directly at all? Because it's part of some other=
object you do want to store there?</div></div></blockquote><div><br></div>=
<div>+1 to all of the above. =C2=A0"Relocatable" in the sense of =
"moveable-around within the same process" is almost entirely orth=
ogonal to "position-independent" in the sense of "positionab=
le-arbitrarily within the same process", which in turn is just a small=
part of "shareable" in the sense of "positionable-arbitrari=
ly simultaneously in multiple processes with potentially different code seg=
ments."</div><div>Another nail in the coffin of this contrived unique_=
ptr example: unique_ptr<T> points to a thing that you're going to=
`delete` later, which depends on the state of the global new/delete heap i=
n the current process =E2=80=94 there's <i>no way</i> that's going =
to work if the current process isn't the one who new'ed the object,=
right?</div><div><br></div><div>I hope that `std::bless` (assuming it work=
s for non-implicit-lifetime types) is good enough to solve the object-lifet=
ime issues raised by "trivially relocatable." Nicol, you seem to =
have a better handle than I do on what exactly these issues are; could I tr=
ouble you (elsethread or via email) for feedback on P1144?</div><div><br></=
div><div>=C2=A0</div><blockquote class=3D"gmail_quote" style=3D"margin: 0;m=
argin-left: 0.8ex;border-left: 1px #ccc solid;padding-left: 1ex;"><div dir=
=3D"ltr"><div></div><div>Somethign similar is true of other popular Trivial=
ly Relocatable types. `vector`, `fstream`, `any`, etc, all of them <i>could=
</i> be stored there, but the utility of doing so is... dubious.<br></div><=
/div></blockquote><div><br></div><div>Nit: Unless I am mistaken, `fstream` =
cannot possibly be trivially relocatable for several reasons. It contains a=
pointer-to-self (to its streambuf), and it has a virtual destructor (which=
could do just about anything, and therefore cannot be assumed to do the Ri=
ght Thing w.r.t. coordinating with the move-constructor). =C2=A0Please corr=
ect me if I'm wrong.</div><div><br></div><div>(Dissimilarly: `any` is n=
ot trivially relocatable on any mainstream implementation, but it can easil=
y be <i>made</i> trivially relocatable. I make it so <a href=3D"https://git=
hub.com/Quuxplusone/libcxx/blob/trivially-relocatable/include/any#L142-L146=
">under an #ifdef</a> in my libc++ fork.)</div><div><br></div><div><br></di=
v><blockquote class=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.8ex;b=
order-left: 1px #ccc solid;padding-left: 1ex;"><div dir=3D"ltr"><div>Your p=
roposal suggests allowing polymorphic types. Now with these, the destroy/re=
create trick becomes tricky. [...]</div></div></blockquote><div><br></div><=
div>I would like to be able to use a single mental model to explain the beh=
avior of both</div><div><br></div><div>=C2=A0 =C2=A0 struct A {</div><div>=
=C2=A0 =C2=A0 =C2=A0 =C2=A0 virtual void foo();</div><div>=C2=A0 =C2=A0 };<=
/div><div><br></div><div>and</div><div><br></div><div>=C2=A0 =C2=A0 struct =
B {</div><div>=C2=A0 =C2=A0 =C2=A0 =C2=A0 int (*f_)();</div><div>=C2=A0 =C2=
=A0 =C2=A0 =C2=A0 int foo() { return (*f_)(); }</div><div>=C2=A0 =C2=A0 };<=
/div><div><br></div><div>What arguments can you provide to convince me that=
polymorphic `A` should be special-cased in some way that does not equally =
apply to non-polymorphic `B`?</div><div><br></div><div>=E2=80=93Arthur</div=
></div>
<p></p>
-- <br />
You received this message because you are subscribed to the Google Groups &=
quot;ISO C++ Standard - Future Proposals" group.<br />
To unsubscribe from this group and stop receiving emails from it, send an e=
mail to <a href=3D"mailto:std-proposals+unsubscribe@isocpp.org">std-proposa=
ls+unsubscribe@isocpp.org</a>.<br />
To post to this group, send email to <a href=3D"mailto:std-proposals@isocpp=
..org">std-proposals@isocpp.org</a>.<br />
To view this discussion on the web visit <a href=3D"https://groups.google.c=
om/a/isocpp.org/d/msgid/std-proposals/979722b6-de69-44ff-a0c0-faa4882c467e%=
40isocpp.org?utm_medium=3Demail&utm_source=3Dfooter">https://groups.google.=
com/a/isocpp.org/d/msgid/std-proposals/979722b6-de69-44ff-a0c0-faa4882c467e=
%40isocpp.org</a>.<br />
------=_Part_59_877306850.1533180020519--
------=_Part_58_873843617.1533180020518--
.
Author: Niall Douglas <nialldouglas14@gmail.com>
Date: Thu, 2 Aug 2018 02:12:52 -0700 (PDT)
Raw View
------=_Part_136_1604344411.1533201172970
Content-Type: multipart/alternative;
boundary="----=_Part_137_442654054.1533201172971"
------=_Part_137_442654054.1533201172971
Content-Type: text/plain; charset="UTF-8"
>
>
> So you have this operation you want to be able to do. You want to take
> objects in one address, make them disappear from that address, and then
> make them appear in a (potentially) different address. And you want to
> change the C++ object model to make this work.
>
> Here's a question: do you *need* to change the object model in order to
> get this effect?
>
So, here's what we want to be able to do without UB:
1. Modify where allocated memory appears in the process (use cases:
expanding arrays without memory copies, patching binaries, mapping page
cached files etc)
2. Share a region of memory with another running process (use cases:
IPC, multiprocessing, etc)
3. Reduce C++ causing unnecessary page faults/prevent use of memory with
changed access permissions by making the compiler aware of what memory is
not to be touched.
4. Handle objects written out in a previous incarnation of a program
being at a different address in a new incarnation of a program.
5. Enforce a new read/write reordering constraint separate to compiler
and thread barriers.
In hindsight, I probably ought to have specified this more succinctly in
the paper. I will change it.
There are also multiple levels of persistability going on here:
1. Trivially copyable types, which are just bits. You can use their
in-memory representation directly, just need to constrain write reordering.
2. Trivially relocatable types, which are moved with just bits, if we
standardise those. You can use their in-memory representation directly,
just need to constrain write reordering.
3. Polymorphic types where if it weren't for the vptr, would be either
of the first two categories. You can use their in-memory representation
directly, bar the vptr restamp on load.
4. Other types whose in-memory representation can be used directly after
some trivial transformation e.g. replace all pointers with offset pointers.
So the common factor to all these is that their in-memory representation is
sufficient for their persisted representation i.e. no memory copying
needed, but there can be a "phase change" between "active" and "sleeping".
Serialisation is where you need to create new objects which represent a
persisted edition of an in-memory form. That's a whole separate topic, to
be addressed as iostreams v2 after we've nailed down the in-memory stuff
which is a prerequisite as the serialised form of an object is clearly a
persistable object meeting at least the criteria of the above.
>
> If we limit our usage of mappable types to P0593's implicit lifetime
> types, then I don't think we to affect the object model at all. Implicit
> lifetime types are objects which are nothing more than a set of bits, where
> their value representation is their logical functionality. Two objects of
> such types with the same value representation have the same value and are
> distinct only in that they have different addresses.
>
> With such a limitation in place, you could define the mapped memory
> operation as a funny sort of memcpy. It doesn't move the object. Unmapping
> the memory range terminates the lifetime of all objects in that memory. The
> *values* are preserved, but not the objects. You then map the memory at
> potentially new addresses.
>
> Now, you still need a way to create new objects in those addresses with
> their value representations intact (see below for why non-template `bless`
> can't do this). But such a feature does not require substantial changes to
> the object model. It's just a new way to create an object.
>
Oh sure. Trivially copyable objects are almost easy. But I think it unwise
to standardise support for those without considering the bigger picture
first, so we know where we might be going eventually.
>
> To me, the principle advantage of significant changes to the object model
> here would be to allow a larger variety of objects to work. So now we have
> the next question: exactly which types of objects are you trying to
> support? What is the minimum set of features you need to make this
> worthwhile? That will help you define what changes you need to the object
> model, if any.
>
Hopefully the above list answers you.
>
> However, because such types are more than just their values, this is of
> limited value. `unique_ptr<T>` is the quintessential Trivially Relocatable
> example. But would it be a good thing to stick in a memory mapped object? I
> don't think so. If the object it points to is not in the same memory map,
> then it is not reasonable to persist the object across executions, since
> that object may well be long dead.
>
> And if `unique_ptr<T>` does point to an object in the same map, then its
> address will have to be updated before you can actually use it. That means
> you can't just cast and go; you have to do some work between mapping the
> memory and using it. Given that... what's the point of storing it in a
> memory map directly at all? Because it's part of some other object you do
> want to store there?
>
Where I'd like to get to is that there is a proposed framework for types
which don't need full fat serialisation and whose in-memory representation
can be used as-is for persisting, perhaps with some trivial manipulation.
I appreciate I haven't written any of that out yet, but it's hard until I
know if WG21 likes my proposed bless/unbless approach.
>
> Your proposal suggests allowing polymorphic types. Now with these, the
> destroy/recreate trick becomes tricky. The manifesting operation is based
> on the value being undisturbed. But you *need* the value to be disturbed,
> since the location of the vtable pointer may need to be fixed up.
>
> Which brings me to my own personal experience. See, I've been a part of a
> team that actually implemented a "memcpy"-style serialization system that
> *could* handle polymorphic types. This system required two phases:
> serialization and deserialization.
>
> Now admittedly, our system did the serialization part through copying (the
> host architecture and the destination architectures were not the same. And
> I *mean* not the same; in one remarkable case, the compiler for the
> deserialization part used a *different order* for the base classes than
> the serialization compiler). But part of what it did was to put something
> special in the vtable pointer slot: an index that represented which type it
> was. The deserialization code didn't copy anything; it just fixed up vtable
> pointers in-situ by reading the type index.
>
> This is important because one of the things this allowed us to do was to
> deserialize without knowing at compile time what each type was. We didn't
> have to know the dynamic type from outside of the system. We were able to
> use the base class type, and the system would know how that the pointer
> actually pointed to a more derived class and therefore adjust the vtable
> accordingly.
>
> That's really important, since most polymorphic type usage is based on
> base class types. If you have a polymorphic object in mapped memory, the
> code that wants to retrieve the address probably doesn't know what its
> dynamic type is; it knows the base class type it wants.
>
> And that's where things get tricky. Because now, we're not talking about
> some kind of memory barrier. You're talking about having to perform a
> process on *each* object in a region of memory before the unmap process
> can be considered complete. Then you have to perform a counter-process on
> each object afterwards to fully map it again. And what's worse is that
> C++... makes this *impossible* to implement generally.
>
So, firstly I'm not proposing at this time any generalised type discovery
mechanism. That's for a future serialisation layer. Under what I've
proposed, the program by definition always knows the types of objects in
mapped storage. So restoring any vptr is trivially easy.
Secondly, it would be UB under this limited scope of proposal for any
program other than the constructing program to use any objects in mapped
storage. So, if there is some shared memory, only instances of the
constructing program can legally use it.
Down the line we'll come back to that, but for now, let's not go there yet.
>
> We could do it in our system only because our system had a limited, fixed
> set of types to work with. C++ does not. Loading DLL/SOs adds more type
> indices. And while type indices will be the same for an execution of the
> code, it will be able to change between executions. But you need to be able
> to store a value in the mapped memory that contains something that is
> persistent across executions.
>
> C++ doesn't have a type identifier like that. So the only way to make this
> work is if you know the *dynamic* type of the object when you unpack it.
> And as someone who has used persistence systems of this sort, you generally
> don't know. And you generally don't *want* to know; if you wanted to
> know, you wouldn't be using a dynamic type ;)
>
All grist for any full serialisation layer.
I'm coming at this from a much lower level. Get the basics in place first.
Reduce the problem down to a manageable subset.
>
> So I don't think that polymorphic type support would be useful if it were
> done in an implementable fashion. However, even if it was implementable, it
> could still work based on having an explicit location in code where the
> lifetime of the old object ends (packing the polymorphic object; replacing
> the vtable pointer with an index) and the lifetime of the new begins
> (unpacking it; replacing the index with a valid vtable pointer). Obviously,
> we would need to create functions to explicitly do so on a per-object basis
> (as your proposal outlines), but this wouldn't need to affect the object
> model. Your "stun" function could be said to terminate the object's
> lifetime, while your "revive" function creates it.
>
First I disagree that storing objects in storage of any form is not useful.
What is the difference between mapped storage and dynamic storage? Merely
duration.
I agree that the nomenclature is confusing. There seems to be fuzziness in
the standard regarding object lifetime. I know Richard and SG12 are still
in the process of fixing that. But I find it a bit confusing, and you can
see that in my proposal text.
>
> I think "Mapped storage duration" is too on-point in terms of where the
> memory comes from, rather than how C++ thinks of it.
>
> The four storage durations are based on how C++ interacts with them.
> Automatic storage duration objects have storage durations determined
> entirely by their objects' scopes. Static storage duration objects have
> their storage duration be the lifetime of the program. Thread-local storage
> duration objects have their storage duration be the lifetime of the thread.
> And dynamic duration objects have their storage duration determined
> dynamically: by direct manipulation of the code.
>
Sure. And I propose that mapped storage duration has the lifetime of its
backing filesystem entity. Same approach.
>
> But I think there is a deeper misunderstanding here. You are confusing
> properties of "storage" with the properties of "storage duration". The
> former is a thing that an object needs to have in order to exist. The
> latter is a property of an object, determined by the means used to
> construct it, and it's purpose is to govern the (default) lifetime of that
> object.
>
> For example, you can create an automatic variable with automatic storage
> duration. You can then reuse that variable's storage to create objects with
> dynamic storage duration. By reusing it, you will have ended the lifetime
> of the automatic variable. You would then have an object with dynamic
> storage duration whose storage happens to be on the stack.
>
> Which is why "stack storage duration" would be a misnomer, much like
> "mapped storage duration".
>
Would "filesystem storage duration" be better?
I originally discounted that naming because there are mapped storage
durations whose backing filesystem entity is the swap file/kernel, so I
felt mentioning filesystem would be confusing.
>
> Of course, C++ has a bunch of rules about what happens when you do this
> and then try to use that variable's name later on. As well as what happens
> when that variable goes out of scope, and what you need to do to make
> leaving scope not invoke UB (namely, create an object of that type *back*
> in that storage).
>
> What you seem to want is simply a wider form of dynamic storage duration.
> You're not changing the meaning of the storage duration; you're simply
> adding ways to create/destroy such objects.
>
It's more I'm looking for a storage duration which exceeds the duration of
the program, but still has a well defined duration. Dynamic storage can't
do that.
>
> *On Bless:*
>
> P0593 proposed these functions to mark regions as reachable:
>>
>
> Your notion of "reachability" has nothing to do with what P0593 is doing.
> `bless` does not make objects "reachable"; that proposal only uses the word
> "reachable" once, and that is merely a description about the relationship
> between a pointer and a size (which really ought to be a
> `std::span<std::byte>`).
>
> And your definition of "reachable" here does not map to what P0593 is
> doing. `std::bless` and equivalent functions declare that a piece of memory
> *creates* implicit objects. When you call `bless` on a range of memory,
> objects will be created to make your code work. What your "reachable"
> behavior seems to want is for them to already exist.
>
Eh ... you're right that I find blessing underspecified. This is that
fuzziness about lifetime I mentioned earlier. I've spent a good few weeks
pondering P0593, indeed I asked a ton of questions about it as the SG12
meeting at Rapperswil. And I'm confused about what it precisely does.
Personally, I find reachable vs unreachable a much less ambiguous
explanation of what it does. And more helpful for compiler writers and
formal verification. But I totally get that it may simply be my
incompetence, and I need to be corrected. I am not a compiler writer after
all.
(Richard Smith, feel free to jump in and correct me now!)
>
> This may seem like a trivial difference, but it isn't. P0593 points it out
> <http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0593r2.html#type-punning>:
> when you create objects, the contents of the storage are rendered
> unspecified. And this is just as true for objects implicitly created by
> `std::bless`.
>
Sure. They become reachable. It says nothing about their content.
>
> You explicitly don't want this. You want the objects to already exist,
> with their values intact. Non-template `bless` can't do that; `bless<T>`
> can, but only for a single complete object of a specified type. And even
> then, it is still *creating* the object, not making it "reachable". It
> wasn't there until `bless<T>` was called.
>
Non template bless doesn't disturb any bytes already there. It is
guaranteed not to do so. So I'm not getting the difference, sorry. Whether
the compiler knows there is an explicit type at some address, or it's some
unspecified array of something unknown but trivial, the common denominator
here is that the specified memory range is now containing alive objects,
where beforehand it might not have been.
This is the difference from std::launder. It requires the memory it
operates upon to contain alive objects beforehand. bless does not require
this.
>
> *On Focus:*
>
> Your section 3.2 has nothing to do with the C++ object model or the needs
> of mapping memory. That's not to say that they aren't useful, but the
> presence of side effects is orthogonal to memory mapping or the object
> model.
>
The paper is full of pieces which will be spun out into separate papers
when they are mature enough.
Thanks for the detailed feedback. It was useful.
Niall
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/f171948c-9c40-4daf-aebb-a221f967d4d6%40isocpp.org.
------=_Part_137_442654054.1533201172971
Content-Type: text/html; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable
<div dir=3D"ltr"><blockquote class=3D"gmail_quote" style=3D"margin: 0;margi=
n-left: 0.8ex;border-left: 1px #ccc solid;padding-left: 1ex;"><div dir=3D"l=
tr"><div><div><br></div><div>So you have this operation you want to be able=
to do. You want to take=20
objects in one address, make them disappear from that address, and then=20
make them appear in a (potentially) different address. And you want to chan=
ge the C++ object model to make this work.</div><div><br></div><div>Here=
9;s a question: do you <i>need</i> to change the object model in order to g=
et this effect?<br></div></div></div></blockquote><div><br></div><div>So, h=
ere's what we want to be able to do without UB:</div><div><ol><li>Modif=
y where allocated memory appears in the process (use cases: expanding array=
s without memory copies, patching binaries, mapping page cached files etc)<=
/li><li>Share a region of memory with another running process (use cases: I=
PC, multiprocessing, etc)</li><li>Reduce C++ causing unnecessary page fault=
s/prevent use of memory with changed access permissions by making the compi=
ler aware of what memory is not to be touched.</li><li>Handle objects writt=
en out in a previous incarnation of a program being at a different address =
in a new incarnation of a program.</li><li>Enforce a new read/write reorder=
ing constraint separate to compiler and thread barriers.</li></ol></div><di=
v>In hindsight, I probably ought to have specified this more succinctly in =
the paper. I will change it.</div><div><br></div><div>There are also multip=
le levels of persistability going on here:</div><div><ol><li>Trivially copy=
able types, which are just bits. You can use their in-memory representation=
directly, just need to constrain write reordering.</li><li>Trivially reloc=
atable types, which are moved with just bits, if we standardise those. You =
can use their in-memory representation directly, just need to constrain wri=
te reordering.</li><li>Polymorphic types where if it weren't for the vp=
tr, would be either of the first two categories. You can use their in-memor=
y representation directly, bar the vptr restamp on load.</li><li>Other type=
s whose in-memory representation can be used directly after some trivial tr=
ansformation e.g. replace all pointers with offset pointers.</li></ol></div=
><div>So the common factor to all these is that their in-memory representat=
ion is sufficient for their persisted representation i.e. no memory copying=
needed, but there can be a "phase change" between "active&q=
uot; and "sleeping".</div><div><br></div><div>Serialisation is wh=
ere you need to create new objects which represent a persisted edition of a=
n in-memory form. That's a whole separate topic, to be addressed as ios=
treams v2 after we've nailed down the in-memory stuff which is a prereq=
uisite as the serialised form of an object is clearly a persistable object =
meeting at least the criteria of the above.</div><div>=C2=A0</div><blockquo=
te class=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.8ex;border-left:=
1px #ccc solid;padding-left: 1ex;"><div dir=3D"ltr"><div><div></div><div><=
br></div><div>If
we limit our usage of mappable types to P0593's implicit lifetime=20
types, then I don't think we to affect the object model at all. Implici=
t lifetime types are objects=20
which are nothing more than a set of bits, where their value=20
representation is their logical functionality. Two objects of such types
with the same value representation have the same value and are distinct
only in that they have different addresses.<br></div><div><br></div>With
such a limitation in place, you could define the mapped memory operation a=
s a=20
funny sort of memcpy. It doesn't move the object. Unmapping the memory =
range terminates the lifetime of all objects in that memory. The <i>values<=
/i> are preserved, but not the objects. You then map the memory at potentia=
lly new addresses.</div><div><br></div><div>Now, you still need a
way to create new objects in those addresses with their value representati=
ons intact (see below for why non-template `bless` can't do this). But =
such a feature does not require substantial changes to the object model. It=
's just a new way to create an object.<br></div></div></blockquote><div=
><br></div><div>Oh sure. Trivially copyable objects are almost easy. But I =
think it unwise to standardise support for those without considering the bi=
gger picture first, so we know where we might be going eventually.</div><di=
v>=C2=A0</div><blockquote class=3D"gmail_quote" style=3D"margin: 0;margin-l=
eft: 0.8ex;border-left: 1px #ccc solid;padding-left: 1ex;"><div dir=3D"ltr"=
><div><br><div>To me, the principle advantage of significant changes to the=
object model here would be to allow a=20
larger variety of objects to work. So now we have the next question: exactl=
y which types of objects are you trying to support? What is the minimum set=
of features you need to make this worthwhile? That will help you define=20
what changes you need to the object model, if any.</div></div></div></block=
quote><div><br></div><div>Hopefully the above list answers you.</div><div>=
=C2=A0</div><blockquote class=3D"gmail_quote" style=3D"margin: 0;margin-lef=
t: 0.8ex;border-left: 1px #ccc solid;padding-left: 1ex;"><div dir=3D"ltr"><=
div><div><br></div></div><div>However, because such types are more than jus=
t their values, this is of limited value. `unique_ptr<T>` is the quin=
tessential Trivially Relocatable example. But would it be a good thing to s=
tick in a memory mapped object? I don't think so. If the object it poin=
ts to is not in the same memory map, then it is not reasonable to persist t=
he object across executions, since that object may well be long dead.=C2=A0=
</div></div></blockquote><blockquote class=3D"gmail_quote" style=3D"margin:=
0;margin-left: 0.8ex;border-left: 1px #ccc solid;padding-left: 1ex;"><div =
dir=3D"ltr"><div><br></div><div>And if `unique_ptr<T>` does point to =
an object in the same map, then its address will have to be updated before =
you can actually use it. That means you can't just cast and go; you hav=
e to do some work between mapping the memory and using it. Given that... wh=
at's the point of storing it in a memory map directly at all? Because i=
t's part of some other object you do want to store there?</div></div></=
blockquote><div><br></div><div>Where I'd like to get to is that there i=
s a proposed framework for types which don't need full fat serialisatio=
n and whose in-memory representation can be used as-is for persisting, perh=
aps with some trivial manipulation.</div><div><br></div><div>I appreciate I=
haven't written any of that out yet, but it's hard until I know if=
WG21 likes my proposed bless/unbless approach.</div><div>=C2=A0</div><bloc=
kquote class=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.8ex;border-l=
eft: 1px #ccc solid;padding-left: 1ex;"><div dir=3D"ltr"><div><br></div><di=
v>Your proposal suggests allowing polymorphic types. Now with these, the de=
stroy/recreate trick becomes tricky. The manifesting operation is based on =
the value being undisturbed. But you <i>need</i> the value to be disturbed,=
since the location of the vtable pointer may need to be fixed up.</div><di=
v><br></div><div>Which brings me to my own personal experience. See, I'=
ve been a part of a team that actually implemented a "memcpy"-sty=
le serialization system that <i>could</i> handle polymorphic types. This sy=
stem required two phases: serialization and deserialization.</div><div><br>=
</div><div>Now admittedly, our system did the serialization part through co=
pying (the host architecture and the destination architectures were not the=
same. And I <i>mean</i> not the same; in one remarkable case, the compiler=
for the deserialization part used a <i>different order</i> for the base cl=
asses than the serialization compiler). But part of what it did was to put =
something special in the vtable pointer slot: an index that represented whi=
ch type it was. The deserialization code didn't copy anything; it just =
fixed up vtable pointers in-situ by reading the type index.</div><div><br><=
/div><div>This is important because one of the things this allowed us to do=
was to deserialize without knowing at compile time what each type was. We =
didn't have to know the dynamic type from outside of the system. We wer=
e able to use the base class type, and the system would know how that the p=
ointer actually pointed to a more derived class and therefore adjust the vt=
able accordingly.</div><div><br></div><div>That's really important, sin=
ce most polymorphic type usage is based on base class types. If you have a =
polymorphic object in mapped memory, the code that wants to retrieve the ad=
dress probably doesn't know what its dynamic type is; it knows the base=
class type it wants.</div><div><br></div><div>And that's where things =
get tricky. Because now, we're not talking about some kind of memory ba=
rrier. You're talking about having to perform a process on <i>each</i> =
object in a region of memory before the unmap process can be considered com=
plete. Then you have to perform a counter-process on each object afterwards=
to fully map it again. And what's worse is that C++... makes this <i>i=
mpossible</i> to implement generally.</div></div></blockquote><div><br></di=
v><div>So, firstly I'm not proposing at this time any generalised type =
discovery mechanism. That's for a future serialisation layer. Under wha=
t I've proposed, the program by definition always knows the types of ob=
jects in mapped storage. So restoring any vptr is trivially easy.</div><div=
><br></div><div>Secondly, it would be UB under this limited scope of propos=
al for any program other than the constructing program to use any objects i=
n mapped storage. So, if there is some shared memory, only instances of the=
constructing program can legally use it.</div><div><br></div><div>Down the=
line we'll come back to that, but for now, let's not go there yet.=
</div><div>=C2=A0</div><blockquote class=3D"gmail_quote" style=3D"margin: 0=
;margin-left: 0.8ex;border-left: 1px #ccc solid;padding-left: 1ex;"><div di=
r=3D"ltr"><div><br></div><div>We could do it in our system only because our=
system had a limited, fixed set of types to work with. C++ does not. Loadi=
ng DLL/SOs adds more type indices. And while type indices will be the same =
for an execution of the code, it will be able to change between executions.=
But you need to be able to store a value in the mapped memory that contain=
s something that is persistent across executions.</div><div><br></div><div>=
C++ doesn't have a type identifier like that. So the only way to make t=
his work is if you know the <i>dynamic</i> type of the object when you unpa=
ck it. And as someone who has used persistence systems of this sort, you ge=
nerally don't know. And you generally don't <i>want</i> to know; if=
you wanted to know, you wouldn't be using a dynamic type ;)<br></div><=
/div></blockquote><div><br></div><div>All grist for any full serialisation =
layer.</div><div><br></div><div>I'm coming at this from a much lower le=
vel. Get the basics in place first. Reduce the problem down to a manageable=
subset.</div><div>=C2=A0</div><blockquote class=3D"gmail_quote" style=3D"m=
argin: 0;margin-left: 0.8ex;border-left: 1px #ccc solid;padding-left: 1ex;"=
><div dir=3D"ltr"><div></div><div><br></div><div>So I don't think that =
polymorphic type support would be useful if it were done in an implementabl=
e fashion. However, even if it was implementable, it could still work based=
on having an explicit location in code where the lifetime of the old objec=
t ends (packing the polymorphic object; replacing the vtable pointer with a=
n index) and the lifetime of the new begins (unpacking it; replacing the in=
dex with a valid vtable pointer). Obviously, we would need to create functi=
ons to explicitly do so on a per-object basis (as your proposal outlines), =
but this wouldn't need to affect the object model. Your "stun"=
; function could be said to terminate the object's lifetime, while your=
"revive" function creates it.<br></div></div></blockquote><div><=
br></div><div>First I disagree that storing objects in storage of any form =
is not useful. What is the difference between mapped storage and dynamic st=
orage? Merely duration.</div><div><br></div><div>I agree that the nomenclat=
ure is confusing. There seems to be fuzziness in the standard regarding obj=
ect lifetime. I know Richard and SG12 are still in the process of fixing th=
at. But I find it a bit confusing, and you can see that in my proposal text=
..</div><div>=C2=A0</div><blockquote class=3D"gmail_quote" style=3D"margin: =
0;margin-left: 0.8ex;border-left: 1px #ccc solid;padding-left: 1ex;"><div d=
ir=3D"ltr"><div></div><div><br></div><div>I think "Mapped storage dura=
tion" is too on-point in terms of where the memory comes from, rather =
than how C++ thinks of it.<br></div><div><br></div><div>The four storage du=
rations are based on how C++ interacts with them. Automatic storage duratio=
n objects have storage durations determined entirely by their objects' =
scopes. Static storage duration objects have their storage duration be the =
lifetime of the program. Thread-local storage duration objects have their s=
torage duration be the lifetime of the thread. And dynamic duration objects=
have their storage duration determined dynamically: by direct manipulation=
of the code.<br></div></div></blockquote><div><br></div><div>Sure. And I p=
ropose that mapped storage duration has the lifetime of its backing filesys=
tem entity. Same approach.</div><div>=C2=A0</div><blockquote class=3D"gmail=
_quote" style=3D"margin: 0;margin-left: 0.8ex;border-left: 1px #ccc solid;p=
adding-left: 1ex;"><div dir=3D"ltr"><div></div><br><div>But I think there i=
s a deeper misunderstanding here. You are confusing properties of "sto=
rage" with the properties of "storage duration". The former =
is a thing that an object needs to have in order to exist. The latter is a =
property of an object, determined by the means used to construct it, and it=
's purpose is to govern the (default) lifetime of that object.<br></div=
><div><br></div><div>For example, you can create an automatic variable with=
automatic storage duration. You can then reuse that variable's storage=
to create objects with dynamic storage duration. By reusing it, you will h=
ave ended the lifetime of the automatic variable. You would then have an ob=
ject with dynamic storage duration whose storage happens to be on the stack=
..</div><div><br></div><div>Which is why "stack storage duration" =
would be a misnomer, much like "mapped storage duration".<br></di=
v></div></blockquote><div><br></div><div>Would "filesystem storage dur=
ation" be better?</div><div><br></div><div>I originally discounted tha=
t naming because there are mapped storage durations whose backing filesyste=
m entity is the swap file/kernel, so I felt mentioning filesystem would be =
confusing.</div><div>=C2=A0</div><blockquote class=3D"gmail_quote" style=3D=
"margin: 0;margin-left: 0.8ex;border-left: 1px #ccc solid;padding-left: 1ex=
;"><div dir=3D"ltr"><div></div><div><br></div><div>Of course, C++ has a bun=
ch of rules about what happens when you do this and then try to use that va=
riable's name later on. As well as what happens when that variable goes=
out of scope, and what you need to do to make leaving scope not invoke UB =
(namely, create an object of that type <i>back</i> in that storage).</div><=
div><br></div><div>What you seem to want is simply a wider form of dynamic =
storage duration. You're not changing the meaning of the storage durati=
on; you're simply adding ways to create/destroy such objects.</div></di=
v></blockquote><div><br></div><div>It's more I'm looking for a stor=
age duration which exceeds the duration of the program, but still has a wel=
l defined duration. Dynamic storage can't do that.</div><div>=C2=A0</di=
v><blockquote class=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.8ex;b=
order-left: 1px #ccc solid;padding-left: 1ex;"><div dir=3D"ltr"><div><br></=
div><div><u>On Bless:</u></div><div><br></div><blockquote class=3D"gmail_qu=
ote" style=3D"margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,20=
4);padding-left:1ex"><div>P0593 proposed these functions to mark regions as=
reachable:</div></blockquote><div><br></div><div></div><div>Your notion of=
"reachability" has nothing to do with what P0593 is doing. `bles=
s` does not make objects "reachable"; that proposal only uses the=
word "reachable" once, and that is merely a description about th=
e relationship between a pointer and a size (which really ought to be a `st=
d::span<std::byte>`).</div><div><br></div><div>And your definition of=
"reachable" here does not map to what P0593 is doing. `std::bles=
s` and equivalent functions declare that a piece of memory <i>creates</i> i=
mplicit objects. When you call `bless` on a range of memory, objects will b=
e created to make your code work. What your "reachable" behavior =
seems to want is for them to already exist.</div></div></blockquote><div><b=
r></div><div>Eh ... you're right that I find blessing underspecified. T=
his is that fuzziness about lifetime I mentioned earlier. I've spent a =
good few weeks pondering P0593, indeed I asked a ton of questions about it =
as the SG12 meeting at Rapperswil. And I'm confused about what it preci=
sely does.</div><div><br></div><div>Personally, I find reachable vs unreach=
able a much less ambiguous explanation of what it does. And more helpful fo=
r compiler writers and formal verification. But I totally get that it may s=
imply be my incompetence, and I need to be corrected. I am not a compiler w=
riter after all.</div><div><br></div><div>(Richard Smith, feel free to jump=
in and correct me now!)</div><div>=C2=A0</div><blockquote class=3D"gmail_q=
uote" style=3D"margin: 0;margin-left: 0.8ex;border-left: 1px #ccc solid;pad=
ding-left: 1ex;"><div dir=3D"ltr"><div><br></div><div>This may seem like a =
trivial difference, but it isn't. <a href=3D"http://www.open-std.org/jt=
c1/sc22/wg21/docs/papers/2018/p0593r2.html#type-punning" target=3D"_blank" =
rel=3D"nofollow" onmousedown=3D"this.href=3D'http://www.google.com/url?=
q\x3dhttp%3A%2F%2Fwww.open-std.org%2Fjtc1%2Fsc22%2Fwg21%2Fdocs%2Fpapers%2F2=
018%2Fp0593r2.html%23type-punning\x26sa\x3dD\x26sntz\x3d1\x26usg\x3dAFQjCNF=
RzByEFFbKgy0oZ4Y5cSYd9Z2fgQ';return true;" onclick=3D"this.href=3D'=
http://www.google.com/url?q\x3dhttp%3A%2F%2Fwww.open-std.org%2Fjtc1%2Fsc22%=
2Fwg21%2Fdocs%2Fpapers%2F2018%2Fp0593r2.html%23type-punning\x26sa\x3dD\x26s=
ntz\x3d1\x26usg\x3dAFQjCNFRzByEFFbKgy0oZ4Y5cSYd9Z2fgQ';return true;">P0=
593 points it out</a>: when you create objects, the contents of the storage=
are rendered unspecified. And this is just as true for objects implicitly =
created by `std::bless`.</div></div></blockquote><div><br></div><div>Sure. =
They become reachable. It says nothing about their content.</div><div>=C2=
=A0</div><blockquote class=3D"gmail_quote" style=3D"margin: 0;margin-left: =
0.8ex;border-left: 1px #ccc solid;padding-left: 1ex;"><div dir=3D"ltr"><div=
><br></div><div>You explicitly don't want this. You want the objects to=
already exist, with their values intact. Non-template `bless` can't do=
that; `bless<T>` can, but only for a single complete object of a spe=
cified type. And even then, it is still <i>creating</i> the object, not mak=
ing it "reachable". It wasn't there until `bless<T>` wa=
s called.<br></div></div></blockquote><div><br></div><div>Non template bles=
s doesn't disturb any bytes already there. It is guaranteed not to do s=
o. So I'm not getting the difference, sorry. Whether the compiler knows=
there is an explicit type at some address, or it's some unspecified ar=
ray of something unknown but trivial, the common denominator here is that t=
he specified memory range is now containing alive objects, where beforehand=
it might not have been.</div><div><br></div><div>This is the difference fr=
om std::launder. It requires the memory it operates upon to contain alive o=
bjects beforehand. bless does not require this.</div><div>=C2=A0</div><bloc=
kquote class=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.8ex;border-l=
eft: 1px #ccc solid;padding-left: 1ex;"><div dir=3D"ltr"><div></div><div><b=
r></div><div></div><div><u>On Focus:</u></div><div><br></div><div>Your sect=
ion 3.2 has nothing to do with the C++ object model or the needs of mapping=
memory. That's not to say that they aren't useful, but the presenc=
e of side effects is orthogonal to memory mapping or the object model.<br><=
/div></div></blockquote><div><br></div><div>The paper is full of pieces whi=
ch will be spun out into separate papers when they are mature enough.</div>=
<div><br></div><div>Thanks for the detailed feedback. It was useful.</div><=
div><br></div><div>Niall</div></div>
<p></p>
-- <br />
You received this message because you are subscribed to the Google Groups &=
quot;ISO C++ Standard - Future Proposals" group.<br />
To unsubscribe from this group and stop receiving emails from it, send an e=
mail to <a href=3D"mailto:std-proposals+unsubscribe@isocpp.org">std-proposa=
ls+unsubscribe@isocpp.org</a>.<br />
To post to this group, send email to <a href=3D"mailto:std-proposals@isocpp=
..org">std-proposals@isocpp.org</a>.<br />
To view this discussion on the web visit <a href=3D"https://groups.google.c=
om/a/isocpp.org/d/msgid/std-proposals/f171948c-9c40-4daf-aebb-a221f967d4d6%=
40isocpp.org?utm_medium=3Demail&utm_source=3Dfooter">https://groups.google.=
com/a/isocpp.org/d/msgid/std-proposals/f171948c-9c40-4daf-aebb-a221f967d4d6=
%40isocpp.org</a>.<br />
------=_Part_137_442654054.1533201172971--
------=_Part_136_1604344411.1533201172970--
.
Author: Niall Douglas <nialldouglas14@gmail.com>
Date: Thu, 2 Aug 2018 02:15:13 -0700 (PDT)
Raw View
------=_Part_113_842239091.1533201313817
Content-Type: multipart/alternative;
boundary="----=_Part_114_1921035936.1533201313817"
------=_Part_114_1921035936.1533201313817
Content-Type: text/plain; charset="UTF-8"
>
> My hot take is that this is waaay too low-level and implementation-detaily
> for a WG21 proposal. C++ programs have to be able to run on hardware that
> might not even have the concept of a "TLB".
>
There is a well defined subset of the proposal for Freestanding C++ and
MMU-less CPUs. So I have it covered, just not explained in the paper yet.
I need to go to work where I have no access to external email or this
forum, but I'll try to return to your other points tomorrow.
Niall
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/d0892891-0034-4684-8b04-c7c3c673a6ff%40isocpp.org.
------=_Part_114_1921035936.1533201313817
Content-Type: text/html; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable
<div dir=3D"ltr"><blockquote class=3D"gmail_quote" style=3D"margin: 0;margi=
n-left: 0.8ex;border-left: 1px #ccc solid;padding-left: 1ex;"><div dir=3D"l=
tr"><div>My hot take is that this is waaay too low-level and implementation=
-detaily for a WG21 proposal. C++ programs have to be able to run on hardwa=
re that might not even have the concept of a "TLB". </div></div><=
/blockquote><div><br></div><div>There is a well defined subset of the propo=
sal for Freestanding C++ and MMU-less CPUs. So I have it covered, just not =
explained in the paper yet.</div><div><br></div><div>I need to go to work w=
here I have no access to external email or this forum, but I'll try to =
return to your other points tomorrow.</div><div><br></div><div>Niall</div><=
/div>
<p></p>
-- <br />
You received this message because you are subscribed to the Google Groups &=
quot;ISO C++ Standard - Future Proposals" group.<br />
To unsubscribe from this group and stop receiving emails from it, send an e=
mail to <a href=3D"mailto:std-proposals+unsubscribe@isocpp.org">std-proposa=
ls+unsubscribe@isocpp.org</a>.<br />
To post to this group, send email to <a href=3D"mailto:std-proposals@isocpp=
..org">std-proposals@isocpp.org</a>.<br />
To view this discussion on the web visit <a href=3D"https://groups.google.c=
om/a/isocpp.org/d/msgid/std-proposals/d0892891-0034-4684-8b04-c7c3c673a6ff%=
40isocpp.org?utm_medium=3Demail&utm_source=3Dfooter">https://groups.google.=
com/a/isocpp.org/d/msgid/std-proposals/d0892891-0034-4684-8b04-c7c3c673a6ff=
%40isocpp.org</a>.<br />
------=_Part_114_1921035936.1533201313817--
------=_Part_113_842239091.1533201313817--
.
Author: Nicol Bolas <jmckesson@gmail.com>
Date: Thu, 2 Aug 2018 13:37:29 -0700 (PDT)
Raw View
------=_Part_400_1349369667.1533242249724
Content-Type: multipart/alternative;
boundary="----=_Part_401_1523194903.1533242249726"
------=_Part_401_1523194903.1533242249726
Content-Type: text/plain; charset="UTF-8"
On Thursday, August 2, 2018 at 5:12:53 AM UTC-4, Niall Douglas wrote:
>
>
>> So you have this operation you want to be able to do. You want to take
>> objects in one address, make them disappear from that address, and then
>> make them appear in a (potentially) different address. And you want to
>> change the C++ object model to make this work.
>>
>> Here's a question: do you *need* to change the object model in order to
>> get this effect?
>>
>
> So, here's what we want to be able to do without UB:
>
> 1. Modify where allocated memory appears in the process (use cases:
> expanding arrays without memory copies, patching binaries, mapping page
> cached files etc)
> 2. Share a region of memory with another running process (use cases:
> IPC, multiprocessing, etc)
> 3. Reduce C++ causing unnecessary page faults/prevent use of memory
> with changed access permissions by making the compiler aware of what memory
> is not to be touched.
> 4. Handle objects written out in a previous incarnation of a program
> being at a different address in a new incarnation of a program.
> 5. Enforce a new read/write reordering constraint separate to compiler
> and thread barriers.
>
> In hindsight, I probably ought to have specified this more succinctly in
> the paper. I will change it.
>
> There are also multiple levels of persistability going on here:
>
> 1. Trivially copyable types, which are just bits. You can use their
> in-memory representation directly, just need to constrain write reordering.
> 2. Trivially relocatable types, which are moved with just bits, if we
> standardise those. You can use their in-memory representation directly,
> just need to constrain write reordering.
> 3. Polymorphic types where if it weren't for the vptr, would be either
> of the first two categories. You can use their in-memory representation
> directly, bar the vptr restamp on load.
> 4. Other types whose in-memory representation can be used directly
> after some trivial transformation e.g. replace all pointers with offset
> pointers.
>
> So the common factor to all these is that their in-memory representation
> is sufficient for their persisted representation i.e. no memory copying
> needed, but there can be a "phase change" between "active" and "sleeping".
>
> Serialisation is where you need to create new objects which represent a
> persisted edition of an in-memory form. That's a whole separate topic, to
> be addressed as iostreams v2 after we've nailed down the in-memory stuff
> which is a prerequisite as the serialised form of an object is clearly a
> persistable object meeting at least the criteria of the above.
>
There may be a common factor in these cases, but there is no common
*solution* with them. Cases #1 and #2 are fundamentally different from #3
and #4. Why? Because it's impossible to do 3&4 on an arbitrary range of
memory.
In cases 1&2, you can logically move these objects' values around and
reconstitute them with no per-object code. This means you could just have
the mapping/unmapping operation perform the "operation" needed to make this
legal.
With 3&4, you're packing an object's values into a form from which it can
be unpacked to reconstitute the original object's value. This operation has
to be done on a per-object basis. That is, you can't just do "unmap range
of memory, map it latter, do some casting, and go." You have to go to each
object and perform some operation; code must be executed for each object
before each unmap and after each map. Also, this operation has to cascade
up and down the subobject hierarchy, applying that operation to each
subobject in turn. Preferably in an order similar to how subobjects are
created and destroyed.
If you're executing code at map/unmap time, you are *doing serialization*.
It may not be the most complex form of serialization, but it is still
serialization.
As such, I would say that what you're talking about is best handled in the
C++ object model via what I said: destruction and construction. If you're
unmapping memory, then any objects within it are destroyed, ending their
lifetimes. When you map them, a new object is constructed in the storage
whose values contain the old objects' values
Since this would be done via beginning and ending object lifetimes, that
provides opportunities to inject user/compiler-code into the process via
specialized constructors and destructors. For some objects (which may *or
may not* equate to implicit lifetime or trivially relocatable types), these
constructors/destructors would be trivial, and as such, we can give them
special properties and effects.
If I were to give a name to this process, I would say that you are
*trivializing* the value of an object. At its core, what you're wanting to
do is to take an object, turn its values into data that is no longer
considered to be "that object", but still is within the same storage. These
bytes can be copied around verbatim, or the underlying storage can be
persisted as you desire. You can later *detrivialize* the value back into
an object of that *exact type*.
The trivialized form of an object is not that object anymore. It's just
bytes of data. For some objects, its trivial form is exactly its regular
form. For other objects, this is not the case. But the trivial form has the
same size as the regular form.
A trivializing destructor takes the object and alters its values to make it
trivial. As implied by this being invoked by a destructor, once you
trivialize the object's data, the object's lifetime has ended. The storage
behind a trivialized object is preserved after the object's lifetime has
ended.
A detrivializing constructor takes the object representation of a
trivialized object of that type and undoes the trivialization.
The one change to the object model that would be needed to do all of this
would be a guarantee of the preservation of the values of a
destroyed/created object. Currently, the C++ object model makes no
guarantees of the value of the object's storage after its lifetime has
ended. And when you begin the lifetime of an object, its storage before
initialization has either zero values (if it's static) or unspecified
values.
So you would need to say that trivializing destructors preserve the value
of the memory, and detrivializing initialization/construction operations
would maintain the storage of the memory at the start of the detrivializing
constructor. And of course, objects with trivial trivializing operations
(maybe "trivializing" wasn't such a good term...) can do these implicitly.
You will still need a placement-new replacement to be able to detrivialize
objects with non-trivial detrivializing construction. The placement part
obtains storage for the object, and that part is legally allowed to
overwrite such storage. So you'd need an alternate version which isn't
allowed to.
Note that a TriviallyCopyable type is not necessarily an object with
trivial trivialization (wow, that's terrible naming ;). The reason being:
pointers. An object with pointers is TriviallyCopyable, so you could
theoretically map or unmap it. But if those pointers point to mapped
memory, you need to manually trivialize and detrivialize them.
Indeed, I'm starting to wonder about that. It seems that the need for
trivialization is more a property of the object than of the object's *type*.
Trivialization ought to be distinct from trivial relocation and trivial
copying. But it should still be something done via constructors/destructors.
I think "Mapped storage duration" is too on-point in terms of where the
>>> memory comes from, rather than how C++ thinks of it.
>>>
>>> The four storage durations are based on how C++ interacts with them.
>>> Automatic storage duration objects have storage durations determined
>>> entirely by their objects' scopes. Static storage duration objects have
>>> their storage duration be the lifetime of the program. Thread-local storage
>>> duration objects have their storage duration be the lifetime of the thread.
>>> And dynamic duration objects have their storage duration determined
>>> dynamically: by direct manipulation of the code.
>>>
>>
>> Sure. And I propose that mapped storage duration has the lifetime of its
>> backing filesystem entity. Same approach.
>>
>>
>>>
>>> But I think there is a deeper misunderstanding here. You are confusing
>>> properties of "storage" with the properties of "storage duration". The
>>> former is a thing that an object needs to have in order to exist. The
>>> latter is a property of an object, determined by the means used to
>>> construct it, and it's purpose is to govern the (default) lifetime of that
>>> object.
>>>
>>> For example, you can create an automatic variable with automatic storage
>>> duration. You can then reuse that variable's storage to create objects with
>>> dynamic storage duration. By reusing it, you will have ended the lifetime
>>> of the automatic variable. You would then have an object with dynamic
>>> storage duration whose storage happens to be on the stack.
>>>
>>> Which is why "stack storage duration" would be a misnomer, much like
>>> "mapped storage duration".
>>>
>>
>> Would "filesystem storage duration" be better?
>>
>
Let me try to explain again.
Automatic storage duration is named based on its high-level concepts: the
storage duration of the object is handled "automatically" by the scope of
the declaration. The memory for that storage may come from something we
call the "stack", but we don't name it "stack storage duration" even though
such objects are (nominally) on the "stack".
The same goes for your notion. Both "mapped" and "filesystem" are all named
based on where the storage comes from. *That* is what is wrong with these
terms. Terms should be based on the high-level concepts of the kind of
storage duration you're imposing on the object, not based on where the
memory is or how it's being interacted with.
I originally discounted that naming because there are mapped storage
> durations whose backing filesystem entity is the swap file/kernel, so I
> felt mentioning filesystem would be confusing.
>
>
>>
>> Of course, C++ has a bunch of rules about what happens when you do this
>> and then try to use that variable's name later on. As well as what happens
>> when that variable goes out of scope, and what you need to do to make
>> leaving scope not invoke UB (namely, create an object of that type *back*
>> in that storage).
>>
>> What you seem to want is simply a wider form of dynamic storage duration.
>> You're not changing the meaning of the storage duration; you're simply
>> adding ways to create/destroy such objects.
>>
>
> It's more I'm looking for a storage duration which exceeds the duration of
> the program, but still has a well defined duration. Dynamic storage can't
> do that.
>
You're misunderstanding something critical here. "Storage duration" is a
misnomer; it has *nothing* to do with the duration of a piece of memory
(yes, really). Storage duration defines how the lifetime of an object
works, based on how it is created. Dynamic storage duration means that the
lifetime of the object being created is governed explicitly by
runtime-executed code.
For example:
int i; //i has automatic storage duration.
auto pf = new(&i) float;
The storage for `*pf` comes from an automatic variable, but the storage
duration of the object is *dynamic*. It has dynamic storage duration
because it is created by `operator new`, rather than a declaration or
somesuch.
Therefore, the only way you would need your hypothetical "mapped storage
duration" would be if you are proposing a new way to create objects. If you
can create objects in mapped storage with `operator new`, then those
objects have dynamic storage duration, *period*.
*On Bless:*
>>
>> P0593 proposed these functions to mark regions as reachable:
>>>
>>
>> Your notion of "reachability" has nothing to do with what P0593 is doing.
>> `bless` does not make objects "reachable"; that proposal only uses the word
>> "reachable" once, and that is merely a description about the relationship
>> between a pointer and a size (which really ought to be a
>> `std::span<std::byte>`).
>>
>> And your definition of "reachable" here does not map to what P0593 is
>> doing. `std::bless` and equivalent functions declare that a piece of memory
>> *creates* implicit objects. When you call `bless` on a range of memory,
>> objects will be created to make your code work. What your "reachable"
>> behavior seems to want is for them to already exist.
>>
>
> Eh ... you're right that I find blessing underspecified. This is that
> fuzziness about lifetime I mentioned earlier. I've spent a good few weeks
> pondering P0593, indeed I asked a ton of questions about it as the SG12
> meeting at Rapperswil. And I'm confused about what it precisely does.
>
> Personally, I find reachable vs unreachable a much less ambiguous
> explanation of what it does.
>
If you're confused by "what it precisely does", then I'm not sure how good
an idea it is to impose your own terminology on it. You need to understand
what it currently means before you can say that your terminology describes
that meaning better or worse. After all, if you don't understand it, your
terminology could be imposing different behavior than what it's trying to
do.
Which is what's happening here. What you say reachability means and what
P0593 says `bless` means are two different things.
`launder` says "there is an object, at this address, of this type. Get me a
pointer to it."
`bless` says "*create* objects of implicit lifetime types within this
storage, such that my following code will be legal."
What you want your "make reachable" operation to say is "there already are
some objects of various types in this storage; I want to access them." Your
"reachable" concept isn't *creating* objects, but `std::bless` *is*.
You're thinking of this from a low-level "what is going on in memory"
perspective. When dealing with the C++ object model, you have to think from
a high-level "what are objects" perspective. This is what leads us to the
following:
And more helpful for compiler writers and formal verification. But I
> totally get that it may simply be my incompetence, and I need to be
> corrected. I am not a compiler writer after all.
>
> (Richard Smith, feel free to jump in and correct me now!)
>
>
>> This may seem like a trivial difference, but it isn't. P0593 points it
>> out
>> <http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0593r2.html#type-punning>:
>> when you create objects, the contents of the storage are rendered
>> unspecified. And this is just as true for objects implicitly created by
>> `std::bless`.
>>
>
> Sure. They become reachable. It says nothing about their content.
>
From the section I linked to (emphasis added):
Specifically, the value held by an object is only stable *throughout its
> lifetime*. When the lifetime of the int object in line #1 ends (when its
> storage is reused by the float object in line #2), *its value is gone*.
> Symmetrically, when the float object is created, the object has an
> indeterminate value ([dcl.init]p12), and therefore *any attempt to load
> its value results in undefined behavior*.
>
The thing about P0593 is that it's actually a bit inconsistent about this.
Since we don't have actual standards wording, we have to do the best we can
with the details we've been given. The area of inconsistency is that
Section 4.1 of the same document says:
In some cases it is desirable to change the dynamic type of existing
> storage while maintaining the object representation. If the destination
> type is an implicit lifetime type, this can be accomplished by usage of
> std::bless to change the type, followed by std::launder to acquire a
> pointer to the newly-created object.
>
That section can only be true if the previously quoted section is *false*.
If there's a newly created object in that storage, the only way to access
the value from the recently-destroyed object that used to live there is if
it's value is not "gone".
However, section 4.1 is the odd man out in P0593. Throughout that paper, it
refers to the operation which `bless` performs as "creating objects". If
you create an object in storage that already contains an object, then you
are reusing that object's storage. Reusing an object's storage ends that
object's lifetime.
And the C++ object model is very clear: the value of an object whose
lifetime has ended is not preserved. The contents of an object's storage
after its lifetime has ended is not defined by the standard.
So my reading of P0593 is that if you `bless` a range of memory, any
objects already residing there have their lifetimes ended. The objects
which have been created by this process therefore have unspecified values,
since all previous objects in that storage have been reused.
And once an object's lifetime ends, the value of that object is gone.
Basically, consider this:
int i = 45;
int *pi = new(&i) int;
std::cout << i;
The standard says that this is not guaranteed to print 45. My reading of
P0593 is that the following is the equivalent of the above:
int i = 45;
std::bless(&i, sizeof(int)); //"Creates objects" in `i`.
std::cout << i; //Newly created `int` object, due to `bless` and access
So this is not guaranteed to print 45 either. And neither
`reinterpret_cast` nor `launder` would change that.
Now, I could be misinterpreting P0593. Like I said, it doesn't have formal
standards wording. But the frequent use of the phrase "create objects",
coupled with the *explicit* prohibition of type punning, suggests that I'm
probably right.
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/f7dffe3e-6e9c-47cb-a834-267e170640ed%40isocpp.org.
------=_Part_401_1523194903.1533242249726
Content-Type: text/html; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable
<div dir=3D"ltr">On Thursday, August 2, 2018 at 5:12:53 AM UTC-4, Niall Dou=
glas wrote:<blockquote class=3D"gmail_quote" style=3D"margin: 0;margin-left=
: 0.8ex;border-left: 1px #ccc solid;padding-left: 1ex;"><div dir=3D"ltr"><b=
lockquote class=3D"gmail_quote" style=3D"margin:0;margin-left:0.8ex;border-=
left:1px #ccc solid;padding-left:1ex"><div dir=3D"ltr"><div><div><br></div>=
<div>So you have this operation you want to be able to do. You want to take=
=20
objects in one address, make them disappear from that address, and then=20
make them appear in a (potentially) different address. And you want to chan=
ge the C++ object model to make this work.</div><div><br></div><div>Here=
9;s a question: do you <i>need</i> to change the object model in order to g=
et this effect?<br></div></div></div></blockquote><div><br></div><div>So, h=
ere's what we want to be able to do without UB:</div><div><ol><li>Modif=
y where allocated memory appears in the process (use cases: expanding array=
s without memory copies, patching binaries, mapping page cached files etc)<=
/li><li>Share a region of memory with another running process (use cases: I=
PC, multiprocessing, etc)</li><li>Reduce C++ causing unnecessary page fault=
s/prevent use of memory with changed access permissions by making the compi=
ler aware of what memory is not to be touched.</li><li>Handle objects writt=
en out in a previous incarnation of a program being at a different address =
in a new incarnation of a program.</li><li>Enforce a new read/write reorder=
ing constraint separate to compiler and thread barriers.</li></ol></div><di=
v>In hindsight, I probably ought to have specified this more succinctly in =
the paper. I will change it.</div><div><br></div><div>There are also multip=
le levels of persistability going on here:</div><div><ol><li>Trivially copy=
able types, which are just bits. You can use their in-memory representation=
directly, just need to constrain write reordering.</li><li>Trivially reloc=
atable types, which are moved with just bits, if we standardise those. You =
can use their in-memory representation directly, just need to constrain wri=
te reordering.</li><li>Polymorphic types where if it weren't for the vp=
tr, would be either of the first two categories. You can use their in-memor=
y representation directly, bar the vptr restamp on load.</li><li>Other type=
s whose in-memory representation can be used directly after some trivial tr=
ansformation e.g. replace all pointers with offset pointers.</li></ol></div=
><div>So the common factor to all these is that their in-memory representat=
ion is sufficient for their persisted representation i.e. no memory copying=
needed, but there can be a "phase change" between "active&q=
uot; and "sleeping".</div><div><br></div><div>Serialisation is wh=
ere you need to create new objects which represent a persisted edition of a=
n in-memory form. That's a whole separate topic, to be addressed as ios=
treams v2 after we've nailed down the in-memory stuff which is a prereq=
uisite as the serialised form of an object is clearly a persistable object =
meeting at least the criteria of the above.</div></div></blockquote><div><b=
r></div><div>There may be a common factor in these cases, but there is no c=
ommon <i>solution</i> with them. Cases #1 and #2 are fundamentally differen=
t from #3 and #4. Why? Because it's impossible to do 3&4 on an arbi=
trary range of memory.</div><div><br></div><div>In cases 1&2, you can l=
ogically move these objects' values around and reconstitute them with n=
o per-object code. This means you could just have the mapping/unmapping ope=
ration perform the "operation" needed to make this legal.<br></di=
v><div><br></div><div>With 3&4, you're packing an object's valu=
es into a form from which it can be unpacked to reconstitute the original o=
bject's value. This operation has to be done on a per-object basis. Tha=
t is, you can't just do "unmap range of memory, map it latter, do =
some casting, and go." You have to go to each object and perform some =
operation; code must be executed for each object before each unmap and afte=
r each map. Also, this operation has to cascade up and down the subobject h=
ierarchy, applying that operation to each subobject in turn. Preferably in =
an order similar to how subobjects are created and destroyed.<br></div><div=
><br></div><div>If you're executing code at map/unmap time, you are <i>=
doing serialization</i>. It may not be the most complex form of serializati=
on, but it is still serialization.</div><div><br></div><div>As such, I woul=
d say that what you're talking about is best handled in the C++ object =
model via what I said: destruction and construction. If you're unmappin=
g memory, then any objects within it are destroyed, ending their lifetimes.=
When you map them, a new object is constructed in the storage whose values=
contain the old objects' values<br></div><div><br></div><div>Since thi=
s would be done via beginning and ending object lifetimes, that provides op=
portunities to inject user/compiler-code into the process via specialized c=
onstructors and destructors. For some objects (which may <i>or may not</i> =
equate to implicit lifetime or trivially relocatable types), these construc=
tors/destructors would be trivial, and as such, we can give them special pr=
operties and effects.<br></div><div><br></div><div>If I were to give a name=
to this process, I would say that you are <i>trivializing</i> the value of=
an object. At its core, what you're wanting to do is to take an object=
, turn its values into data that is no longer considered to be "that o=
bject", but still is within the same storage. These bytes can be copie=
d around verbatim, or the underlying storage can be persisted as you desire=
.. You can later <i>detrivialize</i> the value back into an object of that <=
i>exact type</i>.</div><div><br></div><div>The trivialized form of an objec=
t is not that object anymore. It's just bytes of data. For some objects=
, its trivial form is exactly its regular form. For other objects, this is =
not the case. But the trivial form has the same size as the regular form.</=
div><div><br></div><div></div><div>A trivializing destructor takes the obje=
ct and alters its values to make it trivial. As implied by this being invok=
ed by a destructor, once you trivialize the object's data, the object&#=
39;s lifetime has ended. The storage behind a trivialized object is preserv=
ed after the object's lifetime has ended.<br></div><div><br></div><div>=
A detrivializing constructor takes the object representation of a trivializ=
ed object of that type and undoes the trivialization.</div><div><br></div><=
div>The one change to the object model that would be needed to do all of th=
is would be a guarantee of the preservation of the values of a destroyed/cr=
eated object. Currently, the C++ object model makes no guarantees of the va=
lue of the object's storage after its lifetime has ended. And when you =
begin the lifetime of an object, its storage before initialization has eith=
er zero values (if it's static) or unspecified values.<br></div><div><b=
r></div><div>So you would need to say that trivializing destructors preserv=
e the value of the memory, and detrivializing initialization/construction o=
perations would maintain the storage of the memory at the start of the detr=
ivializing constructor. And of course, objects with trivial trivializing op=
erations (maybe "trivializing" wasn't such a good term...) ca=
n do these implicitly.</div><div><br></div><div>You will still need a place=
ment-new replacement to be able to detrivialize objects with non-trivial de=
trivializing construction. The placement part obtains storage for the objec=
t, and that part is legally allowed to overwrite such storage. So you'd=
need an alternate version which isn't allowed to.</div><div><br></div>=
<div>Note that a TriviallyCopyable type is not necessarily an object with t=
rivial trivialization (wow, that's terrible naming ;). The reason being=
: pointers. An object with pointers is TriviallyCopyable, so you could theo=
retically map or unmap it. But if those pointers point to mapped memory, yo=
u need to manually trivialize and detrivialize them.</div><div><br></div><d=
iv>Indeed, I'm starting to wonder about that. It seems that the need fo=
r trivialization is more a property of the object than of the object's =
<i>type</i>. Trivialization ought to be distinct from trivial relocation an=
d trivial copying. But it should still be something done via constructors/d=
estructors.<br></div><div><br></div><blockquote class=3D"gmail_quote" style=
=3D"margin: 0;margin-left: 0.8ex;border-left: 1px #ccc solid;padding-left: =
1ex;"><div dir=3D"ltr"><div></div><blockquote class=3D"gmail_quote" style=
=3D"margin:0;margin-left:0.8ex;border-left:1px #ccc solid;padding-left:1ex"=
><div dir=3D"ltr"><div dir=3D"ltr"><div></div><blockquote class=3D"gmail_qu=
ote" style=3D"margin:0;margin-left:0.8ex;border-left:1px #ccc solid;padding=
-left:1ex"><div dir=3D"ltr"><div></div><div>I think "Mapped storage du=
ration" is too on-point in terms of where the memory comes from, rathe=
r than how C++ thinks of it.<br></div><div><br></div><div>The four storage =
durations are based on how C++ interacts with them. Automatic storage durat=
ion objects have storage durations determined entirely by their objects'=
; scopes. Static storage duration objects have their storage duration be th=
e lifetime of the program. Thread-local storage duration objects have their=
storage duration be the lifetime of the thread. And dynamic duration objec=
ts have their storage duration determined dynamically: by direct manipulati=
on of the code.<br></div></div></blockquote><div><br></div><div>Sure. And I=
propose that mapped storage duration has the lifetime of its backing files=
ystem entity. Same approach.</div><div>=C2=A0</div><blockquote class=3D"gma=
il_quote" style=3D"margin:0;margin-left:0.8ex;border-left:1px #ccc solid;pa=
dding-left:1ex"><div dir=3D"ltr"><br><div>But I think there is a deeper mis=
understanding here. You are confusing properties of "storage" wit=
h the properties of "storage duration". The former is a thing tha=
t an object needs to have in order to exist. The latter is a property of an=
object, determined by the means used to construct it, and it's purpose=
is to govern the (default) lifetime of that object.<br></div><div><br></di=
v><div>For example, you can create an automatic variable with automatic sto=
rage duration. You can then reuse that variable's storage to create obj=
ects with dynamic storage duration. By reusing it, you will have ended the =
lifetime of the automatic variable. You would then have an object with dyna=
mic storage duration whose storage happens to be on the stack.</div><div><b=
r></div><div>Which is why "stack storage duration" would be a mis=
nomer, much like "mapped storage duration".<br></div></div></bloc=
kquote><div><br></div><div>Would "filesystem storage duration" be=
better?</div></div></div></blockquote></div></blockquote><div><br></div><d=
iv>Let me try to explain again.</div><div><br></div><div>Automatic storage =
duration is named based on its high-level concepts: the storage duration of=
the object is handled "automatically" by the scope of the declar=
ation. The memory for that storage may come from something we call the &quo=
t;stack", but we don't name it "stack storage duration" =
even though such objects are (nominally) on the "stack".<br></div=
><div><br></div><div>The same goes for your notion. Both "mapped"=
and "filesystem" are all named based on where the storage comes =
from. <i>That</i> is what is wrong with these terms. Terms should be based =
on the high-level concepts of the kind of storage duration you're impos=
ing on the object, not based on where the memory is or how it's being i=
nteracted with.</div><div></div><br><blockquote class=3D"gmail_quote" style=
=3D"margin: 0;margin-left: 0.8ex;border-left: 1px #ccc solid;padding-left: =
1ex;"><div dir=3D"ltr"><div></div><div>I originally discounted that naming =
because there are mapped storage durations whose backing filesystem entity =
is the swap file/kernel, so I felt mentioning filesystem would be confusing=
..</div><div>=C2=A0</div><blockquote class=3D"gmail_quote" style=3D"margin:0=
;margin-left:0.8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir=3D=
"ltr"><div></div><div><br></div><div>Of course, C++ has a bunch of rules ab=
out what happens when you do this and then try to use that variable's n=
ame later on. As well as what happens when that variable goes out of scope,=
and what you need to do to make leaving scope not invoke UB (namely, creat=
e an object of that type <i>back</i> in that storage).</div><div><br></div>=
<div>What you seem to want is simply a wider form of dynamic storage durati=
on. You're not changing the meaning of the storage duration; you're=
simply adding ways to create/destroy such objects.</div></div></blockquote=
><div><br></div><div>It's more I'm looking for a storage duration w=
hich exceeds the duration of the program, but still has a well defined dura=
tion. Dynamic storage can't do that.</div></div></blockquote><div><br><=
/div><div>You're misunderstanding something critical here. "Storag=
e duration" is a misnomer; it has <i>nothing</i> to do with the durati=
on of a piece of memory (yes, really). Storage duration defines how the lif=
etime of an object works, based on how it is created. Dynamic storage durat=
ion means that the lifetime of the object being created is governed explici=
tly by runtime-executed code.<br></div><div><br></div><div>For example:</di=
v><div><br></div><div style=3D"background-color: rgb(250, 250, 250); border=
-color: rgb(187, 187, 187); border-style: solid; border-width: 1px; overflo=
w-wrap: break-word;" class=3D"prettyprint"><code class=3D"prettyprint"><div=
class=3D"subprettyprint"><span style=3D"color: #008;" class=3D"styled-by-p=
rettify">int</span><span style=3D"color: #000;" class=3D"styled-by-prettify=
"> i</span><span style=3D"color: #660;" class=3D"styled-by-prettify">;</spa=
n><span style=3D"color: #000;" class=3D"styled-by-prettify"> </span><span s=
tyle=3D"color: #800;" class=3D"styled-by-prettify">//i has automatic storag=
e duration.</span><span style=3D"color: #000;" class=3D"styled-by-prettify"=
><br></span><span style=3D"color: #008;" class=3D"styled-by-prettify">auto<=
/span><span style=3D"color: #000;" class=3D"styled-by-prettify"> pf </span>=
<span style=3D"color: #660;" class=3D"styled-by-prettify">=3D</span><span s=
tyle=3D"color: #000;" class=3D"styled-by-prettify"> </span><span style=3D"c=
olor: #008;" class=3D"styled-by-prettify">new</span><span style=3D"color: #=
660;" class=3D"styled-by-prettify">(&</span><span style=3D"color: #000;=
" class=3D"styled-by-prettify">i</span><span style=3D"color: #660;" class=
=3D"styled-by-prettify">)</span><span style=3D"color: #000;" class=3D"style=
d-by-prettify"> </span><span style=3D"color: #008;" class=3D"styled-by-pret=
tify">float</span><span style=3D"color: #660;" class=3D"styled-by-prettify"=
>;</span><span style=3D"color: #000;" class=3D"styled-by-prettify"><br></sp=
an></div></code></div><div><br>The storage for `*pf` comes from an automati=
c variable, but the storage duration of the object is <i>dynamic</i>. It ha=
s dynamic storage duration because it is created by `operator new`, rather =
than a declaration or somesuch.<br></div><div></div><div><br></div><div>The=
refore, the only way you would need your hypothetical "mapped storage =
duration" would be if you are proposing a new way to create objects. I=
f you can create objects in mapped storage with `operator new`, then those =
objects have dynamic storage duration, <i>period</i>.<br></div><div><br></d=
iv><blockquote class=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.8ex;=
border-left: 1px #ccc solid;padding-left: 1ex;"><div dir=3D"ltr"><div></div=
><blockquote class=3D"gmail_quote" style=3D"margin:0;margin-left:0.8ex;bord=
er-left:1px #ccc solid;padding-left:1ex"><div dir=3D"ltr"><div></div><div><=
u>On Bless:</u></div><div><br></div><blockquote class=3D"gmail_quote" style=
=3D"margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding=
-left:1ex"><div>P0593 proposed these functions to mark regions as reachable=
:</div></blockquote><div><br></div><div></div><div>Your notion of "rea=
chability" has nothing to do with what P0593 is doing. `bless` does no=
t make objects "reachable"; that proposal only uses the word &quo=
t;reachable" once, and that is merely a description about the relation=
ship between a pointer and a size (which really ought to be a `std::span<=
;std::byte>`).</div><div><br></div><div>And your definition of "rea=
chable" here does not map to what P0593 is doing. `std::bless` and equ=
ivalent functions declare that a piece of memory <i>creates</i> implicit ob=
jects. When you call `bless` on a range of memory, objects will be created =
to make your code work. What your "reachable" behavior seems to w=
ant is for them to already exist.</div></div></blockquote><div><br></div><d=
iv>Eh ... you're right that I find blessing underspecified. This is tha=
t fuzziness about lifetime I mentioned earlier. I've spent a good few w=
eeks pondering P0593, indeed I asked a ton of questions about it as the SG1=
2 meeting at Rapperswil. And I'm confused about what it precisely does.=
</div><div><br></div><div>Personally, I find reachable vs unreachable a muc=
h less ambiguous explanation of what it does.</div></div></blockquote><div>=
<br></div><div>If you're confused by "what it precisely does"=
, then I'm not sure how good an idea it is to impose your own terminolo=
gy on it. You need to understand what it currently means before you can say=
that your terminology describes that meaning better or worse. After all, i=
f you don't understand it, your terminology could be imposing different=
behavior than what it's trying to do.<br></div><div><br></div><div>Whi=
ch is what's happening here. What you say reachability means and what P=
0593 says `bless` means are two different things.</div><div><br></div><div>=
`launder` says "there is an object, at this address, of this type. Get=
me a pointer to it."</div><div><br></div><div>`bless` says "<i>c=
reate</i> objects of implicit lifetime types within this storage, such that=
my following code will be legal."</div><div><br></div><div>What you w=
ant your "make reachable" operation to say is "there already=
are some objects of various types in this storage; I want to access them.&=
quot; Your "reachable" concept isn't <i>creating</i> objects,=
but `std::bless` <i>is</i>.<br></div><div><br></div><div>You're thinki=
ng of this from a low-level "what is going on in memory" perspect=
ive. When dealing with the C++ object model, you have to think from a high-=
level "what are objects" perspective. This is what leads us to th=
e following:</div><div><br></div><blockquote class=3D"gmail_quote" style=3D=
"margin: 0;margin-left: 0.8ex;border-left: 1px #ccc solid;padding-left: 1ex=
;"><div dir=3D"ltr"><div> And more helpful for compiler writers and formal =
verification. But I totally get that it may simply be my incompetence, and =
I need to be corrected. I am not a compiler writer after all.</div><div><br=
></div><div>(Richard Smith, feel free to jump in and correct me now!)</div>=
<div>=C2=A0</div><blockquote class=3D"gmail_quote" style=3D"margin:0;margin=
-left:0.8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir=3D"ltr"><=
div></div><div>This may seem like a trivial difference, but it isn't. <=
a href=3D"http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0593r2.h=
tml#type-punning" rel=3D"nofollow" target=3D"_blank" onmousedown=3D"this.hr=
ef=3D'http://www.google.com/url?q\x3dhttp%3A%2F%2Fwww.open-std.org%2Fjt=
c1%2Fsc22%2Fwg21%2Fdocs%2Fpapers%2F2018%2Fp0593r2.html%23type-punning\x26sa=
\x3dD\x26sntz\x3d1\x26usg\x3dAFQjCNFRzByEFFbKgy0oZ4Y5cSYd9Z2fgQ';return=
true;" onclick=3D"this.href=3D'http://www.google.com/url?q\x3dhttp%3A%=
2F%2Fwww.open-std.org%2Fjtc1%2Fsc22%2Fwg21%2Fdocs%2Fpapers%2F2018%2Fp0593r2=
..html%23type-punning\x26sa\x3dD\x26sntz\x3d1\x26usg\x3dAFQjCNFRzByEFFbKgy0o=
Z4Y5cSYd9Z2fgQ';return true;">P0593 points it out</a>: when you create =
objects, the contents of the storage are rendered unspecified. And this is =
just as true for objects implicitly created by `std::bless`.</div></div></b=
lockquote><div><br></div><div>Sure. They become reachable. It says nothing =
about their content.</div></div></blockquote><div><br></div><div>From the s=
ection I linked to (emphasis added):</div><div><br></div><blockquote class=
=3D"gmail_quote" style=3D"margin: 0px 0px 0px 0.8ex; border-left: 1px solid=
rgb(204, 204, 204); padding-left: 1ex;"><div>Specifically, the value held =
by an object is only stable <b>throughout its lifetime</b>. When the lifeti=
me of the int object in line #1 ends (when its storage is reused by the flo=
at object in line #2), <b>its value is gone</b>. Symmetrically, when the fl=
oat object is created, the object has an indeterminate value ([dcl.init]p12=
), and therefore <b>any attempt to load its value results in undefined beha=
vior</b>.<br></div></blockquote><div><br></div><div>The thing about P0593 i=
s that it's actually a bit inconsistent about this. Since we don't =
have actual standards wording, we have to do the best we can with the detai=
ls we've been given. The area of inconsistency is that Section 4.1 of t=
he same document says:</div><div><br></div><blockquote class=3D"gmail_quote=
" style=3D"margin: 0px 0px 0px 0.8ex; border-left: 1px solid rgb(204, 204, =
204); padding-left: 1ex;"><div>In some cases it is desirable to change the =
dynamic type of existing storage while maintaining the object representatio=
n. If the destination type is an implicit lifetime type, this can be accomp=
lished by usage of std::bless to change the type, followed by std::launder =
to acquire a pointer to the newly-created object.<br></div></blockquote><di=
v><br></div><div>That section can only be true if the previously quoted sec=
tion is <i>false</i>. If there's a newly created object in that storage=
, the only way to access the value from the recently-destroyed object that =
used to live there is if it's value is not "gone".</div><div>=
<br></div><div>However, section 4.1 is the odd man out in P0593. Throughout=
that paper, it refers to the operation which `bless` performs as "cre=
ating objects". If you create an object in storage that already contai=
ns an object, then you are reusing that object's storage. Reusing an ob=
ject's storage ends that object's lifetime.</div><div><br></div><di=
v>And the C++ object model is very clear: the value of an object whose life=
time has ended is not preserved. The contents of an object's storage af=
ter its lifetime has ended is not defined by the standard.<br></div><div><b=
r></div><div>So my reading of P0593 is that if you `bless` a range of memor=
y, any objects already residing there have their lifetimes ended. The objec=
ts which have been created by this process therefore have unspecified value=
s, since all previous objects in that storage have been reused.<br></div><d=
iv><br></div><div>And once an object's lifetime ends, the value of that=
object is gone.</div><div><br></div><div>Basically, consider this:</div><d=
iv><br></div><div style=3D"background-color: rgb(250, 250, 250); border-col=
or: rgb(187, 187, 187); border-style: solid; border-width: 1px; overflow-wr=
ap: break-word;" class=3D"prettyprint"><code class=3D"prettyprint"><div cla=
ss=3D"subprettyprint"><span style=3D"color: #008;" class=3D"styled-by-prett=
ify">int</span><span style=3D"color: #000;" class=3D"styled-by-prettify"> i=
</span><span style=3D"color: #660;" class=3D"styled-by-prettify">=3D</span=
><span style=3D"color: #000;" class=3D"styled-by-prettify"> </span><span st=
yle=3D"color: #066;" class=3D"styled-by-prettify">45</span><span style=3D"c=
olor: #660;" class=3D"styled-by-prettify">;</span><span style=3D"color: #00=
0;" class=3D"styled-by-prettify"><br></span><span style=3D"color: #008;" cl=
ass=3D"styled-by-prettify">int</span><span style=3D"color: #000;" class=3D"=
styled-by-prettify"> </span><span style=3D"color: #660;" class=3D"styled-by=
-prettify">*</span><span style=3D"color: #000;" class=3D"styled-by-prettify=
">pi </span><span style=3D"color: #660;" class=3D"styled-by-prettify">=3D</=
span><span style=3D"color: #000;" class=3D"styled-by-prettify"> </span><spa=
n style=3D"color: #008;" class=3D"styled-by-prettify">new</span><span style=
=3D"color: #660;" class=3D"styled-by-prettify">(&</span><span style=3D"=
color: #000;" class=3D"styled-by-prettify">i</span><span style=3D"color: #6=
60;" class=3D"styled-by-prettify">)</span><span style=3D"color: #000;" clas=
s=3D"styled-by-prettify"> </span><span style=3D"color: #008;" class=3D"styl=
ed-by-prettify">int</span><span style=3D"color: #660;" class=3D"styled-by-p=
rettify">;</span><span style=3D"color: #000;" class=3D"styled-by-prettify">=
<br>std</span><span style=3D"color: #660;" class=3D"styled-by-prettify">::<=
/span><span style=3D"color: #000;" class=3D"styled-by-prettify">cout </span=
><span style=3D"color: #660;" class=3D"styled-by-prettify"><<</span><=
span style=3D"color: #000;" class=3D"styled-by-prettify"> i</span><span sty=
le=3D"color: #660;" class=3D"styled-by-prettify">;</span></div></code></div=
><div><br></div><div></div><div>The standard says that this is not guarante=
ed to print 45. My reading of P0593 is that the following is the equivalent=
of the above:</div><div><br></div><div style=3D"background-color: rgb(250,=
250, 250); border-color: rgb(187, 187, 187); border-style: solid; border-w=
idth: 1px; overflow-wrap: break-word;" class=3D"prettyprint"><code class=3D=
"prettyprint"><div class=3D"subprettyprint"><span style=3D"color: #008;" cl=
ass=3D"styled-by-prettify">int</span><span style=3D"color: #000;" class=3D"=
styled-by-prettify"> i </span><span style=3D"color: #660;" class=3D"styled-=
by-prettify">=3D</span><span style=3D"color: #000;" class=3D"styled-by-pret=
tify"> </span><span style=3D"color: #066;" class=3D"styled-by-prettify">45<=
/span><span style=3D"color: #660;" class=3D"styled-by-prettify">;</span><sp=
an style=3D"color: #000;" class=3D"styled-by-prettify"><br>std</span><span =
style=3D"color: #660;" class=3D"styled-by-prettify">::</span><span style=3D=
"color: #000;" class=3D"styled-by-prettify">bless</span><span style=3D"colo=
r: #660;" class=3D"styled-by-prettify">(&</span><span style=3D"color: #=
000;" class=3D"styled-by-prettify">i</span><span style=3D"color: #660;" cla=
ss=3D"styled-by-prettify">,</span><span style=3D"color: #000;" class=3D"sty=
led-by-prettify"> </span><span style=3D"color: #008;" class=3D"styled-by-pr=
ettify">sizeof</span><span style=3D"color: #660;" class=3D"styled-by-pretti=
fy">(</span><span style=3D"color: #008;" class=3D"styled-by-prettify">int</=
span><span style=3D"color: #660;" class=3D"styled-by-prettify">));</span><s=
pan style=3D"color: #000;" class=3D"styled-by-prettify"> </span><span style=
=3D"color: #800;" class=3D"styled-by-prettify">//"Creates objects"=
; in `i`.</span><span style=3D"color: #000;" class=3D"styled-by-prettify"><=
br>std</span><span style=3D"color: #660;" class=3D"styled-by-prettify">::</=
span><span style=3D"color: #000;" class=3D"styled-by-prettify">cout </span>=
<span style=3D"color: #660;" class=3D"styled-by-prettify"><<</span><s=
pan style=3D"color: #000;" class=3D"styled-by-prettify"> i</span><span styl=
e=3D"color: #660;" class=3D"styled-by-prettify">;</span><span style=3D"colo=
r: #000;" class=3D"styled-by-prettify"> </span><span style=3D"color: #800;"=
class=3D"styled-by-prettify">//Newly created `int` object, due to `bless` =
and access</span><span style=3D"color: #000;" class=3D"styled-by-prettify">=
<br></span></div></code></div><div><br></div><div></div><div>So this is not=
guaranteed to print 45 either. And neither `reinterpret_cast` nor `launder=
` would change that.</div><div><br></div><div>Now, I could be misinterpreti=
ng P0593. Like I said, it doesn't have formal standards wording. But th=
e frequent use of the phrase "create objects", coupled with the <=
i>explicit</i> prohibition of type punning, suggests that I'm probably =
right.</div></div>
<p></p>
-- <br />
You received this message because you are subscribed to the Google Groups &=
quot;ISO C++ Standard - Future Proposals" group.<br />
To unsubscribe from this group and stop receiving emails from it, send an e=
mail to <a href=3D"mailto:std-proposals+unsubscribe@isocpp.org">std-proposa=
ls+unsubscribe@isocpp.org</a>.<br />
To post to this group, send email to <a href=3D"mailto:std-proposals@isocpp=
..org">std-proposals@isocpp.org</a>.<br />
To view this discussion on the web visit <a href=3D"https://groups.google.c=
om/a/isocpp.org/d/msgid/std-proposals/f7dffe3e-6e9c-47cb-a834-267e170640ed%=
40isocpp.org?utm_medium=3Demail&utm_source=3Dfooter">https://groups.google.=
com/a/isocpp.org/d/msgid/std-proposals/f7dffe3e-6e9c-47cb-a834-267e170640ed=
%40isocpp.org</a>.<br />
------=_Part_401_1523194903.1533242249726--
------=_Part_400_1349369667.1533242249724--
.
Author: Niall Douglas <nialldouglas14@gmail.com>
Date: Fri, 3 Aug 2018 01:47:10 -0700 (PDT)
Raw View
------=_Part_585_229760114.1533286030741
Content-Type: multipart/alternative;
boundary="----=_Part_586_549836223.1533286030742"
------=_Part_586_549836223.1533286030742
Content-Type: text/plain; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable
>
> [...]
>
>> Mapped storage has the most similarity to dynamic storage, but with the=
=20
>> following differences:=20
>>
>> - It has allocation and alignment granularities with architecture=20
>> specific coarse sizes (=E2=80=98memory page=E2=80=99). 4Kb/2Mb/1Gb pa=
ge sizes are common.=20
>> - New allocations are guaranteed to be all bits zero on creation.=20
>> - It has map on first read semantics. This means that the first read=
=20
>> from a memory page can take hundreds of CPU cycles, and a TLB shootdo=
wn=20
>> causes an interrupt for other CPUs.=20
>> - It has allocate on first write semantics. This means that the first=
=20
>> write to a memory page can take thousands or even hundreds of thousan=
ds of=20
>> CPU cycles, plus a TLB shootdown.=20
>> - Usually, but not always, mapped storage is a memory cache of=20
>> equivalent storage a high latency storage device. Hence they can be=
=20
>> individually pushed to storage, their contents thrown away, deallocat=
ed,=20
>> given different access permission or caching strategies, and lots of =
other=20
>> interesting (i.e. potentially game changing for large STL containers)=
=20
>> operations. Individual pages can be:=20
>> - Read-only, read-write, or copy-on-write. These are self=20
>> describing, and these are hard characteristics: violating them mea=
ns=20
>> program failure.=20
>> - Committed or uncommitted. This indicates whether the page counts=
=20
>> towards the resources used by the C++ program. Uncommitted memory =
is=20
>> inaccessible, and acts as a placeholder for later use (i.e. it is =
reserved=20
>> address space, useful for expanding large arrays without content c=
opying).=20
>> - Dirty or clean. This indicates whether the page contains data=20
>> not yet mirrored onto its backing storage.=20
>> - Allocated or unallocated. This indicates whether storage backing=
=20
>> the page has been allocated on the storage device. If not, the fir=
st write=20
>> to a clean page may be very expensive as the page may need to be c=
opied by=20
>> the kernel and/or space allocated for it on the backing storage de=
vice.
>> =20
>> The storage represented by a section_handle instance can be mapped into=
=20
>> (i.e. made available to) a C++ program by creating a map_handle sourcing=
=20
>> the storage from a section_handle instance. The storage mapped by the=20
>> low level map_handle shall represent unconstructed and unreachable=20
>> memory, and will require the use of std::bless() or map_view to make it=
=20
>> reachable (alternatively, use the convenience class mapped on a section_=
handle=20
>> instance which bundles the aforementioned low level operations on your=
=20
>> behalf).
>>
>
> My hot take is that this is waaay too low-level and implementation-detail=
y=20
> for a WG21 proposal. C++ programs have to be able to run on hardware that=
=20
> might not even have the concept of a "TLB". The stuff in this bulleted=
=20
> list is perfectly reasonable internal documentation for an=20
> *implementation*, but it seems like the sort of thing that ought to be=20
> abstracted away from everyday C++ programmers as much as possible.
>
Ok, back to replies to feedback. Sorry I had to cut off my reply yesterday,=
=20
I hadn't realised the late time.
I think that if C++ is to reach a low latency useful use case i.e. we are=
=20
going to get closer to the metal, you need to start building in an=20
awareness of memory not all being the same. As P1027 up before SG14 next=20
telecon shows in pretty graphs, memory on modern CPUs has significant=20
latency variation. Simply ignoring the reality on the ground does not bring=
=20
a language ecosystem closer to the metal!
Now all that said, I totally agree that the average C++ programmer should=
=20
never see this stuff. But it should be there, if they go looking for it, in=
=20
my opinion.
=20
> I would want the trajectory to be:
> - implement this stuff
> - implement something higher-level and suitably abstract on top of this=
=20
> stuff
> - consider how to standardize that abstract interface
> - don't bother to mention the low-level details in that proposal (but hav=
e=20
> the reference implementation available for vendors to look at)
>
That's an approach. But probably impractical.
My approach is to implement the simple stuff, like wrapping POSIX.2008 into=
=20
standard C++ like this proposal does, and the minimum necessary amount of=
=20
language support. Eric already has ideas for plugging this stuff into=20
Ranges.
Then leave the ecosystem figure out a high level abstract interface,=20
because that one is *hard*. I'd personally like to see a Boost library on=
=20
this before anybody moves on standardising something.
I very strongly disagree that the committee is the right place to decide on=
=20
the higher level interface. I say that as somebody having worked in this=20
specialisation for six years now, and knowing and having spoken to lots of=
=20
Serialisation folk about correct Serialisation. Everybody agrees on the=20
common direction. But the details are hard.
=20
>
> I'm sure I'm ill-informed and there will be apparently good reasons not t=
o=20
> take this trajectory, but it's still what I would *want* to see as an=20
> end-user.
> (And yes, I wish the Networking TS would have taken the same trajectory.)
>
The Networking TS did go via the ecosystem route. Though, what will be=20
standardised as Networking now looks increasingly very far away from ASIO a=
=20
few years ago. I worry.
=20
>
> 3.1.3 A new lifetime stage: unreachable
>>
>> I propose adding a new separate lifetime status for objects: unreachable=
..=20
>> Under P0593, unconstructed objects are unreachable, but there is no=20
>> possibility for a partially constructed, alive or partially destructed=
=20
>> object to be unreachable. I propose that this new status ought to be add=
ed=20
>> [...]
>>
>
> The paper may answer this question, but: Is this related at all to the=20
> existing garbage-collection hooks,=20
> http://eel.is/c++draft/util.dynamic.safety, which already define=20
> terminology such as "declare reachable"?
>
Good point about confusing terminology. No.
=20
>
> P0593 proposed these functions to mark regions as reachable:=20
>>
>>
>> // Requires: [start, (char*)start + length) denotes a region of allocate=
d=20
>> // storage that is a subset of the region of storage reachable through=
=20
>> start.=20
>> // Effects: implicitly creates objects within the denoted region.=20
>> void std::bless(void *start, size_t length);=20
>> =20
>> // Effects: create an object of implicit lifetype type T in the storage=
=20
>> // pointed to by T, while preserving the object representation.=
=20
>> template<typename T> T *std::bless(void *p);
>>
>
> Hooray! For purposes of P1144 relocatability, btw, I am hoping that the=
=20
> words "implicit-lifetime" will be removed from the above comments. My=20
> understanding of "implicit-lifetime" is that implicit-lifetime types are=
=20
> precisely those types which it would be safe to access even *without* a=
=20
> call to std::bless. However, I am out of the loop.
>
Those words were directly copy and pasted from Richard's paper.
=20
>
> Thus the obvious corrollary for marking regions as unreachable:
>>
>>
>> // Requires: [start, (char*)start + length) denotes a region of allocate=
d=20
>> // storage that is a subset of the region of storage reachable through=
=20
>> start.=20
>> // Effects: implicitly uncreates objects within the denoted region.=20
>> void std::unbless(void *start, size_t length);=20
>> =20
>> // Effects: uncreate an object of implicit lifetype type T in the storag=
e=20
>> // pointed to by T, while preserving the object representation.=
=20
>> template<typename T> void *std::unbless(T *p);=20
>>
>
> Nit: The template version should return `void` not `void*`, right?
>
I believe the above is correct. It's the reverse operation of bless, which=
=20
consumes a void * for the typed edition.
=20
> I also don't fully understand the desired semantics of the non-template=
=20
> version of either `bless` or `unbless`.
>
Same as non-template bless.
Richard's paper infers you would be able to non-template bless by calling=
=20
the destructor of some trivial type e.g. std::byte on a range of memory.=20
Proposed unbless() just does the same thing in a more convenient function=
=20
form.
=20
>
> And I don't understand why you need "stun / polymorphic_unbless" at all.=
=20
> What does it even mean to make a polymorphic object "unusable"? How is th=
at=20
> different from just doing nothing at all?
>
The C++ standard doesn't say how to implement polymorphism. You are correct=
=20
that on a vptr based implementation, one doesn't have to reset the vptrs.=
=20
But for a non-vptr implementation, well who knows?
=20
>
> And if "revive / polymorphic_bless" is not a no-op but actually *does*=20
> have an observable effect on memory, that's a big deal; you should=20
> emphasize that somehow.
>
> That's also an interesting question. I am unsure if the polymorphic=20
implementation is considered to have *any* observable effect on memory=20
under the strict language of the standard. In other words, vptrs "don't=20
count" if that makes sense. This is why I don't claim that polymorphic=20
blessing as you put it has any observable effects on memory, because=20
perhaps with some polymorphic implementations it might not?
Niall
--=20
You received this message because you are subscribed to the Google Groups "=
ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an e=
mail to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp=
..org/d/msgid/std-proposals/58f8fc09-102e-4378-97f9-308b96547a1a%40isocpp.or=
g.
------=_Part_586_549836223.1533286030742
Content-Type: text/html; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable
<blockquote class=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.8ex;bor=
der-left: 1px #ccc solid;padding-left: 1ex;"><div dir=3D"ltr"><div>[...]</d=
iv><blockquote class=3D"gmail_quote" style=3D"margin:0;margin-left:0.8ex;bo=
rder-left:1px #ccc solid;padding-left:1ex"><div dir=3D"ltr"><div><p>Mapped =
storage has the most similarity to dynamic storage, but with the following =
differences: </p><ul><li>It has allocation and alignment granularities=
with architecture specific coarse sizes (=E2=80=98memory page=E2=80=99=
). 4Kb/2Mb/1Gb page sizes are common. </li><li>New allocations are gua=
ranteed to be all bits zero on creation. </li><li>It has map on first =
read semantics. This means that the first read from a memory page can t=
ake hundreds of CPU cycles, and a TLB shootdown causes an interrupt for oth=
er CPUs. </li><li>It has allocate on first write semantics. This means=
that the first write to a memory page can take thousands or even hundr=
eds of thousands of CPU cycles, plus a TLB shootdown. </li><li>Usually=
, but not always, mapped storage is a memory cache of equivalent storage a =
high latency storage device. Hence they can be individually pushed to s=
torage, their contents thrown away, deallocated, given different access=
permission or caching strategies, and lots of other interesting (i.e. =
potentially game changing for large STL containers) operations. Individual =
pages can be: <ul><li>Read-only, read-write, or copy-on-write=
.. These are self describing, and these are hard characteristics: v=
iolating them means program failure. </li><li>Committed or unco=
mmitted. This indicates whether the page counts towards the =
resources used by the C++ program. Uncommitted memory is inaccessible, an=
d acts as a placeholder for later use (i.e. it is reserved address=
space, useful for expanding large arrays without content copying)=
.. </li><li>Dirty or clean. This indicates whether the page contai=
ns data not yet mirrored onto its backing storage. </li>=
<li>Allocated or unallocated. This indicates whether storage backing the pa=
ge has been allocated on the storage device. If not, the first wri=
te to a clean page may be very expensive as the page may need to b=
e copied by the kernel and/or space allocated for it on the backin=
g storage device.</li></ul></li></ul><p>The storage represented by a <span>=
section_handle </span>instance can be <span>mapped </span>into (i.e. made a=
vailable to) a C++ program by creating a <span>map_handle </span>sourcing t=
he storage from a <span>section_handle </span>instance. The storage mapped =
by the low level <span>map_handle </span>shall represent unconstructed and =
unreachable memory, and will =
=
require the use of=
<span>std::bless() </span>or <span>map_view </span>to make it reachable (a=
lternatively, use the convenience class <span>mapped </span>on a <span>sect=
ion_handle </span>instance which bundles the aforementioned low level opera=
tions on your behalf).</p></div></div></blockquote><div><br></div><div>My h=
ot take is that this is waaay too low-level and implementation-detaily for =
a WG21 proposal. C++ programs have to be able to run on hardware that might=
not even have the concept of a "TLB". =C2=A0The stuff in this bu=
lleted list is perfectly reasonable internal documentation for an <i>implem=
entation</i>, but it seems like the sort of thing that ought to be abstract=
ed away from everyday C++ programmers as much as possible.</div></div></blo=
ckquote><div><br></div><div>Ok, back to replies to feedback. Sorry I had to=
cut off my reply yesterday, I hadn't realised the late time.</div><div=
><br></div><div>I think that if C++ is to reach a low latency useful use ca=
se i.e. we are going to get closer to the metal, you need to start building=
in an awareness of memory not all being the same. As P1027 up before SG14 =
next telecon shows in pretty graphs, memory on modern CPUs has significant =
latency variation. Simply ignoring the reality on the ground does not bring=
a language ecosystem closer to the metal!</div><div><br></div><div>Now all=
that said, I totally agree that the average C++ programmer should never se=
e this stuff. But it should be there, if they go looking for it, in my opin=
ion.</div><div>=C2=A0<br></div><blockquote class=3D"gmail_quote" style=3D"m=
argin: 0;margin-left: 0.8ex;border-left: 1px #ccc solid;padding-left: 1ex;"=
><div dir=3D"ltr"><div>I would want the trajectory to be:</div><div>- imple=
ment this stuff</div><div>- implement something higher-level and suitably a=
bstract on top of this stuff</div><div>- consider how to standardize that a=
bstract interface</div><div>- don't bother to mention the low-level det=
ails in that proposal (but have the reference implementation available for =
vendors to look at)</div></div></blockquote><div><br></div><div>That's =
an approach. But probably impractical.</div><div><br></div><div>My approach=
is to implement the simple stuff, like wrapping POSIX.2008 into standard C=
++ like this proposal does, and the minimum necessary amount of language su=
pport. Eric already has ideas for plugging this stuff into Ranges.</div><di=
v><br></div><div>Then leave the ecosystem figure out a high level abstract =
interface, because that one is <i>hard</i>. I'd personally like to see =
a Boost library on this before anybody moves on standardising something.</d=
iv><div><br></div><div>I very strongly disagree that the committee is the r=
ight place to decide on the higher level interface. I say that as somebody =
having worked in this specialisation for six years now, and knowing and hav=
ing spoken to lots of Serialisation folk about correct Serialisation. Every=
body agrees on the common direction. But the details are hard.</div><div>=
=C2=A0</div><blockquote class=3D"gmail_quote" style=3D"margin: 0;margin-lef=
t: 0.8ex;border-left: 1px #ccc solid;padding-left: 1ex;"><div dir=3D"ltr"><=
div><br></div><div>I'm sure I'm ill-informed and there will be appa=
rently good reasons not to take this trajectory, but it's still what I =
would <i>want</i> to see as an end-user.</div><div>(And yes, I wish the Net=
working TS would have taken the same trajectory.)</div></div></blockquote><=
div><br></div><div>The Networking TS did go via the ecosystem route. Though=
, what will be standardised as Networking now looks increasingly very far a=
way from ASIO a few years ago. I worry.</div><div>=C2=A0</div><blockquote c=
lass=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.8ex;border-left: 1px=
#ccc solid;padding-left: 1ex;"><div dir=3D"ltr"><div><br></div><blockquote=
class=3D"gmail_quote" style=3D"margin:0;margin-left:0.8ex;border-left:1px =
#ccc solid;padding-left:1ex"><div dir=3D"ltr"><p><span style=3D"font-size:1=
6px">3.1.3 </span><a style=3D"font-size:16px"></a><span style=3D"font-siz=
e:16px">A new lifetime stage: </span><span style=3D"font-size:16px">unreach=
able</span><br></p><p>I propose adding a new separate lifetime status for o=
bjects: <span>unreachable</span>. Under P0593, unconstructed objects are un=
reachable, but there is no possibility for a partially constructed, alive o=
r partially destructed object to be unreachable. I propose that this new st=
atus ought to be added [...]</p></div></blockquote><div><br></div><div>The =
paper may answer this question, but: Is this related at all to the existing=
garbage-collection hooks,=C2=A0<a href=3D"http://eel.is/c++draft/util.dyna=
mic.safety" target=3D"_blank" rel=3D"nofollow" onmousedown=3D"this.href=3D&=
#39;http://www.google.com/url?q\x3dhttp%3A%2F%2Feel.is%2Fc%2B%2Bdraft%2Futi=
l.dynamic.safety\x26sa\x3dD\x26sntz\x3d1\x26usg\x3dAFQjCNGohaRx8baZTB72M-Qn=
fRuet9GHDA';return true;" onclick=3D"this.href=3D'http://www.google=
..com/url?q\x3dhttp%3A%2F%2Feel.is%2Fc%2B%2Bdraft%2Futil.dynamic.safety\x26s=
a\x3dD\x26sntz\x3d1\x26usg\x3dAFQjCNGohaRx8baZTB72M-QnfRuet9GHDA';retur=
n true;">http://eel.is/c++draft/<wbr>util.dynamic.safety</a>, which already=
define terminology such as "declare reachable"?</div></div></blo=
ckquote><div><br></div><div>Good point about confusing terminology. No.</di=
v><div>=C2=A0</div><blockquote class=3D"gmail_quote" style=3D"margin: 0;mar=
gin-left: 0.8ex;border-left: 1px #ccc solid;padding-left: 1ex;"><div dir=3D=
"ltr"><div><br></div><blockquote class=3D"gmail_quote" style=3D"margin:0;ma=
rgin-left:0.8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir=3D"lt=
r"><div><p>P0593 proposed these functions to mark regions as reachable: </p=
><p><br></p><div><div style=3D"background-color:rgb(250,250,250);border-col=
or:rgb(187,187,187);border-style:solid;border-width:1px;word-wrap:break-wor=
d"><code><div><span style=3D"color:#800">// Requires: [start, (char*)start =
+ length) denotes a region of allocated </span><span style=3D"color:#000"><=
br></span><span style=3D"color:#800">// storage that is a subset of the reg=
ion of storage reachable through start. </span><span style=3D"color:#000"><=
br></span><span style=3D"color:#800">// Effects: implicitly creates objects=
within the denoted region. </span><span style=3D"color:#000"><br></span><s=
pan style=3D"color:#008">void</span><span style=3D"color:#000"> std</span><=
span style=3D"color:#660">::</span><span style=3D"color:#000">bless</span><=
span style=3D"color:#660">(</span><span style=3D"color:#008">void</span><sp=
an style=3D"color:#000"> </span><span style=3D"color:#660">*</span><span st=
yle=3D"color:#000">start</span><span style=3D"color:#660">,</span><span sty=
le=3D"color:#000"> size_t length</span><span style=3D"color:#660">);</span>=
<span style=3D"color:#000"> <br>=C2=A0<br></span><span style=3D"color:#800"=
>// Effects: create an object of implicit lifetype type T in the storage </=
span><span style=3D"color:#000"><br></span><span style=3D"color:#800">// =
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0pointed to by T, while preserving the obj=
ect representation. </span><span style=3D"color:#000"><br></span><span styl=
e=3D"color:#008">template</span><span style=3D"color:#660"><</span><span=
style=3D"color:#008">typename</span><span style=3D"color:#000"> T</span><s=
pan style=3D"color:#660">></span><span style=3D"color:#000"> T </span><s=
pan style=3D"color:#660">*</span><span style=3D"color:#000">std</span><span=
style=3D"color:#660">::</span><span style=3D"color:#000">bless</span><span=
style=3D"color:#660">(</span><span style=3D"color:#008">void</span><span s=
tyle=3D"color:#000"> </span><span style=3D"color:#660">*</span><span style=
=3D"color:#000">p</span><span style=3D"color:#660">);</span></div></code></=
div></div></div></div></blockquote><div><br></div><div>Hooray! =C2=A0For pu=
rposes of P1144 relocatability, btw, I am hoping that the words "impli=
cit-lifetime" will be removed from the above comments. =C2=A0My unders=
tanding of "implicit-lifetime" is that implicit-lifetime types ar=
e precisely those types which it would be safe to access even <i><b>without=
</b></i> a call to std::bless. =C2=A0However, I am out of the loop.</div></=
div></blockquote><div><br></div><div>Those words were directly copy and pas=
ted from Richard's paper.</div><div>=C2=A0</div><blockquote class=3D"gm=
ail_quote" style=3D"margin: 0;margin-left: 0.8ex;border-left: 1px #ccc soli=
d;padding-left: 1ex;"><div dir=3D"ltr"><div><br></div><blockquote class=3D"=
gmail_quote" style=3D"margin:0;margin-left:0.8ex;border-left:1px #ccc solid=
;padding-left:1ex"><div dir=3D"ltr"><div><span></span> </div><div>Thus the =
obvious corrollary for marking regions as unreachable:<br></div><p><br></p>=
<div style=3D"background-color:rgb(250,250,250);border-color:rgb(187,187,18=
7);border-style:solid;border-width:1px;word-wrap:break-word"><code><div><sp=
an style=3D"color:#800">// Requires: [start, (char*)start + length) denotes=
a region of allocated </span><span style=3D"color:#000"><br></span><span s=
tyle=3D"color:#800">// storage that is a subset of the region of storage re=
achable through start. </span><span style=3D"color:#000"><br></span><span s=
tyle=3D"color:#800">// Effects: implicitly uncreates objects within the den=
oted region. </span><span style=3D"color:#000"><br></span><span style=3D"co=
lor:#008">void</span><span style=3D"color:#000"> std</span><span style=3D"c=
olor:#660">::</span><span style=3D"color:#000">unbless</span><span style=3D=
"color:#660">(</span><span style=3D"color:#008">void</span><span style=3D"c=
olor:#000"> </span><span style=3D"color:#660">*</span><span style=3D"color:=
#000">start</span><span style=3D"color:#660">,</span><span style=3D"color:#=
000"> size_t length</span><span style=3D"color:#660">);</span><span style=
=3D"color:#000"> <br>=C2=A0<br></span><span style=3D"color:#800">// Effects=
: uncreate an object of implicit lifetype type T in the storage </span><spa=
n style=3D"color:#000"><br></span><span style=3D"color:#800">// =C2=A0 =C2=
=A0 =C2=A0 =C2=A0 =C2=A0pointed to by T, while preserving the object repres=
entation. </span><span style=3D"color:#000"><br></span><span style=3D"color=
:#008">template</span><span style=3D"color:#660"><</span><span style=3D"=
color:#008">typename</span><span style=3D"color:#000"> T</span><span style=
=3D"color:#660">></span><span style=3D"color:#000"> </span><span style=
=3D"color:#008">void</span><span style=3D"color:#000"> </span><span style=
=3D"color:#660">*</span><span style=3D"color:#000">std</span><span style=3D=
"color:#660">::</span><span style=3D"color:#000">unbless</span><span style=
=3D"color:#660">(</span><span style=3D"color:#000">T </span><span style=3D"=
color:#660">*</span><span style=3D"color:#000">p</span><span style=3D"color=
:#660">);</span><span style=3D"color:#000">=C2=A0</span></div></code></div>=
</div></blockquote><div><br></div><div>Nit: The template version should ret=
urn `void` not `void*`, right?</div></div></blockquote><div><br></div><div>=
I believe the above is correct. It's the reverse operation of bless, wh=
ich consumes a void * for the typed edition.</div><div>=C2=A0</div><blockqu=
ote class=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.8ex;border-left=
: 1px #ccc solid;padding-left: 1ex;"><div dir=3D"ltr"><div>I also don't=
fully understand the desired semantics of the non-template version of eith=
er `bless` or `unbless`.</div></div></blockquote><div><br></div><div>Same a=
s non-template bless.</div><div><br></div><div>Richard's paper infers y=
ou would be able to non-template bless by calling the destructor of some tr=
ivial type e.g. std::byte on a range of memory. Proposed unbless() just doe=
s the same thing in a more convenient function form.</div><div>=C2=A0</div>=
<blockquote class=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.8ex;bor=
der-left: 1px #ccc solid;padding-left: 1ex;"><div dir=3D"ltr"><div><br></di=
v><div>And I don't understand why you need "stun / polymorphic_unb=
less" at all. What does it even mean to make a polymorphic object &quo=
t;unusable"? How is that different from just doing nothing at all?</di=
v></div></blockquote><div><br></div><div>The C++ standard doesn't say h=
ow to implement polymorphism. You are correct that on a vptr based implemen=
tation, one doesn't have to reset the vptrs. But for a non-vptr impleme=
ntation, well who knows?</div><div>=C2=A0</div><blockquote class=3D"gmail_q=
uote" style=3D"margin: 0;margin-left: 0.8ex;border-left: 1px #ccc solid;pad=
ding-left: 1ex;"><div dir=3D"ltr"><div><br></div><div>And if "revive /=
polymorphic_bless" is not a no-op but actually <i>does</i> have an ob=
servable effect on memory, that's a big deal; you should emphasize that=
somehow.</div><div><br></div></div></blockquote><div>That's also an in=
teresting question. I am unsure if the polymorphic implementation is consid=
ered to have <i>any</i>=C2=A0observable effect on memory under the strict l=
anguage of the standard. In other words, vptrs "don't count" =
if that makes sense. This is why I don't claim that polymorphic blessin=
g as you put it has any observable effects on memory, because perhaps with =
some polymorphic implementations it might not?</div><div><br></div><div>Nia=
ll</div>
<p></p>
-- <br />
You received this message because you are subscribed to the Google Groups &=
quot;ISO C++ Standard - Future Proposals" group.<br />
To unsubscribe from this group and stop receiving emails from it, send an e=
mail to <a href=3D"mailto:std-proposals+unsubscribe@isocpp.org">std-proposa=
ls+unsubscribe@isocpp.org</a>.<br />
To post to this group, send email to <a href=3D"mailto:std-proposals@isocpp=
..org">std-proposals@isocpp.org</a>.<br />
To view this discussion on the web visit <a href=3D"https://groups.google.c=
om/a/isocpp.org/d/msgid/std-proposals/58f8fc09-102e-4378-97f9-308b96547a1a%=
40isocpp.org?utm_medium=3Demail&utm_source=3Dfooter">https://groups.google.=
com/a/isocpp.org/d/msgid/std-proposals/58f8fc09-102e-4378-97f9-308b96547a1a=
%40isocpp.org</a>.<br />
------=_Part_586_549836223.1533286030742--
------=_Part_585_229760114.1533286030741--
.
Author: Ben Craig <ben.craig@gmail.com>
Date: Fri, 3 Aug 2018 05:12:24 -0700 (PDT)
Raw View
------=_Part_680_469556232.1533298344893
Content-Type: multipart/alternative;
boundary="----=_Part_681_2125822076.1533298344893"
------=_Part_681_2125822076.1533298344893
Content-Type: text/plain; charset="UTF-8"
I haven't read all the replies here, so apologies if I cover old
territory. Also, this is only feedback on the "high priority" section that
Niall posted.
I think I am fine with the idea of mapped storage. I like the ability to
decouple reachability and lifetime. I will offer some bikeshed fodder,
perhaps "persistent storage"... though that doesn't work great for
non-filesystem backed memory mappings.
I think that a large portion of 3.1.2. should be marked as background
information, or perhaps as forward references, but I think a lot of this
section is at the wrong level of detail for the standard. You should focus
on the effects that mapped storage will have on the abstract machine here.
You can mention that section_handle and map_view are ways to acquire /
manipulate mapped storage, but you shouldn't talk about them in detail here.
"New allocations are guaranteed to be all bits zero on creation."
This is almost always going to be the case in hosted environments for
security reasons. On freestanding implementations, the zero'ing isn't a
sunk cost like it is on hosted implementations. I think that you should
say that the contents are unspecified on creation.
3.1.4
I'm not super interested in mapped polymorphic objects. I am interested in
having objects reachable from multiple mappings simultaneously.
Inter-process communication with shared memory is a real thing, and I think
mapped storage in combination with regular C++11 atomics makes it work.
I expect resistance from the implementers when it comes to modifying the
vptr. Optimizers can currently assume it won't change, except when going
through launder barriers.
I think I would prefer only allowing implicit lifetime types into mapped
memory.
3.2
I generally like it. You might want to see if there is any further
inspiration to draw from gcc assembly clobber lists.
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/91b6e70f-1657-43e1-803c-a119c81ca1b9%40isocpp.org.
------=_Part_681_2125822076.1533298344893
Content-Type: text/html; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable
<div dir=3D"ltr">I haven't read all the replies here, so apologies if I=
cover old territory.=C2=A0 Also, this is only feedback on the "high p=
riority" section that Niall posted.<div><br></div><div><div>I think I =
am fine with the idea of mapped storage.=C2=A0 I like the ability to decoup=
le reachability and lifetime.=C2=A0 I will offer some bikeshed fodder, perh=
aps "persistent storage"... though that doesn't work great fo=
r non-filesystem backed memory mappings.</div><div><br></div><div>I think t=
hat a large portion of 3.1.2. should be marked as background information, o=
r perhaps as forward references, but I think a lot of this section is at th=
e wrong level of detail for the standard.=C2=A0 You should focus on the eff=
ects that mapped storage will have on the abstract machine here.=C2=A0 You =
can mention that section_handle and map_view are ways to acquire / manipula=
te mapped storage, but you shouldn't talk about them in detail here.</d=
iv><div><br></div><div>"New allocations are guaranteed to be all bits =
zero on creation."</div><div>This is almost always going to be the cas=
e in hosted environments for security reasons.=C2=A0 On freestanding implem=
entations, the zero'ing isn't a sunk cost like it is on hosted impl=
ementations.=C2=A0 I think that you should say that the contents are unspec=
ified on creation.</div><div><br></div><div>3.1.4</div><div>I'm not sup=
er interested in mapped polymorphic objects.=C2=A0 I am interested in havin=
g objects reachable from multiple mappings simultaneously.=C2=A0 Inter-proc=
ess communication with shared memory is a real thing, and I think mapped st=
orage in combination with regular C++11 atomics makes it work.</div><div><b=
r></div><div>I expect resistance from the implementers when it comes to mod=
ifying the vptr.=C2=A0 Optimizers can currently assume it won't change,=
except when going through launder barriers.</div><div><br></div><div>I thi=
nk I would prefer only allowing implicit lifetime types into mapped memory.=
</div><div><br></div><div>3.2</div><div>I generally like it.=C2=A0 You migh=
t want to see if there is any further inspiration to draw from gcc assembly=
clobber lists.</div><div><br></div></div></div>
<p></p>
-- <br />
You received this message because you are subscribed to the Google Groups &=
quot;ISO C++ Standard - Future Proposals" group.<br />
To unsubscribe from this group and stop receiving emails from it, send an e=
mail to <a href=3D"mailto:std-proposals+unsubscribe@isocpp.org">std-proposa=
ls+unsubscribe@isocpp.org</a>.<br />
To post to this group, send email to <a href=3D"mailto:std-proposals@isocpp=
..org">std-proposals@isocpp.org</a>.<br />
To view this discussion on the web visit <a href=3D"https://groups.google.c=
om/a/isocpp.org/d/msgid/std-proposals/91b6e70f-1657-43e1-803c-a119c81ca1b9%=
40isocpp.org?utm_medium=3Demail&utm_source=3Dfooter">https://groups.google.=
com/a/isocpp.org/d/msgid/std-proposals/91b6e70f-1657-43e1-803c-a119c81ca1b9=
%40isocpp.org</a>.<br />
------=_Part_681_2125822076.1533298344893--
------=_Part_680_469556232.1533298344893--
.
Author: Niall Douglas <nialldouglas14@gmail.com>
Date: Fri, 3 Aug 2018 09:19:51 -0700 (PDT)
Raw View
------=_Part_729_667063304.1533313192025
Content-Type: multipart/alternative;
boundary="----=_Part_730_1816600461.1533313192026"
------=_Part_730_1816600461.1533313192026
Content-Type: text/plain; charset="UTF-8"
>
>
>> There are also multiple levels of persistability going on here:
>>
>> 1. Trivially copyable types, which are just bits. You can use their
>> in-memory representation directly, just need to constrain write reordering.
>> 2. Trivially relocatable types, which are moved with just bits, if we
>> standardise those. You can use their in-memory representation directly,
>> just need to constrain write reordering.
>> 3. Polymorphic types where if it weren't for the vptr, would be
>> either of the first two categories. You can use their in-memory
>> representation directly, bar the vptr restamp on load.
>> 4. Other types whose in-memory representation can be used directly
>> after some trivial transformation e.g. replace all pointers with offset
>> pointers.
>>
>>
> There may be a common factor in these cases, but there is no common
> *solution* with them. Cases #1 and #2 are fundamentally different from #3
> and #4. Why? Because it's impossible to do 3&4 on an arbitrary range of
> memory.
>
Arbitrary *untyped* range of memory, yes.
>
> In cases 1&2, you can logically move these objects' values around and
> reconstitute them with no per-object code. This means you could just have
> the mapping/unmapping operation perform the "operation" needed to make this
> legal.
>
> With 3&4, you're packing an object's values into a form from which it can
> be unpacked to reconstitute the original object's value. This operation has
> to be done on a per-object basis. That is, you can't just do "unmap range
> of memory, map it latter, do some casting, and go." You have to go to each
> object and perform some operation; code must be executed for each object
> before each unmap and after each map. Also, this operation has to cascade
> up and down the subobject hierarchy, applying that operation to each
> subobject in turn. Preferably in an order similar to how subobjects are
> created and destroyed.
>
I do see that having Reflection to hand could turn this from a compiler
generated vptr restamp operation into a library based one.
The level of UB needed to hack a library implementation of vptr restamping
is hideous, but internal UB by the standard library is explicitly defined
as not UB.
>
> If you're executing code at map/unmap time, you are *doing serialization*.
> It may not be the most complex form of serialization, but it is still
> serialization.
>
Maybe it's just me, but serialisation always involves transforming
representation via copy in my head. Nobody calls manipulating data in a
file directly serialisation. You're just working with data in a file
directly. Allocating memory in a file is no different to allocating it
anywhere else in the system.
>
> As such, I would say that what you're talking about is best handled in the
> C++ object model via what I said: destruction and construction. If you're
> unmapping memory, then any objects within it are destroyed, ending their
> lifetimes. When you map them, a new object is constructed in the storage
> whose values contain the old objects' values
>
> Since this would be done via beginning and ending object lifetimes, that
> provides opportunities to inject user/compiler-code into the process via
> specialized constructors and destructors. For some objects (which may *or
> may not* equate to implicit lifetime or trivially relocatable types),
> these constructors/destructors would be trivial, and as such, we can give
> them special properties and effects.
>
I can get onboard with the idea of ending lifetime and starting lifetime if
it makes things easier.
I disagree in the strongest possible terms that constructors and
destructors ought to be used, even if special ones. That would be severely
anti-social to the user base through being extremely confusing, as we are
overloading a feature already far too overloaded with multiple use cases,
meanings and semantics. If anything, we should be purging possible special
constructors and destructors.
I *could* get onboard some sort of brand new operator. So you apply this
special operator to an object - let's call it @<< - it gets put into an
asleep state, and its lifetime is ended as far as the compiler is
concerned. One can then apply the reverse form of that operator @>> to
waken the object from sleep, and its lifetime is new as far as the compiler
is concerned.
But adding operators to C++ has not gone well, historically. I don't
believe anybody has succeeded since operator new and delete in fact. Not
even operator<<< for rotation, which seems a slam dunk to me at least.
>
> If I were to give a name to this process, I would say that you are
> *trivializing* the value of an object. At its core, what you're wanting
> to do is to take an object, turn its values into data that is no longer
> considered to be "that object", but still is within the same storage. These
> bytes can be copied around verbatim, or the underlying storage can be
> persisted as you desire. You can later *detrivialize* the value back into
> an object of that *exact type*.
>
> The trivialized form of an object is not that object anymore. It's just
> bytes of data. For some objects, its trivial form is exactly its regular
> form. For other objects, this is not the case. But the trivial form has the
> same size as the regular form.
>
I can get aboard with this approach.
>
> A trivializing destructor takes the object and alters its values to make
> it trivial. As implied by this being invoked by a destructor, once you
> trivialize the object's data, the object's lifetime has ended. The storage
> behind a trivialized object is preserved after the object's lifetime has
> ended.
>
> A detrivializing constructor takes the object representation of a
> trivialized object of that type and undoes the trivialization.
>
> The one change to the object model that would be needed to do all of this
> would be a guarantee of the preservation of the values of a
> destroyed/created object. Currently, the C++ object model makes no
> guarantees of the value of the object's storage after its lifetime has
> ended. And when you begin the lifetime of an object, its storage before
> initialization has either zero values (if it's static) or unspecified
> values.
>
Agreed. This is why I really don't think constructors and destructors
should be used for this. They have a specific meaning in people's minds.
Which is a birth/death meaning, not a birth/revive vs death/sleep meaning.
>
> So you would need to say that trivializing destructors preserve the value
> of the memory, and detrivializing initialization/construction operations
> would maintain the storage of the memory at the start of the detrivializing
> constructor. And of course, objects with trivial trivializing operations
> (maybe "trivializing" wasn't such a good term...) can do these implicitly.
>
> You will still need a placement-new replacement to be able to detrivialize
> objects with non-trivial detrivializing construction. The placement part
> obtains storage for the object, and that part is legally allowed to
> overwrite such storage. So you'd need an alternate version which isn't
> allowed to.
>
Operator new also has far too many overloaded meanings and use cases. I
appreciate WG21's reticence to add ever more operators, but sometimes a new
operator really is the right thing to do.
>
> Note that a TriviallyCopyable type is not necessarily an object with
> trivial trivialization (wow, that's terrible naming ;). The reason being:
> pointers. An object with pointers is TriviallyCopyable, so you could
> theoretically map or unmap it. But if those pointers point to mapped
> memory, you need to manually trivialize and detrivialize them.
>
One has two options here. One can either add a new offset pointer to the
language/library. They're pretty low overhead in fact, probably
statistically unmeasurable in real world code. One then makes it a compile
time error to "trivialise"/sleep an object containing ordinary pointers.
The other is some sort of pointer colouring, so your pointer via a
non-deterministic piece of code probably can be deduced into which
map_handle it points into, and if it's the same mapping it is converted
into an offset pointer and permitted, otherwise a runtime error occurs. I'm
not so keen on this approach. I'd prefer to leave it to users to choose if
they want to implement that themselves.
>
> Indeed, I'm starting to wonder about that. It seems that the need for
> trivialization is more a property of the object than of the object's
> *type*. Trivialization ought to be distinct from trivial relocation and
> trivial copying. But it should still be something done via
> constructors/destructors.
>
Agreed, except for the point about constructors/destructors.
>
>>> Would "filesystem storage duration" be better?
>>>
>>
> Let me try to explain again.
>
> Automatic storage duration is named based on its high-level concepts: the
> storage duration of the object is handled "automatically" by the scope of
> the declaration. The memory for that storage may come from something we
> call the "stack", but we don't name it "stack storage duration" even though
> such objects are (nominally) on the "stack".
>
> The same goes for your notion. Both "mapped" and "filesystem" are all
> named based on where the storage comes from. *That* is what is wrong with
> these terms. Terms should be based on the high-level concepts of the kind
> of storage duration you're imposing on the object, not based on where the
> memory is or how it's being interacted with.
>
Let's assume you're persuaded me of an operator based approach with
lifetime ending and beginning, so a mapped storage duration would no longer
be necessary.
> Eh ... you're right that I find blessing underspecified. This is that
>> fuzziness about lifetime I mentioned earlier. I've spent a good few weeks
>> pondering P0593, indeed I asked a ton of questions about it as the SG12
>> meeting at Rapperswil. And I'm confused about what it precisely does.
>>
>
>> Personally, I find reachable vs unreachable a much less ambiguous
>> explanation of what it does.
>>
>
> If you're confused by "what it precisely does", then I'm not sure how good
> an idea it is to impose your own terminology on it. You need to understand
> what it currently means before you can say that your terminology describes
> that meaning better or worse. After all, if you don't understand it, your
> terminology could be imposing different behavior than what it's trying to
> do.
>
I'd like to think of it as feedback on P0593's choice of wording from
somebody who studied the paper for some weeks :)
>
> From the section I linked to (emphasis added):
>
> Specifically, the value held by an object is only stable *throughout its
>> lifetime*. When the lifetime of the int object in line #1 ends (when its
>> storage is reused by the float object in line #2), *its value is gone*.
>> Symmetrically, when the float object is created, the object has an
>> indeterminate value ([dcl.init]p12), and therefore *any attempt to load
>> its value results in undefined behavior*.
>>
>
> The thing about P0593 is that it's actually a bit inconsistent about this.
> Since we don't have actual standards wording, we have to do the best we can
> with the details we've been given. The area of inconsistency is that
> Section 4.1 of the same document says:
>
> In some cases it is desirable to change the dynamic type of existing
>> storage while maintaining the object representation. If the destination
>> type is an implicit lifetime type, this can be accomplished by usage of
>> std::bless to change the type, followed by std::launder to acquire a
>> pointer to the newly-created object.
>>
>
> That section can only be true if the previously quoted section is *false*.
> If there's a newly created object in that storage, the only way to access
> the value from the recently-destroyed object that used to live there is if
> it's value is not "gone".
>
> However, section 4.1 is the odd man out in P0593. Throughout that paper,
> it refers to the operation which `bless` performs as "creating objects". If
> you create an object in storage that already contains an object, then you
> are reusing that object's storage. Reusing an object's storage ends that
> object's lifetime.
>
> And the C++ object model is very clear: the value of an object whose
> lifetime has ended is not preserved. The contents of an object's storage
> after its lifetime has ended is not defined by the standard.
>
> So my reading of P0593 is that if you `bless` a range of memory, any
> objects already residing there have their lifetimes ended. The objects
> which have been created by this process therefore have unspecified values,
> since all previous objects in that storage have been reused.
>
> And once an object's lifetime ends, the value of that object is gone.
>
That is a valid reading of Richard's paper. Though, if one blesses a region
holding a type T, and thereafter uses it as a type T, I think that is also
valid reading of Richard's paper. As far as I can tell, blessing in this
situation simply causes the compiler to reload state from that region.
>
> Basically, consider this:
>
> int i = 45;
> int *pi = new(&i) int;
> std::cout << i;
>
> The standard says that this is not guaranteed to print 45. My reading of
> P0593 is that the following is the equivalent of the above:
>
> int i = 45;
> std::bless(&i, sizeof(int)); //"Creates objects" in `i`.
> std::cout << i; //Newly created `int` object, due to `bless` and access
>
> So this is not guaranteed to print 45 either. And neither
> `reinterpret_cast` nor `launder` would change that.
>
I don't see any evidence of this interpretation in Richard's paper.
Blessing is when you tell the compiler "I am telling you that whatever
resides in this range of bytes contains valid objects". As far as I can
tell, if you then access those bytes as a type T, the compiler just takes
your word on it.
Now, equally, Richard's paper does say that blessing can't be used to type
pun any more than reinterpret cast can. But if storage contains some valid
T, and you reinterpret that storage as something else, not modifying it,
and then reinterpret it back into T, that's guaranteed valid in today's
standard, and as far as I understand Richard's paper, that still remains
the case with blessing as it is explicitly guaranteed to not modify what
you bless.
>
> Now, I could be misinterpreting P0593. Like I said, it doesn't have formal
> standards wording. But the frequent use of the phrase "create objects",
> coupled with the *explicit* prohibition of type punning, suggests that
> I'm probably right.
>
I don't find any fault in your reading of P0593. But I don't think it's the
only valid reading of that paper. As I mentioned, I find that paper a bit
confusing. I think speaking in terms of reachability and non-reachability
would greatly improve the clarity of P0593. And the ability of the compiler
to reason about the code. But I am not a compiler writer, and Richard is.
Maybe he'll comment at some point.
Niall
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/ee24577f-0988-4c40-93ea-190d1a364d2c%40isocpp.org.
------=_Part_730_1816600461.1533313192026
Content-Type: text/html; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable
<blockquote class=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.8ex;bor=
der-left: 1px #ccc solid;padding-left: 1ex;"><div dir=3D"ltr"><blockquote c=
lass=3D"gmail_quote" style=3D"margin:0;margin-left:0.8ex;border-left:1px #c=
cc solid;padding-left:1ex"><div dir=3D"ltr"><div><br></div><div>There are a=
lso multiple levels of persistability going on here:</div><div><ol><li>Triv=
ially copyable types, which are just bits. You can use their in-memory repr=
esentation directly, just need to constrain write reordering.</li><li>Trivi=
ally relocatable types, which are moved with just bits, if we standardise t=
hose. You can use their in-memory representation directly, just need to con=
strain write reordering.</li><li>Polymorphic types where if it weren't =
for the vptr, would be either of the first two categories. You can use thei=
r in-memory representation directly, bar the vptr restamp on load.</li><li>=
Other types whose in-memory representation can be used directly after some =
trivial transformation e.g. replace all pointers with offset pointers.</li>=
</ol></div></div></blockquote><div><br></div><div>There may be a common fac=
tor in these cases, but there is no common <i>solution</i> with them. Cases=
#1 and #2 are fundamentally different from #3 and #4. Why? Because it'=
s impossible to do 3&4 on an arbitrary range of memory.</div></div></bl=
ockquote><div><br></div><div>Arbitrary <i>untyped</i>=C2=A0range of memory,=
yes.</div><div>=C2=A0</div><blockquote class=3D"gmail_quote" style=3D"marg=
in: 0;margin-left: 0.8ex;border-left: 1px #ccc solid;padding-left: 1ex;"><d=
iv dir=3D"ltr"><div><br></div><div>In cases 1&2, you can logically move=
these objects' values around and reconstitute them with no per-object =
code. This means you could just have the mapping/unmapping operation perfor=
m the "operation" needed to make this legal.<br></div><div><br></=
div><div>With 3&4, you're packing an object's values into a for=
m from which it can be unpacked to reconstitute the original object's v=
alue. This operation has to be done on a per-object basis. That is, you can=
't just do "unmap range of memory, map it latter, do some casting,=
and go." You have to go to each object and perform some operation; co=
de must be executed for each object before each unmap and after each map. A=
lso, this operation has to cascade up and down the subobject hierarchy, app=
lying that operation to each subobject in turn. Preferably in an order simi=
lar to how subobjects are created and destroyed.<br></div></div></blockquot=
e><div><br></div><div>I do see that having Reflection to hand could turn th=
is from a compiler generated vptr restamp operation into a library based on=
e.</div><div><br></div><div>The level of UB needed to hack a library implem=
entation of vptr restamping is hideous, but internal UB by the standard lib=
rary is explicitly defined as not UB.</div><div>=C2=A0</div><blockquote cla=
ss=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.8ex;border-left: 1px #=
ccc solid;padding-left: 1ex;"><div dir=3D"ltr"><div></div><div><br></div><d=
iv>If you're executing code at map/unmap time, you are <i>doing seriali=
zation</i>. It may not be the most complex form of serialization, but it is=
still serialization.</div></div></blockquote><div><br></div><div>Maybe it&=
#39;s just me, but serialisation always involves transforming representatio=
n via copy in my head. Nobody calls manipulating data in a file directly se=
rialisation. You're just working with data in a file directly. Allocati=
ng memory in a file is no different to allocating it anywhere else in the s=
ystem.</div><div>=C2=A0</div><blockquote class=3D"gmail_quote" style=3D"mar=
gin: 0;margin-left: 0.8ex;border-left: 1px #ccc solid;padding-left: 1ex;"><=
div dir=3D"ltr"><div><br></div><div>As such, I would say that what you'=
re talking about is best handled in the C++ object model via what I said: d=
estruction and construction. If you're unmapping memory, then any objec=
ts within it are destroyed, ending their lifetimes. When you map them, a ne=
w object is constructed in the storage whose values contain the old objects=
' values<br></div><div><br></div><div>Since this would be done via begi=
nning and ending object lifetimes, that provides opportunities to inject us=
er/compiler-code into the process via specialized constructors and destruct=
ors. For some objects (which may <i>or may not</i> equate to implicit lifet=
ime or trivially relocatable types), these constructors/destructors would b=
e trivial, and as such, we can give them special properties and effects.<br=
></div></div></blockquote><div><br></div><div>I can get onboard with the id=
ea of ending lifetime and starting lifetime if it makes things easier.</div=
><div><br></div><div>I disagree in the strongest possible terms that constr=
uctors and destructors ought to be used, even if special ones. That would b=
e severely anti-social to the user base through being extremely confusing, =
as we are overloading a feature already far too overloaded with multiple us=
e cases, meanings and semantics. If anything, we should be purging possible=
special constructors and destructors.</div><div><br></div><div>I <i>could<=
/i>=C2=A0get onboard some sort of brand new operator. So you apply this spe=
cial operator to an object - let's call it @<< - it gets put into=
an asleep state, and its lifetime is ended as far as the compiler is conce=
rned. One can then apply the reverse form of that operator @>> to wak=
en the object from sleep, and its lifetime is new as far as the compiler is=
concerned.</div><div><br></div><div>But adding operators to C++ has not go=
ne well, historically. I don't believe anybody has succeeded since oper=
ator new and delete in fact. Not even operator<<< for rotation, wh=
ich seems a slam dunk to me at least.</div><div>=C2=A0</div><blockquote cla=
ss=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.8ex;border-left: 1px #=
ccc solid;padding-left: 1ex;"><div dir=3D"ltr"><div></div><div><br></div><d=
iv>If I were to give a name to this process, I would say that you are <i>tr=
ivializing</i> the value of an object. At its core, what you're wanting=
to do is to take an object, turn its values into data that is no longer co=
nsidered to be "that object", but still is within the same storag=
e. These bytes can be copied around verbatim, or the underlying storage can=
be persisted as you desire. You can later <i>detrivialize</i> the value ba=
ck into an object of that <i>exact type</i>.</div><div><br></div><div>The t=
rivialized form of an object is not that object anymore. It's just byte=
s of data. For some objects, its trivial form is exactly its regular form. =
For other objects, this is not the case. But the trivial form has the same =
size as the regular form.</div></div></blockquote><div><br></div><div>I can=
get aboard with this approach.</div><div>=C2=A0</div><blockquote class=3D"=
gmail_quote" style=3D"margin: 0;margin-left: 0.8ex;border-left: 1px #ccc so=
lid;padding-left: 1ex;"><div dir=3D"ltr"><div><br></div><div></div><div>A t=
rivializing destructor takes the object and alters its values to make it tr=
ivial. As implied by this being invoked by a destructor, once you trivializ=
e the object's data, the object's lifetime has ended. The storage b=
ehind a trivialized object is preserved after the object's lifetime has=
ended.<br></div><div><br></div><div>A detrivializing constructor takes the=
object representation of a trivialized object of that type and undoes the =
trivialization.</div><div><br></div><div>The one change to the object model=
that would be needed to do all of this would be a guarantee of the preserv=
ation of the values of a destroyed/created object. Currently, the C++ objec=
t model makes no guarantees of the value of the object's storage after =
its lifetime has ended. And when you begin the lifetime of an object, its s=
torage before initialization has either zero values (if it's static) or=
unspecified values.<br></div></div></blockquote><div><br></div><div>Agreed=
.. This is why I really don't think constructors and destructors should =
be used for this. They have a specific meaning in people's minds. Which=
is a birth/death meaning, not a birth/revive vs death/sleep meaning.</div>=
<div>=C2=A0</div><blockquote class=3D"gmail_quote" style=3D"margin: 0;margi=
n-left: 0.8ex;border-left: 1px #ccc solid;padding-left: 1ex;"><div dir=3D"l=
tr"><div></div><div><br></div><div>So you would need to say that trivializi=
ng destructors preserve the value of the memory, and detrivializing initial=
ization/construction operations would maintain the storage of the memory at=
the start of the detrivializing constructor. And of course, objects with t=
rivial trivializing operations (maybe "trivializing" wasn't s=
uch a good term...) can do these implicitly.</div><div><br></div><div>You w=
ill still need a placement-new replacement to be able to detrivialize objec=
ts with non-trivial detrivializing construction. The placement part obtains=
storage for the object, and that part is legally allowed to overwrite such=
storage. So you'd need an alternate version which isn't allowed to=
..</div></div></blockquote><div><br></div><div>Operator new also has far too=
many overloaded meanings and use cases. I appreciate WG21's reticence =
to add ever more operators, but sometimes a new operator really is the righ=
t thing to do.</div><div>=C2=A0</div><blockquote class=3D"gmail_quote" styl=
e=3D"margin: 0;margin-left: 0.8ex;border-left: 1px #ccc solid;padding-left:=
1ex;"><div dir=3D"ltr"><div><br></div><div>Note that a TriviallyCopyable t=
ype is not necessarily an object with trivial trivialization (wow, that'=
;s terrible naming ;). The reason being: pointers. An object with pointers =
is TriviallyCopyable, so you could theoretically map or unmap it. But if th=
ose pointers point to mapped memory, you need to manually trivialize and de=
trivialize them.</div></div></blockquote><div><br></div><div>One has two op=
tions here. One can either add a new offset pointer to the language/library=
.. They're pretty low overhead in fact, probably statistically unmeasura=
ble in real world code. One then makes it a compile time error to "tri=
vialise"/sleep an object containing ordinary pointers.</div><div><br><=
/div><div>The other is some sort of pointer colouring, so your pointer via =
a non-deterministic piece of code probably can be deduced into which map_ha=
ndle it points into, and if it's the same mapping it is converted into =
an offset pointer and permitted, otherwise a runtime error occurs. I'm =
not so keen on this approach. I'd prefer to leave it to users to choose=
if they want to implement that themselves.</div><div>=C2=A0</div><blockquo=
te class=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.8ex;border-left:=
1px #ccc solid;padding-left: 1ex;"><div dir=3D"ltr"><div><br></div><div>In=
deed, I'm starting to wonder about that. It seems that the need for tri=
vialization is more a property of the object than of the object's <i>ty=
pe</i>. Trivialization ought to be distinct from trivial relocation and tri=
vial copying. But it should still be something done via constructors/destru=
ctors.<br></div></div></blockquote><div><br></div><div>Agreed, except for t=
he point about constructors/destructors.</div><div>=C2=A0</div><blockquote =
class=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.8ex;border-left: 1p=
x #ccc solid;padding-left: 1ex;"><div dir=3D"ltr"><div></div><blockquote cl=
ass=3D"gmail_quote" style=3D"margin:0;margin-left:0.8ex;border-left:1px #cc=
c solid;padding-left:1ex"><div dir=3D"ltr"><blockquote class=3D"gmail_quote=
" style=3D"margin:0;margin-left:0.8ex;border-left:1px #ccc solid;padding-le=
ft:1ex"><div dir=3D"ltr"><div dir=3D"ltr"><div><br></div><div>Would "f=
ilesystem storage duration" be better?</div></div></div></blockquote><=
/div></blockquote><div><br></div><div>Let me try to explain again.</div><di=
v><br></div><div>Automatic storage duration is named based on its high-leve=
l concepts: the storage duration of the object is handled "automatical=
ly" by the scope of the declaration. The memory for that storage may c=
ome from something we call the "stack", but we don't name it =
"stack storage duration" even though such objects are (nominally)=
on the "stack".<br></div><div><br></div><div>The same goes for y=
our notion. Both "mapped" and "filesystem" are all name=
d based on where the storage comes from. <i>That</i> is what is wrong with =
these terms. Terms should be based on the high-level concepts of the kind o=
f storage duration you're imposing on the object, not based on where th=
e memory is or how it's being interacted with.</div></div></blockquote>=
<div><br></div><div>Let's assume you're persuaded me of an operator=
based approach with lifetime ending and beginning, so a mapped storage dur=
ation would no longer be necessary.</div><div>=C2=A0</div><blockquote class=
=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.8ex;border-left: 1px #cc=
c solid;padding-left: 1ex;"><div dir=3D"ltr"><div></div><blockquote class=
=3D"gmail_quote" style=3D"margin:0;margin-left:0.8ex;border-left:1px #ccc s=
olid;padding-left:1ex"><div dir=3D"ltr"><div>Eh ... you're right that I=
find blessing underspecified. This is that fuzziness about lifetime I ment=
ioned earlier. I've spent a good few weeks pondering P0593, indeed I as=
ked a ton of questions about it as the SG12 meeting at Rapperswil. And I=
9;m confused about what it precisely does.<br></div></div></blockquote><blo=
ckquote class=3D"gmail_quote" style=3D"margin:0;margin-left:0.8ex;border-le=
ft:1px #ccc solid;padding-left:1ex"><div dir=3D"ltr"><div><br></div><div>Pe=
rsonally, I find reachable vs unreachable a much less ambiguous explanation=
of what it does.</div></div></blockquote><div><br></div><div>If you're=
confused by "what it precisely does", then I'm not sure how =
good an idea it is to impose your own terminology on it. You need to unders=
tand what it currently means before you can say that your terminology descr=
ibes that meaning better or worse. After all, if you don't understand i=
t, your terminology could be imposing different behavior than what it's=
trying to do.<br></div></div></blockquote><div><br></div><div>I'd like=
to think of it as feedback on P0593's choice of wording from somebody =
who studied the paper for some weeks :)</div><div>=C2=A0</div><blockquote c=
lass=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.8ex;border-left: 1px=
#ccc solid;padding-left: 1ex;"><div dir=3D"ltr"><div></div><div><br></div>=
</div></blockquote><blockquote class=3D"gmail_quote" style=3D"margin: 0;mar=
gin-left: 0.8ex;border-left: 1px #ccc solid;padding-left: 1ex;"><div dir=3D=
"ltr"><div>From the section I linked to (emphasis added):</div><div><br></d=
iv><blockquote class=3D"gmail_quote" style=3D"margin:0px 0px 0px 0.8ex;bord=
er-left:1px solid rgb(204,204,204);padding-left:1ex"><div>Specifically, the=
value held by an object is only stable <b>throughout its lifetime</b>. Whe=
n the lifetime of the int object in line #1 ends (when its storage is reuse=
d by the float object in line #2), <b>its value is gone</b>. Symmetrically,=
when the float object is created, the object has an indeterminate value ([=
dcl.init]p12), and therefore <b>any attempt to load its value results in un=
defined behavior</b>.<br></div></blockquote><div><br></div><div>The thing a=
bout P0593 is that it's actually a bit inconsistent about this. Since w=
e don't have actual standards wording, we have to do the best we can wi=
th the details we've been given. The area of inconsistency is that Sect=
ion 4.1 of the same document says:</div><div><br></div><blockquote class=3D=
"gmail_quote" style=3D"margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(2=
04,204,204);padding-left:1ex"><div>In some cases it is desirable to change =
the dynamic type of existing storage while maintaining the object represent=
ation. If the destination type is an implicit lifetime type, this can be ac=
complished by usage of std::bless to change the type, followed by std::laun=
der to acquire a pointer to the newly-created object.<br></div></blockquote=
><div><br></div><div>That section can only be true if the previously quoted=
section is <i>false</i>. If there's a newly created object in that sto=
rage, the only way to access the value from the recently-destroyed object t=
hat used to live there is if it's value is not "gone".</div><=
div><br></div><div>However, section 4.1 is the odd man out in P0593. Throug=
hout that paper, it refers to the operation which `bless` performs as "=
;creating objects". If you create an object in storage that already co=
ntains an object, then you are reusing that object's storage. Reusing a=
n object's storage ends that object's lifetime.</div><div><br></div=
><div>And the C++ object model is very clear: the value of an object whose =
lifetime has ended is not preserved. The contents of an object's storag=
e after its lifetime has ended is not defined by the standard.<br></div><di=
v><br></div><div>So my reading of P0593 is that if you `bless` a range of m=
emory, any objects already residing there have their lifetimes ended. The o=
bjects which have been created by this process therefore have unspecified v=
alues, since all previous objects in that storage have been reused.<br></di=
v><div><br></div><div>And once an object's lifetime ends, the value of =
that object is gone.</div></div></blockquote><div><br></div><div>That is a =
valid reading of Richard's paper. Though, if one blesses a region holdi=
ng a type T, and thereafter uses it as a type T, I think that is also valid=
reading of Richard's paper. As far as I can tell, blessing in this sit=
uation simply causes the compiler to reload state from that region.</div><d=
iv>=C2=A0</div><blockquote class=3D"gmail_quote" style=3D"margin: 0;margin-=
left: 0.8ex;border-left: 1px #ccc solid;padding-left: 1ex;"><div dir=3D"ltr=
"><div><br></div><div>Basically, consider this:</div><div><br></div><div st=
yle=3D"background-color:rgb(250,250,250);border-color:rgb(187,187,187);bord=
er-style:solid;border-width:1px"><code><div><span style=3D"color:#008">int<=
/span><span style=3D"color:#000"> i </span><span style=3D"color:#660">=3D</=
span><span style=3D"color:#000"> </span><span style=3D"color:#066">45</span=
><span style=3D"color:#660">;</span><span style=3D"color:#000"><br></span><=
span style=3D"color:#008">int</span><span style=3D"color:#000"> </span><spa=
n style=3D"color:#660">*</span><span style=3D"color:#000">pi </span><span s=
tyle=3D"color:#660">=3D</span><span style=3D"color:#000"> </span><span styl=
e=3D"color:#008">new</span><span style=3D"color:#660">(&</span><span st=
yle=3D"color:#000">i</span><span style=3D"color:#660">)</span><span style=
=3D"color:#000"> </span><span style=3D"color:#008">int</span><span style=3D=
"color:#660">;</span><span style=3D"color:#000"><br>std</span><span style=
=3D"color:#660">::</span><span style=3D"color:#000">cout </span><span style=
=3D"color:#660"><<</span><span style=3D"color:#000"> i</span><span st=
yle=3D"color:#660">;</span></div></code></div><div><br></div><div></div><di=
v>The standard says that this is not guaranteed to print 45. My reading of =
P0593 is that the following is the equivalent of the above:</div><div><br><=
/div><div style=3D"background-color:rgb(250,250,250);border-color:rgb(187,1=
87,187);border-style:solid;border-width:1px"><code><div><span style=3D"colo=
r:#008">int</span><span style=3D"color:#000"> i </span><span style=3D"color=
:#660">=3D</span><span style=3D"color:#000"> </span><span style=3D"color:#0=
66">45</span><span style=3D"color:#660">;</span><span style=3D"color:#000">=
<br>std</span><span style=3D"color:#660">::</span><span style=3D"color:#000=
">bless</span><span style=3D"color:#660">(&</span><span style=3D"color:=
#000">i</span><span style=3D"color:#660">,</span><span style=3D"color:#000"=
> </span><span style=3D"color:#008">sizeof</span><span style=3D"color:#660"=
>(</span><span style=3D"color:#008">int</span><span style=3D"color:#660">))=
;</span><span style=3D"color:#000"> </span><span style=3D"color:#800">//&qu=
ot;Creates objects" in `i`.</span><span style=3D"color:#000"><br>std</=
span><span style=3D"color:#660">::</span><span style=3D"color:#000">cout </=
span><span style=3D"color:#660"><<</span><span style=3D"color:#000"> =
i</span><span style=3D"color:#660">;</span><span style=3D"color:#000"> </sp=
an><span style=3D"color:#800">//Newly created `int` object, due to `bless` =
and access</span><span style=3D"color:#000"><br></span></div></code></div><=
div><br></div><div></div><div>So this is not guaranteed to print 45 either.=
And neither `reinterpret_cast` nor `launder` would change that.</div></div=
></blockquote><div><br></div><div>I don't see any evidence of this inte=
rpretation in Richard's paper. Blessing is when you tell the compiler &=
quot;I am telling you that whatever resides in this range of bytes contains=
valid objects". As far as I can tell, if you then access those bytes =
as a type T, the compiler just takes your word on it.</div><div><br></div><=
div>Now, equally, Richard's paper does say that blessing can't be u=
sed to type pun any more than reinterpret cast can. But if storage contains=
some valid T, and you reinterpret that storage as something else, not modi=
fying it, and then reinterpret it back into T, that's guaranteed valid =
in today's standard, and as far as I understand Richard's paper, th=
at still remains the case with blessing as it is explicitly guaranteed to n=
ot modify what you bless.</div><div>=C2=A0</div><blockquote class=3D"gmail_=
quote" style=3D"margin: 0;margin-left: 0.8ex;border-left: 1px #ccc solid;pa=
dding-left: 1ex;"><div dir=3D"ltr"><div><br></div><div>Now, I could be misi=
nterpreting P0593. Like I said, it doesn't have formal standards wordin=
g. But the frequent use of the phrase "create objects", coupled w=
ith the <i>explicit</i> prohibition of type punning, suggests that I'm =
probably right.</div></div></blockquote><div><br></div><div>I don't fin=
d any fault in your reading of P0593. But I don't think it's the on=
ly valid reading of that paper. As I mentioned, I find that paper a bit con=
fusing. I think speaking in terms of reachability and non-reachability woul=
d greatly improve the clarity of P0593. And the ability of the compiler to =
reason about the code. But I am not a compiler writer, and Richard is. Mayb=
e he'll comment at some point.</div><div><br></div><div>Niall</div><div=
><br></div>
<p></p>
-- <br />
You received this message because you are subscribed to the Google Groups &=
quot;ISO C++ Standard - Future Proposals" group.<br />
To unsubscribe from this group and stop receiving emails from it, send an e=
mail to <a href=3D"mailto:std-proposals+unsubscribe@isocpp.org">std-proposa=
ls+unsubscribe@isocpp.org</a>.<br />
To post to this group, send email to <a href=3D"mailto:std-proposals@isocpp=
..org">std-proposals@isocpp.org</a>.<br />
To view this discussion on the web visit <a href=3D"https://groups.google.c=
om/a/isocpp.org/d/msgid/std-proposals/ee24577f-0988-4c40-93ea-190d1a364d2c%=
40isocpp.org?utm_medium=3Demail&utm_source=3Dfooter">https://groups.google.=
com/a/isocpp.org/d/msgid/std-proposals/ee24577f-0988-4c40-93ea-190d1a364d2c=
%40isocpp.org</a>.<br />
------=_Part_730_1816600461.1533313192026--
------=_Part_729_667063304.1533313192025--
.
Author: Niall Douglas <nialldouglas14@gmail.com>
Date: Fri, 3 Aug 2018 09:34:41 -0700 (PDT)
Raw View
------=_Part_710_1719717014.1533314081337
Content-Type: multipart/alternative;
boundary="----=_Part_711_473520778.1533314081337"
------=_Part_711_473520778.1533314081337
Content-Type: text/plain; charset="UTF-8"
>
>
> "New allocations are guaranteed to be all bits zero on creation."
> This is almost always going to be the case in hosted environments for
> security reasons. On freestanding implementations, the zero'ing isn't a
> sunk cost like it is on hosted implementations. I think that you should
> say that the contents are unspecified on creation.
>
I haven't written the documentation for section_handle yet, but you'll find
that the unbacked constructor will say exactly this. The backed constructor
says nothing, because it's the backing file_handle which does the zeroing
when you extend the file. And zeroing here isn't writing zeros to memory,
it's the kernel zero page, on first write the page fault then allocates
storage on the drive which is only then zeroed on non-TRIM devices.
Historically some embedded systems with filing systems did not zero file
extensions, but I am unaware of any major embedded systems with filing
systems in the past decade which don't. For example Windows CE didn't
originally, but then started to because it was a giant security hole.
>
> 3.1.4
> I'm not super interested in mapped polymorphic objects. I am interested
> in having objects reachable from multiple mappings simultaneously.
> Inter-process communication with shared memory is a real thing, and I think
> mapped storage in combination with regular C++11 atomics makes it work.
>
Technically speaking, if you read the standard wording, it currently
provides no guarantees that atomics work outside of the threads running in
the current process.
I am all for your help in changing that, but I would assume that your plate
is fairly full right now.
>
> I expect resistance from the implementers when it comes to modifying the
> vptr. Optimizers can currently assume it won't change, except when going
> through launder barriers.
>
Maybe Nicol is right. If we end and start lifetime, that enables vptr
restamping within the current object lifetime framework. But you are right,
the compiler vendors are between cold and freezing on this proposal.
> 3.2
> I generally like it. You might want to see if there is any further
> inspiration to draw from gcc assembly clobber lists.
>
> It's probably not widely known yet, but there is a new effort to deprecate
volatile in C++ which may or may not become a big push soon. So we may
actually see a std::clobber(std::byte *, std::size_t) which tells the
compiler to reload any bytes within that range.
Niall
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/d6c1a29e-eab4-460e-a4e7-22faa32a50e6%40isocpp.org.
------=_Part_711_473520778.1533314081337
Content-Type: text/html; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable
<div dir=3D"ltr"><blockquote class=3D"gmail_quote" style=3D"margin: 0;margi=
n-left: 0.8ex;border-left: 1px #ccc solid;padding-left: 1ex;"><div dir=3D"l=
tr"><div><div><br></div><div>"New allocations are guaranteed to be all=
bits zero on creation."</div><div>This is almost always going to be t=
he case in hosted environments for security reasons.=C2=A0 On freestanding =
implementations, the zero'ing isn't a sunk cost like it is on hoste=
d implementations.=C2=A0 I think that you should say that the contents are =
unspecified on creation.</div></div></div></blockquote><div><br></div><div>=
I haven't written the documentation for section_handle yet, but you'=
;ll find that the unbacked constructor will say exactly this. The backed co=
nstructor says nothing, because it's the backing file_handle which does=
the zeroing when you extend the file. And zeroing here isn't writing z=
eros to memory, it's the kernel zero page, on first write the page faul=
t then allocates storage on the drive which is only then zeroed on non-TRIM=
devices.</div><div><br></div><div>Historically some embedded systems with =
filing systems did not zero file extensions, but I am unaware of any major =
embedded systems with filing systems in the past decade which don't. Fo=
r example Windows CE didn't originally, but then started to because it =
was a giant security hole.</div><div>=C2=A0</div><blockquote class=3D"gmail=
_quote" style=3D"margin: 0;margin-left: 0.8ex;border-left: 1px #ccc solid;p=
adding-left: 1ex;"><div dir=3D"ltr"><div><div><br></div><div>3.1.4</div><di=
v>I'm not super interested in mapped polymorphic objects.=C2=A0 I am in=
terested in having objects reachable from multiple mappings simultaneously.=
=C2=A0 Inter-process communication with shared memory is a real thing, and =
I think mapped storage in combination with regular C++11 atomics makes it w=
ork.</div></div></div></blockquote><div><br></div><div>Technically speaking=
, if you read the standard wording, it currently provides no guarantees tha=
t atomics work outside of the threads running in the current process.</div>=
<div><br></div><div>I am all for your help in changing that, but I would as=
sume that your plate is fairly full right now.</div><div>=C2=A0</div><block=
quote class=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.8ex;border-le=
ft: 1px #ccc solid;padding-left: 1ex;"><div dir=3D"ltr"><div><div><br></div=
><div>I expect resistance from the implementers when it comes to modifying =
the vptr.=C2=A0 Optimizers can currently assume it won't change, except=
when going through launder barriers.</div></div></div></blockquote><div><b=
r></div><div>Maybe Nicol is right. If we end and start lifetime, that enabl=
es vptr restamping within the current object lifetime framework. But you ar=
e right, the compiler vendors are between cold and freezing on this proposa=
l.</div><div>=C2=A0</div><blockquote class=3D"gmail_quote" style=3D"margin:=
0;margin-left: 0.8ex;border-left: 1px #ccc solid;padding-left: 1ex;"><div =
dir=3D"ltr"><div>3.2<br></div><div>I generally like it.=C2=A0 You might wan=
t to see if there is any further inspiration to draw from gcc assembly clob=
ber lists.</div><div><br></div></div></blockquote><div>It's probably no=
t widely known yet, but there is a new effort to deprecate volatile in C++ =
which may or may not become a big push soon. So we may actually see a std::=
clobber(std::byte *, std::size_t) which tells the compiler to reload any by=
tes within that range.</div><div><br></div><div>Niall</div><div><br></div><=
/div>
<p></p>
-- <br />
You received this message because you are subscribed to the Google Groups &=
quot;ISO C++ Standard - Future Proposals" group.<br />
To unsubscribe from this group and stop receiving emails from it, send an e=
mail to <a href=3D"mailto:std-proposals+unsubscribe@isocpp.org">std-proposa=
ls+unsubscribe@isocpp.org</a>.<br />
To post to this group, send email to <a href=3D"mailto:std-proposals@isocpp=
..org">std-proposals@isocpp.org</a>.<br />
To view this discussion on the web visit <a href=3D"https://groups.google.c=
om/a/isocpp.org/d/msgid/std-proposals/d6c1a29e-eab4-460e-a4e7-22faa32a50e6%=
40isocpp.org?utm_medium=3Demail&utm_source=3Dfooter">https://groups.google.=
com/a/isocpp.org/d/msgid/std-proposals/d6c1a29e-eab4-460e-a4e7-22faa32a50e6=
%40isocpp.org</a>.<br />
------=_Part_711_473520778.1533314081337--
------=_Part_710_1719717014.1533314081337--
.
Author: Nicol Bolas <jmckesson@gmail.com>
Date: Fri, 3 Aug 2018 10:33:25 -0700 (PDT)
Raw View
------=_Part_699_424605402.1533317605676
Content-Type: multipart/alternative;
boundary="----=_Part_700_604177518.1533317605676"
------=_Part_700_604177518.1533317605676
Content-Type: text/plain; charset="UTF-8"
I understand your point on the whole parallel constructor/destructor thing.
The reason I suggested to use them is that it fits within the existing
paradigm, which means you don't have to do something like create a new
series of special member functions. But making new functions may be the
cleaner alternative.
I'm going to invent some new strawman terminology because "trivialization"
just isn't working out. Let's call it "reduction" and "regeneration". So
every type has "reducers", which work like destructors in that they end the
object's lifetime, but they ensure that the object's values remain present.
And every type has "regenerators", which work like constructors in that
they begin the object's lifetime but can access the reduced value of the
objects in their current space.
Both reduction and regeneration functions need to be able to take arbitrary
parameters. And you need special syntax to invoke regeneration, for a
specific type, on a piece of memory.
Reduction and regeneration can be trivial operations. For implicit lifetime
and TriviallyRelocatable types, reduction can be done trivially. But you
can also perform non-trivial reduction/regeneration of specific objects of
those types.
This is actually a lot like the relationship between such types and their
constructors. implicit lifetime types can have non-trivial constructors,
but there are still trivial ways of interacting with them.
Some operations invoke "trivial reduction" on all complete objects in a
piece of storage. These would be things like functions that unmap objects,
but much like your original `unbless`, we can have a function to directly
invoke this process. If that storage contains objects which do not have
trivial reduction, undefined behavior results. So if you want to do
non-trivial reduction, you have to do it before performing such reduction.
Similarly, some operations invoke "trivial regeneration", like the memory
mapping process. And this is where we have to start getting into
`bless`-like wording, where objects of trivially regeneratable types can
"magically" appear based on usage. But you can also explicitly invoke
non-trivial regeneration for specific objects, which causes those objects
to pop into being.
So let's explore some rules. Trivial reduction requires:
* There is no user-provided default reducer overload.
* All subobjects must have trivial reduction.
* The type must be TriviallyRelocatable or Implicit Lifetime (which
requires a trivial destructor).
And similarly, trivial regeneration requires:
* There is no user-provided default regeration overload.
* All subobjects must have trivial regeneration.
* The type must be TriviallyRelocatable or Implicit Lifetime.
So, here's what we need:
1. Syntax for declaring reducers/regenerator member functions. It needs to
not conflict with existing names.
* Regenerators probably need similar abilities that constructors have.
Things like member initializer lists, inheriting regenerators, forwarding
regenerators, etc.
2. Changes to the object model to allow for reducers/regenerators to
destroy/create objects, but preserving the stored bitpattern of the results
of the reduction and providing it to the regenerator on regeneration.
* There needs to be some idea of how exceptions work with regeneration.
That is, when exactly each subobject is considered live, so that exceptions
thrown from later subobject regeneration can destroy them. These rules
could work much like the rules of constructors, but we need to spell them
out all the same.
3. Syntax for invoking regeneration on a piece of memory (which is required
to contain the data from a previous reduction operation for an object of
that type).
4. Syntax for invoking reduction on an object. It would probably look like
an explicit destructor call.
Thinking about this further, "trivial relocation" could be redefined as
trivial reduction, memcpy, and trivial regeneration. And thus, a type is
trivially relocatable if it has no-argument reducer and regenerators that
are trivial, which can be declared with `= default` syntax.
Now, I'm not saying we should go changing Arthur's proposal, since it is
much farther along than this. But it would allow us to have a second way to
declare that a type is TriviallyRelocatable.
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/60f7f1a2-1b35-4166-866c-6002579d0ba2%40isocpp.org.
------=_Part_700_604177518.1533317605676
Content-Type: text/html; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable
<div dir=3D"ltr"><div>I understand your point on the whole parallel constru=
ctor/destructor thing. The reason I suggested to use them is that it fits w=
ithin the existing paradigm, which means you don't have to do something=
like create a new series of special member functions. But making new funct=
ions may be the cleaner alternative.<br></div><div><br></div><div>I'm g=
oing to invent some new strawman terminology because "trivialization&q=
uot; just isn't working out. Let's call it "reduction" an=
d "regeneration". So every type has "reducers", which w=
ork like destructors in that they end the object's lifetime, but they e=
nsure that the object's values remain present. And every type has "=
;regenerators", which work like constructors in that they begin the ob=
ject's lifetime but can access the reduced value of the objects in thei=
r current space.</div><div><br></div><div>Both reduction and regeneration f=
unctions need to be able to take arbitrary parameters. And you need special=
syntax to invoke regeneration, for a specific type, on a piece of memory.<=
/div><div><br></div><div>Reduction and regeneration can be trivial operatio=
ns. For implicit lifetime and TriviallyRelocatable types, reduction can be =
done trivially. But you can also perform non-trivial reduction/regeneration=
of specific objects of those types.</div><div><br></div><div>This is actua=
lly a lot like the relationship between such types and their constructors. =
implicit lifetime types can have non-trivial constructors, but there are st=
ill trivial ways of interacting with them.<br></div><div><br></div><div>Som=
e operations invoke "trivial reduction" on all complete objects i=
n a piece of storage. These would be things like functions that unmap objec=
ts, but much like your original `unbless`, we can have a function to direct=
ly invoke this process. If that storage contains objects which do not have =
trivial reduction, undefined behavior results. So if you want to do non-tri=
vial reduction, you have to do it before performing such reduction.</div><d=
iv><br></div><div>Similarly, some operations invoke "trivial regenerat=
ion", like the memory mapping process. And this is where we have to st=
art getting into `bless`-like wording, where objects of trivially regenerat=
able types can "magically" appear based on usage. But you can als=
o explicitly invoke non-trivial regeneration for specific objects, which ca=
uses those objects to pop into being.</div><div><br></div><div>So let's=
explore some rules. Trivial reduction requires:</div><div><br></div><div><=
div><div>* There is no user-provided default reducer overload.<br></div><di=
v></div>* All subobjects must have trivial reduction.</div><div>* The type =
must be TriviallyRelocatable or Implicit Lifetime (which requires a trivial=
destructor).</div><br><div>And similarly, trivial regeneration requires:</=
div><div><br></div><div>* There is no user-provided default regeration over=
load.<br></div><div>* All subobjects must have trivial regeneration.</div><=
div>* The type must be TriviallyRelocatable or Implicit Lifetime.<br></div>=
<br><div>So, here's what we need:</div><div><br></div><div>1. Syntax fo=
r declaring reducers/regenerator member functions. It needs to not conflict=
with existing names.</div><div>=C2=A0=C2=A0=C2=A0 * Regenerators probably =
need similar abilities that constructors have. Things like member initializ=
er lists, inheriting regenerators, forwarding regenerators, etc.<br></div><=
div><br></div><div>2. Changes to the object model to allow for reducers/reg=
enerators to destroy/create objects, but preserving the stored bitpattern o=
f the results of the reduction and providing it to the regenerator on regen=
eration.</div><div><br></div><div>=C2=A0=C2=A0=C2=A0 * There needs to be so=
me idea of how exceptions work with regeneration. That is, when exactly eac=
h subobject is considered live, so that exceptions thrown from later subobj=
ect regeneration can destroy them. These rules could work much like the rul=
es of constructors, but we need to spell them out all the same.<br></div><d=
iv><br></div><div>3. Syntax for invoking regeneration on a piece of memory =
(which is required to contain the data from a previous reduction operation =
for an object of that type).</div><div><br></div><div>4. Syntax for invokin=
g reduction on an object. It would probably look like an explicit destructo=
r call.<br></div><div><br></div><div>Thinking about this further, "tri=
vial relocation" could be redefined as trivial reduction, memcpy, and =
trivial regeneration. And thus, a type is trivially relocatable if it has n=
o-argument reducer and regenerators that are trivial, which can be declared=
with `=3D default` syntax.</div><div><br></div><div>Now, I'm not sayin=
g we should go changing Arthur's proposal, since it is much farther alo=
ng than this. But it would allow us to have a second way to declare that a =
type is TriviallyRelocatable.<br></div><div><br></div></div></div>
<p></p>
-- <br />
You received this message because you are subscribed to the Google Groups &=
quot;ISO C++ Standard - Future Proposals" group.<br />
To unsubscribe from this group and stop receiving emails from it, send an e=
mail to <a href=3D"mailto:std-proposals+unsubscribe@isocpp.org">std-proposa=
ls+unsubscribe@isocpp.org</a>.<br />
To post to this group, send email to <a href=3D"mailto:std-proposals@isocpp=
..org">std-proposals@isocpp.org</a>.<br />
To view this discussion on the web visit <a href=3D"https://groups.google.c=
om/a/isocpp.org/d/msgid/std-proposals/60f7f1a2-1b35-4166-866c-6002579d0ba2%=
40isocpp.org?utm_medium=3Demail&utm_source=3Dfooter">https://groups.google.=
com/a/isocpp.org/d/msgid/std-proposals/60f7f1a2-1b35-4166-866c-6002579d0ba2=
%40isocpp.org</a>.<br />
------=_Part_700_604177518.1533317605676--
------=_Part_699_424605402.1533317605676--
.
Author: Niall Douglas <nialldouglas14@gmail.com>
Date: Fri, 3 Aug 2018 10:59:43 -0700 (PDT)
Raw View
------=_Part_901_530441525.1533319183320
Content-Type: multipart/alternative;
boundary="----=_Part_902_2024073176.1533319183320"
------=_Part_902_2024073176.1533319183320
Content-Type: text/plain; charset="UTF-8"
On Friday, August 3, 2018 at 6:33:25 PM UTC+1, Nicol Bolas wrote:
>
> I understand your point on the whole parallel constructor/destructor
> thing. The reason I suggested to use them is that it fits within the
> existing paradigm, which means you don't have to do something like create a
> new series of special member functions. But making new functions may be the
> cleaner alternative.
>
> I'm going to invent some new strawman terminology because "trivialization"
> just isn't working out. Let's call it "reduction" and "regeneration". So
> every type has "reducers", which work like destructors in that they end the
> object's lifetime, but they ensure that the object's values remain present.
> And every type has "regenerators", which work like constructors in that
> they begin the object's lifetime but can access the reduced value of the
> objects in their current space.
>
> Both reduction and regeneration functions need to be able to take
> arbitrary parameters. And you need special syntax to invoke regeneration,
> for a specific type, on a piece of memory.
>
> Reduction and regeneration can be trivial operations. For implicit
> lifetime and TriviallyRelocatable types, reduction can be done trivially.
> But you can also perform non-trivial reduction/regeneration of specific
> objects of those types.
>
> This is actually a lot like the relationship between such types and their
> constructors. implicit lifetime types can have non-trivial constructors,
> but there are still trivial ways of interacting with them.
>
> Some operations invoke "trivial reduction" on all complete objects in a
> piece of storage. These would be things like functions that unmap objects,
> but much like your original `unbless`, we can have a function to directly
> invoke this process. If that storage contains objects which do not have
> trivial reduction, undefined behavior results. So if you want to do
> non-trivial reduction, you have to do it before performing such reduction.
>
> Similarly, some operations invoke "trivial regeneration", like the memory
> mapping process. And this is where we have to start getting into
> `bless`-like wording, where objects of trivially regeneratable types can
> "magically" appear based on usage. But you can also explicitly invoke
> non-trivial regeneration for specific objects, which causes those objects
> to pop into being.
>
> So let's explore some rules. Trivial reduction requires:
>
> * There is no user-provided default reducer overload.
> * All subobjects must have trivial reduction.
> * The type must be TriviallyRelocatable or Implicit Lifetime (which
> requires a trivial destructor).
>
> And similarly, trivial regeneration requires:
>
> * There is no user-provided default regeration overload.
> * All subobjects must have trivial regeneration.
> * The type must be TriviallyRelocatable or Implicit Lifetime.
>
> So, here's what we need:
>
> 1. Syntax for declaring reducers/regenerator member functions. It needs to
> not conflict with existing names.
> * Regenerators probably need similar abilities that constructors have.
> Things like member initializer lists, inheriting regenerators, forwarding
> regenerators, etc.
>
> 2. Changes to the object model to allow for reducers/regenerators to
> destroy/create objects, but preserving the stored bitpattern of the results
> of the reduction and providing it to the regenerator on regeneration.
>
> * There needs to be some idea of how exceptions work with
> regeneration. That is, when exactly each subobject is considered live, so
> that exceptions thrown from later subobject regeneration can destroy them.
> These rules could work much like the rules of constructors, but we need to
> spell them out all the same.
>
> 3. Syntax for invoking regeneration on a piece of memory (which is
> required to contain the data from a previous reduction operation for an
> object of that type).
>
> 4. Syntax for invoking reduction on an object. It would probably look like
> an explicit destructor call.
>
This is one of those very few occasions in standards work where I can get
onboard with everything you just stated above. Well, apart from the naming
of reduction/regeneration.
Tony van Eerd, I summon thee! Any thoughts on naming? You have a knack for
thinking of naming acceptable to a majority.
>
> Thinking about this further, "trivial relocation" could be redefined as
> trivial reduction, memcpy, and trivial regeneration. And thus, a type is
> trivially relocatable if it has no-argument reducer and regenerators that
> are trivial, which can be declared with `= default` syntax.
>
> Now, I'm not saying we should go changing Arthur's proposal, since it is
> much farther along than this. But it would allow us to have a second way to
> declare that a type is TriviallyRelocatable.
>
> Still though, that's *exciting* because this gives Arthur the ability to
relocate polymorphic objects. I agree that the current revision of his
proposal shouldn't propose this, but a future addendum paper certainly
could extend his work via this mechanism.
Thanks Nicol. That was a very productive discussion.
Now, I'll be frank here, I don't think I have the spare time between now
and SD mailing deadline to write this up. Getting a lot of pressure to
issue new editions of my other papers, especially on the deterministic
exceptions front where WG14 are super keen for detail, as is Herb. But I'll
certainly aim for Kona.
(BTW this is to not shut down any further feedback on my draft paper. I
certainly would be very interested in what people think of Nicol's
proposal. Oh, and should these operations be functions, or operators?)
Niall
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/76f49e93-09c4-4eec-98d3-c7f0bdeea573%40isocpp.org.
------=_Part_902_2024073176.1533319183320
Content-Type: text/html; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable
<div dir=3D"ltr">On Friday, August 3, 2018 at 6:33:25 PM UTC+1, Nicol Bolas=
wrote:<blockquote class=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.=
8ex;border-left: 1px #ccc solid;padding-left: 1ex;"><div dir=3D"ltr"><div>I=
understand your point on the whole parallel constructor/destructor thing. =
The reason I suggested to use them is that it fits within the existing para=
digm, which means you don't have to do something like create a new seri=
es of special member functions. But making new functions may be the cleaner=
alternative.<br></div><div><br></div><div>I'm going to invent some new=
strawman terminology because "trivialization" just isn't wor=
king out. Let's call it "reduction" and "regeneration&qu=
ot;. So every type has "reducers", which work like destructors in=
that they end the object's lifetime, but they ensure that the object&#=
39;s values remain present. And every type has "regenerators", wh=
ich work like constructors in that they begin the object's lifetime but=
can access the reduced value of the objects in their current space.</div><=
div><br></div><div>Both reduction and regeneration functions need to be abl=
e to take arbitrary parameters. And you need special syntax to invoke regen=
eration, for a specific type, on a piece of memory.</div><div><br></div><di=
v>Reduction and regeneration can be trivial operations. For implicit lifeti=
me and TriviallyRelocatable types, reduction can be done trivially. But you=
can also perform non-trivial reduction/regeneration of specific objects of=
those types.</div><div><br></div><div>This is actually a lot like the rela=
tionship between such types and their constructors. implicit lifetime types=
can have non-trivial constructors, but there are still trivial ways of int=
eracting with them.<br></div><div><br></div><div>Some operations invoke &qu=
ot;trivial reduction" on all complete objects in a piece of storage. T=
hese would be things like functions that unmap objects, but much like your =
original `unbless`, we can have a function to directly invoke this process.=
If that storage contains objects which do not have trivial reduction, unde=
fined behavior results. So if you want to do non-trivial reduction, you hav=
e to do it before performing such reduction.</div><div><br></div><div>Simil=
arly, some operations invoke "trivial regeneration", like the mem=
ory mapping process. And this is where we have to start getting into `bless=
`-like wording, where objects of trivially regeneratable types can "ma=
gically" appear based on usage. But you can also explicitly invoke non=
-trivial regeneration for specific objects, which causes those objects to p=
op into being.</div><div><br></div><div>So let's explore some rules. Tr=
ivial reduction requires:</div><div><br></div><div><div><div>* There is no =
user-provided default reducer overload.<br></div><div></div>* All subobject=
s must have trivial reduction.</div><div>* The type must be TriviallyReloca=
table or Implicit Lifetime (which requires a trivial destructor).</div><br>=
<div>And similarly, trivial regeneration requires:</div><div><br></div><div=
>* There is no user-provided default regeration overload.<br></div><div>* A=
ll subobjects must have trivial regeneration.</div><div>* The type must be =
TriviallyRelocatable or Implicit Lifetime.<br></div><br><div>So, here's=
what we need:</div><div><br></div><div>1. Syntax for declaring reducers/re=
generator member functions. It needs to not conflict with existing names.</=
div><div>=C2=A0=C2=A0=C2=A0 * Regenerators probably need similar abilities =
that constructors have. Things like member initializer lists, inheriting re=
generators, forwarding regenerators, etc.<br></div><div><br></div><div>2. C=
hanges to the object model to allow for reducers/regenerators to destroy/cr=
eate objects, but preserving the stored bitpattern of the results of the re=
duction and providing it to the regenerator on regeneration.</div><div><br>=
</div><div>=C2=A0=C2=A0=C2=A0 * There needs to be some idea of how exceptio=
ns work with regeneration. That is, when exactly each subobject is consider=
ed live, so that exceptions thrown from later subobject regeneration can de=
stroy them. These rules could work much like the rules of constructors, but=
we need to spell them out all the same.<br></div><div><br></div><div>3. Sy=
ntax for invoking regeneration on a piece of memory (which is required to c=
ontain the data from a previous reduction operation for an object of that t=
ype).</div><div><br></div><div>4. Syntax for invoking reduction on an objec=
t. It would probably look like an explicit destructor call.<br></div></div>=
</div></blockquote><div><br></div><div>This is one of those very few occasi=
ons in standards work where I can get onboard with everything you just stat=
ed above. Well, apart from the naming of reduction/regeneration.</div><div>=
<br></div><div>Tony van Eerd, I summon thee! Any thoughts on naming? You ha=
ve a knack for thinking of naming acceptable to a majority.</div><div>=C2=
=A0</div><blockquote class=3D"gmail_quote" style=3D"margin: 0;margin-left: =
0.8ex;border-left: 1px #ccc solid;padding-left: 1ex;"><div dir=3D"ltr"><div=
><div></div><div><br></div><div>Thinking about this further, "trivial =
relocation" could be redefined as trivial reduction, memcpy, and trivi=
al regeneration. And thus, a type is trivially relocatable if it has no-arg=
ument reducer and regenerators that are trivial, which can be declared with=
`=3D default` syntax.</div><div><br></div><div>Now, I'm not saying we =
should go changing Arthur's proposal, since it is much farther along th=
an this. But it would allow us to have a second way to declare that a type =
is TriviallyRelocatable.<br></div><div><br></div></div></div></blockquote><=
div>Still though, that's <i>exciting</i>=C2=A0because this gives Arthur=
the ability to relocate polymorphic objects. I agree that the current revi=
sion of his proposal shouldn't propose this, but a future addendum pape=
r certainly could extend his work via this mechanism.</div><div><br></div><=
div>Thanks Nicol. That was a very productive discussion.</div><div><br></di=
v><div>Now, I'll be frank here, I don't think I have the spare time=
between now and SD mailing deadline to write this up. Getting a lot of pre=
ssure to issue new editions of my other papers, especially on the determini=
stic exceptions front where WG14 are super keen for detail, as is Herb. But=
I'll certainly aim for Kona.</div><div><br></div><div>(BTW this is to =
not shut down any further feedback on my draft paper. I certainly would be =
very interested in what people think of Nicol's proposal. Oh, and shoul=
d these operations be functions, or operators?)</div><div><br></div><div>Ni=
all</div><div><br></div></div>
<p></p>
-- <br />
You received this message because you are subscribed to the Google Groups &=
quot;ISO C++ Standard - Future Proposals" group.<br />
To unsubscribe from this group and stop receiving emails from it, send an e=
mail to <a href=3D"mailto:std-proposals+unsubscribe@isocpp.org">std-proposa=
ls+unsubscribe@isocpp.org</a>.<br />
To post to this group, send email to <a href=3D"mailto:std-proposals@isocpp=
..org">std-proposals@isocpp.org</a>.<br />
To view this discussion on the web visit <a href=3D"https://groups.google.c=
om/a/isocpp.org/d/msgid/std-proposals/76f49e93-09c4-4eec-98d3-c7f0bdeea573%=
40isocpp.org?utm_medium=3Demail&utm_source=3Dfooter">https://groups.google.=
com/a/isocpp.org/d/msgid/std-proposals/76f49e93-09c4-4eec-98d3-c7f0bdeea573=
%40isocpp.org</a>.<br />
------=_Part_902_2024073176.1533319183320--
------=_Part_901_530441525.1533319183320--
.
Author: Nicol Bolas <jmckesson@gmail.com>
Date: Fri, 3 Aug 2018 11:37:57 -0700 (PDT)
Raw View
------=_Part_767_1317762750.1533321477522
Content-Type: multipart/alternative;
boundary="----=_Part_768_898221547.1533321477522"
------=_Part_768_898221547.1533321477522
Content-Type: text/plain; charset="UTF-8"
On Friday, August 3, 2018 at 1:59:43 PM UTC-4, Niall Douglas wrote:
>
> On Friday, August 3, 2018 at 6:33:25 PM UTC+1, Nicol Bolas wrote:
>>
>> I understand your point on the whole parallel constructor/destructor
>> thing. The reason I suggested to use them is that it fits within the
>> existing paradigm, which means you don't have to do something like create a
>> new series of special member functions. But making new functions may be the
>> cleaner alternative.
>>
>> I'm going to invent some new strawman terminology because
>> "trivialization" just isn't working out. Let's call it "reduction" and
>> "regeneration". So every type has "reducers", which work like destructors
>> in that they end the object's lifetime, but they ensure that the object's
>> values remain present. And every type has "regenerators", which work like
>> constructors in that they begin the object's lifetime but can access the
>> reduced value of the objects in their current space.
>>
>> Both reduction and regeneration functions need to be able to take
>> arbitrary parameters. And you need special syntax to invoke regeneration,
>> for a specific type, on a piece of memory.
>>
>> Reduction and regeneration can be trivial operations. For implicit
>> lifetime and TriviallyRelocatable types, reduction can be done trivially.
>> But you can also perform non-trivial reduction/regeneration of specific
>> objects of those types.
>>
>> This is actually a lot like the relationship between such types and their
>> constructors. implicit lifetime types can have non-trivial constructors,
>> but there are still trivial ways of interacting with them.
>>
>> Some operations invoke "trivial reduction" on all complete objects in a
>> piece of storage. These would be things like functions that unmap objects,
>> but much like your original `unbless`, we can have a function to directly
>> invoke this process. If that storage contains objects which do not have
>> trivial reduction, undefined behavior results. So if you want to do
>> non-trivial reduction, you have to do it before performing such reduction.
>>
>> Similarly, some operations invoke "trivial regeneration", like the memory
>> mapping process. And this is where we have to start getting into
>> `bless`-like wording, where objects of trivially regeneratable types can
>> "magically" appear based on usage. But you can also explicitly invoke
>> non-trivial regeneration for specific objects, which causes those objects
>> to pop into being.
>>
>> So let's explore some rules. Trivial reduction requires:
>>
>> * There is no user-provided default reducer overload.
>> * All subobjects must have trivial reduction.
>> * The type must be TriviallyRelocatable or Implicit Lifetime (which
>> requires a trivial destructor).
>>
>> And similarly, trivial regeneration requires:
>>
>> * There is no user-provided default regeration overload.
>> * All subobjects must have trivial regeneration.
>> * The type must be TriviallyRelocatable or Implicit Lifetime.
>>
>> So, here's what we need:
>>
>> 1. Syntax for declaring reducers/regenerator member functions. It needs
>> to not conflict with existing names.
>> * Regenerators probably need similar abilities that constructors
>> have. Things like member initializer lists, inheriting regenerators,
>> forwarding regenerators, etc.
>>
>> 2. Changes to the object model to allow for reducers/regenerators to
>> destroy/create objects, but preserving the stored bitpattern of the results
>> of the reduction and providing it to the regenerator on regeneration.
>>
>> * There needs to be some idea of how exceptions work with
>> regeneration. That is, when exactly each subobject is considered live, so
>> that exceptions thrown from later subobject regeneration can destroy them.
>> These rules could work much like the rules of constructors, but we need to
>> spell them out all the same.
>>
>> 3. Syntax for invoking regeneration on a piece of memory (which is
>> required to contain the data from a previous reduction operation for an
>> object of that type).
>>
>> 4. Syntax for invoking reduction on an object. It would probably look
>> like an explicit destructor call.
>>
>
> This is one of those very few occasions in standards work where I can get
> onboard with everything you just stated above. Well, apart from the naming
> of reduction/regeneration.
>
> Tony van Eerd, I summon thee! Any thoughts on naming? You have a knack for
> thinking of naming acceptable to a majority.
>
>
>>
>> Thinking about this further, "trivial relocation" could be redefined as
>> trivial reduction, memcpy, and trivial regeneration. And thus, a type is
>> trivially relocatable if it has no-argument reducer and regenerators that
>> are trivial, which can be declared with `= default` syntax.
>>
>> Now, I'm not saying we should go changing Arthur's proposal, since it is
>> much farther along than this. But it would allow us to have a second way to
>> declare that a type is TriviallyRelocatable.
>>
>> Still though, that's *exciting* because this gives Arthur the ability to
> relocate polymorphic objects.
>
By my above definition, they would not have *trivial* relocation abilities,
since reduction/regeneration would have to execute code. And as Arthur
pointed out, there are differences between relocation and
reduction/regeneration. Indeed, based on his post, TriviallyRelocatable is
not something we can use to allow trivial reduction/regeneration. Or
rather, it would have to be TriviallyRelocatable & non-polymorphic.
I need to think on this some more.
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/8d7a58bf-1e05-4a74-9fac-bbadc9f8273c%40isocpp.org.
------=_Part_768_898221547.1533321477522
Content-Type: text/html; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable
<div dir=3D"ltr">On Friday, August 3, 2018 at 1:59:43 PM UTC-4, Niall Dougl=
as wrote:<blockquote class=3D"gmail_quote" style=3D"margin: 0;margin-left: =
0.8ex;border-left: 1px #ccc solid;padding-left: 1ex;"><div dir=3D"ltr">On F=
riday, August 3, 2018 at 6:33:25 PM UTC+1, Nicol Bolas wrote:<blockquote cl=
ass=3D"gmail_quote" style=3D"margin:0;margin-left:0.8ex;border-left:1px #cc=
c solid;padding-left:1ex"><div dir=3D"ltr"><div>I understand your point on =
the whole parallel constructor/destructor thing. The reason I suggested to =
use them is that it fits within the existing paradigm, which means you don&=
#39;t have to do something like create a new series of special member funct=
ions. But making new functions may be the cleaner alternative.<br></div><di=
v><br></div><div>I'm going to invent some new strawman terminology beca=
use "trivialization" just isn't working out. Let's call i=
t "reduction" and "regeneration". So every type has &qu=
ot;reducers", which work like destructors in that they end the object&=
#39;s lifetime, but they ensure that the object's values remain present=
.. And every type has "regenerators", which work like constructors=
in that they begin the object's lifetime but can access the reduced va=
lue of the objects in their current space.</div><div><br></div><div>Both re=
duction and regeneration functions need to be able to take arbitrary parame=
ters. And you need special syntax to invoke regeneration, for a specific ty=
pe, on a piece of memory.</div><div><br></div><div>Reduction and regenerati=
on can be trivial operations. For implicit lifetime and TriviallyRelocatabl=
e types, reduction can be done trivially. But you can also perform non-triv=
ial reduction/regeneration of specific objects of those types.</div><div><b=
r></div><div>This is actually a lot like the relationship between such type=
s and their constructors. implicit lifetime types can have non-trivial cons=
tructors, but there are still trivial ways of interacting with them.<br></d=
iv><div><br></div><div>Some operations invoke "trivial reduction"=
on all complete objects in a piece of storage. These would be things like =
functions that unmap objects, but much like your original `unbless`, we can=
have a function to directly invoke this process. If that storage contains =
objects which do not have trivial reduction, undefined behavior results. So=
if you want to do non-trivial reduction, you have to do it before performi=
ng such reduction.</div><div><br></div><div>Similarly, some operations invo=
ke "trivial regeneration", like the memory mapping process. And t=
his is where we have to start getting into `bless`-like wording, where obje=
cts of trivially regeneratable types can "magically" appear based=
on usage. But you can also explicitly invoke non-trivial regeneration for =
specific objects, which causes those objects to pop into being.</div><div><=
br></div><div>So let's explore some rules. Trivial reduction requires:<=
/div><div><br></div><div><div><div>* There is no user-provided default redu=
cer overload.<br></div><div></div>* All subobjects must have trivial reduct=
ion.</div><div>* The type must be TriviallyRelocatable or Implicit Lifetime=
(which requires a trivial destructor).</div><br><div>And similarly, trivia=
l regeneration requires:</div><div><br></div><div>* There is no user-provid=
ed default regeration overload.<br></div><div>* All subobjects must have tr=
ivial regeneration.</div><div>* The type must be TriviallyRelocatable or Im=
plicit Lifetime.<br></div><br><div>So, here's what we need:</div><div><=
br></div><div>1. Syntax for declaring reducers/regenerator member functions=
.. It needs to not conflict with existing names.</div><div>=C2=A0=C2=A0=C2=
=A0 * Regenerators probably need similar abilities that constructors have. =
Things like member initializer lists, inheriting regenerators, forwarding r=
egenerators, etc.<br></div><div><br></div><div>2. Changes to the object mod=
el to allow for reducers/regenerators to destroy/create objects, but preser=
ving the stored bitpattern of the results of the reduction and providing it=
to the regenerator on regeneration.</div><div><br></div><div>=C2=A0=C2=A0=
=C2=A0 * There needs to be some idea of how exceptions work with regenerati=
on. That is, when exactly each subobject is considered live, so that except=
ions thrown from later subobject regeneration can destroy them. These rules=
could work much like the rules of constructors, but we need to spell them =
out all the same.<br></div><div><br></div><div>3. Syntax for invoking regen=
eration on a piece of memory (which is required to contain the data from a =
previous reduction operation for an object of that type).</div><div><br></d=
iv><div>4. Syntax for invoking reduction on an object. It would probably lo=
ok like an explicit destructor call.<br></div></div></div></blockquote><div=
><br></div><div>This is one of those very few occasions in standards work w=
here I can get onboard with everything you just stated above. Well, apart f=
rom the naming of reduction/regeneration.</div><div><br></div><div>Tony van=
Eerd, I summon thee! Any thoughts on naming? You have a knack for thinking=
of naming acceptable to a majority.</div><div>=C2=A0</div><blockquote clas=
s=3D"gmail_quote" style=3D"margin:0;margin-left:0.8ex;border-left:1px #ccc =
solid;padding-left:1ex"><div dir=3D"ltr"><div><div></div><div><br></div><di=
v>Thinking about this further, "trivial relocation" could be rede=
fined as trivial reduction, memcpy, and trivial regeneration. And thus, a t=
ype is trivially relocatable if it has no-argument reducer and regenerators=
that are trivial, which can be declared with `=3D default` syntax.</div><d=
iv><br></div><div>Now, I'm not saying we should go changing Arthur'=
s proposal, since it is much farther along than this. But it would allow us=
to have a second way to declare that a type is TriviallyRelocatable.<br></=
div><div><br></div></div></div></blockquote><div>Still though, that's <=
i>exciting</i>=C2=A0because this gives Arthur the ability to relocate polym=
orphic objects.</div></div></blockquote><div><br></div><div>By my above def=
inition, they would not have <i>trivial</i> relocation abilities, since red=
uction/regeneration would have to execute code. And as Arthur pointed out, =
there are differences between relocation and reduction/regeneration. Indeed=
, based on his post, TriviallyRelocatable is not something we can use to al=
low trivial reduction/regeneration. Or rather, it would have to be Triviall=
yRelocatable & non-polymorphic.</div><div><br></div><div>I need to thin=
k on this some more.</div><br></div>
<p></p>
-- <br />
You received this message because you are subscribed to the Google Groups &=
quot;ISO C++ Standard - Future Proposals" group.<br />
To unsubscribe from this group and stop receiving emails from it, send an e=
mail to <a href=3D"mailto:std-proposals+unsubscribe@isocpp.org">std-proposa=
ls+unsubscribe@isocpp.org</a>.<br />
To post to this group, send email to <a href=3D"mailto:std-proposals@isocpp=
..org">std-proposals@isocpp.org</a>.<br />
To view this discussion on the web visit <a href=3D"https://groups.google.c=
om/a/isocpp.org/d/msgid/std-proposals/8d7a58bf-1e05-4a74-9fac-bbadc9f8273c%=
40isocpp.org?utm_medium=3Demail&utm_source=3Dfooter">https://groups.google.=
com/a/isocpp.org/d/msgid/std-proposals/8d7a58bf-1e05-4a74-9fac-bbadc9f8273c=
%40isocpp.org</a>.<br />
------=_Part_768_898221547.1533321477522--
------=_Part_767_1317762750.1533321477522--
.
Author: Matthew Woehlke <mwoehlke.floss@gmail.com>
Date: Fri, 3 Aug 2018 16:41:48 -0400
Raw View
On 2018-08-03 16:17, Ville Voutilainen wrote:
> On 3 August 2018 at 23:11, Nicol Bolas <jmckesson@gmail.com> wrote:
>>> Bike shedding is probably a bit premature here, but...
>>> - hibernate & awaken
>>> - dismiss & summon
>>> - disavow & reclaim
>>> - drop & retrieve/recover
>>
>> Suspension! That's the term. It gives the right impression, that the object
>> goes away yet is in a form that can return.
>>
>> When you suspend an object, you end its lifetime but you leave the values of
>> its storage alone. You can later unsuspend the values of that memory,
>> beginning the lifetime of a new object.
>
> I don't think so, suspension is an overloaded term. When a suspended
> coroutine resumes, the objects that were alive during suspension time
> are still alive. The same objects, not different ones.
Yeah...
If not for its other baggage (in particular, that we might want it for
real serialization), I personally would lean toward [un]pickle...
(I guess freeze/thaw already got rejected?)
Hmm, what else?
- reduce / revive
- chill / warm
- dissolve / reform
- disjoin / rejoin (good symmetry but otherwise lame)
- adjourn / assemble (or adjourn/convene)
- [de]mobilize
- mothball / ??
- retain / restore
- dehydrate / rehydrate
....and now a few "just for fun" ;-)
- parch / soak
- desiccate / moisten
- mummify / unwrap
--
Matthew
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/e1679731-1b71-1d3b-d663-a6610d14b767%40gmail.com.
.
Author: Ross Smith <ross.smith@otoy.com>
Date: Mon, 6 Aug 2018 08:45:36 +1200
Raw View
On 2018-08-04 10:41, Tom Honermann wrote:
> On 08/03/2018 06:02 PM, Ville Voutilainen wrote:
>> On 4 August 2018 at 00:49, Nicol Bolas <jmckesson@gmail.com> wrote:
>>>> dematerialize / reify
>>>> dematerialize / manifest
>>>> dematerialize / materialize
>>>>
>>>> My vocabulary is not good enough to find anything better than
>>>> dematerialize on the left hand side, so materialize
>>>> would be a decent counterpart for it. I do like reify and manifest
>>>> slightly better, because the facility reifies/manifests an
>>>> object from raw bits.
>>>
>>> Materialize is already taken by prvalues and temporaries. So=20
>>> "dematerialize"
>>> would be confusing.
>>>
>>> For the time being, I've settled on "activate/deactivate".
>> Fine. dissolve / reify, then.
>>
> More fine.=C2=A0 detach / attach.=C2=A0 These correlate with existing mem=
ory=20
> mapping terminology as exhibited by shmdt() and shmat(). Detach is also=
=20
> used by std::thread though.
>=20
> Tom.
deconstruct/reconstruct
Ross Smith
--=20
You received this message because you are subscribed to the Google Groups "=
ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an e=
mail to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp=
..org/d/msgid/std-proposals/221299bd-f48d-4bbf-cc29-f29f982a1e26%40otoy.com.
.
Author: Niall Douglas <nialldouglas14@gmail.com>
Date: Mon, 6 Aug 2018 10:13:52 -0700 (PDT)
Raw View
------=_Part_1410_1824025490.1533575633062
Content-Type: multipart/alternative;
boundary="----=_Part_1411_1541952294.1533575633063"
------=_Part_1411_1541952294.1533575633063
Content-Type: text/plain; charset="UTF-8"
>
>
>>> Thinking about this further, "trivial relocation" could be redefined as
>>> trivial reduction, memcpy, and trivial regeneration. And thus, a type is
>>> trivially relocatable if it has no-argument reducer and regenerators that
>>> are trivial, which can be declared with `= default` syntax.
>>>
>>>
>>> By my above definition, they would not have *trivial* relocation
> abilities, since reduction/regeneration would have to execute code. And as
> Arthur pointed out, there are differences between relocation and
> reduction/regeneration. Indeed, based on his post, TriviallyRelocatable is
> not something we can use to allow trivial reduction/regeneration. Or
> rather, it would have to be TriviallyRelocatable & non-polymorphic.
>
> I need to think on this some more.
>
> Trivial operation X simply means that the compiler can perform operation X
entirely on its own, as frequently and infrequently as it so chooses,
including ignoring the programmer if it would be as-if the same. That's the
meaning of triviality.
If the compiler can trivially reduce, copy and regenerate on its own,
that's still trivial by definition. It's also optimisable, so if the
compiler knows that nothing will modify the bytes between the reduction and
regeneration, it can optimise out say vptr modification and just use
straight memcpy.
So, in principle, this is fine. Same thing as guaranteed copy elision.
However, I am unsure if it is wise to - especially right now - muddy the
waters about relocation. We sorely need relocation sooner rather than
later. A good enough stopgap implementation even.
I appreciate, Nicol, that you feel that deterministic exceptions ought to
be static exceptions, however as I write up the C _Fails paper, if we want
C compatibility, I think we need be a bit more flexible and impure on that
point.
You may not have noticed, but D1031R1 draft 1's draft TS wording is
specified in P0709 deterministic exceptions. It throws file_io_error, not
error.
Niall
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/12f5bc0a-4a55-495c-9f37-c6423f901585%40isocpp.org.
------=_Part_1411_1541952294.1533575633063
Content-Type: text/html; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable
<blockquote class=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.8ex;bor=
der-left: 1px #ccc solid;padding-left: 1ex;"><div dir=3D"ltr"><blockquote c=
lass=3D"gmail_quote" style=3D"margin:0;margin-left:0.8ex;border-left:1px #c=
cc solid;padding-left:1ex"><div dir=3D"ltr"><blockquote class=3D"gmail_quot=
e" style=3D"margin:0;margin-left:0.8ex;border-left:1px #ccc solid;padding-l=
eft:1ex"><div dir=3D"ltr"><div><div><br></div><div>Thinking about this furt=
her, "trivial relocation" could be redefined as trivial reduction=
, memcpy, and trivial regeneration. And thus, a type is trivially relocatab=
le if it has no-argument reducer and regenerators that are trivial, which c=
an be declared with `=3D default` syntax.</div><div><br></div><div><br></di=
v></div></div></blockquote></div></blockquote><div>By my above definition, =
they would not have <i>trivial</i> relocation abilities, since reduction/re=
generation would have to execute code. And as Arthur pointed out, there are=
differences between relocation and reduction/regeneration. Indeed, based o=
n his post, TriviallyRelocatable is not something we can use to allow trivi=
al reduction/regeneration. Or rather, it would have to be TriviallyRelocata=
ble & non-polymorphic.</div><div><br></div><div>I need to think on this=
some more.</div><br></div></blockquote><div>Trivial operation X simply mea=
ns that the compiler can perform operation X entirely on its own, as freque=
ntly and infrequently as it so chooses, including ignoring the programmer i=
f it would be as-if the same. That's the meaning of triviality.=C2=A0</=
div><div><br></div><div>If the compiler can trivially reduce, copy and rege=
nerate on its own, that's still trivial by definition. It's also op=
timisable, so if the compiler knows that nothing will modify the bytes betw=
een the reduction and regeneration, it can optimise out say vptr modificati=
on and just use straight memcpy.</div><div><br></div><div>So, in principle,=
this is fine. Same thing as guaranteed copy elision.</div><div><br></div><=
div>However, I am unsure if it is wise to - especially right now - muddy th=
e waters about relocation. We sorely need relocation sooner rather than lat=
er. A good enough stopgap implementation even.</div><div><br></div><div>I a=
ppreciate, Nicol, that you feel that deterministic exceptions ought to be s=
tatic exceptions, however as I write up the C _Fails paper, if we want C co=
mpatibility, I think we need be a bit more flexible and impure on that poin=
t.</div><div><br></div><div>You may not have noticed, but D1031R1 draft 1&#=
39;s draft TS wording is specified in P0709 deterministic exceptions. It th=
rows file_io_error, not error.</div><div><br></div><div>Niall</div>
<p></p>
-- <br />
You received this message because you are subscribed to the Google Groups &=
quot;ISO C++ Standard - Future Proposals" group.<br />
To unsubscribe from this group and stop receiving emails from it, send an e=
mail to <a href=3D"mailto:std-proposals+unsubscribe@isocpp.org">std-proposa=
ls+unsubscribe@isocpp.org</a>.<br />
To post to this group, send email to <a href=3D"mailto:std-proposals@isocpp=
..org">std-proposals@isocpp.org</a>.<br />
To view this discussion on the web visit <a href=3D"https://groups.google.c=
om/a/isocpp.org/d/msgid/std-proposals/12f5bc0a-4a55-495c-9f37-c6423f901585%=
40isocpp.org?utm_medium=3Demail&utm_source=3Dfooter">https://groups.google.=
com/a/isocpp.org/d/msgid/std-proposals/12f5bc0a-4a55-495c-9f37-c6423f901585=
%40isocpp.org</a>.<br />
------=_Part_1411_1541952294.1533575633063--
------=_Part_1410_1824025490.1533575633062--
.
Author: Arthur O'Dwyer <arthur.j.odwyer@gmail.com>
Date: Mon, 6 Aug 2018 13:40:12 -0700 (PDT)
Raw View
------=_Part_1715_2054760768.1533588012975
Content-Type: multipart/alternative;
boundary="----=_Part_1716_1459947070.1533588012975"
------=_Part_1716_1459947070.1533588012975
Content-Type: text/plain; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable
On Tuesday, July 31, 2018 at 12:08:27 PM UTC-7, Niall Douglas wrote:
>
> So after many weeks of pondering and writing this outside of work each=20
> day, please find attached an early draft of revision 1 of P1031 destined=
=20
> for San Diego. Changes since R0:
>
> - Wrote partial draft TS wording for deadline, handle, io_handle,=20
> mapped, mapped_view, native_handle_type and file_io_error.
> - Added impact on the standard regarding potential changes to the C++=
=20
> object model.
> - Added impact on the standard regarding the proposed=20
> [[no_side_effects]] et al contracts attributes.
>
>
`class mapped` is referred to as `mapped_span` in two places.
`class handle`'s move-constructor is marked [[move_relocates]] even though=
=20
`class handle` has a virtual destructor.
Your P1029R0=20
<http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1029r0.pdf>=20
proposes that this should mean the attribute is silently ignored.
My D1144 proposes that if you were to mark `class handle` as=20
[[trivially_relocatable]], the compiler would trust you; but that wouldn't=
=20
change the fact that you'd be lying to it, and you'd get UB at runtime when=
=20
the compiler incorrectly assumed it could replace some arbitrary derived=20
class's destructor with a `memcpy`.
Should we open a side thread to talk about under what circumstances it=20
might be okay to memcpy a thing with a virtual destructor? (My D1144 has a=
=20
well-formed opinion on the topic, but I admit I don't do much mixing of=20
classical polymorphism with move-semantics in my own code.)
=E2=80=93Arthur
--=20
You received this message because you are subscribed to the Google Groups "=
ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an e=
mail to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp=
..org/d/msgid/std-proposals/d4821a6e-7825-4bef-9ba7-c5ddf57b4853%40isocpp.or=
g.
------=_Part_1716_1459947070.1533588012975
Content-Type: text/html; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable
<div dir=3D"ltr">On Tuesday, July 31, 2018 at 12:08:27 PM UTC-7, Niall Doug=
las wrote:<blockquote class=3D"gmail_quote" style=3D"margin: 0;margin-left:=
0.8ex;border-left: 1px #ccc solid;padding-left: 1ex;"><div dir=3D"ltr">So =
after many weeks of pondering and writing this outside of work each day, pl=
ease find attached an early draft of revision 1 of P1031 destined for San D=
iego. Changes since R0:<div><ul><li>Wrote partial draft TS wording for dead=
line, handle, io_handle, mapped, mapped_view, native_handle_type and file_i=
o_error.</li><li>Added impact on the standard regarding potential changes t=
o the C++ object model.</li><li>Added impact on the standard regarding the =
proposed [[no_side_effects]] et al contracts attributes.</li></ul></div></d=
iv></blockquote><div><div><br></div><div>`class mapped` is referred to as `=
mapped_span` in two places.</div></div><div><br></div><div>`class handle`&#=
39;s move-constructor is marked [[move_relocates]] even though `class handl=
e` has a virtual destructor.</div><div>Your=C2=A0<a href=3D"http://www.open=
-std.org/jtc1/sc22/wg21/docs/papers/2018/p1029r0.pdf">P1029R0</a> proposes =
that this should mean the attribute is silently ignored.</div><div>My D1144=
proposes that if you were to mark `class handle` as [[trivially_relocatabl=
e]], the compiler would trust you; but that wouldn't change the fact th=
at you'd be lying to it, and you'd get UB at runtime when the compi=
ler incorrectly assumed it could replace some arbitrary derived class's=
destructor with a `memcpy`.</div><div><br></div><div>Should we open a side=
thread to talk about under what circumstances it might be okay to memcpy a=
thing with a virtual destructor? =C2=A0(My D1144 has a well-formed opinion=
on the topic, but I admit I don't do much mixing of classical polymorp=
hism with move-semantics in my own code.)</div><div><br></div><div>=E2=80=
=93Arthur</div></div>
<p></p>
-- <br />
You received this message because you are subscribed to the Google Groups &=
quot;ISO C++ Standard - Future Proposals" group.<br />
To unsubscribe from this group and stop receiving emails from it, send an e=
mail to <a href=3D"mailto:std-proposals+unsubscribe@isocpp.org">std-proposa=
ls+unsubscribe@isocpp.org</a>.<br />
To post to this group, send email to <a href=3D"mailto:std-proposals@isocpp=
..org">std-proposals@isocpp.org</a>.<br />
To view this discussion on the web visit <a href=3D"https://groups.google.c=
om/a/isocpp.org/d/msgid/std-proposals/d4821a6e-7825-4bef-9ba7-c5ddf57b4853%=
40isocpp.org?utm_medium=3Demail&utm_source=3Dfooter">https://groups.google.=
com/a/isocpp.org/d/msgid/std-proposals/d4821a6e-7825-4bef-9ba7-c5ddf57b4853=
%40isocpp.org</a>.<br />
------=_Part_1716_1459947070.1533588012975--
------=_Part_1715_2054760768.1533588012975--
.
Author: Niall Douglas <nialldouglas14@gmail.com>
Date: Tue, 7 Aug 2018 01:48:47 -0700 (PDT)
Raw View
------=_Part_1723_1489216722.1533631727763
Content-Type: multipart/alternative;
boundary="----=_Part_1724_1078433731.1533631727763"
------=_Part_1724_1078433731.1533631727763
Content-Type: text/plain; charset="UTF-8"
>
>
> `class mapped` is referred to as `mapped_span` in two places.
>
Fixed. Thanks.
>
> `class handle`'s move-constructor is marked [[move_relocates]] even though
> `class handle` has a virtual destructor.
> Your P1029R0
> <http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1029r0.pdf>
> proposes that this should mean the attribute is silently ignored.
>
R1 permits virtual destructors. If the programmer uses the attribute,
they're giving a guarantee to the compiler. The compiler trusts the
programmer.
> My D1144 proposes that if you were to mark `class handle` as
> [[trivially_relocatable]], the compiler would trust you; but that wouldn't
> change the fact that you'd be lying to it, and you'd get UB at runtime when
> the compiler incorrectly assumed it could replace some arbitrary derived
> class's destructor with a `memcpy`.
>
> Should we open a side thread to talk about under what circumstances it
> might be okay to memcpy a thing with a virtual destructor? (My D1144 has a
> well-formed opinion on the topic, but I admit I don't do much mixing of
> classical polymorphism with move-semantics in my own code.)
>
I'm not sure how much there is to discuss. The real opposition on committee
will be to the memcpy of any polymorphic type at all.
Niall
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/fde39588-d020-4b8a-a2bc-00971785a768%40isocpp.org.
------=_Part_1724_1078433731.1533631727763
Content-Type: text/html; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable
<blockquote class=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.8ex;bor=
der-left: 1px #ccc solid;padding-left: 1ex;"><div dir=3D"ltr"><div><div><br=
></div><div>`class mapped` is referred to as `mapped_span` in two places.</=
div></div></div></blockquote><div><br></div><div>Fixed. Thanks.</div><div>=
=C2=A0</div><blockquote class=3D"gmail_quote" style=3D"margin: 0;margin-lef=
t: 0.8ex;border-left: 1px #ccc solid;padding-left: 1ex;"><div dir=3D"ltr"><=
div><br></div><div>`class handle`'s move-constructor is marked [[move_r=
elocates]] even though `class handle` has a virtual destructor.</div><div>Y=
our=C2=A0<a href=3D"http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018=
/p1029r0.pdf" target=3D"_blank" rel=3D"nofollow" onmousedown=3D"this.href=
=3D'http://www.google.com/url?q\x3dhttp%3A%2F%2Fwww.open-std.org%2Fjtc1=
%2Fsc22%2Fwg21%2Fdocs%2Fpapers%2F2018%2Fp1029r0.pdf\x26sa\x3dD\x26sntz\x3d1=
\x26usg\x3dAFQjCNGBQgX7XCXIEvDs0sj6h9NquGMikA';return true;" onclick=3D=
"this.href=3D'http://www.google.com/url?q\x3dhttp%3A%2F%2Fwww.open-std.=
org%2Fjtc1%2Fsc22%2Fwg21%2Fdocs%2Fpapers%2F2018%2Fp1029r0.pdf\x26sa\x3dD\x2=
6sntz\x3d1\x26usg\x3dAFQjCNGBQgX7XCXIEvDs0sj6h9NquGMikA';return true;">=
P1029R0</a> proposes that this should mean the attribute is silently ignore=
d.</div></div></blockquote><div><br></div><div>R1 permits virtual destructo=
rs. If the programmer uses the attribute, they're giving a guarantee to=
the compiler. The compiler trusts the programmer.</div><div>=C2=A0</div><b=
lockquote class=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.8ex;borde=
r-left: 1px #ccc solid;padding-left: 1ex;"><div dir=3D"ltr"><div>My D1144 p=
roposes that if you were to mark `class handle` as [[trivially_relocatable]=
], the compiler would trust you; but that wouldn't change the fact that=
you'd be lying to it, and you'd get UB at runtime when the compile=
r incorrectly assumed it could replace some arbitrary derived class's d=
estructor with a `memcpy`.</div><div><br></div><div>Should we open a side t=
hread to talk about under what circumstances it might be okay to memcpy a t=
hing with a virtual destructor? =C2=A0(My D1144 has a well-formed opinion o=
n the topic, but I admit I don't do much mixing of classical polymorphi=
sm with move-semantics in my own code.)</div></div></blockquote><div>=C2=A0=
</div><div>I'm not sure how much there is to discuss. The real oppositi=
on on committee will be to the memcpy of any polymorphic type at all.</div>=
<div><br></div><div>Niall</div><div><br></div>
<p></p>
-- <br />
You received this message because you are subscribed to the Google Groups &=
quot;ISO C++ Standard - Future Proposals" group.<br />
To unsubscribe from this group and stop receiving emails from it, send an e=
mail to <a href=3D"mailto:std-proposals+unsubscribe@isocpp.org">std-proposa=
ls+unsubscribe@isocpp.org</a>.<br />
To post to this group, send email to <a href=3D"mailto:std-proposals@isocpp=
..org">std-proposals@isocpp.org</a>.<br />
To view this discussion on the web visit <a href=3D"https://groups.google.c=
om/a/isocpp.org/d/msgid/std-proposals/fde39588-d020-4b8a-a2bc-00971785a768%=
40isocpp.org?utm_medium=3Demail&utm_source=3Dfooter">https://groups.google.=
com/a/isocpp.org/d/msgid/std-proposals/fde39588-d020-4b8a-a2bc-00971785a768=
%40isocpp.org</a>.<br />
------=_Part_1724_1078433731.1533631727763--
------=_Part_1723_1489216722.1533631727763--
.