Source code

Revision control

Other Tools

1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
3
/* This Source Code Form is subject to the terms of the Mozilla Public
4
* License, v. 2.0. If a copy of the MPL was not distributed with this
5
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
#ifndef mozilla_dom_SyncedContext_h
8
#define mozilla_dom_SyncedContext_h
9
10
#include "mozilla/dom/MaybeDiscarded.h"
11
#include "mozilla/EnumSet.h"
12
#include "mozilla/Maybe.h"
13
#include "mozilla/RefPtr.h"
14
#include "mozilla/Tuple.h"
15
#include <utility>
16
17
namespace mozilla {
18
namespace ipc {
19
class IProtocol;
20
class IPCResult;
21
template <typename T>
22
struct IPDLParamTraits;
23
} // namespace ipc
24
25
namespace dom {
26
class ContentParent;
27
class ContentChild;
28
29
namespace syncedcontext {
30
31
template <size_t I>
32
using Index = typename std::integral_constant<size_t, I>;
33
34
using IndexSet = EnumSet<size_t, uint64_t>;
35
36
template <typename Context>
37
class Transaction {
38
public:
39
// Set a field at the given index in this `Transaction`. Creating a
40
// `Transaction` object and setting multiple fields on it allows for
41
// multiple mutations to be performed atomically.
42
template <size_t I, typename U>
43
void Set(U&& aValue) {
44
auto& field = mozilla::Get<I>(mMaybeFields);
45
field.emplace(std::forward<U>(aValue));
46
}
47
48
// Apply the changes from this transaction to the specified Context in all
49
// processes. This method will call the correct `MaySet` and `DidSet` methods,
50
// as well as move the value.
51
//
52
// If the target has been discarded, changes will be ignored.
53
//
54
// NOTE: This method mutates `this`, resetting all members to `Nothing()`
55
nsresult Commit(Context* aOwner);
56
57
// Called from `ContentParent` in response to a transaction from content.
58
mozilla::ipc::IPCResult CommitFromIPC(const MaybeDiscarded<Context>& aOwner,
59
ContentParent* aSource);
60
61
// Called from `ContentChild` in response to a transaction from the parent.
62
mozilla::ipc::IPCResult CommitFromIPC(const MaybeDiscarded<Context>& aOwner,
63
uint64_t aEpoch, ContentChild* aSource);
64
65
void Write(IPC::Message* aMsg, mozilla::ipc::IProtocol* aActor) const;
66
bool Read(const IPC::Message* aMsg, PickleIterator* aIter,
67
mozilla::ipc::IProtocol* aActor);
68
69
private:
70
friend struct mozilla::ipc::IPDLParamTraits<Transaction<Context>>;
71
72
// You probably don't want to directly call this method - instead call
73
// `Commit`, which will perform the necessary synchronization.
74
//
75
// `Validate` must be called before calling this method.
76
void Apply(Context* aOwner);
77
78
// Returns the set of fields which failed to validate, or an empty set if
79
// there were no validation errors.
80
IndexSet Validate(Context* aOwner, ContentParent* aSource);
81
82
using FieldStorage = typename Context::FieldStorage;
83
static FieldStorage& GetFieldStorage(Context* aContext) {
84
return aContext->mFields;
85
}
86
87
// Call a generic lambda with a `Index<I>` for each index less than the number
88
// of elements in `FieldStorage`.
89
template <typename F>
90
static void EachIndexInner(Index<0>, F&&) {}
91
template <size_t I, typename F>
92
static void EachIndexInner(Index<I>, F&& aCallback) {
93
aCallback(Index<I - 1>());
94
EachIndexInner(Index<I - 1>(), aCallback);
95
}
96
template <typename F>
97
static void EachIndex(F&& aCallback) {
98
EachIndexInner(Index<FieldStorage::fieldCount>(), aCallback);
99
}
100
101
// Helper for invoking `std::get` or `mozilla::Get` using a
102
// `SyncedFieldIndex<I>` value.
103
template <size_t I, typename... Ts>
104
static auto& GetAt(Index<I>, mozilla::Tuple<Ts...>& aTarget) {
105
return mozilla::Get<I>(aTarget);
106
}
107
template <size_t I, typename T, size_t L>
108
static auto& GetAt(Index<I>, std::array<T, L>& aTarget) {
109
return std::get<I>(aTarget);
110
}
111
112
typename FieldStorage::MaybeFieldTuple mMaybeFields;
113
};
114
115
// Storage related to synchronized context fields. Contains both a tuple of
116
// individual field values, and epoch information for field synchronization.
117
template <typename, typename... Ts>
118
class FieldStorage {
119
public:
120
using FieldTuple = mozilla::Tuple<Ts...>;
121
122
static constexpr size_t fieldCount = sizeof...(Ts);
123
static_assert(fieldCount < 64,
124
"At most 64 synced fields are supported. Please file a bug if "
125
"you need to additional fields.");
126
127
const FieldTuple& Fields() const { return mFields; }
128
129
// Get an individual field by index.
130
template <size_t I>
131
const auto& Get() const {
132
return mozilla::Get<I>(mFields);
133
}
134
135
// Set the value of a field without telling other processes about the change.
136
//
137
// This is only sound in specific code which is already messaging other
138
// processes, and doesn't need to worry about epochs or other properties of
139
// field synchronization.
140
template <size_t I, typename U>
141
void SetWithoutSyncing(U&& aValue) {
142
mozilla::Get<I>(mFields) = std::move(aValue);
143
}
144
145
// Get a reference to a field that can modify without telling other
146
// processes about the change.
147
//
148
// This is only sound in specific code which is already messaging other
149
// processes, and doesn't need to worry about epochs or other properties of
150
// field synchronization.
151
template <size_t I>
152
auto& GetNonSyncingReference() {
153
return mozilla::Get<I>(mFields);
154
}
155
156
FieldStorage() = default;
157
explicit FieldStorage(FieldTuple&& aInit) : mFields(std::move(aInit)) {}
158
159
private:
160
template <typename Context>
161
friend class Transaction;
162
163
// Helper type for `Transaction`.
164
using MaybeFieldTuple = mozilla::Tuple<mozilla::Maybe<Ts>...>;
165
166
// Data Members
167
std::array<uint64_t, sizeof...(Ts)> mEpochs{};
168
FieldTuple mFields;
169
};
170
171
#define MOZ_DECL_SYNCED_CONTEXT_FIELD_TYPE(name, type) , type
172
#define MOZ_DECL_SYNCED_CONTEXT_FIELD_INDEX(name, type) IDX_##name,
173
#define MOZ_DECL_SYNCED_CONTEXT_FIELD_GETSET(name, type) \
174
const type& Get##name() const { return mFields.template Get<IDX_##name>(); } \
175
\
176
template <typename U> \
177
void Set##name(U&& aValue) { \
178
Transaction txn; \
179
txn.template Set<IDX_##name>(std::forward<U>(aValue)); \
180
txn.Commit(this); \
181
}
182
#define MOZ_DECL_SYNCED_CONTEXT_TRANSACTION_SET(name, type) \
183
template <typename U> \
184
void Set##name(U&& aValue) { \
185
this->template Set<IDX_##name>(std::forward<U>(aValue)); \
186
}
187
#define MOZ_DECL_SYNCED_CONTEXT_INDEX_TO_NAME(name, type) \
188
case IDX_##name: \
189
return #name;
190
191
// Declare a type as a synced context type.
192
//
193
// clazz is the name of the type being declared, and `eachfield` is a macro
194
// which, when called with the name of the macro, will call that macro once for
195
// each field in the synced context.
196
#define MOZ_DECL_SYNCED_CONTEXT(clazz, eachfield) \
197
protected: \
198
friend class ::mozilla::dom::syncedcontext::Transaction<clazz>; \
199
enum FieldIndexes { eachfield(MOZ_DECL_SYNCED_CONTEXT_FIELD_INDEX) }; \
200
using FieldStorage = \
201
typename ::mozilla::dom::syncedcontext::FieldStorage<void eachfield( \
202
MOZ_DECL_SYNCED_CONTEXT_FIELD_TYPE)>; \
203
FieldStorage mFields; \
204
\
205
public: \
206
/* Helper for overloading methods like `CanSet` and `DidSet` */ \
207
template <size_t I> \
208
using FieldIndex = typename ::mozilla::dom::syncedcontext::Index<I>; \
209
\
210
/* Field tuple type for use by initializers */ \
211
using FieldTuple = typename FieldStorage::FieldTuple; \
212
\
213
/* Transaction types for bulk mutations */ \
214
using BaseTransaction = ::mozilla::dom::syncedcontext::Transaction<clazz>; \
215
class Transaction final : public BaseTransaction { \
216
public: \
217
eachfield(MOZ_DECL_SYNCED_CONTEXT_TRANSACTION_SET) \
218
}; \
219
\
220
/* Field name getter by field index */ \
221
static const char* FieldIndexToName(size_t aIndex) { \
222
switch (aIndex) { eachfield(MOZ_DECL_SYNCED_CONTEXT_INDEX_TO_NAME) } \
223
return "<unknown>"; \
224
} \
225
eachfield(MOZ_DECL_SYNCED_CONTEXT_FIELD_GETSET)
226
227
} // namespace syncedcontext
228
} // namespace dom
229
230
namespace ipc {
231
232
template <typename Context>
233
struct IPDLParamTraits<dom::syncedcontext::Transaction<Context>> {
234
typedef dom::syncedcontext::Transaction<Context> paramType;
235
236
static void Write(IPC::Message* aMsg, IProtocol* aActor,
237
const paramType& aParam) {
238
aParam.Write(aMsg, aActor);
239
}
240
241
static bool Read(const IPC::Message* aMsg, PickleIterator* aIter,
242
IProtocol* aActor, paramType* aResult) {
243
return aResult->Read(aMsg, aIter, aActor);
244
}
245
};
246
247
} // namespace ipc
248
} // namespace mozilla
249
250
#endif // !defined(mozilla_dom_SyncedContext_h)