Add C++ API for fiber mutex deadlock checking.

This commit is contained in:
zhengshuxin 2023-01-11 22:29:45 +08:00
parent 758dd3852a
commit 3ef3fa8128
20 changed files with 503 additions and 15 deletions

View File

@ -32,7 +32,7 @@ typedef struct ACL_FIBER_MUTEX_STATS {
FIBER_API void acl_fiber_mutex_profile(void);
FIBER_API ACL_FIBER_MUTEX_STATS *acl_fiber_mutex_deadcheck(void);
FIBER_API ACL_FIBER_MUTEX_STATS *acl_fiber_mutex_deadlock(void);
FIBER_API void acl_fiber_mutex_stats_free(ACL_FIBER_MUTEX_STATS *stats);
FIBER_API void acl_fiber_mutex_stats_show(const ACL_FIBER_MUTEX_STATS *stats);

View File

@ -259,7 +259,6 @@ static ACL_FIBER_MUTEX_STATS *check_deadlock2(MUTEX_CHECK *check)
SNPRINTF(hkey, sizeof(hkey), "%d", acl_fiber_id(fiber));
if (htable_find(table, hkey) != NULL) {
deadlocked = 1;
printf("DeadLock happened!\r\n");
break;
}
}
@ -314,7 +313,7 @@ static ACL_FIBER_MUTEX_STATS *check_deadlock(THREAD_MUTEXES *mutexes)
return stats;
}
ACL_FIBER_MUTEX_STATS *acl_fiber_mutex_deadcheck(void)
ACL_FIBER_MUTEX_STATS *acl_fiber_mutex_deadlock(void)
{
if (__locks != NULL) {
ACL_FIBER_MUTEX_STATS *stats;

View File

@ -17,9 +17,8 @@ typedef enum
FIBER_EVENT_T_IO_URING, // Linux
} fiber_event_t;
class FIBER_CPP_API fiber_frame
struct FIBER_CPP_API fiber_frame
{
public:
fiber_frame(void) : pc(0), off(0) {}
~fiber_frame(void) {}
@ -34,6 +33,9 @@ public:
class FIBER_CPP_API fiber
{
public:
fiber(void);
fiber(ACL_FIBER *fb);
/**
*
* @param running {bool} true
@ -41,7 +43,8 @@ public:
* start ; false start
*
*/
fiber(bool running = false);
fiber(bool running);
virtual ~fiber(void);
/**
@ -87,6 +90,12 @@ public:
*/
static unsigned int self(void);
/**
* ID号
* @return {unsigned int}
*/
static unsigned int id(const fiber& fb);
/**
* API
* return {int}

View File

@ -1,5 +1,7 @@
#pragma once
#include <vector>
#include "fiber_cpp_define.hpp"
#include "fiber_mutex_stat.hpp"
struct ACL_FIBER_MUTEX;
@ -12,7 +14,13 @@ namespace acl {
class FIBER_CPP_API fiber_mutex
{
public:
fiber_mutex(void);
/**
*
* @param mutex {ACL_FIBER_MUTEX*} , C C++
* , C ;,
* C .
*/
fiber_mutex(ACL_FIBER_MUTEX* mutex = NULL);
~fiber_mutex(void);
/**
@ -43,8 +51,16 @@ public:
return mutex_;
}
/**
*
* @param out {fiber_mutex_stats&}
* @return {bool} true , out
*/
static bool deadlock(fiber_mutex_stats& out);
private:
ACL_FIBER_MUTEX* mutex_;
ACL_FIBER_MUTEX* mutex_internal_;
fiber_mutex(const fiber_mutex&);
void operator=(const fiber_mutex&);

View File

@ -0,0 +1,27 @@
#pragma once
struct ACL_FIBER_MUTEX;
namespace acl {
class fiber;
struct FIBER_CPP_API fiber_mutex_stat
{
fiber_mutex_stat(void) : fb(NULL), waiting(NULL) {}
~fiber_mutex_stat(void) {}
fiber *fb;
ACL_FIBER_MUTEX *waiting;
std::vector<ACL_FIBER_MUTEX*> holding;
};
struct FIBER_CPP_API fiber_mutex_stats
{
fiber_mutex_stats(void) {}
~fiber_mutex_stats(void);
std::vector<fiber_mutex_stat> stats;
};
} // namespace acl

View File

@ -4,6 +4,8 @@
#include "fiber_lock.hpp"
#include "fiber_event.hpp"
#include "fiber_mutex.hpp"
#include "fiber_mutex_stat.hpp"
#include "fiber_cond.hpp"
#include "wait_group.hpp"
#include "fiber_sem.hpp"

View File

@ -315,6 +315,8 @@
<ClInclude Include="include\fiber\fiber_cpp_define.hpp" />
<ClInclude Include="include\fiber\fiber_event.hpp" />
<ClInclude Include="include\fiber\fiber_lock.hpp" />
<ClInclude Include="include\fiber\fiber_mutex.hpp" />
<ClInclude Include="include\fiber\fiber_mutex_stat.hpp" />
<ClInclude Include="include\fiber\fiber_redis_pipeline.hpp" />
<ClInclude Include="include\fiber\fiber_sem.hpp" />
<ClInclude Include="include\fiber\fiber_tbox.hpp" />
@ -331,6 +333,8 @@
<ClCompile Include="src\fiber_cond.cpp" />
<ClCompile Include="src\fiber_event.cpp" />
<ClCompile Include="src\fiber_lock.cpp" />
<ClCompile Include="src\fiber_mutex.cpp" />
<ClCompile Include="src\fiber_mutex_stat.cpp" />
<ClCompile Include="src\fiber_redis_pipeline.cpp" />
<ClCompile Include="src\fiber_sem.cpp" />
<ClCompile Include="src\fiber_server.cpp" />

View File

@ -45,6 +45,12 @@
<ClInclude Include="include\fiber\fiber_lock.hpp">
<Filter>头文件</Filter>
</ClInclude>
<ClInclude Include="include\fiber\fiber_mutex.hpp">
<Filter>头文件</Filter>
</ClInclude>
<ClInclude Include="include\fiber\fiber_mutex_stat.hpp">
<Filter>头文件</Filter>
</ClInclude>
<ClInclude Include="include\fiber\fiber_redis_pipeline.hpp">
<Filter>头文件</Filter>
</ClInclude>
@ -83,6 +89,12 @@
<ClCompile Include="src\fiber_lock.cpp">
<Filter>源文件</Filter>
</ClCompile>
<ClCompile Include="src\fiber_mutex.cpp">
<Filter>源文件</Filter>
</ClCompile>
<ClCompile Include="src\fiber_mutex_stat.cpp">
<Filter>源文件</Filter>
</ClCompile>
<ClCompile Include="src\fiber_redis_pipeline.cpp">
<Filter>源文件</Filter>
</ClCompile>

View File

@ -309,6 +309,8 @@
<ClInclude Include="include\fiber\fiber_cpp_define.hpp" />
<ClInclude Include="include\fiber\fiber_event.hpp" />
<ClInclude Include="include\fiber\fiber_lock.hpp" />
<ClInclude Include="include\fiber\fiber_mutex.hpp" />
<ClInclude Include="include\fiber\fiber_mutex_stat.hpp" />
<ClInclude Include="include\fiber\fiber_redis_pipeline.hpp" />
<ClInclude Include="include\fiber\fiber_sem.hpp" />
<ClInclude Include="include\fiber\fiber_tbox.hpp" />
@ -325,6 +327,8 @@
<ClCompile Include="src\fiber_cond.cpp" />
<ClCompile Include="src\fiber_event.cpp" />
<ClCompile Include="src\fiber_lock.cpp" />
<ClCompile Include="src\fiber_mutex.cpp" />
<ClCompile Include="src\fiber_mutex_stat.cpp" />
<ClCompile Include="src\fiber_redis_pipeline.cpp" />
<ClCompile Include="src\fiber_sem.cpp" />
<ClCompile Include="src\fiber_server.cpp" />

View File

@ -36,6 +36,12 @@
<ClCompile Include="src\fiber_lock.cpp">
<Filter>源文件</Filter>
</ClCompile>
<ClCompile Include="src\fiber_mutex.cpp">
<Filter>源文件</Filter>
</ClCompile>
<ClCompile Include="src\fiber_mutex_stat.cpp">
<Filter>源文件</Filter>
</ClCompile>
<ClCompile Include="src\fiber_redis_pipeline.cpp">
<Filter>源文件</Filter>
</ClCompile>
@ -74,6 +80,12 @@
<ClInclude Include="include\fiber\fiber_lock.hpp">
<Filter>头文件</Filter>
</ClInclude>
<ClInclude Include="include\fiber\fiber_mutex.hpp">
<Filter>头文件</Filter>
</ClInclude>
<ClInclude Include="include\fiber\fiber_mutex_stat.hpp">
<Filter>头文件</Filter>
</ClInclude>
<ClInclude Include="include\fiber\fiber_redis_pipeline.hpp">
<Filter>头文件</Filter>
</ClInclude>

View File

@ -316,6 +316,7 @@
<ClInclude Include="include\fiber\fiber_event.hpp" />
<ClInclude Include="include\fiber\fiber_lock.hpp" />
<ClInclude Include="include\fiber\fiber_mutex.hpp" />
<ClInclude Include="include\fiber\fiber_mutex_stat.hpp" />
<ClInclude Include="include\fiber\fiber_redis_pipeline.hpp" />
<ClInclude Include="include\fiber\fiber_sem.hpp" />
<ClInclude Include="include\fiber\fiber_tbox.hpp" />
@ -345,6 +346,7 @@
<ClCompile Include="src\fiber_event.cpp" />
<ClCompile Include="src\fiber_lock.cpp" />
<ClCompile Include="src\fiber_mutex.cpp" />
<ClCompile Include="src\fiber_mutex_stat.cpp" />
<ClCompile Include="src\fiber_redis_pipeline.cpp" />
<ClCompile Include="src\fiber_sem.cpp" />
<ClCompile Include="src\fiber_server.cpp" />

View File

@ -75,6 +75,9 @@
<ClCompile Include="src\fiber_mutex.cpp">
<Filter>源文件</Filter>
</ClCompile>
<ClCompile Include="src\fiber_mutex_stat.cpp">
<Filter>源文件</Filter>
</ClCompile>
<ClCompile Include="src\tcp_keeper.cpp">
<Filter>源文件</Filter>
</ClCompile>
@ -140,6 +143,9 @@
<ClInclude Include="include\fiber\fiber_mutex.hpp">
<Filter>头文件</Filter>
</ClInclude>
<ClInclude Include="include\fiber\fiber_mutex_stat.hpp">
<Filter>头文件</Filter>
</ClInclude>
<ClInclude Include="include\fiber\http_server.hpp">
<Filter>头文件</Filter>
</ClInclude>

View File

@ -4,7 +4,17 @@
namespace acl {
fiber::fiber(bool running /* = false */)
fiber::fiber(void)
{
f_ = NULL;
}
fiber::fiber(ACL_FIBER *f)
{
f_ = f;
}
fiber::fiber(bool running)
{
if (running) {
f_ = acl_fiber_running();
@ -31,6 +41,12 @@ unsigned int fiber::self(void)
return acl_fiber_self();
}
unsigned int fiber::id(const fiber& fb)
{
ACL_FIBER *f = fb.get_fiber();
return f ? acl_fiber_id(f) : 0;
}
int fiber::get_errno(void) const
{
return f_ ? acl_fiber_errno(f_) : -1;

View File

@ -1,16 +1,24 @@
#include "stdafx.hpp"
#include "fiber/fiber.hpp"
#include "fiber/fiber_mutex.hpp"
namespace acl {
fiber_mutex::fiber_mutex(void)
fiber_mutex::fiber_mutex(ACL_FIBER_MUTEX *mutex /* NULL */)
{
mutex_ = acl_fiber_mutex_create(0);
if (mutex) {
mutex_ = mutex;
mutex_internal_ = NULL;
} else {
mutex_internal_ = mutex_ = acl_fiber_mutex_create(0);
}
}
fiber_mutex::~fiber_mutex(void)
{
acl_fiber_mutex_free(mutex_);
if (mutex_internal_) {
acl_fiber_mutex_free(mutex_internal_);
}
}
bool fiber_mutex::lock(void)
@ -30,4 +38,29 @@ bool fiber_mutex::unlock(void)
return true;
}
bool fiber_mutex::deadlock(fiber_mutex_stats& out)
{
ACL_FIBER_MUTEX_STATS *stats = acl_fiber_mutex_deadlock();
if (stats == NULL) {
return false;
}
for (size_t i = 0; i < stats->count; i++) {
fiber_mutex_stat stat;
stat.fb = new fiber(stats->stats[i].fiber);
stat.waiting = stats->stats[i].waiting;
for (size_t j = 0; j < stats->stats[i].count; j++) {
ACL_FIBER_MUTEX* mutex = stats->stats[i].holding[j];
stat.holding.push_back(mutex);
}
out.stats.push_back(stat);
}
acl_fiber_mutex_stats_free(stats);
return true;
}
} // namespace acl

View File

@ -0,0 +1,16 @@
#include "stdafx.hpp"
#include "fiber/fiber.hpp"
#include "fiber/fiber_mutex.hpp"
#include "fiber/fiber_mutex_stat.hpp"
namespace acl {
fiber_mutex_stats::~fiber_mutex_stats(void)
{
for (std::vector<fiber_mutex_stat>::iterator it = stats.begin();
it != stats.end(); ++it) {
delete (*it).fb;
}
}
} // namespace acl

View File

@ -65,7 +65,7 @@ static void fiber_check(ACL_FIBER *fb, void *ctx)
sleep(1);
printf("\r\n");
//acl_fiber_mutex_profile();
stats = acl_fiber_mutex_deadcheck();
stats = acl_fiber_mutex_deadlock();
if (stats) {
acl_fiber_mutex_stats_show(stats);
acl_fiber_mutex_stats_free(stats);

View File

@ -0,0 +1,6 @@
# Don't build the sample on MacOS because libunwind don't support it.
include ./Makefile.in
ifeq ($(findstring Linux, $(UNIXNAME)), Linux)
SYSLIB += -lunwind -lunwind-x86_64
endif
PROG = dead_lock

View File

@ -0,0 +1,174 @@
CC = g++
CFLAGS = -c -g -W -Wall -Wcast-qual -Wcast-align \
-Wno-long-long -Wpointer-arith -Werror -Wshadow -O0 \
-D_REENTRANT -D_POSIX_PTHREAD_SEMANTICS -D_USE_FAST_MACRO
###########################################################
#Check system:
# Linux, SunOS, Solaris, BSD variants, AIX, HP-UX
SYSLIB = -ldl -lpthread -lz
CHECKSYSRES = @echo "Unknow system type!";exit 1
UNIXNAME = $(shell uname -sm)
OSTYPE = $(shell uname -p)
RPATH = linux64
ifeq ($(CC),)
CC = g++
endif
OS_ENV=$(shell uname -a)
ifeq ($(findstring WSL, $(OS_ENV)), WSL)
SYSLIB += -liconv
endif
ifeq ($(findstring ubuntu, $(OS_ENV)), ubuntu)
SYSLIB += -liconv
endif
ifeq ($(findstring Alpine, $(shell uname -a)), Alpine)
SYSLIB += -lucontext
endif
ifeq ($(findstring g++, $(CC)), g++)
GCC_VERSION:=$(shell echo `gcc --version|grep ^gcc|cut -d' ' -f3`)
GCC_MAJOR:=$(shell echo "$(GCC_VERSION)" | cut -d'.' -f1)
GCC_MINOR:=$(shell echo "$(GCC_VERSION)" | cut -d'.' -f2)
GCC_VER:=$(shell [ $(GCC_MAJOR) -gt 4 -o \( $(GCC_MAJOR) -eq 4 -a $(GCC_MINOR) -ge 7 \) ] && echo true)
ifeq ($(GCC_VER), true)
CFLAGS += -std=c++11 -DACL_USE_CPP11
endif
endif
# For FreeBSD
ifeq ($(findstring FreeBSD, $(UNIXNAME)), FreeBSD)
ifeq ($(findstring gcc, $(CC)), gcc)
CFLAGS += -Wstrict-prototypes
endif
CFLAGS += -DFREEBSD -D_REENTRANT
SYSLIB = -lcrypt -lpthread -lz
RPATH = freebsd
endif
# For Darwin
ifeq ($(findstring Darwin, $(UNIXNAME)), Darwin)
# CC += -arch x86_64 -arch arm64
CFLAGS += -DMACOSX -Wno-invalid-source-encoding \
-Wno-invalid-offsetof
UNIXTYPE = MACOSX
SYSLIB += -liconv -rdynamic
RPATH = macos
endif
#Path for Linux
ifeq ($(findstring Linux, $(UNIXNAME)), Linux)
ifeq ($CC, "gcc")
CFLAGS += -Wstrict-prototypes
endif
ifeq ($(findstring i686, $(OSTYPE)), i686)
RPATH = linux32
endif
ifeq ($(findstring x86_64, $(OSTYPE)), x86_64)
RPATH = linux64
endif
ifeq ($(HAS_IO_URING), yes)
SYSLIB += -luring
endif
ifeq ($(has_io_uring), yes)
SYSLIB += -luring
endif
# CFLAGS += -DLINUX2 -D_REENTRANT
CFLAGS += -D_REENTRANT
SYSLIB += -lcrypt
endif
#Path for SunOS
ifeq ($(findstring SunOS, $(UNIXNAME)), SunOS)
ifeq ($(findstring 86, $(UNIXNAME)), 86)
SYSLIB += -lsocket -lnsl -lrt
endif
ifeq ($(findstring sun4u, $(UNIXNAME)), sun4u)
SYSLIB += -lsocket -lnsl -lrt
endif
ifeq ($CC, "gcc")
CFLAGS += -Wstrict-prototypes
endif
CFLAGS += -DSUNOS5 -D_REENTRANT
RPATH = sunos_x86
endif
#Path for HP-UX
ifeq ($(findstring HP-UX, $(UNIXNAME)), HP-UX)
ifeq ($CC, "gcc")
CFLAGS += -Wstrict-prototypes
endif
CFLAGS += -DHP_UX -DHPUX11
PLAT_NAME=hp-ux
endif
# For CYGWIN
ifeq ($(findstring CYGWIN, $(UNIXNAME)), CYGWIN)
SYSLIB = -lpthread -rdynamic
CFLAGS += -DLINUX2 -DMINGW
UNIXTYPE = LINUX
endif
# For MINGW
ifeq ($(findstring MINGW, $(UNIXNAME)), MINGW)
SYSLIB = -lpthread -rdynamic
CFLAGS += -DLINUX2 -DMINGW
UNIXTYPE = LINUX
endif
# For MSYS
ifeq ($(findstring MSYS, $(UNIXNAME)), MSYS)
SYSLIB = -lpthread -rdynamic
CFLAGS += -DLINUX2 -DMINGW
UNIXTYPE = LINUX
endif
#Find system type.
ifneq ($(SYSPATH),)
CHECKSYSRES = @echo "System is $(shell uname -sm)"
endif
###########################################################
CFLAGS += -I.. -I../../../lib_acl/include -I../../../lib_protocol/include -I../../../lib_acl_cpp/include \
-I../../../lib_fiber/c/include -I../../../lib_fiber/cpp/include
EXTLIBS =
LDFLAGS = -L../../../lib_fiber/lib -lfiber_cpp \
-L../../../lib_acl_cpp/lib -lacl_cpp \
-L../../../lib_protocol/lib -lprotocol \
-L../../../lib_acl/lib -lacl \
-lfiber $(EXTLIBS) $(SYSLIB)
COMPILE = $(CC) $(CFLAGS)
LINK = $(CC) $(OBJ) $(LDFLAGS)
###########################################################
OBJ_PATH = .
#Project's objs
SRC = $(wildcard *.cpp)
OBJ = $(patsubst %.cpp, $(OBJ_PATH)/%.o, $(notdir $(SRC)))
$(OBJ_PATH)/%.o: %.cpp
$(COMPILE) $< -o $@
.PHONY = all clean
all: RM $(OBJ)
$(LINK) -o $(PROG)
@echo ""
@echo "All ok! Output:$(PROG)"
@echo ""
RM:
rm -f $(PROG)
clean cl:
rm -f $(PROG)
rm -f $(OBJ)
rebuild rb: clean all
install:
cp $(PROG) ../../../dist/master/libexec/$(RPATH)/
cp $(PROG).cf ../../../dist/master/conf/service/
###########################################################

View File

@ -0,0 +1,152 @@
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include "fiber/libfiber.h"
#include "fiber/libfiber.hpp"
class myfiber : public acl::fiber {
public:
myfiber(std::vector<acl::fiber_mutex*>& locks) : locks_(locks) {}
~myfiber(void) {}
private:
std::vector<acl::fiber_mutex*>& locks_;
// @override
void run(void) {
size_t i = acl::fiber::self() % locks_.size();
acl::fiber_mutex* lock = locks_[i];
printf("fiber-%d wait, locks[%zd]=%p\r\n",
acl::fiber::self(), i, lock);
lock->lock();
printf("fiber-%d locked, locks[%ld]=%p\r\n",
acl::fiber::self(), i, lock);
sleep(1);
i = (acl::fiber::self() + 1) % locks_.size();
lock = locks_[i];
printf("fiber-%d wait,locks[%zd]=%p\r\n",
acl::fiber::self(), i, lock);
lock->lock();
printf("fiber-%d locked, locks[%zd]=%p\r\n",
acl::fiber::self(), i, lock);
}
};
class fiber_check : public acl::fiber {
public:
fiber_check(void) {}
~fiber_check(void) {}
private:
// @override
void run(void) {
while (true) {
sleep(2);
printf("\r\n");
acl::fiber_mutex_stats stats;
if (acl::fiber_mutex::deadlock(stats)) {
printf("Deadlock happened!\r\n\r\n");
show(stats);
} else {
printf("No deadlock happened!\r\n");
}
printf("=======================================\r\n");
}
}
void show(const acl::fiber_mutex_stats& stats) {
for (std::vector<acl::fiber_mutex_stat>::const_iterator
cit = stats.stats.begin(); cit != stats.stats.end();
++cit) {
show(*cit);
printf("\r\n");
}
}
void show(const acl::fiber_mutex_stat& stat) {
printf("fiber-%d:\r\n", acl::fiber::id(*stat.fb));
std::vector<acl::fiber_frame> stack;
acl::fiber::stacktrace(*stat.fb, stack, 50);
show_stack(stack);
for (std::vector<ACL_FIBER_MUTEX*>::const_iterator
cit = stat.holding.begin();
cit != stat.holding.end(); ++cit) {
printf("Holding mutex=%p\r\n", *cit);
}
printf("Waiting for mutex=%p\r\n", stat.waiting);
}
void show_stack(const std::vector<acl::fiber_frame>& stack)
{
for (std::vector<acl::fiber_frame>::const_iterator
cit = stack.begin(); cit != stack.end(); ++cit) {
printf("0x%lx(%s)+0x%lx\r\n",
(*cit).pc, (*cit).func.c_str(), (*cit).off);
}
}
};
static void usage(const char *procname)
{
printf("usage: %s -h [help] -c fibers_count -n locks_count\r\n", procname);
}
int main(int argc, char *argv[])
{
int i, ch, nlocks = 2, nfibers = 2;
std::vector<acl::fiber_mutex*> locks;
while ((ch = getopt(argc, argv, "hc:n:")) > 0) {
switch (ch) {
case 'h':
usage(argv[0]);
return 0;
case 'c':
nfibers = atoi(optarg);
break;
case 'n':
nlocks = atoi(optarg);
if (nlocks <= 0) {
nlocks = 1;
}
break;
default:
break;
}
}
for (i = 0; i < nlocks; i++) {
acl::fiber_mutex* lock = new acl::fiber_mutex;
locks.push_back(lock);
}
std::vector<acl::fiber*> fibers;
for (i = 0; i < nfibers; i++) {
acl::fiber* fb = new myfiber(locks);
fibers.push_back(fb);
fb->start();
}
fiber_check check;
check.start();
acl::fiber::schedule();
for (std::vector<acl::fiber_mutex*>::iterator it = locks.begin();
it != locks.end(); ++it) {
delete *it;
}
for (std::vector<acl::fiber*>::iterator it = fibers.begin();
it != fibers.end(); ++it) {
delete *it;
}
return 0;
}

View File

@ -50,14 +50,12 @@ private:
void run(void)
{
std::vector<acl::fiber_frame> stack;
for (int i = 0; i < count_; i++) {
std::vector<acl::fiber_frame> stack;
acl::fiber::stacktrace(*fb_, stack, 50);
show_stack(stack);
printf("\r\n");
stack.clear();
sleep(2);
}