mirror of
https://github.com/dart-lang/sdk
synced 2024-11-02 10:49:00 +00:00
d856e058ea
fibers. TEST=ci Change-Id: I1308b2450d77ac83c89208acb7d18ad1be5bdcbf Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/243856 Reviewed-by: Ryan Macnak <rmacnak@google.com> Commit-Queue: Siva Annamalai <asiva@google.com>
333 lines
10 KiB
C++
333 lines
10 KiB
C++
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
|
|
// for details. All rights reserved. Use of this source code is governed by a
|
|
// BSD-style license that can be found in the LICENSE file.
|
|
|
|
#ifndef RUNTIME_VM_ZONE_H_
|
|
#define RUNTIME_VM_ZONE_H_
|
|
|
|
#include "platform/utils.h"
|
|
#include "vm/allocation.h"
|
|
#include "vm/handles.h"
|
|
#include "vm/memory_region.h"
|
|
#include "vm/thread_state.h"
|
|
|
|
namespace dart {
|
|
|
|
// Zones support very fast allocation of small chunks of memory. The
|
|
// chunks cannot be deallocated individually, but instead zones
|
|
// support deallocating all chunks in one fast operation.
|
|
|
|
class Zone {
|
|
public:
|
|
// Allocate an array sized to hold 'len' elements of type
|
|
// 'ElementType'. Checks for integer overflow when performing the
|
|
// size computation.
|
|
template <class ElementType>
|
|
inline ElementType* Alloc(intptr_t len);
|
|
|
|
// Allocates an array sized to hold 'len' elements of type
|
|
// 'ElementType'. The new array is initialized from the memory of
|
|
// 'old_array' up to 'old_len'.
|
|
template <class ElementType>
|
|
inline ElementType* Realloc(ElementType* old_array,
|
|
intptr_t old_len,
|
|
intptr_t new_len);
|
|
|
|
// Allocates 'size' bytes of memory in the zone; expands the zone by
|
|
// allocating new segments of memory on demand using 'new'.
|
|
//
|
|
// It is preferred to use Alloc<T>() instead, as that function can
|
|
// check for integer overflow. If you use AllocUnsafe, you are
|
|
// responsible for avoiding integer overflow yourself.
|
|
inline uword AllocUnsafe(intptr_t size);
|
|
|
|
// Make a copy of the string in the zone allocated area.
|
|
char* MakeCopyOfString(const char* str);
|
|
|
|
// Make a copy of the first n characters of a string in the zone
|
|
// allocated area.
|
|
char* MakeCopyOfStringN(const char* str, intptr_t len);
|
|
|
|
// Concatenate strings |a| and |b|. |a| may be NULL. If |a| is not NULL,
|
|
// |join| will be inserted between |a| and |b|.
|
|
char* ConcatStrings(const char* a, const char* b, char join = ',');
|
|
|
|
// TODO(zra): Remove these calls and replace them with calls to OS::SCreate
|
|
// and OS::VSCreate.
|
|
// These calls are deprecated. Do not add further calls to these functions.
|
|
// instead use OS::SCreate and OS::VSCreate.
|
|
// Make a zone-allocated string based on printf format and args.
|
|
char* PrintToString(const char* format, ...) PRINTF_ATTRIBUTE(2, 3);
|
|
char* VPrint(const char* format, va_list args);
|
|
|
|
// Compute the total size of allocations in this zone.
|
|
uintptr_t SizeInBytes() const;
|
|
|
|
// Computes the amount of space used by the zone.
|
|
uintptr_t CapacityInBytes() const;
|
|
|
|
// Dump the current allocated sizes in the zone object.
|
|
void Print() const;
|
|
|
|
// Structure for managing handles allocation.
|
|
VMHandles* handles() { return &handles_; }
|
|
|
|
void VisitObjectPointers(ObjectPointerVisitor* visitor);
|
|
|
|
Zone* previous() const { return previous_; }
|
|
|
|
bool ContainsNestedZone(Zone* other) const {
|
|
while (other != NULL) {
|
|
if (this == other) return true;
|
|
other = other->previous_;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// All pointers returned from AllocateUnsafe() and New() have this alignment.
|
|
static const intptr_t kAlignment = kDoubleSize;
|
|
|
|
static void Init();
|
|
static void Cleanup();
|
|
|
|
static void ClearCache();
|
|
static intptr_t Size() { return total_size_; }
|
|
|
|
private:
|
|
Zone();
|
|
~Zone(); // Delete all memory associated with the zone.
|
|
|
|
// Default initial chunk size.
|
|
static const intptr_t kInitialChunkSize = 128;
|
|
|
|
// Default segment size.
|
|
static const intptr_t kSegmentSize = 64 * KB;
|
|
|
|
// Zap value used to indicate deleted zone area (debug purposes).
|
|
static const unsigned char kZapDeletedByte = 0x42;
|
|
|
|
// Zap value used to indicate uninitialized zone area (debug purposes).
|
|
static const unsigned char kZapUninitializedByte = 0xab;
|
|
|
|
// Total size of current zone segments.
|
|
static RelaxedAtomic<intptr_t> total_size_;
|
|
|
|
// Expand the zone to accommodate an allocation of 'size' bytes.
|
|
uword AllocateExpand(intptr_t size);
|
|
|
|
// Allocate a large segment.
|
|
uword AllocateLargeSegment(intptr_t size);
|
|
|
|
// Insert zone into zone chain, after current_zone.
|
|
void Link(Zone* current_zone) { previous_ = current_zone; }
|
|
|
|
// Delete all objects and free all memory allocated in the zone.
|
|
void Reset();
|
|
|
|
// Does not actually free any memory. Enables templated containers like
|
|
// BaseGrowableArray to use different allocators.
|
|
template <class ElementType>
|
|
void Free(ElementType* old_array, intptr_t len) {
|
|
#ifdef DEBUG
|
|
if (len > 0) {
|
|
ASSERT(old_array != nullptr);
|
|
memset(static_cast<void*>(old_array), kZapUninitializedByte,
|
|
len * sizeof(ElementType));
|
|
}
|
|
#endif
|
|
}
|
|
|
|
// Overflow check (FATAL) for array length.
|
|
template <class ElementType>
|
|
static inline void CheckLength(intptr_t len);
|
|
|
|
// The free region in the current (head) segment or the initial buffer is
|
|
// represented as the half-open interval [position, limit). The 'position'
|
|
// variable is guaranteed to be aligned as dictated by kAlignment.
|
|
uword position_;
|
|
uword limit_;
|
|
|
|
// Zone segments are internal data structures used to hold information
|
|
// about the memory segmentations that constitute a zone. The entire
|
|
// implementation is in zone.cc.
|
|
class Segment;
|
|
|
|
// Total size of all allocations in this zone.
|
|
intptr_t size_ = 0;
|
|
|
|
// Total size of all segments in [head_].
|
|
intptr_t small_segment_capacity_ = 0;
|
|
|
|
// List of all segments allocated in this zone; may be NULL.
|
|
Segment* segments_;
|
|
|
|
// Used for chaining zones in order to allow unwinding of stacks.
|
|
Zone* previous_;
|
|
|
|
// Structure for managing handles allocation.
|
|
VMHandles handles_;
|
|
|
|
// This buffer is used for allocation before any segments.
|
|
// This would act as the initial stack allocated chunk so that we don't
|
|
// end up calling malloc/free on zone scopes that allocate less than
|
|
// kChunkSize
|
|
COMPILE_ASSERT(kAlignment <= 8);
|
|
ALIGN8 uint8_t buffer_[kInitialChunkSize];
|
|
|
|
friend class StackZone;
|
|
friend class ApiZone;
|
|
friend class AllocOnlyStackZone;
|
|
template <typename T, typename B, typename Allocator>
|
|
friend class BaseGrowableArray;
|
|
template <typename T, typename B, typename Allocator>
|
|
friend class BaseDirectChainedHashMap;
|
|
DISALLOW_COPY_AND_ASSIGN(Zone);
|
|
};
|
|
|
|
class StackZone : public StackResource {
|
|
public:
|
|
// Create an empty zone and set is at the current zone for the Thread.
|
|
explicit StackZone(ThreadState* thread);
|
|
|
|
// Delete all memory associated with the zone.
|
|
virtual ~StackZone();
|
|
|
|
// DART_USE_ABSL encodes the use of fibers in the Dart VM for threading,
|
|
#if defined(DART_USE_ABSL)
|
|
// Compute the total size of this zone. This includes wasted space that is
|
|
// due to internal fragmentation in the segments.
|
|
uintptr_t SizeInBytes() const { return zone_->SizeInBytes(); }
|
|
|
|
// Computes the used space in the zone.
|
|
intptr_t CapacityInBytes() const { return zone_->CapacityInBytes(); }
|
|
|
|
Zone* GetZone() { return zone_; }
|
|
#else
|
|
// Compute the total size of this zone. This includes wasted space that is
|
|
// due to internal fragmentation in the segments.
|
|
uintptr_t SizeInBytes() const {
|
|
return zone_.SizeInBytes();
|
|
}
|
|
|
|
// Computes the used space in the zone.
|
|
intptr_t CapacityInBytes() const {
|
|
return zone_.CapacityInBytes();
|
|
}
|
|
|
|
Zone* GetZone() {
|
|
return &zone_;
|
|
}
|
|
#endif // defined(DART_USE_ABSL)
|
|
|
|
private:
|
|
#if defined(DART_USE_ABSL)
|
|
// When fibers are used we have to make do with a smaller stack size and hence
|
|
// the first zone is allocated instead of being a stack resource.
|
|
Zone* zone_;
|
|
#else
|
|
// For regular configurations that have larger stack sizes it is ok to
|
|
// have the first zone be a stack resource, avoids the overhead of a malloc
|
|
// call for every stack zone creation.
|
|
Zone zone_;
|
|
#endif // defined(DART_USE_ABSL)
|
|
|
|
template <typename T>
|
|
friend class GrowableArray;
|
|
template <typename T>
|
|
friend class ZoneGrowableArray;
|
|
|
|
DISALLOW_IMPLICIT_CONSTRUCTORS(StackZone);
|
|
};
|
|
|
|
class AllocOnlyStackZone : public ValueObject {
|
|
public:
|
|
AllocOnlyStackZone() : zone_() {}
|
|
~AllocOnlyStackZone() {
|
|
// This zone is not linked into the thread, so any handles would not be
|
|
// visited.
|
|
ASSERT(zone_.handles()->IsEmpty());
|
|
}
|
|
|
|
Zone* GetZone() { return &zone_; }
|
|
|
|
private:
|
|
Zone zone_;
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(AllocOnlyStackZone);
|
|
};
|
|
|
|
inline uword Zone::AllocUnsafe(intptr_t size) {
|
|
ASSERT(size >= 0);
|
|
// Round up the requested size to fit the alignment.
|
|
if (size > (kIntptrMax - kAlignment)) {
|
|
FATAL1("Zone::Alloc: 'size' is too large: size=%" Pd "", size);
|
|
}
|
|
size = Utils::RoundUp(size, kAlignment);
|
|
|
|
// Check if the requested size is available without expanding.
|
|
uword result;
|
|
intptr_t free_size = (limit_ - position_);
|
|
if (free_size >= size) {
|
|
result = position_;
|
|
position_ += size;
|
|
size_ += size;
|
|
} else {
|
|
result = AllocateExpand(size);
|
|
}
|
|
|
|
// Check that the result has the proper alignment and return it.
|
|
ASSERT(Utils::IsAligned(result, kAlignment));
|
|
return result;
|
|
}
|
|
|
|
template <class ElementType>
|
|
inline void Zone::CheckLength(intptr_t len) {
|
|
const intptr_t kElementSize = sizeof(ElementType);
|
|
if (len > (kIntptrMax / kElementSize)) {
|
|
FATAL2("Zone::Alloc: 'len' is too large: len=%" Pd ", kElementSize=%" Pd,
|
|
len, kElementSize);
|
|
}
|
|
}
|
|
|
|
template <class ElementType>
|
|
inline ElementType* Zone::Alloc(intptr_t len) {
|
|
CheckLength<ElementType>(len);
|
|
return reinterpret_cast<ElementType*>(AllocUnsafe(len * sizeof(ElementType)));
|
|
}
|
|
|
|
template <class ElementType>
|
|
inline ElementType* Zone::Realloc(ElementType* old_data,
|
|
intptr_t old_len,
|
|
intptr_t new_len) {
|
|
CheckLength<ElementType>(new_len);
|
|
const intptr_t kElementSize = sizeof(ElementType);
|
|
if (old_data != nullptr) {
|
|
uword old_end =
|
|
reinterpret_cast<uword>(old_data) + (old_len * kElementSize);
|
|
// Resize existing allocation if nothing was allocated in between...
|
|
if (Utils::RoundUp(old_end, kAlignment) == position_) {
|
|
uword new_end =
|
|
reinterpret_cast<uword>(old_data) + (new_len * kElementSize);
|
|
// ...and there is sufficient space.
|
|
if (new_end <= limit_) {
|
|
ASSERT(new_len >= old_len);
|
|
position_ = Utils::RoundUp(new_end, kAlignment);
|
|
size_ += (new_end - old_end);
|
|
return old_data;
|
|
}
|
|
}
|
|
if (new_len <= old_len) {
|
|
return old_data;
|
|
}
|
|
}
|
|
ElementType* new_data = Alloc<ElementType>(new_len);
|
|
if (old_data != nullptr) {
|
|
memmove(reinterpret_cast<void*>(new_data),
|
|
reinterpret_cast<void*>(old_data), old_len * kElementSize);
|
|
}
|
|
return new_data;
|
|
}
|
|
|
|
} // namespace dart
|
|
|
|
#endif // RUNTIME_VM_ZONE_H_
|