Jordan
Jordan

Reputation: 529

extern global object doesn't seem to initialise its member

I'm getting a segfault when executing this code. Specifically after g_lru_stack.add_node(&lru_node) is called.

Running under GDB shows that the dummy node doesn't look like it has been initialised. Is this to do with how extern globals are initialised? If so any help would be greatly appreciated here.

I have included snippets from both the header and cpp file.

Specifically, my question is this: How can I get LRU_Stack g_lru_stack , declared at the top of object.cc, to call the LRU_Node ctor that takes no arguments.

It seems this ctor never gets called, hence why my dummy node isn't initialised.

Object.h

class obj_payload;
extern cas_mutex g_lru_stack_mutex;


class LRU_Node {

private:
        obj_payload* payload;
    LRU_Node* up;
    LRU_Node* down;
    size_t predicted_bytes_in_cache;

public:
    LRU_Node() : payload(nullptr), up(this), down(this), predicted_bytes_in_cache(1337) {}      // Dummy ctor

    LRU_Node(obj_payload* p) : payload(p), up(nullptr), down(nullptr), predicted_bytes_in_cache(88) {} // Normal Creation of node

    //Adds a node to the top of the stack
    //Has dummy context
    void add_to_stack(LRU_Node* newNode);

    //Sets how many bytes of the object are predicted to be in the cache
    //Has dummy context
    void is_node_in_cache(LRU_Node* node);

    //Moves a node to the top of the stack
    //Has dummy context
    void move_node_to_top(LRU_Node* node);

    //Has context of caller
    size_t get_predicted_bytes_in_cache();
};

class LRU_Stack {
    LRU_Node dummy;

public:
void add_node(LRU_Node* node);

void move_node_to_top(LRU_Node* node);
};
extern LRU_Stack g_lru_stack;



class obj_payload {
    typedef uint32_t ctr_t;
private:
    ctr_t refcnt;
    const uint32_t sz;                // size of the data space in bytes
    LRU_Node lru_node;                // Jordan -- This arg objects node for the LRU_Stack


    obj_payload( typeinfo tinfo_,
         uint32_t size_,
         int refcnt_init=1 )
    : refcnt( refcnt_init ),
      sz( size_ ),
      tinfo( tinfo_ ), lru_node(this) {

        g_lru_stack.add_node(&lru_node);    


    }

Object.cc

#include "object.h"

namespace obj {

//Jordan -- Global LRU_Node Stack 
cas_mutex g_lru_stack_mutex;
LRU_Stack g_lru_stack;

//Adds a node to the top of the stack
//Has dummy context
void LRU_Node::add_to_stack(LRU_Node* newNode) {  
        newNode->down = down;           // Set the new nodes previous -> dummys previous
        newNode->up = this;         // Set new nodes next -> dummy
        down->up = newNode;         // Dummy next -> new node (i.e. Previous top of stack node up -> newNode)
        down = newNode;             // Dummy previous -> new node (i.e. Dummy down pointer now links back round to the new node at the top)
}

//Sets how many bytes of the object are predicted to be in the cache
//Has dummy context
void LRU_Node::is_node_in_cache(LRU_Node* node) {
        size_t total = 0;
        LRU_Node* orignal = node;

        while (node != this) {
                total += node->payload->get_size(); // Add current size to total
                node = node->up;                        // Go to next node
        }
        node = orignal; //Reset node to the passed in node, then set how many bytes it has contained within cache

        if (total <= cache_size) {
                node->predicted_bytes_in_cache = node->payload->get_size();
        }
        else {
                node->predicted_bytes_in_cache = (node->payload->get_size()) - (total - cache_size) < node->payload->get_size() ? (node->payload->get_size()) - (total - cache_size) : 0;
        }
}

//Moves a node to the top of the stack
//Has dummy context
void LRU_Node::move_node_to_top(LRU_Node* node) {

        if (down != node) { // Check that the node to move is not already top of stack
                node->down->up = node->up;
                node->up->down = node->down;

                if (down == node->up) { // If the node is seccond top of stack 
                        node->up->up = node;
                }

                node->down = down;
                node->up = this;
                down->up = node;
                down = node;
        }
}

//Has context of caller
size_t LRU_Node::get_predicted_bytes_in_cache() {
        return predicted_bytes_in_cache;
}

//Has dummy context
bool LRU_Node::is_empty() {
        return (up == this);
}

void LRU_Stack::add_node(LRU_Node* node) {
    g_lru_stack_mutex.lock();
    dummy.add_to_stack(node);
    g_lru_stack_mutex.unlock();
}

void LRU_Stack::move_node_to_top(LRU_Node* node) {
    g_lru_stack_mutex.lock();
    dummy.is_node_in_cache(node);
    dummy.move_node_to_top(node);
    g_lru_stack_mutex.unlock();
}

Upvotes: 0

Views: 60

Answers (1)

Deduplicator
Deduplicator

Reputation: 45654

"extern globals" are not objects (unless they include an initializer): They are forward-declarations.

Global objects are initialized in two phases:

  • All those with compile-time constant initializers are done, the rest is zeroed.
  • The run-time initializers are run in order of object definition. (No ordering guarantees for objects in different compilation units!).

Seems like UB got you there.

To solve the error, do one of these:

  • Put the definition of the object before its first use in the same compilation unit (The most efficient method).
  • Put the object as a static in an accessor function. Init will be on first use (thread-safe!).

    Type& getTypeSingleton() {
        static Type x/*optional initializer*/;
        return x;
    }
    
  • (Implementation dependent) The compilation unit first mentioned on the command-line will be initialized first in all current implementations (As effifient as the first, but fragile).

Upvotes: 2

Related Questions