root/src/system/kernel/slab/ObjectCache.h
/*
 * Copyright 2008-2010, Axel Dörfler. All Rights Reserved.
 * Copyright 2007, Hugo Santos. All Rights Reserved.
 *
 * Distributed under the terms of the MIT License.
 */
#ifndef OBJECT_CACHE_H
#define OBJECT_CACHE_H


#include <condition_variable.h>
#include <lock.h>
#include <slab/Slab.h>
#include <util/DoublyLinkedList.h>

#include "ObjectDepot.h"
#include "slab_queue.h"

#include "kernel_debug_config.h"
#include "slab_debug.h"


struct ResizeRequest;


struct slab : DoublyLinkedListLinkImpl<slab> {
        void*                   pages;
        size_t                  size;           // total number of objects
        size_t                  count;          // free objects
        size_t                  offset;
        slab_queue              free;
#if SLAB_OBJECT_CACHE_ALLOCATION_TRACKING
        AllocationTrackingInfo* tracking;
#endif
};

typedef DoublyLinkedList<slab> SlabList;

struct ObjectCacheResizeEntry {
        ConditionVariable       condition;
        thread_id                       thread;
};

struct ObjectCache : DoublyLinkedListLinkImpl<ObjectCache> {
                        char                            name[32];
                        mutex                           lock;
                        size_t                          object_size;
                        size_t                          alignment;
                        size_t                          cache_color_cycle;
                        SlabList                        empty;
                        SlabList                        partial;
                        SlabList                        full;
                        size_t                          total_objects;          // total number of objects
                        size_t                          used_count;                     // used objects
                        size_t                          empty_count;            // empty slabs
                        size_t                          pressure;
                        size_t                          min_object_reserve;
                                                                        // minimum number of free objects

                        size_t                          slab_size;
                        size_t                          usage;
                        size_t                          maximum;
                        uint32                          flags;

                        ResizeRequest*          resize_request;

                        ObjectCacheResizeEntry* resize_entry_can_wait;
                        ObjectCacheResizeEntry* resize_entry_dont_wait;

                        DoublyLinkedListLink<ObjectCache> maintenance_link;
                        bool                            maintenance_pending;
                        bool                            maintenance_in_progress;
                        bool                            maintenance_resize;
                        bool                            maintenance_delete;

                        void*                           cookie;
                        object_cache_constructor constructor;
                        object_cache_destructor destructor;
                        object_cache_reclaimer reclaimer;

                        object_depot            depot;

public:
        virtual                                         ~ObjectCache();

                        status_t                        Init(const char* name, size_t objectSize,
                                                                        size_t alignment, size_t maximum,
                                                                        size_t magazineCapacity,
                                                                        size_t maxMagazineCount, uint32 flags,
                                                                        void* cookie,
                                                                        object_cache_constructor constructor,
                                                                        object_cache_destructor destructor,
                                                                        object_cache_reclaimer reclaimer);
        virtual void                            Delete() = 0;

        virtual slab*                           CreateSlab(uint32 flags) = 0;
        virtual void                            ReturnSlab(slab* slab, uint32 flags) = 0;
        virtual slab*                           ObjectSlab(void* object) const = 0;

                        slab*                           InitSlab(slab* slab, void* pages,
                                                                        size_t byteCount, uint32 flags);
                        void                            UninitSlab(slab* slab);

                        void                            ReturnObjectToSlab(slab* source, void* object,
                                                                        uint32 flags);
                        void*                           ObjectAtIndex(slab* source, int32 index) const;

                        bool                            Lock()  { return mutex_lock(&lock) == B_OK; }
                        void                            Unlock()        { mutex_unlock(&lock); }

                        status_t                        AllocatePages(void** pages, uint32 flags);
                        void                            FreePages(void* pages);
                        status_t                        EarlyAllocatePages(void** pages, uint32 flags);
                        void                            EarlyFreePages(void* pages);

#if PARANOID_KERNEL_FREE
                        bool                            AssertObjectNotFreed(void* object);
#endif

                        status_t                        AllocateTrackingInfos(slab* slab,
                                                                        size_t byteCount, uint32 flags);
                        void                            FreeTrackingInfos(slab* slab, uint32 flags);

#if SLAB_OBJECT_CACHE_ALLOCATION_TRACKING
                        AllocationTrackingInfo*
                                                                TrackingInfoFor(void* object) const;
#endif
};


static inline void*
link_to_object(slab_queue_link* link, size_t objectSize)
{
        return ((uint8*)link) - (objectSize - sizeof(slab_queue_link));
}


static inline slab_queue_link*
object_to_link(void* object, size_t objectSize)
{
        return (slab_queue_link*)(((uint8*)object)
                + (objectSize - sizeof(slab_queue_link)));
}


static inline void*
lower_boundary(const void* object, size_t byteCount)
{
        return (void*)((addr_t)object & ~(byteCount - 1));
}


static inline bool
check_cache_quota(ObjectCache* cache)
{
        if (cache->maximum == 0)
                return true;

        return (cache->usage + cache->slab_size) <= cache->maximum;
}


#if !SLAB_OBJECT_CACHE_ALLOCATION_TRACKING

inline status_t
ObjectCache::AllocateTrackingInfos(slab* slab, size_t byteCount, uint32 flags)
{
        return B_OK;
}


inline void
ObjectCache::FreeTrackingInfos(slab* slab, uint32 flags)
{
}

#endif // !SLAB_OBJECT_CACHE_ALLOCATION_TRACKING

#endif  // OBJECT_CACHE_H