libslack(list) - list module
#include <slack/std.h> #include <slack/list.h>
typedef struct List List; typedef struct Lister Lister; typedef void list_release_t(void *item); typedef void *list_copy_t(const void *item); typedef int list_cmp_t(const void *a, const void *b); typedef void list_action_t(void *item, size_t *index, void *data); typedef void *list_map_t(void *item, size_t *index, void *data); typedef int list_query_t(void *item, size_t *index, void *data);
List *list_create(list_release_t *destroy); List *list_make(list_release_t *destroy, ...); List *list_vmake(list_release_t *destroy, va_list args); List *list_copy(const List *src, list_copy_t *copy); List *list_create_with_locker(Locker *locker, list_release_t *destroy); List *list_make_with_locker(Locker *locker, list_release_t *destroy, ...); List *list_vmake_with_locker(Locker *locker, list_release_t *destroy, va_list args); List *list_copy_with_locker(Locker *locker, const List *src, list_copy_t *copy); int list_rdlock(const List *list); int list_wrlock(const List *list); int list_unlock(const List *list); void list_release(List *list); void *list_destroy(List **list); int list_own(List *list, list_release_t *destroy); int list_own_unlocked(List *list, list_release_t *destroy); list_release_t *list_disown(List *list); list_release_t *list_disown_unlocked(List *list); void *list_item(const List *list, ssize_t index); void *list_item_unlocked(const List *list, ssize_t index); int list_item_int(const List *list, ssize_t index); int list_item_int_unlocked(const List *list, ssize_t index); int list_empty(const List *list); int list_empty_unlocked(const List *list); ssize_t list_length(const List *list); ssize_t list_length_unlocked(const List *list); ssize_t list_last(const List *list); ssize_t list_last_unlocked(const List *list); List *list_remove(List *list, ssize_t index); List *list_remove_unlocked(List *list, ssize_t index); List *list_remove_range(List *list, ssize_t index, ssize_t range); List *list_remove_range_unlocked(List *list, ssize_t index, ssize_t range); List *list_insert(List *list, ssize_t index, void *item); List *list_insert_unlocked(List *list, ssize_t index, void *item); List *list_insert_int(List *list, ssize_t index, int item); List *list_insert_int_unlocked(List *list, ssize_t index, int item); List *list_insert_list(List *list, ssize_t index, const List *src, list_copy_t *copy); List *list_insert_list_unlocked(List *list, ssize_t index, const List *src, list_copy_t *copy); List *list_append(List *list, void *item); List *list_append_unlocked(List *list, void *item); List *list_append_int(List *list, int item); List *list_append_int_unlocked(List *list, int item); List *list_append_list(List *list, const List *src, list_copy_t *copy); List *list_append_list_unlocked(List *list, const List *src, list_copy_t *copy); List *list_prepend(List *list, void *item); List *list_prepend_unlocked(List *list, void *item); List *list_prepend_int(List *list, int item); List *list_prepend_int_unlocked(List *list, int item); List *list_prepend_list(List *list, const List *src, list_copy_t *copy); List *list_prepend_list_unlocked(List *list, const List *src, list_copy_t *copy); List *list_replace(List *list, ssize_t index, ssize_t range, void *item); List *list_replace_unlocked(List *list, ssize_t index, ssize_t range, void *item); List *list_replace_int(List *list, ssize_t index, ssize_t range, int item); List *list_replace_int_unlocked(List *list, ssize_t index, ssize_t range, int item); List *list_replace_list(List *list, ssize_t index, ssize_t range, const List *src, list_copy_t *copy); List *list_replace_list_unlocked(List *list, ssize_t index, ssize_t range, const List *src, list_copy_t *copy); List *list_extract(const List *list, ssize_t index, ssize_t range, list_copy_t *copy); List *list_extract_unlocked(const List *list, ssize_t index, ssize_t range, list_copy_t *copy); List *list_extract_with_locker(Locker *locker, const List *list, ssize_t index, ssize_t range, list_copy_t *copy); List *list_extract_with_locker_unlocked(Locker *locker, const List *list, ssize_t index, ssize_t range, list_copy_t *copy); List *list_push(List *list, void *item); List *list_push_unlocked(List *list, void *item); List *list_push_int(List *list, int item); List *list_push_int_unlocked(List *list, int item); void *list_pop(List *list); void *list_pop_unlocked(List *list); int list_pop_int(List *list); int list_pop_int_unlocked(List *list); void *list_shift(List *list); void *list_shift_unlocked(List *list); int list_shift_int(List *list); int list_shift_int_unlocked(List *list); List *list_unshift(List *list, void *item); List *list_unshift_unlocked(List *list, void *item); List *list_unshift_int(List *list, int item); List *list_unshift_int_unlocked(List *list, int item); List *list_splice(List *list, ssize_t index, ssize_t range, list_copy_t *copy); List *list_splice_unlocked(List *list, ssize_t index, ssize_t range, list_copy_t *copy); List *list_splice_with_locker(Locker *locker, List *list, ssize_t index, ssize_t range, list_copy_t *copy); List *list_splice_with_locker_unlocked(Locker *locker, List *list, ssize_t index, ssize_t range, list_copy_t *copy); List *list_sort(List *list, list_cmp_t *cmp); List *list_sort_unlocked(List *list, list_cmp_t *cmp); void list_apply(List *list, list_action_t *action, void *data); void list_apply_rdlocked(List *list, list_action_t *action, void *data); void list_apply_wrlocked(List *list, list_action_t *action, void *data); void list_apply_unlocked(List *list, list_action_t *action, void *data); List *list_map(List *list, list_release_t *destroy, list_map_t *map, void *data); List *list_map_unlocked(List *list, list_release_t *destroy, list_map_t *map, void *data); List *list_map_with_locker(Locker *locker, List *list, list_release_t *destroy, list_map_t *map, void *data); List *list_map_with_locker_unlocked(Locker *locker, List *list, list_release_t *destroy, list_map_t *map, void *data); List *list_grep(List *list, list_query_t *grep, void *data); List *list_grep_unlocked(List *list, list_query_t *grep, void *data); List *list_grep_with_locker(Locker *locker, List *list, list_query_t *grep, void *data); List *list_grep_with_locker_unlocked(Locker *locker, List *list, list_query_t *grep, void *data); ssize_t list_query(List *list, ssize_t *index, list_query_t *query, void *data); ssize_t list_query_unlocked(List *list, ssize_t *index, list_query_t *query, void *data); Lister *lister_create(List *list); Lister *lister_create_rdlocked(List *list); Lister *lister_create_wrlocked(List *list); Lister *lister_create_unlocked(const List *list); void lister_release(Lister *lister); void lister_release_unlocked(Lister *lister); void *lister_destroy(Lister **lister); void *lister_destroy_unlocked(Lister **lister); int lister_has_next(Lister *lister); void *lister_next(Lister *lister); int lister_next_int(Lister *lister); void lister_remove(Lister *lister); int list_has_next(List *list); void list_break(List *list); void *list_next(List *list); int list_next_int(List *list); void list_remove_current(List *list);
This module provides functions for manipulating and iterating over lists of
homogeneous data (or heterogeneous data if it's polymorphic). Lists may
own their items. Lists created with a non-null
destroy function use
that function to destroy an item when it is removed from the list and to
destroy each item when the list itself is destroyed. Be careful not to
insert items owned by one list into a list that doesn't own its own items
unless you know that the source list (and all of the shared items) will
outlive the destination list.
List *list_create(list_release_t *destroy)
destroy
as its item destructor. It is the caller's
responsbility to deallocate the new list with list_release(3) or
list_destroy(3). On success, returns the new list. On error, returns
null
with errno
set appropriately.
List *list_make(list_release_t *destroy, ...)
destroy
as its item destructor and the remaining
arguments as its initial items. It is the caller's responsibility to
deallocate the new list with list_release(3) or list_destroy(3). On
success, returns the new list. On error, return null
with errno
set
appropriately.
List *list_vmake(list_release_t *destroy, va_list args)
List *list_copy(const List *src, list_copy_t *copy)
src
using copy
as the copy constructor (if not
null
). It is the caller's responsibility to deallocate the new list with
list_release(3) or list_destroy(3). On success, returns the new copy.
On error, returns null
with errno
set appropriately.
List *list_create_with_locker(Locker *locker, list_release_t *destroy)
locker
.
List *list_make_with_locker(Locker *locker, list_release_t *destroy, ...)
locker
.
List *list_vmake_with_locker(Locker *locker, list_release_t *destroy, va_list args)
locker
.
List *list_copy_with_locker(Locker *locker, const List *src, list_copy_t *copy)
locker
.
int list_rdlock(const List *list)
list
(if list
was created with a Locker).
This is needed when multiple read only list(3) module functions
need to be called atomically. It is the client's responsibility to call
list_unlock(3) after the atomic operation. The only functions that may be
called on list
between calls to list_rdlock(3) and list_unlock(3)
are any read only list(3) module functions whose name ends with
_unlocked
. On success, returns 0
. On error, returns an error code.
int list_wrlock(const List *list)
list
(if list
was created with a Locker).
This is needed when multiple read/write list(3) module functions
need to be called atomically. It is the client's responsibility to call
list_unlock(3) after the atomic operation. The only functions that may be
called on list
between calls to list_wrlock(3) and list_unlock(3)
are any list(3) module functions whose name ends with
_unlocked
. On success, returns 0
. On error, returns an error code.
int list_unlock(const List *list)
list
obtained with list_rdlock(3) or
list_wrlock(3) (if list
was created with a locker
). On success,
returns 0
. On error, returns an error code.
void list_release(List *list)
list
, destroying its items if necessary. On error,
sets errno
appropriately.
void *list_destroy(List **list)
null
) *list
. Returns null
.
Note: lists shared by multiple threads must not be destroyed until after
all threads have finished with it.
int list_own(List *list, list_release_t *destroy)
list
to take ownership of its items. The items will be destroyed
using destroy
when they are removed or when list
is destroyed. On
success, returns 0
. On error, returns -1
with errno
set
appropriately.
int list_own_unlocked(List *list, list_release_t *destroy)
list
is not write locked.
list_release_t *list_disown(List *list)
list
to relinquish ownership of its items. The items will not be
destroyed when they are removed from list
or when list
is destroyed.
On success, returns the previous destroy function, if any. On error, returns
null
with errno
set appropriately.
list_release_t *list_disown_unlocked(List *list)
list
is not write locked.
void *list_item(const List *list, ssize_t index)
index
'th item in list
. If index
is negative, it refers
to an item position relative to the end of the list (-1
is the position
after the last item, -2
is the position of the last item and so on). On
error, returns null
with errno
set appropriately.
void *list_item_unlocked(const List *list, ssize_t index)
list
is not read locked.
int list_item_int(const List *list, ssize_t index)
int list_item_int_unlocked(const List *list, ssize_t index)
list
is not read locked.
int list_empty(const List *list)
list
is empty. On error, returns -1
with
errno
set appropriately.
int list_empty_unlocked(const List *list)
list
is not read locked.
ssize_t list_length(const List *list)
list
. On error, returns -1
with errno
set
appropriately.
ssize_t list_length_unlocked(const List *list)
list
is not read locked.
ssize_t list_last(const List *list)
list
, or -1
if there are no
items. On error, returns -1
with errno
set appropriately.
ssize_t list_last_unlocked(const List *list)
list
is not read locked.
List *list_remove(List *list, ssize_t index)
index
'th item from list
. If index
is negative, it
refers to an item position relative to the end of the list (-1
is the
position after the last item, -2
is the position of the last item and so
on). On success, returns list
. On error, returns null
with errno
set appropriately.
List *list_remove_unlocked(List *list, ssize_t index)
list
is not write locked.
List *list_remove_range(List *list, ssize_t index, ssize_t range)
range
items from list
starting at index
. If index
or
range
are negative, they refer to item positions relative to the end of
the list (-1
is the position after the last item, -2
is the position
of the last item and so on). On success, returns list
. On error, returns
null
with errno
set appropriately.
List *list_remove_range_unlocked(List *list, ssize_t index, ssize_t range)
list
is not write
locked.
List *list_insert(List *list, ssize_t index, void *item)
item
to list
at position index
. If index
is negative, it
refers to an item position relative to the end of the list (-1
is the
position after the last item, -2
is the position of the last item and so
on). On success, returns list
. On error, returns null
with errno
set appropriately.
List *list_insert_unlocked(List *list, ssize_t index, void *item)
list
is not write locked.
List *list_insert_int(List *list, ssize_t index, int item)
item
is an integer type.
List *list_insert_int_unlocked(List *list, ssize_t index, int item)
list
is not write locked.
List *list_insert_list(List *list, ssize_t index, const List *src, list_copy_t *copy)
src
into list
, starting at position index
using copy
as the copy constructor (if not null
). If index
is
negative, it refers to an item position relative to the end of the list
(-1
is the position after the last item, -2
is the position of the
last item and so on). On success, returns list
. On error, returns null
with errno
set appropriately.
List *list_insert_list_unlocked(List *list, ssize_t index, const List *src, list_copy_t *copy)
list
is not write locked
and src
is not read locked. Note: If src
needs to be read locked, it
is the caller's responsibility to lock and unlock it explicitly with
list_rdlock(3) and list_unlock(3).
List *list_append(List *list, void *item)
item
to list
. On success, returns list
. On error, returns
null
with errno
set appropriately.
List *list_append_unlocked(List *list, void *item)
list
is not write locked.
List *list_append_int(List *list, int item)
item
is an integer.
List *list_append_int_unlocked(List *list, int item)
list
is not write locked.
List *list_append_list(List *list, const List *src, list_copy_t *copy)
src
to list
using copy
as the copy constructor
(if not null
). On success, returns list
. On error, returns null
with errno
set appropriately.
List *list_append_list_unlocked(List *list, const List *src, list_copy_t *copy)
list
is not write
locked.
List *list_prepend(List *list, void *item)
item
to list
. On success, returns list
. On error, returns
null
with errno
set appropriately.
List *list_prepend_unlocked(List *list, void *item)
list
is not write locked.
List *list_prepend_int(List *list, int item)
item
is an integer.
List *list_prepend_int_unlocked(List *list, int item)
list
is not write
locked.
List *list_prepend_list(List *list, const List *src, list_copy_t *copy)
src
to list
using copy
as the copy
constructor (if not null
). On success, returns list
. On error, returns
null
with errno
set appropriately.
List *list_prepend_list_unlocked(List *list, const List *src, list_copy_t *copy)
list
is not write
locked.
List *list_replace(List *list, ssize_t index, ssize_t range, void *item)
range
items in list
, starting at index
, with item
. If
index
or range
are negative, they refer to item positions relative to
the end of the list (-1
is the position after the last item, -2
is the
position of the last item and so on). On success, returns list
. On error,
returns null
with errno
set appropriately.
List *list_replace_unlocked(List *list, ssize_t index, ssize_t range, void *item)
list
is not write locked.
List *list_replace_int(List *list, ssize_t index, ssize_t range, int item)
item
is an integer type.
List *list_replace_int_unlocked(List *list, ssize_t index, ssize_t range, int item)
list
is not write locked.
List *list_replace_list(List *list, ssize_t index, ssize_t range, const List *src, list_copy_t *copy)
range
items in list
, starting at index
, with the items in
src
using copy
as the copy constructor (if not null
). If index
or range
are negative, they refer to item positions relative to the end
of the list (-1
is the position after the last item, -2
is the
position of the last item and so on). On success, return list
. On error,
returns null
with errno
set appropriately.
List *list_replace_list_unlocked(List *list, ssize_t index, ssize_t range, const List *src, list_copy_t *copy)
list
is not write
locked and src
is not read locked. Note: If src
needs to be read
locked, it is the caller's responsibility to lock and unlock it explicitly
with list_rdlock(3) and list_unlock(3).
List *list_extract(const List *list, ssize_t index, ssize_t range, list_copy_t *copy)
range
items from list
, starting at
index
, using copy
as the copy constructor (if not null
). If
index
or range
are negative, they refer to item positions relative to
the end of the list (-1
is the position after the last item, -2
is the
position of the last item and so on). It is the caller's responsibility to
deallocate the new list with list_release(3) or list_destroy(3). On
success, returns the new list. On error, returns null
with errno
set
appropriately.
List *list_extract_unlocked(const List *list, ssize_t index, ssize_t range, list_copy_t *copy)
list
is not read locked.
List *list_extract_with_locker(Locker *locker, const List *list, ssize_t index, ssize_t range, list_copy_t *copy)
locker
.
List *list_extract_with_locker_unlocked(Locker *locker, const List *list, ssize_t index, ssize_t range, list_copy_t *copy)
list
is not read
locked.
List *list_push(List *list, void *item)
item
onto the end of list
. On success, returns list
. On
error, returns null
with errno
set appropriately.
List *list_push_unlocked(List *list, void *item)
list
is not write locked.
List *list_push_int(List *list, int item)
item
is an integer.
List *list_push_int_unlocked(List *list, int item)
list
is not write locked.
void *list_pop(List *list)
list
. On success, returns the item popped. On
error, returns null
with errno
set appropriately.
void *list_pop_unlocked(List *list)
list
is not write locked.
int list_pop_int(List *list)
int list_pop_int_unlocked(List *list)
list
is not write locked.
void *list_shift(List *list)
list
. On success, returns the item
shifted. On error, returns null
with errno
set appropriately.
void *list_shift_unlocked(List *list)
list
is not write locked.
int list_shift_int(List *list)
int list_shift_int_unlocked(List *list)
list
is not write locked.
List *list_unshift(List *list, void *item)
item
at the start of list
. On success, returns list
. On
error, returns null
with errno
set appropriately.
List *list_unshift_unlocked(List *list, void *item)
list
is not write locked.
List *list_unshift_int(List *list, int item)
item
is an integer.
List *list_unshift_int_unlocked(List *list, int item)
list
is not write
locked.
List *list_splice(List *list, ssize_t index, ssize_t range, list_copy_t *copy)
list
starting at index
with length range
after copying the items with copy
(if not null
). If index
or
range
are negative, they refer to item positions relative to the end of
the list (-1
is the position after the last item, -2
is the position
of the last item and so on). On success, returns the sublist. It is the
caller's responsibility to deallocate the new list with list_release(3)
or list_destroy(3). On error, returns null
with errno
set
appropriately.
List *list_splice_unlocked(List *list, ssize_t index, ssize_t range, list_copy_t *copy)
list
is not write locked.
List *list_splice_with_locker(Locker *locker, List *list, ssize_t index, ssize_t range, list_copy_t *copy)
locker
.
List *list_splice_with_locker_unlocked(Locker *locker, List *list, ssize_t index, ssize_t range, list_copy_t *copy)
list
is not write
locked.
List *list_sort(List *list, list_cmp_t *cmp)
list
using the item comparison function cmp
and
qsort(3) for lists of fewer than 10000 items and hsort(3) for larger
lists. On success, returns list
. On error, returns null
with errno
set appropriately.
List *list_sort_unlocked(List *list, list_cmp_t *cmp)
list
is not write locked.
void list_apply(List *list, list_action_t *action, void *data)
action
for each of list
's items. The arguments passed to
action
are the item, a pointer to the loop variable containing the item's
position within list
and data
. On error, sets errno
appropriately.
void list_apply_rdlocked(List *list, list_action_t *action, void *data)
list
is read locked rather
than write locked. Use this in preference to list_apply(3) when list
and its items will not be modified during the iteration.
void list_apply_wrlocked(List *list, list_action_t *action, void *data)
list
is write locked explicit.
void list_apply_unlocked(List *list, list_action_t *action, void *data)
list
is not write locked.
List *list_map(List *list, list_release_t *destroy, list_map_t *map, void *data)
map
,
invoked once for each item in list
. The arguments passed to map
are
the item, a pointer to the loop variable containing the item's position
within list
and data
. destroy
will be the item destructor for the
new list. It is the caller's responsibility to deallocate the new list with
list_release(3) or list_destroy(3). On success, returns the new list.
On error, returns null
with errno
set appropriately.
List *list_map_unlocked(List *list, list_release_t *destroy, list_map_t *map, void *data)
list
is not read locked.
List *list_map_with_locker(Locker *locker, List *list, list_release_t *destroy, list_map_t *map, void *data)
locker
.
List *list_map_with_locker_unlocked(Locker *locker, List *list, list_release_t *destroy, list_map_t *map, void *data)
list
is not read
locked.
List *list_grep(List *list, list_query_t *grep, void *data)
grep
for each item in list
,
and appending the items for which grep
returned a non-zero value. The
arguments passed to grep
are the item, a pointer to the loop variable
containing the item's position within list
and data
. It is the
caller's responsibility to deallocate the new list with list_release(3)
or list_destroy(3). Note that the new list does not own its items since
it does not copy them. On success, returns the new list. On error, returns
null
with errno
set appropriately.
List *list_grep_unlocked(List *list, list_query_t *grep, void *data)
list
is not read locked.
List *list_grep_with_locker(Locker *locker, List *list, list_query_t *grep, void *data)
locker
.
List *list_grep_with_locker_unlocked(Locker *locker, List *list, list_query_t *grep, void *data)
list
is not read
locked.
ssize_t list_query(List *list, ssize_t *index, list_query_t *query, void *data)
query
on each item in list
, starting at *index
until
query
returns a non-zero value. The arguments passed to query
are the
item, index
and data
. Returns the index of the item that satisfied
query
, or -1
when query
is not satisfied by any remaining items.
The value pointed to by index
is set to the return value. On error, sets
errno
appropriately.
ssize_t list_query_unlocked(List *list, ssize_t *index, list_query_t *query, void *data)
list
is not read locked.
Lister *lister_create(List *list)
list
. It is the caller's responsibility to
deallocate the iterator with lister_release(3) or lister_destroy(3).
The iterator keeps list
write locked until it is released with
lister_release(3) or lister_destroy(3). Note that the iterator itself
is not locked so it must not be shared between threads. On success, returns
the iterator. On error, returns null
with errno
set appropriately.
Lister *lister_create_rdlocked(List *list)
list
is read locked rather
than write locked. Use this in preference to lister_create(3) when no
calls to lister_remove(3) will be made during the iteration.
Lister *lister_create_wrlocked(List *list)
list
is write locked explicit.
Lister *lister_create_unlocked(const List *list)
list
is not write locked.
void lister_release(Lister *lister)
lister
and unlocks the associated list.
void lister_release_unlocked(Lister *lister)
void *lister_destroy(Lister **lister)
null
) *lister
and unlocks the
associated list. Returns null
. On error, sets errno
appropriately.
void *lister_destroy_unlocked(Lister **lister)
int lister_has_next(Lister *lister)
lister
is iterating. On error, returns -1
with errno
set
appropriately.
void *lister_next(Lister *lister)
lister
. On error, returns null
with errno
set appropriately.
int lister_next_int(Lister *lister)
void lister_remove(Lister *lister)
lister
. The next item in the
iteration is the item following the removed item, if any. This must be
called after lister_next(3) or lister_next_int(3). On error, sets
errno
appropriately.
int list_has_next(List *list)
list
using an internal
iterator. The first time this is called, a new internal Lister will be
created (Note: There can be only one). When there are no more items, returns
0
and destroys the internal iterator. When it returns 1
, use
list_next(3) or list_next_int(3) to retrieve the next item. On error,
returns -1
with errno
set appropriately.
Note: If an iteration using an internal iterator terminates before the end
of the list, it is the caller's responsibility to call list_break(3).
Failure to do so will cause the internal iterator to leak. It will also
break the next call to list_has_next(3) which will continue where the
current iteration stopped rather than starting at the beginning again.
list_release(3) assumes that there is no internal iterator so it is the
caller's responsibility to complete the iteration or call list_break(3)
before releasing list
with list_release(3) or list_destroy(3).
Note: The internal Lister does not lock list
so this function is not
threadsafe. It can only be used with lists created in the current function
(to guarantee that no other thread can access it). This practice should be
observed even in single threaded applications to avoid breaking iterator
semantics (possible with nested function calls). If list
is a parameter
or a variable declared outside the function, it is best to create an
explicit Lister instead. If this function is used on such lists instead,
it is the caller's responsibility to explicitly lock list
first with
list_wrlock(3) and explicitly unlock it with list_unlock(3). Do this
even if you are writing single threaded code in case your function may one
day be used in a multi threaded application.
void list_break(List *list)
list
and destroys its internal iterator. Must be used only when
an iteration using an internal iterator has terminated before reaching the
end of list
. On error, returns null
with errno
set appropriately.
void *list_next(List *list)
list
using it's internal iterator. On error,
returns null
with errno
set appropriately.
int list_next_int(List *list)
void list_remove_current(List *list)
list
using it's internal iterator. The next
item in the iteration is the item following the removed item, if any. This
must be called after list_next(3). On error, sets errno
appropriately.
On error, errno
is set either by an underlying function, or as follows:
null
or out of range.
MT-Disciplined
By default, Lists are not MT-Safe because most programs are single threaded and synchronisation doesn't come for free. Even in multi threaded programs, not all Lists are necessarily shared between multiple threads.
When a List is shared between multiple threads which need to be synchronised, the method of synchronisation must be carefully selected by the client code. There are tradeoffs between concurrency and overhead. The greater the concurrency, the greater the overhead. More locks give greater concurrency but have greater overhead. Readers/Writer locks can give greater concurrency than mutex locks but have greater overhead. One lock for each List may be required, or one lock for all (or a set of) Lists may be more appropriate.
Generally, the best synchronisation strategy for a given application can only be determined by testing/benchmarking the written application. It is important to be able to experiment with the synchronisation strategy at this stage of development without pain.
To facilitate this, Lists can be created with list_create_with_locker(3) which takes a Locker argument. The Locker specifies a lock and a set of functions for manipulating the lock. Each List can have it's own lock by creating a separate Locker for each List. Multiple Lists can share the same lock by sharing the same Locker. Only the application developer can determine what is appropriate for each application on a case by case basis.
MT-Disciplined means that the application developer has a mechanism for specifying the synchronisation requirements to be applied to library code.
Little attempt is made to protect the client from sharing items between lists with differing ownership policies and getting it wrong. When copying items from any list to an owning list, a copy function must be supplied. When adding a single item to an owning list, it is assumed that the list may take over ownership of the item. When an owning list is destroyed, all of its items are destroyed. If any of these items had been shared with a non-owning list that outlived the owning list, then the non-owning list will contain items that point to deallocated memory.
If you use an internal iterator in a loop that terminates before the end of the list, and fail to call list_break(3), the internal iterator will leak.
Uses malloc(3). The type of memory used and the allocation strategy need to be decoupled from this code.
libslack(3), map(3), mem(3), hsort(3), qsort(3), locker(3)
20020916 raf <raf@raf.org>