RFR: 8286876: NMT.test_unaliged_block_address_vm_assert fails if using clang toolchain [v6]

Albert Mingkun Yang ayang at openjdk.org
Tue Jan 17 09:34:16 UTC 2023


On Tue, 17 Jan 2023 06:25:35 GMT, Thomas Stuefe <stuefe at openjdk.org> wrote:

>> The input arg should be `const void*`, which accepts both `void*` and `const void*`, because that's a promise to the caller that the current method will not mutate the mem pointed by the input arg. (Ofc, one could throw away `const` and mutate the mem anyway inside the method, but that's just bad code.)
>> 
>>> but that should yield a const pointer too, not a non-const one.
>> 
>> I believe your reasoning is that in&out pointers are essentially the same (off by 1), so they should have the same const-ness.
>> 
>> However, the in&out args are two semantically different entities, `void*` vs `MallocHeader*`. For instance, a caller may construct a `MallocHeader*` out of a `const void*` -- the mem is immutable via `void*` but mutable via `MallocHeader*` due to the extra info offered by the more concrete type. IOW, whether the mem indicated by `MallocHeader*` is mutable or not is up to the caller.
>> 
>> Looking at the `const`-variant caller (I can just find this single one), it's more or less the same as a caller of the non-`const` variant.
>> 
>> 
>> static void check_expected_malloc_header(const void* payload, MEMFLAGS type, size_t size) {
>>   const MallocHeader* hdr = MallocHeader::resolve_checked(payload);
>>   EXPECT_EQ(hdr->size(), size);
>>   EXPECT_EQ(hdr->flags(), type);
>> }
>> 
>> 
>> vs
>> 
>> 
>>     void* const memblock = ...
>> 
>>     MallocHeader* header2 = MallocHeader::resolve_checked(memblock);
>>     assert(header2->size() == size, "Wrong size");
>>     assert(header2->flags() == flags, "Wrong flags");
>> 
>> 
>> (Seems to me, `header2` should be `const` as well -- here we promise not to mutate the obj via `header2`.)
>
>> The input arg should be `const void*`, which accepts both `void*` and `const void*`, because that's a promise to the caller that the current method will not mutate the mem pointed by the input arg. 
> 
> Yes,
> 
> (Ofc, one could throw away `const` and mutate the mem anyway inside the method, but that's just bad code.)
> 
> Yes.
> 
>> 
>> > but that should yield a const pointer too, not a non-const one.
>> 
>> I believe your reasoning is that in&out pointers are essentially the same (off by 1), so they should have the same const-ness.
>> 
>> However, the in&out args are two semantically different entities, `void*` vs `MallocHeader*`. For instance, a caller may construct a `MallocHeader*` out of a `const void*` -- the mem is immutable via `void*` but mutable via `MallocHeader*` due to the extra info offered by the more concrete type. IOW, whether the mem indicated by `MallocHeader*` is mutable or not is up to the caller.
> 
> Not sure if we talk at cross purposes, but:
> 
> - const variant is for safe *querying* while preventing accidental write access. You don't need write access for querying.
> - non-const variant is for modifying the header. The header is mostly immutable, the only thing we change are the canaries. Without that, we could dispose of the non-const variant altogether.
> 
> The whole thing follows the same logic as a class where you differentiate between read-only and write-access for the same accessor, e.g.
> 
> 
> class X {
>    Y y;
>    const Y& get_y() const;
>    Y& get_y();
> }
> 
> 
> see e.g. Effective C++, Item 3. Regrettably, C++ forces us to duplicate the method.
> 
>> 
>> Looking at the `const`-variant caller (I can just find this single one), it's more or less the same as a caller of the non-`const` variant.
>> 
>> ```c++
>> static void check_expected_malloc_header(const void* payload, MEMFLAGS type, size_t size) {
>>   const MallocHeader* hdr = MallocHeader::resolve_checked(payload);
>>   EXPECT_EQ(hdr->size(), size);
>>   EXPECT_EQ(hdr->flags(), type);
>> }
>> ```
>> 
>> vs
>> 
>> ```c++
>>     void* const memblock = ...
>> 
>>     MallocHeader* header2 = MallocHeader::resolve_checked(memblock);
>>     assert(header2->size() == size, "Wrong size");
>>     assert(header2->flags() == flags, "Wrong flags");
>> ```
>> 
>> (Seems to me, `header2` should be `const` as well -- here we promise not to mutate the obj via `header2`.)
> 
> Agreed.

When you agree with "The input arg should be `const void*`...", do you mean this?


  inline static const MallocHeader* resolve_checked(const void* memblock);
  inline static       MallocHeader* resolve_checked(const void* memblock);

I don't think this is valid C++.

What I suggested is to have a single method and let the caller decide regarding `const` of the returned value.

-------------

PR: https://git.openjdk.org/jdk/pull/11465


More information about the hotspot-runtime-dev mailing list