Bug 1883861 - Part 1: Move visitMemoryBarrier into the common CodeGenerator file...
[gecko.git] / xpcom / build / PoisonIOInterposerBase.cpp
blob0a25a3d1f8173ddc3585dc4764e36e54425d185c
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/. */
7 #include "mozilla/Maybe.h"
8 #include "mozilla/Mutex.h"
9 #include "mozilla/UniquePtr.h"
11 #include <algorithm>
13 #include "PoisonIOInterposer.h"
15 #include "prlock.h"
17 #ifdef MOZ_REPLACE_MALLOC
18 # include "replace_malloc_bridge.h"
19 #endif
21 // Auxiliary method to convert file descriptors to ids
22 #if defined(XP_WIN)
23 # include <io.h>
24 inline mozilla::Maybe<intptr_t> FileDescriptorToHandle(int aFd) {
25 intptr_t handle = _get_osfhandle(aFd);
26 if ((handle == -1) || (handle == -2)) {
27 // -1: Invalid handle. -2: stdin/out/err not associated with a stream.
28 return mozilla::Nothing();
30 return mozilla::Some(handle);
32 #else
33 inline mozilla::Maybe<intptr_t> FileDescriptorToHandle(int aFd) {
34 return mozilla::Some<intptr_t>(aFd);
36 #endif /* if not XP_WIN */
38 namespace {
40 class DebugFilesAutoLock final {
41 public:
42 static PRLock* getDebugFileIDsLock() {
43 static PRLock* sLock = PR_NewLock();
44 return sLock;
47 DebugFilesAutoLock() { PR_Lock(getDebugFileIDsLock()); }
49 ~DebugFilesAutoLock() { PR_Unlock(getDebugFileIDsLock()); }
52 // The ChunkedList<T> class implements, at the high level, a non-iterable
53 // list of instances of T. Its goal is to be somehow minimalist for the
54 // use case of storing the debug files handles here, with the property of
55 // not requiring a lock to look up whether it contains a specific value.
56 // It is also chunked in blocks of chunk_size bytes so that its
57 // initialization doesn't require a memory allocation, while keeping the
58 // possibility to increase its size as necessary. Note that chunks are
59 // never deallocated (except in the destructor).
60 // All operations are essentially O(N) but N is not expected to be large
61 // enough to matter.
62 template <typename T, size_t chunk_size = 64>
63 class ChunkedList {
64 struct ListChunk {
65 static const size_t kLength =
66 (chunk_size - sizeof(ListChunk*)) / sizeof(mozilla::Atomic<T>);
68 mozilla::Atomic<T> mElements[kLength];
69 mozilla::UniquePtr<ListChunk> mNext;
71 ListChunk() : mNext(nullptr) {}
74 ListChunk mList;
75 mozilla::Atomic<size_t> mLength;
77 public:
78 ChunkedList() : mLength(0) {}
80 ~ChunkedList() {
81 // There can be writes happening after this destructor runs, so keep
82 // the list contents and don't reset mLength. But if there are more
83 // elements left than the first chunk can hold, then all hell breaks
84 // loose for any write that would happen after that because any extra
85 // chunk would be deallocated, so just crash in that case.
86 MOZ_RELEASE_ASSERT(mLength <= ListChunk::kLength);
89 // Add an element at the end of the last chunk of the list. Create a new
90 // chunk if there is not enough room.
91 // This is not thread-safe with another thread calling Add or Remove.
92 void Add(T aValue) {
93 ListChunk* list = &mList;
94 size_t position = mLength;
95 for (; position >= ListChunk::kLength; position -= ListChunk::kLength) {
96 if (!list->mNext) {
97 list->mNext.reset(new ListChunk());
99 list = list->mNext.get();
101 // Use an order of operations that ensures any racing Contains call
102 // can't be hurt.
103 list->mElements[position] = aValue;
104 mLength++;
107 // Remove an element from the list by replacing it with the last element
108 // of the list, and then shrinking the list.
109 // This is not thread-safe with another thread calling Add or Remove.
110 void Remove(T aValue) {
111 if (!mLength) {
112 return;
114 ListChunk* list = &mList;
115 size_t last = mLength - 1;
116 do {
117 size_t position = 0;
118 // Look for an element matching the given value.
119 for (; position < ListChunk::kLength; position++) {
120 if (aValue == list->mElements[position]) {
121 ListChunk* last_list = list;
122 // Look for the last element in the list, starting from where we are
123 // instead of starting over.
124 for (; last >= ListChunk::kLength; last -= ListChunk::kLength) {
125 last_list = last_list->mNext.get();
127 // Use an order of operations that ensures any racing Contains call
128 // can't be hurt.
129 T value = last_list->mElements[last];
130 list->mElements[position] = value;
131 mLength--;
132 return;
135 last -= ListChunk::kLength;
136 list = list->mNext.get();
137 } while (list);
140 // Returns whether the list contains the given value. It is meant to be safe
141 // to use without locking, with the tradeoff of being not entirely accurate
142 // if another thread adds or removes an element while this function runs.
143 bool Contains(T aValue) {
144 ListChunk* list = &mList;
145 // Fix the range of the lookup to whatever the list length is when the
146 // function is called.
147 size_t length = mLength;
148 do {
149 size_t list_length = ListChunk::kLength;
150 list_length = std::min(list_length, length);
151 for (size_t position = 0; position < list_length; position++) {
152 if (aValue == list->mElements[position]) {
153 return true;
156 length -= ListChunk::kLength;
157 list = list->mNext.get();
158 } while (list);
160 return false;
164 typedef ChunkedList<intptr_t> FdList;
166 // Return a list used to hold the IDs of the current debug files. On unix
167 // an ID is a file descriptor. On Windows it is a file HANDLE.
168 FdList& getDebugFileIDs() {
169 static FdList DebugFileIDs;
170 return DebugFileIDs;
173 } // namespace
175 namespace mozilla {
177 // Auxiliary Method to test if a file descriptor is registered to be ignored
178 // by the poisoning IO interposer
179 bool IsDebugFile(intptr_t aFileID) {
180 return getDebugFileIDs().Contains(aFileID);
183 } // namespace mozilla
185 extern "C" {
187 void MozillaRegisterDebugHandle(intptr_t aHandle) {
188 DebugFilesAutoLock lockedScope;
189 FdList& DebugFileIDs = getDebugFileIDs();
190 MOZ_ASSERT(!DebugFileIDs.Contains(aHandle));
191 DebugFileIDs.Add(aHandle);
194 void MozillaRegisterDebugFD(int aFd) {
195 mozilla::Maybe<intptr_t> handle = FileDescriptorToHandle(aFd);
196 if (!handle.isSome()) {
197 return;
199 MozillaRegisterDebugHandle(handle.value());
202 void MozillaRegisterDebugFILE(FILE* aFile) {
203 int fd = fileno(aFile);
204 if (fd == 1 || fd == 2) {
205 return;
207 MozillaRegisterDebugFD(fd);
210 void MozillaUnRegisterDebugHandle(intptr_t aHandle) {
211 DebugFilesAutoLock lockedScope;
212 FdList& DebugFileIDs = getDebugFileIDs();
213 MOZ_ASSERT(DebugFileIDs.Contains(aHandle));
214 DebugFileIDs.Remove(aHandle);
217 void MozillaUnRegisterDebugFD(int aFd) {
218 mozilla::Maybe<intptr_t> handle = FileDescriptorToHandle(aFd);
219 if (!handle.isSome()) {
220 return;
222 MozillaUnRegisterDebugHandle(handle.value());
225 void MozillaUnRegisterDebugFILE(FILE* aFile) {
226 int fd = fileno(aFile);
227 if (fd == 1 || fd == 2) {
228 return;
230 fflush(aFile);
231 MozillaUnRegisterDebugFD(fd);
234 } // extern "C"
236 #ifdef MOZ_REPLACE_MALLOC
237 void mozilla::DebugFdRegistry::RegisterHandle(intptr_t aHandle) {
238 MozillaRegisterDebugHandle(aHandle);
241 void mozilla::DebugFdRegistry::UnRegisterHandle(intptr_t aHandle) {
242 MozillaUnRegisterDebugHandle(aHandle);
244 #endif