Main Page | Alphabetical List | Data Structures | Directories | File List | Data Fields | Globals

list.c

Go to the documentation of this file.
00001 /* ==================================================================== 
00002  * The Kannel Software License, Version 1.0 
00003  * 
00004  * Copyright (c) 2001-2008 Kannel Group  
00005  * Copyright (c) 1998-2001 WapIT Ltd.   
00006  * All rights reserved. 
00007  * 
00008  * Redistribution and use in source and binary forms, with or without 
00009  * modification, are permitted provided that the following conditions 
00010  * are met: 
00011  * 
00012  * 1. Redistributions of source code must retain the above copyright 
00013  *    notice, this list of conditions and the following disclaimer. 
00014  * 
00015  * 2. Redistributions in binary form must reproduce the above copyright 
00016  *    notice, this list of conditions and the following disclaimer in 
00017  *    the documentation and/or other materials provided with the 
00018  *    distribution. 
00019  * 
00020  * 3. The end-user documentation included with the redistribution, 
00021  *    if any, must include the following acknowledgment: 
00022  *       "This product includes software developed by the 
00023  *        Kannel Group (http://www.kannel.org/)." 
00024  *    Alternately, this acknowledgment may appear in the software itself, 
00025  *    if and wherever such third-party acknowledgments normally appear. 
00026  * 
00027  * 4. The names "Kannel" and "Kannel Group" must not be used to 
00028  *    endorse or promote products derived from this software without 
00029  *    prior written permission. For written permission, please  
00030  *    contact org@kannel.org. 
00031  * 
00032  * 5. Products derived from this software may not be called "Kannel", 
00033  *    nor may "Kannel" appear in their name, without prior written 
00034  *    permission of the Kannel Group. 
00035  * 
00036  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED 
00037  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 
00038  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 
00039  * DISCLAIMED.  IN NO EVENT SHALL THE KANNEL GROUP OR ITS CONTRIBUTORS 
00040  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,  
00041  * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT  
00042  * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR  
00043  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,  
00044  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE  
00045  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,  
00046  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
00047  * ==================================================================== 
00048  * 
00049  * This software consists of voluntary contributions made by many 
00050  * individuals on behalf of the Kannel Group.  For more information on  
00051  * the Kannel Group, please see <http://www.kannel.org/>. 
00052  * 
00053  * Portions of this software are based upon software originally written at  
00054  * WapIT Ltd., Helsinki, Finland for the Kannel project.  
00055  */ 
00056 
00057 /*
00058  * list.c - generic dynamic list
00059  *
00060  * This module implements the generic list. See list.h for an explanation
00061  * of how to use the list.
00062  *
00063  * The list is implemented as an array, a starting index into the array,
00064  * and an integer giving the length of the list. The list's element i is
00065  * not necessarily at array element i, but instead it is found at element
00066  *
00067  *  (start + i) % len
00068  *
00069  * This is because we need to make it fast to use the list as a queue,
00070  * meaning that adding elements to the end and removing them from the
00071  * beginning must be very fast. Insertions into the middle of the list
00072  * need not be fast, however. It would be possible to implement the list
00073  * with a linked list, of course, but this would cause many more memory
00074  * allocations: every time an item is added to the list, a new node would
00075  * need to be allocated, and when it is removed, it would need to be freed.
00076  * Using an array lets us reduce the number of allocations. It also lets
00077  * us access an arbitrary element in constant time, which is specially
00078  * useful since it lets us simplify the list API by not adding iterators
00079  * or an explicit list item type.
00080  *
00081  * If insertions and deletions into the middle of the list become common,
00082  * it would be more efficient to use a buffer gap implementation, but
00083  * there's no point in doing that until the need arises.
00084  *
00085  * Lars Wirzenius <liw@wapit.com>
00086  */
00087 
00088 #include "gw-config.h"
00089 
00090 #include <string.h>
00091 #include <unistd.h>
00092 #include <stdlib.h>
00093 #include <errno.h>
00094 
00095 #include "gwassert.h"
00096 #include "list.h"
00097 #include "log.h"
00098 #include "thread.h"
00099 #include "gwmem.h"
00100 
00101 
00102 struct List
00103 {
00104     void **tab;
00105     long tab_size;
00106     long start;
00107     long len;
00108     Mutex *single_operation_lock;
00109     Mutex *permanent_lock;
00110     pthread_cond_t nonempty;
00111     long num_producers;
00112 };
00113 
00114 #define INDEX(list, i)  (((list)->start + i) % (list)->tab_size)
00115 #define GET(list, i)    ((list)->tab[INDEX(list, i)])
00116 
00117 
00118 long gwthread_self(void);
00119 
00120 static void lock(List *list);
00121 static void unlock(List *list);
00122 static void make_bigger(List *list, long items);
00123 static void delete_items_from_list(List *list, long pos, long count);
00124 
00125 
00126 List *gwlist_create_real(void)
00127 {
00128     List *list;
00129 
00130     list = gw_malloc(sizeof(List));
00131     list->tab = NULL;
00132     list->tab_size = 0;
00133     list->start = 0;
00134     list->len = 0;
00135     list->single_operation_lock = mutex_create();
00136     list->permanent_lock = mutex_create();
00137     pthread_cond_init(&list->nonempty, NULL);
00138     list->num_producers = 0;
00139     return list;
00140 }
00141 
00142 
00143 void gwlist_destroy(List *list, gwlist_item_destructor_t *destructor)
00144 {
00145     void *item;
00146 
00147     if (list == NULL)
00148         return;
00149 
00150     if (destructor != NULL) {
00151         while ((item = gwlist_extract_first(list)) != NULL)
00152             destructor(item);
00153     }
00154 
00155     mutex_destroy(list->permanent_lock);
00156     mutex_destroy(list->single_operation_lock);
00157     pthread_cond_destroy(&list->nonempty);
00158     gw_free(list->tab);
00159     gw_free(list);
00160 }
00161 
00162 
00163 long gwlist_len(List *list)
00164 {
00165     long len;
00166 
00167     if (list == NULL)
00168         return 0;
00169     lock(list);
00170     len = list->len;
00171     unlock(list);
00172     return len;
00173 }
00174 
00175 
00176 void gwlist_append(List *list, void *item)
00177 {
00178     lock(list);
00179     make_bigger(list, 1);
00180     list->tab[INDEX(list, list->len)] = item;
00181     ++list->len;
00182     pthread_cond_signal(&list->nonempty);
00183     unlock(list);
00184 }
00185 
00186 
00187 void gwlist_append_unique(List *list, void *item, int (*cmp)(void *, void *))
00188 {
00189     void *it;
00190     long i;
00191 
00192     lock(list);
00193     it = NULL;
00194     for (i = 0; i < list->len; ++i) {
00195         it = GET(list, i);
00196         if (cmp(it, item)) {
00197             break;
00198         }
00199     }
00200     if (i == list->len) {
00201         /* not yet in list, so add it */
00202         make_bigger(list, 1);
00203         list->tab[INDEX(list, list->len)] = item;
00204         ++list->len;
00205         pthread_cond_signal(&list->nonempty);
00206     }
00207     unlock(list);
00208 }
00209         
00210 
00211 void gwlist_insert(List *list, long pos, void *item)
00212 {
00213     long i;
00214 
00215     lock(list);
00216     gw_assert(pos >= 0);
00217     gw_assert(pos <= list->len);
00218 
00219     make_bigger(list, 1);
00220     for (i = list->len; i > pos; --i)
00221         list->tab[INDEX(list, i)] = GET(list, i - 1);
00222     list->tab[INDEX(list, pos)] = item;
00223     ++list->len;
00224     pthread_cond_signal(&list->nonempty);
00225     unlock(list);
00226 }
00227 
00228 
00229 void gwlist_delete(List *list, long pos, long count)
00230 {
00231     lock(list);
00232     delete_items_from_list(list, pos, count);
00233     unlock(list);
00234 }
00235 
00236 
00237 long gwlist_delete_matching(List *list, void *pat, gwlist_item_matches_t *matches)
00238 {
00239     long i;
00240     long count;
00241 
00242     lock(list);
00243 
00244     /* XXX this could be made more efficient by noticing
00245        consecutive items to be removed, but leave that for later.
00246        --liw */
00247     i = 0;
00248     count = 0;
00249     while (i < list->len) {
00250         if (matches(GET(list, i), pat)) {
00251             delete_items_from_list(list, i, 1);
00252             count++;
00253         } else {
00254             ++i;
00255         }
00256     }
00257     unlock(list);
00258 
00259     return count;
00260 }
00261 
00262 
00263 long gwlist_delete_equal(List *list, void *item)
00264 {
00265     long i;
00266     long count;
00267 
00268     lock(list);
00269 
00270     /* XXX this could be made more efficient by noticing
00271        consecutive items to be removed, but leave that for later.
00272        --liw */
00273     i = 0;
00274     count = 0;
00275     while (i < list->len) {
00276         if (GET(list, i) == item) {
00277             delete_items_from_list(list, i, 1);
00278             count++;
00279         } else {
00280             ++i;
00281         }
00282     }
00283     unlock(list);
00284 
00285     return count;
00286 }
00287 
00288 
00289 void *gwlist_get(List *list, long pos)
00290 {
00291     void *item;
00292 
00293     lock(list);
00294     gw_assert(pos >= 0);
00295     gw_assert(pos < list->len);
00296     item = GET(list, pos);
00297     unlock(list);
00298     return item;
00299 }
00300 
00301 
00302 void *gwlist_extract_first(List *list)
00303 {
00304     void *item;
00305 
00306     gw_assert(list != NULL);
00307     lock(list);
00308     if (list->len == 0)
00309         item = NULL;
00310     else {
00311         item = GET(list, 0);
00312         delete_items_from_list(list, 0, 1);
00313     }
00314     unlock(list);
00315     return item;
00316 }
00317 
00318 
00319 List *gwlist_extract_matching(List *list, void *pat, gwlist_item_matches_t *cmp)
00320 {
00321     List *new_list;
00322     long i;
00323 
00324     new_list = gwlist_create();
00325     lock(list);
00326     i = 0;
00327     while (i < list->len) {
00328         if (cmp(GET(list, i), pat)) {
00329             gwlist_append(new_list, GET(list, i));
00330             delete_items_from_list(list, i, 1);
00331         } else
00332             ++i;
00333     }
00334     unlock(list);
00335 
00336     if (gwlist_len(new_list) == 0) {
00337         gwlist_destroy(new_list, NULL);
00338         return NULL;
00339     }
00340     return new_list;
00341 }
00342 
00343 
00344 void gwlist_lock(List *list)
00345 {
00346     gw_assert(list != NULL);
00347     mutex_lock(list->permanent_lock);
00348 }
00349 
00350 
00351 void gwlist_unlock(List *list)
00352 {
00353     gw_assert(list != NULL);
00354     mutex_unlock(list->permanent_lock);
00355 }
00356 
00357 
00358 int gwlist_wait_until_nonempty(List *list)
00359 {
00360     int ret;
00361 
00362     lock(list);
00363     while (list->len == 0 && list->num_producers > 0) {
00364         list->single_operation_lock->owner = -1;
00365         pthread_cond_wait(&list->nonempty,
00366                           &list->single_operation_lock->mutex);
00367         list->single_operation_lock->owner = gwthread_self();
00368     }
00369     if (list->len > 0)
00370         ret = 1;
00371     else
00372         ret = -1;
00373     unlock(list);
00374     return ret;
00375 }
00376 
00377 
00378 void gwlist_add_producer(List *list)
00379 {
00380     lock(list);
00381     ++list->num_producers;
00382     unlock(list);
00383 }
00384 
00385 
00386 int gwlist_producer_count(List *list)
00387 {
00388     int ret;
00389     lock(list);
00390     ret = list->num_producers;
00391     unlock(list);
00392     return ret;
00393 }
00394 
00395 
00396 void gwlist_remove_producer(List *list)
00397 {
00398     lock(list);
00399     gw_assert(list->num_producers > 0);
00400     --list->num_producers;
00401     pthread_cond_broadcast(&list->nonempty);
00402     unlock(list);
00403 }
00404 
00405 
00406 void gwlist_produce(List *list, void *item)
00407 {
00408     gwlist_append(list, item);
00409 }
00410 
00411 
00412 void *gwlist_consume(List *list)
00413 {
00414     void *item;
00415 
00416     lock(list);
00417     while (list->len == 0 && list->num_producers > 0) {
00418         list->single_operation_lock->owner = -1;
00419         pthread_cond_wait(&list->nonempty,
00420                           &list->single_operation_lock->mutex);
00421         list->single_operation_lock->owner = gwthread_self();
00422     }
00423     if (list->len > 0) {
00424         item = GET(list, 0);
00425         delete_items_from_list(list, 0, 1);
00426     } else {
00427         item = NULL;
00428     }
00429     unlock(list);
00430     return item;
00431 }
00432 
00433 
00434 void *gwlist_timed_consume(List *list, long sec)
00435 {
00436     void *item;
00437     struct timespec abstime;
00438     int rc;
00439 
00440     abstime.tv_sec = time(NULL) + sec;
00441     abstime.tv_nsec = 0;
00442 
00443     lock(list);
00444     while (list->len == 0 && list->num_producers > 0) {
00445         list->single_operation_lock->owner = -1;
00446         rc = pthread_cond_timedwait(&list->nonempty,
00447                           &list->single_operation_lock->mutex, &abstime);
00448         list->single_operation_lock->owner = gwthread_self();
00449         if (rc == ETIMEDOUT)
00450             break;
00451     }
00452     if (list->len > 0) {
00453         item = GET(list, 0);
00454         delete_items_from_list(list, 0, 1);
00455     } else {
00456         item = NULL;
00457     }
00458     unlock(list);
00459     return item;
00460 }
00461 
00462 
00463 void *gwlist_search(List *list, void *pattern, int (*cmp)(void *, void *))
00464 {
00465     void *item;
00466     long i;
00467 
00468     lock(list);
00469     item = NULL;
00470     for (i = 0; i < list->len; ++i) {
00471         item = GET(list, i);
00472         if (cmp(item, pattern)) {
00473             break;
00474         }
00475     }
00476     if (i == list->len) {
00477         item = NULL;
00478     }
00479     unlock(list);
00480 
00481     return item;
00482 }
00483 
00484 
00485 
00486 List *gwlist_search_all(List *list, void *pattern, int (*cmp)(void *, void *))
00487 {
00488     List *new_list;
00489     void *item;
00490     long i;
00491 
00492     new_list = gwlist_create();
00493 
00494     lock(list);
00495     item = NULL;
00496     for (i = 0; i < list->len; ++i) {
00497         item = GET(list, i);
00498         if (cmp(item, pattern))
00499             gwlist_append(new_list, item);
00500     }
00501     unlock(list);
00502 
00503     if (gwlist_len(new_list) == 0) {
00504         gwlist_destroy(new_list, NULL);
00505         new_list = NULL;
00506     }
00507 
00508     return new_list;
00509 }
00510 
00511 
00512 void gwlist_sort(List *list, int(*cmp)(const void *, const void *))
00513 {
00514     gw_assert(list != NULL && cmp != NULL);
00515 
00516     lock(list);
00517     if (list->len == 0) {
00518         /* nothing to sort */
00519         unlock(list);
00520         return;
00521     }
00522     qsort(&GET(list, 0), list->len, sizeof(void*), cmp);
00523     unlock(list);
00524 }
00525 
00526 
00527 /*************************************************************************/
00528 
00529 static void lock(List *list)
00530 {
00531     gw_assert(list != NULL);
00532     mutex_lock(list->single_operation_lock);
00533 }
00534 
00535 static void unlock(List *list)
00536 {
00537     gw_assert(list != NULL);
00538     mutex_unlock(list->single_operation_lock);
00539 }
00540 
00541 
00542 /*
00543  * Make the array bigger. It might be more efficient to make the size
00544  * bigger than what is explicitly requested.
00545  *
00546  * Assume list has been locked for a single operation already.
00547  */
00548 static void make_bigger(List *list, long items)
00549 {
00550     long old_size, new_size;
00551     long len_at_beginning, len_at_end;
00552 
00553     if (list->len + items <= list->tab_size)
00554         return;
00555 
00556     old_size = list->tab_size;
00557     new_size = old_size + items;
00558     list->tab = gw_realloc(list->tab, new_size * sizeof(void *));
00559     list->tab_size = new_size;
00560 
00561     /*
00562      * Now, one of the following situations is in effect
00563      * (* is used, empty is unused element):
00564      *
00565      * Case 1: Used area did not wrap. No action is necessary.
00566      * 
00567      *             old_size              new_size
00568      *             v                     v
00569      * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
00570      * | |*|*|*|*|*|*| | | | | | | | | | | | | | | | |
00571      * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
00572      *   ^           ^
00573      *   start       start+len
00574      * 
00575      * Case 2: Used area wrapped, but the part at the beginning
00576      * of the array fits into the new area. Action: move part
00577      * from beginning to new area.
00578      * 
00579      *             old_size              new_size
00580      *             v                     v
00581      * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
00582      * |*|*| | | | | | | |*|*|*| | | | | | | | | | | |
00583      * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
00584      *     ^             ^
00585      *     start+len     start
00586      * 
00587      * Case 3: Used area wrapped, and the part at the beginning
00588      * of the array does not fit into the new area. Action: move
00589      * as much as will fit from beginning to new area and move
00590      * the rest to the beginning.
00591      * 
00592      *                    old_size   new_size
00593      *                       v   v
00594      * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
00595      * |*|*|*|*|*|*|*|*|*| | | | | | | | |*|*|*|*| | |
00596      * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
00597      *           ^               ^
00598      *           start+len       start
00599      */
00600 
00601     gw_assert(list->start < old_size ||
00602               (list->start == 0 && old_size == 0));
00603     if (list->start + list->len > old_size) {
00604         len_at_end = old_size - list->start;
00605         len_at_beginning = list->len - len_at_end;
00606         if (len_at_beginning <= new_size - old_size) {
00607             /* This is Case 2. */
00608             memmove(list->tab + old_size,
00609                     list->tab,
00610                     len_at_beginning * sizeof(void *));
00611         } else {
00612             /* This is Case 3. */
00613             memmove(list->tab + old_size,
00614                     list->tab,
00615                     (new_size - old_size) * sizeof(void *));
00616             memmove(list->tab,
00617                     list->tab + (new_size - old_size),
00618                     (len_at_beginning - (new_size - old_size))
00619                     * sizeof(void *));
00620         }
00621     }
00622 }
00623 
00624 
00625 /*
00626  * Remove items `pos' through `pos+count-1' from list. Assume list has
00627  * been locked by caller already.
00628  */
00629 static void delete_items_from_list(List *list, long pos, long count)
00630 {
00631     long i, from, to;
00632 
00633     gw_assert(pos >= 0);
00634     gw_assert(pos < list->len);
00635     gw_assert(count >= 0);
00636     gw_assert(pos + count <= list->len);
00637 
00638     /*
00639      * There are four cases:
00640      *
00641      * Case 1: Deletion at beginning of list. Just move start
00642      * marker forwards (wrapping it at end of array). No need
00643      * to move any items.
00644      *
00645      * Case 2: Deletion at end of list. Just shorten the length
00646      * of the list. No need to move any items.
00647      *
00648      * Case 3: Deletion in the middle so that the list does not
00649      * wrap in the array. Move remaining items at end of list
00650      * to the place of the deletion.
00651      *
00652      * Case 4: Deletion in the middle so that the list does indeed
00653      * wrap in the array. Move as many remaining items at the end
00654      * of the list as will fit to the end of the array, then move
00655      * the rest to the beginning of the array.
00656      */
00657     if (pos == 0) {
00658         list->start = (list->start + count) % list->tab_size;
00659         list->len -= count;
00660     } else if (pos + count == list->len) {
00661         list->len -= count;
00662     } else if (list->start + list->len < list->tab_size) {
00663         memmove(list->tab + list->start + pos,
00664                 list->tab + list->start + pos + count,
00665                 (list->len - pos - count) * sizeof(void *));
00666         list->len -= count;
00667     } else {
00668         /*
00669          * This is not specially efficient, but it's simple and
00670          * works. Faster methods would have to take more special
00671          * cases into account. 
00672          */
00673         for (i = 0; i < list->len - count - pos; ++i) {
00674             from = INDEX(list, pos + i + count);
00675             to = INDEX(list, pos + i);
00676             list->tab[to] = list->tab[from];
00677         }
00678         list->len -= count;
00679     }
00680 }
See file LICENSE for details about the license agreement for using, modifying, copying or deriving work from this software.