Data-Histogram-Shared

 view release on metacpan or  search on metacpan

hist.h  view on Meta::CPAN

    for (int spin = 0; ; spin++) {
        uint32_t expected = 0;
        if (__atomic_compare_exchange_n(lock, &expected, mypid,
                1, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED))
            return;
        if (__builtin_expect(spin < HIST_RWLOCK_SPIN_LIMIT, 1)) {
            hist_rwlock_spin_pause();
            continue;
        }
        hist_park_writer(h);
        uint32_t cur = __atomic_load_n(lock, __ATOMIC_RELAXED);
        if (cur != 0) {
            long rc = syscall(SYS_futex, lock, FUTEX_WAIT, cur,
                              &hist_lock_timeout, NULL, 0);
            if (rc == -1 && errno == ETIMEDOUT) {
                hist_unpark_writer(h);
                hist_recover_after_timeout(h);
                spin = 0;
                continue;
            }
        }
        hist_unpark_writer(h);
        spin = 0;
    }
}

static inline void hist_rwlock_wrunlock(HistHandle *h) {
    HistHeader *hdr = h->hdr;
    __atomic_store_n(&hdr->rwlock, 0, __ATOMIC_RELEASE);
    if (__atomic_load_n(&hdr->rwlock_waiters, __ATOMIC_RELAXED) > 0)
        syscall(SYS_futex, &hdr->rwlock, FUTEX_WAKE, INT_MAX, NULL, NULL, 0);
}

/* ================================================================
 * Layout math + create / open / destroy
 *
 * Layout: Header -> reader_slots[1024] -> counts[counts_len]  (int64_t)
 * ================================================================ */

/* Single source of truth for the mmap region layout offsets. */
typedef struct { uint64_t reader_slots, counts; } HistLayout;

static inline HistLayout hist_layout(void) {
    HistLayout L;
    L.reader_slots = sizeof(HistHeader);
    L.counts       = L.reader_slots + (uint64_t)HIST_READER_SLOTS * sizeof(HistReaderSlot);
    L.counts       = (L.counts + 7) & ~(uint64_t)7;   /* 8-byte align the counts array (int64_t words) */
    return L;
}

static inline uint64_t hist_total_size(int64_t counts_len) {
    HistLayout L = hist_layout();
    return L.counts + (uint64_t)counts_len * sizeof(int64_t);   /* counts_len int64_t cells */
}

static inline int64_t *hist_counts(HistHandle *h) {
    return (int64_t *)((char *)h->base + h->hdr->counts_off);
}

/* ================================================================
 * HdrHistogram geometry -- canonical formulas (see HdrHistogram_c).
 * All derived fields are computed once here and stored in the header.
 * ================================================================ */

typedef struct {
    int64_t lowest;
    int64_t highest;
    int32_t sig_figs;
    int32_t unit_magnitude;
    int32_t sub_bucket_count_magnitude;
    int32_t sub_bucket_half_count_magnitude;
    int32_t sub_bucket_count;
    int32_t sub_bucket_half_count;
    int64_t sub_bucket_mask;
    int32_t bucket_count;
    int64_t counts_len;
} HistGeometry;

/* Validate args + compute the full geometry.  Single source of truth: the XS
 * layer does NOT duplicate these range checks. */
static int hist_validate_create_args(int64_t lowest, int64_t highest, int32_t sig_figs,
                                     HistGeometry *g, char *errbuf) {
    if (errbuf) errbuf[0] = '\0';
    if (lowest < 1) { HIST_ERR("lowest must be >= 1"); return 0; }
    if (highest < 2 * lowest) { HIST_ERR("highest must be >= 2 * lowest"); return 0; }
    if (sig_figs < HIST_MIN_SIG || sig_figs > HIST_MAX_SIG) {
        HIST_ERR("sig_figs must be between %d and %d", HIST_MIN_SIG, HIST_MAX_SIG); return 0;
    }

    int32_t unit_magnitude = (int32_t)floor(log2((double)lowest));
    int32_t sbc_magnitude  = (int32_t)ceil(log2(2.0 * pow(10.0, (double)sig_figs)));
    if (sbc_magnitude < 1) sbc_magnitude = 1;
    int32_t shc_magnitude  = sbc_magnitude - 1;
    if (unit_magnitude + shc_magnitude > 61) {
        HIST_ERR("lowest too large for sig_figs (unit_magnitude %d + sub_bucket_half_count_magnitude %d exceeds 61)", unit_magnitude, shc_magnitude);
        return 0;
    }
    int32_t sub_bucket_count      = (int32_t)(1 << sbc_magnitude);
    int32_t sub_bucket_half_count = sub_bucket_count / 2;
    int64_t sub_bucket_mask       = ((int64_t)sub_bucket_count - 1) << unit_magnitude;

    /* bucket_count: smallest count of buckets covering 'highest' */
    int64_t smallest_untrackable = (int64_t)sub_bucket_count << unit_magnitude;
    int32_t bucket_count = 1;
    while (smallest_untrackable <= highest) {
        if (smallest_untrackable > (INT64_MAX / 2)) { bucket_count++; break; }
        smallest_untrackable <<= 1;
        bucket_count++;
    }
    int64_t counts_len = (int64_t)(bucket_count + 1) * sub_bucket_half_count;

    g->lowest                          = lowest;
    g->highest                         = highest;
    g->sig_figs                        = sig_figs;
    g->unit_magnitude                  = unit_magnitude;
    g->sub_bucket_count_magnitude      = sbc_magnitude;
    g->sub_bucket_half_count_magnitude = shc_magnitude;
    g->sub_bucket_count                = sub_bucket_count;
    g->sub_bucket_half_count           = sub_bucket_half_count;
    g->sub_bucket_mask                 = sub_bucket_mask;
    g->bucket_count                    = bucket_count;



( run in 2.872 seconds using v1.01-cache-2.11-cpan-c966e8aa7e8 )