skia2/include/core/SkRWBuffer.h
scroggo 6351640285 Fixes for SkRWBuffer
Do not call SkBufferHead::validate in SkROBuffer's destructor, which
may be called in a separate thread from SkRWBuffer::append. validate()
reads SkBufferBlock::fUsed, and append() writes to it, resulting in
a data race.

Update some comments to be more clear about how it is safe to use
these classes across threads.

Test the readers in separate threads.

In addition, make sure it is safe to create a reader even when no
data has been appended. Add tests for this case.

Mark a parameter to SkBufferHead::validate() as const, reflecting
its use.

BUG=chromium:601578
BUG=chromium:605479

GOLD_TRYBOT_URL= https://gold.skia.org/search2?unt=true&query=source_type%3Dgm&master=false&issue=1871953002

Review URL: https://codereview.chromium.org/1871953002
2016-04-22 06:59:01 -07:00

100 lines
2.6 KiB
C++

/*
* Copyright 2015 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef SkRWBuffer_DEFINED
#define SkRWBuffer_DEFINED
#include "SkRefCnt.h"
struct SkBufferBlock;
struct SkBufferHead;
class SkRWBuffer;
class SkStreamAsset;
/**
* Contains a read-only, thread-sharable block of memory. To access the memory, the caller must
* instantiate a local iterator, as the memory is stored in 1 or more contiguous blocks.
*/
class SK_API SkROBuffer : public SkRefCnt {
public:
/**
* Return the logical length of the data owned/shared by this buffer. It may be stored in
* multiple contiguous blocks, accessible via the iterator.
*/
size_t size() const { return fAvailable; }
class SK_API Iter {
public:
Iter(const SkROBuffer*);
void reset(const SkROBuffer*);
/**
* Return the current continuous block of memory, or nullptr if the iterator is exhausted
*/
const void* data() const;
/**
* Returns the number of bytes in the current continguous block of memory, or 0 if the
* iterator is exhausted.
*/
size_t size() const;
/**
* Advance to the next contiguous block of memory, returning true if there is another
* block, or false if the iterator is exhausted.
*/
bool next();
private:
const SkBufferBlock* fBlock;
size_t fRemaining;
const SkROBuffer* fBuffer;
};
private:
SkROBuffer(const SkBufferHead* head, size_t available, const SkBufferBlock* fTail);
virtual ~SkROBuffer();
const SkBufferHead* fHead;
const size_t fAvailable;
const SkBufferBlock* fTail;
friend class SkRWBuffer;
};
/**
* Accumulates bytes of memory that are "appended" to it, growing internal storage as needed.
* The growth is done such that at any time in the writer's thread, an RBuffer or StreamAsset
* can be snapped off (and safely passed to another thread). The RBuffer/StreamAsset snapshot
* can see the previously stored bytes, but will be unaware of any future writes.
*/
class SK_API SkRWBuffer {
public:
SkRWBuffer(size_t initialCapacity = 0);
~SkRWBuffer();
size_t size() const { return fTotalUsed; }
void append(const void* buffer, size_t length);
SkROBuffer* newRBufferSnapshot() const;
SkStreamAsset* newStreamSnapshot() const;
#ifdef SK_DEBUG
void validate() const;
#else
void validate() const {}
#endif
private:
SkBufferHead* fHead;
SkBufferBlock* fTail;
size_t fTotalUsed;
};
#endif