mirror of
https://github.com/Llewellynvdm/conky.git
synced 2024-11-18 11:05:18 +00:00
introduce a generic priority queue implementation
This commit is contained in:
parent
bb72583a95
commit
4229fbd380
@ -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 \
|
||||
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 \
|
||||
mboxscan.h read_tcp.c read_tcp.h scroll.c scroll.h specials.c \
|
||||
specials.h tailhead.c tailhead.h temphelper.c temphelper.h \
|
||||
mboxscan.h prioqueue.c prioqueue.h read_tcp.c read_tcp.h scroll.c scroll.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 \
|
||||
algebra.h proc.c proc.h user.c user.h
|
||||
|
||||
|
207
src/prioqueue.c
Normal file
207
src/prioqueue.c
Normal 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
66
src/prioqueue.h
Normal 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
175
src/top.c
@ -29,6 +29,7 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include "prioqueue.h"
|
||||
#include "top.h"
|
||||
#include "logging.h"
|
||||
|
||||
@ -595,25 +596,11 @@ static void calc_io_each(void)
|
||||
* Find the top processes *
|
||||
******************************************/
|
||||
|
||||
/* free a sp_process structure */
|
||||
static void free_sp(struct sorted_process *sp)
|
||||
/* cpu comparison function for prio queue */
|
||||
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) {
|
||||
return 1;
|
||||
} 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 */
|
||||
static int compare_mem(struct process *a, struct process *b)
|
||||
/* mem comparison function for prio queue */
|
||||
static int compare_mem(void *va, void *vb)
|
||||
{
|
||||
struct process *a = va, *b = vb;
|
||||
|
||||
if (a->rss < b->rss) {
|
||||
return 1;
|
||||
} 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 */
|
||||
static int compare_time(struct process *a, struct process *b)
|
||||
/* CPU time comparision function for prio queue */
|
||||
static int compare_time(void *va, void *vb)
|
||||
{
|
||||
struct process *a = va, *b = vb;
|
||||
|
||||
return b->total_cpu_time - a->total_cpu_time;
|
||||
}
|
||||
|
||||
#ifdef IOSTATS
|
||||
/* I/O comparision function for insert_sp_element */
|
||||
static int compare_io(struct process *a, struct process *b)
|
||||
/* I/O comparision function for prio queue */
|
||||
static int compare_io(void *va, void *vb)
|
||||
{
|
||||
struct process *a = va, *b = vb;
|
||||
|
||||
if (a->io_perc < b->io_perc) {
|
||||
return 1;
|
||||
} else if (a->io_perc > b->io_perc) {
|
||||
@ -655,78 +648,6 @@ static int compare_io(struct process *a, struct process *b)
|
||||
}
|
||||
#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. *
|
||||
* 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 */
|
||||
)
|
||||
{
|
||||
struct sorted_process *spc_head = NULL, *spc_tail = NULL, *spc_cur = NULL;
|
||||
struct sorted_process *spm_head = NULL, *spm_tail = NULL, *spm_cur = NULL;
|
||||
struct sorted_process *spt_head = NULL, *spt_tail = NULL, *spt_cur = NULL;
|
||||
prio_queue_t cpu_queue, mem_queue, time_queue
|
||||
#ifdef IOSTATS
|
||||
struct sorted_process *spi_head = NULL, *spi_tail = NULL, *spi_cur = NULL;
|
||||
#endif /* IOSTATS */
|
||||
, io_queue
|
||||
#endif
|
||||
;
|
||||
struct process *cur_proc = NULL;
|
||||
unsigned long long total = 0;
|
||||
int i;
|
||||
|
||||
if (!top_cpu && !top_mem && !top_time
|
||||
#ifdef IOSTATS
|
||||
@ -757,6 +678,24 @@ void process_find_top(struct process **cpu, struct process **mem,
|
||||
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 */
|
||||
update_process_table(); /* update the table with process list */
|
||||
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) {
|
||||
if (top_cpu) {
|
||||
spc_cur = malloc_sp(cur_proc);
|
||||
insert_sp_element(spc_cur, &spc_head, &spc_tail, MAX_SP,
|
||||
&compare_cpu);
|
||||
insert_prio_elem(cpu_queue, cur_proc);
|
||||
}
|
||||
if (top_mem) {
|
||||
spm_cur = malloc_sp(cur_proc);
|
||||
insert_sp_element(spm_cur, &spm_head, &spm_tail, MAX_SP,
|
||||
&compare_mem);
|
||||
insert_prio_elem(mem_queue, cur_proc);
|
||||
}
|
||||
if (top_time) {
|
||||
spt_cur = malloc_sp(cur_proc);
|
||||
insert_sp_element(spt_cur, &spt_head, &spt_tail, MAX_SP,
|
||||
&compare_time);
|
||||
insert_prio_elem(time_queue, cur_proc);
|
||||
}
|
||||
#ifdef IOSTATS
|
||||
if (top_io) {
|
||||
spi_cur = malloc_sp(cur_proc);
|
||||
insert_sp_element(spi_cur, &spi_head, &spi_tail, MAX_SP,
|
||||
&compare_io);
|
||||
insert_prio_elem(io_queue, cur_proc);
|
||||
}
|
||||
#endif /* IOSTATS */
|
||||
cur_proc = cur_proc->next;
|
||||
}
|
||||
|
||||
if (top_cpu) sp_acopy(spc_head, cpu, MAX_SP);
|
||||
if (top_mem) sp_acopy(spm_head, mem, MAX_SP);
|
||||
if (top_time) sp_acopy(spt_head, ptime, MAX_SP);
|
||||
for (i = 0; i < MAX_SP; i++) {
|
||||
if (top_cpu)
|
||||
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
|
||||
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 */
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user