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"
13 #include "PoisonIOInterposer.h"
17 #ifdef MOZ_REPLACE_MALLOC
18 # include "replace_malloc_bridge.h"
21 // Auxiliary method to convert file descriptors to ids
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
);
33 inline mozilla::Maybe
<intptr_t> FileDescriptorToHandle(int aFd
) {
34 return mozilla::Some
<intptr_t>(aFd
);
36 #endif /* if not XP_WIN */
40 class DebugFilesAutoLock final
{
42 static PRLock
* getDebugFileIDsLock() {
43 static PRLock
* sLock
= PR_NewLock();
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
62 template <typename T
, size_t chunk_size
= 64>
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) {}
75 mozilla::Atomic
<size_t> mLength
;
78 ChunkedList() : mLength(0) {}
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.
93 ListChunk
* list
= &mList
;
94 size_t position
= mLength
;
95 for (; position
>= ListChunk::kLength
; position
-= ListChunk::kLength
) {
97 list
->mNext
.reset(new ListChunk());
99 list
= list
->mNext
.get();
101 // Use an order of operations that ensures any racing Contains call
103 list
->mElements
[position
] = aValue
;
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
) {
114 ListChunk
* list
= &mList
;
115 size_t last
= mLength
- 1;
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
129 T value
= last_list
->mElements
[last
];
130 list
->mElements
[position
] = value
;
135 last
-= ListChunk::kLength
;
136 list
= list
->mNext
.get();
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
;
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
]) {
156 length
-= ListChunk::kLength
;
157 list
= list
->mNext
.get();
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
;
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
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()) {
199 MozillaRegisterDebugHandle(handle
.value());
202 void MozillaRegisterDebugFILE(FILE* aFile
) {
203 int fd
= fileno(aFile
);
204 if (fd
== 1 || fd
== 2) {
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()) {
222 MozillaUnRegisterDebugHandle(handle
.value());
225 void MozillaUnRegisterDebugFILE(FILE* aFile
) {
226 int fd
= fileno(aFile
);
227 if (fd
== 1 || fd
== 2) {
231 MozillaUnRegisterDebugFD(fd
);
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
);