1621 lines
70 KiB
C#
1621 lines
70 KiB
C#
|
//
|
||
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||
|
//
|
||
|
|
||
|
/*******************************************************************/
|
||
|
/* WARNING */
|
||
|
/* This file should be identical in the Bartok and Singularity */
|
||
|
/* depots. Master copy resides in Bartok Depot. Changes should be */
|
||
|
/* made to Bartok Depot and propagated to Singularity Depot. */
|
||
|
/*******************************************************************/
|
||
|
|
||
|
namespace System.GCs {
|
||
|
|
||
|
using Microsoft.Bartok.Runtime;
|
||
|
using Microsoft.Bartok.Options;
|
||
|
using System.Runtime.CompilerServices;
|
||
|
using System.Runtime.InteropServices;
|
||
|
using System.Threading;
|
||
|
|
||
|
internal unsafe class ExpandingCoCoBarrier: LowAbortCoCoBarrier {
|
||
|
|
||
|
internal static new ExpandingCoCoBarrier instance;
|
||
|
|
||
|
[NoBarriers]
|
||
|
internal static new void Initialize()
|
||
|
{
|
||
|
ExpandingCoCoBarrier.instance =
|
||
|
(ExpandingCoCoBarrier)
|
||
|
BootstrapMemory.Allocate(typeof(ExpandingCoCoBarrier));
|
||
|
ExpandingCoCoBarrier.instance.InitEarly();
|
||
|
LowAbortCoCoBarrier.Initialize();
|
||
|
}
|
||
|
|
||
|
internal enum ObjState {
|
||
|
SimpleOrForwarded = 0,
|
||
|
Tagged = 1,
|
||
|
Tainted = 2,
|
||
|
Expanded = 3,
|
||
|
}
|
||
|
|
||
|
[NoBarriers]
|
||
|
internal struct ForwardingWord {
|
||
|
private UIntPtr original;
|
||
|
private UIntPtr CoCoWord;
|
||
|
|
||
|
internal ForwardingWord(Object original,
|
||
|
UIntPtr CoCoWord)
|
||
|
{
|
||
|
this.original = Magic.addressOf(original);
|
||
|
this.CoCoWord = CoCoWord;
|
||
|
}
|
||
|
|
||
|
internal ForwardingWord(UIntPtr original,
|
||
|
UIntPtr CoCoWord)
|
||
|
{
|
||
|
this.original = original;
|
||
|
this.CoCoWord = CoCoWord;
|
||
|
}
|
||
|
|
||
|
internal ForwardingWord(Object original,
|
||
|
ObjState state,
|
||
|
UIntPtr forward)
|
||
|
{
|
||
|
this.original = Magic.addressOf(original);
|
||
|
this.CoCoWord = ((UIntPtr)(uint)(int)state)|forward;
|
||
|
}
|
||
|
|
||
|
internal ForwardingWord(Object original,
|
||
|
ObjState state,
|
||
|
Object forward)
|
||
|
{
|
||
|
this.original = Magic.addressOf(original);
|
||
|
this.CoCoWord = ((UIntPtr)(uint)(int)state)|Magic.addressOf(forward);
|
||
|
}
|
||
|
|
||
|
internal ForwardingWord(UIntPtr original,
|
||
|
ObjState state,
|
||
|
UIntPtr forward)
|
||
|
{
|
||
|
this.original = original;
|
||
|
this.CoCoWord = ((UIntPtr)(uint)(int)state)|forward;
|
||
|
}
|
||
|
|
||
|
internal bool IsClear
|
||
|
{
|
||
|
get {
|
||
|
return CoCoWord == UIntPtr.Zero;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
internal UIntPtr Bits
|
||
|
{
|
||
|
get {
|
||
|
return CoCoWord;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// This is messed up. I don't like it.
|
||
|
|
||
|
internal UIntPtr ForwardBitsRaw
|
||
|
{
|
||
|
get {
|
||
|
return CoCoWord&~(UIntPtr)3;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
internal UIntPtr ForwardBits
|
||
|
{
|
||
|
get {
|
||
|
UIntPtr result = UIntPtr.Zero;
|
||
|
if (State==ObjState.SimpleOrForwarded) {
|
||
|
result = ForwardBitsRaw;
|
||
|
}
|
||
|
if (result==UIntPtr.Zero) {
|
||
|
return original;
|
||
|
} else {
|
||
|
return result;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
internal Object Forward {
|
||
|
get { return Magic.fromAddress(ForwardBits); }
|
||
|
}
|
||
|
|
||
|
internal WideObject Wide
|
||
|
{
|
||
|
get {
|
||
|
return (WideObject)Magic.fromAddress(ForwardBitsRaw);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
internal UIntPtr ForwardInternal(UIntPtr oldPtr)
|
||
|
{
|
||
|
return (oldPtr-original)+ForwardBits;
|
||
|
}
|
||
|
|
||
|
internal ObjState State
|
||
|
{
|
||
|
get {
|
||
|
return (ObjState)(int)(CoCoWord&3);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
internal ForwardingWord WithState(ObjState state)
|
||
|
{
|
||
|
return new ForwardingWord(original,
|
||
|
state,
|
||
|
ForwardBitsRaw);
|
||
|
}
|
||
|
|
||
|
internal ForwardingWord WithForwardBits(UIntPtr forwardBits)
|
||
|
{
|
||
|
return new ForwardingWord(original,
|
||
|
State,
|
||
|
forwardBits);
|
||
|
}
|
||
|
|
||
|
internal ForwardingWord WithForward(Object o)
|
||
|
{
|
||
|
return WithForwardBits(Magic.addressOf(o));
|
||
|
}
|
||
|
|
||
|
internal ForwardingWord WithWide(WideObject w)
|
||
|
{
|
||
|
return WithForward(w);
|
||
|
}
|
||
|
|
||
|
internal ForwardingWord Clear()
|
||
|
{
|
||
|
return new ForwardingWord(original,
|
||
|
ObjState.SimpleOrForwarded,
|
||
|
UIntPtr.Zero);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[NoBarriers]
|
||
|
internal static ForwardingWord GetForwardingWord(Object o)
|
||
|
{
|
||
|
UIntPtr word;
|
||
|
if (fVerbose && DebugThread) {
|
||
|
VTable.DebugPrint("GetForwardingWord: reading from ");
|
||
|
VTable.DebugPrint((ulong)Magic.addressOf(o));
|
||
|
VTable.DebugPrint("\n");
|
||
|
|
||
|
UIntPtr *wordPtr =
|
||
|
Magic.toPointer(ref MixinObject(o).preHeader.CoCoWord);
|
||
|
|
||
|
VTable.DebugPrint("GetForwardingWord: wordPtr = ");
|
||
|
VTable.DebugPrint((ulong)wordPtr);
|
||
|
VTable.DebugPrint("\n");
|
||
|
|
||
|
word = *wordPtr;
|
||
|
|
||
|
VTable.DebugPrint("GetForwardingWord: from ");
|
||
|
VTable.DebugPrint((ulong)Magic.addressOf(o));
|
||
|
VTable.DebugPrint(" read ");
|
||
|
VTable.DebugPrint((ulong)word);
|
||
|
VTable.DebugPrint("\n");
|
||
|
} else {
|
||
|
word = MixinObject(o).preHeader.CoCoWord;
|
||
|
}
|
||
|
return new ForwardingWord(o, word);
|
||
|
}
|
||
|
|
||
|
[Inline]
|
||
|
[NoBarriers]
|
||
|
internal override UIntPtr ToSpaceImplBeforeReadyNonNull(Object o)
|
||
|
{
|
||
|
UIntPtr CoCoWord = MixinObject(o).preHeader.CoCoWord;
|
||
|
UIntPtr ForwardBitsRaw=CoCoWord&~(UIntPtr)(uint)3;
|
||
|
if (ForwardBitsRaw == 0) {
|
||
|
return Magic.addressOf(o);
|
||
|
} else if (((uint)(CoCoWord&3)) == (uint)ObjState.SimpleOrForwarded) {
|
||
|
return ForwardBitsRaw;
|
||
|
} else {
|
||
|
return Magic.addressOf(((WideObject)Magic.fromAddress(ForwardBitsRaw)).copy);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[Inline]
|
||
|
[NoBarriers]
|
||
|
internal override bool IsInToSpace(UIntPtr ptr)
|
||
|
{
|
||
|
UIntPtr CoCoWord = MixinObject(Magic.fromAddress(ptr)).preHeader.CoCoWord;
|
||
|
UIntPtr ForwardBitsRaw=CoCoWord&~(UIntPtr)(uint)3;
|
||
|
return ForwardBitsRaw == 0
|
||
|
|| ((uint)(CoCoWord&3)) != (uint)ObjState.SimpleOrForwarded;
|
||
|
}
|
||
|
|
||
|
[Inline]
|
||
|
[NoBarriers]
|
||
|
internal override UIntPtr ToSpaceImplNonNull(Object o)
|
||
|
{
|
||
|
// this needs some memory barriers.
|
||
|
UIntPtr CoCoWord = MixinObject(o).preHeader.CoCoWord;
|
||
|
UIntPtr ForwardBitsRaw=CoCoWord&~(UIntPtr)(uint)3;
|
||
|
if (ForwardBitsRaw == 0 ||
|
||
|
((uint)(CoCoWord&3)) != (uint)ObjState.SimpleOrForwarded) {
|
||
|
return Magic.addressOf(o);
|
||
|
} else {
|
||
|
return ForwardBitsRaw;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[NoBarriers]
|
||
|
internal static bool SwapObjectState(Object o,
|
||
|
ForwardingWord value,
|
||
|
ForwardingWord comparand)
|
||
|
{
|
||
|
return CAS(ref MixinObject(o).preHeader.CoCoWord,
|
||
|
value.Bits,
|
||
|
comparand.Bits)
|
||
|
== comparand.Bits;
|
||
|
}
|
||
|
|
||
|
[NoBarriers]
|
||
|
internal static void SetObjectState(Object o,
|
||
|
ForwardingWord f)
|
||
|
{
|
||
|
MixinObject(o).preHeader.CoCoWord = f.Bits;
|
||
|
}
|
||
|
|
||
|
internal enum WordState {
|
||
|
Original = 0,
|
||
|
InWide = 1,
|
||
|
InCopy = 2,
|
||
|
}
|
||
|
|
||
|
[NoBarriers]
|
||
|
internal struct WordStatus {
|
||
|
private UIntPtr word;
|
||
|
|
||
|
internal WordStatus(UIntPtr word)
|
||
|
{
|
||
|
this.word = word;
|
||
|
}
|
||
|
|
||
|
internal WordStatus(WordState state,
|
||
|
bool inAction,
|
||
|
UIntPtr version)
|
||
|
{
|
||
|
word = ((UIntPtr)state)
|
||
|
| (UIntPtr)(inAction?4:0)
|
||
|
| (version<<3);
|
||
|
}
|
||
|
|
||
|
internal UIntPtr Word
|
||
|
{
|
||
|
get {
|
||
|
return word;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
internal UIntPtr* asPointer {
|
||
|
get { return Magic.toPointer(ref word); }
|
||
|
}
|
||
|
|
||
|
internal WordState State
|
||
|
{
|
||
|
get {
|
||
|
return (WordState)(int)(word&(UIntPtr)3);
|
||
|
}
|
||
|
set {
|
||
|
word=((word&~(UIntPtr)3)|((UIntPtr)value));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
internal bool InAction {
|
||
|
get { return (word&4)!=0; }
|
||
|
set {
|
||
|
if (value) word|=(UIntPtr)4;
|
||
|
else word&=~(UIntPtr)4;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
internal UIntPtr Version {
|
||
|
get { return word>>3; }
|
||
|
set { word=(value<<3); }
|
||
|
}
|
||
|
|
||
|
internal WordStatus WithState(WordState value) {
|
||
|
WordStatus result=this;
|
||
|
result.State=value;
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
internal WordStatus WithInAction(bool value) {
|
||
|
WordStatus result=this;
|
||
|
result.InAction=value;
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
internal WordStatus WithVersion(UIntPtr value) {
|
||
|
WordStatus result=this;
|
||
|
result.Version=value;
|
||
|
return result;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
internal enum ActionResult {
|
||
|
None = 0,
|
||
|
Success = 1,
|
||
|
Failure = 2,
|
||
|
}
|
||
|
|
||
|
internal struct WordAction {
|
||
|
internal int index;
|
||
|
|
||
|
internal WordStatus statusValue;
|
||
|
internal UIntPtr payloadValue;
|
||
|
|
||
|
internal WordStatus statusComparand;
|
||
|
internal UIntPtr payloadComparand;
|
||
|
|
||
|
internal WordAction(int index,
|
||
|
WordStatus statusValue,
|
||
|
UIntPtr payloadValue,
|
||
|
WordStatus statusComparand,
|
||
|
UIntPtr payloadComparand)
|
||
|
{
|
||
|
this.index = index;
|
||
|
this.statusValue = statusValue;
|
||
|
this.payloadValue = payloadValue;
|
||
|
this.statusComparand = statusComparand;
|
||
|
this.payloadComparand = payloadComparand;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
internal class Action {
|
||
|
private int res;
|
||
|
internal WordAction[] words;
|
||
|
|
||
|
internal Action(uint n) {
|
||
|
res=(int)ActionResult.None;
|
||
|
words=new WordAction[n];
|
||
|
}
|
||
|
|
||
|
internal ActionResult Res {
|
||
|
get { return (ActionResult)res; }
|
||
|
}
|
||
|
|
||
|
private UIntPtr myVersion {
|
||
|
get { return Magic.addressOf(this)>>3; }
|
||
|
}
|
||
|
|
||
|
[NoBarriers]
|
||
|
private bool setResult(ActionResult newRes) {
|
||
|
int oldRes=CAS(ref this.res,
|
||
|
(int)newRes,
|
||
|
(int)ActionResult.None);
|
||
|
return oldRes == (int)ActionResult.None
|
||
|
|| oldRes == (int)newRes;
|
||
|
}
|
||
|
|
||
|
private void rollback(WideObject wide,
|
||
|
uint limit) {
|
||
|
// NOTE: this completely and truly reverts the status and
|
||
|
// payload. No trace is left of this action having ever
|
||
|
// taken place, not even in the version number.
|
||
|
for (uint i=0;i<limit;++i) {
|
||
|
WordAction wa=words[i];
|
||
|
if (wa.statusComparand.State==WordState.Original) {
|
||
|
wide.words[wa.index]
|
||
|
.CAS(wa.statusComparand,UIntPtr.Zero,
|
||
|
wa.statusComparand
|
||
|
.WithVersion(myVersion).WithInAction(true),
|
||
|
UIntPtr.Zero);
|
||
|
} else {
|
||
|
wide.words[wa.index]
|
||
|
.CAS(wa.statusComparand,
|
||
|
wa.payloadComparand,
|
||
|
wa.statusComparand
|
||
|
.WithVersion(myVersion).WithInAction(true),
|
||
|
wa.payloadComparand);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
internal void complete(WideObject wide) {
|
||
|
// prepare version numbers
|
||
|
for (uint i=0;i<words.Length;++i) {
|
||
|
words[i].statusValue.Version=myVersion;
|
||
|
}
|
||
|
|
||
|
// now proceed with the compare phase
|
||
|
for (uint i=0;i<words.Length && Res==ActionResult.None;++i) {
|
||
|
WordAction wa=words[i];
|
||
|
if (wa.statusComparand.State==WordState.Original) {
|
||
|
UIntPtr oldVal=*(UIntPtr*)
|
||
|
(Magic.addressOf(wide.from) + ToOffset(wa.index));
|
||
|
if (oldVal!=wa.payloadComparand) {
|
||
|
if (setResult(ActionResult.Failure)) {
|
||
|
rollback(wide, i);
|
||
|
}
|
||
|
return;
|
||
|
}
|
||
|
WordStatus statusResult;
|
||
|
UIntPtr payloadResult; // ignored
|
||
|
wide.words[wa.index]
|
||
|
.CAS(wa.statusComparand
|
||
|
.WithVersion(myVersion).WithInAction(true),
|
||
|
UIntPtr.Zero,
|
||
|
wa.statusComparand,
|
||
|
UIntPtr.Zero,
|
||
|
out statusResult,
|
||
|
out payloadResult);
|
||
|
if (statusResult.Version==myVersion) {
|
||
|
// someone is ahead of us. we know this
|
||
|
// because the statusValue.Version is our version.
|
||
|
} else if (statusResult.Word
|
||
|
!= wa.statusComparand.Word) {
|
||
|
// note that we know that if we were expecting
|
||
|
// the status to be Original then the only way
|
||
|
// for the payload to change is if the status
|
||
|
// changes too.
|
||
|
|
||
|
// if we get here we know that either the
|
||
|
// action had completed a _long_ time ago or
|
||
|
// it has genuinely failed. for this reason
|
||
|
// we do everything with a CAS.
|
||
|
if (setResult(ActionResult.Failure)) {
|
||
|
rollback(wide, i);
|
||
|
}
|
||
|
return;
|
||
|
}
|
||
|
} else {
|
||
|
WordStatus statusResult;
|
||
|
UIntPtr payloadResult;
|
||
|
wide.words[wa.index]
|
||
|
.CAS(wa.statusComparand
|
||
|
.WithVersion(myVersion).WithInAction(true),
|
||
|
wa.payloadComparand,
|
||
|
wa.statusComparand,wa.payloadComparand,
|
||
|
out statusResult,out payloadResult);
|
||
|
if (statusResult.Version==wa.statusValue.Version) {
|
||
|
// someone is ahead of us (see discussion above)
|
||
|
} else if (statusResult.Word
|
||
|
!= wa.statusComparand.Word ||
|
||
|
payloadResult
|
||
|
!= wa.payloadComparand) {
|
||
|
// if we get here we know that either the
|
||
|
// action had completed a _long_ time ago or
|
||
|
// it has genuinely failed. for this reason
|
||
|
// we do everything with a CAS.
|
||
|
if (setResult(ActionResult.Failure)) {
|
||
|
rollback(wide, i);
|
||
|
}
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// We will only get here if the CAS has a genuine right to
|
||
|
// succeed. But there are really two possibilities here:
|
||
|
// -> The CAS really should succeed.
|
||
|
// -> There is at least one other concurrent attempt to
|
||
|
// complete this action. There was a non-atomic write to
|
||
|
// the words that we're writing that occurred after the
|
||
|
// other attempt had already concluded failure; this
|
||
|
// attempt makes the words appear as if they are correct
|
||
|
// for the CAS to succeed. In this case the CAS can
|
||
|
// either succeed or fail - either would be a linearizable
|
||
|
// result. So, we race to be the first to set the CAS
|
||
|
// result.
|
||
|
|
||
|
if (setResult(ActionResult.Success)) {
|
||
|
for (uint i=0;i<words.Length && Res==ActionResult.None;++i) {
|
||
|
WordAction wa=words[i];
|
||
|
if (wa.statusComparand.State==WordState.Original) {
|
||
|
wide.words[wa.index]
|
||
|
.CAS(wa.statusValue,wa.payloadValue,
|
||
|
wa.statusComparand
|
||
|
.WithVersion(myVersion).WithInAction(true),
|
||
|
UIntPtr.Zero);
|
||
|
} else {
|
||
|
wide.words[wa.index]
|
||
|
.CAS(wa.statusValue,wa.payloadValue,
|
||
|
wa.statusComparand
|
||
|
.WithVersion(myVersion).WithInAction(true),
|
||
|
wa.payloadComparand);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// BUGBUG: in order to allow the collector to run concurrently
|
||
|
// with the Copy phase, turn this into an object (rather than a
|
||
|
// struct) and have two subclasses, one for primitives and one
|
||
|
// for object references. Create the instances lazily.
|
||
|
[StructLayout(LayoutKind.Sequential)]
|
||
|
internal struct WideWord {
|
||
|
internal WordStatus status;
|
||
|
internal UIntPtr payload; // may be object pointer!!
|
||
|
|
||
|
internal WideWord(WordStatus status,
|
||
|
UIntPtr payload) {
|
||
|
this.status = status;
|
||
|
this.payload = payload;
|
||
|
}
|
||
|
|
||
|
[NoBarriers]
|
||
|
internal void CAS(WordStatus statusValue,
|
||
|
UIntPtr payloadValue,
|
||
|
WordStatus statusComparand,
|
||
|
UIntPtr payloadComparand,
|
||
|
out WordStatus statusResult,
|
||
|
out UIntPtr payloadResult) {
|
||
|
// BUGBUG: rudely assuming little-endian 32-bit architecture
|
||
|
long result=CoCoBarrier.CAS((long*)status.asPointer,
|
||
|
((long)statusValue.Word)
|
||
|
|(((long)payloadValue)<<32),
|
||
|
((long)statusComparand.Word)
|
||
|
|(((long)payloadComparand)<<32));
|
||
|
long lower32=(((long)1)<<32)-1;
|
||
|
statusResult=new WordStatus((UIntPtr)(result&lower32));
|
||
|
payloadResult=(UIntPtr)((result>>32)&lower32);
|
||
|
}
|
||
|
|
||
|
[NoBarriers]
|
||
|
internal bool CAS(WordStatus statusValue,
|
||
|
UIntPtr payloadValue,
|
||
|
WordStatus statusComparand,
|
||
|
UIntPtr payloadComparand) {
|
||
|
WordStatus statusResult;
|
||
|
UIntPtr payloadResult;
|
||
|
CAS(statusValue,payloadValue,
|
||
|
statusComparand,payloadComparand,
|
||
|
out statusResult,out payloadResult);
|
||
|
return statusResult.Word==statusComparand.Word
|
||
|
&& payloadResult==payloadComparand;
|
||
|
}
|
||
|
|
||
|
[NoBarriers]
|
||
|
internal void AtomicRead(out WordStatus statusResult,
|
||
|
out UIntPtr payloadResult) {
|
||
|
// BUGBUG: rudely assuming little-endian 32-bit architecture
|
||
|
long result=*(long*)status.asPointer;
|
||
|
long lower32=(((long)1)<<32)-1;
|
||
|
statusResult=new WordStatus((UIntPtr)(result&lower32));
|
||
|
payloadResult=(UIntPtr)((result>>32)&lower32);
|
||
|
}
|
||
|
|
||
|
[NoBarriers]
|
||
|
internal WideWord AtomicRead() {
|
||
|
WordStatus statusResult;
|
||
|
UIntPtr payloadResult;
|
||
|
AtomicRead(out statusResult,out payloadResult);
|
||
|
return new WideWord(statusResult,payloadResult);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
internal class WideObject {
|
||
|
internal WideObject next;
|
||
|
internal Object from;
|
||
|
internal Object copy;
|
||
|
internal Object action; /* must be Object not Action because
|
||
|
of CompareExchange! */
|
||
|
|
||
|
internal WideWord[] words;
|
||
|
|
||
|
internal WideObject(WideObject next,
|
||
|
Object from,
|
||
|
Object copy) {
|
||
|
this.next=next;
|
||
|
this.from=from;
|
||
|
this.copy=copy;
|
||
|
|
||
|
UIntPtr nwords=
|
||
|
(ObjectLayout.Sizeof(from)+sizeof(UIntPtr)-1)
|
||
|
/(uint)sizeof(UIntPtr);
|
||
|
|
||
|
this.words=new WideWord[(int)nwords];
|
||
|
|
||
|
this.action=null;
|
||
|
}
|
||
|
|
||
|
internal UIntPtr spaceOverhead {
|
||
|
get {
|
||
|
return ObjectLayout.Sizeof(this)
|
||
|
+ ObjectLayout.Sizeof(words);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
internal void completeOneAction() {
|
||
|
#if !ARM && !ISA_ARM
|
||
|
// HACK: MemoryBarrier is unimplemented in ARM
|
||
|
// and causes compile-time failures when building
|
||
|
// mscorlib in sepcomp mode. This change will break
|
||
|
// CoCo if built with ARM.
|
||
|
Thread.MemoryBarrier();
|
||
|
#endif
|
||
|
Action a=(Action)this.action;
|
||
|
if (a!=null) {
|
||
|
a.complete(this);
|
||
|
CAS(ref this.action,null,a);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
internal static int ToIndex(UIntPtr offset)
|
||
|
{
|
||
|
return (int)((offset + PreHeader.Size)/(uint)sizeof(UIntPtr));
|
||
|
}
|
||
|
|
||
|
internal static UIntPtr ToOffset(int index)
|
||
|
{
|
||
|
return (UIntPtr)index*(UIntPtr)sizeof(UIntPtr)
|
||
|
- PreHeader.Size;
|
||
|
}
|
||
|
|
||
|
internal override UIntPtr DoPin(UIntPtr address,
|
||
|
Pinner pinner)
|
||
|
{
|
||
|
if (fCount) {
|
||
|
numPins++;
|
||
|
}
|
||
|
|
||
|
UIntPtr baseAddr = FindObjectForInteriorPtr(address);
|
||
|
if (baseAddr != UIntPtr.Zero) {
|
||
|
Object o = Magic.fromAddress(baseAddr);
|
||
|
|
||
|
ForwardingWord f;
|
||
|
|
||
|
bool waited=false;
|
||
|
for (;;) {
|
||
|
f = GetForwardingWord(o);
|
||
|
if (f.State == ObjState.SimpleOrForwarded) {
|
||
|
break;
|
||
|
} else if (f.State == ObjState.Expanded) {
|
||
|
VTable.Assert(pinner==Pinner.Barrier);
|
||
|
if (fCount) {
|
||
|
if (!waited) {
|
||
|
waited=true;
|
||
|
numWaitPins++;
|
||
|
}
|
||
|
numPinWaits++;
|
||
|
}
|
||
|
// wait until it's copied
|
||
|
lock (interlock) {
|
||
|
CoCoThread t = MixinThread(Thread.CurrentThread);
|
||
|
t.pinnedOut = true;
|
||
|
Monitor.PulseAll(interlock);
|
||
|
while (t.pinnedOut) {
|
||
|
Monitor.Wait(interlock);
|
||
|
}
|
||
|
}
|
||
|
// ok, now try again (by the time we get here the
|
||
|
// object could already be in the process of being
|
||
|
// copied agagin!)
|
||
|
} else {
|
||
|
SwapObjectState(o,f.Clear(),f);
|
||
|
NotifyPin(baseAddr);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// what does it mean to get here? the object cannot
|
||
|
// be moved until the next pinning safepoint prior to
|
||
|
// a transition out of Idle.
|
||
|
|
||
|
VTable.Assert(f.State == ObjState.SimpleOrForwarded);
|
||
|
|
||
|
// correct address
|
||
|
f = GetForwardingWord(o);
|
||
|
address -= baseAddr;
|
||
|
address += f.ForwardBits;
|
||
|
}
|
||
|
|
||
|
return address;
|
||
|
}
|
||
|
|
||
|
[NoInline]
|
||
|
[CalledRarely]
|
||
|
protected override UIntPtr ReadWordSlow(Object o,
|
||
|
UIntPtr offset)
|
||
|
{
|
||
|
// FIXME: add debug here!
|
||
|
ForwardingWord f=GetForwardingWord(o);
|
||
|
if (fVerbose && DebugThread) {
|
||
|
VTable.DebugPrint("f.State = ");
|
||
|
VTable.DebugPrint((int)f.State);
|
||
|
VTable.DebugPrint(", f.ForwardBits = ");
|
||
|
VTable.DebugPrint((ulong)f.ForwardBits);
|
||
|
VTable.DebugPrint("\n");
|
||
|
}
|
||
|
UIntPtr result;
|
||
|
if (IgnoreOffset(offset)) {
|
||
|
result=*(UIntPtr*)(Magic.addressOf(o) + offset);
|
||
|
} else if (f.State==ObjState.Expanded) {
|
||
|
/*
|
||
|
if (false && phase==Phase.Idle) {
|
||
|
VTable.DebugPrint("PROBLEM: found expanded object in idle phase, o = ");
|
||
|
VTable.DebugPrint((ulong)Magic.addressOf(o));
|
||
|
VTable.DebugPrint("\n");
|
||
|
VTable.NotReached();
|
||
|
}
|
||
|
*/
|
||
|
if (true && f.Wide.from!=o) {
|
||
|
VTable.DebugPrint("PROBLEM: expanded object is corrupt. object = ");
|
||
|
VTable.DebugPrint((ulong)Magic.addressOf(o));
|
||
|
VTable.DebugPrint(", forwarding word = ");
|
||
|
VTable.DebugPrint((ulong)f.Bits);
|
||
|
VTable.DebugPrint(", wide.from = ");
|
||
|
VTable.DebugPrint((ulong)Magic.addressOf(f.Wide.from));
|
||
|
VTable.DebugPrint(", ToSpace(o) = ");
|
||
|
VTable.DebugPrint((ulong)ToSpaceAsPtr(o));
|
||
|
VTable.DebugPrint(", ToSpace(wide.from) = ");
|
||
|
VTable.DebugPrint((ulong)ToSpaceAsPtr(f.Wide.from));
|
||
|
VTable.DebugPrint("\n");
|
||
|
VTable.NotReached();
|
||
|
}
|
||
|
WideObject wide=f.Wide;
|
||
|
WideWord ww=wide.words[ToIndex(offset)].AtomicRead();
|
||
|
WordState s=ww.status.State;
|
||
|
switch (s) {
|
||
|
case WordState.Original:
|
||
|
result=*(UIntPtr*)(Magic.addressOf(wide.from)+offset);
|
||
|
break;
|
||
|
case WordState.InWide:
|
||
|
if (fVerbose) {
|
||
|
VTable.DebugPrint("actually reading from wide\n");
|
||
|
}
|
||
|
result=ww.payload;
|
||
|
break;
|
||
|
case WordState.InCopy:
|
||
|
if (fVerbose) {
|
||
|
VTable.DebugPrint("actually reading from copy\n");
|
||
|
}
|
||
|
result=*(UIntPtr*)(Magic.addressOf(wide.copy)+offset);
|
||
|
break;
|
||
|
default:
|
||
|
VTable.NotReached();
|
||
|
result=UIntPtr.Zero; // make compiler happy
|
||
|
break;
|
||
|
}
|
||
|
} else {
|
||
|
result=*(UIntPtr*)(f.ForwardBits + offset);
|
||
|
}
|
||
|
if (fVerboseRead) {
|
||
|
UIntPtr oldRes=*(UIntPtr*)(Magic.addressOf(o)+offset);
|
||
|
if (result!=oldRes) {
|
||
|
VTable.DebugPrint("read from ");
|
||
|
VTable.DebugPrint((ulong)Magic.addressOf(o));
|
||
|
VTable.DebugPrint("+");
|
||
|
VTable.DebugPrint((ulong)offset);
|
||
|
VTable.DebugPrint(": result = ");
|
||
|
VTable.DebugPrint((ulong)result);
|
||
|
VTable.DebugPrint(", oldRes = ");
|
||
|
VTable.DebugPrint((ulong)oldRes);
|
||
|
VTable.DebugPrint("\n");
|
||
|
}
|
||
|
}
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
internal static void WriteWordNoForward(Object o,
|
||
|
UIntPtr offset,
|
||
|
UIntPtr mask,
|
||
|
UIntPtr shiftedValue,
|
||
|
bool isObject)
|
||
|
{
|
||
|
UIntPtr *ptr = (UIntPtr*)(Magic.addressOf(o) + offset);
|
||
|
if (mask == UIntPtr.MaxValue) {
|
||
|
if (isObject) {
|
||
|
TargetBarrierWithForward(*ptr);
|
||
|
}
|
||
|
*ptr = shiftedValue;
|
||
|
} else {
|
||
|
for (;;) {
|
||
|
UIntPtr oldVal=*ptr;
|
||
|
if (CAS(ptr,
|
||
|
(oldVal&~mask)|(shiftedValue&mask),
|
||
|
oldVal)
|
||
|
== oldVal) {
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
internal static void WriteWordOriginal(ForwardingWord f,
|
||
|
UIntPtr offset,
|
||
|
UIntPtr mask,
|
||
|
UIntPtr shiftedValue,
|
||
|
bool isObject) {
|
||
|
WriteWordNoForward(f.Forward,offset,mask,shiftedValue,isObject);
|
||
|
}
|
||
|
|
||
|
internal static bool ExpandIfNecessary(ref ForwardingWord f,
|
||
|
Object o) {
|
||
|
if (f.State == ObjState.Tagged) {
|
||
|
// the Object is not yet expanded so attempt to
|
||
|
// expand it.
|
||
|
if (fVerbose) {
|
||
|
VTable.DebugPrint(" >> Expanding: ");
|
||
|
VTable.DebugPrint((ulong)Magic.addressOf(o));
|
||
|
VTable.DebugPrint("\n");
|
||
|
}
|
||
|
ForwardingWord newf=f.WithState(ObjState.Expanded);
|
||
|
if (!SwapObjectState(o,newf,f)) {
|
||
|
return false;
|
||
|
}
|
||
|
f=newf;
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
[NoInline]
|
||
|
[CalledRarely]
|
||
|
protected override void WriteWordSlow(Object o,
|
||
|
UIntPtr offset,
|
||
|
UIntPtr mask,
|
||
|
UIntPtr shiftedValue,
|
||
|
bool isObject)
|
||
|
{
|
||
|
if (fVerbose && DebugThread) {
|
||
|
VTable.DebugPrint("Writing slowly to ");
|
||
|
VTable.DebugPrint((ulong)Magic.addressOf(o));
|
||
|
VTable.DebugPrint(" + ");
|
||
|
VTable.DebugPrint((ulong)offset);
|
||
|
VTable.DebugPrint(" the value ");
|
||
|
VTable.DebugPrint((ulong)shiftedValue);
|
||
|
VTable.DebugPrint(" & ");
|
||
|
VTable.DebugPrint((ulong)mask);
|
||
|
if (isObject) {
|
||
|
VTable.DebugPrint(", which represents an object");
|
||
|
}
|
||
|
VTable.DebugPrint(".\n");
|
||
|
}
|
||
|
|
||
|
// this is a transaction that keeps retrying until it
|
||
|
// succeeds. it is lock-free but not wait-free. for
|
||
|
// word-sized fields it could be wait-free if we wanted
|
||
|
// it to be. to understand this code, I'd suggest looking
|
||
|
// for the return statements - these signify the commit
|
||
|
// points of the transaction. any time you fall out of the
|
||
|
// if branches and reloop, this signifies an abort-and-retry.
|
||
|
// at one point there is also an abort-and-retry performed
|
||
|
// using a continue statement.
|
||
|
for (;;) {
|
||
|
Phase p=phase;
|
||
|
ForwardingWord f=GetForwardingWord(o);
|
||
|
if (IgnoreOffset(offset)) {
|
||
|
WriteWordNoForward(o,offset,mask,shiftedValue,isObject);
|
||
|
return;
|
||
|
} else if (f.State==ObjState.SimpleOrForwarded ||
|
||
|
p==Phase.Idle) {
|
||
|
// If we get here it means that one of the following
|
||
|
// is going on:
|
||
|
// -> The object is in some weird state but we're still
|
||
|
// in the idle phase.
|
||
|
// -> The object became Simple at some point after the
|
||
|
// write fast path observed that it was not Simple.
|
||
|
// This could have occurred through the use of
|
||
|
// pinning.
|
||
|
// -> The object became Forwarded at some point after
|
||
|
// the write fast path observed that it was not
|
||
|
// Forwarded. This could have happened because
|
||
|
// copying for this object concluded.
|
||
|
|
||
|
WriteWordOriginal(f,offset,mask,shiftedValue,isObject);
|
||
|
return;
|
||
|
} else if (f.State == ObjState.Tainted &&
|
||
|
SwapObjectState(o,f.Clear(),f)) {
|
||
|
// -> The object was Tainted because of a concurrent
|
||
|
// write, and we managed to pin it (because that's
|
||
|
// our conservative way of handling writes that
|
||
|
// happen at the same time as tainted writes).
|
||
|
|
||
|
numTaintPins++;
|
||
|
NotifyPin(Magic.addressOf(o));
|
||
|
WriteWordOriginal(f,offset,mask,shiftedValue,isObject);
|
||
|
return;
|
||
|
} else if (p==Phase.Prep &&
|
||
|
f.State==ObjState.Tagged &&
|
||
|
SwapObjectState(o,
|
||
|
f.WithState(ObjState.Tainted),
|
||
|
f)) {
|
||
|
// This means that we are (or were) in the Prep phase
|
||
|
// and tried to write into a Tagged object. Since
|
||
|
// some threads may still be Idle, we cannot write by
|
||
|
// expanding the object - but at the same time we have
|
||
|
// to be aware of threads that may be in Copy, and so
|
||
|
// may be actively expanding objects. The compromise is
|
||
|
// to taint the object. This will prevent object
|
||
|
// expansion for the duration of the write.
|
||
|
WriteWordNoForward(o,offset,mask,shiftedValue,isObject);
|
||
|
|
||
|
// The write has succeeded, so we attempt to untaint the
|
||
|
// object and make it tagged again. Of course this
|
||
|
// will fail if a concurrent write had pinned the object.
|
||
|
SwapObjectState(o,f,f.WithState(ObjState.Tainted));
|
||
|
return;
|
||
|
} else if (p==Phase.Copy &&
|
||
|
(f.State==ObjState.Tagged ||
|
||
|
f.State==ObjState.Expanded)) {
|
||
|
// Now we have the interesting case: we're in the Copy
|
||
|
// phase and we have an object that is either tagged
|
||
|
// for expansion or is already expanded.
|
||
|
|
||
|
if (!ExpandIfNecessary(ref f,o)) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
// Now we have a forwarding word that is
|
||
|
// guaranteed to have a reference to a wide
|
||
|
// object. We know that the world is either in the
|
||
|
// Copy phase or in the Idle phase following the
|
||
|
// Copy phase. At worst, the wide object is "defunct",
|
||
|
// meaning that all of the fields are in the InCopy
|
||
|
// state. That's fine, since the wide object will
|
||
|
// have a reference to the to-space copy.
|
||
|
|
||
|
WideObject wide=f.Wide;
|
||
|
WideWord ww=wide.words[ToIndex(offset)];
|
||
|
WordStatus stat=ww.status;
|
||
|
if (stat.InAction) {
|
||
|
wide.completeOneAction();
|
||
|
} else if (stat.State==WordState.InCopy) {
|
||
|
WriteWordNoForward(wide.copy,offset,mask,
|
||
|
shiftedValue,isObject);
|
||
|
return;
|
||
|
} else if (stat.State==WordState.Original) {
|
||
|
if (fVerbose) {
|
||
|
VTable.DebugPrint(" ** copying field into wide object: from = ");
|
||
|
VTable.DebugPrint((ulong)Magic.addressOf(o));
|
||
|
VTable.DebugPrint(", wide = ");
|
||
|
VTable.DebugPrint((ulong)Magic.addressOf(wide));
|
||
|
VTable.DebugPrint(", offset = ");
|
||
|
VTable.DebugPrint((ulong)offset);
|
||
|
VTable.DebugPrint("\n");
|
||
|
}
|
||
|
UIntPtr oldVal=*(UIntPtr*)(Magic.addressOf(o)+offset);
|
||
|
if (isObject) {
|
||
|
TargetBarrierWithForward(oldVal);
|
||
|
}
|
||
|
if (wide.words[ToIndex(offset)]
|
||
|
.CAS(stat.WithState(WordState.InWide),
|
||
|
(shiftedValue&mask)|(oldVal&~mask),
|
||
|
stat,UIntPtr.Zero)) {
|
||
|
return;
|
||
|
}
|
||
|
} else /* stat.State==InWide */{
|
||
|
if (isObject) {
|
||
|
TargetBarrierWithForward(ww.payload);
|
||
|
}
|
||
|
if (wide.words[ToIndex(offset)]
|
||
|
.CAS(stat,(shiftedValue&mask)|(ww.payload&~mask),
|
||
|
stat,ww.payload)) {
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static bool WeakCASWordNoForward(Object o,
|
||
|
UIntPtr offset,
|
||
|
UIntPtr mask,
|
||
|
UIntPtr shiftedValue,
|
||
|
UIntPtr shiftedComparand) {
|
||
|
UIntPtr *ptr=(UIntPtr*)(Magic.addressOf(o)+offset);
|
||
|
UIntPtr oldVal=*ptr;
|
||
|
UIntPtr comparand = (oldVal&~mask)|(shiftedComparand&mask);
|
||
|
return CAS(ptr,
|
||
|
(oldVal&~mask)|(shiftedValue&mask),
|
||
|
comparand)
|
||
|
== comparand;
|
||
|
}
|
||
|
|
||
|
static bool WeakCASWordOriginal(ForwardingWord f,
|
||
|
UIntPtr offset,
|
||
|
UIntPtr mask,
|
||
|
UIntPtr shiftedValue,
|
||
|
UIntPtr shiftedComparand) {
|
||
|
return WeakCASWordNoForward(f.Forward,offset,mask,
|
||
|
shiftedValue,shiftedComparand);
|
||
|
}
|
||
|
|
||
|
[NoInline]
|
||
|
[CalledRarely]
|
||
|
protected override bool WeakCASWordSlow(Object o,
|
||
|
UIntPtr offset,
|
||
|
UIntPtr mask,
|
||
|
UIntPtr shiftedValue,
|
||
|
UIntPtr shiftedComparand,
|
||
|
bool isObject) {
|
||
|
// This is more-or-less modeled after WriteWordSlow, except
|
||
|
// that it will fail spuriously. Thus the large comment
|
||
|
// blocks are removed.
|
||
|
|
||
|
Phase p=phase;
|
||
|
ForwardingWord f=GetForwardingWord(o);
|
||
|
if (IgnoreOffset(offset)) {
|
||
|
return WeakCASWordNoForward(o,offset,mask,
|
||
|
shiftedValue,
|
||
|
shiftedComparand);
|
||
|
} else if (f.State==ObjState.SimpleOrForwarded ||
|
||
|
p==Phase.Idle) {
|
||
|
return WeakCASWordOriginal(f,offset,mask,
|
||
|
shiftedValue,
|
||
|
shiftedComparand);
|
||
|
} else if (f.State==ObjState.Tainted &&
|
||
|
SwapObjectState(o,f.Clear(),f)) {
|
||
|
numTaintPins++;
|
||
|
NotifyPin(Magic.addressOf(o));
|
||
|
return WeakCASWordOriginal(f,offset,mask,
|
||
|
shiftedValue,
|
||
|
shiftedComparand);
|
||
|
} else if (p==Phase.Prep &&
|
||
|
f.State==ObjState.Tagged &&
|
||
|
SwapObjectState(o,f.WithState(ObjState.Tainted),f)) {
|
||
|
bool result=
|
||
|
WeakCASWordNoForward(o,offset,mask,
|
||
|
shiftedValue,
|
||
|
shiftedComparand);
|
||
|
SwapObjectState(o,f,f.WithState(ObjState.Tainted));
|
||
|
return result;
|
||
|
} else if (p==Phase.Copy &&
|
||
|
(f.State==ObjState.Tagged ||
|
||
|
f.State==ObjState.Expanded)) {
|
||
|
// Now we have the interesting case: we're in the Copy
|
||
|
// phase and we have an object that is either tagged
|
||
|
// for expansion or is already expanded.
|
||
|
|
||
|
if (!ExpandIfNecessary(ref f,o)) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
WideObject wide=f.Wide;
|
||
|
WideWord ww=wide.words[ToIndex(offset)];
|
||
|
WordStatus stat=ww.status;
|
||
|
if (stat.InAction) {
|
||
|
wide.completeOneAction();
|
||
|
} else if (stat.State==WordState.InCopy) {
|
||
|
return WeakCASWordNoForward(wide.copy,offset,mask,
|
||
|
shiftedValue,
|
||
|
shiftedComparand);
|
||
|
} else if (stat.State==WordState.Original) {
|
||
|
UIntPtr oldVal=*(UIntPtr*)(Magic.addressOf(o)+offset);
|
||
|
if ((oldVal&mask)!=(shiftedComparand&mask)) {
|
||
|
return false;
|
||
|
}
|
||
|
return wide.words[ToIndex(offset)]
|
||
|
.CAS(stat.WithState(WordState.InWide),
|
||
|
(oldVal&~mask)|(shiftedValue&mask),
|
||
|
stat,UIntPtr.Zero);
|
||
|
} else /* stat.State == InWide */ {
|
||
|
return wide.words[ToIndex(offset)]
|
||
|
.CAS(stat,(ww.payload&~mask)|(shiftedValue&mask),
|
||
|
stat,(ww.payload&~mask)|(shiftedComparand&mask));
|
||
|
}
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
static bool WeakCASArbitraryNoForward(Object o,
|
||
|
UIntPtr offset,
|
||
|
UIntPtr size,
|
||
|
ulong value,
|
||
|
ulong comparand) {
|
||
|
UIntPtr ptr=Magic.addressOf(o)+offset;
|
||
|
if (size==4) {
|
||
|
return CAS((int*)ptr,(int)value,(int)comparand)
|
||
|
== (int)comparand;
|
||
|
} else if (size==8) {
|
||
|
return CAS((long*)ptr,(long)value,(long)comparand)
|
||
|
== (long)comparand;
|
||
|
} else {
|
||
|
VTable.NotReached();
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static bool WeakCASArbitraryOriginal(ForwardingWord f,
|
||
|
UIntPtr offset,
|
||
|
UIntPtr size,
|
||
|
ulong value,
|
||
|
ulong comparand) {
|
||
|
return WeakCASArbitraryNoForward(f.Forward,offset,size,
|
||
|
value,comparand);
|
||
|
}
|
||
|
|
||
|
[NoInline]
|
||
|
[CalledRarely]
|
||
|
protected override bool WeakCASArbitrarySlow(Object o,
|
||
|
UIntPtr offset,
|
||
|
UIntPtr size,
|
||
|
ulong value,
|
||
|
ulong comparand) {
|
||
|
Phase p=phase;
|
||
|
ulong pv=CurThreadPhaseVersion;
|
||
|
ForwardingWord f=GetForwardingWord(o);
|
||
|
if (f.State==ObjState.SimpleOrForwarded ||
|
||
|
p==Phase.Idle) {
|
||
|
return WeakCASArbitraryOriginal(f,offset,size,
|
||
|
value,
|
||
|
comparand);
|
||
|
} else if (f.State==ObjState.Tainted &&
|
||
|
SwapObjectState(o,f.Clear(),f)) {
|
||
|
numTaintPins++;
|
||
|
NotifyPin(Magic.addressOf(o));
|
||
|
return WeakCASArbitraryOriginal(f,offset,size,
|
||
|
value,
|
||
|
comparand);
|
||
|
} else if (p==Phase.Prep &&
|
||
|
f.State==ObjState.Tagged &&
|
||
|
SwapObjectState(o,f.WithState(ObjState.Tainted),f)) {
|
||
|
bool result=
|
||
|
WeakCASArbitraryNoForward(o,offset,size,
|
||
|
value,
|
||
|
comparand);
|
||
|
SwapObjectState(o,f,f.WithState(ObjState.Tainted));
|
||
|
return result;
|
||
|
} else if (p==Phase.Copy &&
|
||
|
(f.State==ObjState.Tagged ||
|
||
|
f.State==ObjState.Expanded)) {
|
||
|
if (!ExpandIfNecessary(ref f,o)) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
WideObject wide=f.Wide;
|
||
|
|
||
|
UIntPtr maxLowOff=(UIntPtr)sizeof(UIntPtr);
|
||
|
UIntPtr lowMask=maxLowOff-1;
|
||
|
UIntPtr lowOff=offset&lowMask;
|
||
|
UIntPtr baseOff=offset&~lowMask;
|
||
|
UIntPtr tailOff=(offset+size+lowMask)&~lowMask;
|
||
|
|
||
|
// check what the deal is with field states.
|
||
|
// if all fields are copied, then we just do
|
||
|
// a CAS directly on the to-space copy. if
|
||
|
// none of the fields are copied then we do
|
||
|
// an action-CAS. else we completeOneAction()
|
||
|
// and fail.
|
||
|
bool foundNotCopied=false;
|
||
|
bool foundCopied=false;
|
||
|
for (UIntPtr i=baseOff;i<tailOff;++i) {
|
||
|
WordState s=wide.words[ToIndex(i)].status.State;
|
||
|
if (s!=WordState.InCopy) {
|
||
|
foundNotCopied=true;
|
||
|
}
|
||
|
if (s==WordState.InCopy) {
|
||
|
foundCopied=true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (foundNotCopied && foundCopied) {
|
||
|
wide.completeOneAction();
|
||
|
return false;
|
||
|
} else if (!foundNotCopied) {
|
||
|
// they're all copied - do direct CAS on the
|
||
|
// to-space copy
|
||
|
return WeakCASArbitraryNoForward(wide.copy,
|
||
|
offset,
|
||
|
size,
|
||
|
value,
|
||
|
comparand);
|
||
|
} else /* !foundCopied */ {
|
||
|
// none of them are copied - do an action-CAS
|
||
|
|
||
|
// DANGER! this is a safepoint!
|
||
|
Action a=new Action((uint)(tailOff-baseOff));
|
||
|
|
||
|
// check if the world has changed
|
||
|
if (CurThreadPhaseVersion != pv) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
for (UIntPtr i=baseOff;i<tailOff;++i) {
|
||
|
WideWord ww=wide.words[ToIndex(i)];
|
||
|
|
||
|
if (ww.status.State==WordState.InCopy ||
|
||
|
ww.status.InAction) {
|
||
|
// no good - give up and try again, but
|
||
|
// try to completeOneAction for good
|
||
|
// measure.
|
||
|
wide.completeOneAction();
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
UIntPtr oldVal;
|
||
|
|
||
|
if (ww.status.State == WordState.Original) {
|
||
|
oldVal = *(UIntPtr*)(Magic.addressOf(o) + i);
|
||
|
} else {
|
||
|
oldVal = ww.payload;
|
||
|
}
|
||
|
|
||
|
// offsetIntoField tells us the position of
|
||
|
// i in the field we're CASing. this may be
|
||
|
// negative
|
||
|
int begin=(int)(i-offset);
|
||
|
int end=(int)(i+(UIntPtr)sizeof(UIntPtr)-offset);
|
||
|
|
||
|
UIntPtr curValueWord=UIntPtr.Zero;
|
||
|
UIntPtr curComparandWord=UIntPtr.Zero;
|
||
|
UIntPtr curWordMask=UIntPtr.Zero;
|
||
|
|
||
|
// what follows is pure genius
|
||
|
byte *valuePtr=
|
||
|
(byte*)Magic.toPointer(ref value);
|
||
|
byte *comparandPtr=
|
||
|
(byte*)Magic.toPointer(ref comparand);
|
||
|
if (instance.BigEndian) {
|
||
|
valuePtr+=(UIntPtr)8-size;
|
||
|
comparandPtr+=(UIntPtr)8-size;
|
||
|
}
|
||
|
valuePtr+=begin;
|
||
|
comparandPtr+=begin;
|
||
|
byte *curValueWordPtr=
|
||
|
(byte*)Magic.toPointer(ref curValueWord);
|
||
|
byte *curComparandWordPtr=
|
||
|
(byte*)Magic.toPointer(ref curComparandWord);
|
||
|
byte *curWordMaskPtr=
|
||
|
(byte*)Magic.toPointer(ref curWordMask);
|
||
|
// this loop is not very quick, but then again,
|
||
|
// it doesn't have to be at this point.
|
||
|
for (int j=begin;j<end;++j) {
|
||
|
if (j>=0 && j<(int)size) {
|
||
|
*curValueWordPtr=*valuePtr;
|
||
|
*curComparandWordPtr=*comparandPtr;
|
||
|
*curWordMaskPtr=255;
|
||
|
}
|
||
|
valuePtr++;
|
||
|
comparandPtr++;
|
||
|
curValueWordPtr++;
|
||
|
curComparandWordPtr++;
|
||
|
curWordMaskPtr++;
|
||
|
}
|
||
|
|
||
|
a.words[(int)((i-baseOff)/(uint)sizeof(UIntPtr))]
|
||
|
=new WordAction(ToIndex(i),
|
||
|
ww.status.WithState(WordState.InWide),
|
||
|
(curValueWord&curWordMask)
|
||
|
|(oldVal&~curWordMask),
|
||
|
ww.status,
|
||
|
(curComparandWord&curWordMask)
|
||
|
|(oldVal&~curWordMask));
|
||
|
}
|
||
|
|
||
|
// really work hard to try to install this action -
|
||
|
// it took enough effort to build that giving up
|
||
|
// would be silly.
|
||
|
while (CAS(ref wide.action,a,null) != null) {
|
||
|
wide.completeOneAction();
|
||
|
}
|
||
|
|
||
|
// this either completes our action or some action
|
||
|
// that follows it.
|
||
|
wide.completeOneAction();
|
||
|
|
||
|
// our action must be complete here. if it isn't then
|
||
|
// it's a bug and we should abort.
|
||
|
if (a.Res==ActionResult.None) {
|
||
|
VTable.NotReached();
|
||
|
}
|
||
|
|
||
|
return a.Res==ActionResult.Success;
|
||
|
} /* !foundCopied */
|
||
|
} /* p==Copy &&
|
||
|
(f.state==Tagged ||
|
||
|
f.state==Expanded) */
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
internal static WideObject wideHead;
|
||
|
|
||
|
internal override bool AnyTaggedForCopying {
|
||
|
get {
|
||
|
return wideHead!=null;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
internal override bool TagObjectForCopy(Object from,
|
||
|
Object to,
|
||
|
out UIntPtr spaceOverhead)
|
||
|
{
|
||
|
WideObject wide=
|
||
|
new WideObject(wideHead,
|
||
|
from,
|
||
|
to);
|
||
|
spaceOverhead=wide.spaceOverhead;
|
||
|
if (fVerbose) {
|
||
|
VTable.DebugPrint(" $$ tagging object ");
|
||
|
VTable.DebugPrint((ulong)Magic.addressOf(from));
|
||
|
VTable.DebugPrint(", wide = ");
|
||
|
VTable.DebugPrint((ulong)Magic.addressOf(wide));
|
||
|
VTable.DebugPrint(", copy = ");
|
||
|
VTable.DebugPrint((ulong)Magic.addressOf(to));
|
||
|
VTable.DebugPrint("\n");
|
||
|
}
|
||
|
if (SwapObjectState(from,
|
||
|
new ForwardingWord(from,
|
||
|
ObjState.Tagged,
|
||
|
wide),
|
||
|
new ForwardingWord(from,
|
||
|
UIntPtr.Zero))) {
|
||
|
wideHead=wide;
|
||
|
return true;
|
||
|
} else {
|
||
|
if (fDebug) {
|
||
|
VTable.DebugPrint(" did not tag object because the CAS on the forwarding word failed\n");
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
internal override bool NeedsPrepPhase {
|
||
|
get {
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[NoBarriers]
|
||
|
internal override ulong Copy()
|
||
|
{
|
||
|
if (fDebug) {
|
||
|
VTable.DebugPrint(" --> CoCo in Copy\n");
|
||
|
}
|
||
|
|
||
|
ulong numAttempted=0, numCopied=0, numActionCopied=0;
|
||
|
WideObject wideHead=ExpandingCoCoBarrier.wideHead;
|
||
|
ExpandingCoCoBarrier.wideHead=null;
|
||
|
for (WideObject wide=wideHead;
|
||
|
wide!=null;
|
||
|
wide=wide.next) {
|
||
|
|
||
|
numAttempted++;
|
||
|
|
||
|
Object from=wide.from;
|
||
|
|
||
|
if (fGorierCopy && from==interlock) {
|
||
|
VTable.DebugPrint(" AWESOME: attempting to move interlock from ");
|
||
|
VTable.DebugPrint((ulong)Magic.addressOf(interlock));
|
||
|
VTable.DebugPrint(" to ");
|
||
|
VTable.DebugPrint((ulong)Magic.addressOf(wide.copy));
|
||
|
VTable.DebugPrint("\n");
|
||
|
}
|
||
|
|
||
|
if (fGorierCopy) {
|
||
|
VTable.DebugPrint(" ---> operating on ");
|
||
|
VTable.DebugPrint((ulong)Magic.addressOf(wide));
|
||
|
VTable.DebugPrint(" (from = ");
|
||
|
VTable.DebugPrint((ulong)Magic.addressOf(wide.from));
|
||
|
VTable.DebugPrint(", copy = ");
|
||
|
VTable.DebugPrint((ulong)Magic.addressOf(wide.copy));
|
||
|
VTable.DebugPrint(")\n");
|
||
|
}
|
||
|
|
||
|
ForwardingWord f;
|
||
|
for (long cnt=0;;cnt++) {
|
||
|
f=GetForwardingWord(from);
|
||
|
if (cnt>10 && fDebug) {
|
||
|
VTable.DebugPrint(" for ");
|
||
|
VTable.DebugPrint((ulong)Magic.addressOf(from));
|
||
|
VTable.DebugPrint(", forwarding word = ");
|
||
|
VTable.DebugPrint((ulong)f.Bits);
|
||
|
VTable.DebugPrint("\n");
|
||
|
}
|
||
|
if (f.State==ObjState.Tainted ||
|
||
|
(f.State==ObjState.Tagged &&
|
||
|
ShouldPin(Magic.addressOf(from)))) {
|
||
|
numTaintPins++;
|
||
|
NotifyPin(Magic.addressOf(from));
|
||
|
SwapObjectState(from,f.Clear(),f);
|
||
|
} else if (f.State==ObjState.SimpleOrForwarded ||
|
||
|
f.State==ObjState.Expanded) {
|
||
|
break;
|
||
|
} else if (f.State==ObjState.Tagged) {
|
||
|
SwapObjectState(from,f.WithState(ObjState.Expanded),f);
|
||
|
}
|
||
|
}
|
||
|
if (f.State!=ObjState.Expanded) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if (fGorierCopy && from==interlock) {
|
||
|
VTable.DebugPrint(" AWESOME: moving interlock from ");
|
||
|
VTable.DebugPrint((ulong)Magic.addressOf(interlock));
|
||
|
VTable.DebugPrint(" to ");
|
||
|
VTable.DebugPrint((ulong)Magic.addressOf(wide.copy));
|
||
|
VTable.DebugPrint("\n");
|
||
|
}
|
||
|
|
||
|
if (fVerboseCopy) {
|
||
|
VTable.DebugPrint(" - actually copying ");
|
||
|
VTable.DebugPrint((ulong)Magic.addressOf(wide));
|
||
|
VTable.DebugPrint(" (from = ");
|
||
|
VTable.DebugPrint((ulong)Magic.addressOf(wide.from));
|
||
|
VTable.DebugPrint(", copy = ");
|
||
|
VTable.DebugPrint((ulong)Magic.addressOf(wide.copy));
|
||
|
VTable.DebugPrint(")\n");
|
||
|
}
|
||
|
|
||
|
// copy fields into wide object
|
||
|
for (int i=0;i<wide.words.Length;++i) {
|
||
|
UIntPtr offset=ToOffset(i);
|
||
|
if (IgnoreOffset(offset)) {
|
||
|
continue;
|
||
|
}
|
||
|
for (;;) {
|
||
|
UIntPtr val=*(UIntPtr*)
|
||
|
(Magic.addressOf(from) + offset);
|
||
|
|
||
|
WordStatus oldStatus=wide.words[i].status;
|
||
|
if (oldStatus.InAction) {
|
||
|
wide.completeOneAction();
|
||
|
} else if (oldStatus.State
|
||
|
!=WordState.Original) {
|
||
|
break;
|
||
|
} else if (wide.words[i]
|
||
|
.CAS(oldStatus.WithState(WordState.InWide),
|
||
|
val,
|
||
|
oldStatus,
|
||
|
UIntPtr.Zero)) {
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bool didActionCopy=false;
|
||
|
|
||
|
// copy fields from wide object into to-space
|
||
|
// object
|
||
|
if (fGorierCopy) {
|
||
|
VTable.DebugPrint(" - copying:");
|
||
|
}
|
||
|
for (int i=0;i<wide.words.Length;i++) {
|
||
|
UIntPtr offset=ToOffset(i);
|
||
|
if (IgnoreOffset(offset)) {
|
||
|
// ignore
|
||
|
} else if (// check that we're not in the header. the
|
||
|
// header is not necessarily double-word aligned, and
|
||
|
// we ASSUME that it contains no double-words, so
|
||
|
// we should not be doing double-word copies there.
|
||
|
// Note that we use sign comparison because we want the
|
||
|
// offsets into the pre header to appear negative.
|
||
|
((IntPtr)offset)>=(IntPtr)PostHeader.Size &&
|
||
|
|
||
|
// there have to be two or more words left for us to
|
||
|
// do a double-word copy.
|
||
|
i+1<wide.words.Length &&
|
||
|
|
||
|
// check that the alignment of the object is at least
|
||
|
// double word. otherwise we know that there are no
|
||
|
// double words and so a double-word copy is not
|
||
|
// needed.
|
||
|
from.vtable.baseAlignment>=UIntPtr.Size*2) {
|
||
|
|
||
|
if (fGorierCopy) {
|
||
|
VTable.DebugPrint("\n*** using action-copy ***\n");
|
||
|
}
|
||
|
didActionCopy=true;
|
||
|
|
||
|
// double-word copying needed
|
||
|
UIntPtr *trg=(UIntPtr*)
|
||
|
(Magic.addressOf(wide.copy) + offset);
|
||
|
for (;;) {
|
||
|
WordStatus s1 = wide.words[i+0].status;
|
||
|
UIntPtr p1 = wide.words[i+0].payload;
|
||
|
|
||
|
WordStatus s2 = wide.words[i+1].status;
|
||
|
UIntPtr p2 = wide.words[i+1].payload;
|
||
|
|
||
|
if (s1.InAction || s2.InAction) {
|
||
|
wide.completeOneAction();
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
trg[0] = p1;
|
||
|
trg[1] = p2;
|
||
|
|
||
|
Action a = new Action(2);
|
||
|
|
||
|
a.words[0].index = i+0;
|
||
|
a.words[0].statusValue = s1.WithState(WordState.InCopy);
|
||
|
a.words[0].payloadValue = p1;
|
||
|
a.words[0].statusComparand = s1;
|
||
|
a.words[0].payloadComparand = p1;
|
||
|
|
||
|
a.words[1].index = i+1;
|
||
|
a.words[1].statusValue = s2.WithState(WordState.InCopy);
|
||
|
a.words[1].payloadValue = p2;
|
||
|
a.words[1].statusComparand = s2;
|
||
|
a.words[1].payloadComparand = p2;
|
||
|
|
||
|
while (CAS(ref wide.action,a,null) != null) {
|
||
|
wide.completeOneAction();
|
||
|
}
|
||
|
|
||
|
wide.completeOneAction();
|
||
|
|
||
|
if (a.Res == ActionResult.None) {
|
||
|
VTable.NotReached();
|
||
|
}
|
||
|
|
||
|
if (a.Res == ActionResult.Success) {
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
i++;
|
||
|
} else {
|
||
|
// only need single-word copying
|
||
|
UIntPtr *trg=(UIntPtr*)
|
||
|
(Magic.addressOf(wide.copy) + offset);
|
||
|
for (;;) {
|
||
|
WideWord ww=wide.words[i];
|
||
|
if (ww.status.InAction) {
|
||
|
// this is highly unlikely
|
||
|
wide.completeOneAction();
|
||
|
continue;
|
||
|
}
|
||
|
*trg=ww.payload;
|
||
|
if (wide.words[i]
|
||
|
.CAS(ww.status.WithState(WordState.InCopy),
|
||
|
ww.payload,
|
||
|
ww.status,
|
||
|
ww.payload)) {
|
||
|
if (fGorierCopy) {
|
||
|
VTable.DebugPrint(" [");
|
||
|
VTable.DebugPrint((ulong)offset);
|
||
|
VTable.DebugPrint(", ");
|
||
|
VTable.DebugPrint((ulong)ww.payload);
|
||
|
VTable.DebugPrint("]");
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (fGorierCopy) {
|
||
|
VTable.DebugPrint("\n");
|
||
|
}
|
||
|
|
||
|
if (didActionCopy) {
|
||
|
numActionCopied++;
|
||
|
}
|
||
|
|
||
|
// finish up for this object
|
||
|
ForwardingWord newf=
|
||
|
f.WithState(ObjState.SimpleOrForwarded)
|
||
|
.WithForward(wide.copy);
|
||
|
|
||
|
bool res = SwapObjectState(from,newf,f);
|
||
|
if (fGorierCopy) {
|
||
|
VTable.DebugPrint(" --> res = ");
|
||
|
VTable.DebugPrint(res);
|
||
|
VTable.DebugPrint("\n");
|
||
|
}
|
||
|
VTable.Assert(res, "object transitioned out of expanded without our approval");
|
||
|
|
||
|
numCopied++;
|
||
|
|
||
|
if (fDebug && (numAttempted%10000)==0) {
|
||
|
VTable.DebugPrint(" ---> copied ");
|
||
|
VTable.DebugPrint(numCopied);
|
||
|
VTable.DebugPrint(" / ");
|
||
|
VTable.DebugPrint(numAttempted);
|
||
|
VTable.DebugPrint("\n");
|
||
|
}
|
||
|
}
|
||
|
if (fDebug) {
|
||
|
VTable.DebugPrint(" --> copied ");
|
||
|
VTable.DebugPrint(numCopied);
|
||
|
VTable.DebugPrint(" / ");
|
||
|
VTable.DebugPrint(numAttempted);
|
||
|
VTable.DebugPrint("; ");
|
||
|
VTable.DebugPrint(numActionCopied);
|
||
|
VTable.DebugPrint(" action-copied\n");
|
||
|
}
|
||
|
|
||
|
return numCopied;
|
||
|
}
|
||
|
}
|
||
|
}
|