feature merge bs-coreoptimize->trunk: Atomic Operations, Threading, Spinlock implemnetation. [commit 1/2, windows will followup]

- Added Abstractions for Atomic Operations (lock instructions.. windows guy's may now this as Interlocked* stuff ..)
- Added Threading api abstraction for Pthread based OS's and Windows
- Added Spinlock Implementation (uses CAS / if you need more informations - just read the source - its simple.)

- Due to Interlocked(Compare)Exchange64 .. we now require at least i686 (Pentium Pro) for 32Bit Builds :)
	youll also may feel some performance improvements when using 32bit builsd due to "newer" minimal arch
	the compiler is now able to use CMOV's ....


================================================================
= Important Warning: 
================================================================
Dont use threading at the moment athena is not threadsafe!
you'll mess up everthing when accessing data from other threads .., no synchronization is provided.

A way to process tasks asynchronously will come up after / with the new socket system.




git-svn-id: https://svn.code.sf.net/p/rathena/svn/trunk@16221 54d463be-8e91-2dee-dedb-b68131a5f0ec
This commit is contained in:
blacksirius 2012-06-03 18:53:02 +00:00
parent 024deea2ac
commit 04165cfcff
11 changed files with 1256 additions and 2 deletions

View File

@ -28,6 +28,7 @@ endif
map_sql \
tools \
import \
test \
clean help
all: $(ALL_DEPENDS)
@ -55,6 +56,9 @@ map_sql: $(MAP_SQL_DEPENDS)
tools:
@$(MAKE) -C src/tool
test:
@$(MAKE) -C src/test
import:
# 1) create conf/import folder
# 2) add missing files
@ -72,6 +76,7 @@ clean:
@$(MAKE) -C src/char $@
@$(MAKE) -C src/map $@
@$(MAKE) -C src/tool $@
@$(MAKE) -C src/test $@
help:
@echo "most common targets are 'all' 'sql' 'conf' 'clean' 'help'"
@ -87,6 +92,7 @@ help:
@echo "'all' - builds all the above targets"
@echo "'sql' - builds sql servers (targets 'common_sql' 'login_sql' 'char_sql'"
@echo " 'map_sql' and 'import')"
@echo "'test' - builds tests"
@echo "'clean' - cleans builds and objects"
@echo "'help' - outputs this message"

381
configure vendored
View File

@ -1,5 +1,5 @@
#! /bin/sh
# From configure.in Revision: 16200 .
# From configure.in Revision: 16203 .
# Guess values for system-dependent variables and create Makefiles.
# Generated by GNU Autoconf 2.67.
#
@ -2126,6 +2126,8 @@ ac_config_files="$ac_config_files src/char/Makefile src/login/Makefile"
ac_config_files="$ac_config_files src/map/Makefile src/tool/Makefile"
ac_config_files="$ac_config_files src/test/Makefile"
ac_ext=c
ac_cpp='$CPP $CPPFLAGS'
@ -4666,6 +4668,46 @@ $as_echo "$pointers_fit_in_ints" >&6; }
fi
#
# check if we're producing 32bit code - so well produce binarys for at least i686 (speedup: cmovs, and cmpchg8 support)
#
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC produces 32bit code" >&5
$as_echo_n "checking whether $CC produces 32bit code... " >&6; }
if test "$cross_compiling" = yes; then :
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: guessing no" >&5
$as_echo "guessing no" >&6; }
else
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int main(int argc, char **argv){
if(sizeof(void*) == 4) return 0;
else return 1;
}
_ACEOF
if ac_fn_c_try_run "$LINENO"; then :
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
CFLAGS="$CFLAGS -march=i686"
LDFLAGS="$LDFLAGS -march=i686"
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
conftest.$ac_objext conftest.beam conftest.$ac_ext
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC supports -Wno-unused-parameter" >&5
$as_echo_n "checking whether $CC supports -Wno-unused-parameter... " >&6; }
OLD_CFLAGS="$CFLAGS"
@ -5284,6 +5326,342 @@ fi
fi
#
# pthread
#
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for pthread_create in -lpthread" >&5
$as_echo_n "checking for pthread_create in -lpthread... " >&6; }
if test "${ac_cv_lib_pthread_pthread_create+set}" = set; then :
$as_echo_n "(cached) " >&6
else
ac_check_lib_save_LIBS=$LIBS
LIBS="-lpthread $LIBS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
builtin and then its argument prototype would still apply. */
#ifdef __cplusplus
extern "C"
#endif
char pthread_create ();
int
main ()
{
return pthread_create ();
;
return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
ac_cv_lib_pthread_pthread_create=yes
else
ac_cv_lib_pthread_pthread_create=no
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
LIBS=$ac_check_lib_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_pthread_pthread_create" >&5
$as_echo "$ac_cv_lib_pthread_pthread_create" >&6; }
if test "x$ac_cv_lib_pthread_pthread_create" = x""yes; then :
cat >>confdefs.h <<_ACEOF
#define HAVE_LIBPTHREAD 1
_ACEOF
LIBS="-lpthread $LIBS"
else
as_fn_error $? "pthread library not found or incompatible" "$LINENO" 5
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for pthread_sigmask in -lpthread" >&5
$as_echo_n "checking for pthread_sigmask in -lpthread... " >&6; }
if test "${ac_cv_lib_pthread_pthread_sigmask+set}" = set; then :
$as_echo_n "(cached) " >&6
else
ac_check_lib_save_LIBS=$LIBS
LIBS="-lpthread $LIBS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
builtin and then its argument prototype would still apply. */
#ifdef __cplusplus
extern "C"
#endif
char pthread_sigmask ();
int
main ()
{
return pthread_sigmask ();
;
return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
ac_cv_lib_pthread_pthread_sigmask=yes
else
ac_cv_lib_pthread_pthread_sigmask=no
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
LIBS=$ac_check_lib_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_pthread_pthread_sigmask" >&5
$as_echo "$ac_cv_lib_pthread_pthread_sigmask" >&6; }
if test "x$ac_cv_lib_pthread_pthread_sigmask" = x""yes; then :
cat >>confdefs.h <<_ACEOF
#define HAVE_LIBPTHREAD 1
_ACEOF
LIBS="-lpthread $LIBS"
else
as_fn_error $? "pthread library not found or incompatible" "$LINENO" 5
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for pthread_attr_init in -lpthread" >&5
$as_echo_n "checking for pthread_attr_init in -lpthread... " >&6; }
if test "${ac_cv_lib_pthread_pthread_attr_init+set}" = set; then :
$as_echo_n "(cached) " >&6
else
ac_check_lib_save_LIBS=$LIBS
LIBS="-lpthread $LIBS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
builtin and then its argument prototype would still apply. */
#ifdef __cplusplus
extern "C"
#endif
char pthread_attr_init ();
int
main ()
{
return pthread_attr_init ();
;
return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
ac_cv_lib_pthread_pthread_attr_init=yes
else
ac_cv_lib_pthread_pthread_attr_init=no
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
LIBS=$ac_check_lib_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_pthread_pthread_attr_init" >&5
$as_echo "$ac_cv_lib_pthread_pthread_attr_init" >&6; }
if test "x$ac_cv_lib_pthread_pthread_attr_init" = x""yes; then :
cat >>confdefs.h <<_ACEOF
#define HAVE_LIBPTHREAD 1
_ACEOF
LIBS="-lpthread $LIBS"
else
as_fn_error $? "pthread library not found or incompatible" "$LINENO" 5
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for pthread_attr_setstacksize in -lpthread" >&5
$as_echo_n "checking for pthread_attr_setstacksize in -lpthread... " >&6; }
if test "${ac_cv_lib_pthread_pthread_attr_setstacksize+set}" = set; then :
$as_echo_n "(cached) " >&6
else
ac_check_lib_save_LIBS=$LIBS
LIBS="-lpthread $LIBS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
builtin and then its argument prototype would still apply. */
#ifdef __cplusplus
extern "C"
#endif
char pthread_attr_setstacksize ();
int
main ()
{
return pthread_attr_setstacksize ();
;
return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
ac_cv_lib_pthread_pthread_attr_setstacksize=yes
else
ac_cv_lib_pthread_pthread_attr_setstacksize=no
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
LIBS=$ac_check_lib_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_pthread_pthread_attr_setstacksize" >&5
$as_echo "$ac_cv_lib_pthread_pthread_attr_setstacksize" >&6; }
if test "x$ac_cv_lib_pthread_pthread_attr_setstacksize" = x""yes; then :
cat >>confdefs.h <<_ACEOF
#define HAVE_LIBPTHREAD 1
_ACEOF
LIBS="-lpthread $LIBS"
else
as_fn_error $? "pthread library not found or incompatible" "$LINENO" 5
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for pthread_attr_destroy in -lpthread" >&5
$as_echo_n "checking for pthread_attr_destroy in -lpthread... " >&6; }
if test "${ac_cv_lib_pthread_pthread_attr_destroy+set}" = set; then :
$as_echo_n "(cached) " >&6
else
ac_check_lib_save_LIBS=$LIBS
LIBS="-lpthread $LIBS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
builtin and then its argument prototype would still apply. */
#ifdef __cplusplus
extern "C"
#endif
char pthread_attr_destroy ();
int
main ()
{
return pthread_attr_destroy ();
;
return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
ac_cv_lib_pthread_pthread_attr_destroy=yes
else
ac_cv_lib_pthread_pthread_attr_destroy=no
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
LIBS=$ac_check_lib_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_pthread_pthread_attr_destroy" >&5
$as_echo "$ac_cv_lib_pthread_pthread_attr_destroy" >&6; }
if test "x$ac_cv_lib_pthread_pthread_attr_destroy" = x""yes; then :
cat >>confdefs.h <<_ACEOF
#define HAVE_LIBPTHREAD 1
_ACEOF
LIBS="-lpthread $LIBS"
else
as_fn_error $? "pthread library not found or incompatible" "$LINENO" 5
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for pthread_cancel in -lpthread" >&5
$as_echo_n "checking for pthread_cancel in -lpthread... " >&6; }
if test "${ac_cv_lib_pthread_pthread_cancel+set}" = set; then :
$as_echo_n "(cached) " >&6
else
ac_check_lib_save_LIBS=$LIBS
LIBS="-lpthread $LIBS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
builtin and then its argument prototype would still apply. */
#ifdef __cplusplus
extern "C"
#endif
char pthread_cancel ();
int
main ()
{
return pthread_cancel ();
;
return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
ac_cv_lib_pthread_pthread_cancel=yes
else
ac_cv_lib_pthread_pthread_cancel=no
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
LIBS=$ac_check_lib_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_pthread_pthread_cancel" >&5
$as_echo "$ac_cv_lib_pthread_pthread_cancel" >&6; }
if test "x$ac_cv_lib_pthread_pthread_cancel" = x""yes; then :
cat >>confdefs.h <<_ACEOF
#define HAVE_LIBPTHREAD 1
_ACEOF
LIBS="-lpthread $LIBS"
else
as_fn_error $? "pthread library not found or incompatible" "$LINENO" 5
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for pthread_join in -lpthread" >&5
$as_echo_n "checking for pthread_join in -lpthread... " >&6; }
if test "${ac_cv_lib_pthread_pthread_join+set}" = set; then :
$as_echo_n "(cached) " >&6
else
ac_check_lib_save_LIBS=$LIBS
LIBS="-lpthread $LIBS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
builtin and then its argument prototype would still apply. */
#ifdef __cplusplus
extern "C"
#endif
char pthread_join ();
int
main ()
{
return pthread_join ();
;
return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
ac_cv_lib_pthread_pthread_join=yes
else
ac_cv_lib_pthread_pthread_join=no
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
LIBS=$ac_check_lib_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_pthread_pthread_join" >&5
$as_echo "$ac_cv_lib_pthread_pthread_join" >&6; }
if test "x$ac_cv_lib_pthread_pthread_join" = x""yes; then :
cat >>confdefs.h <<_ACEOF
#define HAVE_LIBPTHREAD 1
_ACEOF
LIBS="-lpthread $LIBS"
else
as_fn_error $? "pthread library not found or incompatible" "$LINENO" 5
fi
LDFLAGS="$LDFLAGS -lpthread"
#
# MySQL library
#
@ -6304,6 +6682,7 @@ do
"src/login/Makefile") CONFIG_FILES="$CONFIG_FILES src/login/Makefile" ;;
"src/map/Makefile") CONFIG_FILES="$CONFIG_FILES src/map/Makefile" ;;
"src/tool/Makefile") CONFIG_FILES="$CONFIG_FILES src/tool/Makefile" ;;
"src/test/Makefile") CONFIG_FILES="$CONFIG_FILES src/test/Makefile" ;;
*) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5 ;;
esac

View File

@ -9,6 +9,7 @@ AC_CONFIG_FILES([Makefile src/common/Makefile])
AC_CONFIG_FILES([3rdparty/mt19937ar/Makefile 3rdparty/libconfig/Makefile])
AC_CONFIG_FILES([src/char/Makefile src/login/Makefile])
AC_CONFIG_FILES([src/map/Makefile src/tool/Makefile])
AC_CONFIG_FILES([src/test/Makefile])
AC_GNU_SOURCE
@ -337,6 +338,31 @@ if test "$enable_64bit" = "no" ; then
fi
#
# check if we're producing 32bit code - so well produce binarys for at least i686 (speedup: cmovs, and cmpchg8 support)
#
AC_MSG_CHECKING([whether $CC produces 32bit code])
AC_RUN_IFELSE(
[
int main(int argc, char **argv){
if(sizeof(void*) == 4) return 0;
else return 1;
}
],
[
AC_MSG_RESULT([yes])
CFLAGS="$CFLAGS -march=i686"
LDFLAGS="$LDFLAGS -march=i686"
],
[
AC_MSG_RESULT([no])
],
[
AC_MSG_RESULT([guessing no])
]
)
AC_MSG_CHECKING([whether $CC supports -Wno-unused-parameter])
OLD_CFLAGS="$CFLAGS"
CFLAGS="$CFLAGS -Wno-unused-parameter"
@ -636,6 +662,20 @@ if test "$ac_cv_search_clock_gettime" != "no" ; then
fi
#
# pthread
#
AC_CHECK_LIB([pthread], [pthread_create], [], [AC_MSG_ERROR([pthread library not found or incompatible])])
AC_CHECK_LIB([pthread], [pthread_sigmask], [], [AC_MSG_ERROR([pthread library not found or incompatible])])
AC_CHECK_LIB([pthread], [pthread_attr_init], [], [AC_MSG_ERROR([pthread library not found or incompatible])])
AC_CHECK_LIB([pthread], [pthread_attr_setstacksize], [], [AC_MSG_ERROR([pthread library not found or incompatible])])
AC_CHECK_LIB([pthread], [pthread_attr_destroy], [], [AC_MSG_ERROR([pthread library not found or incompatible])])
AC_CHECK_LIB([pthread], [pthread_cancel], [], [AC_MSG_ERROR([pthread library not found or incompatible])])
AC_CHECK_LIB([pthread], [pthread_join], [], [AC_MSG_ERROR([pthread library not found or incompatible])])
LDFLAGS="$LDFLAGS -lpthread"
#
# MySQL library
#

View File

@ -3,7 +3,7 @@ COMMON_OBJ = obj_all/core.o obj_all/socket.o obj_all/timer.o obj_all/db.o obj_al
obj_all/nullpo.o obj_all/malloc.o obj_all/showmsg.o obj_all/strlib.o obj_all/utils.o \
obj_all/grfio.o obj_all/mapindex.o obj_all/ers.o obj_all/md5calc.o \
obj_all/minicore.o obj_all/minisocket.o obj_all/minimalloc.o obj_all/random.o obj_all/des.o \
obj_all/conf.o
obj_all/conf.o obj_all/thread.o
COMMON_H = $(shell ls ../common/*.h)

157
src/common/atomic.h Normal file
View File

@ -0,0 +1,157 @@
#ifndef _rA_ATOMIC_H_
#define _rA_ATOMIC_H_
// Atomic Operations
// (Interlocked CompareExchange, Add .. and so on ..)
//
// Implementation varies / depends on:
// - Architecture
// - Compiler
// - Operating System
//
// our Abstraction is fully API-Compatible to Microsofts implementation @ NT5.0+
//
#include "../common/cbasetypes.h"
#if defined(_MSC_VER)
#include "../common/winapi.h"
#if !defined(_M_X64)
// When compiling for windows 32bit, the 8byte interlocked operations are not provided by microsoft
// (because they need at least i586 so its not generic enough.. ... )
forceinline int64 InterlockedCompareExchange64(volatile int64 *dest, int64 exch, int64 _cmp){
_asm{
lea esi,_cmp;
lea edi,exch;
mov eax,[esi];
mov edx,4[esi];
mov ebx,[edi];
mov ecx,4[edi];
mov esi,dest;
lock CMPXCHG8B [esi];
}
}
forceinline volatile int64 InterlockedIncrement64(volatile int64 *addend){
__int64 old;
do{
old = *addend;
}while(InterlockedCompareExchange64(addend, (old+1), old) != old);
return (old + 1);
}
forceinline volatile int64 InterlockedDecrement64(volatile int64 *addend){
__int64 old;
do{
old = *addend;
}while(InterlockedCompareExchange64(addend, (old-1), old) != old);
return (old - 1);
}
forceinline volatile int64 InterlockedExchangeAdd64(volatile int64 *addend, int64 increment){
__int64 old;
do{
old = *addend;
}while(InterlockedCompareExchange64(addend, (old + increment), old) != old);
return old;
}
forceinline volatile int64 InterlockedExchange64(volatile int64 *target, int64 val){
__int64 old;
do{
old = *target;
}while(InterlockedCompareExchange64(target, val, old) != old);
return old;
}
#endif //endif 32bit windows
#elif defined(__GNUC__)
#if !defined(__x86_64__) && !defined(__i386__)
#error Your Target Platfrom is not supported
#endif
static forceinline volatile int64 InterlockedExchangeAdd64(volatile int64 *addend, int64 increment){
return __sync_fetch_and_add(addend, increment);
}//end: InterlockedExchangeAdd64()
static forceinline volatile int32 InterlockedExchangeAdd(volatile int32 *addend, int32 increment){
return __sync_fetch_and_add(addend, increment);
}//end: InterlockedExchangeAdd()
static forceinline volatile int64 InterlockedIncrement64(volatile int64 *addend){
return __sync_add_and_fetch(addend, 1);
}//end: InterlockedIncrement64()
static forceinline volatile int32 InterlockedIncrement(volatile int32 *addend){
return __sync_add_and_fetch(addend, 1);
}//end: InterlockedIncrement()
static forceinline volatile int64 InterlockedDecrement64(volatile int64 *addend){
return __sync_sub_and_fetch(addend, 1);
}//end: InterlockedDecrement64()
static forceinline volatile int32 InterlockedDecrement(volatile int32 *addend){
return __sync_sub_and_fetch(addend, 1);
}//end: InterlockedDecrement()
static forceinline volatile int64 InterlockedCompareExchange64(volatile int64 *dest, int64 exch, int64 cmp){
return __sync_val_compare_and_swap(dest, cmp, exch);
}//end: InterlockedCompareExchange64()
static forceinline volatile int32 InterlockedCompareExchange(volatile int32 *dest, int32 exch, int32 cmp){
return __sync_val_compare_and_swap(dest, cmp, exch);
}//end: InterlockedCompareExchnage()
static forceinline volatile int64 InterlockedExchange64(volatile int64 *target, int64 val){
int ret;
__asm__ __volatile__(
"lock xchg %2, (%1)"
:"=r" (ret)
:"r" (target), "0" (val)
:"memory"
);
return ret;
}//end: InterlockedExchange64()
static forceinline volatile int32 InterlockedExchange(volatile int32 *target, int32 val){
int ret;
__asm__ __volatile__(
"lock xchgl %2, (%1)"
:"=r" (ret)
:"r" (target), "0" (val)
:"memory"
);
return ret;
}//end: InterlockedExchange()
#endif //endif compiler decission
#endif

View File

@ -9,6 +9,7 @@
#include "../common/db.h"
#include "../common/socket.h"
#include "../common/timer.h"
#include "../common/thread.h"
#endif
#include <stdio.h>
@ -278,6 +279,8 @@ int main (int argc, char **argv)
display_title();
usercheck();
rathread_init();
db_init();
signals_init();
@ -303,6 +306,8 @@ int main (int argc, char **argv)
timer_final();
socket_final();
db_final();
rathread_final();
#endif
malloc_final();

104
src/common/spinlock.h Normal file
View File

@ -0,0 +1,104 @@
#pragma once
#ifndef _rA_SPINLOCK_H_
#define _rA_SPINLOCK_H_
//
// CAS based Spinlock Implementation
//
// CamelCase names are choosen to be consistent with microsofts winapi
// which implements CriticalSection by this naming...
//
// Author: Florian Wilkemeyer <fw@f-ws.de>
//
// Copyright (c) rAthena Project (www.rathena.org) - Licensed under GNU GPL
// For more information, see LICENCE in the main folder
//
//
#ifdef WIN32
#include "../common/winapi.h"
#endif
#include "../common/cbasetypes.h"
#include "../common/atomic.h"
#include "../common/thread.h"
#ifdef WIN32
typedef struct __declspec( align(64) ) SPIN_LOCK{
volatile LONG lock;
volatile LONG nest;
volatile LONG sync_lock;
} SPIN_LOCK, *PSPIN_LOCK;
#else
typedef struct SPIN_LOCK{
volatile int32 lock;
volatile int32 nest; // nesting level.
volatile int32 sync_lock;
} __attribute__((aligned(64))) SPIN_LOCK, *PSPIN_LOCK;
#endif
static forceinline void InitializeSpinLock(PSPIN_LOCK lck){
lck->lock = 0;
lck->nest = 0;
lck->sync_lock = 0;
}
static forceinline void FinalizeSpinLock(PSPIN_LOCK lck){
return;
}
#define getsynclock(l) { while(1){ if(InterlockedCompareExchange(l, 1, 0) == 0) break; rathread_yield(); } }
#define dropsynclock(l) { InterlockedExchange(l, 0); }
static forceinline void EnterSpinLock(PSPIN_LOCK lck){
int tid = rathread_get_tid();
// Get Sync Lock && Check if the requester thread already owns the lock.
// if it owns, increase nesting level
getsynclock(&lck->sync_lock);
if(InterlockedCompareExchange(&lck->lock, tid, tid) == tid){
InterlockedIncrement(&lck->nest);
dropsynclock(&lck->sync_lock);
return; // Got Lock
}
// drop sync lock
dropsynclock(&lck->sync_lock);
// Spin until we've got it !
while(1){
if(InterlockedCompareExchange(&lck->lock, tid, 0) == 0){
InterlockedIncrement(&lck->nest);
return; // Got Lock
}
rathread_yield(); // Force ctxswitch to another thread.
}
}
static forceinline void LeaveSpinLock(PSPIN_LOCK lck){
int tid = rathread_get_tid();
getsynclock(&lck->sync_lock);
if(InterlockedCompareExchange(&lck->lock, tid, tid) == tid){ // this thread owns the lock.
if(InterlockedDecrement(&lck->nest) == 0)
InterlockedExchange(&lck->lock, 0); // Unlock!
}
dropsynclock(&lck->sync_lock);
}
#endif

270
src/common/thread.c Normal file
View File

@ -0,0 +1,270 @@
#ifdef WIN32
#include "../common/winapi.h"
#define getpagesize() 4096 // @TODO: implement this properly (GetSystemInfo .. dwPageSize..). (Atm as on all supported win platforms its 4k its static.)
#define __thread __declspec( thread )
#else
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <pthread.h>
#include <sched.h>
#endif
#include "cbasetypes.h"
#include "malloc.h"
#include "showmsg.h"
#include "thread.h"
#define RA_THREADS_MAX 64
struct rAthread {
unsigned int myID;
RATHREAD_PRIO prio;
rAthreadProc proc;
void *param;
#ifdef WIN32
HANDLE hThread;
#else
pthread_t hThread;
#endif
};
__thread int g_rathread_ID = -1;
///
/// Subystem Code
///
static struct rAthread l_threads[RA_THREADS_MAX];
void rathread_init(){
register unsigned int i;
memset(&l_threads, 0x00, RA_THREADS_MAX * sizeof(struct rAthread) );
for(i = 0; i < RA_THREADS_MAX; i++){
l_threads[i].myID = i;
}
// now lets init thread id 0, which represnts the main thread
g_rathread_ID = 0;
l_threads[0].prio = RAT_PRIO_NORMAL;
l_threads[0].proc = (rAthreadProc)0xDEADCAFE;
}//end: rathread_init()
void rathread_final(){
register unsigned int i;
// Unterminated Threads Left?
// Should'nt happen ..
// Kill 'em all!
//
for(i = 1; i < RA_THREADS_MAX; i++){
if(l_threads[i].proc != NULL){
ShowWarning("rAthread_final: unterminated Thread (tid %u entryPoint %p) - forcing to terminate (kill)\n", i, l_threads[i].proc);
rathread_destroy(&l_threads[i]);
}
}
}//end: rathread_final()
// gets called whenever a thread terminated ..
static void rat_thread_terminated( rAthread handle ){
int id_backup = handle->myID;
// Simply set all members to 0 (except the id)
memset(handle, 0x00, sizeof(struct rAthread));
handle->myID = id_backup; // done ;)
}//end: rat_thread_terminated()
#ifdef WIN32
DWORD WINAPI _raThreadMainRedirector(LPVOID p){
#else
static void *_raThreadMainRedirector( void *p ){
sigset_t set; // on Posix Thread platforms
#endif
void *ret;
// Update myID @ TLS to right id.
g_rathread_ID = ((rAthread)p)->myID;
#ifndef WIN32
// When using posix threads
// the threads inherits the Signal mask from the thread which's spawned
// this thread
// so we've to block everything we dont care about.
sigemptyset(&set);
sigaddset(&set, SIGINT);
sigaddset(&set, SIGTERM);
sigaddset(&set, SIGPIPE);
pthread_sigmask(SIG_BLOCK, &set, NULL);
#endif
ret = ((rAthread)p)->proc( ((rAthread)p)->param ) ;
#ifdef WIN32
CloseHandle( ((rAthread)p)->hThread );
#endif
rat_thread_terminated( (rAthread)p );
#ifdef WIN32
return (DWORD)ret;
#else
return ret;
#endif
}//end: _raThreadMainRedirector()
///
/// API Level
///
rAthread rathread_create( rAthreadProc entryPoint, void *param ){
return rathread_createEx( entryPoint, param, (1<<23) /*8MB*/, RAT_PRIO_NORMAL );
}//end: rathread_create()
rAthread rathread_createEx( rAthreadProc entryPoint, void *param, size_t szStack, RATHREAD_PRIO prio ){
#ifndef WIN32
pthread_attr_t attr;
#endif
size_t tmp;
unsigned int i;
rAthread handle = NULL;
// given stacksize aligned to systems pagesize?
tmp = szStack % getpagesize();
if(tmp != 0)
szStack += tmp;
// Get a free Thread Slot.
for(i = 0; i < RA_THREADS_MAX; i++){
if(l_threads[i].proc == NULL){
handle = &l_threads[i];
break;
}
}
if(handle == NULL){
ShowError("rAthread: cannot create new thread (entryPoint: %p) - no free thread slot found!", entryPoint);
return NULL;
}
handle->proc = entryPoint;
handle->param = param;
#ifdef WIN32
handle->hThread = CreateThread(NULL, szStack, _raThreadMainRedirector, (void*)handle, 0, NULL);
#else
pthread_attr_init(&attr);
pthread_attr_setstacksize(&attr, szStack);
if(pthread_create(&handle->hThread, &attr, _raThreadMainRedirector, (void*)handle) != 0){
handle->proc = NULL;
handle->param = NULL;
return NULL;
}
pthread_attr_destroy(&attr);
#endif
rathread_prio_set( handle, prio );
return handle;
}//end: rathread_createEx
void rathread_destroy ( rAthread handle ){
#ifdef WIN32
if( TerminateThread(handle->hThread, 0) != FALSE){
CloseHandle(handle->hThread);
rat_thread_terminated(handle);
}
#else
if( pthread_cancel( handle->hThread ) == 0){
// We have to join it, otherwise pthread wont re-cycle its internal ressources assoc. with this thread.
//
pthread_join( handle->hThread, NULL );
// Tell our manager to release ressources ;)
rat_thread_terminated(handle);
}
#endif
}//end: rathread_destroy()
rAthread rathread_self( ){
rAthread handle = &l_threads[g_rathread_ID];
if(handle->proc != NULL) // entry point set, so its used!
return handle;
return NULL;
}//end: rathread_self()
int rathread_get_tid(){
return g_rathread_ID;
}//end: rathread_get_tid()
bool rathread_wait( rAthread handle, void* *out_exitCode ){
// Hint:
// no thread data cleanup routine call here!
// its managed by the callProxy itself..
//
#ifdef WIN32
WaitForSingleObject(handle->hThread, INFINITE);
return true;
#else
if(pthread_join(handle->hThread, out_exitCode) == 0)
return true;
return false;
#endif
}//end: rathread_wait()
void rathread_prio_set( rAthread handle, RATHREAD_PRIO prio ){
handle->prio = RAT_PRIO_NORMAL;
//@TODO
}//end: rathread_prio_set()
RATHREAD_PRIO rathread_prio_get( rAthread handle){
return handle->prio;
}//end: rathread_prio_get()
void rathread_yield(){
#ifdef WIN32
SwitchToThread();
#else
sched_yield();
#endif
}//end: rathread_yield()

115
src/common/thread.h Normal file
View File

@ -0,0 +1,115 @@
#pragma once
#ifndef _rA_THREAD_H_
#define _rA_THREAD_H_
#include "../common/cbasetypes.h"
typedef struct rAthread *rAthread;
typedef void* (*rAthreadProc)(void*);
typedef enum RATHREAD_PRIO {
RAT_PRIO_LOW = 0,
RAT_PRIO_NORMAL,
RAT_PRIO_HIGH
} RATHREAD_PRIO;
/**
* Creates a new Thread
*
* @param entyPoint - entryProc,
* @param param - general purpose parameter, would be given as parameter to the thread's entrypoint.
*
* @return not NULL if success
*/
rAthread rathread_create( rAthreadProc entryPoint, void *param );
/**
* Creates a new Thread (with more creation options)
*
* @param entyPoint - entryProc,
* @param param - general purpose parameter, would be given as parameter to the thread's entrypoint
* @param szStack - stack Size in bytes
* @param prio - Priority of the Thread @ OS Scheduler..
*
* @return not NULL if success
*/
rAthread rathread_createEx( rAthreadProc entryPoint, void *param, size_t szStack, RATHREAD_PRIO prio );
/**
* Destroys the given Thread immediatly
*
* @note The Handle gets invalid after call! dont use it afterwards.
*
* @param handle - thread to destroy.
*/
void rathread_destroy ( rAthread handle );
/**
* Returns the thread handle of the thread calling this function
*
* @note this wont work @ programms main thread
* @note the underlying implementation might not perform very well, cache the value received!
*
* @return not NULL if success
*/
rAthread rathread_self( );
/**
* Returns own thrad id (TID)
*
* @note this is not the operating system THREAD ID!
*
* @return -1 when fails, otherwise >= 0
*/
int rathread_get_tid();
/**
* Waits for the given thread to terminate
*
* @param handle - thread to wait (join) for
* @param out_Exitcode - [OPTIONAL] - if given => Exitcode (value) of the given thread - if it's terminated
*
* @return true - if the given thread has been terminated.
*/
bool rathread_wait( rAthread handle, void* *out_exitCode );
/**
* Sets the given PRIORITY @ OS Task Scheduler
*
* @param handle - thread to set prio for
* @param rio - the priority (RAT_PRIO_LOW ... )
*/
void rathread_prio_set( rAthread handle, RATHREAD_PRIO prio );
/**
* Gets the current Prio of the given trhead
*
* @param handle - the thread to get the prio for.
*/
RATHREAD_PRIO rathread_prio_get( rAthread handle);
/**
* Tells the OS scheduler to yield the execution of the calling thread
*
* @note: this will not "pause" the thread,
* it just allows the OS to spent the remaining time
* of the slice to another thread.
*/
void rathread_yield();
void rathread_init();
void rathread_final();
#endif

61
src/test/Makefile.in Normal file
View File

@ -0,0 +1,61 @@
COMMON_H = $(shell ls ../common/*.h)
MT19937AR_OBJ = ../../3rdparty/mt19937ar/mt19937ar.o
MT19937AR_H = ../../3rdparty/mt19937ar/mt19937ar.h
MT19937AR_INCLUDE = -I../../3rdparty/mt19937ar
LIBCONFIG_OBJ = ../../3rdparty/libconfig/libconfig.o ../../3rdparty/libconfig/grammar.o \
../../3rdparty/libconfig/scanctx.o ../../3rdparty/libconfig/scanner.o ../../3rdparty/libconfig/strbuf.o
LIBCONFIG_H = ../../3rdparty/libconfig/libconfig.h ../../3rdparty/libconfig/grammar.h \
../../3rdparty/libconfig/parsectx.h ../../3rdparty/libconfig/scanctx.h ../../3rdparty/libconfig/scanner.h \
../../3rdparty/libconfig/strbuf.h ../../3rdparty/libconfig/wincompat.h
LIBCONFIG_INCLUDE = -I../../3rdparty/libconfig
TEST_SPINLOCK_OBJ=obj/test_spinlock.o
TEST_SPINLOCK_H=
TEST_SPINLOCK_DEPENDS=obj $(TEST_SPINLOCK_OBJ) ../common/obj_sql/common_sql.a ../common/obj_all/common.a $(MT19937AR_OBJ)
@SET_MAKE@
#####################################################################
.PHONY :all test_spinlock
all: test_spinlock
clean:
@echo " CLEAN test"
@rm -rf *.o obj ../../test_spinlock@EXEEXT@
#####################################################################
# object directories
obj:
@echo " MKDIR obj"
@-mkdir obj
#executables
test_spinlock: $(TEST_SPINLOCK_DEPENDS)
@echo " LD $@"
@@CC@ @LDFLAGS@ -o ../../test_spinlock@EXEEXT@ $(TEST_SPINLOCK_OBJ) ../common/obj_sql/common_sql.a ../common/obj_all/common.a $(MT19937AR_OBJ) $(LIBCONFIG_OBJ) @LIBS@ @MYSQL_LIBS@
# login object files
obj/%.o: %.c $(COMMON_H) $(MT19937AR_H) $(LIBCONFIG_H)
@echo " CC $<"
@@CC@ @CFLAGS@ $(MT19937AR_INCLUDE) $(LIBCONFIG_INCLUDE) -DWITH_SQL @MYSQL_CFLAGS@ @CPPFLAGS@ -c $(OUTPUT_OPTION) $<
# missing object files
../common/obj_all/common.a:
@$(MAKE) -C ../common sql
../common/obj_sql/common_sql.a:
@$(MAKE) -C ../common sql
MT19937AR_OBJ:
@$(MAKE) -C ../../3rdparty/mt19937ar
LIBCONFIG_OBJ:
@$(MAKE) -C ../../3rdparty/libconfig

117
src/test/test_spinlock.c Normal file
View File

@ -0,0 +1,117 @@
#include "../common/core.h"
#include "../common/atomic.h"
#include "../common/thread.h"
#include "../common/spinlock.h"
#include "../common/showmsg.h"
#include <stdio.h>
#include <stdlib.h>
//
// Simple test for the spinlock implementation to see if it works properly..
//
#define THRC 32 //thread Count
#define PERINC 100000
#define LOOPS 47
static SPIN_LOCK lock;
static int val = 0;
static volatile int32 done_threads = 0;
static void *worker(void *p){
register int i;
for(i = 0; i < PERINC; i++){
EnterSpinLock(&lock);
EnterSpinLock(&lock);
val++;
LeaveSpinLock(&lock);
LeaveSpinLock(&lock);
}
InterlockedIncrement(&done_threads);
return NULL;
}//end: worker()
int do_init(int argc, char **argv){
rAthread t[THRC];
int j, i;
int ok;
ShowStatus("==========\n");
ShowStatus("TEST: %u Runs, (%u Threads)\n", LOOPS, THRC);
ShowStatus("This can take a while\n");
ShowStatus("\n\n");
ok =0;
for(j = 0; j < LOOPS; j++){
val = 0;
done_threads = 0;
InitializeSpinLock(&lock);
for(i =0; i < THRC; i++){
t[i] = rathread_createEx( worker, NULL, 1024*512, RAT_PRIO_NORMAL);
}
while(1){
if(InterlockedCompareExchange(&done_threads, THRC, THRC) == THRC)
break;
rathread_yield();
}
FinalizeSpinLock(&lock);
// Everything fine?
if(val != (THRC*PERINC) ){
printf("FAILED! (Result: %u, Expected: %u)\n", val, (THRC*PERINC) );
}else{
printf("OK! (Result: %u, Expected: %u)\n", val, (THRC*PERINC) );
ok++;
}
}
if(ok != LOOPS){
ShowFatalError("Test failed.\n");
exit(1);
}else{
ShowStatus("Test passed.\n");
exit(0);
}
return 0;
}//end: do_init()
void do_abort(){
}//end: do_abort()
void set_server_type(){
SERVER_TYPE = ATHENA_SERVER_NONE;
}//end: set_server_type()
void do_final(){
}//end: do_final()
int parse_console(const char* command){
return 0;
}//end: parse_console