2009-12-06 16:17:27 +00:00
|
|
|
/* -*- 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/>.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2010-02-24 01:46:45 +00:00
|
|
|
#include "conky.h"
|
2009-12-06 16:17:27 +00:00
|
|
|
#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;
|
|
|
|
|
2010-02-24 01:46:45 +00:00
|
|
|
retval = (struct prio_queue *) malloc(sizeof(struct prio_queue));
|
2009-12-06 16:17:27 +00:00
|
|
|
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;
|
|
|
|
|
2010-02-24 01:46:45 +00:00
|
|
|
retval = (struct prio_elem *) malloc(sizeof(struct prio_elem));
|
2009-12-06 16:17:27 +00:00
|
|
|
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);
|
2010-02-24 01:46:45 +00:00
|
|
|
free_and_zero(queue->tail->next);
|
2009-12-06 16:17:27 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|