Merge mozilla-central to autoland. CLOSED TREE
[gecko.git] / xpcom / io / nsSegmentedBuffer.cpp
blob01d075368a02e722ad90e16f60ab86d1ced9917a
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"
8 #include "nsNetCID.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;
19 return NS_OK;
22 char* nsSegmentedBuffer::AppendNewSegment() {
23 if (!mSegmentArray) {
24 uint32_t bytes = mSegmentArrayCount * sizeof(char*);
25 mSegmentArray = (char**)moz_xmalloc(bytes);
26 memset(mSegmentArray, 0, bytes);
29 if (IsFull()) {
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()) {
34 return nullptr;
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*));
47 } else {
48 memset(&mSegmentArray[mLastSegmentIndex], 0,
49 (newArraySize.value() - mLastSegmentIndex) * sizeof(char*));
51 mSegmentArrayCount = newArraySize.value();
54 char* seg = (char*)malloc(mSegmentSize);
55 if (!seg) {
56 return nullptr;
58 mSegmentArray[mLastSegmentIndex] = seg;
59 mLastSegmentIndex = ModSegArraySize(mLastSegmentIndex + 1);
60 return seg;
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;
71 return true;
72 } else {
73 mFirstSegmentIndex = ModSegArraySize(mFirstSegmentIndex + 1);
74 return false;
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);
91 if (newSegment) {
92 mSegmentArray[last] = newSegment;
93 return true;
95 return false;
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) {
107 return;
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]);
118 free(segmentArray);
122 void nsSegmentedBuffer::FreeOMT(void* aPtr) {
123 FreeOMT([aPtr]() { free(aPtr); });
126 void nsSegmentedBuffer::FreeOMT(std::function<void()>&& aTask) {
127 if (!NS_IsMainThread()) {
128 aTask();
129 return;
132 if (mFreeOMT) {
133 // There is a runnable pending which will handle this object
134 if (mFreeOMT->AddTask(std::move(aTask)) > 1) {
135 return;
137 } else {
138 mFreeOMT = mozilla::MakeRefPtr<FreeOMTPointers>();
139 mFreeOMT->AddTask(std::move(aTask));
142 if (!mIOThread) {
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(); })))) {
151 mFreeOMT->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);
162 }();
164 // Finally run all the tasks to free memory.
165 for (auto& task : tasks) {
166 task();
170 ////////////////////////////////////////////////////////////////////////////////