Note: Only the rc and so regions are currently supported.

To allocate a new value, we ask a region to get it done:

imm nbrref = &rc imm 5  // the 'rc' region counts references

There are many kinds of regions, each using a different strategy for allocating memory and then later safely reclaiming that memory after an object has no references that point to it. Variations in their strategies can deliver noticeably different behavior with regard to:

Cone offers a wide selection of regions because no one strategy handles all these criteria well. This power of choice ensures each allocated object can select the optimal region that best serves the way the program needs that object to behave.

This page does not list every possible region that can be used by a Cone program, as new ones can be defined within the language itself. Instead, this page summarizes the five distinct, commonly-used region strategies used to allocate new objects pointed at by an owning reference. The focus is to describe the mechanism used by each of these region strategies and assess how they compare on the above criteria. In reality, each of these region strategies may be implemented by multiple concrete regions that vary somewhat in how they work (e.g., &rc might only work within a single thread, whereas &arc might allow multiple threads to share references to the same object).

rc - Reference Counting

As the name suggests, the rc region keeps a running counter of the number of references to each allocated object. When the object is first allocated, its counter starts at 1. Every time a copy is made of an rc owning reference, the counter is incremented. Whenever an owning reference goes out of scope, the counter is decremented. When the counter reaches 0, the object is reclaimed.

rc is a good choice if you are looking for:

The biggest shortcomings are:

To prevent cycle-based memory leaks, one can use weak references to make any cycle breakable. A weak reference may be obtained from any owning reference:

childnode.parent = parentnoderef.weak

gc - Tracing Garbage Collection

The gc region captures metadata about every object it allocates, including detailed information about its references to other allocated objects. Periodically, runtime logic associated with the gc region will trace out all references reachable from a root set of objects (including execution stacks). Any allocated value that cannot be traced from the root is considered unreachable, and therefore is safe to automatically finalize and free.

gc is a good choice if you are looking for:

The biggest shortcomings are:

so - Single Owner

The single-owner (so) region is unusual and possibly unfamiliar (as only a few languages support it). Its mechanism, strengths and weaknesses derive from a very simple constraint: there is only ever one owning reference to an object it allocates. That reference may be passed around from function to function or from thread to thread, but it does so using move mechanics to ensure no copy of the reference is ever made. Because there is only one reference to the object, the memory for that value can be automatically finalized and freed when the reference expires.

so is a good choice if you are looking for:

The key shortcomings are:

Note: An 'own' owning reference only accepts the 'uni' or 'imm' permissions.

ar - Arena

The ar region begins life pre-allocating a large (and growable) region of memory Every new allocation takes a bite out of this arena using a fast bump pointer. Allocations are never individually freed. The entire arena is freed as a single event when the program ends.

ar is a good choice if you are looking for:

The biggest shortcomings are:

The ar region is a surprisingly good fit for a limited-lifetime program that allocates a lot of small objects but wants optimal performance. This would apply well to many command-line tools, including compilers.

Pool

The mechanism for pools derives from this constraint: all objects in a pool have the same type and size. A pool region begins with a pre-allocated (and growable) memory area that is logically partitioned into identically-sized slots. Allocating space for a new object is efficient: it either reuses a slot found on a linked-list of free slots, or else uses a bump pointer to carve out a new slot from the unused area.

A pool region must be created before using it to allocate values:

mut eventPool RcPool<Event>

This example creates a new region called eventPool. All its allocated object have the type Event (which might be an enum type). This pool is an RcPool, which means references to pool-allocated objects are reference-counted, thereby determining when to free the object. An SoPool would use single-owner references, and so on.

Once a pool region is defined, the region may be specified to allocate a new value:

imm newevent = &eventPool mut Event::QuitEvent[]

Pools are a good choice if you are looking for:

The biggest shortcoming is:

When a program has a large number of objects of the same type being regularly allocated and freed, as is true in many games, pools can be very useful strategy. This is particularly true when functions process all objects sequentially in a cache-friendly way.

_