Table Of Contents

Linear Pool

The concepts

Designed for performance

Linear Pool is designed to boost performance under fully concurrent GC.

Allocating an object from linear pool is very fast, most of time it just adjusts a bump pointer or so. Once an object is created from a linear pool, it is a managed object the same as others.

Objects allocated from linear pool are automatically set as non-RC, thus there is no RC operation or cost associated with it when assign references between objects in heap.

Unused linear pool object are reclaimed by garbage collection.

A pool for a mission

A Linear Pool itself is a managed object in heap. You can create as many linear pools as you need. Each pool can be dedicated to a specific mission, or thread(s), or whatever suits your design.

Thread safe for objects created from pool

Objects created from linear pool, the same as others, can be thread-safely assigned to smart pointers or other references. For example, a reference can be shared among multiple threads. The system maintains reference counting of linear pool objects in a multi-threading safe way.

Objects are discarded as a group as pool block

A linear pool is consisted of a linked list of pool blocks, from which objects are allocated. Each pool block is a managed object also. Pool block usually has a fixed size, e.g. a typical value of 64K bytes of memory.

When a pool block is empty (there is no effective references to it), the pool block is discard with all objects in it, very efficient comparing to deallocating each objects in it.

In addition, you can explicitly evacuate all live objects out of a specific linear pool. After evacuation, all pool blocks of the pool are eligible for discard.

Safe evacuation with concurrent threads

Evacuation involves moving objects and updating references (pointing to the objects) with new memory addresses. Meanwhile, other threads are running concurrently.

To safely evacuate a linear pool, no threads are allowed to work on objects of the linear pool during evacuation operation. Holding member references to objects of the pool is allowed, as well as working on objects of other linear pools. Member references to these objects will be updated. If there are caches on these member references, these caches should be flushed.

Linear Pool Stack

For the sake of convenience, the system maintains a per-thread stack of linear pools. As stacked linear pools are always there, object allocation from a default per-thread linear pool is simpler than from specified linear pool. We will demo it in the following sections.

Create Objective-C objects

Using Objective-C methods

A set of Objective-C methods have been declared to facilitate the process.

@interface NSObject (HXPool)
+ (id)poolAlloc;
+ (id)poolAllocWithPool:(id)pool;
+ (id)poolNew;
- (id)poolInit;
@end

These methods are available for all NSObject subclasses. All of these methods return unretained reference (__unsafe_unretained in ARC term) to an object newly created from linear pool. It is safe to use this return value shortly before the containing linear pool is released or drained. If the object need to survive longer, you should retain it or assign it to an effective references, e.g. an instance variable of an object. Otherwise the object will be discarded along with its containing linear pool blocks.

The -[poolInit] method is by default implemented as return [self init];. If the behaviour of -[init] method is not compatible with -[poolInit], which returns unretained object, then you may need to re-implement the -[poolInit] to do the similar instance initialization works for instances from linear pool. For example, if -[init] released the original self, and return another retained object, then you may need to redesign it for -[poolInit].

@implementation Foo
- (id)poolInit { ... do something similar in -[init] ... }
@end

int main() {
    Foo * foo = [[Foo poolAlloc] poolInit];

    // `foo' is a weak reference, but it is safe to use it because we
    //  hold the stacked linear pool.
    ...

    // retain `foo' so that linear pool can be drain
    [fooB retain];

    // discard all empty pool blocks except the one `foo' resides.
    HXLinearPoolStack_drainTop(LPD_FullDrain);

    // it is safe to use `foo' after linear pool drain
    ...
}

Using C functions

For performance consideration, alternatively you can use C functions to create Objective-C objects from linear pool. This way is faster than using -[poolAlloc] described above.

@implementation Foo
+ (id)fooFromLinearPool {
    Foo * foo = HXLinearPool_createInstanceOfClass(self);

    // The contents of object `foo' is already zero-filled except
    // extra bytes, do additional instance initialization here
    // if necessary.
    ...

    return foo;
}
@end

Create C++ objects

C++ helper macro pool_gcnew and others are provided to create objects in linear pool. The grammar is similar to gcnew.

class Foo {
public:
    HNXGC_ENABLE(Foo)
    HNXGC_ENABLE_POOL           // enable allocation from linear pool
    ...
};

// create an instance of GC class from default pool
gcptr<Foo>::weak p = pool_gcnew Foo;

// create a double from default pool with initial value
gcptr<double>::weak p = pool_gcnew_(double) (3.1415);

// create an object with extra bytes and initialization parameters
gcptr<CDataHead>::weak pData =
    pool_gcnew_ex(pool, CDataHead, 64) (param1, param2);