Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package libqb for openSUSE:Factory checked in at 2021-11-18 10:33:14 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/libqb (Old) and /work/SRC/openSUSE:Factory/.libqb.new.1895 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "libqb" Thu Nov 18 10:33:14 2021 rev:31 rq:931807 version:2.0.4+20211112.a2691b9 Changes: -------- --- /work/SRC/openSUSE:Factory/libqb/libqb.changes 2021-10-11 16:48:50.846189828 +0200 +++ /work/SRC/openSUSE:Factory/.libqb.new.1895/libqb.changes 2021-11-18 10:33:37.555895807 +0100 @@ -1,0 +2,9 @@ +Tue Nov 16 08:42:20 UTC 2021 - Yan Gao <y...@suse.com> + +- Update to version 2.0.4+20211112.a2691b9 (v2.0.4): +- poll: Don't log in a signal handler (gh#ClusterLabs/libqb##447) +- Fix pthread returns (gh#ClusterLabs/libqb#444) +- doxygen2man: print structure descriptions (gh#ClusterLabs/libqb#443) +- Implement heap based timer list (gh#ClusterLabs/libqb#439) + +------------------------------------------------------------------- Old: ---- libqb-2.0.3+20210303.404adbc.tar.xz New: ---- libqb-2.0.4+20211112.a2691b9.tar.xz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ libqb.spec ++++++ --- /var/tmp/diff_new_pack.cKb8C7/_old 2021-11-18 10:33:38.075896292 +0100 +++ /var/tmp/diff_new_pack.cKb8C7/_new 2021-11-18 10:33:38.079896295 +0100 @@ -21,7 +21,7 @@ %bcond_without doxygen2man Name: libqb -Version: 2.0.3+20210303.404adbc +Version: 2.0.4+20211112.a2691b9 Release: 0 Summary: An IPC library for high performance servers License: LGPL-2.1-or-later ++++++ _service ++++++ --- /var/tmp/diff_new_pack.cKb8C7/_old 2021-11-18 10:33:38.103896317 +0100 +++ /var/tmp/diff_new_pack.cKb8C7/_new 2021-11-18 10:33:38.103896317 +0100 @@ -8,10 +8,10 @@ To update to a new release, change "revision" to the desired git commit hash and bump "version" if necessary - <param name="version">2.0.3</param> + <param name="version">2.0.4</param> --> - <param name="versionformat">2.0.3+%cd.%h</param> - <param name="revision">v2.0.3</param> + <param name="versionformat">2.0.4+%cd.%h</param> + <param name="revision">v2.0.4</param> <param name="changesgenerate">enable</param> </service> ++++++ _servicedata ++++++ --- /var/tmp/diff_new_pack.cKb8C7/_old 2021-11-18 10:33:38.119896332 +0100 +++ /var/tmp/diff_new_pack.cKb8C7/_new 2021-11-18 10:33:38.119896332 +0100 @@ -1,6 +1,6 @@ <servicedata> <service name="tar_scm"> <param name="url">git://github.com/ClusterLabs/libqb.git</param> - <param name="changesrevision">404adbcd998ec83643e47d92b3ea8d9c3970e68b</param> + <param name="changesrevision">a2691b96188033b5ad5c08871982048ae1f4f4e8</param> </service> </servicedata> \ No newline at end of file ++++++ libqb-2.0.3+20210303.404adbc.tar.xz -> libqb-2.0.4+20211112.a2691b9.tar.xz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libqb-2.0.3+20210303.404adbc/doxygen2man/cstring.c new/libqb-2.0.4+20211112.a2691b9/doxygen2man/cstring.c --- old/libqb-2.0.3+20210303.404adbc/doxygen2man/cstring.c 2021-03-03 09:32:09.000000000 +0100 +++ new/libqb-2.0.4+20211112.a2691b9/doxygen2man/cstring.c 2021-11-12 14:18:47.000000000 +0100 @@ -41,7 +41,7 @@ char *cstring_to_chars(cstring_t cstring) { - struct cstring_header *h = (struct cstring_header *)(char *)cstring; + struct cstring_header *h = (struct cstring_header *)cstring; if (!h) { return NULL; @@ -51,9 +51,22 @@ return strdup(h->the_string); } +size_t cstring_len(cstring_t cstring) +{ + struct cstring_header *h = (struct cstring_header *)cstring; + + if (!h) { + return 0; + } + + assert(h->checker == CHECKER_WORD); + return h->used; +} + + cstring_t cstring_append_chars(cstring_t cstring, const char *newstring) { - struct cstring_header *h = (struct cstring_header *)(char *)cstring; + struct cstring_header *h = (struct cstring_header *)cstring; size_t newlen; if (!h) { @@ -74,7 +87,7 @@ } cstring = tmp; - h = (struct cstring_header *)(char *)cstring; + h = (struct cstring_header *)cstring; h->allocated = new_allocsize; } strncat(h->the_string, newstring, h->allocated - h->used -1); @@ -85,7 +98,7 @@ cstring_t cstring_append_cstring(cstring_t cstring, cstring_t newstring) { /* Just check the newstring - cstring_append_chars() will check the target */ - struct cstring_header *h = (struct cstring_header *)(char *)newstring; + struct cstring_header *h = (struct cstring_header *)newstring; if (!h) { return NULL; @@ -106,7 +119,7 @@ void cstring_free(cstring_t cstring) { - struct cstring_header *h = (struct cstring_header *)(char *)cstring; + struct cstring_header *h = (struct cstring_header *)cstring; if (!h) { return; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libqb-2.0.3+20210303.404adbc/doxygen2man/cstring.h new/libqb-2.0.4+20211112.a2691b9/doxygen2man/cstring.h --- old/libqb-2.0.3+20210303.404adbc/doxygen2man/cstring.h 2021-03-03 09:32:09.000000000 +0100 +++ new/libqb-2.0.4+20211112.a2691b9/doxygen2man/cstring.h 2021-11-12 14:18:47.000000000 +0100 @@ -10,5 +10,6 @@ cstring_t cstring_append_chars(cstring_t cstring, const char *newstring); cstring_t cstring_append_cstring(cstring_t cstring, cstring_t newstring); void cstring_free(cstring_t cstring); +size_t cstring_len(cstring_t cstring); #endif diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libqb-2.0.3+20210303.404adbc/doxygen2man/doxygen2man.c new/libqb-2.0.4+20211112.a2691b9/doxygen2man/doxygen2man.c --- old/libqb-2.0.3+20210303.404adbc/doxygen2man/doxygen2man.c 2021-03-03 09:32:09.000000000 +0100 +++ new/libqb-2.0.4+20211112.a2691b9/doxygen2man/doxygen2man.c 2021-11-12 14:18:47.000000000 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018-2020 Red Hat, Inc. All rights reserved. + * Copyright (C) 2018-2021 Red Hat, Inc. All rights reserved. * * Author: Christine Caulfield <ccaul...@redhat.com> * @@ -43,6 +43,9 @@ */ #define LINE_LENGTH 80 +/* Similar - but for structure member comments */ +#define STRUCT_COMMENT_LENGTH 50 + static int print_ascii = 1; static int print_man = 0; static int print_params = 0; @@ -87,9 +90,10 @@ struct qb_list_head list; }; -static cstring_t get_texttree(int *type, xmlNode *cur_node, char **returntext, char **notetext); +static cstring_t get_texttree(int *type, xmlNode *cur_node, char **returntext, char **notetext, int add_nl); static void traverse_node(xmlNode *parentnode, const char *leafname, void (do_members(xmlNode*, void*)), void *arg); static cstring_t get_text(xmlNode *cur_node, char **returntext, char **notetext); +static void man_print_long_string(FILE *manfile, char *text); static void free_paraminfo(struct param_info *pi) { @@ -189,7 +193,8 @@ sub_tag->children->next->children) { paramname = (char*)sub_tag->children->next->children->content; } - if (sub_tag->type == XML_ELEMENT_NODE && strcmp((char *)sub_tag->name, "parameterdescription") == 0 && + if (sub_tag->type == XML_ELEMENT_NODE && + strcmp((char *)sub_tag->name, "parameterdescription") == 0 && paramname && sub_tag->children->next->children) { paramdesc = (char*)sub_tag->children->next->children->content; @@ -353,7 +358,7 @@ buffer = cstring_append_chars(buffer, "\n"); cstring_free(tmp); - tmp = get_texttree(&type,this_tag, NULL, NULL); + tmp = get_texttree(&type,this_tag, NULL, NULL, 1); buffer = cstring_append_cstring(buffer, tmp); buffer = cstring_append_chars(buffer, "\n"); } @@ -392,12 +397,12 @@ for (this_tag = cur_node->children; this_tag; this_tag = this_tag->next) { if (strcmp((char*)this_tag->name, "detaileddescription") == 0) { - cstring_t desc = get_texttree(NULL, this_tag, NULL, NULL); + cstring_t desc = get_texttree(NULL, this_tag, NULL, NULL, 1); si->description = cstring_to_chars(desc); cstring_free(desc); } if (strcmp((char*)this_tag->name, "briefdescription") == 0) { - cstring_t brief = get_texttree(NULL, this_tag, NULL, NULL); + cstring_t brief = get_texttree(NULL, this_tag, NULL, NULL, 1); si->brief_description = cstring_to_chars(brief); } } @@ -422,10 +427,11 @@ { xmlNode *this_tag; struct struct_info *si=arg; - struct param_info *pi; + struct param_info *pi = NULL; char fullname[1024]; char *type = NULL; char *name = NULL; + char *desc = NULL; const char *args=""; for (this_tag = cur_node->children; this_tag; this_tag = this_tag->next) { @@ -444,6 +450,13 @@ if (this_tag->children && strcmp((char*)this_tag->name, "argsstring") == 0) { args = (char*)this_tag->children->content; } + if (this_tag->children && strcmp((char*)this_tag->name, "detaileddescription") == 0) { + cstring_t *desc_cs = get_texttree(NULL, this_tag, NULL, NULL, 0); + if (cstring_len(desc_cs) > 1) { + desc = cstring_to_chars(desc_cs); + } + cstring_free(desc_cs); + } } if (name) { @@ -452,10 +465,14 @@ snprintf(fullname, sizeof(fullname), "%s%s", name, args); pi->paramtype = type?strdup(type):strdup(""); pi->paramname = strdup(fullname); - pi->paramdesc = NULL; + pi->paramdesc = desc; qb_list_add_tail(&pi->list, &si->params_list); } } + /* Tidy */ + if (!name || !pi) { + free(desc); + } } static int read_structure_from_xml(const char *refid, const char *name) @@ -517,7 +534,31 @@ return buffer; } -static void print_param(FILE *manfile, struct param_info *pi, int field_width, int bold, const char *delimiter) +/* + * Print a structure comment that would be too long + * to fit after the structure member, in a style ... + * well, in a style like this! + */ +static void print_long_structure_comment(FILE *manfile, char *comment) +{ + char *ptr = strtok(comment, " "); + int column = 7; + + fprintf(manfile, "\\fP /*"); + fprintf(manfile, "\n *"); + while (ptr) { + column += strlen(ptr)+1; + if (column > 80) { + fprintf(manfile, "\n *"); + column = 7; + } + fprintf(manfile, " %s", ptr); + ptr = strtok(NULL, " "); + } + fprintf(manfile, "\n */\n"); +} + +static void print_param(FILE *manfile, struct param_info *pi, int type_field_width, int name_field_width, int bold, const char *delimiter) { const char *asterisks = " "; char *type = pi->paramtype; @@ -542,10 +583,32 @@ } } - fprintf(manfile, " %s%-*s%s%s\\fI%s\\fP%s\n", - bold?"\\fB":"", field_width, type, - asterisks, bold?"\\fP":"", - pi->paramname?pi->paramname:"", delimiter); + /* Print structure description if available */ + if (pi->paramdesc) { + /* Too long to go on the same line? */ + if (strlen(pi->paramdesc) > STRUCT_COMMENT_LENGTH) { + print_long_structure_comment(manfile, pi->paramdesc); + fprintf(manfile, " %s%-*s%s%s\\fI%s\\fP%s\n", + bold?"\\fB":"", type_field_width, type, + asterisks, bold?"\\fP":"", + pi->paramname?pi->paramname:"", delimiter); + } else { + /* Pad out so they all line up */ + int pad_length = name_field_width - + (pi->paramname?strlen(pi->paramname):0) + 1; + fprintf(manfile, " %s%-*s%s%s\\fI%s\\fP%s\\fR%*s/* %s*/\n", + bold?"\\fB":"", type_field_width, type, + asterisks, bold?"\\fP":"", + pi->paramname?pi->paramname:"", delimiter, + pad_length, " ", + pi->paramdesc); + } + } else { + fprintf(manfile, " %s%-*s%s%s\\fI%s\\fP%s\n", + bold?"\\fB":"", type_field_width, type, + asterisks, bold?"\\fP":"", + pi->paramname?pi->paramname:"", delimiter); + } if (type != pi->paramtype) { free(type); @@ -557,9 +620,9 @@ struct param_info *pi; struct qb_list_head *iter; unsigned int max_param_length=0; + unsigned int max_param_name_length=0; fprintf(manfile, ".nf\n"); - fprintf(manfile, "\\fB\n"); if (si->brief_description) { fprintf(manfile, "%s\n", si->brief_description); @@ -573,8 +636,12 @@ if (strlen(pi->paramtype) > max_param_length) { max_param_length = strlen(pi->paramtype); } + if (strlen(pi->paramname) > max_param_name_length) { + max_param_name_length = strlen(pi->paramname); + } } + fprintf(manfile, "\\fB\n"); if (si->kind == STRUCTINFO_STRUCT) { fprintf(manfile, "struct %s {\n", si->structname); } else if (si->kind == STRUCTINFO_ENUM) { @@ -582,10 +649,12 @@ } else { fprintf(manfile, "%s {\n", si->structname); } + fprintf(manfile, "\\fR\n"); qb_list_for_each(iter, &si->params_list) { + fprintf(manfile, "\\fB\n"); pi = qb_list_entry(iter, struct param_info, list); - print_param(manfile, pi, max_param_length, 0,";"); + print_param(manfile, pi, max_param_length, max_param_name_length, 1, ";"); } fprintf(manfile, "};\n"); @@ -593,7 +662,7 @@ fprintf(manfile, ".fi\n"); } -cstring_t get_texttree(int *type, xmlNode *cur_node, char **returntext, char **notetext) +cstring_t get_texttree(int *type, xmlNode *cur_node, char **returntext, char **notetext, int add_nl) { xmlNode *this_tag; cstring_t tmp; @@ -604,7 +673,9 @@ if (this_tag->type == XML_ELEMENT_NODE && strcmp((char *)this_tag->name, "para") == 0) { tmp = get_text(this_tag, returntext, notetext); buffer = cstring_append_cstring(buffer, tmp); - buffer = cstring_append_chars(buffer, "\n"); + if (add_nl) { + buffer = cstring_append_chars(buffer, "\n"); + } cstring_free(tmp); } @@ -779,8 +850,8 @@ qb_list_for_each(iter, param_map) { pi = qb_list_entry(iter, struct param_info, list); - if (pi->paramtype[0] != '\0') { //CC - print_param(manfile, pi, max_param_type_len, 1, ++param_num < param_count?",":""); + if (pi->paramtype[0] != '\0') { + print_param(manfile, pi, max_param_type_len, 0, 1, ++param_num < param_count?",":""); } } @@ -1007,7 +1078,7 @@ name = strdup((char *)this_tag->children->content); if (this_tag->type == XML_ELEMENT_NODE && strcmp((char *)this_tag->name, "briefdescription") == 0) { - cstring_t tmp = get_texttree(&type, this_tag, &returntext, ¬etext); + cstring_t tmp = get_texttree(&type, this_tag, &returntext, ¬etext, 1); if (!brief) { brief = cstring_to_chars(tmp); } else { @@ -1016,7 +1087,7 @@ cstring_free(tmp); } if (this_tag->type == XML_ELEMENT_NODE && strcmp((char *)this_tag->name, "detaileddescription") == 0) { - cstring_t tmp = get_texttree(&type, this_tag, &returntext, ¬etext); + cstring_t tmp = get_texttree(&type, this_tag, &returntext, ¬etext, 1); if (!detailed) { detailed = cstring_to_chars(tmp); } else { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libqb-2.0.3+20210303.404adbc/include/tlist.h new/libqb-2.0.4+20211112.a2691b9/include/tlist.h --- old/libqb-2.0.3+20210303.404adbc/include/tlist.h 2021-03-03 09:32:09.000000000 +0100 +++ new/libqb-2.0.4+20211112.a2691b9/include/tlist.h 2021-11-12 14:18:47.000000000 +0100 @@ -1,7 +1,8 @@ /* - * Copyright (c) 2006-2007, 2009 Red Hat, Inc. + * Copyright (c) 2006-2007, 2009-2021 Red Hat, Inc. * - * Author: Steven Dake <sd...@redhat.com> + * Author: Jan Friesse <jfrie...@redhat.com> + * Steven Dake <sd...@redhat.com> * * This file is part of libqb. * @@ -35,52 +36,284 @@ static int64_t timerlist_hertz; struct timerlist { - struct qb_list_head timer_head; + struct timerlist_timer **heap_entries; + size_t allocated; + size_t size; pthread_mutex_t list_mutex; }; struct timerlist_timer { - struct qb_list_head list; uint64_t expire_time; int32_t is_absolute_timer; void (*timer_fn) (void *data); void *data; timer_handle handle_addr; + size_t heap_pos; }; +/* + * Heap helper functions + */ +static inline size_t +timerlist_heap_index_left(size_t index) +{ + + return (2 * index + 1); +} + +static inline size_t +timerlist_heap_index_right(size_t index) +{ + + return (2 * index + 2); +} + +static inline size_t +timerlist_heap_index_parent(size_t index) +{ + + return ((index - 1) / 2); +} + +static inline void +timerlist_heap_entry_set(struct timerlist *timerlist, size_t item_pos, struct timerlist_timer *timer) +{ + + assert(item_pos < timerlist->size); + + timerlist->heap_entries[item_pos] = timer; + timerlist->heap_entries[item_pos]->heap_pos = item_pos; +} + +static inline struct timerlist_timer * +timerlist_heap_entry_get(struct timerlist *timerlist, size_t item_pos) +{ + + assert(item_pos < timerlist->size); + + return (timerlist->heap_entries[item_pos]); +} + +static inline int +timerlist_entry_cmp(const struct timerlist_timer *t1, const struct timerlist_timer *t2) +{ + + if (t1->expire_time == t2->expire_time) { + return (0); + } else if (t1->expire_time < t2->expire_time) { + return (-1); + } else { + return (1); + } +} + +static inline void +timerlist_heap_sift_up(struct timerlist *timerlist, size_t item_pos) +{ + size_t parent_pos; + struct timerlist_timer *parent_timer; + struct timerlist_timer *timer; + + timer = timerlist_heap_entry_get(timerlist, item_pos); + + parent_pos = timerlist_heap_index_parent(item_pos); + + while (item_pos > 0 && + (parent_timer = timerlist_heap_entry_get(timerlist, parent_pos), + timerlist_entry_cmp(parent_timer, timer) > 0)) { + /* + * Swap item and parent + */ + timerlist_heap_entry_set(timerlist, parent_pos, timer); + timerlist_heap_entry_set(timerlist, item_pos, parent_timer); + + item_pos = parent_pos; + parent_pos = timerlist_heap_index_parent(item_pos); + } +} + +static inline void +timerlist_heap_sift_down(struct timerlist *timerlist, size_t item_pos) +{ + int cont; + size_t left_pos, right_pos, smallest_pos; + struct timerlist_timer *left_entry; + struct timerlist_timer *right_entry; + struct timerlist_timer *smallest_entry; + struct timerlist_timer *tmp_entry; + + cont = 1; + + while (cont) { + smallest_pos = item_pos; + left_pos = timerlist_heap_index_left(item_pos); + right_pos = timerlist_heap_index_right(item_pos); + + smallest_entry = timerlist_heap_entry_get(timerlist, smallest_pos); + + if (left_pos < timerlist->size && + (left_entry = timerlist_heap_entry_get(timerlist, left_pos), + timerlist_entry_cmp(left_entry, smallest_entry) < 0)) { + smallest_entry = left_entry; + smallest_pos = left_pos; + } + + if (right_pos < timerlist->size && + (right_entry = timerlist_heap_entry_get(timerlist, right_pos), + timerlist_entry_cmp(right_entry, smallest_entry) < 0)) { + smallest_entry = right_entry; + smallest_pos = right_pos; + } + + if (smallest_pos == item_pos) { + /* + * Item is smallest (or has no children) -> heap property is restored + */ + cont = 0; + } else { + /* + * Swap item with smallest child + */ + tmp_entry = timerlist_heap_entry_get(timerlist, item_pos); + timerlist_heap_entry_set(timerlist, item_pos, smallest_entry); + timerlist_heap_entry_set(timerlist, smallest_pos, tmp_entry); + + item_pos = smallest_pos; + } + } +} + +static inline void +timerlist_heap_delete(struct timerlist *timerlist, struct timerlist_timer *entry) +{ + size_t entry_pos; + struct timerlist_timer *replacement_entry; + int cmp_entries; + + entry_pos = entry->heap_pos; + entry->heap_pos = (~(size_t)0); + + /* + * Swap element with last element + */ + replacement_entry = timerlist_heap_entry_get(timerlist, timerlist->size - 1); + timerlist_heap_entry_set(timerlist, entry_pos, replacement_entry); + + /* + * And "remove" last element (= entry) + */ + timerlist->size--; + + /* + * Up (or down) heapify based on replacement item size + */ + cmp_entries = timerlist_entry_cmp(replacement_entry, entry); + + if (cmp_entries < 0) { + timerlist_heap_sift_up(timerlist, entry_pos); + } else if (cmp_entries > 0) { + timerlist_heap_sift_down(timerlist, entry_pos); + } +} + +/* + * Check if heap is valid. + * - Shape property is always fullfiled because of storage in array + * - Check heap property + */ +static inline int +timerlist_debug_is_valid_heap(struct timerlist *timerlist) +{ + size_t i; + size_t left_pos, right_pos; + struct timerlist_timer *left_entry; + struct timerlist_timer *right_entry; + struct timerlist_timer *cur_entry; + + for (i = 0; i < timerlist->size; i++) { + cur_entry = timerlist_heap_entry_get(timerlist, i); + + left_pos = timerlist_heap_index_left(i); + right_pos = timerlist_heap_index_right(i); + + if (left_pos < timerlist->size && + (left_entry = timerlist_heap_entry_get(timerlist, left_pos), + timerlist_entry_cmp(left_entry, cur_entry) < 0)) { + return (0); + } + + if (right_pos < timerlist->size && + (right_entry = timerlist_heap_entry_get(timerlist, right_pos), + timerlist_entry_cmp(right_entry, cur_entry) < 0)) { + return (0); + } + } + + return (1); +} + +/* + * Main functions implementation + */ static inline void timerlist_init(struct timerlist *timerlist) { - qb_list_init(&timerlist->timer_head); + + memset(timerlist, 0, sizeof(*timerlist)); + + timerlist->heap_entries = NULL; pthread_mutex_init(&timerlist->list_mutex, NULL); timerlist_hertz = qb_util_nano_monotonic_hz(); } +static inline void timerlist_destroy(struct timerlist *timerlist) +{ + size_t zi; + + pthread_mutex_destroy(&timerlist->list_mutex); + + for (zi = 0; zi < timerlist->size; zi++) { + free(timerlist->heap_entries[zi]); + } + free(timerlist->heap_entries); +} + static inline int32_t timerlist_add(struct timerlist *timerlist, struct timerlist_timer *timer) { - struct qb_list_head *timer_list = 0; - struct timerlist_timer *timer_from_list; - int32_t found = QB_FALSE; + size_t new_size; + struct timerlist_timer **new_heap_entries; + int32_t res = 0; - if (pthread_mutex_lock(&timerlist->list_mutex)) { - return -errno; + if ( (res=pthread_mutex_lock(&timerlist->list_mutex))) { + return -res; } - qb_list_for_each(timer_list, &timerlist->timer_head) { - timer_from_list = qb_list_entry(timer_list, - struct timerlist_timer, list); + /* + * Check that heap array is large enough + */ + if (timerlist->size + 1 > timerlist->allocated) { + new_size = (timerlist->allocated + 1) * 2; - if (timer_from_list->expire_time > timer->expire_time) { - qb_list_add_tail(&timer->list, timer_list); - found = QB_TRUE; - break; /* for timer iteration */ + new_heap_entries = realloc(timerlist->heap_entries, + new_size * sizeof(timerlist->heap_entries[0])); + if (new_heap_entries == NULL) { + res = -errno; + + goto cleanup; } + + timerlist->allocated = new_size; + timerlist->heap_entries = new_heap_entries; } - if (found == QB_FALSE) { - qb_list_add_tail(&timer->list, &timerlist->timer_head); - } + + timerlist->size++; + + timerlist_heap_entry_set(timerlist, timerlist->size - 1, timer); + timerlist_heap_sift_up(timerlist, timerlist->size - 1); + +cleanup: pthread_mutex_unlock(&timerlist->list_mutex); - return 0; + return res; } static inline int32_t timerlist_add_duration(struct timerlist *timerlist, @@ -94,7 +327,8 @@ timer = (struct timerlist_timer *)malloc(sizeof(struct timerlist_timer)); - if (timer == 0) { + + if (timer == NULL) { return -ENOMEM; } @@ -113,15 +347,23 @@ return (0); } -static inline void timerlist_del(struct timerlist *timerlist, +static inline int32_t timerlist_del(struct timerlist *timerlist, timer_handle _timer_handle) { struct timerlist_timer *timer = (struct timerlist_timer *)_timer_handle; + int res; + + if ( (res=pthread_mutex_lock(&timerlist->list_mutex))) { + return -res; + } memset(timer->handle_addr, 0, sizeof(struct timerlist_timer *)); - qb_list_del(&timer->list); - qb_list_init(&timer->list); + + timerlist_heap_delete(timerlist, timer); free(timer); + + pthread_mutex_unlock(&timerlist->list_mutex); + return 0; } static inline uint64_t timerlist_expire_time(struct timerlist @@ -140,8 +382,8 @@ struct timerlist_timer *timer = (struct timerlist_timer *)_timer_handle; memset(timer->handle_addr, 0, sizeof(struct timerlist_timer *)); - qb_list_del(&timer->list); - qb_list_init(&timer->list); + + timerlist_heap_delete(timerlist, timer); } static inline void timerlist_post_dispatch(struct timerlist *timerlist, @@ -162,14 +404,27 @@ volatile uint64_t msec_duration_to_expire; /* + * There is really no reasonable value to return when mutex lock fails + */ + if (pthread_mutex_lock(&timerlist->list_mutex)) { + return (-1); + } + + /* * empty list, no expire */ - if (qb_list_empty(&timerlist->timer_head)) { + if (timerlist->size == 0) { + pthread_mutex_unlock(&timerlist->list_mutex); + return (-1); } - timer_from_list = qb_list_first_entry(&timerlist->timer_head, - struct timerlist_timer, list); + timer_from_list = timerlist_heap_entry_get(timerlist, 0); + + /* + * Mutex is no longer needed + */ + pthread_mutex_unlock(&timerlist->list_mutex); if (timer_from_list->is_absolute_timer) { current_time = qb_util_nano_from_epoch_get(); @@ -193,38 +448,43 @@ /* * Expires any timers that should be expired */ -static inline void timerlist_expire(struct timerlist *timerlist) +static inline int32_t timerlist_expire(struct timerlist *timerlist) { - struct timerlist_timer *timer_from_list; - struct qb_list_head *pos; - struct qb_list_head *next; + struct timerlist_timer *timer; uint64_t current_time_from_epoch; uint64_t current_monotonic_time; uint64_t current_time; + int res; current_monotonic_time = qb_util_nano_current_get(); current_time_from_epoch = qb_util_nano_from_epoch_get(); - qb_list_for_each_safe(pos, next, &timerlist->timer_head) { + if ( (res=pthread_mutex_lock(&timerlist->list_mutex))) { + return -res; + } - timer_from_list = qb_list_entry(pos, - struct timerlist_timer, list); + while (timerlist->size > 0) { + timer = timerlist_heap_entry_get(timerlist, 0); current_time = - (timer_from_list-> + (timer-> is_absolute_timer ? current_time_from_epoch : current_monotonic_time); - if (timer_from_list->expire_time < current_time) { + if (timer->expire_time < current_time) { - timerlist_pre_dispatch(timerlist, timer_from_list); + timerlist_pre_dispatch(timerlist, timer); - timer_from_list->timer_fn(timer_from_list->data); + timer->timer_fn(timer->data); - timerlist_post_dispatch(timerlist, timer_from_list); + timerlist_post_dispatch(timerlist, timer); } else { break; /* for timer iteration */ } } + + pthread_mutex_unlock(&timerlist->list_mutex); + + return (0); } #endif /* QB_TLIST_H_DEFINED */ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libqb-2.0.3+20210303.404adbc/lib/Makefile.am new/libqb-2.0.4+20211112.a2691b9/lib/Makefile.am --- old/libqb-2.0.3+20210303.404adbc/lib/Makefile.am 2021-03-03 09:32:09.000000000 +0100 +++ new/libqb-2.0.4+20211112.a2691b9/lib/Makefile.am 2021-11-12 14:18:47.000000000 +0100 @@ -30,7 +30,7 @@ lib_LTLIBRARIES = libqb.la -libqb_la_LDFLAGS = -version-info 102:0:2 +libqb_la_LDFLAGS = -version-info 102:1:2 source_to_lint = util.c hdb.c ringbuffer.c ringbuffer_helper.c \ array.c loop.c loop_poll.c loop_job.c \ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libqb-2.0.3+20210303.404adbc/lib/loop_poll.c new/libqb-2.0.4+20211112.a2691b9/lib/loop_poll.c --- old/libqb-2.0.3+20210303.404adbc/lib/loop_poll.c 2021-03-03 09:32:09.000000000 +0100 +++ new/libqb-2.0.4+20211112.a2691b9/lib/loop_poll.c 2021-11-12 14:18:47.000000000 +0100 @@ -475,12 +475,8 @@ res = write(pipe_fds[1], &sig, sizeof(int32_t)); if (res == -1 && errno == EAGAIN) { goto try_again; - } else if (res != sizeof(int32_t)) { - qb_util_log(LOG_ERR, - "failed to write signal to pipe [%d]", res); } } - qb_util_log(LOG_TRACE, "got real signal [%d] sent to pipe", sig); } static void diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libqb-2.0.3+20210303.404adbc/lib/loop_timerlist.c new/libqb-2.0.4+20211112.a2691b9/lib/loop_timerlist.c --- old/libqb-2.0.3+20210303.404adbc/lib/loop_timerlist.c 2021-03-03 09:32:09.000000000 +0100 +++ new/libqb-2.0.4+20211112.a2691b9/lib/loop_timerlist.c 2021-11-12 14:18:47.000000000 +0100 @@ -76,7 +76,9 @@ { struct qb_timer_source *ts = (struct qb_timer_source *)s; expired_timers = 0; - timerlist_expire(&ts->timerlist); + if (timerlist_expire(&ts->timerlist) != 0) { + qb_util_log(LOG_ERR, "timerlist_expire failed"); + } return expired_timers; } @@ -115,6 +117,8 @@ { struct qb_timer_source *my_src = (struct qb_timer_source *)l->timer_source; + + timerlist_destroy(&my_src->timerlist); qb_array_free(my_src->timers); free(l->timer_source); } @@ -183,6 +187,7 @@ struct qb_loop_timer *t; struct qb_timer_source *my_src; int32_t i; + int res; struct qb_loop *l = lp; if (l == NULL) { @@ -194,8 +199,8 @@ } my_src = (struct qb_timer_source *)l->timer_source; - if (pthread_mutex_lock(&my_src->lock)) { - return -errno; + if ( (res=pthread_mutex_lock(&my_src->lock))) { + return -res; } i = _get_empty_array_position_(my_src); assert(qb_array_index(my_src->timers, i, (void **)&t) >= 0); @@ -263,7 +268,9 @@ } if (t->timerlist_handle) { - timerlist_del(&s->timerlist, t->timerlist_handle); + if (timerlist_del(&s->timerlist, t->timerlist_handle) != 0) { + qb_util_log(LOG_ERR, "Could not delete timer from timerlist"); + } } t->state = QB_POLL_ENTRY_EMPTY; return 0; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libqb-2.0.3+20210303.404adbc/lib/rpl_sem.c new/libqb-2.0.4+20211112.a2691b9/lib/rpl_sem.c --- old/libqb-2.0.3+20210303.404adbc/lib/rpl_sem.c 2021-03-03 09:32:09.000000000 +0100 +++ new/libqb-2.0.4+20211112.a2691b9/lib/rpl_sem.c 2021-11-12 14:18:47.000000000 +0100 @@ -76,7 +76,7 @@ { int retval = pthread_mutex_lock(&sem->mutex); if (retval != 0) { - return -errno; + return -retval; } if (sem->destroy_request) { retval = -EINVAL; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libqb-2.0.3+20210303.404adbc/tests/Makefile.am new/libqb-2.0.4+20211112.a2691b9/tests/Makefile.am --- old/libqb-2.0.3+20210303.404adbc/tests/Makefile.am 2021-03-03 09:32:09.000000000 +0100 +++ new/libqb-2.0.4+20211112.a2691b9/tests/Makefile.am 2021-11-12 14:18:47.000000000 +0100 @@ -38,7 +38,8 @@ bmc_LDADD = $(top_builddir)/lib/libqb.la bmcpt_SOURCES = bmcpt.c -bmcpt_LDADD = $(top_builddir)/lib/libqb.la +bmcpt_CFLAGS = $(PTHREAD_CFLAGS) +bmcpt_LDADD = $(PTHREAD_LIBS) $(top_builddir)/lib/libqb.la bms_SOURCES = bms.c bms_CFLAGS = $(GLIB_CFLAGS) @@ -124,7 +125,7 @@ check_LTLIBRARIES = check_PROGRAMS = array.test ipc.test list.test log.test loop.test \ - map.test rb.test util.test \ + map.test rb.test util.test tlist.test \ crash_test_dummy file_change_bytes dist_check_SCRIPTS = start.test resources.test blackbox-segfault.sh @@ -161,6 +162,10 @@ loop_test_CFLAGS = @CHECK_CFLAGS@ loop_test_LDADD = $(top_builddir)/lib/libqb.la @CHECK_LIBS@ +tlist_test_SOURCES = check_tlist.c +tlist_test_CFLAGS = @CHECK_CFLAGS@ +tlist_test_LDADD = $(top_builddir)/lib/libqb.la @CHECK_LIBS@ + ipc_test_SOURCES = check_ipc.c ipc_test_CFLAGS = @CHECK_CFLAGS@ ipc_test_LDADD = $(top_builddir)/lib/libqb.la @CHECK_LIBS@ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libqb-2.0.3+20210303.404adbc/tests/check_loop.c new/libqb-2.0.4+20211112.a2691b9/tests/check_loop.c --- old/libqb-2.0.3+20210303.404adbc/tests/check_loop.c 2021-03-03 09:32:09.000000000 +0100 +++ new/libqb-2.0.4+20211112.a2691b9/tests/check_loop.c 2021-11-12 14:18:47.000000000 +0100 @@ -448,7 +448,7 @@ res = qb_loop_timer_add(l, QB_LOOP_LOW, 5*QB_TIME_NS_IN_MSEC, l, one_shot_tmo, &test_tht); ck_assert_int_eq(res, 0); - res = qb_loop_timer_is_running(l, test_th); + res = qb_loop_timer_is_running(l, test_tht); ck_assert_int_eq(res, QB_TRUE); sleep(5); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libqb-2.0.3+20210303.404adbc/tests/check_tlist.c new/libqb-2.0.4+20211112.a2691b9/tests/check_tlist.c --- old/libqb-2.0.3+20210303.404adbc/tests/check_tlist.c 1970-01-01 01:00:00.000000000 +0100 +++ new/libqb-2.0.4+20211112.a2691b9/tests/check_tlist.c 2021-11-12 14:18:47.000000000 +0100 @@ -0,0 +1,294 @@ +/* + * Copyright (c) 2021 Red Hat, Inc. + * + * All rights reserved. + * + * Author: Jan Friesse <jfrie...@redhat.com> + * + * This file is part of libqb. + * + * libqb is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 2.1 of the License, or + * (at your option) any later version. + * + * libqb is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with libqb. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "os_base.h" + +#include "check_common.h" + +#include "tlist.h" + +#include <poll.h> + +#include <qb/qbdefs.h> +#include <qb/qbutil.h> +#include <qb/qblog.h> + +#define SHORT_TIMEOUT (100 * QB_TIME_NS_IN_MSEC) +#define LONG_TIMEOUT (60 * QB_TIME_NS_IN_SEC) + +#define SPEED_TEST_NO_ITEMS 10000 + +#define HEAP_TEST_NO_ITEMS 20 +/* + * Valid heap checking is slow + */ +#define HEAP_SPEED_TEST_NO_ITEMS 1000 + +static int timer_list_fn1_called = 0; + +static void +timer_list_fn1(void *data) +{ + + ck_assert(data == &timer_list_fn1_called); + + timer_list_fn1_called++; +} + +static void +sleep_ns(long long int ns) +{ + + (void)poll(NULL, 0, (ns / QB_TIME_NS_IN_MSEC)); +} + +START_TEST(test_check_basic) +{ + struct timerlist tlist; + timer_handle thandle; + int res; + uint64_t u64; + + timerlist_init(&tlist); + + /* + * Check adding short duration and calling callback + */ + res = timerlist_add_duration(&tlist, timer_list_fn1, &timer_list_fn1_called, SHORT_TIMEOUT / 2, &thandle); + ck_assert_int_eq(res, 0); + + sleep_ns(SHORT_TIMEOUT); + u64 = timerlist_msec_duration_to_expire(&tlist); + ck_assert(u64 == 0); + + timer_list_fn1_called = 0; + timerlist_expire(&tlist); + ck_assert_int_eq(timer_list_fn1_called, 1); + + u64 = timerlist_msec_duration_to_expire(&tlist); + ck_assert(u64 == -1); + + /* + * Check callback is not called (long timeout) + */ + res = timerlist_add_duration(&tlist, timer_list_fn1, &timer_list_fn1_called, LONG_TIMEOUT / 2, &thandle); + ck_assert_int_eq(res, 0); + + sleep_ns(SHORT_TIMEOUT); + u64 = timerlist_msec_duration_to_expire(&tlist); + ck_assert(u64 > 0); + + timer_list_fn1_called = 0; + timerlist_expire(&tlist); + ck_assert_int_eq(timer_list_fn1_called, 0); + + u64 = timerlist_msec_duration_to_expire(&tlist); + ck_assert(u64 > 0); + + /* + * Delete timer + */ + timerlist_del(&tlist, thandle); + u64 = timerlist_msec_duration_to_expire(&tlist); + ck_assert(u64 == -1); + + timerlist_destroy(&tlist); +} +END_TEST + +START_TEST(test_check_speed) +{ + struct timerlist tlist; + timer_handle thandle[SPEED_TEST_NO_ITEMS]; + int res; + uint64_t u64; + int i; + + timerlist_init(&tlist); + + /* + * Check adding a lot of short duration and deleting + */ + for (i = 0; i < SPEED_TEST_NO_ITEMS; i++) { + res = timerlist_add_duration(&tlist, timer_list_fn1, &timer_list_fn1_called, + SHORT_TIMEOUT / 2, &thandle[i]); + ck_assert_int_eq(res, 0); + } + + for (i = 0; i < SPEED_TEST_NO_ITEMS; i++) { + timerlist_del(&tlist, thandle[i]); + } + + u64 = timerlist_msec_duration_to_expire(&tlist); + ck_assert(u64 == -1); + + /* + * Check adding a lot of short duration and calling callback + */ + for (i = 0; i < SPEED_TEST_NO_ITEMS; i++) { + res = timerlist_add_duration(&tlist, timer_list_fn1, &timer_list_fn1_called, + SHORT_TIMEOUT / 2, &thandle[i]); + ck_assert_int_eq(res, 0); + } + + u64 = timerlist_msec_duration_to_expire(&tlist); + ck_assert(u64 != -1); + + sleep_ns(SHORT_TIMEOUT); + + timer_list_fn1_called = 0; + timerlist_expire(&tlist); + ck_assert_int_eq(timer_list_fn1_called, SPEED_TEST_NO_ITEMS); + + u64 = timerlist_msec_duration_to_expire(&tlist); + ck_assert(u64 == -1); + + timerlist_destroy(&tlist); +} +END_TEST + +START_TEST(test_check_heap) +{ + struct timerlist tlist; + int i; + timer_handle tlist_entry[HEAP_TEST_NO_ITEMS]; + timer_handle tlist_speed_entry[HEAP_SPEED_TEST_NO_ITEMS]; + int res; + + timerlist_init(&tlist); + + /* + * Empty tlist + */ + ck_assert(timerlist_msec_duration_to_expire(&tlist) == -1); + + /* + * Add items in standard and reverse order + */ + for (i = 0; i < HEAP_TEST_NO_ITEMS / 2; i++) { + res = timerlist_add_duration(&tlist, timer_list_fn1, &timer_list_fn1_called, + LONG_TIMEOUT * ((HEAP_TEST_NO_ITEMS - i) + 1), &tlist_entry[i * 2]); + ck_assert_int_eq(res, 0); + + res = timerlist_add_duration(&tlist, timer_list_fn1, &timer_list_fn1_called, + LONG_TIMEOUT * (i + 1), &tlist_entry[i * 2 + 1]); + ck_assert_int_eq(res, 0); + + ck_assert(timerlist_debug_is_valid_heap(&tlist)); + } + + /* + * Remove items + */ + for (i = 0; i < HEAP_TEST_NO_ITEMS; i++) { + timerlist_del(&tlist, tlist_entry[i]); + + ck_assert(timerlist_debug_is_valid_heap(&tlist)); + } + + ck_assert(timerlist_msec_duration_to_expire(&tlist) == -1); + + /* + * Add items again in increasing order + */ + for (i = 0; i < HEAP_TEST_NO_ITEMS; i++) { + res = timerlist_add_duration(&tlist, timer_list_fn1, &timer_list_fn1_called, + LONG_TIMEOUT * (i + 1), &tlist_entry[i]); + ck_assert_int_eq(res, 0); + + ck_assert(timerlist_debug_is_valid_heap(&tlist)); + } + + + /* + * Try delete every third item and test if heap property is kept + */ + i = 0; + while (tlist.size > 0) { + i = (i + 3) % HEAP_TEST_NO_ITEMS; + + while (tlist_entry[i] == NULL) { + i = (i + 1) % HEAP_TEST_NO_ITEMS; + } + + timerlist_del(&tlist, tlist_entry[i]); + tlist_entry[i] = NULL; + ck_assert(timerlist_debug_is_valid_heap(&tlist)); + } + + ck_assert(timerlist_msec_duration_to_expire(&tlist) == -1); + + /* + * Speed test + */ + for (i = 0; i < HEAP_SPEED_TEST_NO_ITEMS; i++) { + res = timerlist_add_duration(&tlist, timer_list_fn1, &timer_list_fn1_called, + SHORT_TIMEOUT / 2, &tlist_speed_entry[i]); + ck_assert_int_eq(res, 0); + + ck_assert(timerlist_debug_is_valid_heap(&tlist)); + } + + for (i = 0; i < HEAP_SPEED_TEST_NO_ITEMS; i++) { + timerlist_del(&tlist, tlist_speed_entry[i]); + ck_assert(timerlist_debug_is_valid_heap(&tlist)); + } + + /* + * Free list + */ + timerlist_destroy(&tlist); +} +END_TEST + +static Suite *tlist_suite(void) +{ + TCase *tc; + Suite *s = suite_create("tlist"); + + add_tcase(s, tc, test_check_basic); + add_tcase(s, tc, test_check_speed, 30); + add_tcase(s, tc, test_check_heap, 30); + + return s; +} + +int32_t main(void) +{ + int32_t number_failed; + + Suite *s = tlist_suite(); + SRunner *sr = srunner_create(s); + + qb_log_init("check", LOG_USER, LOG_EMERG); + atexit(qb_log_fini); + qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_ENABLED, QB_FALSE); + qb_log_filter_ctl(QB_LOG_STDERR, QB_LOG_FILTER_ADD, + QB_LOG_FILTER_FILE, "*", LOG_INFO); + qb_log_ctl(QB_LOG_STDERR, QB_LOG_CONF_ENABLED, QB_TRUE); + + srunner_run_all(sr, CK_VERBOSE); + number_failed = srunner_ntests_failed(sr); + srunner_free(sr); + return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +}