summaryrefslogblamecommitdiff
path: root/src/common/netbuffer.c
blob: 9ce13ffc31150e0c2568cf27fec54b77158cba04 (plain) (tree)



























                                                                           
  
                                                

                                                                    











                                                            



















































































                                                                                                                                                                                                                                      



                        


























                                                                                                                                                              


                         












































                                                                                                               


                       


















                                                        


                       




                                      
                       
//
// Network Buffer Subsystem (iobuffer)
//
//
// Author: Florian Wilkemeyer <fw@f-ws.de>
//
// Copyright (c) rAthena Project (www.rathena.org) - Licensed under GNU GPL
// For more information, see LICENCE in the main folder
//
//

#include <stdlib.h>
#include <string.h>
#include <stdio.h>

#include "../common/cbasetypes.h"
#include "../common/atomic.h"
#include "../common/mempool.h"
#include "../common/showmsg.h"
#include "../common/raconf.h"
#include "../common/thread.h"
#include "../common/malloc.h"
#include "../common/core.h"

#include "../common/netbuffer.h"


//
// Buffers are available in the following sizes:
//  48,     192,    2048,       8192
//  65536 (inter server connects may use it for charstatus struct..)
//


///
// Implementation:
//
static volatile int32 l_nEmergencyAllocations = 0; // stats.
static sysint l_nPools = 0;
static sysint *l_poolElemSize = NULL;
static mempool *l_pool = NULL;


void netbuffer_init()
{
    char localsection[32];
    raconf conf;
    sysint i;

    // Initialize Statistic counters:
    l_nEmergencyAllocations = 0;

    // Set localsection name according to running serverype.
    switch (SERVER_TYPE) {
        case ATHENA_SERVER_LOGIN:
            strcpy(localsection, "login-netbuffer");
            break;
        case ATHENA_SERVER_CHAR:
            strcpy(localsection, "char-netbuffer");
            break;
        case ATHENA_SERVER_INTER:
            strcpy(localsection, "inter-netbuffer");
            break;
        case ATHENA_SERVER_MAP:
            strcpy(localsection, "map-netbuffer");
            break;
        default:
            strcpy(localsection, "unsupported_type");
            break;
    }


    conf = raconf_parse("conf/network.conf");
    if (conf == NULL) {
        ShowFatalError("Failed to Parse required Configuration (conf/network.conf)");
        exit(EXIT_FAILURE);
    }

    // Get Values from config file
    l_nPools = (sysint)raconf_getintEx(conf,  localsection,  "netbuffer", "num", 0);
    if (l_nPools == 0) {
        ShowFatalError("Netbuffer (network.conf) failure - requires at least 1 Pool.\n");
        exit(EXIT_FAILURE);
    }

    // Allocate arrays.
    l_poolElemSize = (sysint *)aCalloc(l_nPools, sizeof(sysint));
    l_pool = (mempool *)aCalloc(l_nPools, sizeof(mempool));


    for (i = 0; i < l_nPools; i++) {
        int64 num_prealloc, num_realloc;
        char key[32];

        sprintf(key, "pool_%u_size", (uint32)i+1);
        l_poolElemSize[i] = (sysint)raconf_getintEx(conf, localsection, "netbuffer", key, 4096);
        if (l_poolElemSize[i] < 32) {
            ShowWarning("Netbuffer (network.conf) failure - minimum allowed buffer size is 32 byte) - fixed.\n");
            l_poolElemSize[i] = 32;
        }

        sprintf(key, "pool_%u_prealloc", (uint32)i+1);
        num_prealloc = raconf_getintEx(conf, localsection, "netbuffer", key, 150);

        sprintf(key, "pool_%u_realloc_step", (uint32)i+1);
        num_realloc = raconf_getintEx(conf, localsection, "netbuffer", key, 100);

        // Create Pool!
        sprintf(key, "Netbuffer %u", (uint32)l_poolElemSize[i]); // name.

        // Info
        ShowInfo("NetBuffer: Creating Pool %u (Prealloc: %u, Realloc Step: %u) - %0.2f MiB\n", l_poolElemSize[i], num_prealloc, num_realloc, (float)((sizeof(struct netbuf) + l_poolElemSize[i] - 32)* num_prealloc)/1024.0f/1024.0f);

        //
        // Size Calculation:
        //  struct netbuf  +  requested buffer size - 32 (because the struct already contains 32 byte buffer space at the end of struct)
        l_pool[i] = mempool_create(key, (sizeof(struct netbuf) + l_poolElemSize[i] - 32),  num_prealloc,  num_realloc, NULL, NULL);
        if (l_pool[i] == NULL) {
            ShowFatalError("Netbuffer: cannot create Pool for %u byte buffers.\n", l_poolElemSize[i]);
            // @leak: clean everything :D
            exit(EXIT_FAILURE);
        }

    }//


    raconf_destroy(conf);

}//end: netbuffer_init()


void netbuffer_final()
{
    sysint i;

    if (l_nPools > 0) {
        /// .. finalize mempools
        for (i = 0; i < l_nPools; i++) {
            mempool_stats stats = mempool_get_stats(l_pool[i]);

            ShowInfo("Netbuffer: Freeing Pool %u (Peak Usage: %u, Realloc Events: %u)\n", l_poolElemSize[i], stats.peak_nodes_used, stats.num_realloc_events);

            mempool_destroy(l_pool[i]);
        }

        if (l_nEmergencyAllocations > 0) {
            ShowWarning("Netbuffer: did %u Emergency Allocations, please tune your network.conf!\n", l_nEmergencyAllocations);
            l_nEmergencyAllocations = 0;
        }

        aFree(l_poolElemSize);
        l_poolElemSize = NULL;
        aFree(l_pool);
        l_pool = NULL;
        l_nPools = 0;
    }


}//end: netbuffer_final()


netbuf netbuffer_get(sysint sz)
{
    sysint i;
    netbuf nb = NULL;

    // Search an appropriate pool
    for (i = 0; i < l_nPools; i++) {
        if (sz <= l_poolElemSize[i]) {
            // match

            nb = (netbuf)mempool_node_get(l_pool[i]);
            nb->pool = i;

            break;
        }
    }

    // No Bufferpool found that mets there quirements?.. (thats bad..)
    if (nb == NULL) {
        ShowWarning("Netbuffer: get(%u): => no appropriate pool found - emergency allocation required.\n", sz);
        ShowWarning("Please reconfigure your network.conf!");

        InterlockedIncrement(&l_nEmergencyAllocations);

        // .. better to check (netbuf struct provides 32 byte bufferspace itself.
        if (sz < 32) sz = 32;

        // allocate memory using malloc ..
        while (1) {
            nb = (netbuf) aMalloc((sizeof(struct netbuf) + sz - 32));
            if (nb != NULL) {
                memset(nb, 0x00, (sizeof(struct netbuf) + sz - 32));  // zero memory! (to enforce commit @ os.)
                nb->pool = -1; // emergency alloc.
                break;
            }

            rathread_yield();
        }// spin allocation.

    }


    nb->refcnt = 1;  // Initial refcount is 1

    return nb;
}//end: netbuffer_get()


void netbuffer_put(netbuf nb)
{

    // Decrement reference counter, if > 0 do nothing :)
    if (InterlockedDecrement(&nb->refcnt) > 0)
        return;

    // Is this buffer an emergency allocated buffer?
    if (nb->pool == -1) {
        aFree(nb);
        return;
    }


    // Otherwise its a normal mempool based buffer
    // return it to the according mempool:
    mempool_node_put(l_pool[nb->pool], nb);


}//end: netbuffer_put()


void netbuffer_incref(netbuf nb)
{

    InterlockedIncrement(&nb->refcnt);

}//end: netbuf_incref()