1
0
mirror of https://github.com/Llewellynvdm/conky.git synced 2024-09-30 14:09:13 +00:00

introduce a generic priority queue implementation

This commit is contained in:
Phil Sutter 2009-12-06 17:17:27 +01:00
parent bb72583a95
commit 4229fbd380
4 changed files with 334 additions and 118 deletions

View File

@ -53,8 +53,8 @@ mandatory_sources = colours.c colours.h combine.c combine.h common.c common.h \
conky.cc conky.h core.cc core.h diskio.c diskio.h entropy.c entropy.h \ conky.cc conky.h core.cc core.h diskio.c diskio.h entropy.c entropy.h \
exec.c exec.h fs.c fs.h logging.h mail.c mail.h mixer.c mixer.h net_stat.c \ exec.c exec.h fs.c fs.h logging.h mail.c mail.h mixer.c mixer.h net_stat.c \
net_stat.h template.c template.h timed_thread.c timed_thread.h mboxscan.c \ net_stat.h template.c template.h timed_thread.c timed_thread.h mboxscan.c \
mboxscan.h read_tcp.c read_tcp.h scroll.c scroll.h specials.c \ mboxscan.h prioqueue.c prioqueue.h read_tcp.c read_tcp.h scroll.c scroll.h \
specials.h tailhead.c tailhead.h temphelper.c temphelper.h \ specials.c specials.h tailhead.c tailhead.h temphelper.c temphelper.h \
text_object.c text_object.h timeinfo.c timeinfo.h top.c top.h algebra.c \ text_object.c text_object.h timeinfo.c timeinfo.h top.c top.h algebra.c \
algebra.h proc.c proc.h user.c user.h algebra.h proc.c proc.h user.c user.h

207
src/prioqueue.c Normal file
View File

@ -0,0 +1,207 @@
/* -*- mode: c; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: t -*-
* vim: ts=4 sw=4 noet ai cindent syntax=c
*
* prioqueue: a simple priority queue implementation
*
* The queue organises it's data internally using a doubly linked
* list, into which elements are inserted at the right position. This
* is definitely not the best algorithm for a priority queue, but it
* fits best for the given purpose, i.e. the top process sorting.
* This means we have a rather little amount of total elements (~200
* on a normal system), which are to be inserted into a queue of only
* the few top-most elements (10 at the current state). Additionally,
* at each update interval, the queue is drained completely and
* refilled from scratch.
*
* Copyright (C) 2009 Phil Sutter <phil@nwl.cc>
*
* Initially based on the former implementation of sorted processes in
* top.c, Copyright (C) 2005 David Carter <boojit@pundo.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <limits.h> /* INT_MAX */
#include <stdlib.h>
#include <string.h>
struct prio_elem {
struct prio_elem *next, *prev;
void *data;
};
struct prio_queue {
/* Compare a and b. Return:
* <0 if a should come before b,
* >0 if b should come before a,
* 0 if don't care */
int (*compare)(void *a, void *b);
/* Free element payload. Called when dropping elements. */
void (*free)(void *a);
/* Maximum size of queue. The first
* elements in the list take precedence. */
int max_size;
/* The pointers to the actual list. */
struct prio_elem *head, *tail;
/* The current number of elements in the list. */
int cur_size;
};
/* nop callback to save us from conditional calling */
static void pq_free_nop(void *a) { (void)a; }
struct prio_queue *init_prio_queue(void)
{
struct prio_queue *retval;
retval = malloc(sizeof(struct prio_queue));
memset(retval, 0, sizeof(struct prio_queue));
/* use pq_free_nop by default */
retval->free = &pq_free_nop;
/* Default to maximum possible size as restricted
* by the used data type. This also saves us from
* checking if caller has set this field or not. */
retval->max_size = INT_MAX;
return retval;
}
void pq_set_compare(struct prio_queue *queue, int (*pqcompare)(void *a, void *b))
{
if (pqcompare)
queue->compare = pqcompare;
}
void pq_set_free(struct prio_queue *queue, void (*pqfree)(void *a))
{
if (pqfree)
queue->free = pqfree;
}
void pq_set_max_size(struct prio_queue *queue, int max_size)
{
if (max_size >= 0)
queue->max_size = max_size;
}
int pq_get_cur_size(struct prio_queue *queue)
{
return queue->cur_size;
}
static struct prio_elem *init_prio_elem(void *data)
{
struct prio_elem *retval;
retval = malloc(sizeof(struct prio_elem));
memset(retval, 0, sizeof(struct prio_elem));
retval->data = data;
return retval;
}
void insert_prio_elem(struct prio_queue *queue, void *data)
{
struct prio_elem *cur;
/* queue->compare is a must-have */
if (!queue->compare)
return;
/* empty queue, insert the first item */
if (!queue->cur_size) {
queue->cur_size++;
queue->head = queue->tail = init_prio_elem(data);
return;
}
/* short-cut 1: new item is lower than all others */
if (queue->compare(queue->tail->data, data) <= 0) {
if (queue->cur_size < queue->max_size) {
queue->cur_size++;
queue->tail->next = init_prio_elem(data);
queue->tail->next->prev = queue->tail;
queue->tail = queue->tail->next;
} else /* list was already full */
(*queue->free)(data);
return;
}
/* short-cut 2: we have a new maximum */
if (queue->compare(queue->head->data, data) >= 0) {
queue->cur_size++;
queue->head->prev = init_prio_elem(data);
queue->head->prev->next = queue->head;
queue->head = queue->head->prev;
goto check_cur_size;
}
/* find the actual position if short-cuts failed */
for (cur = queue->head->next; cur; cur = cur->next) {
if (queue->compare(cur->data, data) >= 0) {
queue->cur_size++;
cur->prev->next = init_prio_elem(data);
cur->prev->next->prev = cur->prev;
cur->prev->next->next = cur;
cur->prev = cur->prev->next;
break;
}
}
check_cur_size:
/* drop the lowest item if queue overrun */
if (queue->cur_size > queue->max_size) {
queue->cur_size--;
queue->tail = queue->tail->prev;
(*queue->free)(queue->tail->next->data);
free(queue->tail->next);
queue->tail->next = NULL;
}
}
void *pop_prio_elem(struct prio_queue *queue)
{
struct prio_elem *tmp;
void *data;
if (queue->cur_size <= 0)
return NULL;
tmp = queue->head;
data = tmp->data;
queue->head = queue->head->next;
queue->cur_size--;
if (queue->head)
queue->head->prev = NULL;
else /* list is now empty */
queue->tail = NULL;
free(tmp);
return data;
}
void free_prio_queue(struct prio_queue *queue)
{
void *data;
while((data = pop_prio_elem(queue)))
(*queue->free)(data);
free(queue);
}

66
src/prioqueue.h Normal file
View File

@ -0,0 +1,66 @@
/* -*- mode: c; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: t -*-
* vim: ts=4 sw=4 noet ai cindent syntax=c
*
* prioqueue: a simple priority queue implementation
*
* Copyright (C) 2009 Phil Sutter <phil@nwl.cc>
*
* Initially based on the former implementation of sorted processes in
* top.c, Copyright (C) 2005 David Carter <boojit@pundo.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef _PRIOQUEUE_H
#define _PRIOQUEUE_H
/* forward-define for private data */
struct prio_queue;
/* typedef for a distinct prioqueue object */
typedef struct prio_queue *prio_queue_t;
/* initialise a prioqueue object (mandatory) */
prio_queue_t init_prio_queue(void);
/* set the compare function (mandatory)
* (*compare) shall return:
* <0 if a should come before b,
* >0 if b should come before a,
* 0 if doesn't matter */
void pq_set_compare(prio_queue_t, int (*compare)(void *a, void *b));
/* set the data free function (optional)
* (*free) will be called when:
* - dropping elements from the end of a limited size queue and
* - free_prio_queue() finds leftover elements in the given queue */
void pq_set_free(prio_queue_t, void (*free)(void *));
/* set a maximum queue size (optional) (defaults to INT_MAX) */
void pq_set_max_size(prio_queue_t, int);
/* insert an element into the given queue */
void insert_prio_elem(prio_queue_t, void *);
/* return the number of elements in the queue */
int pq_get_cur_size(prio_queue_t queue);
/* pop the top-most element from the queue
* returns NULL if queue is empty */
void *pop_prio_elem(prio_queue_t);
/* clear and free the given queue */
void free_prio_queue(prio_queue_t);
#endif /* _PRIOQUEUE_H */

175
src/top.c
View File

@ -29,6 +29,7 @@
* *
*/ */
#include "prioqueue.h"
#include "top.h" #include "top.h"
#include "logging.h" #include "logging.h"
@ -595,25 +596,11 @@ static void calc_io_each(void)
* Find the top processes * * Find the top processes *
******************************************/ ******************************************/
/* free a sp_process structure */ /* cpu comparison function for prio queue */
static void free_sp(struct sorted_process *sp) static int compare_cpu(void *va, void *vb)
{ {
free(sp); struct process *a = va, *b = vb;
}
/* create a new sp_process structure */
static struct sorted_process *malloc_sp(struct process *proc)
{
struct sorted_process *sp;
sp = malloc(sizeof(struct sorted_process));
memset(sp, 0, sizeof(struct sorted_process));
sp->proc = proc;
return sp;
}
/* cpu comparison function for insert_sp_element */
static int compare_cpu(struct process *a, struct process *b)
{
if (a->amount < b->amount) { if (a->amount < b->amount) {
return 1; return 1;
} else if (a->amount > b->amount) { } else if (a->amount > b->amount) {
@ -623,9 +610,11 @@ static int compare_cpu(struct process *a, struct process *b)
} }
} }
/* mem comparison function for insert_sp_element */ /* mem comparison function for prio queue */
static int compare_mem(struct process *a, struct process *b) static int compare_mem(void *va, void *vb)
{ {
struct process *a = va, *b = vb;
if (a->rss < b->rss) { if (a->rss < b->rss) {
return 1; return 1;
} else if (a->rss > b->rss) { } else if (a->rss > b->rss) {
@ -635,16 +624,20 @@ static int compare_mem(struct process *a, struct process *b)
} }
} }
/* CPU time comparision function for insert_sp_element */ /* CPU time comparision function for prio queue */
static int compare_time(struct process *a, struct process *b) static int compare_time(void *va, void *vb)
{ {
struct process *a = va, *b = vb;
return b->total_cpu_time - a->total_cpu_time; return b->total_cpu_time - a->total_cpu_time;
} }
#ifdef IOSTATS #ifdef IOSTATS
/* I/O comparision function for insert_sp_element */ /* I/O comparision function for prio queue */
static int compare_io(struct process *a, struct process *b) static int compare_io(void *va, void *vb)
{ {
struct process *a = va, *b = vb;
if (a->io_perc < b->io_perc) { if (a->io_perc < b->io_perc) {
return 1; return 1;
} else if (a->io_perc > b->io_perc) { } else if (a->io_perc > b->io_perc) {
@ -655,78 +648,6 @@ static int compare_io(struct process *a, struct process *b)
} }
#endif /* IOSTATS */ #endif /* IOSTATS */
/* insert this process into the list in a sorted fashion,
* or destroy it if it doesn't fit on the list */
static int insert_sp_element(struct sorted_process *sp_cur,
struct sorted_process **p_sp_head, struct sorted_process **p_sp_tail,
int max_elements, int compare_funct(struct process *, struct process *))
{
struct sorted_process *sp_readthru = NULL, *sp_destroy = NULL;
int did_insert = 0, x = 0;
if (*p_sp_head == NULL) {
*p_sp_head = sp_cur;
*p_sp_tail = sp_cur;
return 1;
}
for (sp_readthru = *p_sp_head, x = 0;
sp_readthru != NULL && x < max_elements;
sp_readthru = sp_readthru->less, x++) {
if (compare_funct(sp_readthru->proc, sp_cur->proc) > 0 && !did_insert) {
/* sp_cur is bigger than sp_readthru
* so insert it before sp_readthru */
sp_cur->less = sp_readthru;
if (sp_readthru == *p_sp_head) {
/* insert as the new head of the list */
*p_sp_head = sp_cur;
} else {
/* insert inside the list */
sp_readthru->greater->less = sp_cur;
sp_cur->greater = sp_readthru->greater;
}
sp_readthru->greater = sp_cur;
/* element was inserted, so increase the counter */
did_insert = ++x;
}
}
if (x < max_elements && sp_readthru == NULL && !did_insert) {
/* sp_cur is the smallest element and list isn't full,
* so insert at the end */
(*p_sp_tail)->less = sp_cur;
sp_cur->greater = *p_sp_tail;
*p_sp_tail = sp_cur;
did_insert = x;
} else if (x >= max_elements) {
/* We inserted an element and now the list is too big by one.
* Destroy the smallest element */
sp_destroy = *p_sp_tail;
*p_sp_tail = sp_destroy->greater;
(*p_sp_tail)->less = NULL;
free_sp(sp_destroy);
}
if (!did_insert) {
/* sp_cur wasn't added to the sorted list, so destroy it */
free_sp(sp_cur);
}
return did_insert;
}
/* copy the procs in the sorted list to the array, and destroy the list */
static void sp_acopy(struct sorted_process *sp_head, struct process **ar, int max_size)
{
struct sorted_process *sp_cur, *sp_tmp;
int x;
sp_cur = sp_head;
for (x = 0; x < max_size && sp_cur != NULL; x++) {
ar[x] = sp_cur->proc;
sp_tmp = sp_cur;
sp_cur = sp_cur->less;
free_sp(sp_tmp);
}
}
/* ****************************************************************** * /* ****************************************************************** *
* Get a sorted list of the top cpu hogs and top mem hogs. * * Get a sorted list of the top cpu hogs and top mem hogs. *
* Results are stored in the cpu,mem arrays in decreasing order[0-9]. * * Results are stored in the cpu,mem arrays in decreasing order[0-9]. *
@ -739,14 +660,14 @@ void process_find_top(struct process **cpu, struct process **mem,
#endif /* IOSTATS */ #endif /* IOSTATS */
) )
{ {
struct sorted_process *spc_head = NULL, *spc_tail = NULL, *spc_cur = NULL; prio_queue_t cpu_queue, mem_queue, time_queue
struct sorted_process *spm_head = NULL, *spm_tail = NULL, *spm_cur = NULL;
struct sorted_process *spt_head = NULL, *spt_tail = NULL, *spt_cur = NULL;
#ifdef IOSTATS #ifdef IOSTATS
struct sorted_process *spi_head = NULL, *spi_tail = NULL, *spi_cur = NULL; , io_queue
#endif /* IOSTATS */ #endif
;
struct process *cur_proc = NULL; struct process *cur_proc = NULL;
unsigned long long total = 0; unsigned long long total = 0;
int i;
if (!top_cpu && !top_mem && !top_time if (!top_cpu && !top_mem && !top_time
#ifdef IOSTATS #ifdef IOSTATS
@ -757,6 +678,24 @@ void process_find_top(struct process **cpu, struct process **mem,
return; return;
} }
cpu_queue = init_prio_queue();
pq_set_compare(cpu_queue, &compare_cpu);
pq_set_max_size(cpu_queue, MAX_SP);
mem_queue = init_prio_queue();
pq_set_compare(mem_queue, &compare_mem);
pq_set_max_size(mem_queue, MAX_SP);
time_queue = init_prio_queue();
pq_set_compare(time_queue, &compare_time);
pq_set_max_size(time_queue, MAX_SP);
#ifdef IOSTATS
io_queue = init_prio_queue();
pq_set_compare(io_queue, &compare_io);
pq_set_max_size(io_queue, MAX_SP);
#endif
total = calc_cpu_total(); /* calculate the total of the processor */ total = calc_cpu_total(); /* calculate the total of the processor */
update_process_table(); /* update the table with process list */ update_process_table(); /* update the table with process list */
calc_cpu_each(total); /* and then the percentage for each task */ calc_cpu_each(total); /* and then the percentage for each task */
@ -769,35 +708,39 @@ void process_find_top(struct process **cpu, struct process **mem,
while (cur_proc != NULL) { while (cur_proc != NULL) {
if (top_cpu) { if (top_cpu) {
spc_cur = malloc_sp(cur_proc); insert_prio_elem(cpu_queue, cur_proc);
insert_sp_element(spc_cur, &spc_head, &spc_tail, MAX_SP,
&compare_cpu);
} }
if (top_mem) { if (top_mem) {
spm_cur = malloc_sp(cur_proc); insert_prio_elem(mem_queue, cur_proc);
insert_sp_element(spm_cur, &spm_head, &spm_tail, MAX_SP,
&compare_mem);
} }
if (top_time) { if (top_time) {
spt_cur = malloc_sp(cur_proc); insert_prio_elem(time_queue, cur_proc);
insert_sp_element(spt_cur, &spt_head, &spt_tail, MAX_SP,
&compare_time);
} }
#ifdef IOSTATS #ifdef IOSTATS
if (top_io) { if (top_io) {
spi_cur = malloc_sp(cur_proc); insert_prio_elem(io_queue, cur_proc);
insert_sp_element(spi_cur, &spi_head, &spi_tail, MAX_SP,
&compare_io);
} }
#endif /* IOSTATS */ #endif /* IOSTATS */
cur_proc = cur_proc->next; cur_proc = cur_proc->next;
} }
if (top_cpu) sp_acopy(spc_head, cpu, MAX_SP); for (i = 0; i < MAX_SP; i++) {
if (top_mem) sp_acopy(spm_head, mem, MAX_SP); if (top_cpu)
if (top_time) sp_acopy(spt_head, ptime, MAX_SP); cpu[i] = pop_prio_elem(cpu_queue);
if (top_mem)
mem[i] = pop_prio_elem(mem_queue);
if (top_time)
ptime[i] = pop_prio_elem(time_queue);
#ifdef IOSTATS #ifdef IOSTATS
if (top_io) sp_acopy(spi_head, io, MAX_SP); if (top_io)
io[i] = pop_prio_elem(io_queue);
#endif /* IOSTATS */
}
free_prio_queue(cpu_queue);
free_prio_queue(mem_queue);
free_prio_queue(time_queue);
#ifdef IOSTATS
free_prio_queue(io_queue);
#endif /* IOSTATS */ #endif /* IOSTATS */
} }