Skip to content

MaybeUninit is not aligned, and mmtk_get_next_mutator should not use global variable #80

Open
@wks

Description

@wks

In openjdk/mmtkUpcalls.cpp, there is a struct MaybeUninit<T> which is used to prevent the constructor to be called on a global variable. It is implemented with char _data[sizeof(T)] as its member.

However, char[sizeof(T)] does not necessarily have the same alignment requirement as T.

I added a static assertion and a run-time assertion:

diff --git a/openjdk/mmtkUpcalls.cpp b/openjdk/mmtkUpcalls.cpp
index a11e186..aaebfb1 100644
--- a/openjdk/mmtkUpcalls.cpp
+++ b/openjdk/mmtkUpcalls.cpp
@@ -118,7 +118,18 @@ private:
 static MaybeUninit<JavaThreadIteratorWithHandle> jtiwh;
 static bool mutator_iteration_start = true;
 
+static_assert(alignof(jtiwh) == alignof(JavaThreadIteratorWithHandle),
+        "jtiwh and JavaThreadIteratorWithHandle does not have the same alignment");
+
 static void* mmtk_get_next_mutator() {
+    if ((uintptr_t)&jtiwh % alignof(JavaThreadIteratorWithHandle) != 0) {
+        fprintf(stderr, "[mmtk_get_next_mutator] error: jtiwh is not aligned. jtiwh: %p, align: %zd\n",
+                &jtiwh, alignof(JavaThreadIteratorWithHandle));
+        exit(1);
+    } else {
+        fprintf(stderr, "[mmtk_get_next_mutator] OK: jtiwh is aligned. jtiwh: %p, align: %zd\n",
+                &jtiwh, alignof(JavaThreadIteratorWithHandle));
+    }
     if (mutator_iteration_start) {
         *jtiwh = JavaThreadIteratorWithHandle();
         mutator_iteration_start = false;

The static assertion fails. But if I comment out the static_assert, the run-time assertion always succeeds on my x86_64 machine running Linux. I did not find any ABI documentation that requires static variables (jtiwh) to have any particular alignment (such as word-aligned, 8-byte aligned, 16-byte aligned, ...). So there is a chance that jtiwh may be mis-aligned on some platform-compiler combinations, but not manifested yet. The proper way to avoid initializing a global variable is to use std::opitonal<T>, or manually implement it using a union if OpenJDK does not support C++17.

However, from a higher level, it is almost always bad to use global variables. mmtk_get_next_mutator and mmtk_reset_mutator_iterator depends on the global jtiwh variable to maintain the iterator between successive calls. In a reentrant API, they should take a parameter which points to an allocated JavaThreadIteratorWithHandle instance. It shouldn't be a problem to just new JavaThreadIteratorWithHandle and allocate it on the heap, I think. See opendir and readdir_r for example.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions