Browse Source

Fix #2591 spinlock issue. (#2637)

* Fix #2591 spinlock issue.
Add basic_test, for few integration test.
Add thread_local_storage support in cmake
Fix thread.c for linux and fix small typo
lighta 7 years ago
parent
commit
618b9356fc

+ 6 - 0
3rdparty/cmake/tests/HAVE_TLS.c

@@ -0,0 +1,6 @@
+__thread int tls;
+
+int main(int argc, char** argv)
+{
+	return 0;
+}

+ 1 - 1
3rdparty/yaml-cpp/CMakeLists.txt

@@ -79,5 +79,5 @@ set( YAML_DEFINITIONS
 	"-std=c++11"
 	"-std=c++11"
 	CACHE INTERNAL "yaml definitions" )
 	CACHE INTERNAL "yaml definitions" )
 include_directories(${YAML_INCLUDE_DIRS} ${YAML_SOURCE_DIR})
 include_directories(${YAML_INCLUDE_DIRS} ${YAML_SOURCE_DIR})
-message(STATUS "YAML_INCLUDE_DIRS : ${YAML_INCLUDE_DIRS}, YAML_SOURCE_DIR=${YAML_SOURCE_DIR}")
+#message(STATUS "YAML_INCLUDE_DIRS : ${YAML_INCLUDE_DIRS}, YAML_SOURCE_DIR=${YAML_SOURCE_DIR}")
 ADD_LIBRARY(${this_target} STATIC ${YAML_SOURCES} )
 ADD_LIBRARY(${this_target} STATIC ${YAML_SOURCES} )

+ 11 - 0
CMakeLists.txt

@@ -107,6 +107,7 @@ include( CheckFunctionExists )
 include( FindFunctionLibrary )
 include( FindFunctionLibrary )
 include( TestBigEndian )
 include( TestBigEndian )
 
 
+
 #
 #
 # PACKETVER
 # PACKETVER
 #
 #
@@ -149,6 +150,16 @@ if( CMAKE_THREAD_LIBS_INIT )
 endif()
 endif()
 message( STATUS "Detecting threads library - done" )
 message( STATUS "Detecting threads library - done" )
 
 
+message( STATUS "Check if supporting Thread local storage (TLS)" )
+file( READ "${CMAKE_SOURCE_DIR}/3rdparty/cmake/tests/HAVE_TLS.c" _SOURCE )
+CHECK_C_SOURCE_RUNS( "${_SOURCE}" HAVE_TLS )
+if( HAVE_TLS )
+	message( STATUS "Check for TLS- yes" )
+	set_property( CACHE GLOBAL_DEFINITIONS  PROPERTY VALUE "${GLOBAL_DEFINITIONS} -DHAVE_TLS" )
+else()
+	message( STATUS "Check for TLS - no" )
+endif()
+
 
 
 #
 #
 # math library (FreeBSD/Linux/Solaris)
 # math library (FreeBSD/Linux/Solaris)

+ 14 - 0
rAthena.sln

@@ -62,6 +62,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "map-cache", "src\tool\map-c
 EndProject
 EndProject
 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "yaml-cpp", "3rdparty\yaml-cpp\yaml-cpp.vcxproj", "{61D6A599-6BED-4154-A9FC-40553BD972E0}"
 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "yaml-cpp", "3rdparty\yaml-cpp\yaml-cpp.vcxproj", "{61D6A599-6BED-4154-A9FC-40553BD972E0}"
 EndProject
 EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "basic_test", "src\test\basic_test.vcxproj", "{99B4DF6A-6180-4E7F-9227-F812FF30414A}"
+EndProject
 Global
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Win32 = Debug|Win32
 		Debug|Win32 = Debug|Win32
@@ -142,6 +144,14 @@ Global
 		{61D6A599-6BED-4154-A9FC-40553BD972E0}.Release|Win32.Build.0 = Release|Win32
 		{61D6A599-6BED-4154-A9FC-40553BD972E0}.Release|Win32.Build.0 = Release|Win32
 		{61D6A599-6BED-4154-A9FC-40553BD972E0}.Release|x64.ActiveCfg = Release|x64
 		{61D6A599-6BED-4154-A9FC-40553BD972E0}.Release|x64.ActiveCfg = Release|x64
 		{61D6A599-6BED-4154-A9FC-40553BD972E0}.Release|x64.Build.0 = Release|x64
 		{61D6A599-6BED-4154-A9FC-40553BD972E0}.Release|x64.Build.0 = Release|x64
+		{99B4DF6A-6180-4E7F-9227-F812FF30414A}.Debug|Win32.ActiveCfg = Debug|Win32
+		{99B4DF6A-6180-4E7F-9227-F812FF30414A}.Debug|Win32.Build.0 = Debug|Win32
+		{99B4DF6A-6180-4E7F-9227-F812FF30414A}.Debug|x64.ActiveCfg = Debug|x64
+		{99B4DF6A-6180-4E7F-9227-F812FF30414A}.Debug|x64.Build.0 = Debug|x64
+		{99B4DF6A-6180-4E7F-9227-F812FF30414A}.Release|Win32.ActiveCfg = Release|Win32
+		{99B4DF6A-6180-4E7F-9227-F812FF30414A}.Release|Win32.Build.0 = Release|Win32
+		{99B4DF6A-6180-4E7F-9227-F812FF30414A}.Release|x64.ActiveCfg = Release|x64
+		{99B4DF6A-6180-4E7F-9227-F812FF30414A}.Release|x64.Build.0 = Release|x64
 	EndGlobalSection
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE
 		HideSolutionNode = FALSE
@@ -157,5 +167,9 @@ Global
 		{352B45B3-FE88-4431-9D89-48CF811446DB} = {C0A6FC9A-3A5C-48F8-A4B6-8D463C61C021}
 		{352B45B3-FE88-4431-9D89-48CF811446DB} = {C0A6FC9A-3A5C-48F8-A4B6-8D463C61C021}
 		{FC4C071B-2C26-4B03-948A-335C94A88B5E} = {9F328FE9-129D-4C0C-820B-BE4AA5996652}
 		{FC4C071B-2C26-4B03-948A-335C94A88B5E} = {9F328FE9-129D-4C0C-820B-BE4AA5996652}
 		{61D6A599-6BED-4154-A9FC-40553BD972E0} = {6ABA1767-6242-4CA0-BA22-A30972DC8918}
 		{61D6A599-6BED-4154-A9FC-40553BD972E0} = {6ABA1767-6242-4CA0-BA22-A30972DC8918}
+		{99B4DF6A-6180-4E7F-9227-F812FF30414A} = {9F328FE9-129D-4C0C-820B-BE4AA5996652}
+	EndGlobalSection
+	GlobalSection(ExtensibilityGlobals) = postSolution
+		SolutionGuid = {026DA20F-820C-40AA-983E-0E231EA90AD5}
 	EndGlobalSection
 	EndGlobalSection
 EndGlobal
 EndGlobal

+ 2 - 0
src/CMakeLists.txt

@@ -16,4 +16,6 @@ endif()
 add_subdirectory( login )
 add_subdirectory( login )
 add_subdirectory( char )
 add_subdirectory( char )
 add_subdirectory( map )
 add_subdirectory( map )
+add_subdirectory( test )
 add_subdirectory( tool )
 add_subdirectory( tool )
+

+ 2 - 0
src/char/CMakeLists.txt

@@ -22,7 +22,9 @@ source_group( common FILES ${COMMON_BASE_HEADERS} ${COMMON_HEADERS} )
 source_group( char FILES ${CHAR_HEADERS} ${CHAR_SOURCES} )
 source_group( char FILES ${CHAR_HEADERS} ${CHAR_SOURCES} )
 include_directories( ${INCLUDE_DIRS} )
 include_directories( ${INCLUDE_DIRS} )
 
 
+#message( STATUS "char-server SOURCE_FILES=${SOURCE_FILES}")
 add_executable( char-server ${SOURCE_FILES} )
 add_executable( char-server ${SOURCE_FILES} )
+#message( STATUS "char-server LIBRARIES=${LIBRARIES}, DEPENDENCIES=${DEPENDENCIES} DEFINITIONS=${DEFINITIONS}")
 add_dependencies( char-server ${DEPENDENCIES} )
 add_dependencies( char-server ${DEPENDENCIES} )
 target_link_libraries( char-server ${LIBRARIES} ${DEPENDENCIES} )
 target_link_libraries( char-server ${LIBRARIES} ${DEPENDENCIES} )
 set_target_properties( char-server PROPERTIES COMPILE_FLAGS "${DEFINITIONS}" )
 set_target_properties( char-server PROPERTIES COMPILE_FLAGS "${DEFINITIONS}" )

+ 2 - 0
src/common/CMakeLists.txt

@@ -139,6 +139,7 @@ source_group( mt19937ar FILES ${MT19937AR_HEADERS} ${MT19937AR_SOURCES} )
 source_group( common FILES ${COMMON_BASE_HEADERS} ${COMMON_BASE_SOURCES} )
 source_group( common FILES ${COMMON_BASE_HEADERS} ${COMMON_BASE_SOURCES} )
 
 
 add_library( common_base ${SOURCE_FILES} )
 add_library( common_base ${SOURCE_FILES} )
+#message( STATUS "common_base LIBRARIES=${LIBRARIES}, DEFINITIONS=${DEFINITIONS}")
 target_link_libraries( common_base ${LIBRARIES} )
 target_link_libraries( common_base ${LIBRARIES} )
 set_target_properties( common_base PROPERTIES COMPILE_FLAGS "${DEFINITIONS}" )
 set_target_properties( common_base PROPERTIES COMPILE_FLAGS "${DEFINITIONS}" )
 include_directories( ${INCLUDE_DIRS} )
 include_directories( ${INCLUDE_DIRS} )
@@ -172,6 +173,7 @@ set( SOURCE_FILES ${COMMON_HEADERS} ${COMMON_SOURCES} )
 source_group( common FILES ${COMMON_HEADERS} ${COMMON_SOURCES} )
 source_group( common FILES ${COMMON_HEADERS} ${COMMON_SOURCES} )
 
 
 add_library( common ${SOURCE_FILES} )
 add_library( common ${SOURCE_FILES} )
+#message( STATUS "common LIBRARIES=${LIBRARIES}, DEPENDENCIES=${DEPENDENCIES} DEFINITIONS=${DEFINITIONS}")
 add_dependencies( common ${DEPENDENCIES} )
 add_dependencies( common ${DEPENDENCIES} )
 target_link_libraries( common ${LIBRARIES} ${DEPENDENCIES} )
 target_link_libraries( common ${LIBRARIES} ${DEPENDENCIES} )
 set_target_properties( common PROPERTIES COMPILE_FLAGS "${DEFINITIONS}" )
 set_target_properties( common PROPERTIES COMPILE_FLAGS "${DEFINITIONS}" )

+ 5 - 7
src/common/spinlock.h

@@ -71,14 +71,11 @@ static forceinline void EnterSpinLock(PSPIN_LOCK lck){
 		
 		
 		
 		
 		// Spin until we've got it ! 
 		// Spin until we've got it ! 
-		while(1){
-				
-				if(InterlockedCompareExchange(&lck->lock, tid, 0) == 0){
-						
+		while(1){			
+				if(InterlockedCompareExchange(&lck->lock, tid, RA_INVALID_THID) == RA_INVALID_THID){					
 						InterlockedIncrement(&lck->nest);
 						InterlockedIncrement(&lck->nest);
 						return; // Got Lock
 						return; // Got Lock
 				}
 				}
-				
 				rathread_yield(); // Force ctxswitch to another thread.
 				rathread_yield(); // Force ctxswitch to another thread.
 		}
 		}
 
 
@@ -92,13 +89,14 @@ static forceinline void LeaveSpinLock(PSPIN_LOCK lck){
 		
 		
 		if(InterlockedCompareExchange(&lck->lock, tid, tid) == tid){ // this thread owns the lock.
 		if(InterlockedCompareExchange(&lck->lock, tid, tid) == tid){ // this thread owns the lock.
 			if(InterlockedDecrement(&lck->nest) == 0)
 			if(InterlockedDecrement(&lck->nest) == 0)
-					InterlockedExchange(&lck->lock, 0); // Unlock!
+					InterlockedExchange(&lck->lock, RA_INVALID_THID); // Unlock!
 		}
 		}
 		
 		
 		dropsynclock(&lck->sync_lock);
 		dropsynclock(&lck->sync_lock);
 }
 }
 
 
 
 
-
+#undef getsynclock
+#undef dropsynclock
 
 
 #endif
 #endif

+ 14 - 4
src/common/thread.c

@@ -25,7 +25,7 @@
 #include "thread.h"
 #include "thread.h"
 
 
 // When Compiling using MSC (on win32..) we know we have support in any case!
 // When Compiling using MSC (on win32..) we know we have support in any case!
-#ifdef _MSC_VER 
+#if defined(_MSC_VER ) && !defined(HAS_TLS)
 #define HAS_TLS 
 #define HAS_TLS 
 #endif
 #endif
 
 
@@ -48,7 +48,7 @@ struct rAthread {
 
 
 
 
 #ifdef HAS_TLS
 #ifdef HAS_TLS
-__thread int g_rathread_ID = -1;
+__thread int g_rathread_ID = RA_INVALID_THID;
 #endif
 #endif
 
 
 
 
@@ -57,6 +57,12 @@ __thread int g_rathread_ID = -1;
 ///
 ///
 static struct rAthread l_threads[RA_THREADS_MAX];
 static struct rAthread l_threads[RA_THREADS_MAX];
 
 
+prAthread rathenat_getThread( int idx ) {
+  if(idx < RA_THREADS_MAX)
+    return &l_threads[idx];
+  return NULL;
+}
+
 void rathread_init(){
 void rathread_init(){
 	register unsigned int i;
 	register unsigned int i;
 	memset(&l_threads, 0x00, RA_THREADS_MAX * sizeof(struct rAthread) );
 	memset(&l_threads, 0x00, RA_THREADS_MAX * sizeof(struct rAthread) );
@@ -70,7 +76,11 @@ void rathread_init(){
 	g_rathread_ID = 0;
 	g_rathread_ID = 0;
 #endif
 #endif
 	l_threads[0].prio = RAT_PRIO_NORMAL;
 	l_threads[0].prio = RAT_PRIO_NORMAL;
-	l_threads[0].proc = (rAthreadProc)0xDEADCAFE;
+#if defined(_WIN64) || defined(__x86_64__) || defined(__ppc64__)
+	l_threads[0].proc = (rAthreadProc)0xDEADCAFEDEADCAFE;
+#else
+  l_threads[0].proc = (rAthreadProc)0xDEADCAFE;
+#endif
 
 
 }//end: rathread_init()
 }//end: rathread_init()
 
 
@@ -221,7 +231,7 @@ void rathread_destroy ( prAthread handle ){
 		rat_thread_terminated(handle);
 		rat_thread_terminated(handle);
 	}
 	}
 #else
 #else
-	if( pthread_cancel( handle->hThread ) == 0){
+	if(handle->hThread && pthread_cancel( handle->hThread ) == 0){
 	
 	
 		// We have to join it, otherwise pthread wont re-cycle its internal ressources assoc. with this thread.
 		// We have to join it, otherwise pthread wont re-cycle its internal ressources assoc. with this thread.
 		// 
 		// 

+ 9 - 0
src/common/thread.h

@@ -20,6 +20,15 @@ typedef enum RATHREAD_PRIO {
 	RAT_PRIO_HIGH	
 	RAT_PRIO_HIGH	
 } RATHREAD_PRIO;
 } RATHREAD_PRIO;
 
 
+#define RA_INVALID_THID -1
+
+/**
+ * Get the handle of a specific thread 
+ *
+ * @param idx  -  The index of the thread
+ * @return not NULL if success
+ */
+prAthread rathenat_getThread( int idx );
 
 
 /**
 /**
  * Creates a new Thread
  * Creates a new Thread

+ 3 - 1
src/map/CMakeLists.txt

@@ -12,7 +12,7 @@ message( STATUS "Creating target map-server" )
 file(GLOB MAP_HEADERS ${MAP_SOURCE_DIR}/*.hpp)
 file(GLOB MAP_HEADERS ${MAP_SOURCE_DIR}/*.hpp)
 file(GLOB MAP_SOURCES ${MAP_SOURCE_DIR}/*.cpp)
 file(GLOB MAP_SOURCES ${MAP_SOURCE_DIR}/*.cpp)
 set( DEPENDENCIES common yaml-cpp)
 set( DEPENDENCIES common yaml-cpp)
-set( LIBRARIES ${GLOBAL_LIBRARIES} yaml-cpp)
+set( LIBRARIES ${GLOBAL_LIBRARIES})
 set( INCLUDE_DIRS ${GLOBAL_INCLUDE_DIRS} ${COMMON_BASE_INCLUDE_DIRS} )
 set( INCLUDE_DIRS ${GLOBAL_INCLUDE_DIRS} ${COMMON_BASE_INCLUDE_DIRS} )
 set( DEFINITIONS "${GLOBAL_DEFINITIONS} ${COMMON_BASE_DEFINITIONS}" )
 set( DEFINITIONS "${GLOBAL_DEFINITIONS} ${COMMON_BASE_DEFINITIONS}" )
 if( WITH_PCRE )
 if( WITH_PCRE )
@@ -28,7 +28,9 @@ source_group( common FILES ${COMMON_BASE_HEADERS} ${COMMON_HEADERS} )
 source_group( map FILES ${MAP_HEADERS} ${MAP_SOURCES} )
 source_group( map FILES ${MAP_HEADERS} ${MAP_SOURCES} )
 include_directories( ${INCLUDE_DIRS} )
 include_directories( ${INCLUDE_DIRS} )
 
 
+#message( STATUS "map-server SOURCE_FILES=${SOURCE_FILES}")
 add_executable( map-server ${SOURCE_FILES} )
 add_executable( map-server ${SOURCE_FILES} )
+#message( STATUS "map-server LIBRARIES=${LIBRARIES}, DEPENDENCIES=${DEPENDENCIES} DEFINITIONS=${DEFINITIONS}")
 add_dependencies( map-server ${DEPENDENCIES} )
 add_dependencies( map-server ${DEPENDENCIES} )
 target_link_libraries( map-server ${LIBRARIES} ${DEPENDENCIES} )
 target_link_libraries( map-server ${LIBRARIES} ${DEPENDENCIES} )
 set_target_properties( map-server PROPERTIES COMPILE_FLAGS "${DEFINITIONS}" )
 set_target_properties( map-server PROPERTIES COMPILE_FLAGS "${DEFINITIONS}" )

+ 41 - 0
src/test/CMakeLists.txt

@@ -0,0 +1,41 @@
+
+#
+# basic_test
+#
+
+option( BUILD_TESTS "build basic_test executable" ON )
+
+if( BUILD_TESTS )
+
+	set( TESTS_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}  CACHE INTERNAL "" )
+	message( STATUS "Creating target basic_test" )
+	file(GLOB TESTS_HEADERS ${TESTS_SOURCE_DIR}/*.hpp)
+	file(GLOB TESTS_SOURCES ${TESTS_SOURCE_DIR}/*.cpp)
+	
+	
+	set( DEPENDENCIES common)
+	set( LIBRARIES ${GLOBAL_LIBRARIES})
+	set( INCLUDE_DIRS ${GLOBAL_INCLUDE_DIRS} ${COMMON_BASE_INCLUDE_DIRS} )
+	set( DEFINITIONS "${GLOBAL_DEFINITIONS} ${COMMON_BASE_DEFINITIONS} -DCMAKE" )
+	set( SOURCE_FILES ${COMMON_BASE_HEADERS} ${COMMON_HEADERS} ${TESTS_HEADERS} ${TESTS_SOURCES} )
+	source_group( common FILES ${COMMON_BASE_HEADERS} ${COMMON_HEADERS} )
+	source_group( test FILES ${TESTS_HEADERS} ${TESTS_SOURCES} )
+	include_directories( ${INCLUDE_DIRS} )
+
+	#message( STATUS "basic_test SOURCE_FILES=${SOURCE_FILES}")
+	add_executable( basic_test ${SOURCE_FILES} )
+	#message( STATUS "basic_test LIBRARIES=${LIBRARIES}, DEPENDENCIES=${DEPENDENCIES} DEFINITIONS=${DEFINITIONS}")
+	add_dependencies( basic_test ${DEPENDENCIES} )
+	target_link_libraries( basic_test ${LIBRARIES} ${DEPENDENCIES} )
+	set_target_properties( basic_test PROPERTIES COMPILE_FLAGS "${DEFINITIONS}" )
+
+	if( INSTALL_COMPONENT_RUNTIME )
+		cpack_add_component( Runtime_tests DESCRIPTION "basic integration test" DISPLAY_NAME "basic_test" GROUP Runtime )
+		install( TARGETS basic_test
+			DESTINATION "."
+			COMPONENT Runtime_tests )
+	endif( INSTALL_COMPONENT_RUNTIME )
+	set( TARGET_LIST ${TARGET_LIST} basic_test  CACHE INTERNAL "" )
+	message( STATUS "Creating target basic_test - done" )
+
+endif( BUILD_TESTS )

+ 171 - 0
src/test/basic_test.vcxproj

@@ -0,0 +1,171 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup Label="ProjectConfigurations">
+    <ProjectConfiguration Include="Debug|Win32">
+      <Configuration>Debug</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|Win32">
+      <Configuration>Release</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Debug|x64">
+      <Configuration>Debug</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|x64">
+      <Configuration>Release</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+  </ItemGroup>
+  <PropertyGroup Label="Globals">
+    <ProjectGuid>{99B4DF6A-6180-4E7F-9227-F812FF30414A}</ProjectGuid>
+    <Keyword>Win32Proj</Keyword>
+    <RootNamespace>basic_test</RootNamespace>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <PlatformToolset>$(DefaultPlatformToolset)</PlatformToolset>
+    <CharacterSet>MultiByte</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <PlatformToolset>$(DefaultPlatformToolset)</PlatformToolset>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+    <CharacterSet>MultiByte</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <PlatformToolset>$(DefaultPlatformToolset)</PlatformToolset>
+    <CharacterSet>MultiByte</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <PlatformToolset>$(DefaultPlatformToolset)</PlatformToolset>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+    <CharacterSet>MultiByte</CharacterSet>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <ImportGroup Label="ExtensionSettings">
+  </ImportGroup>
+  <ImportGroup Label="Shared">
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <PropertyGroup Label="UserMacros" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <LinkIncremental>true</LinkIncremental>
+    <OutDir>$(SolutionDir)</OutDir>
+    <IntDir>$(SolutionDir).vs\build\$(ProjectName)\$(Platform)\$(Configuration)\</IntDir>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <LinkIncremental>true</LinkIncremental>
+    <OutDir>$(SolutionDir)</OutDir>
+    <IntDir>$(SolutionDir).vs\build\$(ProjectName)\$(Platform)\$(Configuration)\</IntDir>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <LinkIncremental>false</LinkIncremental>
+    <OutDir>$(SolutionDir)</OutDir>
+    <IntDir>$(SolutionDir).vs\build\$(ProjectName)\$(Platform)\$(Configuration)\</IntDir>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <LinkIncremental>false</LinkIncremental>
+    <OutDir>$(SolutionDir)</OutDir>
+    <IntDir>$(SolutionDir).vs\build\$(ProjectName)\$(Platform)\$(Configuration)\</IntDir>
+  </PropertyGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <ClCompile>
+      <PrecompiledHeader>NotUsing</PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <SDLCheck>true</SDLCheck>
+      <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <AdditionalDependencies>ws2_32.lib;$(SolutionDir).vs\build\common.lib;$(SolutionDir).vs\build\libconfig.lib;$(SolutionDir).vs\build\mt19937ar.lib;$(SolutionDir)3rdparty\zlib\lib\$(Platform)\zlib.lib;$(SolutionDir)3rdparty\pcre\lib\$(Platform)\pcre8.lib;$(SolutionDir)3rdparty\mysql\lib\$(Platform)\libmysql.lib;$(SolutionDir).vs\build\yaml-cpp.lib;%(AdditionalDependencies)</AdditionalDependencies>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <ClCompile>
+      <PrecompiledHeader>NotUsing</PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <SDLCheck>true</SDLCheck>
+      <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <AdditionalDependencies>ws2_32.lib;$(SolutionDir).vs\build\common.lib;$(SolutionDir).vs\build\libconfig.lib;$(SolutionDir).vs\build\mt19937ar.lib;$(SolutionDir)3rdparty\zlib\lib\$(Platform)\zlib.lib;$(SolutionDir)3rdparty\pcre\lib\$(Platform)\pcre8.lib;$(SolutionDir)3rdparty\mysql\lib\$(Platform)\libmysql.lib;$(SolutionDir).vs\build\yaml-cpp.lib;%(AdditionalDependencies)</AdditionalDependencies>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <ClCompile>
+      <PrecompiledHeader>NotUsing</PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>MaxSpeed</Optimization>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <SDLCheck>true</SDLCheck>
+      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <AdditionalDependencies>ws2_32.lib;$(SolutionDir).vs\build\common.lib;$(SolutionDir).vs\build\libconfig.lib;$(SolutionDir).vs\build\mt19937ar.lib;$(SolutionDir)3rdparty\zlib\lib\$(Platform)\zlib.lib;$(SolutionDir)3rdparty\pcre\lib\$(Platform)\pcre8.lib;$(SolutionDir)3rdparty\mysql\lib\$(Platform)\libmysql.lib;$(SolutionDir).vs\build\yaml-cpp.lib;%(AdditionalDependencies)</AdditionalDependencies>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <ClCompile>
+      <PrecompiledHeader>NotUsing</PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>MaxSpeed</Optimization>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <SDLCheck>true</SDLCheck>
+      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <AdditionalDependencies>ws2_32.lib;$(SolutionDir).vs\build\common.lib;$(SolutionDir).vs\build\libconfig.lib;$(SolutionDir).vs\build\mt19937ar.lib;$(SolutionDir)3rdparty\zlib\lib\$(Platform)\zlib.lib;$(SolutionDir)3rdparty\pcre\lib\$(Platform)\pcre8.lib;$(SolutionDir)3rdparty\mysql\lib\$(Platform)\libmysql.lib;$(SolutionDir).vs\build\yaml-cpp.lib;%(AdditionalDependencies)</AdditionalDependencies>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemGroup>
+    <ClCompile Include="basictest.cpp" />
+    <ClCompile Include="test_spinlock.cpp" />
+    <ClCompile Include="test_thread.cpp" />
+  </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="test_spinlock.hpp" />
+    <ClInclude Include="test_thread.hpp" />
+  </ItemGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets">
+  </ImportGroup>
+</Project>

+ 32 - 0
src/test/basic_test.vcxproj.filters

@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup>
+    <Filter Include="Source Files">
+      <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
+      <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
+    </Filter>
+    <Filter Include="Header Files">
+      <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
+      <Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
+    </Filter>
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="basictest.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="test_thread.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="test_spinlock.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+  </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="test_spinlock.hpp">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="test_thread.hpp">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+  </ItemGroup>
+</Project>

+ 39 - 0
src/test/basictest.cpp

@@ -0,0 +1,39 @@
+// basictest.cpp : Sets the entry point for the console application.
+//
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifndef _WIN32
+#include <unistd.h>
+#endif
+
+#include "../common/core.h"
+#include "test_thread.hpp"
+#include "test_spinlock.hpp"
+
+using namespace ra::unit_tests;
+
+int do_init( int, char** )
+{
+  test_thread_creation_and_wait();
+  test_thread_spinlock();
+  return 0;
+}
+
+//just some empty function to comply link
+void do_abort(void) {}
+void do_final(void) {}
+void set_server_type(void) {}
+
+//tmp tp avoid link issue in cmake
+#if defined(CMAKE) 
+#ifdef __cplusplus
+extern "C" {
+#endif
+ void Sql_Init(void) {}
+#ifdef __cplusplus
+}
+#endif
+#endif

+ 102 - 0
src/test/test_spinlock.cpp

@@ -0,0 +1,102 @@
+#include "test_spinlock.hpp"
+#include <chrono>
+#include <thread>
+
+#include "../common/thread.h"
+#include "../common/spinlock.h"
+#include "../common/showmsg.h"
+#include "test_thread.hpp"
+
+namespace ra 
+{
+  namespace unit_tests
+  {
+    static SPIN_LOCK lSpinLock;
+
+    void* thread_test_critical( void *x )
+    {
+      size_t count = 0;
+      while ( count++ < 10 )
+      { 
+        EnterSpinLock( &lSpinLock );
+        ShowStatus( "TH Entering critical_section recursion_count=%d iteration_count=%d\n", lSpinLock.nest,count );
+        std::this_thread::sleep_for(std::chrono::milliseconds( 100 )); //a simple test with critical section
+        ShowStatus( "TH Leaving critical_section recursion_count=%d\n iteration_count=%d", lSpinLock.nest,count );
+        LeaveSpinLock( &lSpinLock );
+        rathread_yield();
+        std::this_thread::sleep_for(std::chrono::milliseconds( 200 )); //let try to lose the lock here
+      }
+      return nullptr;
+    }
+
+    void test_thread_spinlock()
+    {
+      ShowStatus( "Testing test_thread_spinlock\n" );
+      InitializeSpinLock( &lSpinLock );
+
+      EnterSpinLock( &lSpinLock ); //fake critical section (we take it before creation to ensure we have the lock)
+      ShowStatus( "Main Entering critical_section recursion_count=%d\n", lSpinLock.nest );
+
+      prAthread lnd2th = rathread_create( &thread_test_critical, nullptr );
+      int lMainID = rathread_get_tid();
+      ShowStatus( "Continuing main th id=%d\n", lMainID );
+      rathread_yield();
+      std::this_thread::sleep_for( std::chrono::milliseconds( 100 ) ); //fake doing stuff
+      ShowStatus( "Main Leaving critical_section recursion_count=%d\n", lSpinLock.nest );
+      LeaveSpinLock( &lSpinLock );
+
+      std::this_thread::sleep_for( std::chrono::milliseconds( 100 ) ); //fake doing stuff
+      EnterSpinLock( &lSpinLock ); //now we should be waiting
+      ShowStatus( "Main Entering critical_section recursion_count=%d\n", lSpinLock.nest );
+      
+      EnterSpinLock( &lSpinLock ); //testing with a recursion
+      ShowStatus( "Main Entering critical_section recursion_count=%d\n", lSpinLock.nest );
+      std::this_thread::sleep_for( std::chrono::milliseconds( 100 ) ); //fake doing stuff
+      ShowStatus( "Main Leaving critical_section recursion_count=%d\n", lSpinLock.nest );
+      LeaveSpinLock( &lSpinLock );
+
+      ShowStatus( "Main Leaving critical_section recursion_count=%d\n", lSpinLock.nest );
+      LeaveSpinLock( &lSpinLock );
+
+      while ( !rathread_wait( lnd2th, nullptr ) )
+      {
+        ShowStatus( "Waiting 2nd thread\n" );
+        std::this_thread::sleep_for( std::chrono::seconds( 1 ) );
+      }
+      rathread_destroy( lnd2th );
+      FinalizeSpinLock( &lSpinLock );
+    }
+
+  }
+}
+
+/* Expected result
+[Status]: Testing test_thread_spinlock
+[Status]: Main Entering critical_section recursion_count=1
+[Status]: Continuing main th id=0
+[Status]: Main Leaving critical_section recursion_count=1
+[Status]: TH Entering critical_section recursion_count=1 iteration_count=1
+[Status]: TH Leaving critical_section recursion_count=1 iteration_count=1
+[Status]: Main Entering critical_section recursion_count=1
+[Status]: Main Entering critical_section recursion_count=2
+[Status]: Main Leaving critical_section recursion_count=2
+[Status]: Main Leaving critical_section recursion_count=1
+[Status]: TH Entering critical_section recursion_count=1 iteration_count=2
+[Status]: TH Leaving critical_section recursion_count=1 iteration_count=2
+[Status]: TH Entering critical_section recursion_count=1 iteration_count=3
+[Status]: TH Leaving critical_section recursion_count=1 iteration_count=3
+[Status]: TH Entering critical_section recursion_count=1 iteration_count=4
+[Status]: TH Leaving critical_section recursion_count=1 iteration_count=4
+[Status]: TH Entering critical_section recursion_count=1 iteration_count=5
+[Status]: TH Leaving critical_section recursion_count=1 iteration_count=5
+[Status]: TH Entering critical_section recursion_count=1 iteration_count=6
+[Status]: TH Leaving critical_section recursion_count=1 iteration_count=6
+[Status]: TH Entering critical_section recursion_count=1 iteration_count=7
+[Status]: TH Leaving critical_section recursion_count=1 iteration_count=7
+[Status]: TH Entering critical_section recursion_count=1 iteration_count=8
+[Status]: TH Leaving critical_section recursion_count=1 iteration_count=8
+[Status]: TH Entering critical_section recursion_count=1 iteration_count=9
+[Status]: TH Leaving critical_section recursion_count=1 iteration_count=9
+[Status]: TH Entering critical_section recursion_count=1 iteration_count=10
+[Status]: TH Leaving critical_section recursion_count=1 iteration_count=10
+*/

+ 12 - 0
src/test/test_spinlock.hpp

@@ -0,0 +1,12 @@
+#pragma once
+
+namespace ra 
+{
+  namespace unit_tests
+  {
+    //main
+    void test_thread_spinlock();
+    //subs
+    void* thread_test_critical( void *x ); //must match rAthreadProc signature
+  }
+}

+ 48 - 0
src/test/test_thread.cpp

@@ -0,0 +1,48 @@
+#include "test_thread.hpp"
+#include <chrono>
+#include <thread>
+
+#include "../common/thread.h"
+#include "../common/showmsg.h"
+
+
+namespace ra
+{
+  namespace unit_tests
+  {
+    void* thread_test(void *x)
+    {
+       ShowStatus( "Entering thread_test\n" );
+       int lTHID = rathread_get_tid();
+       ShowStatus( "test th id=%d\n",lTHID); 
+       std::this_thread::sleep_for(std::chrono::milliseconds(500));
+       ShowStatus( "Leaving thread_test\n" );
+       return nullptr;
+    }
+
+    void test_thread_creation_and_wait()
+    {
+      ShowStatus( "Testing thread_creation_and_wait\n" );
+      prAthread lnd2th = rathread_create(&thread_test,nullptr); 
+      //rathread_yield();
+      std::this_thread::sleep_for(std::chrono::milliseconds(100)); //fake doing stuff
+      int lMainID = rathread_get_tid();
+      ShowStatus( "Continuing main th id=%d\n",lMainID); 
+      while ( !rathread_wait( lnd2th, nullptr ) )
+      {
+          ShowStatus( "Waiting 2nd thread\n" );
+          std::this_thread::sleep_for(std::chrono::seconds(1));
+      }
+      rathread_destroy(lnd2th); //will be destroy anyway by manager if not
+    }
+
+  }
+}
+
+/* Expected result
+[Status]: Testing thread_creation_and_wait
+[Status]: Entering thread_test
+[Status]: test th id=1
+[Status]: Continuing main th id=0
+[Status]: Leaving thread_test
+*/

+ 12 - 0
src/test/test_thread.hpp

@@ -0,0 +1,12 @@
+#pragma once
+
+namespace ra 
+{
+  namespace unit_tests
+  {
+    //main
+    void test_thread_creation_and_wait();
+    //sub
+    void* thread_test( void *x ); //must match rAthreadProc signature
+  }
+}

+ 1 - 1
tools/config.pl

@@ -101,7 +101,7 @@ sub Main {
 			CHAR_PORT => "6121",
 			CHAR_PORT => "6121",
 			LOGIN_PORT => "6900",
 			LOGIN_PORT => "6900",
 			MD5_ENABLE => "yes",
 			MD5_ENABLE => "yes",
-			PINCODE_ENABLE => "no",
+			PINCODE_ENABLE => "yes",
 			SQL_HOST => "localhost",
 			SQL_HOST => "localhost",
 			SQL_PORT => "3306",
 			SQL_PORT => "3306",
 			SQL_UID => "ragnarok",
 			SQL_UID => "ragnarok",