Skip to content

Atomic single linked list (pj_atomic_slist) #4330

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 42 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
c011722
vs2022 compatible project files
LeonidGoltsblat Oct 20, 2024
81996e6
stack implementation and tests
LeonidGoltsblat Oct 20, 2024
b2ab472
code style fix
LeonidGoltsblat Oct 20, 2024
79062c1
fix to Added more logs (#4098)
LeonidGoltsblat Oct 27, 2024
267e1e0
remove Debug-Dynamic-Client and Release-Dynamic-Client configurations
LeonidGoltsblat Oct 25, 2024
79f47fb
use traditional select ioqueue instead of iocp
LeonidGoltsblat Oct 27, 2024
95f49c1
re-enabled lost stack test
LeonidGoltsblat Oct 27, 2024
97ba6f3
Merge branch 'stack' into pj_stack
LeonidGoltsblat Oct 27, 2024
24c959f
untabify
LeonidGoltsblat Oct 27, 2024
48f47a2
makefiles fixes
LeonidGoltsblat Oct 28, 2024
dd3bdc3
VS projects and solution files changes
LeonidGoltsblat Oct 28, 2024
52e0086
define appropriate PJ_POOL_ALIGNMENT for Windows platform
LeonidGoltsblat Oct 29, 2024
6a331a1
make files fix
LeonidGoltsblat Oct 29, 2024
5fec115
stack implementation files added to aconfugure.*
LeonidGoltsblat Oct 29, 2024
547b72d
merge conflicts resolved
LeonidGoltsblat Nov 24, 2024
61e69ef
merge conflicts resolved
LeonidGoltsblat Nov 24, 2024
722022d
Merge branch 'master' into pj_stack
LeonidGoltsblat Nov 24, 2024
44dd759
added some comments about data alignment and macros to control alignment
LeonidGoltsblat Nov 24, 2024
0588d57
Merge remote-tracking branch 'myfork/pj_stack' into pj_stack
LeonidGoltsblat Nov 24, 2024
f0eb2c4
stack_stress_test() added
LeonidGoltsblat Nov 26, 2024
246a20a
fix pj_stack multithreading for non-windows platforms
LeonidGoltsblat Nov 28, 2024
5db76f6
stack tests
LeonidGoltsblat Nov 29, 2024
1eea899
Merge branch 'pjsip:master' into pj_stack
LeonidGoltsblat Nov 29, 2024
37f4a8a
stack tests optimized
LeonidGoltsblat Nov 29, 2024
d7323fc
Merge branch 'pjsip:master' into pj_stack
LeonidGoltsblat Dec 3, 2024
abbad93
simplified pj_stack impl
LeonidGoltsblat Dec 8, 2024
3b0e0a4
some irreleveant changes in the patch removed
LeonidGoltsblat Dec 10, 2024
23610d6
pjlib\build\pjlib.vcxproj.filters
LeonidGoltsblat Dec 11, 2024
6fefbf5
copyright info added
LeonidGoltsblat Dec 11, 2024
6c83473
copyright 2
LeonidGoltsblat Dec 11, 2024
88e02f3
line ending
LeonidGoltsblat Dec 11, 2024
445f8a1
undone ..
LeonidGoltsblat Dec 11, 2024
9f38c20
line ending
LeonidGoltsblat Dec 12, 2024
40f57cc
rewind some unrelated changes
LeonidGoltsblat Dec 12, 2024
934ba4a
line ending
LeonidGoltsblat Dec 12, 2024
bb1307d
unrelated...
LeonidGoltsblat Dec 12, 2024
9841571
little formal changes
LeonidGoltsblat Dec 13, 2024
a102249
fix
LeonidGoltsblat Dec 13, 2024
a424e39
Merge remote-tracking branch 'github.com/master' into pj_stack
LeonidGoltsblat Dec 18, 2024
a05b77c
Merge branch 'pj_stack' into pj_atomic_slist
LeonidGoltsblat Feb 27, 2025
7d18f2c
code style fix
LeonidGoltsblat Mar 2, 2025
e78d602
pj_atomic_slist_size() fix and tests
LeonidGoltsblat Mar 5, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions pjlib/build/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ export _LDFLAGS := $(CC_LDFLAGS) $(OS_LDFLAGS) $(M_LDFLAGS) $(HOST_LDFLAGS) \
#
export PJLIB_SRCDIR = ../src/pj
export PJLIB_OBJS += $(OS_OBJS) $(M_OBJS) $(CC_OBJS) $(HOST_OBJS) \
activesock.o array.o atomic_queue.o config.o ctype.o errno.o except.o \
activesock.o array.o atomic_slist.o atomic_queue.o config.o ctype.o \
errno.o except.o \
fifobuf.o guid.o hash.o ip_helper_generic.o list.o lock.o log.o \
os_time_common.o os_info.o pool.o pool_buf.o pool_caching.o pool_dbg.o \
rand.o rbtree.o sock_common.o sock_qos_common.o \
Expand All @@ -45,7 +46,8 @@ export PJLIB_LDFLAGS += $(_LDFLAGS)
# Defines for building test application
#
export TEST_SRCDIR = ../src/pjlib-test
export TEST_OBJS += activesock.o atomic.o echo_clt.o errno.o exception.o \
export TEST_OBJS += activesock.o atomic.o atomic_slist.o \
echo_clt.o errno.o exception.o \
fifobuf.o file.o hash_test.o ioq_perf.o ioq_udp.o \
ioq_stress_test.o ioq_unreg.o ioq_tcp.o ioq_iocp_unreg_test.o \
list.o mutex.o os.o pool.o pool_perf.o rand.o rbtree.o \
Expand Down
2 changes: 2 additions & 0 deletions pjlib/build/pjlib.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -1000,6 +1000,7 @@
<ClCompile Include="..\src\pj\ssl_sock_ossl.c" />
<ClCompile Include="..\src\pj\ssl_sock_gtls.c" />
<ClCompile Include="..\src\pj\ssl_sock_schannel.c" />
<ClCompile Include="..\src\pj\atomic_slist.c" />
<ClCompile Include="..\src\pj\string.c" />
<ClCompile Include="..\src\pj\symbols.c">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug-Dynamic|Win32'">true</ExcludedFromBuild>
Expand Down Expand Up @@ -1098,6 +1099,7 @@
<ClInclude Include="..\include\pj\sock_qos.h" />
<ClInclude Include="..\include\pj\sock_select.h" />
<ClInclude Include="..\include\pj\ssl_sock.h" />
<ClInclude Include="..\include\pj\atomic_slist.h" />
<ClInclude Include="..\include\pj\string.h" />
<ClInclude Include="..\include\pj\string_i.h" />
<ClInclude Include="..\include\pj\timer.h" />
Expand Down
6 changes: 6 additions & 0 deletions pjlib/build/pjlib.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,9 @@
<ClCompile Include="..\src\pj\os_rwmutex.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\src\pj\atomic_slist.c">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\src\pj\ioqueue_common_abs.h">
Expand Down Expand Up @@ -337,6 +340,9 @@
<ClInclude Include="..\include\pj\ssl_sock.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\include\pj\atomic_slist.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\include\pj\string.h">
<Filter>Header Files</Filter>
</ClInclude>
Expand Down
1 change: 1 addition & 0 deletions pjlib/build/pjlib_test.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -789,6 +789,7 @@
<ClCompile Include="..\src\pjlib-test\sock.c" />
<ClCompile Include="..\src\pjlib-test\sock_perf.c" />
<ClCompile Include="..\src\pjlib-test\ssl_sock.c" />
<ClCompile Include="..\src\pjlib-test\atomic_slist.c" />
<ClCompile Include="..\src\pjlib-test\string.c" />
<ClCompile Include="..\src\pjlib-test\test.c" />
<ClCompile Include="..\src\pjlib-test\thread.c" />
Expand Down
3 changes: 3 additions & 0 deletions pjlib/build/pjlib_test.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,9 @@
<ClCompile Include="..\src\pjlib-test\ioq_iocp_unreg_test.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\src\pjlib-test\atomic_slist.c">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\src\pjlib-test\test.h">
Expand Down
236 changes: 236 additions & 0 deletions pjlib/include/pj/atomic_slist.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,236 @@
/*
* Copyright (C) 2008-2025 Teluu Inc. (http://www.teluu.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 2 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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef __PJ_ATOMIC_SLIST_H__
#define __PJ_ATOMIC_SLIST_H__

/* 2022-2025 Leonid Goltsblat <[email protected]> */

/**
* @file atomic_slist.h
* @brief Single Linked List data structure.
*/

#include <pj/types.h>

#if defined(PJ_ATOMIC_SLIST_IMPLEMENTATION) && PJ_ATOMIC_SLIST_IMPLEMENTATION==PJ_ATOMIC_SLIST_WIN32

/* the Windows platform atomic_slist implementation. */

/**
* PJ_ATOMIC_SLIST_ALIGN_PREFIX, PJ_ATOMIC_SLIST_ALIGN_SUFFIX is a readable syntax to declare
* platform default alignment for the atomic_slist item (see example below).
*/

# if defined(MEMORY_ALLOCATION_ALIGNMENT)
# define PJ_ATOMIC_SLIST_ALIGNMENT MEMORY_ALLOCATION_ALIGNMENT
# elif defined(_WIN64) || defined(_M_ALPHA)
# define PJ_ATOMIC_SLIST_ALIGNMENT 16
# else
# define PJ_ATOMIC_SLIST_ALIGNMENT 8
# endif

# define PJ_ATOMIC_SLIST_ALIGN_PREFIX PJ_ALIGN_DATA_PREFIX(PJ_ATOMIC_SLIST_ALIGNMENT)
# define PJ_ATOMIC_SLIST_ALIGN_SUFFIX PJ_ALIGN_DATA_SUFFIX(PJ_ATOMIC_SLIST_ALIGNMENT)

#else // PJ_ATOMIC_SLIST_IMPLEMENTATION != PJ_ATOMIC_SLIST_WIN32

/* compilation of crossplatform atomic_slist implementation */

# define PJ_ATOMIC_SLIST_ALIGNMENT 0

# define PJ_ATOMIC_SLIST_ALIGN_PREFIX
# define PJ_ATOMIC_SLIST_ALIGN_SUFFIX

#endif // PJ_ATOMIC_SLIST_IMPLEMENTATION


PJ_BEGIN_DECL

/*
* @defgroup PJ_DS Data Structure.
*/

/**
* @defgroup PJ_ATOMIC_SLIST Single Linked List with LIFO in/out policy
* @ingroup PJ_DS
* @{
*
* 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).
*
* \section pj_atomic_slist_example_sec Examples
*
* See below for examples on how to manipulate slist:
* - @ref page_pjlib_atomic_slist_test
*/


/**
* 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 @a 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;
*
* @hideinitializer
*/
#if defined(PJ_ATOMIC_SLIST_IMPLEMENTATION) && PJ_ATOMIC_SLIST_IMPLEMENTATION==PJ_ATOMIC_SLIST_WIN32
# define PJ_DECL_ATOMIC_SLIST_MEMBER(type) \
/** Slist @a next. */ \
SLIST_ENTRY next
#else // PJ_ATOMIC_SLIST_IMPLEMENTATION != PJ_ATOMIC_SLIST_WIN32
# define PJ_DECL_ATOMIC_SLIST_MEMBER(type) \
/** Slist @a next. */ \
type *next
#endif // PJ_ATOMIC_SLIST_IMPLEMENTATION


/**
* 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).
*
* @param pool Pool to allocate memory from.
* @param slist The slist head.
*
* @return PJ_SUCCESS or the appropriate error code.
*/
PJ_DECL(pj_status_t) pj_atomic_slist_create(pj_pool_t *pool,
pj_atomic_slist **slist);

/**
* Free OS resources allocated by pj_atomic_slist_create().
*
* @param slist The target slist.
*
* @return PJ_SUCCESS or the appropriate error code.
*/
PJ_DECL(pj_status_t) pj_atomic_slist_destroy(pj_atomic_slist *slist);


/**
* Insert (push) the node to the front of the slist
* as atomic (thread safe) operation.
*
* @param slist The slist.
* @param node The element to be inserted.
*
* @return PJ_SUCCESS or the appropriate error code.
*/
PJ_DECL(pj_status_t) pj_atomic_slist_push(pj_atomic_slist *slist,
pj_atomic_slist_node_t *node);


/**
* Extract (pop) element from the front of the slist
* (removing it from the slist) as atomic (thread safe) operation.
*
* @param slist The target slist.
*
* @return NULL if the slist is empty,
* or else pointer to element extracted from slist.
*/
PJ_DECL(pj_atomic_slist_node_t*) pj_atomic_slist_pop(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.
*
* @param slist The target slist.
*
* @return Number of elements.
*/
PJ_DECL(pj_size_t) pj_atomic_slist_size(/*const*/ pj_atomic_slist *slist);

/**
* 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.
*
* @param pool the pool.
* @param count the number of slist elements in the array.
* @param elem the size of individual slist element.
*
* @return Pointer to the allocated memory.
*/
PJ_DECL(void*) pj_atomic_slist_calloc(pj_pool_t *pool, pj_size_t count, pj_size_t elem);

/**
* @}
*/

PJ_END_DECL

#endif /* __PJ_ATOMIC_SLIST_H__ */

14 changes: 12 additions & 2 deletions pjlib/include/pj/compat/cc_gcc.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,18 @@

#define PJ_UNREACHED(x)

#define PJ_ALIGN_DATA(declaration, alignment) declaration __attribute__((aligned (alignment)))

/*
* Usage example:
*
* typedef struct PJ_ALIGN_DATA_PREFIX(8) a { int value; } PJ_ALIGN_DATA_SUFFIX(8) a;
* typedef struct PJ_ALIGN_DATA(a{ int value; }, 8) a;
*
* Both options are equivalent, but perhaps the first is a little more readable than the second.
*/
//#define PJ_ALIGN_DATA(declaration, alignment) declaration __attribute__((aligned (alignment)))
#define PJ_ALIGN_DATA_PREFIX(alignment)
#define PJ_ALIGN_DATA_SUFFIX(alignment) __attribute__((aligned (alignment)))
#define PJ_ALIGN_DATA(declaration, alignment) PJ_ALIGN_DATA_PREFIX(alignment) declaration PJ_ALIGN_DATA_SUFFIX(alignment)

#endif /* __PJ_COMPAT_CC_GCC_H__ */

16 changes: 14 additions & 2 deletions pjlib/include/pj/compat/cc_msvc.h
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,20 @@ typedef unsigned __int64 pj_uint64_t;

#define PJ_UNREACHED(x)

#define PJ_ALIGN_DATA(declaration, alignment) __declspec(align(alignment)) declaration

/*
* Usage example:
*
* typedef struct PJ_ALIGN_DATA_PREFIX(8) a { int value; } PJ_ALIGN_DATA_SUFFIX(8) a;
* typedef struct PJ_ALIGN_DATA(a{ int value; }, 8) a;
*
* Both options are equivalent, but perhaps the first is a little more readable than the second.
*/
//#define PJ_ALIGN_DATA(declaration, alignment) __declspec(align(alignment)) declaration
//#pragma warning(disable:4324) // structure padded due to align()
//#define PJ_ALIGN_DATA(declaration, alignment) __pragma(warning(push)) __pragma(warning(disable:4324)) __declspec(align(alignment)) declaration __pragma(warning(pop))
#define PJ_ALIGN_DATA_PREFIX(alignment) __pragma(warning(push)) __pragma(warning(disable:4324)) __declspec(align(alignment))
#define PJ_ALIGN_DATA_SUFFIX(alignment) __pragma(warning(pop))
#define PJ_ALIGN_DATA(declaration, alignment) PJ_ALIGN_DATA_PREFIX(alignment) declaration PJ_ALIGN_DATA_SUFFIX(alignment)

#endif /* __PJ_COMPAT_CC_MSVC_H__ */

28 changes: 28 additions & 0 deletions pjlib/include/pj/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -1216,6 +1216,34 @@
# define PJ_JNI_HAS_JNI_ONLOAD PJ_ANDROID
#endif

/**
* pj_atomic_slist implementation.
* Select one of these implementations in PJ_ATOMIC_SLIST_IMPLEMENTATION.
*/
/** Using os independent "cross-platform" implementation */
#define PJ_ATOMIC_SLIST_GENERIC 0

/** Using Windows's single linked list */
#define PJ_ATOMIC_SLIST_WIN32 1

/**
* Select which pj_atomic_slist implementation to use. Currently pjlib supports
* PJ_ATOMIC_SLIST_GENERIC, which uses internal pjsip os independent
* "cross-platform" implementation, and
* PJ_ATOMIC_SLIST_WIN32, which uses Windows's single linked list.
* The last option is very fast, but is supported on Windows platform only.
*
* Default is PJ_ATOMIC_SLIST_WIN32 on Windows platform,
* otherwise PJ_ATOMIC_SLIST_GENERIC.
*/
#ifndef PJ_ATOMIC_SLIST_IMPLEMENTATION
# ifdef PJ_WIN32
# define PJ_ATOMIC_SLIST_IMPLEMENTATION PJ_ATOMIC_SLIST_WIN32
# else
# define PJ_ATOMIC_SLIST_IMPLEMENTATION PJ_ATOMIC_SLIST_GENERIC
# endif // PJ_WIN32
#endif //PJ_ATOMIC_SLIST_IMPLEMENTATION


/** @} */

Expand Down
Loading
Loading