Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/netwerk/cache2/CacheFileChunk.cpp
Line
Count
Source (jump to first uncovered line)
1
/* This Source Code Form is subject to the terms of the Mozilla Public
2
 * License, v. 2.0. If a copy of the MPL was not distributed with this
3
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5
#include "CacheLog.h"
6
#include "CacheFileChunk.h"
7
8
#include "CacheFile.h"
9
#include "nsThreadUtils.h"
10
11
#include "mozilla/IntegerPrintfMacros.h"
12
13
namespace mozilla {
14
namespace net {
15
16
0
#define kMinBufSize        512
17
18
CacheFileChunkBuffer::CacheFileChunkBuffer(CacheFileChunk *aChunk)
19
  : mChunk(aChunk)
20
  , mBuf(nullptr)
21
  , mBufSize(0)
22
  , mDataSize(0)
23
  , mReadHandlesCount(0)
24
  , mWriteHandleExists(false)
25
0
{
26
0
}
27
28
CacheFileChunkBuffer::~CacheFileChunkBuffer()
29
0
{
30
0
  if (mBuf) {
31
0
    CacheFileUtils::FreeBuffer(mBuf);
32
0
    mBuf = nullptr;
33
0
    mChunk->BuffersAllocationChanged(mBufSize, 0);
34
0
    mBufSize = 0;
35
0
  }
36
0
}
37
38
void
39
CacheFileChunkBuffer::CopyFrom(CacheFileChunkBuffer *aOther)
40
0
{
41
0
  MOZ_RELEASE_ASSERT(mBufSize >= aOther->mDataSize);
42
0
  mDataSize = aOther->mDataSize;
43
0
  memcpy(mBuf, aOther->mBuf, mDataSize);
44
0
}
45
46
nsresult
47
CacheFileChunkBuffer::FillInvalidRanges(CacheFileChunkBuffer *aOther,
48
                                        CacheFileUtils::ValidityMap *aMap)
49
0
{
50
0
  nsresult rv;
51
0
52
0
  rv = EnsureBufSize(aOther->mDataSize);
53
0
  if (NS_FAILED(rv)) {
54
0
    return rv;
55
0
  }
56
0
57
0
  uint32_t invalidOffset = 0;
58
0
  uint32_t invalidLength;
59
0
60
0
  for (uint32_t i = 0; i < aMap->Length(); ++i) {
61
0
    uint32_t validOffset = (*aMap)[i].Offset();
62
0
    uint32_t validLength = (*aMap)[i].Len();
63
0
64
0
    MOZ_RELEASE_ASSERT(invalidOffset <= validOffset);
65
0
    invalidLength = validOffset - invalidOffset;
66
0
    if (invalidLength > 0) {
67
0
      MOZ_RELEASE_ASSERT(invalidOffset + invalidLength <= aOther->mDataSize);
68
0
      memcpy(mBuf + invalidOffset, aOther->mBuf + invalidOffset, invalidLength);
69
0
    }
70
0
    invalidOffset = validOffset + validLength;
71
0
  }
72
0
73
0
  if (invalidOffset < aOther->mDataSize) {
74
0
    invalidLength = aOther->mDataSize - invalidOffset;
75
0
    memcpy(mBuf + invalidOffset, aOther->mBuf + invalidOffset, invalidLength);
76
0
  }
77
0
78
0
  return NS_OK;
79
0
}
80
81
MOZ_MUST_USE nsresult
82
CacheFileChunkBuffer::EnsureBufSize(uint32_t aBufSize)
83
0
{
84
0
  AssertOwnsLock();
85
0
86
0
  if (mBufSize >= aBufSize) {
87
0
    return NS_OK;
88
0
  }
89
0
90
0
  // find smallest power of 2 greater than or equal to aBufSize
91
0
  aBufSize--;
92
0
  aBufSize |= aBufSize >> 1;
93
0
  aBufSize |= aBufSize >> 2;
94
0
  aBufSize |= aBufSize >> 4;
95
0
  aBufSize |= aBufSize >> 8;
96
0
  aBufSize |= aBufSize >> 16;
97
0
  aBufSize++;
98
0
99
0
  const uint32_t minBufSize = kMinBufSize;
100
0
  const uint32_t maxBufSize = kChunkSize;
101
0
  aBufSize = clamped(aBufSize, minBufSize, maxBufSize);
102
0
103
0
  if (!mChunk->CanAllocate(aBufSize - mBufSize)) {
104
0
    return NS_ERROR_OUT_OF_MEMORY;
105
0
  }
106
0
107
0
  char *newBuf = static_cast<char *>(realloc(mBuf, aBufSize));
108
0
  if (!newBuf) {
109
0
    return NS_ERROR_OUT_OF_MEMORY;
110
0
  }
111
0
112
0
  mChunk->BuffersAllocationChanged(mBufSize, aBufSize);
113
0
  mBuf = newBuf;
114
0
  mBufSize = aBufSize;
115
0
116
0
  return NS_OK;
117
0
}
118
119
void
120
CacheFileChunkBuffer::SetDataSize(uint32_t aDataSize)
121
0
{
122
0
  MOZ_RELEASE_ASSERT(
123
0
    // EnsureBufSize must be called before SetDataSize, so the new data size
124
0
    // is guaranteed to be smaller than or equal to mBufSize.
125
0
    aDataSize <= mBufSize ||
126
0
    // The only exception is an optimization when we read the data from the
127
0
    // disk. The data is read to a separate buffer and CacheFileChunk::mBuf is
128
0
    // empty (see CacheFileChunk::Read). We need to set mBuf::mDataSize
129
0
    // accordingly so that DataSize() methods return correct value, but we don't
130
0
    // want to allocate the buffer since it wouldn't be used in most cases.
131
0
    (mBufSize == 0 && mChunk->mState == CacheFileChunk::READING));
132
0
133
0
  mDataSize = aDataSize;
134
0
}
135
136
void
137
CacheFileChunkBuffer::AssertOwnsLock() const
138
0
{
139
0
  mChunk->AssertOwnsLock();
140
0
}
141
142
void
143
CacheFileChunkBuffer::RemoveReadHandle()
144
0
{
145
0
  AssertOwnsLock();
146
0
  MOZ_RELEASE_ASSERT(mReadHandlesCount);
147
0
  MOZ_RELEASE_ASSERT(!mWriteHandleExists);
148
0
  mReadHandlesCount--;
149
0
150
0
  if (mReadHandlesCount == 0 && mChunk->mBuf != this) {
151
0
    DebugOnly<bool> removed = mChunk->mOldBufs.RemoveElement(this);
152
0
    MOZ_ASSERT(removed);
153
0
  }
154
0
}
155
156
void
157
CacheFileChunkBuffer::RemoveWriteHandle()
158
0
{
159
0
  AssertOwnsLock();
160
0
  MOZ_RELEASE_ASSERT(mReadHandlesCount == 0);
161
0
  MOZ_RELEASE_ASSERT(mWriteHandleExists);
162
0
  mWriteHandleExists = false;
163
0
}
164
165
size_t
166
CacheFileChunkBuffer::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const
167
0
{
168
0
  size_t n = mallocSizeOf(this);
169
0
170
0
  if (mBuf) {
171
0
    n += mallocSizeOf(mBuf);
172
0
  }
173
0
174
0
  return n;
175
0
}
176
177
uint32_t
178
CacheFileChunkHandle::DataSize()
179
0
{
180
0
  MOZ_ASSERT(mBuf, "Unexpected call on dummy handle");
181
0
  mBuf->AssertOwnsLock();
182
0
  return mBuf->mDataSize;
183
0
}
184
185
uint32_t
186
CacheFileChunkHandle::Offset()
187
0
{
188
0
  MOZ_ASSERT(mBuf, "Unexpected call on dummy handle");
189
0
  mBuf->AssertOwnsLock();
190
0
  return mBuf->mChunk->Index() * kChunkSize;
191
0
}
192
193
CacheFileChunkReadHandle::CacheFileChunkReadHandle(CacheFileChunkBuffer *aBuf)
194
0
{
195
0
  mBuf = aBuf;
196
0
  mBuf->mReadHandlesCount++;
197
0
}
198
199
CacheFileChunkReadHandle::~CacheFileChunkReadHandle()
200
0
{
201
0
  mBuf->RemoveReadHandle();
202
0
}
203
204
const char *
205
CacheFileChunkReadHandle::Buf()
206
0
{
207
0
  return mBuf->mBuf;
208
0
}
209
210
CacheFileChunkWriteHandle::CacheFileChunkWriteHandle(CacheFileChunkBuffer *aBuf)
211
0
{
212
0
  mBuf = aBuf;
213
0
  if (mBuf) {
214
0
    MOZ_ASSERT(!mBuf->mWriteHandleExists);
215
0
    mBuf->mWriteHandleExists = true;
216
0
  }
217
0
}
218
219
CacheFileChunkWriteHandle::~CacheFileChunkWriteHandle()
220
0
{
221
0
  if (mBuf) {
222
0
    mBuf->RemoveWriteHandle();
223
0
  }
224
0
}
225
226
char *
227
CacheFileChunkWriteHandle::Buf()
228
0
{
229
0
  return mBuf ? mBuf->mBuf : nullptr;
230
0
}
231
232
void
233
CacheFileChunkWriteHandle::UpdateDataSize(uint32_t aOffset, uint32_t aLen)
234
0
{
235
0
  MOZ_ASSERT(mBuf, "Write performed on dummy handle?");
236
0
  MOZ_ASSERT(aOffset <= mBuf->mDataSize);
237
0
  MOZ_ASSERT(aOffset + aLen <= mBuf->mBufSize);
238
0
239
0
  if (aOffset + aLen > mBuf->mDataSize) {
240
0
    mBuf->mDataSize = aOffset + aLen;
241
0
  }
242
0
243
0
  mBuf->mChunk->UpdateDataSize(aOffset, aLen);
244
0
}
245
246
247
class NotifyUpdateListenerEvent : public Runnable {
248
public:
249
  NotifyUpdateListenerEvent(CacheFileChunkListener* aCallback,
250
                            CacheFileChunk* aChunk)
251
    : Runnable("net::NotifyUpdateListenerEvent")
252
    , mCallback(aCallback)
253
    , mChunk(aChunk)
254
0
  {
255
0
    LOG(("NotifyUpdateListenerEvent::NotifyUpdateListenerEvent() [this=%p]",
256
0
         this));
257
0
  }
258
259
protected:
260
  ~NotifyUpdateListenerEvent()
261
0
  {
262
0
    LOG(("NotifyUpdateListenerEvent::~NotifyUpdateListenerEvent() [this=%p]",
263
0
         this));
264
0
  }
265
266
public:
267
  NS_IMETHOD Run() override
268
0
  {
269
0
    LOG(("NotifyUpdateListenerEvent::Run() [this=%p]", this));
270
0
271
0
    mCallback->OnChunkUpdated(mChunk);
272
0
    return NS_OK;
273
0
  }
274
275
protected:
276
  nsCOMPtr<CacheFileChunkListener> mCallback;
277
  RefPtr<CacheFileChunk>           mChunk;
278
};
279
280
bool
281
CacheFileChunk::DispatchRelease()
282
0
{
283
0
  if (NS_IsMainThread()) {
284
0
    return false;
285
0
  }
286
0
287
0
  NS_DispatchToMainThread(NewNonOwningRunnableMethod(
288
0
    "net::CacheFileChunk::Release", this, &CacheFileChunk::Release));
289
0
290
0
  return true;
291
0
}
292
293
NS_IMPL_ADDREF(CacheFileChunk)
294
NS_IMETHODIMP_(MozExternalRefCountType)
295
CacheFileChunk::Release()
296
0
{
297
0
  nsrefcnt count = mRefCnt - 1;
298
0
  if (DispatchRelease()) {
299
0
    // Redispatched to the main thread.
300
0
    return count;
301
0
  }
302
0
303
0
  MOZ_ASSERT(0 != mRefCnt, "dup release");
304
0
  count = --mRefCnt;
305
0
  NS_LOG_RELEASE(this, count, "CacheFileChunk");
306
0
307
0
  if (0 == count) {
308
0
    mRefCnt = 1;
309
0
    delete (this);
310
0
    return 0;
311
0
  }
312
0
313
0
  // We can safely access this chunk after decreasing mRefCnt since we re-post
314
0
  // all calls to Release() happening off the main thread to the main thread.
315
0
  // I.e. no other Release() that would delete the object could be run before
316
0
  // we call CacheFile::DeactivateChunk().
317
0
  //
318
0
  // NOTE: we don't grab the CacheFile's lock, so the chunk might be addrefed
319
0
  // on another thread before CacheFile::DeactivateChunk() grabs the lock on
320
0
  // this thread. To make sure we won't deactivate chunk that was just returned
321
0
  // to a new consumer we check mRefCnt once again in
322
0
  // CacheFile::DeactivateChunk() after we grab the lock.
323
0
  if (mActiveChunk && count == 1) {
324
0
    mFile->DeactivateChunk(this);
325
0
  }
326
0
327
0
  return count;
328
0
}
329
330
0
NS_INTERFACE_MAP_BEGIN(CacheFileChunk)
331
0
  NS_INTERFACE_MAP_ENTRY(mozilla::net::CacheFileIOListener)
332
0
  NS_INTERFACE_MAP_ENTRY(nsISupports)
333
0
NS_INTERFACE_MAP_END
334
335
CacheFileChunk::CacheFileChunk(CacheFile *aFile, uint32_t aIndex,
336
                               bool aInitByWriter)
337
  : CacheMemoryConsumer(aFile->mOpenAsMemoryOnly ? MEMORY_ONLY : DONT_REPORT)
338
  , mIndex(aIndex)
339
  , mState(INITIAL)
340
  , mStatus(NS_OK)
341
  , mActiveChunk(false)
342
  , mIsDirty(false)
343
  , mDiscardedChunk(false)
344
  , mBuffersSize(0)
345
  , mLimitAllocation(!aFile->mOpenAsMemoryOnly && aInitByWriter)
346
  , mIsPriority(aFile->mPriority)
347
  , mExpectedHash(0)
348
  , mFile(aFile)
349
0
{
350
0
  LOG(("CacheFileChunk::CacheFileChunk() [this=%p, index=%u, initByWriter=%d]",
351
0
       this, aIndex, aInitByWriter));
352
0
  mBuf = new CacheFileChunkBuffer(this);
353
0
}
354
355
CacheFileChunk::~CacheFileChunk()
356
0
{
357
0
  LOG(("CacheFileChunk::~CacheFileChunk() [this=%p]", this));
358
0
}
359
360
void
361
CacheFileChunk::AssertOwnsLock() const
362
0
{
363
0
  mFile->AssertOwnsLock();
364
0
}
365
366
void
367
CacheFileChunk::InitNew()
368
0
{
369
0
  AssertOwnsLock();
370
0
371
0
  LOG(("CacheFileChunk::InitNew() [this=%p]", this));
372
0
373
0
  MOZ_ASSERT(mState == INITIAL);
374
0
  MOZ_ASSERT(NS_SUCCEEDED(mStatus));
375
0
  MOZ_ASSERT(!mBuf->Buf());
376
0
  MOZ_ASSERT(!mWritingStateHandle);
377
0
  MOZ_ASSERT(!mReadingStateBuf);
378
0
  MOZ_ASSERT(!mIsDirty);
379
0
380
0
  mBuf = new CacheFileChunkBuffer(this);
381
0
  mState = READY;
382
0
}
383
384
nsresult
385
CacheFileChunk::Read(CacheFileHandle *aHandle, uint32_t aLen,
386
                     CacheHash::Hash16_t aHash,
387
                     CacheFileChunkListener *aCallback)
388
0
{
389
0
  AssertOwnsLock();
390
0
391
0
  LOG(("CacheFileChunk::Read() [this=%p, handle=%p, len=%d, listener=%p]",
392
0
       this, aHandle, aLen, aCallback));
393
0
394
0
  MOZ_ASSERT(mState == INITIAL);
395
0
  MOZ_ASSERT(NS_SUCCEEDED(mStatus));
396
0
  MOZ_ASSERT(!mBuf->Buf());
397
0
  MOZ_ASSERT(!mWritingStateHandle);
398
0
  MOZ_ASSERT(!mReadingStateBuf);
399
0
  MOZ_ASSERT(aLen);
400
0
401
0
  nsresult rv;
402
0
403
0
  mState = READING;
404
0
405
0
  RefPtr<CacheFileChunkBuffer> tmpBuf = new CacheFileChunkBuffer(this);
406
0
  rv = tmpBuf->EnsureBufSize(aLen);
407
0
  if (NS_FAILED(rv)) {
408
0
    SetError(rv);
409
0
    return mStatus;
410
0
  }
411
0
  tmpBuf->SetDataSize(aLen);
412
0
413
0
  rv = CacheFileIOManager::Read(aHandle, mIndex * kChunkSize,
414
0
                                tmpBuf->Buf(), aLen,
415
0
                                this);
416
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
417
0
    rv = mIndex ? NS_ERROR_FILE_CORRUPTED : NS_ERROR_FILE_NOT_FOUND;
418
0
    SetError(rv);
419
0
  } else {
420
0
    mReadingStateBuf.swap(tmpBuf);
421
0
    mListener = aCallback;
422
0
    // mBuf contains no data but we set datasize to size of the data that will
423
0
    // be read from the disk. No handle is allowed to access the non-existent
424
0
    // data until reading finishes, but data can be appended or overwritten.
425
0
    // These pieces are tracked in mValidityMap and will be merged with the data
426
0
    // read from disk in OnDataRead().
427
0
    mBuf->SetDataSize(aLen);
428
0
    mExpectedHash = aHash;
429
0
  }
430
0
431
0
  return rv;
432
0
}
433
434
nsresult
435
CacheFileChunk::Write(CacheFileHandle *aHandle,
436
                      CacheFileChunkListener *aCallback)
437
0
{
438
0
  AssertOwnsLock();
439
0
440
0
  LOG(("CacheFileChunk::Write() [this=%p, handle=%p, listener=%p]",
441
0
       this, aHandle, aCallback));
442
0
443
0
  MOZ_ASSERT(mState == READY);
444
0
  MOZ_ASSERT(NS_SUCCEEDED(mStatus));
445
0
  MOZ_ASSERT(!mWritingStateHandle);
446
0
  MOZ_ASSERT(mBuf->DataSize()); // Don't write chunk when it is empty
447
0
  MOZ_ASSERT(mBuf->ReadHandlesCount() == 0);
448
0
  MOZ_ASSERT(!mBuf->WriteHandleExists());
449
0
450
0
  nsresult rv;
451
0
452
0
  mState = WRITING;
453
0
  mWritingStateHandle = new CacheFileChunkReadHandle(mBuf);
454
0
455
0
  rv = CacheFileIOManager::Write(aHandle, mIndex * kChunkSize,
456
0
                                 mWritingStateHandle->Buf(),
457
0
                                 mWritingStateHandle->DataSize(),
458
0
                                 false, false, this);
459
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
460
0
    mWritingStateHandle = nullptr;
461
0
    SetError(rv);
462
0
  } else {
463
0
    mListener = aCallback;
464
0
    mIsDirty = false;
465
0
  }
466
0
467
0
  return rv;
468
0
}
469
470
void
471
CacheFileChunk::WaitForUpdate(CacheFileChunkListener *aCallback)
472
0
{
473
0
  AssertOwnsLock();
474
0
475
0
  LOG(("CacheFileChunk::WaitForUpdate() [this=%p, listener=%p]",
476
0
       this, aCallback));
477
0
478
0
  MOZ_ASSERT(mFile->mOutput);
479
0
  MOZ_ASSERT(IsReady());
480
0
481
#ifdef DEBUG
482
  for (uint32_t i = 0 ; i < mUpdateListeners.Length() ; i++) {
483
    MOZ_ASSERT(mUpdateListeners[i]->mCallback != aCallback);
484
  }
485
#endif
486
487
0
  ChunkListenerItem *item = new ChunkListenerItem();
488
0
  item->mTarget = CacheFileIOManager::IOTarget();
489
0
  if (!item->mTarget) {
490
0
    LOG(("CacheFileChunk::WaitForUpdate() - Cannot get Cache I/O thread! Using "
491
0
         "main thread for callback."));
492
0
    item->mTarget = GetMainThreadEventTarget();
493
0
  }
494
0
  item->mCallback = aCallback;
495
0
  MOZ_ASSERT(item->mTarget);
496
0
  item->mCallback = aCallback;
497
0
498
0
  mUpdateListeners.AppendElement(item);
499
0
}
500
501
nsresult
502
CacheFileChunk::CancelWait(CacheFileChunkListener *aCallback)
503
0
{
504
0
  AssertOwnsLock();
505
0
506
0
  LOG(("CacheFileChunk::CancelWait() [this=%p, listener=%p]", this, aCallback));
507
0
508
0
  MOZ_ASSERT(IsReady());
509
0
510
0
  uint32_t i;
511
0
  for (i = 0 ; i < mUpdateListeners.Length() ; i++) {
512
0
    ChunkListenerItem *item = mUpdateListeners[i];
513
0
514
0
    if (item->mCallback == aCallback) {
515
0
      mUpdateListeners.RemoveElementAt(i);
516
0
      delete item;
517
0
      break;
518
0
    }
519
0
  }
520
0
521
#ifdef DEBUG
522
  for ( ; i < mUpdateListeners.Length() ; i++) {
523
    MOZ_ASSERT(mUpdateListeners[i]->mCallback != aCallback);
524
  }
525
#endif
526
527
0
  return NS_OK;
528
0
}
529
530
nsresult
531
CacheFileChunk::NotifyUpdateListeners()
532
0
{
533
0
  AssertOwnsLock();
534
0
535
0
  LOG(("CacheFileChunk::NotifyUpdateListeners() [this=%p]", this));
536
0
537
0
  MOZ_ASSERT(IsReady());
538
0
539
0
  nsresult rv, rv2;
540
0
541
0
  rv = NS_OK;
542
0
  for (uint32_t i = 0 ; i < mUpdateListeners.Length() ; i++) {
543
0
    ChunkListenerItem *item = mUpdateListeners[i];
544
0
545
0
    LOG(("CacheFileChunk::NotifyUpdateListeners() - Notifying listener %p "
546
0
         "[this=%p]", item->mCallback.get(), this));
547
0
548
0
    RefPtr<NotifyUpdateListenerEvent> ev;
549
0
    ev = new NotifyUpdateListenerEvent(item->mCallback, this);
550
0
    rv2 = item->mTarget->Dispatch(ev, NS_DISPATCH_NORMAL);
551
0
    if (NS_FAILED(rv2) && NS_SUCCEEDED(rv))
552
0
      rv = rv2;
553
0
    delete item;
554
0
  }
555
0
556
0
  mUpdateListeners.Clear();
557
0
558
0
  return rv;
559
0
}
560
561
uint32_t
562
CacheFileChunk::Index() const
563
0
{
564
0
  return mIndex;
565
0
}
566
567
CacheHash::Hash16_t
568
CacheFileChunk::Hash() const
569
0
{
570
0
  MOZ_ASSERT(IsReady());
571
0
572
0
  return CacheHash::Hash16(mBuf->Buf(), mBuf->DataSize());
573
0
}
574
575
uint32_t
576
CacheFileChunk::DataSize() const
577
0
{
578
0
  return mBuf->DataSize();
579
0
}
580
581
void
582
CacheFileChunk::UpdateDataSize(uint32_t aOffset, uint32_t aLen)
583
0
{
584
0
  AssertOwnsLock();
585
0
586
0
  // UpdateDataSize() is called only when we've written some data to the chunk
587
0
  // and we never write data anymore once some error occurs.
588
0
  MOZ_ASSERT(NS_SUCCEEDED(mStatus));
589
0
590
0
  LOG(("CacheFileChunk::UpdateDataSize() [this=%p, offset=%d, len=%d]",
591
0
       this, aOffset, aLen));
592
0
593
0
  mIsDirty = true;
594
0
595
0
  int64_t fileSize = static_cast<int64_t>(kChunkSize) * mIndex + aOffset + aLen;
596
0
  bool notify = false;
597
0
598
0
  if (fileSize > mFile->mDataSize) {
599
0
    mFile->mDataSize = fileSize;
600
0
    notify = true;
601
0
  }
602
0
603
0
  if (mState == READY || mState == WRITING) {
604
0
    MOZ_ASSERT(mValidityMap.Length() == 0);
605
0
606
0
    if (notify) {
607
0
      NotifyUpdateListeners();
608
0
    }
609
0
610
0
    return;
611
0
  }
612
0
613
0
  // We're still waiting for data from the disk. This chunk cannot be used by
614
0
  // input stream, so there must be no update listener. We also need to keep
615
0
  // track of where the data is written so that we can correctly merge the new
616
0
  // data with the old one.
617
0
618
0
  MOZ_ASSERT(mUpdateListeners.Length() == 0);
619
0
  MOZ_ASSERT(mState == READING);
620
0
621
0
  mValidityMap.AddPair(aOffset, aLen);
622
0
  mValidityMap.Log();
623
0
}
624
625
nsresult
626
CacheFileChunk::Truncate(uint32_t aOffset)
627
0
{
628
0
  MOZ_RELEASE_ASSERT(mState == READY || mState == WRITING || mState == READING);
629
0
630
0
  if (mState == READING) {
631
0
    mIsDirty = true;
632
0
  }
633
0
634
0
  mBuf->SetDataSize(aOffset);
635
0
  return NS_OK;
636
0
}
637
638
nsresult
639
CacheFileChunk::OnFileOpened(CacheFileHandle *aHandle, nsresult aResult)
640
0
{
641
0
  MOZ_CRASH("CacheFileChunk::OnFileOpened should not be called!");
642
0
  return NS_ERROR_UNEXPECTED;
643
0
}
644
645
nsresult
646
CacheFileChunk::OnDataWritten(CacheFileHandle *aHandle, const char *aBuf,
647
                              nsresult aResult)
648
0
{
649
0
  LOG(("CacheFileChunk::OnDataWritten() [this=%p, handle=%p, result=0x%08" PRIx32 "]",
650
0
       this, aHandle, static_cast<uint32_t>(aResult)));
651
0
652
0
  nsCOMPtr<CacheFileChunkListener> listener;
653
0
654
0
  {
655
0
    CacheFileAutoLock lock(mFile);
656
0
657
0
    MOZ_ASSERT(mState == WRITING);
658
0
    MOZ_ASSERT(mListener);
659
0
660
0
    mWritingStateHandle = nullptr;
661
0
662
0
    if (NS_WARN_IF(NS_FAILED(aResult))) {
663
0
      SetError(aResult);
664
0
    }
665
0
666
0
    mState = READY;
667
0
    mListener.swap(listener);
668
0
  }
669
0
670
0
  listener->OnChunkWritten(aResult, this);
671
0
672
0
  return NS_OK;
673
0
}
674
675
nsresult
676
CacheFileChunk::OnDataRead(CacheFileHandle *aHandle, char *aBuf,
677
                           nsresult aResult)
678
0
{
679
0
  LOG(("CacheFileChunk::OnDataRead() [this=%p, handle=%p, result=0x%08" PRIx32 "]",
680
0
       this, aHandle, static_cast<uint32_t>(aResult)));
681
0
682
0
  nsCOMPtr<CacheFileChunkListener> listener;
683
0
684
0
  {
685
0
    CacheFileAutoLock lock(mFile);
686
0
687
0
    MOZ_ASSERT(mState == READING);
688
0
    MOZ_ASSERT(mListener);
689
0
    MOZ_ASSERT(mReadingStateBuf);
690
0
    MOZ_RELEASE_ASSERT(mBuf->ReadHandlesCount() == 0);
691
0
    MOZ_RELEASE_ASSERT(!mBuf->WriteHandleExists());
692
0
693
0
    RefPtr<CacheFileChunkBuffer> tmpBuf;
694
0
    tmpBuf.swap(mReadingStateBuf);
695
0
696
0
    if (NS_SUCCEEDED(aResult)) {
697
0
      CacheHash::Hash16_t hash = CacheHash::Hash16(tmpBuf->Buf(),
698
0
                                                   tmpBuf->DataSize());
699
0
      if (hash != mExpectedHash) {
700
0
        LOG(("CacheFileChunk::OnDataRead() - Hash mismatch! Hash of the data is"
701
0
             " %hx, hash in metadata is %hx. [this=%p, idx=%d]",
702
0
             hash, mExpectedHash, this, mIndex));
703
0
        aResult = NS_ERROR_FILE_CORRUPTED;
704
0
      } else {
705
0
        if (mBuf->DataSize() < tmpBuf->DataSize()) {
706
0
          // Truncate() was called while the data was being read.
707
0
          tmpBuf->SetDataSize(mBuf->DataSize());
708
0
        }
709
0
710
0
        if (!mBuf->Buf()) {
711
0
          // Just swap the buffers if mBuf is still empty
712
0
          mBuf.swap(tmpBuf);
713
0
        } else {
714
0
          LOG(("CacheFileChunk::OnDataRead() - Merging buffers. [this=%p]",
715
0
               this));
716
0
717
0
          mValidityMap.Log();
718
0
          aResult = mBuf->FillInvalidRanges(tmpBuf, &mValidityMap);
719
0
          mValidityMap.Clear();
720
0
        }
721
0
      }
722
0
    }
723
0
724
0
    if (NS_FAILED(aResult)) {
725
0
      aResult = mIndex ? NS_ERROR_FILE_CORRUPTED : NS_ERROR_FILE_NOT_FOUND;
726
0
      SetError(aResult);
727
0
      mBuf->SetDataSize(0);
728
0
    }
729
0
730
0
    mState = READY;
731
0
    mListener.swap(listener);
732
0
  }
733
0
734
0
  listener->OnChunkRead(aResult, this);
735
0
736
0
  return NS_OK;
737
0
}
738
739
nsresult
740
CacheFileChunk::OnFileDoomed(CacheFileHandle *aHandle, nsresult aResult)
741
0
{
742
0
  MOZ_CRASH("CacheFileChunk::OnFileDoomed should not be called!");
743
0
  return NS_ERROR_UNEXPECTED;
744
0
}
745
746
nsresult
747
CacheFileChunk::OnEOFSet(CacheFileHandle *aHandle, nsresult aResult)
748
0
{
749
0
  MOZ_CRASH("CacheFileChunk::OnEOFSet should not be called!");
750
0
  return NS_ERROR_UNEXPECTED;
751
0
}
752
753
nsresult
754
CacheFileChunk::OnFileRenamed(CacheFileHandle *aHandle, nsresult aResult)
755
0
{
756
0
  MOZ_CRASH("CacheFileChunk::OnFileRenamed should not be called!");
757
0
  return NS_ERROR_UNEXPECTED;
758
0
}
759
760
bool
761
CacheFileChunk::IsKilled()
762
0
{
763
0
  return mFile->IsKilled();
764
0
}
765
766
bool
767
CacheFileChunk::IsReady() const
768
0
{
769
0
  return (NS_SUCCEEDED(mStatus) && (mState == READY || mState == WRITING));
770
0
}
771
772
bool
773
CacheFileChunk::IsDirty() const
774
0
{
775
0
  AssertOwnsLock();
776
0
777
0
  return mIsDirty;
778
0
}
779
780
nsresult
781
CacheFileChunk::GetStatus()
782
0
{
783
0
  return mStatus;
784
0
}
785
786
void
787
CacheFileChunk::SetError(nsresult aStatus)
788
0
{
789
0
  LOG(("CacheFileChunk::SetError() [this=%p, status=0x%08" PRIx32 "]",
790
0
       this, static_cast<uint32_t>(aStatus)));
791
0
792
0
  MOZ_ASSERT(NS_FAILED(aStatus));
793
0
794
0
  if (NS_FAILED(mStatus)) {
795
0
    // Remember only the first error code.
796
0
    return;
797
0
  }
798
0
799
0
  mStatus = aStatus;
800
0
}
801
802
CacheFileChunkReadHandle
803
CacheFileChunk::GetReadHandle()
804
0
{
805
0
  LOG(("CacheFileChunk::GetReadHandle() [this=%p]", this));
806
0
807
0
  AssertOwnsLock();
808
0
809
0
  MOZ_RELEASE_ASSERT(mState == READY || mState == WRITING);
810
0
  // We don't release the lock when writing the data and CacheFileOutputStream
811
0
  // doesn't get the read handle, so there cannot be a write handle when read
812
0
  // handle is obtained.
813
0
  MOZ_RELEASE_ASSERT(!mBuf->WriteHandleExists());
814
0
815
0
  return CacheFileChunkReadHandle(mBuf);
816
0
}
817
818
CacheFileChunkWriteHandle
819
CacheFileChunk::GetWriteHandle(uint32_t aEnsuredBufSize)
820
0
{
821
0
  LOG(("CacheFileChunk::GetWriteHandle() [this=%p, ensuredBufSize=%u]",
822
0
       this, aEnsuredBufSize));
823
0
824
0
  AssertOwnsLock();
825
0
826
0
  if (NS_FAILED(mStatus)) {
827
0
    return CacheFileChunkWriteHandle(nullptr); // dummy handle
828
0
  }
829
0
830
0
  nsresult rv;
831
0
832
0
  // We don't support multiple write handles
833
0
  MOZ_RELEASE_ASSERT(!mBuf->WriteHandleExists());
834
0
835
0
  if (mBuf->ReadHandlesCount()) {
836
0
    LOG(("CacheFileChunk::GetWriteHandle() - cloning buffer because of existing"
837
0
         " read handle"));
838
0
839
0
    MOZ_RELEASE_ASSERT(mState != READING);
840
0
    RefPtr<CacheFileChunkBuffer> newBuf = new CacheFileChunkBuffer(this);
841
0
    rv = newBuf->EnsureBufSize(std::max(aEnsuredBufSize, mBuf->DataSize()));
842
0
    if (NS_SUCCEEDED(rv)) {
843
0
      newBuf->CopyFrom(mBuf);
844
0
      mOldBufs.AppendElement(mBuf);
845
0
      mBuf = newBuf;
846
0
    }
847
0
  } else {
848
0
    rv = mBuf->EnsureBufSize(aEnsuredBufSize);
849
0
  }
850
0
851
0
  if (NS_FAILED(rv)) {
852
0
    SetError(NS_ERROR_OUT_OF_MEMORY);
853
0
    return CacheFileChunkWriteHandle(nullptr); // dummy handle
854
0
  }
855
0
856
0
  return CacheFileChunkWriteHandle(mBuf);
857
0
}
858
859
// Memory reporting
860
861
size_t
862
CacheFileChunk::SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const
863
0
{
864
0
  size_t n = mBuf->SizeOfIncludingThis(mallocSizeOf);
865
0
866
0
  if (mReadingStateBuf) {
867
0
    n += mReadingStateBuf->SizeOfIncludingThis(mallocSizeOf);
868
0
  }
869
0
870
0
  for (uint32_t i = 0; i < mOldBufs.Length(); ++i) {
871
0
    n += mOldBufs[i]->SizeOfIncludingThis(mallocSizeOf);
872
0
  }
873
0
874
0
  n += mValidityMap.SizeOfExcludingThis(mallocSizeOf);
875
0
876
0
  return n;
877
0
}
878
879
size_t
880
CacheFileChunk::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const
881
0
{
882
0
  return mallocSizeOf(this) + SizeOfExcludingThis(mallocSizeOf);
883
0
}
884
885
bool
886
CacheFileChunk::CanAllocate(uint32_t aSize) const
887
0
{
888
0
  if (!mLimitAllocation) {
889
0
    return true;
890
0
  }
891
0
892
0
  LOG(("CacheFileChunk::CanAllocate() [this=%p, size=%u]", this, aSize));
893
0
894
0
  uint32_t limit = CacheObserver::MaxDiskChunksMemoryUsage(mIsPriority);
895
0
  if (limit == 0) {
896
0
    return true;
897
0
  }
898
0
899
0
  uint32_t usage = ChunksMemoryUsage();
900
0
  if (usage + aSize > limit) {
901
0
    LOG(("CacheFileChunk::CanAllocate() - Returning false. [this=%p]", this));
902
0
    return false;
903
0
  }
904
0
905
0
  return true;
906
0
}
907
908
void
909
CacheFileChunk::BuffersAllocationChanged(uint32_t aFreed, uint32_t aAllocated)
910
0
{
911
0
  uint32_t oldBuffersSize = mBuffersSize;
912
0
  mBuffersSize += aAllocated;
913
0
  mBuffersSize -= aFreed;
914
0
915
0
  DoMemoryReport(sizeof(CacheFileChunk) + mBuffersSize);
916
0
917
0
  if (!mLimitAllocation) {
918
0
    return;
919
0
  }
920
0
921
0
  ChunksMemoryUsage() -= oldBuffersSize;
922
0
  ChunksMemoryUsage() += mBuffersSize;
923
0
  LOG(("CacheFileChunk::BuffersAllocationChanged() - %s chunks usage %u "
924
0
       "[this=%p]", mIsPriority ? "Priority" : "Normal",
925
0
       static_cast<uint32_t>(ChunksMemoryUsage()), this));
926
0
}
927
928
mozilla::Atomic<uint32_t, ReleaseAcquire>& CacheFileChunk::ChunksMemoryUsage() const
929
0
{
930
0
  static mozilla::Atomic<uint32_t, ReleaseAcquire> chunksMemoryUsage(0);
931
0
  static mozilla::Atomic<uint32_t, ReleaseAcquire> prioChunksMemoryUsage(0);
932
0
  return mIsPriority ? prioChunksMemoryUsage : chunksMemoryUsage;
933
0
}
934
935
} // namespace net
936
} // namespace mozilla