Skip to content

Commit e26f96b

Browse files
author
Jianchun Xu
committed
swb: annotate BaseDictionary (partial)
Expand `Pointer` macro to choose `WriteBarrierPtr/NoWriteBarrierPtr` based on optional `Allocator` type using `WriteBarrierPtrTraits`. (E.g. pointer fields but using with Arena, choose NoWriteBarrierPtr.) Add `WriteBarrier` function to trigger write barrier on array content when needed. Added `CopyArray` function to copy array data. Triggers write barrier on the array content when needed. By default only pointer types, WriteBarrierPtr wrapper, or explicit _write_barrier_policy trigger write barrier (when using Recycler). Users can also use template instantiation to alter custom allocator/type write barrier policy. This change does not handle individual `Item` write barrier yet.
1 parent bc973a5 commit e26f96b

File tree

9 files changed

+171
-55
lines changed

9 files changed

+171
-55
lines changed

lib/Common/DataStructures/BaseDictionary.h

Lines changed: 25 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -90,17 +90,17 @@ namespace JsUtil
9090
friend class Js::RemoteDictionary<BaseDictionary>;
9191
template <typename ValueOrKey> struct ComparerType { typedef Comparer<ValueOrKey> Type; }; // Used by diagnostics to access Comparer type
9292

93-
int* buckets;
94-
EntryType* entries;
95-
AllocatorType* alloc;
96-
int size;
97-
uint bucketCount;
98-
int count;
99-
int freeList;
100-
int freeCount;
93+
Pointer(int, TAllocator) buckets;
94+
Pointer(EntryType, TAllocator) entries;
95+
PointerNoBarrier(AllocatorType) alloc;
96+
Field(int) size;
97+
Field(uint) bucketCount;
98+
Field(int) count;
99+
Field(int) freeList;
100+
Field(int) freeCount;
101101

102102
#if PROFILE_DICTIONARY
103-
DictionaryStats *stats;
103+
PointerNoBarrier(DictionaryStats) stats;
104104
#endif
105105
enum InsertOperations
106106
{
@@ -179,11 +179,8 @@ namespace JsUtil
179179
freeList = other.freeList;
180180
freeCount = other.freeCount;
181181

182-
size_t copySize = bucketCount * sizeof(buckets[0]);
183-
js_memcpy_s(buckets, copySize, other.buckets, copySize);
184-
185-
copySize = size * sizeof(entries[0]);
186-
js_memcpy_s(entries, copySize, other.entries, copySize);
182+
CopyArray<TAllocator>(buckets, bucketCount, other.buckets, bucketCount);
183+
CopyArray<TAllocator, EntryType, ValueType>(entries, size, other.entries, size);
187184

188185
#if PROFILE_DICTIONARY
189186
stats = DictionaryStats::Create(typeid(this).name(), size);
@@ -698,11 +695,8 @@ namespace JsUtil
698695
freeList = other->freeList;
699696
freeCount = other->freeCount;
700697

701-
size_t copySize = bucketCount * sizeof(buckets[0]);
702-
js_memcpy_s(buckets, copySize, other->buckets, copySize);
703-
704-
copySize = size * sizeof(entries[0]);
705-
js_memcpy_s(entries, copySize, other->entries, copySize);
698+
CopyArray<TAllocator>(buckets, bucketCount, other->buckets, bucketCount);
699+
CopyArray<TAllocator, EntryType, ValueType>(entries, size, other->entries, size);
706700

707701
#if PROFILE_DICTIONARY
708702
stats = DictionaryStats::Create(typeid(this).name(), size);
@@ -860,9 +854,14 @@ namespace JsUtil
860854
int initSize = max(capacity, 4);
861855
uint initBucketCount = SizePolicy::GetBucketSize(initSize);
862856
AssertMsg(initBucketCount > 0, "Size returned by policy should be greater than 0");
863-
Allocate(&buckets, &entries, initBucketCount, initSize);
864857

865-
// Allocation can throw - assign the size only after allocation has succeeded.
858+
int* newBuckets = nullptr;
859+
EntryType* newEntries = nullptr;
860+
Allocate(&newBuckets, &newEntries, initBucketCount, initSize);
861+
862+
// Allocation can throw - assign only after allocation has succeeded.
863+
this->buckets = newBuckets;
864+
this->entries = newEntries;
866865
this->bucketCount = initBucketCount;
867866
this->size = initSize;
868867
Assert(this->freeCount == 0);
@@ -1002,7 +1001,7 @@ namespace JsUtil
10021001
{
10031002
// no need to rehash
10041003
newEntries = AllocateEntries(newSize);
1005-
js_memcpy_s(newEntries, sizeof(EntryType) * newSize, entries, sizeof(EntryType) * count);
1004+
CopyArray<TAllocator, EntryType, ValueType>(newEntries, newSize, entries, count);
10061005

10071006
DeleteEntries(entries, size);
10081007

@@ -1012,7 +1011,7 @@ namespace JsUtil
10121011
}
10131012

10141013
Allocate(&newBuckets, &newEntries, newBucketCount, newSize);
1015-
js_memcpy_s(newEntries, sizeof(EntryType) * newSize, entries, sizeof(EntryType) * count);
1014+
CopyArray<TAllocator, EntryType, ValueType>(newEntries, newSize, entries, count);
10161015

10171016
// When TAllocator is of type Recycler, it is possible that the Allocate above causes a collection, which
10181017
// in turn can cause entries in the dictionary to be removed - i.e. the dictionary contains weak references
@@ -1037,10 +1036,10 @@ namespace JsUtil
10371036
if (stats)
10381037
stats->Resize(newSize, /*emptyBuckets=*/ newSize - size);
10391038
#endif
1040-
buckets = newBuckets;
1039+
this->buckets = newBuckets;
1040+
this->entries = newEntries;
10411041
bucketCount = newBucketCount;
10421042
size = newSize;
1043-
entries = newEntries;
10441043
}
10451044

10461045
__ecount(bucketCount) int *AllocateBuckets(DECLSPEC_GUARD_OVERFLOW const uint bucketCount)
@@ -1560,7 +1559,7 @@ namespace JsUtil
15601559
class SynchronizedDictionary: protected BaseDictionary<TKey, TValue, TAllocator, SizePolicy, Comparer, Entry>
15611560
{
15621561
private:
1563-
SyncObject* syncObj;
1562+
PointerNoBarrier(SyncObject) syncObj;
15641563

15651564
typedef BaseDictionary<TKey, TValue, TAllocator, SizePolicy, Comparer, Entry> Base;
15661565
public:
@@ -1812,4 +1811,3 @@ namespace JsUtil
18121811
PREVENT_COPY(SynchronizedDictionary);
18131812
};
18141813
}
1815-

lib/Common/DataStructures/DefaultContainerLockPolicy.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ namespace Js
1414
{
1515
public:
1616
template <class SyncObject>
17-
NoLock(SyncObject*)
17+
NoLock(const SyncObject&)
1818
{
1919
// No lock, do nothing.
2020
}

lib/Common/Exceptions/Throw.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ namespace Memory {
3737
using namespace Memory;
3838
#include "Memory/Allocator.h"
3939
#include "Memory/HeapAllocator.h"
40+
#include "Memory/RecyclerPointers.h"
4041

4142
// Data structure
4243
#include "DataStructures/Comparer.h"

lib/Common/Memory/RecyclerPointers.h

Lines changed: 128 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,135 @@
66

77
namespace Memory
88
{
9+
class Recycler;
10+
class RecyclerNonLeafAllocator;
11+
12+
// Dummy tag classes to mark yes/no write barrier policy
13+
//
14+
struct _write_barrier_policy {};
15+
struct _no_write_barrier_policy {};
16+
17+
// Type write barrier policy
18+
//
19+
// By default following potentially contains GC pointers and use write barrier policy:
20+
// pointer, WriteBarrierPtr, _write_barrier_policy
21+
//
22+
template <class T>
23+
struct TypeWriteBarrierPolicy { typedef _no_write_barrier_policy Policy; };
24+
template <class T>
25+
struct TypeWriteBarrierPolicy<T*> { typedef _write_barrier_policy Policy; };
26+
template <class T>
27+
struct TypeWriteBarrierPolicy<WriteBarrierPtr<T>> { typedef _write_barrier_policy Policy; };
28+
template <>
29+
struct TypeWriteBarrierPolicy<_write_barrier_policy> { typedef _write_barrier_policy Policy; };
30+
31+
// AllocatorType write barrier policy
32+
//
33+
// Recycler allocator type => _write_barrier_policy
34+
// Note that Recycler allocator type consists of multiple allocators:
35+
// Recycler, RecyclerNonLeafAllocator, RecyclerLeafAllocator
36+
//
37+
template <class AllocatorType>
38+
struct _AllocatorTypeWriteBarrierPolicy { typedef _no_write_barrier_policy Policy; };
39+
template <>
40+
struct _AllocatorTypeWriteBarrierPolicy<Recycler> { typedef _write_barrier_policy Policy; };
41+
42+
template <class Policy1, class Policy2>
43+
struct _AndWriteBarrierPolicy { typedef _no_write_barrier_policy Policy; };
44+
template <>
45+
struct _AndWriteBarrierPolicy<_write_barrier_policy, _write_barrier_policy>
46+
{
47+
typedef _write_barrier_policy Policy;
48+
};
49+
50+
// Combine Allocator + Data => write barrier policy
51+
// Specialize RecyclerNonLeafAllocator
52+
//
53+
template <class Allocator, class T>
54+
struct AllocatorWriteBarrierPolicy
55+
{
56+
typedef typename AllocatorInfo<Allocator, void>::AllocatorType AllocatorType;
57+
typedef typename _AndWriteBarrierPolicy<
58+
typename _AllocatorTypeWriteBarrierPolicy<AllocatorType>::Policy,
59+
typename TypeWriteBarrierPolicy<T>::Policy>::Policy Policy;
60+
};
61+
template <class T>
62+
struct AllocatorWriteBarrierPolicy<RecyclerNonLeafAllocator, T> { typedef _write_barrier_policy Policy; };
63+
template <>
64+
struct AllocatorWriteBarrierPolicy<RecyclerNonLeafAllocator, int> { typedef _no_write_barrier_policy Policy; };
65+
66+
// Choose WriteBarrierPtr or NoWriteBarrierPtr based on Policy
67+
//
68+
template <class T, class Policy>
69+
struct _WriteBarrierPtrPolicy { typedef NoWriteBarrierPtr<T> Ptr; };
70+
template <class T>
71+
struct _WriteBarrierPtrPolicy<T, _write_barrier_policy> { typedef WriteBarrierPtr<T> Ptr; };
72+
73+
// Choose WriteBarrierPtr or NoWriteBarrierPtr based on Allocator and T* type
74+
//
75+
template <class T,
76+
class Allocator = Recycler,
77+
class Policy = typename AllocatorWriteBarrierPolicy<Allocator, T*>::Policy>
78+
struct WriteBarrierPtrTraits { typedef typename _WriteBarrierPtrPolicy<T, Policy>::Ptr Ptr; };
79+
80+
// ArrayWriteBarrier behavior
81+
//
82+
template <class Policy>
83+
struct _ArrayWriteBarrier
84+
{
85+
template <class T>
86+
static void WriteBarrier(T * address, size_t count) {}
87+
};
88+
89+
#ifdef RECYCLER_WRITE_BARRIER
90+
template <>
91+
struct _ArrayWriteBarrier<_write_barrier_policy>
92+
{
93+
template <class T>
94+
static void WriteBarrier(T * address, size_t count)
95+
{
96+
RecyclerWriteBarrierManager::WriteBarrier(address, sizeof(T) * count);
97+
}
98+
};
99+
#endif
100+
101+
// Trigger write barrier on changing array content if Allocator and element type
102+
// determines write barrier is needed. Ignore otherwise.
103+
//
104+
template <class T, class Allocator = Recycler, class PolicyType = T>
105+
void WriteBarrier(T * address, size_t count)
106+
{
107+
typedef typename AllocatorWriteBarrierPolicy<Allocator, PolicyType>::Policy Policy;
108+
return _ArrayWriteBarrier<Policy>::WriteBarrier(address, count);
109+
}
110+
111+
// Copy array content. Triggers write barrier on the dst array content if if
112+
// Allocator and element type determines write barrier is needed.
113+
//
114+
template <class Allocator, class T, class PolicyType = T>
115+
void CopyArray(T* dst, size_t dstCount, const T* src, size_t srcCount)
116+
{
117+
js_memcpy_s(dst, sizeof(T) * dstCount, src, sizeof(T) * srcCount);
118+
WriteBarrier<T, Allocator, PolicyType>(dst, dstCount);
119+
}
120+
template <class Allocator, class T, class PolicyType = T>
121+
void CopyArray(NoWriteBarrierPtr<T>& dst, size_t dstCount, const NoWriteBarrierPtr<T>& src, size_t srcCount)
122+
{
123+
return CopyArray<Allocator, T, PolicyType>((T*)dst, dstCount, (const T*)src, srcCount);
124+
}
125+
template <class Allocator, class T, class PolicyType = T>
126+
void CopyArray(WriteBarrierPtr<T>& dst, size_t dstCount, const WriteBarrierPtr<T>& src, size_t srcCount)
127+
{
128+
return CopyArray<Allocator, T, PolicyType>((T*)dst, dstCount, (const T*)src, srcCount);
129+
}
130+
131+
9132
template <typename T>
10133
class NoWriteBarrierField
11134
{
12135
public:
13136
NoWriteBarrierField() {}
14-
NoWriteBarrierField(T const& value) : value(value) {}
137+
explicit NoWriteBarrierField(T const& value) : value(value) {}
15138

16139
// Getters
17140
operator T const&() const { return value; }
@@ -136,23 +259,17 @@ class WriteBarrierPtr
136259
static void MoveArray(WriteBarrierPtr * dst, WriteBarrierPtr * src, size_t count)
137260
{
138261
memmove((void *)dst, src, sizeof(WriteBarrierPtr) * count);
139-
#ifdef RECYCLER_WRITE_BARRIER
140-
RecyclerWriteBarrierManager::WriteBarrier(dst, count);
141-
#endif
262+
WriteBarrier(dst, count);
142263
}
143264
static void CopyArray(WriteBarrierPtr * dst, size_t dstCount, T const* src, size_t srcCount)
144265
{
145266
js_memcpy_s((void *)dst, sizeof(WriteBarrierPtr) * dstCount, src, sizeof(T *) * srcCount);
146-
#ifdef RECYCLER_WRITE_BARRIER
147-
RecyclerWriteBarrierManager::WriteBarrier(dst, dstCount);
148-
#endif
267+
WriteBarrier(dst, dstCount);
149268
}
150269
static void CopyArray(WriteBarrierPtr * dst, size_t dstCount, WriteBarrierPtr const* src, size_t srcCount)
151270
{
152271
js_memcpy_s((void *)dst, sizeof(WriteBarrierPtr) * dstCount, src, sizeof(WriteBarrierPtr) * srcCount);
153-
#ifdef RECYCLER_WRITE_BARRIER
154-
RecyclerWriteBarrierManager::WriteBarrier(dst, dstCount);
155-
#endif
272+
WriteBarrier(dst, dstCount);
156273
}
157274
static void ClearArray(WriteBarrierPtr * dst, size_t count)
158275
{
@@ -162,7 +279,7 @@ class WriteBarrierPtr
162279
private:
163280
T * ptr;
164281
};
165-
}
282+
} // namespace Memory
166283

167284

168285
template<class T> inline

lib/Common/Memory/RecyclerWriteBarrierManager.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ X64WriteBarrierCardTableManager::OnThreadInit()
5757
ULONG_PTR stackEnd = 0;
5858
::GetCurrentThreadStackLimits(&stackEnd, &stackBase);
5959
#endif
60-
60+
6161
size_t numPages = (stackBase - stackEnd) / AutoSystemInfo::PageSize;
6262
// stackEnd is the lower boundary
6363
return OnSegmentAlloc((char*) stackEnd, numPages);
@@ -283,11 +283,11 @@ RecyclerWriteBarrierManager::WriteBarrier(void * address)
283283

284284

285285
void
286-
RecyclerWriteBarrierManager::WriteBarrier(void * address, size_t ptrCount)
286+
RecyclerWriteBarrierManager::WriteBarrier(void * address, size_t bytes)
287287
{
288288
#ifdef RECYCLER_WRITE_BARRIER_BYTE
289289
uintptr_t startIndex = GetCardTableIndex(address);
290-
char * endAddress = (char *)Math::Align<INT_PTR>((INT_PTR)((char *)address + sizeof(void *) * ptrCount), s_WriteBarrierPageSize);
290+
char * endAddress = (char *)Math::Align<INT_PTR>((INT_PTR)((char *)address + bytes), s_WriteBarrierPageSize);
291291
uintptr_t endIndex = GetCardTableIndex(endAddress);
292292
Assert(startIndex <= endIndex);
293293
memset(cardTable + startIndex, 1, endIndex - startIndex);
@@ -297,7 +297,7 @@ RecyclerWriteBarrierManager::WriteBarrier(void * address, size_t ptrCount)
297297
uint bitMask = 0xFFFFFFFF << bitShift;
298298
uint cardIndex = ((uint)address) / s_BytesPerCard);
299299

300-
char * endAddress = (char *)Math::Align((INT_PTR)((char *)address + sizeof(void *) * ptrCount), s_BytesPerCardBit);
300+
char * endAddress = (char *)Math::Align((INT_PTR)((char *)address + bytes), s_BytesPerCardBit);
301301
char * alignedAddress = (char *)Math::Align((INT_PTR)address, s_WriteBarrierPageSize);
302302
if (alignedAddress > endAddress)
303303
{

lib/Common/Memory/RecyclerWriteBarrierManager.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ class RecyclerWriteBarrierManager
123123
{
124124
public:
125125
static void WriteBarrier(void * address);
126-
static void WriteBarrier(void * address, size_t ptrCount);
126+
static void WriteBarrier(void * address, size_t bytes);
127127

128128
// For JIT
129129
static uintptr_t GetCardTableIndex(void * address);

lib/Common/Memory/WriteBarrierMacros.h

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
// Usage:
1313
// In general, don't need to use anything, just use the macros to annotate your fields
1414
//
15-
// If you want to force your fields to always generate the right write-barrier types,
15+
// If you want to force your fields to always generate the right write-barrier types,
1616
// at the start of the file, add the following:
1717
// #define FORCE_USE_WRITE_BARRIER
1818
// #include <Memory/WriteBarrierMacros.h>
@@ -27,34 +27,34 @@
2727
#define PushMacro(x) __pragma(push_macro( #x ))
2828

2929
#define SAVE_WRITE_BARRIER_MACROS() \
30-
PushMacro("PointerNoBarrier") \
3130
PushMacro("Field") \
31+
PushMacro("PointerNoBarrier") \
3232
PushMacro("Pointer")
3333

3434
#define RESTORE_WRITE_BARRIER_MACROS() \
35-
PopMacro("PointerNoBarrier") \
3635
PopMacro("Field") \
36+
PopMacro("PointerNoBarrier") \
3737
PopMacro("Pointer")
3838

3939
#endif
4040

4141
#ifdef FORCE_USE_WRITE_BARRIER
4242
SAVE_WRITE_BARRIER_MACROS()
43-
#undef PointerNoBarrier
4443
#undef Field
44+
#undef PointerNoBarrier
4545
#undef Pointer
4646
#endif
4747

4848
// TODO: Turn off these annotations on Win32
4949
#if defined(__clang__) || defined(FORCE_USE_WRITE_BARRIER)
5050
// Various macros for defining field attributes
51-
#define PointerNoBarrier(type) NoWriteBarrierPtr<type>
5251
#define Field(type) NoWriteBarrierField<type>
53-
#define Pointer(type) WriteBarrierPtr<type>
52+
#define PointerNoBarrier(type) NoWriteBarrierPtr<type>
53+
#define Pointer(type, ...) typename WriteBarrierPtrTraits<type, ##__VA_ARGS__>::Ptr
5454
#else
55-
#define PointerNoBarrier(type) type*
5655
#define Field(type) type
57-
#define Pointer(type) type*
56+
#define PointerNoBarrier(type) type*
57+
#define Pointer(type, ...) type*
5858
#endif
5959

6060
#undef FORCE_USE_WRITE_BARRIER

0 commit comments

Comments
 (0)