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"
10 #include "nsServiceManagerUtils.h"
11 #include "nsThreadUtils.h"
12 #include "mozilla/ScopeExit.h"
14 nsresult
nsSegmentedBuffer::Init(uint32_t aSegmentSize
, uint32_t aMaxSize
) {
15 if (mSegmentArrayCount
!= 0) {
16 return NS_ERROR_FAILURE
; // initialized more than once
18 mSegmentSize
= aSegmentSize
;
21 mSegmentArrayCount
= 2;
23 mSegmentArrayCount
= NS_SEGMENTARRAY_INITIAL_COUNT
;
28 char* nsSegmentedBuffer::AppendNewSegment() {
29 if (GetSize() >= mMaxSize
) {
34 uint32_t bytes
= mSegmentArrayCount
* sizeof(char*);
35 mSegmentArray
= (char**)moz_xmalloc(bytes
);
36 memset(mSegmentArray
, 0, bytes
);
40 uint32_t newArraySize
= mSegmentArrayCount
* 2;
41 uint32_t bytes
= newArraySize
* sizeof(char*);
42 mSegmentArray
= (char**)moz_xrealloc(mSegmentArray
, bytes
);
43 // copy wrapped content to new extension
44 if (mFirstSegmentIndex
> mLastSegmentIndex
) {
45 // deal with wrap around case
46 memcpy(&mSegmentArray
[mSegmentArrayCount
], mSegmentArray
,
47 mLastSegmentIndex
* sizeof(char*));
48 memset(mSegmentArray
, 0, mLastSegmentIndex
* sizeof(char*));
49 mLastSegmentIndex
+= mSegmentArrayCount
;
50 memset(&mSegmentArray
[mLastSegmentIndex
], 0,
51 (newArraySize
- mLastSegmentIndex
) * sizeof(char*));
53 memset(&mSegmentArray
[mLastSegmentIndex
], 0,
54 (newArraySize
- mLastSegmentIndex
) * sizeof(char*));
56 mSegmentArrayCount
= newArraySize
;
59 char* seg
= (char*)malloc(mSegmentSize
);
63 mSegmentArray
[mLastSegmentIndex
] = seg
;
64 mLastSegmentIndex
= ModSegArraySize(mLastSegmentIndex
+ 1);
68 bool nsSegmentedBuffer::DeleteFirstSegment() {
69 NS_ASSERTION(mSegmentArray
[mFirstSegmentIndex
] != nullptr,
70 "deleting bad segment");
71 FreeOMT(mSegmentArray
[mFirstSegmentIndex
]);
72 mSegmentArray
[mFirstSegmentIndex
] = nullptr;
73 int32_t last
= ModSegArraySize(mLastSegmentIndex
- 1);
74 if (mFirstSegmentIndex
== last
) {
75 mLastSegmentIndex
= last
;
78 mFirstSegmentIndex
= ModSegArraySize(mFirstSegmentIndex
+ 1);
83 bool nsSegmentedBuffer::DeleteLastSegment() {
84 int32_t last
= ModSegArraySize(mLastSegmentIndex
- 1);
85 NS_ASSERTION(mSegmentArray
[last
] != nullptr, "deleting bad segment");
86 FreeOMT(mSegmentArray
[last
]);
87 mSegmentArray
[last
] = nullptr;
88 mLastSegmentIndex
= last
;
89 return (bool)(mLastSegmentIndex
== mFirstSegmentIndex
);
92 bool nsSegmentedBuffer::ReallocLastSegment(size_t aNewSize
) {
93 int32_t last
= ModSegArraySize(mLastSegmentIndex
- 1);
94 NS_ASSERTION(mSegmentArray
[last
] != nullptr, "realloc'ing bad segment");
95 char* newSegment
= (char*)realloc(mSegmentArray
[last
], aNewSize
);
97 mSegmentArray
[last
] = newSegment
;
103 void nsSegmentedBuffer::Empty() {
104 auto clearMembers
= mozilla::MakeScopeExit([&] {
105 mSegmentArray
= nullptr;
106 mSegmentArrayCount
= NS_SEGMENTARRAY_INITIAL_COUNT
;
107 mFirstSegmentIndex
= mLastSegmentIndex
= 0;
110 // If mSegmentArray is null, there's no need to actually free anything
111 if (!mSegmentArray
) {
115 // Dispatch a task that frees up the array. This may run immediately or on
116 // a background thread.
117 FreeOMT([segmentArray
= mSegmentArray
, arrayCount
= mSegmentArrayCount
]() {
118 for (uint32_t i
= 0; i
< arrayCount
; i
++) {
119 if (segmentArray
[i
]) {
120 free(segmentArray
[i
]);
127 void nsSegmentedBuffer::FreeOMT(void* aPtr
) {
128 FreeOMT([aPtr
]() { free(aPtr
); });
131 void nsSegmentedBuffer::FreeOMT(std::function
<void()>&& aTask
) {
132 if (!NS_IsMainThread()) {
138 // There is a runnable pending which will handle this object
139 if (mFreeOMT
->AddTask(std::move(aTask
)) > 1) {
143 mFreeOMT
= MakeRefPtr
<FreeOMTPointers
>();
144 mFreeOMT
->AddTask(std::move(aTask
));
148 mIOThread
= do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID
);
151 // During the shutdown we are not able to obtain the IOThread and/or the
152 // dispatching of runnable fails.
153 if (!mIOThread
|| NS_FAILED(mIOThread
->Dispatch(NS_NewRunnableFunction(
154 "nsSegmentedBuffer::FreeOMT",
155 [obj
= mFreeOMT
]() { obj
->FreeAll(); })))) {
160 void nsSegmentedBuffer::FreeOMTPointers::FreeAll() {
161 // Take all the tasks from the object. If AddTask is called after we release
162 // the lock, then another runnable will be dispatched for that task. This is
163 // necessary to avoid blocking the main thread while memory is being freed.
164 nsTArray
<std::function
<void()>> tasks
= [this]() {
165 auto t
= mTasks
.Lock();
166 return std::move(*t
);
169 // Finally run all the tasks to free memory.
170 for (auto& task
: tasks
) {
175 ////////////////////////////////////////////////////////////////////////////////