6351640285
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
100 lines
2.6 KiB
C++
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
|