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 "nsSegmentedBuffer.h"
9 #include "nsServiceManagerUtils.h"
10 #include "nsThreadUtils.h"
11 #include "mozilla/ScopeExit.h"
13 nsresult
nsSegmentedBuffer::Init(uint32_t aSegmentSize
) {
14 if (mSegmentArrayCount
!= 0) {
15 return NS_ERROR_FAILURE
; // initialized more than once
17 mSegmentSize
= aSegmentSize
;
18 mSegmentArrayCount
= NS_SEGMENTARRAY_INITIAL_COUNT
;
22 char* nsSegmentedBuffer::AppendNewSegment() {
24 uint32_t bytes
= mSegmentArrayCount
* sizeof(char*);
25 mSegmentArray
= (char**)moz_xmalloc(bytes
);
26 memset(mSegmentArray
, 0, bytes
);
30 mozilla::CheckedInt
<uint32_t> newArraySize
=
31 mozilla::CheckedInt
<uint32_t>(mSegmentArrayCount
) * 2;
32 mozilla::CheckedInt
<uint32_t> bytes
= newArraySize
* sizeof(char*);
33 if (!bytes
.isValid()) {
37 mSegmentArray
= (char**)moz_xrealloc(mSegmentArray
, bytes
.value());
38 // copy wrapped content to new extension
39 if (mFirstSegmentIndex
> mLastSegmentIndex
) {
40 // deal with wrap around case
41 memcpy(&mSegmentArray
[mSegmentArrayCount
], mSegmentArray
,
42 mLastSegmentIndex
* sizeof(char*));
43 memset(mSegmentArray
, 0, mLastSegmentIndex
* sizeof(char*));
44 mLastSegmentIndex
+= mSegmentArrayCount
;
45 memset(&mSegmentArray
[mLastSegmentIndex
], 0,
46 (newArraySize
.value() - mLastSegmentIndex
) * sizeof(char*));
48 memset(&mSegmentArray
[mLastSegmentIndex
], 0,
49 (newArraySize
.value() - mLastSegmentIndex
) * sizeof(char*));
51 mSegmentArrayCount
= newArraySize
.value();
54 char* seg
= (char*)malloc(mSegmentSize
);
58 mSegmentArray
[mLastSegmentIndex
] = seg
;
59 mLastSegmentIndex
= ModSegArraySize(mLastSegmentIndex
+ 1);
63 bool nsSegmentedBuffer::DeleteFirstSegment() {
64 NS_ASSERTION(mSegmentArray
[mFirstSegmentIndex
] != nullptr,
65 "deleting bad segment");
66 FreeOMT(mSegmentArray
[mFirstSegmentIndex
]);
67 mSegmentArray
[mFirstSegmentIndex
] = nullptr;
68 int32_t last
= ModSegArraySize(mLastSegmentIndex
- 1);
69 if (mFirstSegmentIndex
== last
) {
70 mLastSegmentIndex
= last
;
73 mFirstSegmentIndex
= ModSegArraySize(mFirstSegmentIndex
+ 1);
78 bool nsSegmentedBuffer::DeleteLastSegment() {
79 int32_t last
= ModSegArraySize(mLastSegmentIndex
- 1);
80 NS_ASSERTION(mSegmentArray
[last
] != nullptr, "deleting bad segment");
81 FreeOMT(mSegmentArray
[last
]);
82 mSegmentArray
[last
] = nullptr;
83 mLastSegmentIndex
= last
;
84 return (bool)(mLastSegmentIndex
== mFirstSegmentIndex
);
87 bool nsSegmentedBuffer::ReallocLastSegment(size_t aNewSize
) {
88 int32_t last
= ModSegArraySize(mLastSegmentIndex
- 1);
89 NS_ASSERTION(mSegmentArray
[last
] != nullptr, "realloc'ing bad segment");
90 char* newSegment
= (char*)realloc(mSegmentArray
[last
], aNewSize
);
92 mSegmentArray
[last
] = newSegment
;
98 void nsSegmentedBuffer::Empty() {
99 auto clearMembers
= mozilla::MakeScopeExit([&] {
100 mSegmentArray
= nullptr;
101 mSegmentArrayCount
= NS_SEGMENTARRAY_INITIAL_COUNT
;
102 mFirstSegmentIndex
= mLastSegmentIndex
= 0;
105 // If mSegmentArray is null, there's no need to actually free anything
106 if (!mSegmentArray
) {
110 // Dispatch a task that frees up the array. This may run immediately or on
111 // a background thread.
112 FreeOMT([segmentArray
= mSegmentArray
, arrayCount
= mSegmentArrayCount
]() {
113 for (uint32_t i
= 0; i
< arrayCount
; i
++) {
114 if (segmentArray
[i
]) {
115 free(segmentArray
[i
]);
122 void nsSegmentedBuffer::FreeOMT(void* aPtr
) {
123 FreeOMT([aPtr
]() { free(aPtr
); });
126 void nsSegmentedBuffer::FreeOMT(std::function
<void()>&& aTask
) {
127 if (!NS_IsMainThread()) {
133 // There is a runnable pending which will handle this object
134 if (mFreeOMT
->AddTask(std::move(aTask
)) > 1) {
138 mFreeOMT
= mozilla::MakeRefPtr
<FreeOMTPointers
>();
139 mFreeOMT
->AddTask(std::move(aTask
));
143 mIOThread
= do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID
);
146 // During the shutdown we are not able to obtain the IOThread and/or the
147 // dispatching of runnable fails.
148 if (!mIOThread
|| NS_FAILED(mIOThread
->Dispatch(NS_NewRunnableFunction(
149 "nsSegmentedBuffer::FreeOMT",
150 [obj
= mFreeOMT
]() { obj
->FreeAll(); })))) {
155 void nsSegmentedBuffer::FreeOMTPointers::FreeAll() {
156 // Take all the tasks from the object. If AddTask is called after we release
157 // the lock, then another runnable will be dispatched for that task. This is
158 // necessary to avoid blocking the main thread while memory is being freed.
159 nsTArray
<std::function
<void()>> tasks
= [this]() {
160 auto t
= mTasks
.Lock();
161 return std::move(*t
);
164 // Finally run all the tasks to free memory.
165 for (auto& task
: tasks
) {
170 ////////////////////////////////////////////////////////////////////////////////