Blame src/izem/src/lock/zm_hmcs.c

Packit Service c5cf8c
/* -*- Mode: C; c-basic-offset:4 ; indent-tabs-mode:nil ; -*- */
Packit Service c5cf8c
/*
Packit Service c5cf8c
 * See COPYRIGHT in top-level directory.
Packit Service c5cf8c
 */
Packit Service c5cf8c
Packit Service c5cf8c
/*
Packit Service c5cf8c
 * The original version of this code was contributed by Milind Chabbi
Packit Service c5cf8c
 * based on the work when he was at Rice University. It relies on the
Packit Service c5cf8c
 * HMCS lock description in [1] and the fast path described in [2].
Packit Service c5cf8c
 *
Packit Service c5cf8c
 * [1] Chabbi, Milind, Michael Fagan, and John Mellor-Crummey. "High
Packit Service c5cf8c
 * performance locks for multi-level NUMA systems." In Proceedings of
Packit Service c5cf8c
 * the ACM SIGPLAN Symposium on Principles and Practice of Parallel
Packit Service c5cf8c
 * Programming (PPoPP'15), ACM, 2015.
Packit Service c5cf8c
 *
Packit Service c5cf8c
 * [2] Chabbi, Milind, and John Mellor-Crummey. "Contention-conscious,
Packit Service c5cf8c
 * locality-preserving locks." In Proceedings of the 21st ACM SIGPLAN
Packit Service c5cf8c
 * Symposium on Principles and Practice of Parallel Programming (PPoPP'16,
Packit Service c5cf8c
 * p. 22. ACM, 2016.
Packit Service c5cf8c
 */
Packit Service c5cf8c
Packit Service c5cf8c
#include "lock/zm_lock_types.h"
Packit Service c5cf8c
#include <hwloc.h>
Packit Service c5cf8c
Packit Service c5cf8c
#ifndef DEFAULT_THRESHOLD
Packit Service c5cf8c
#define DEFAULT_THRESHOLD 256
Packit Service c5cf8c
#endif
Packit Service c5cf8c
Packit Service c5cf8c
#ifndef HMCS_DEFAULT_MAX_LEVELS
Packit Service c5cf8c
#define HMCS_DEFAULT_MAX_LEVELS 3
Packit Service c5cf8c
#endif
Packit Service c5cf8c
Packit Service c5cf8c
#define WAIT (0xffffffff)
Packit Service c5cf8c
#define COHORT_START (0x1)
Packit Service c5cf8c
#define ACQUIRE_PARENT (0xcffffffc)
Packit Service c5cf8c
Packit Service c5cf8c
#ifndef TRUE
Packit Service c5cf8c
#define TRUE 1
Packit Service c5cf8c
#else
Packit Service c5cf8c
#error "TRUE already defined"
Packit Service c5cf8c
#endif
Packit Service c5cf8c
Packit Service c5cf8c
#ifndef FALSE
Packit Service c5cf8c
#define FALSE 0
Packit Service c5cf8c
#else
Packit Service c5cf8c
#error "TRUE already defined"
Packit Service c5cf8c
#endif
Packit Service c5cf8c
Packit Service c5cf8c
/* Atomic operation shorthands. The memory ordering defaults to:
Packit Service c5cf8c
 * 1- Acquire ordering for loads
Packit Service c5cf8c
 * 2- Release ordering for stores
Packit Service c5cf8c
 * 3- Acquire+Release ordering for read-modify-write operations
Packit Service c5cf8c
 * */
Packit Service c5cf8c
Packit Service c5cf8c
#define LOAD(addr)                  zm_atomic_load(addr, zm_memord_acquire)
Packit Service c5cf8c
#define STORE(addr, val)            zm_atomic_store(addr, val, zm_memord_release)
Packit Service c5cf8c
#define SWAP(addr, desire)          zm_atomic_exchange_ptr(addr, desire, zm_memord_acq_rel)
Packit Service c5cf8c
#define CAS(addr, expect, desire)   zm_atomic_compare_exchange_strong(addr,\
Packit Service c5cf8c
                                                                      expect,\
Packit Service c5cf8c
                                                                      desire,\
Packit Service c5cf8c
                                                                      zm_memord_acq_rel,\
Packit Service c5cf8c
                                                                      zm_memord_acquire)
Packit Service c5cf8c
Packit Service c5cf8c
struct hnode{
Packit Service c5cf8c
    unsigned threshold __attribute__((aligned(ZM_CACHELINE_SIZE)));
Packit Service c5cf8c
    struct hnode * parent __attribute__((aligned(ZM_CACHELINE_SIZE)));
Packit Service c5cf8c
    zm_atomic_ptr_t lock __attribute__((aligned(ZM_CACHELINE_SIZE)));
Packit Service c5cf8c
    zm_mcs_qnode_t node __attribute__((aligned(ZM_CACHELINE_SIZE)));
Packit Service c5cf8c
Packit Service c5cf8c
}__attribute__((aligned(ZM_CACHELINE_SIZE)));
Packit Service c5cf8c
Packit Service c5cf8c
struct leaf{
Packit Service c5cf8c
    struct hnode * cur_node;
Packit Service c5cf8c
    struct hnode * root_node;
Packit Service c5cf8c
    zm_mcs_qnode_t I;
Packit Service c5cf8c
    int curDepth;
Packit Service c5cf8c
    int took_fast_path;
Packit Service c5cf8c
};
Packit Service c5cf8c
Packit Service c5cf8c
struct lock{
Packit Service c5cf8c
    // Assumes tids range from [0.. max_threads)
Packit Service c5cf8c
    // Assumes that tid 0 is close to tid and so on.
Packit Service c5cf8c
    struct leaf ** leaf_nodes __attribute__((aligned(ZM_CACHELINE_SIZE)));
Packit Service c5cf8c
    hwloc_topology_t topo;
Packit Service c5cf8c
    int levels;
Packit Service c5cf8c
};
Packit Service c5cf8c
Packit Service c5cf8c
static zm_thread_local int tid = -1;
Packit Service c5cf8c
Packit Service c5cf8c
/* TODO: automate hardware topology detection
Packit Service c5cf8c
 * instead of the below hard-coded method */
Packit Service c5cf8c
Packit Service c5cf8c
/* Check the actual affinity mask assigned to the thread */
Packit Service c5cf8c
static void check_affinity(hwloc_topology_t topo) {
Packit Service c5cf8c
    hwloc_cpuset_t cpuset = hwloc_bitmap_alloc();
Packit Service c5cf8c
    int set_length;
Packit Service c5cf8c
    hwloc_get_cpubind(topo, cpuset, HWLOC_CPUBIND_THREAD);
Packit Service c5cf8c
    set_length = hwloc_get_nbobjs_inside_cpuset_by_type(topo, cpuset, HWLOC_OBJ_PU);
Packit Service c5cf8c
    hwloc_bitmap_free(cpuset);
Packit Service c5cf8c
Packit Service c5cf8c
    if(set_length != 1) {
Packit Service c5cf8c
        printf("IZEM:HMCS:ERROR: thread bound to more than one HW thread!\n");
Packit Service c5cf8c
        exit(EXIT_FAILURE);
Packit Service c5cf8c
    }
Packit Service c5cf8c
}
Packit Service c5cf8c
Packit Service c5cf8c
static inline void reuse_qnode(zm_mcs_qnode_t *I){
Packit Service c5cf8c
    STORE(&I->status, WAIT);
Packit Service c5cf8c
    STORE(&I->next, ZM_NULL);
Packit Service c5cf8c
}
Packit Service c5cf8c
Packit Service c5cf8c
static void* new_hnode() {
Packit Service c5cf8c
    int err;
Packit Service c5cf8c
    void *storage;
Packit Service c5cf8c
    err = posix_memalign(&storage, ZM_CACHELINE_SIZE, sizeof(struct hnode));
Packit Service c5cf8c
    if (err != 0) {
Packit Service c5cf8c
        printf("posix_memalign failed in HMCS : new_hnode \n");
Packit Service c5cf8c
        exit(EXIT_FAILURE);
Packit Service c5cf8c
    }
Packit Service c5cf8c
    return storage;
Packit Service c5cf8c
}
Packit Service c5cf8c
Packit Service c5cf8c
/* TODO: Macro or Template this for fast comprison */
Packit Service c5cf8c
static inline unsigned get_threshold(struct hnode *L) {
Packit Service c5cf8c
    return L->threshold;
Packit Service c5cf8c
}
Packit Service c5cf8c
Packit Service c5cf8c
static inline void normal_mcs_release_with_value(struct hnode * L, zm_mcs_qnode_t *I, unsigned val){
Packit Service c5cf8c
Packit Service c5cf8c
    zm_mcs_qnode_t *succ = (zm_mcs_qnode_t *)LOAD(&I->next);
Packit Service c5cf8c
    if(succ) {
Packit Service c5cf8c
        STORE(&succ->status, val);
Packit Service c5cf8c
        return;
Packit Service c5cf8c
    }
Packit Service c5cf8c
    zm_mcs_qnode_t *tmp = I;
Packit Service c5cf8c
    if (CAS(&(L->lock), (zm_ptr_t*)&tmp,ZM_NULL))
Packit Service c5cf8c
        return;
Packit Service c5cf8c
    while(succ == NULL)
Packit Service c5cf8c
        succ = (zm_mcs_qnode_t *)LOAD(&I->next); /* SPIN */
Packit Service c5cf8c
    STORE(&succ->status, val);
Packit Service c5cf8c
    return;
Packit Service c5cf8c
}
Packit Service c5cf8c
Packit Service c5cf8c
static inline void acquire_root(struct hnode * L, zm_mcs_qnode_t *I) {
Packit Service c5cf8c
    // Prepare the node for use.
Packit Service c5cf8c
    reuse_qnode(I);
Packit Service c5cf8c
    zm_mcs_qnode_t *pred = (zm_mcs_qnode_t*) SWAP(&(L->lock), (zm_ptr_t)I);
Packit Service c5cf8c
Packit Service c5cf8c
    if(!pred) {
Packit Service c5cf8c
        // I am the first one at this level
Packit Service c5cf8c
        return;
Packit Service c5cf8c
    }
Packit Service c5cf8c
Packit Service c5cf8c
    STORE(&pred->next, I);
Packit Service c5cf8c
    while(LOAD(&I->status) == WAIT)
Packit Service c5cf8c
        ; /* SPIN */
Packit Service c5cf8c
    return;
Packit Service c5cf8c
}
Packit Service c5cf8c
Packit Service c5cf8c
static inline void tryacq_root(struct hnode * L, zm_mcs_qnode_t *I, int *success) {
Packit Service c5cf8c
    zm_ptr_t expected = ZM_NULL;
Packit Service c5cf8c
    // Prepare the node for use.
Packit Service c5cf8c
    reuse_qnode(I);
Packit Service c5cf8c
    *success = CAS(&(L->lock), &expected, (zm_ptr_t)I);
Packit Service c5cf8c
Packit Service c5cf8c
    return;
Packit Service c5cf8c
}
Packit Service c5cf8c
Packit Service c5cf8c
static inline void release_root(struct hnode * L, zm_mcs_qnode_t *I) {
Packit Service c5cf8c
    // Top level release is usual MCS
Packit Service c5cf8c
    // At the top level MCS we always writr COHORT_START since
Packit Service c5cf8c
    // 1. It will release the lock
Packit Service c5cf8c
    // 2. Will never grow large
Packit Service c5cf8c
    // 3. Avoids a read from I->status
Packit Service c5cf8c
    normal_mcs_release_with_value(L, I, COHORT_START);
Packit Service c5cf8c
}
Packit Service c5cf8c
Packit Service c5cf8c
static inline int nowaiters_root(struct hnode * L, zm_mcs_qnode_t *I) {
Packit Service c5cf8c
    return (LOAD(&I->next) == ZM_NULL);
Packit Service c5cf8c
}
Packit Service c5cf8c
Packit Service c5cf8c
static inline void acquire_helper(int level, struct hnode * L, zm_mcs_qnode_t *I) {
Packit Service c5cf8c
    // Trivial case = root level
Packit Service c5cf8c
    if (level == 1)
Packit Service c5cf8c
        acquire_root(L, I);
Packit Service c5cf8c
    else {
Packit Service c5cf8c
        // Prepare the node for use.
Packit Service c5cf8c
        reuse_qnode(I);
Packit Service c5cf8c
        zm_mcs_qnode_t* pred = (zm_mcs_qnode_t*)SWAP(&(L->lock), (zm_ptr_t)I);
Packit Service c5cf8c
        if(!pred) {
Packit Service c5cf8c
            // I am the first one at this level
Packit Service c5cf8c
            // begining of cohort
Packit Service c5cf8c
            STORE(&I->status, COHORT_START);
Packit Service c5cf8c
            // acquire at next level if not at the top level
Packit Service c5cf8c
            acquire_helper(level - 1, L->parent, &(L->node));
Packit Service c5cf8c
            return;
Packit Service c5cf8c
        } else {
Packit Service c5cf8c
            STORE(&pred->next, I);
Packit Service c5cf8c
            for(;;){
Packit Service c5cf8c
                unsigned myStatus = LOAD(&I->status);
Packit Service c5cf8c
                if(myStatus < ACQUIRE_PARENT) {
Packit Service c5cf8c
                    return;
Packit Service c5cf8c
                }
Packit Service c5cf8c
                if(myStatus == ACQUIRE_PARENT) {
Packit Service c5cf8c
                    // beginning of cohort
Packit Service c5cf8c
                    STORE(&I->status, COHORT_START);
Packit Service c5cf8c
                    // This means this level is acquired and we can start the next level
Packit Service c5cf8c
                    acquire_helper(level - 1, L->parent, &(L->node));
Packit Service c5cf8c
                    return;
Packit Service c5cf8c
                }
Packit Service c5cf8c
                // spin back; (I->status == WAIT)
Packit Service c5cf8c
            }
Packit Service c5cf8c
        }
Packit Service c5cf8c
    }
Packit Service c5cf8c
}
Packit Service c5cf8c
Packit Service c5cf8c
static inline void release_helper(int level, struct hnode * L, zm_mcs_qnode_t *I) {
Packit Service c5cf8c
    // Trivial case = root level
Packit Service c5cf8c
    if (level == 1) {
Packit Service c5cf8c
        release_root(L, I);
Packit Service c5cf8c
    } else {
Packit Service c5cf8c
        unsigned cur_count = LOAD(&(I->status)) ;
Packit Service c5cf8c
        zm_mcs_qnode_t * succ;
Packit Service c5cf8c
Packit Service c5cf8c
        // Lower level releases
Packit Service c5cf8c
        if(cur_count == get_threshold(L)) {
Packit Service c5cf8c
            // NO KNOWN SUCCESSORS / DESCENDENTS
Packit Service c5cf8c
            // reached threshold and have next level
Packit Service c5cf8c
            // release to next level
Packit Service c5cf8c
            release_helper(level - 1, L->parent, &(L->node));
Packit Service c5cf8c
            //COMMIT_ALL_WRITES();
Packit Service c5cf8c
            // Tap successor at this level and ask to spin acquire next level lock
Packit Service c5cf8c
            normal_mcs_release_with_value(L, I, ACQUIRE_PARENT);
Packit Service c5cf8c
            return;
Packit Service c5cf8c
        }
Packit Service c5cf8c
Packit Service c5cf8c
        succ = (zm_mcs_qnode_t*)LOAD(&(I->next));
Packit Service c5cf8c
        // Not reached threshold
Packit Service c5cf8c
        if(succ) {
Packit Service c5cf8c
            STORE(&succ->status, cur_count + 1);
Packit Service c5cf8c
            return; // released
Packit Service c5cf8c
        }
Packit Service c5cf8c
        // No known successor, so release
Packit Service c5cf8c
        release_helper(level - 1, L->parent, &(L->node));
Packit Service c5cf8c
        // Tap successor at this level and ask to spin acquire next level lock
Packit Service c5cf8c
        normal_mcs_release_with_value(L, I, ACQUIRE_PARENT);
Packit Service c5cf8c
    }
Packit Service c5cf8c
}
Packit Service c5cf8c
Packit Service c5cf8c
static inline int nowaiters_helper(int level, struct hnode * L, zm_mcs_qnode_t *I) {
Packit Service c5cf8c
    if (level == 1 ) {
Packit Service c5cf8c
        return nowaiters_root(L,I);
Packit Service c5cf8c
    } else {
Packit Service c5cf8c
        if(LOAD(&I->next) != ZM_NULL)
Packit Service c5cf8c
            return FALSE;
Packit Service c5cf8c
        else
Packit Service c5cf8c
            return nowaiters_helper(level - 1, L->parent, &(L->node));
Packit Service c5cf8c
    }
Packit Service c5cf8c
}
Packit Service c5cf8c
Packit Service c5cf8c
static void* new_leaf(struct hnode *h, int depth) {
Packit Service c5cf8c
    int err;
Packit Service c5cf8c
    struct leaf *leaf;
Packit Service c5cf8c
    err = posix_memalign((void **) &leaf, ZM_CACHELINE_SIZE, sizeof(struct leaf));
Packit Service c5cf8c
    if (err != 0) {
Packit Service c5cf8c
        printf("posix_memalign failed in HMCS : new_leaf \n");
Packit Service c5cf8c
        exit(EXIT_FAILURE);
Packit Service c5cf8c
    }
Packit Service c5cf8c
    leaf->cur_node = h;
Packit Service c5cf8c
    leaf->curDepth = depth;
Packit Service c5cf8c
    leaf->took_fast_path = FALSE;
Packit Service c5cf8c
    struct hnode *tmp, *root_node;
Packit Service c5cf8c
    for(tmp = leaf->cur_node; tmp->parent != NULL; tmp = tmp->parent);
Packit Service c5cf8c
    root_node = tmp;
Packit Service c5cf8c
    leaf->root_node = root_node;
Packit Service c5cf8c
    return leaf;
Packit Service c5cf8c
}
Packit Service c5cf8c
Packit Service c5cf8c
static inline void acquire_from_leaf(int level, struct leaf *L){
Packit Service c5cf8c
    if((zm_ptr_t)L->cur_node->lock == ZM_NULL
Packit Service c5cf8c
    && (zm_ptr_t)L->root_node->lock == ZM_NULL) {
Packit Service c5cf8c
        // go FP
Packit Service c5cf8c
        L->took_fast_path = TRUE;
Packit Service c5cf8c
        acquire_root(L->root_node, &L->I);
Packit Service c5cf8c
        return;
Packit Service c5cf8c
    }
Packit Service c5cf8c
    acquire_helper(level, L->cur_node, &L->I);
Packit Service c5cf8c
    return;
Packit Service c5cf8c
}
Packit Service c5cf8c
Packit Service c5cf8c
static inline void tryacq_from_leaf(int level, struct leaf *L, int *success){
Packit Service c5cf8c
    *success = 0;
Packit Service c5cf8c
    if((zm_ptr_t)L->cur_node->lock == ZM_NULL
Packit Service c5cf8c
    && (zm_ptr_t)L->root_node->lock == ZM_NULL) {
Packit Service c5cf8c
        tryacq_root(L->root_node, &L->I, success);
Packit Service c5cf8c
        if (*success)
Packit Service c5cf8c
            L->took_fast_path = TRUE;
Packit Service c5cf8c
    }
Packit Service c5cf8c
    return;
Packit Service c5cf8c
}
Packit Service c5cf8c
Packit Service c5cf8c
static inline void release_from_leaf(int level, struct leaf *L){
Packit Service c5cf8c
    //myrelease(cur_node, I);
Packit Service c5cf8c
    if(L->took_fast_path) {
Packit Service c5cf8c
        release_root(L->root_node, &L->I);
Packit Service c5cf8c
        L->took_fast_path = FALSE;
Packit Service c5cf8c
        return;
Packit Service c5cf8c
    }
Packit Service c5cf8c
    release_helper(level, L->cur_node, &L->I);
Packit Service c5cf8c
    return;
Packit Service c5cf8c
}
Packit Service c5cf8c
Packit Service c5cf8c
static inline int nowaiters_from_leaf(int level, struct leaf *L){
Packit Service c5cf8c
    // Shouldnt this be nowaiters(root_node, I)?
Packit Service c5cf8c
    if(L->took_fast_path) {
Packit Service c5cf8c
        return nowaiters_root(L->cur_node, &L->I);
Packit Service c5cf8c
    }
Packit Service c5cf8c
Packit Service c5cf8c
    return nowaiters_helper(level, L->cur_node, &L->I);
Packit Service c5cf8c
}
Packit Service c5cf8c
Packit Service c5cf8c
static int get_hwthread_id(hwloc_topology_t topo){
Packit Service c5cf8c
    hwloc_cpuset_t cpuset = hwloc_bitmap_alloc();
Packit Service c5cf8c
    hwloc_obj_t obj;
Packit Service c5cf8c
    hwloc_get_cpubind(topo, cpuset, HWLOC_CPUBIND_THREAD);
Packit Service c5cf8c
    obj = hwloc_get_obj_inside_cpuset_by_type(topo, cpuset, HWLOC_OBJ_PU, 0);
Packit Service c5cf8c
    hwloc_bitmap_free(cpuset);
Packit Service c5cf8c
    return obj->logical_index;
Packit Service c5cf8c
}
Packit Service c5cf8c
Packit Service c5cf8c
static void set_hierarchy(struct lock *L, int *max_threads, int** particip_per_level) {
Packit Service c5cf8c
    int max_depth, levels = 0, max_levels = HMCS_DEFAULT_MAX_LEVELS, explicit_levels = 0;
Packit Service c5cf8c
    char tmp[20];
Packit Service c5cf8c
    char *s = getenv("ZM_HMCS_MAX_LEVELS");
Packit Service c5cf8c
    if (s != NULL)
Packit Service c5cf8c
        max_levels = atoi(s);
Packit Service c5cf8c
    int depths[max_levels];
Packit Service c5cf8c
    int idx = 0;
Packit Service c5cf8c
    /* advice to users: run `hwloc-ls -s --no-io --no-icaches` and choose
Packit Service c5cf8c
     * depths of interest in ascending order. The first depth must be `0' */
Packit Service c5cf8c
Packit Service c5cf8c
    s = getenv("ZM_HMCS_EXPLICIT_LEVELS");
Packit Service c5cf8c
    if (s != NULL) {
Packit Service c5cf8c
        strcpy(tmp, s);
Packit Service c5cf8c
        explicit_levels = 1;
Packit Service c5cf8c
        char* token;
Packit Service c5cf8c
        token = strtok(tmp,",");
Packit Service c5cf8c
        while(token != NULL) {
Packit Service c5cf8c
            depths[idx] = atoi(token);
Packit Service c5cf8c
            if (idx == 0)
Packit Service c5cf8c
                assert(depths[idx] == 0 && "the first depth must be machine level (i.e., depth 0), run `hwloc-ls -s --no-io --no-icaches` and choose appropriate depth values");
Packit Service c5cf8c
            idx++;
Packit Service c5cf8c
            token = strtok(NULL,",");
Packit Service c5cf8c
        }
Packit Service c5cf8c
        assert(idx == max_levels);
Packit Service c5cf8c
    }
Packit Service c5cf8c
Packit Service c5cf8c
    hwloc_topology_init(&L->topo);
Packit Service c5cf8c
    hwloc_topology_load(L->topo);
Packit Service c5cf8c
Packit Service c5cf8c
    *max_threads = hwloc_get_nbobjs_by_type(L->topo, HWLOC_OBJ_PU);
Packit Service c5cf8c
Packit Service c5cf8c
    max_depth = hwloc_topology_get_depth(L->topo);
Packit Service c5cf8c
    assert(max_depth >= 2); /* At least Machine and Core levels exist */
Packit Service c5cf8c
Packit Service c5cf8c
    *particip_per_level = (int*) malloc(max_levels * sizeof(int));
Packit Service c5cf8c
    int prev_nobjs = -1;
Packit Service c5cf8c
    if(!explicit_levels) {
Packit Service c5cf8c
        for (int d = max_depth - 2; d > 1; d--) {
Packit Service c5cf8c
            int cur_nobjs = hwloc_get_nbobjs_by_depth(L->topo, d);
Packit Service c5cf8c
            /* Check if this level has a hierarchical impact */
Packit Service c5cf8c
            if(cur_nobjs != prev_nobjs) {
Packit Service c5cf8c
                prev_nobjs = cur_nobjs;
Packit Service c5cf8c
                (*particip_per_level)[levels] = (*max_threads)/cur_nobjs;
Packit Service c5cf8c
                levels++;
Packit Service c5cf8c
                if(levels >= max_levels - 1)
Packit Service c5cf8c
                    break;
Packit Service c5cf8c
            }
Packit Service c5cf8c
        }
Packit Service c5cf8c
        (*particip_per_level)[levels] = *max_threads;
Packit Service c5cf8c
        levels++;
Packit Service c5cf8c
    } else {
Packit Service c5cf8c
        for(int i = max_levels - 1; i >= 0; i--){
Packit Service c5cf8c
            int d = depths[i];
Packit Service c5cf8c
            int cur_nobjs = hwloc_get_nbobjs_by_depth(L->topo, d);
Packit Service c5cf8c
            /* Check if this level has a hierarchical impact */
Packit Service c5cf8c
            if(cur_nobjs != prev_nobjs) {
Packit Service c5cf8c
                prev_nobjs = cur_nobjs;
Packit Service c5cf8c
                (*particip_per_level)[levels] = (*max_threads)/cur_nobjs;
Packit Service c5cf8c
                levels++;
Packit Service c5cf8c
            } else {
Packit Service c5cf8c
                assert(0 && "plz choose levels that have a hierarchical impact");
Packit Service c5cf8c
            }
Packit Service c5cf8c
        }
Packit Service c5cf8c
    }
Packit Service c5cf8c
Packit Service c5cf8c
    L->levels = levels;
Packit Service c5cf8c
}
Packit Service c5cf8c
Packit Service c5cf8c
static void free_hierarchy(int* particip_per_level){
Packit Service c5cf8c
    free(particip_per_level);
Packit Service c5cf8c
}
Packit Service c5cf8c
Packit Service c5cf8c
static void* new_lock(){
Packit Service c5cf8c
Packit Service c5cf8c
    struct lock *L;
Packit Service c5cf8c
    posix_memalign((void **) &L, ZM_CACHELINE_SIZE, sizeof(struct lock));
Packit Service c5cf8c
Packit Service c5cf8c
    int max_threads;
Packit Service c5cf8c
    int *participants_at_level;
Packit Service c5cf8c
    set_hierarchy(L, &max_threads, &participants_at_level);
Packit Service c5cf8c
Packit Service c5cf8c
    // Total locks needed = participantsPerLevel[1] + participantsPerLevel[2] + .. participantsPerLevel[levels-1] + 1
Packit Service c5cf8c
    // Save affinity
Packit Service c5cf8c
    hwloc_cpuset_t cpuset = hwloc_bitmap_alloc();
Packit Service c5cf8c
    hwloc_get_cpubind(L->topo, cpuset, HWLOC_CPUBIND_THREAD);
Packit Service c5cf8c
Packit Service c5cf8c
    int total_locks_needed = 0;
Packit Service c5cf8c
    int levels = L->levels;
Packit Service c5cf8c
Packit Service c5cf8c
    for (int i=0; i < levels; i++) {
Packit Service c5cf8c
        total_locks_needed += max_threads / participants_at_level[i] ;
Packit Service c5cf8c
    }
Packit Service c5cf8c
    struct hnode ** lock_locations;
Packit Service c5cf8c
    posix_memalign((void **) &lock_locations, ZM_CACHELINE_SIZE, sizeof(struct hnode*) * total_locks_needed);
Packit Service c5cf8c
    struct leaf ** leaf_nodes;
Packit Service c5cf8c
    posix_memalign((void **) &leaf_nodes, ZM_CACHELINE_SIZE, sizeof(struct leaf*) * max_threads);
Packit Service c5cf8c
Packit Service c5cf8c
    int threshold = DEFAULT_THRESHOLD;
Packit Service c5cf8c
    char *s = getenv("ZM_HMCS_THRESHOLD");
Packit Service c5cf8c
    if (s != NULL)
Packit Service c5cf8c
        threshold = atoi(s);
Packit Service c5cf8c
Packit Service c5cf8c
    hwloc_obj_t obj;
Packit Service c5cf8c
    for(int tid = 0 ; tid < max_threads; tid ++){
Packit Service c5cf8c
        obj = hwloc_get_obj_by_type (L->topo, HWLOC_OBJ_PU, tid);
Packit Service c5cf8c
        hwloc_set_cpubind(L->topo, obj->cpuset, HWLOC_CPUBIND_THREAD);
Packit Service c5cf8c
        // Pin me to hw-thread-id = tid
Packit Service c5cf8c
        int last_lock_location_end = 0;
Packit Service c5cf8c
        for(int cur_level = 0 ; cur_level < levels; cur_level++){
Packit Service c5cf8c
            if (tid%participants_at_level[cur_level] == 0) {
Packit Service c5cf8c
                // master, initialize the lock
Packit Service c5cf8c
                int lock_location = last_lock_location_end + tid/participants_at_level[cur_level];
Packit Service c5cf8c
                last_lock_location_end += max_threads/participants_at_level[cur_level];
Packit Service c5cf8c
                struct hnode * cur_hnode = new_hnode();
Packit Service c5cf8c
                cur_hnode->threshold = threshold;
Packit Service c5cf8c
                cur_hnode->parent = NULL;
Packit Service c5cf8c
                cur_hnode->lock = ZM_NULL;
Packit Service c5cf8c
                lock_locations[lock_location] = cur_hnode;
Packit Service c5cf8c
            }
Packit Service c5cf8c
        }
Packit Service c5cf8c
    }
Packit Service c5cf8c
Packit Service c5cf8c
    // setup parents
Packit Service c5cf8c
    for(int tid = 0 ; tid < max_threads; tid ++){
Packit Service c5cf8c
        obj = hwloc_get_obj_by_type (L->topo, HWLOC_OBJ_PU, tid);
Packit Service c5cf8c
        hwloc_set_cpubind(L->topo, obj->cpuset, HWLOC_CPUBIND_THREAD);
Packit Service c5cf8c
        int last_lock_location_end = 0;
Packit Service c5cf8c
        for(int cur_level = 0 ; cur_level < levels - 1; cur_level++){
Packit Service c5cf8c
            if (tid%participants_at_level[cur_level] == 0) {
Packit Service c5cf8c
                int lock_location = last_lock_location_end + tid/participants_at_level[cur_level];
Packit Service c5cf8c
                last_lock_location_end += max_threads/participants_at_level[cur_level];
Packit Service c5cf8c
                int parentLockLocation = last_lock_location_end + tid/participants_at_level[cur_level+1];
Packit Service c5cf8c
                lock_locations[lock_location]->parent = lock_locations[parentLockLocation];
Packit Service c5cf8c
            }
Packit Service c5cf8c
        }
Packit Service c5cf8c
        leaf_nodes[tid] = (struct leaf*)new_leaf(lock_locations[tid/participants_at_level[0]], levels);
Packit Service c5cf8c
    }
Packit Service c5cf8c
    free(lock_locations);
Packit Service c5cf8c
    free_hierarchy(participants_at_level);
Packit Service c5cf8c
    // Restore affinity
Packit Service c5cf8c
    hwloc_set_cpubind(L->topo, cpuset, HWLOC_CPUBIND_THREAD);
Packit Service c5cf8c
    L->leaf_nodes = leaf_nodes;
Packit Service c5cf8c
Packit Service c5cf8c
    hwloc_bitmap_free(cpuset);
Packit Service c5cf8c
Packit Service c5cf8c
    return L;
Packit Service c5cf8c
}
Packit Service c5cf8c
Packit Service c5cf8c
static void search_nodes_rec(struct hnode *node, struct hnode **nodes_to_free, int *num_ptrs, int max_threads) {
Packit Service c5cf8c
    int i;
Packit Service c5cf8c
    if(node != NULL) {
Packit Service c5cf8c
        for(i = 0; i < *num_ptrs; i++) {
Packit Service c5cf8c
            if(node == nodes_to_free[i])
Packit Service c5cf8c
                break; /* already marked to be free'd */
Packit Service c5cf8c
        }
Packit Service c5cf8c
        if(i == *num_ptrs) { /* newly encountered pointer */
Packit Service c5cf8c
            nodes_to_free[*num_ptrs] = node;
Packit Service c5cf8c
            (*num_ptrs)++;
Packit Service c5cf8c
            assert(*num_ptrs < 2*max_threads);
Packit Service c5cf8c
        }
Packit Service c5cf8c
        search_nodes_rec(node->parent, nodes_to_free, num_ptrs, max_threads);
Packit Service c5cf8c
    }
Packit Service c5cf8c
}
Packit Service c5cf8c
Packit Service c5cf8c
static void free_lock(struct lock* L) {
Packit Service c5cf8c
    int max_threads = hwloc_get_nbobjs_by_type(L->topo, HWLOC_OBJ_PU);
Packit Service c5cf8c
    int num_ptrs = 0;
Packit Service c5cf8c
    struct hnode **nodes_to_free = (struct hnode**) malloc(2*max_threads*sizeof(struct hnode*));
Packit Service c5cf8c
    for (int tid = 0; tid < max_threads; tid++) {
Packit Service c5cf8c
        search_nodes_rec(L->leaf_nodes[tid]->cur_node, nodes_to_free, &num_ptrs, max_threads);
Packit Service c5cf8c
        free(L->leaf_nodes[tid]);
Packit Service c5cf8c
    }
Packit Service c5cf8c
    free(L->leaf_nodes);
Packit Service c5cf8c
    for(int i = 0; i < num_ptrs; i++)
Packit Service c5cf8c
        free(nodes_to_free[i]);
Packit Service c5cf8c
    free(nodes_to_free);
Packit Service c5cf8c
    hwloc_topology_destroy(L->topo);
Packit Service c5cf8c
    free(L);
Packit Service c5cf8c
}
Packit Service c5cf8c
Packit Service c5cf8c
static inline void hmcs_acquire(struct lock *L){
Packit Service c5cf8c
    if (zm_unlikely(tid == -1)) {
Packit Service c5cf8c
        check_affinity(L->topo);
Packit Service c5cf8c
        tid = get_hwthread_id(L->topo);
Packit Service c5cf8c
    }
Packit Service c5cf8c
    acquire_from_leaf(L->levels, L->leaf_nodes[tid]);
Packit Service c5cf8c
}
Packit Service c5cf8c
Packit Service c5cf8c
static inline void hmcs_tryacq(struct lock *L, int *success){
Packit Service c5cf8c
    if (zm_unlikely(tid == -1)) {
Packit Service c5cf8c
        check_affinity(L->topo);
Packit Service c5cf8c
        tid = get_hwthread_id(L->topo);
Packit Service c5cf8c
    }
Packit Service c5cf8c
    tryacq_from_leaf(L->levels, L->leaf_nodes[tid], success);
Packit Service c5cf8c
}
Packit Service c5cf8c
Packit Service c5cf8c
static inline void hmcs_release(struct lock *L){
Packit Service c5cf8c
    release_from_leaf(L->levels, L->leaf_nodes[tid]);
Packit Service c5cf8c
}
Packit Service c5cf8c
Packit Service c5cf8c
static inline int hmcs_nowaiters(struct lock *L){
Packit Service c5cf8c
    return nowaiters_from_leaf(L->levels, L->leaf_nodes[tid]);
Packit Service c5cf8c
}
Packit Service c5cf8c
Packit Service c5cf8c
int zm_hmcs_init(zm_hmcs_t * handle) {
Packit Service c5cf8c
    void *p = new_lock();
Packit Service c5cf8c
    *handle  = (zm_hmcs_t) p;
Packit Service c5cf8c
    return 0;
Packit Service c5cf8c
}
Packit Service c5cf8c
Packit Service c5cf8c
int zm_hmcs_destroy(zm_hmcs_t *L) {
Packit Service c5cf8c
    free_lock((struct lock*)(*L));
Packit Service c5cf8c
    return 0;
Packit Service c5cf8c
}
Packit Service c5cf8c
Packit Service c5cf8c
int zm_hmcs_acquire(zm_hmcs_t L){
Packit Service c5cf8c
    hmcs_acquire((struct lock*)L);
Packit Service c5cf8c
    return 0;
Packit Service c5cf8c
}
Packit Service c5cf8c
Packit Service c5cf8c
int zm_hmcs_tryacq(zm_hmcs_t L, int *success){
Packit Service c5cf8c
    hmcs_tryacq((struct lock*)L, success);
Packit Service c5cf8c
    return 0;
Packit Service c5cf8c
}
Packit Service c5cf8c
int zm_hmcs_release(zm_hmcs_t L){
Packit Service c5cf8c
    hmcs_release((struct lock*)L);
Packit Service c5cf8c
    return 0;
Packit Service c5cf8c
}
Packit Service c5cf8c
int zm_hmcs_nowaiters(zm_hmcs_t L){
Packit Service c5cf8c
    return hmcs_nowaiters((struct lock*)L);
Packit Service c5cf8c
}
Packit Service c5cf8c