Group PJ_ATOMIC_SLIST

group PJ_ATOMIC_SLIST

Atomic slist in PJLIB is single-linked list with First In Last Out logic. Atomic slist is thread safe. Common PJLIB slist implementation uses internal locking mechanism so is thread-safe. Implementation for Windows platform uses locking free Windows embeded single linked list implementation. The performance of pj_atomic_slist implementation for Windows platform is considerably higher than cross-platform.

By default pjlib compile and link os independent “cross-platform” (generic) implementation. To select implementation you may optionaly define PJ_ATOMIC_SLIST_IMPLEMENTATION as PJ_ATOMIC_SLIST_WIN32 or PJ_ATOMIC_SLIST_GENERIC. The last option is default for all platforms except Windows where the default is PJ_ATOMIC_SLIST_WIN32.

Windows single linked list implementation (PJ_ATOMIC_SLIST_WIN32) requires aligned data, both slist item and slist itself should be aligned by 8 (for x86) or 16 (for x64) byte. winnt.h define MEMORY_ALLOCATION_ALIGNMENT macro for this purpose. For the same purpose pjsip defines PJ_ATOMIC_SLIST_ALIGNMENT macro calculating value based on curent platform. To use MEMORY_ALLOCATION_ALIGNMENT macro in the build system as the value of PJ_ATOMIC_SLIST_ALIGNMENT macro we recomend (this is optional) to add #include <windows.h> to your config_site.h. For other implementation PJ_ATOMIC_SLIST_ALIGNMENT macro is defined as 0, which causes pj_pool_aligned_alloc() to use the default pool alignment.

To allocate slist element (node) or array of slist nodes application should use call pj_pool_aligned_alloc() with PJ_ATOMIC_SLIST_ALIGNMENT as the value of an alignment parameter. To hide these implementation details from the application, the API provides a pj_atomic_slist_calloc() function that internally handles the implementation-specific alignment. However application developer should declare slist nodes types properly aligned. The macros PJ_ATOMIC_SLIST_ALIGN_PREFIX and PJ_ATOMIC_SLIST_ALIGN_SUFFIX are provided for this purpose (see below).

Atomic slist won’t require dynamic memory allocation (just as all PJLIB data structures). The slist here should be viewed more like a low level C slist instead of high level C++ slist (which normally are easier to use but requires dynamic memory allocations), therefore all caveats with C slist apply here too (such as you can NOT put a node in more than one slists).

Examples

See below for examples on how to manipulate slist:

  • Test: Slist

Defines

PJ_DECL_ATOMIC_SLIST_MEMBER(type)

Use PJ_DECL_ATOMIC_SLIST_MEMBER macro in the start of the structure declaration to declare that the structure can be used in the slist operation. This macro simply declares additional member next to the structure.

The full declaration of slist item should contain alignment macro and may look like this:

typedef struct PJ_ATOMIC_SLIST_ALIGN_PREFIX slist_node { PJ_DECL_ATOMIC_SLIST_MEMBER(struct slist_node); … your data here … } PJ_ATOMIC_SLIST_ALIGN_SUFFIX slist_node;

Functions

pj_status_t pj_atomic_slist_create(pj_pool_t *pool, pj_atomic_slist **slist)

Create the slist: allocate memory, allocate and initialize OS resources. Initially, the slist will have no member, and function pj_atomic_slist_pop() will always return NULL for the newly initialized slist (which indicates there are no any items in the slist currently).

Parameters:
  • pool – Pool to allocate memory from.

  • slist – The slist head.

Returns:

PJ_SUCCESS or the appropriate error code.

pj_status_t pj_atomic_slist_destroy(pj_atomic_slist *slist)

Free OS resources allocated by pj_atomic_slist_create().

Parameters:

slist – The target slist.

Returns:

PJ_SUCCESS or the appropriate error code.

pj_status_t pj_atomic_slist_push(pj_atomic_slist *slist, pj_atomic_slist_node_t *node)

Insert (push) the node to the front of the slist as atomic (thread safe) operation.

Parameters:
  • slist – The slist.

  • node – The element to be inserted.

Returns:

PJ_SUCCESS or the appropriate error code.

pj_atomic_slist_node_t *pj_atomic_slist_pop(pj_atomic_slist *slist)

Extract (pop) element from the front of the slist (removing it from the slist) as atomic (thread safe) operation.

Parameters:

slist – The target slist.

Returns:

NULL if the slist is empty, or else pointer to element extracted from slist.

pj_size_t pj_atomic_slist_size(pj_atomic_slist *slist)

Traverse the slist and get it’s elements quantity. The return value of pj_atomic_slist_size should not be relied upon in multithreaded applications because the item count can be changed at any time by another thread. For Windows platform returns the number of entries in the slist modulo 65535. For example, if the specified slist contains 65536 entries, pj_atomic_slist_size returns zero.

Parameters:

slist – The target slist.

Returns:

Number of elements.

void *pj_atomic_slist_calloc(pj_pool_t *pool, pj_size_t count, pj_size_t elem)

Allocate storage for slist nodes array or single node from the pool and initialize it to zero. This is simple wrapper arround pj_pool_aligned_alloc() or pj_pool_calloc() that internally handles the slist implementation-specific alignment.

Parameters:
  • pool – the pool.

  • count – the number of slist elements in the array.

  • elem – the size of individual slist element.

Returns:

Pointer to the allocated memory.