#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "frag_list.h"
#ifdef ENABLE_STATS
static ucs_stats_class_t ucs_frag_list_stats_class = {
.name = "frag_list",
.num_counters = UCS_FRAG_LIST_STAT_LAST,
.class_id = UCS_STATS_CLASS_ID_INVALID,
.counter_names = {
[UCS_FRAG_LIST_STAT_GAPS] = "gaps",
[UCS_FRAG_LIST_STAT_GAP_LEN] = "gap_len",
[UCS_FRAG_LIST_STAT_GAP_OUT] = "gap_out",
[UCS_FRAG_LIST_STAT_BURSTS] = "bursts",
[UCS_FRAG_LIST_STAT_BURST_LEN] = "burst_len",
}
};
#endif
ucs_status_t ucs_frag_list_init(ucs_frag_list_sn_t initial_sn, ucs_frag_list_t *frag_list,
int max_holes
UCS_STATS_ARG(ucs_stats_node_t *stats_parent)
)
{
ucs_status_t status;
ucs_assert(max_holes == 0 || max_holes == -1);
frag_list->head_sn = initial_sn;
frag_list->elem_count = 0;
frag_list->list_count = 0;
frag_list->max_holes = max_holes;
ucs_queue_head_init(&frag_list->list);
ucs_queue_head_init(&frag_list->ready_list);
#ifdef ENABLE_STATS
frag_list->prev_sn = initial_sn;
#endif
status = UCS_STATS_NODE_ALLOC(&frag_list->stats,
&ucs_frag_list_stats_class,
stats_parent, "-%p", frag_list);
return status;
}
void ucs_frag_list_cleanup(ucs_frag_list_t *frag_list)
{
ucs_assert(frag_list->elem_count == 0);
ucs_assert(frag_list->list_count == 0);
ucs_assert(ucs_queue_is_empty(&frag_list->list));
ucs_assert(ucs_queue_is_empty(&frag_list->ready_list));
UCS_STATS_NODE_FREE(frag_list->stats);
}
static inline void
frag_list_replace_head(ucs_frag_list_t *frag_list, ucs_frag_list_elem_t *prevh,
ucs_frag_list_elem_t *h, ucs_frag_list_elem_t *new_h)
{
ucs_frag_list_elem_t UCS_V_UNUSED *e;
ucs_trace_data("replace=%u %u", (unsigned)h->head.first_sn-1,
(unsigned)h->head.last_sn);
new_h->head.first_sn = h->head.first_sn-1;
new_h->head.last_sn = h->head.last_sn;
if (prevh == NULL) {
e = ucs_queue_pull_elem_non_empty(&frag_list->list, ucs_frag_list_elem_t, list);
ucs_assert(e == h);
ucs_queue_push_head(&frag_list->list, &new_h->list);
} else {
prevh->list.next = &new_h->list;
new_h->list.next = h->list.next;
if (frag_list->list.ptail == &h->list.next) {
frag_list->list.ptail = &new_h->list.next;
}
}
ucs_queue_head_init(&new_h->head.list);
ucs_queue_splice(&new_h->head.list, &h->head.list);
ucs_queue_push_head(&new_h->head.list, &h->list);
}
static inline void frag_list_add_tail(ucs_frag_list_elem_t *h, ucs_frag_list_elem_t *elem)
{
h->head.last_sn++;
ucs_trace_data("add_tail=%u %u", (unsigned)h->head.first_sn, (unsigned)h->head.last_sn);
ucs_queue_push(&h->head.list, &elem->list);
}
static inline void frag_list_merge_heads(ucs_frag_list_t *head, ucs_frag_list_elem_t *h1, ucs_frag_list_elem_t *h2)
{
ucs_trace_data("merge_heads=%u %u", (unsigned)h1->head.first_sn, (unsigned)h2->head.last_sn);
h1->head.last_sn = h2->head.last_sn;
h1->list.next = h2->list.next;
if (head->list.ptail == &h2->list.next) {
head->list.ptail = &h1->list.next;
}
ucs_queue_push_head(&h2->head.list, &h2->list);
ucs_queue_splice(&h1->head.list, &h2->head.list);
}
static inline void frag_list_insert_head(ucs_frag_list_t *head,
ucs_frag_list_elem_t *prevh, ucs_frag_list_elem_t *h, ucs_frag_list_elem_t *new_h, ucs_frag_list_sn_t sn)
{
ucs_trace_data("insert_head=%u prevh=%p", (unsigned)sn, prevh);
new_h->head.first_sn = new_h->head.last_sn = sn;
ucs_queue_head_init(&new_h->head.list);
if (prevh == NULL) {
ucs_queue_push_head(&head->list, &new_h->list);
}
else {
prevh->list.next = &new_h->list;
new_h->list.next = &h->list;
}
}
static inline void frag_list_insert_tail(ucs_frag_list_t *head,
ucs_frag_list_elem_t *new_h,
ucs_frag_list_sn_t sn)
{
ucs_trace_data("insert_tail=%u", (unsigned)sn);
new_h->head.first_sn = new_h->head.last_sn = sn;
ucs_queue_head_init(&new_h->head.list);
ucs_queue_push(&head->list, &new_h->list);
}
ucs_frag_list_ooo_type_t
ucs_frag_list_insert_head(ucs_frag_list_t *head, ucs_frag_list_elem_t *elem,
ucs_frag_list_sn_t sn)
{
ucs_frag_list_elem_t *h;
if (!ucs_queue_is_empty(&head->list)) {
h = ucs_queue_head_elem_non_empty(&head->list, ucs_frag_list_elem_t, list);
if (UCS_FRAG_LIST_SN_CMP(sn, >=, h->head.first_sn)) {
return UCS_FRAG_LIST_INSERT_DUP;
}
}
else {
h = NULL;
}
head->head_sn++;
if (!ucs_queue_is_empty(&head->ready_list)) {
ucs_queue_push(&head->ready_list, &elem->list);
return UCS_FRAG_LIST_INSERT_READY;
}
if (h != NULL && UCS_FRAG_LIST_SN_CMP(h->head.first_sn, ==, sn + 1)) {
return UCS_FRAG_LIST_INSERT_FIRST;
}
return UCS_FRAG_LIST_INSERT_FAST;
}
ucs_frag_list_ooo_type_t
ucs_frag_list_insert_slow(ucs_frag_list_t *head, ucs_frag_list_elem_t *elem,
ucs_frag_list_sn_t sn)
{
ucs_frag_list_elem_t *h, *prevh, *nexth;
if (UCS_FRAG_LIST_SN_CMP(sn, ==, head->head_sn + 1)) {
return ucs_frag_list_insert_head(head, elem, sn);
}
if (UCS_FRAG_LIST_SN_CMP(sn, <=, head->head_sn)) {
return UCS_FRAG_LIST_INSERT_DUP;
}
if (head->max_holes == 0) {
return UCS_FRAG_LIST_INSERT_FAIL;
}
prevh = NULL;
ucs_queue_for_each(h, &head->list, list) {
if (UCS_FRAG_LIST_SN_CMP(sn, >=, h->head.first_sn) &&
UCS_FRAG_LIST_SN_CMP(sn, <=, h->head.last_sn)) {
return UCS_FRAG_LIST_INSERT_DUP;
}
if (UCS_FRAG_LIST_SN_CMP(sn+1, ==, h->head.first_sn)) {
frag_list_replace_head(head, prevh, h, elem);
head->elem_count++;
return UCS_FRAG_LIST_INSERT_SLOW;
}
if (UCS_FRAG_LIST_SN_CMP(h->head.last_sn + 1, ==, sn)) {
ucs_assertv(UCS_FRAG_LIST_SN_CMP(h->head.first_sn, <=, h->head.last_sn),
"h=%p first_sn=%u last_sn=%u", h, h->head.first_sn,
h->head.last_sn);
frag_list_add_tail(h, elem);
nexth = ucs_container_of(h->list.next, ucs_frag_list_elem_t, list);
if (!ucs_queue_is_tail(&head->list, &h->list) &&
(nexth->head.first_sn == (sn + 1))) {
frag_list_merge_heads(head, h, nexth);
head->list_count--;
}
head->elem_count++;
return UCS_FRAG_LIST_INSERT_SLOW;
}
if (UCS_FRAG_LIST_SN_CMP(sn, <, h->head.first_sn)) {
if (prevh) {
ucs_assert(UCS_FRAG_LIST_SN_CMP(prevh->head.last_sn+1, <, sn));
}
UCS_STATS_UPDATE_COUNTER(head->stats, UCS_FRAG_LIST_STAT_GAP_LEN,
prevh ? sn-prevh->head.last_sn : sn-head->head_sn);
UCS_STATS_UPDATE_COUNTER(head->stats, UCS_FRAG_LIST_STAT_GAPS, 1);
frag_list_insert_head(head, prevh, h, elem, sn);
head->elem_count++;
head->list_count++;
return UCS_FRAG_LIST_INSERT_SLOW;
}
ucs_assert(UCS_FRAG_LIST_SN_CMP(h->head.last_sn+1, <, sn));
prevh = h;
}
frag_list_insert_tail(head, elem, sn);
head->elem_count++;
head->list_count++;
UCS_STATS_UPDATE_COUNTER(head->stats, UCS_FRAG_LIST_STAT_GAP_LEN,
sn-head->head_sn);
UCS_STATS_UPDATE_COUNTER(head->stats, UCS_FRAG_LIST_STAT_GAPS, 1);
return UCS_FRAG_LIST_INSERT_SLOW;
}
ucs_frag_list_elem_t *ucs_frag_list_pull_slow(ucs_frag_list_t *head)
{
ucs_frag_list_elem_t *h;
h = ucs_queue_head_elem_non_empty(&head->list, ucs_frag_list_elem_t, list);
if (UCS_FRAG_LIST_SN_CMP(h->head.first_sn, !=, head->head_sn+1)) {
ucs_trace_data("first_sn(%u) != head_sn(%u) + 1", (unsigned)h->head.first_sn,
(unsigned)head->head_sn);
return NULL;
}
ucs_trace_data("ready list %d to %d", (unsigned)head->head_sn,
(unsigned)h->head.last_sn);
head->head_sn = h->head.last_sn;
head->elem_count--;
head->list_count--;
h = ucs_queue_pull_elem_non_empty(&head->list, ucs_frag_list_elem_t, list);
ucs_queue_splice(&head->ready_list, &h->head.list);
return h;
}
void ucs_frag_list_dump(ucs_frag_list_t *head, int how)
{
ucs_frag_list_elem_t *h, *e;
int list_count, elem_count;
int cnt;
list_count = 0;
elem_count = 0;
ucs_queue_for_each(e, &head->ready_list, list) {
elem_count++;
}
ucs_queue_for_each(h, &head->list, list) {
list_count++;
cnt = 0;
ucs_queue_for_each(e, &h->head.list, list) {
cnt++;
elem_count++;
}
elem_count++;
if (how == 1) {
ucs_trace_data("%d: %d-%d %d/%d", list_count, h->head.first_sn,
h->head.last_sn, h->head.last_sn - h->head.first_sn,
cnt);
}
}
if (how == 1) {
ucs_trace_data("elem count(expected/real)=%d/%d list_count(expected/real)=%d/%d\n",
head->elem_count, elem_count,
head->list_count, list_count);
}
ucs_assert(head->elem_count == elem_count);
ucs_assert(head->list_count == list_count);
}