Skip to content

Commit

Permalink
Merge branch 'pj_stack' into pj_atomic_slist
Browse files Browse the repository at this point in the history
# Conflicts:
#	pjlib/build/Makefile
#	pjlib/build/pjlib_test.vcxproj.filters
#	pjlib/src/pjlib-test/test.c
  • Loading branch information
LeonidGoltsblat committed Feb 27, 2025
2 parents 398bc21 + a424e39 commit a05b77c
Show file tree
Hide file tree
Showing 15 changed files with 1,022 additions and 6 deletions.
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 <lgoltsblat@gmail.com> */

/**
* @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

0 comments on commit a05b77c

Please sign in to comment.