/*
 * Copyright (C) 1996-2023 The Squid Software Foundation and contributors
 *
 * Squid software is distributed under GPLv2+ license and includes
 * contributions from numerous individuals and organizations.
 * Please see the COPYING and CONTRIBUTORS files for details.
 */

/* DEBUG: section 54    Interprocess Communication */

#include "squid.h"
#include "base/RunnersRegistry.h"
#include "base/TextException.h"
#include "ipc/mem/PagePool.h"
#include "ipc/mem/Pages.h"
#include "tools.h"

// Uses a single PagePool instance, for now.
// Eventually, we may have pools dedicated to memory caching, disk I/O, etc.

// TODO: make pool id more unique so it does not conflict with other Squids?
static const char *PagePoolId = "squid-page-pool";
static Ipc::Mem::PagePool *ThePagePool = nullptr;
static int TheLimits[Ipc::Mem::PageId::maxPurpose+1];

// TODO: make configurable to avoid waste when mem-cached objects are small/big
size_t
Ipc::Mem::PageSize()
{
    return 32*1024;
}

bool
Ipc::Mem::GetPage(const PageId::Purpose purpose, PageId &page)
{
    return ThePagePool && PagesAvailable(purpose) > 0 ?
           ThePagePool->get(purpose, page) : false;
}

void
Ipc::Mem::PutPage(PageId &page)
{
    Must(ThePagePool);
    ThePagePool->put(page);
}

char *
Ipc::Mem::PagePointer(const PageId &page)
{
    Must(ThePagePool);
    return ThePagePool->pagePointer(page);
}

size_t
Ipc::Mem::PageLimit()
{
    size_t limit = 0;
    for (int i = 0; i <= PageId::maxPurpose; ++i)
        limit += PageLimit(i);
    return limit;
}

size_t
Ipc::Mem::PageLimit(const int purpose)
{
    Must(0 <= purpose && purpose <= PageId::maxPurpose);
    return TheLimits[purpose];
}

// note: adjust this if we start recording needs during reconfigure
void
Ipc::Mem::NotePageNeed(const int purpose, const int count)
{
    Must(0 <= purpose && purpose <= PageId::maxPurpose);
    Must(count >= 0);
    TheLimits[purpose] += count;
}

size_t
Ipc::Mem::PageLevel()
{
    return ThePagePool ? ThePagePool->level() : 0;
}

size_t
Ipc::Mem::PageLevel(const int purpose)
{
    return ThePagePool ? ThePagePool->level(purpose) : 0;
}

/// initializes shared memory pages
class SharedMemPagesRr: public Ipc::Mem::RegisteredRunner
{
public:
    /* RegisteredRunner API */
    SharedMemPagesRr(): owner(nullptr) {}
    void useConfig() override;
    void create() override;
    void open() override;
    ~SharedMemPagesRr() override;

private:
    Ipc::Mem::PagePool::Owner *owner;
};

DefineRunnerRegistrator(SharedMemPagesRr);

void
SharedMemPagesRr::useConfig()
{
    if (Ipc::Mem::PageLimit() <= 0)
        return;

    Ipc::Mem::RegisteredRunner::useConfig();
}

void
SharedMemPagesRr::create()
{
    Must(!owner);
    owner = Ipc::Mem::PagePool::Init(PagePoolId,
                                     Ipc::Mem::PageStack::IdForMultipurposePool(),
                                     Ipc::Mem::PageLimit(),
                                     Ipc::Mem::PageSize());
}

void
SharedMemPagesRr::open()
{
    Must(!ThePagePool);
    ThePagePool = new Ipc::Mem::PagePool(PagePoolId);
}

SharedMemPagesRr::~SharedMemPagesRr()
{
    delete ThePagePool;
    ThePagePool = nullptr;
    delete owner;
}

