Ancient

 view release on metacpan or  search on metacpan

xs/doubly/doubly.c  view on Meta::CPAN

    }
}

static void doubly_init(pTHX) {
#ifdef USE_ITHREADS
    if (!doubly_mutex_initialized) {
        MUTEX_INIT(&doubly_mutex);
        doubly_mutex_initialized = 1;
    }
#endif
    if (!doubly_initialized) {
        g_lists_size = 16;
        Newxz(g_lists, g_lists_size, DoublyListMeta);
        g_nodes_size = 64;
        Newxz(g_nodes, g_nodes_size, DoublyNodeSlot);
        g_free_nodes_size = 16;
        Newxz(g_free_nodes, g_free_nodes_size, IV);
        g_free_lists_size = 16;
        Newxz(g_free_lists, g_free_lists_size, IV);
        doubly_initialized = 1;
    }
}

/* ============================================
   Node Management
   ============================================ */

/* Allocate a new node slot, returns index */
static IV alloc_node(pTHX) {
    IV idx;

    /* Check free list first */
    if (g_free_nodes_count > 0) {
        idx = g_free_nodes[--g_free_nodes_count];
    } else {
        ensure_nodes_capacity(g_nodes_count);
        idx = g_nodes_count++;
    }

    /* Initialize node */
    g_nodes[idx].data = NULL;
    g_nodes[idx].prev_idx = -1;
    g_nodes[idx].next_idx = -1;
    g_nodes[idx].list_idx = -1;
    g_nodes[idx].node_id = g_next_node_id++;
#ifdef USE_ITHREADS
    g_nodes[idx].data_interp = NULL;
#endif

    return idx;
}

/* Free a node slot (add to free list) */
static void free_node(pTHX_ IV idx) {
    DoublyNodeSlot* node;

    if (idx < 0 || idx >= g_nodes_count) return;

    node = &g_nodes[idx];

    /* Decrement SV refcount if not during global destruction
     * and only if we're in the interpreter that owns the SV */
    if (node->data && !PL_dirty) {
#ifdef USE_ITHREADS
        /* Only free if this interpreter owns the SV */
        if (node->data_interp == PERL_GET_THX) {
            SvREFCNT_dec(node->data);
        }
        /* Note: if we're not the owner, we "leak" this SV intentionally.
         * The owning interpreter will clean it up during its destruction. */
#else
        SvREFCNT_dec(node->data);
#endif
    }
    node->data = NULL;
    node->prev_idx = -1;
    node->next_idx = -1;
    node->list_idx = -1;
    node->node_id = 0;
#ifdef USE_ITHREADS
    node->data_interp = NULL;
#endif

    /* Add to free list */
    if (g_free_nodes_count >= g_free_nodes_size) {
        g_free_nodes_size *= 2;
        Renew(g_free_nodes, g_free_nodes_size, IV);
    }
    g_free_nodes[g_free_nodes_count++] = idx;
}

/* Create a node with data */
static IV create_node(pTHX_ SV* data, IV list_idx) {
    IV idx = alloc_node(aTHX);
    DoublyNodeSlot* node = &g_nodes[idx];

    if (data && SvOK(data)) {
        node->data = newSVsv(data);  /* Copy the SV */
    } else {
        node->data = newSV(0);  /* Create empty SV for undef */
    }
    node->list_idx = list_idx;
#ifdef USE_ITHREADS
    node->data_interp = PERL_GET_THX;  /* Track which interpreter owns this SV */
#endif

    return idx;
}

/* ============================================
   List Management
   ============================================ */

/* Allocate a new list, returns index */
static IV alloc_list(pTHX) {
    IV idx;

    /* Check free list first */
    if (g_free_lists_count > 0) {
        idx = g_free_lists[--g_free_lists_count];
    } else {

xs/doubly/doubly.c  view on Meta::CPAN


            if (!found) {
                /* Re-find node by ID since list may have changed during callback */
                node_idx = find_node_by_id(list, current_node_id);
                if (node_idx >= 0) {
                    node = get_node(node_idx);
                    if (node) {
                        node_idx = node->next_idx;
                        pos++;
                    } else {
                        break;
                    }
                } else {
                    /* Node was removed during callback */
                    break;
                }
            }
        }
    }
    DOUBLY_UNLOCK();

    if (found) {
        list_insert_at_pos(aTHX_ list_idx, pos, data);
    } else {
        list_add(aTHX_ list_idx, data);
    }

    /* Return $self for chaining - no refcount increment needed */
    XSRETURN(1);
}

static XS(xs_destroy) {
    dXSARGS;
    HV* hash;
    SV** id_sv;
    IV list_idx;

    if (items < 1) croak("Usage: doubly::destroy(self)");

    hash = (HV*)SvRV(ST(0));
    id_sv = hv_fetch(hash, "_id", 3, 0);
    list_idx = id_sv ? SvIV(*id_sv) : -1;

    list_destroy(aTHX_ list_idx);
    XSRETURN_EMPTY;
}

static XS(xs_DESTROY) {
    dXSARGS;
    HV* hash;
    SV** id_sv;
    IV list_idx;
#ifdef USE_ITHREADS
    SV** owner_tid_sv;
    UV owner_tid;
    UV my_tid;
#endif

    PERL_UNUSED_VAR(items);

    /* Skip cleanup during global destruction */
    if (PL_dirty) {
        XSRETURN_EMPTY;
    }

    hash = (HV*)SvRV(ST(0));
    id_sv = hv_fetch(hash, "_id", 3, 0);
    list_idx = id_sv ? SvIV(*id_sv) : -1;

#ifdef USE_ITHREADS
    owner_tid_sv = hv_fetch(hash, "_owner_tid", 10, 0);
    owner_tid = owner_tid_sv ? SvUV(*owner_tid_sv) : 0;
    my_tid = PTR2UV(PERL_GET_THX);

    /* Always decrement refcount - list_decref handles SV ownership internally.
     * This prevents memory leaks from cloned objects that never get cleaned up. */
    list_decref(aTHX_ list_idx);
    (void)owner_tid; /* Suppress unused variable warning */
    (void)my_tid;
#else
    list_decref(aTHX_ list_idx);
#endif
    XSRETURN_EMPTY;
}

static XS(xs_CLONE_SKIP) {
    dXSARGS;
    PERL_UNUSED_VAR(items);
    /* Return 1 - do NOT clone doubly objects to new threads.
     * Each thread must create its own lists. Sharing lists across
     * threads is not supported. */
    XSRETURN_IV(1);
}

/* ============================================
   Boot
   ============================================ */

XS_EXTERNAL(boot_doubly) {
    dXSBOOTARGSXSAPIVERCHK;
    CV *method_cv;
    SV *ckobj;
    PERL_UNUSED_VAR(items);

    doubly_init(aTHX);

    /* Register custom OPs */
    XopENTRY_set(&doubly_data_get_xop, xop_name, "doubly_data_get");
    XopENTRY_set(&doubly_data_get_xop, xop_desc, "doubly data getter");
    Perl_custom_op_register(aTHX_ pp_doubly_data_get, &doubly_data_get_xop);

    XopENTRY_set(&doubly_data_set_xop, xop_name, "doubly_data_set");
    XopENTRY_set(&doubly_data_set_xop, xop_desc, "doubly data setter");
    Perl_custom_op_register(aTHX_ pp_doubly_data_set, &doubly_data_set_xop);

    XopENTRY_set(&doubly_length_xop, xop_name, "doubly_length");
    XopENTRY_set(&doubly_length_xop, xop_desc, "doubly length");
    Perl_custom_op_register(aTHX_ pp_doubly_length, &doubly_length_xop);

    XopENTRY_set(&doubly_next_xop, xop_name, "doubly_next");
    XopENTRY_set(&doubly_next_xop, xop_desc, "doubly next");



( run in 0.763 second using v1.01-cache-2.11-cpan-df04353d9ac )