+#if defined(COPY_OR_MARK_PARALLEL)
+static MONO_NEVER_INLINE GCObject *
+copy_object_no_checks_par (GCObject *obj, SgenGrayQueue *queue)
+{
+ mword vtable_word = *(mword*)obj;
+ GCObject *destination;
+
+ destination = (GCObject*) SGEN_VTABLE_IS_FORWARDED (vtable_word);
+
+ if (!destination) {
+ GCVTable vt = (GCVTable) vtable_word;
+ GCObject *final_destination;
+ /*
+ * At this point we know vt is not tagged and we shouldn't access the vtable through obj
+ * since it could get copied at any time by another thread.
+ */
+ gboolean has_references = SGEN_VTABLE_HAS_REFERENCES (vt);
+ mword objsize = SGEN_ALIGN_UP (sgen_client_par_object_get_size (vt, obj));
+ destination = major_collector.alloc_object_par (vt, objsize, has_references);
+
+ par_copy_object_no_checks ((char*)destination, vt, obj, objsize);
+
+ /* FIXME we might need a membar here so other threads see the vtable before we forward */
+
+ /* set the forwarding pointer */
+ SGEN_FORWARD_OBJECT_PAR (obj, destination, final_destination);
+
+ if (destination == final_destination) {
+ /* In a racing case, only the worker that allocated the object enqueues it */
+ if (has_references) {
+ SGEN_LOG (9, "Enqueuing gray object %p (%s)", destination, sgen_client_vtable_get_name (vt));
+ GRAY_OBJECT_ENQUEUE_PARALLEL (queue, (GCObject *)destination, sgen_vtable_get_descriptor (vt));
+ }
+ } else {
+ /*
+ * Unlikely case. Clear the allocated object so it doesn't confuse nursery
+ * card table scanning, since it can contain old invalid refs.
+ * FIXME make sure it is not a problem if another threads scans it while we clear
+ */
+ mono_gc_bzero_aligned (destination, objsize);
+ destination = final_destination;
+ }
+ }
+
+ return destination;
+}
+#endif
+