1
0
Fork 0
mirror of https://github.com/ossrs/srs.git synced 2025-03-09 15:49:59 +00:00

Support MIPS 64bits for loongson 3A4000/3B3000. v5.0.34

This commit is contained in:
winlin 2022-08-06 13:03:45 +08:00
parent aba6667357
commit 1589858cb0
16 changed files with 543 additions and 185 deletions

View file

@ -4,7 +4,7 @@ ST_DIR = ..
# The main dir of st utest.
ST_UTEST = .
# The main dir of gtest.
GTEST_DIR = $(ST_UTEST)/gtest
GTEST_DIR = $(ST_UTEST)/gtest-fit/googletest
# Flags passed to the C++ compiler.
CXXFLAGS += -g -O0 -std=c++11
@ -45,16 +45,29 @@ $(ST_DIR)/obj/gtest.a : $(ST_DIR)/obj/gtest-all.o
ST_UTEST_DEPS = $(ST_DIR)/obj/libst.a
# Depends, utest header files
UTEST_DEPS = $(ST_UTEST)/st_utest.hpp
UTEST_DEPS = $(ST_UTEST)/st_utest.hpp Makefile
# Compile all sources files at current directory. For example:
# (st_utest.cpp st_utest_coroutines.cpp st_utest_tcp.cpp)
SOURCE_FILES = $(shell ls *.cpp)
#
# Convert all source files to object files. For example:
# (st_utest.o st_utest_coroutines.o st_utest_tcp.o)
# https://ftp.gnu.org/old-gnu/Manuals/make-3.79.1/html_chapter/make_8.html
OBJECTS_FILES = $(patsubst %.cpp,%.o,$(SOURCE_FILES))
#
# Prefix object files to objects. For example:
# ($(ST_DIR)/obj/st_utest.o $(ST_DIR)/obj/st_utest_coroutines.o $(ST_DIR)/obj/st_utest_tcp.o)
OBJECTS = $(addprefix $(ST_DIR)/obj/,$(OBJECTS_FILES))
# Objects, build each object of utest
$(ST_DIR)/obj/st_utest.o : st_utest.cpp $(ST_UTEST_DEPS) $(UTEST_DEPS)
$(CXX) -c st_utest.cpp -o $@ \
$(ST_DIR)/obj/%.o : %.cpp $(ST_UTEST_DEPS) $(UTEST_DEPS)
$(CXX) -c $< -o $@ \
$(CXXFLAGS) $(UTEST_FLAGS) \
$(WARNFLAGS) \
-I$(GTEST_DIR)/include -I$(ST_UTEST) -I$(ST_DIR) -I$(ST_DIR)/obj
# generate the utest binary
$(ST_DIR)/obj/st_utest : $(ST_DIR)/obj/st_utest.o $(ST_DIR)/obj/gtest.a $(ST_UTEST_DEPS)
# Generate the utest binary
$(ST_DIR)/obj/st_utest : $(OBJECTS) $(ST_DIR)/obj/gtest.a $(ST_UTEST_DEPS)
$(CXX) -o $@ $(CXXFLAGS) $(UTEST_FLAGS) \
-lpthread -ldl $^

View file

@ -6,6 +6,14 @@
#include <st.h>
#include <assert.h>
std::ostream& operator<<(std::ostream& out, const ErrorObject* err) {
if (!err) return out;
if (err->r0_) out << "r0=" << err->r0_;
if (err->errno_) out << ", errno=" << err->errno_;
if (!err->message_.empty()) out << ", msg=" << err->message_;
return out;
}
// We could do something in the main of utest.
// Copy from gtest-1.6.0/src/gtest_main.cc
GTEST_API_ int main(int argc, char **argv) {
@ -25,7 +33,7 @@ GTEST_API_ int main(int argc, char **argv) {
}
// basic test and samples.
VOID TEST(SampleTest, FastSampleInt64Test)
VOID TEST(SampleTest, ExampleIntSizeTest)
{
EXPECT_EQ(1, (int)sizeof(int8_t));
EXPECT_EQ(2, (int)sizeof(int16_t));
@ -33,107 +41,3 @@ VOID TEST(SampleTest, FastSampleInt64Test)
EXPECT_EQ(8, (int)sizeof(int64_t));
}
void* pfn_coroutine(void* /*arg*/)
{
st_usleep(0);
return NULL;
}
VOID TEST(SampleTest, StartCoroutine)
{
st_thread_t trd = st_thread_create(pfn_coroutine, NULL, 1, 0);
EXPECT_TRUE(trd != NULL);
// Wait for joinable coroutine to quit.
st_thread_join(trd, NULL);
}
VOID TEST(SampleTest, StartCoroutineX3)
{
st_thread_t trd0 = st_thread_create(pfn_coroutine, NULL, 1, 0);
st_thread_t trd1 = st_thread_create(pfn_coroutine, NULL, 1, 0);
st_thread_t trd2 = st_thread_create(pfn_coroutine, NULL, 1, 0);
EXPECT_TRUE(trd0 != NULL && trd1 != NULL && trd2 != NULL);
// Wait for joinable coroutine to quit.
st_thread_join(trd1, NULL);
st_thread_join(trd2, NULL);
st_thread_join(trd0, NULL);
}
void* pfn_coroutine_add(void* arg)
{
int v = 0;
int* pi = (int*)arg;
// Load the change of arg.
while (v != *pi) {
v = *pi;
st_usleep(0);
}
// Add with const.
v += 100;
*pi = v;
return NULL;
}
VOID TEST(SampleTest, StartCoroutineAdd)
{
int v = 0;
st_thread_t trd = st_thread_create(pfn_coroutine_add, &v, 1, 0);
EXPECT_TRUE(trd != NULL);
// Wait for joinable coroutine to quit.
st_thread_join(trd, NULL);
EXPECT_EQ(100, v);
}
VOID TEST(SampleTest, StartCoroutineAddX3)
{
int v = 0;
st_thread_t trd0 = st_thread_create(pfn_coroutine_add, &v, 1, 0);
st_thread_t trd1 = st_thread_create(pfn_coroutine_add, &v, 1, 0);
st_thread_t trd2 = st_thread_create(pfn_coroutine_add, &v, 1, 0);
EXPECT_TRUE(trd0 != NULL && trd1 != NULL && trd2 != NULL);
// Wait for joinable coroutine to quit.
st_thread_join(trd0, NULL);
st_thread_join(trd1, NULL);
st_thread_join(trd2, NULL);
EXPECT_EQ(300, v);
}
int pfn_coroutine_params_x4(int a, int b, int c, int d)
{
int e = 0;
st_usleep(0);
e += a + b + c + d;
e += 100;
return e;
}
void* pfn_coroutine_params(void* arg)
{
int r0 = pfn_coroutine_params_x4(1, 2, 3, 4);
*(int*)arg = r0;
return NULL;
}
VOID TEST(SampleTest, StartCoroutineParams)
{
int r0 = 0;
st_thread_t trd = st_thread_create(pfn_coroutine_params, &r0, 1, 0);
EXPECT_TRUE(trd != NULL);
// Wait for joinable coroutine to quit.
st_thread_join(trd, NULL);
EXPECT_EQ(110, r0);
}

View file

@ -10,7 +10,118 @@
// @see https://stackoverflow.com/questions/47839718/sstream-redeclared-with-public-access-compiler-error
#include <gtest/gtest.h>
#include <st.h>
#include <string>
#define VOID
// Close the fd automatically.
#define StFdCleanup(fd, stfd) impl__StFdCleanup _ST_free_##fd(&fd, &stfd)
#define StStfdCleanup(stfd) impl__StFdCleanup _ST_free_##stfd(NULL, &stfd)
class impl__StFdCleanup {
int* fd_;
st_netfd_t* stfd_;
public:
impl__StFdCleanup(int* fd, st_netfd_t* stfd) : fd_(fd), stfd_(stfd) {
}
virtual ~impl__StFdCleanup() {
if (stfd_ && *stfd_) {
st_netfd_close(*stfd_);
} else if (fd_ && *fd_ > 0) {
::close(*fd_);
}
}
};
// For coroutine function to return with error object.
struct ErrorObject {
int r0_;
int errno_;
std::string message_;
ErrorObject(int r0, std::string message) : r0_(r0), errno_(errno), message_(message) {
}
};
extern std::ostream& operator<<(std::ostream& out, const ErrorObject* err);
#define ST_ASSERT_ERROR(error, r0, message) if (error) return new ErrorObject(r0, message)
#define ST_COROUTINE_JOIN(trd, r0) ErrorObject* r0 = NULL; SrsAutoFree(ErrorObject, r0); if (trd) st_thread_join(trd, (void**)&r0)
#define ST_EXPECT_SUCCESS(r0) EXPECT_TRUE(!r0) << r0
#define ST_EXPECT_FAILED(r0) EXPECT_TRUE(r0) << r0
#include <stdlib.h>
// To free the instance in the current scope, for instance, MyClass* ptr,
// which is a ptr and this class will:
// 1. free the ptr.
// 2. set ptr to NULL.
//
// Usage:
// MyClass* po = new MyClass();
// // ...... use po
// SrsAutoFree(MyClass, po);
//
// Usage for array:
// MyClass** pa = new MyClass*[size];
// // ....... use pa
// SrsAutoFreeA(MyClass*, pa);
//
// @remark the MyClass can be basic type, for instance, SrsAutoFreeA(char, pstr),
// where the char* pstr = new char[size].
// To delete object.
#define SrsAutoFree(className, instance) \
impl_SrsAutoFree<className> _auto_free_##instance(&instance, false, false, NULL)
// To delete array.
#define SrsAutoFreeA(className, instance) \
impl_SrsAutoFree<className> _auto_free_array_##instance(&instance, true, false, NULL)
// Use free instead of delete.
#define SrsAutoFreeF(className, instance) \
impl_SrsAutoFree<className> _auto_free_##instance(&instance, false, true, NULL)
// Use hook instead of delete.
#define SrsAutoFreeH(className, instance, hook) \
impl_SrsAutoFree<className> _auto_free_##instance(&instance, false, false, hook)
// The template implementation.
template<class T>
class impl_SrsAutoFree
{
private:
T** ptr;
bool is_array;
bool _use_free;
void (*_hook)(T*);
public:
// If use_free, use free(void*) to release the p.
// If specified hook, use hook(p) to release it.
// Use delete to release p, or delete[] if p is an array.
impl_SrsAutoFree(T** p, bool array, bool use_free, void (*hook)(T*)) {
ptr = p;
is_array = array;
_use_free = use_free;
_hook = hook;
}
virtual ~impl_SrsAutoFree() {
if (ptr == NULL || *ptr == NULL) {
return;
}
if (_use_free) {
free(*ptr);
} else if (_hook) {
_hook(*ptr);
} else {
if (is_array) {
delete[] *ptr;
} else {
delete *ptr;
}
}
*ptr = NULL;
}
};
// The time unit in ms, for example 100 * SRS_UTIME_MILLISECONDS means 100ms.
#define SRS_UTIME_MILLISECONDS 1000
#endif

View file

@ -0,0 +1,120 @@
/* SPDX-License-Identifier: MIT */
/* Copyright (c) 2021 Winlin */
#include <st_utest.hpp>
#include <st.h>
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// The utest for empty coroutine.
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void* coroutine(void* /*arg*/)
{
st_usleep(0);
return NULL;
}
VOID TEST(CoroutineTest, StartCoroutine)
{
st_thread_t trd = st_thread_create(coroutine, NULL, 1, 0);
EXPECT_TRUE(trd != NULL);
// Wait for joinable coroutine to quit.
st_thread_join(trd, NULL);
}
VOID TEST(CoroutineTest, StartCoroutineX3)
{
st_thread_t trd0 = st_thread_create(coroutine, NULL, 1, 0);
st_thread_t trd1 = st_thread_create(coroutine, NULL, 1, 0);
st_thread_t trd2 = st_thread_create(coroutine, NULL, 1, 0);
EXPECT_TRUE(trd0 != NULL && trd1 != NULL && trd2 != NULL);
// Wait for joinable coroutine to quit.
st_thread_join(trd1, NULL);
st_thread_join(trd2, NULL);
st_thread_join(trd0, NULL);
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// The utest for adding coroutine.
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void* coroutine_add(void* arg)
{
int v = 0;
int* pi = (int*)arg;
// Load the change of arg.
while (v != *pi) {
v = *pi;
st_usleep(0);
}
// Add with const.
v += 100;
*pi = v;
return NULL;
}
VOID TEST(CoroutineTest, StartCoroutineAdd)
{
int v = 0;
st_thread_t trd = st_thread_create(coroutine_add, &v, 1, 0);
EXPECT_TRUE(trd != NULL);
// Wait for joinable coroutine to quit.
st_thread_join(trd, NULL);
EXPECT_EQ(100, v);
}
VOID TEST(CoroutineTest, StartCoroutineAddX3)
{
int v = 0;
st_thread_t trd0 = st_thread_create(coroutine_add, &v, 1, 0);
st_thread_t trd1 = st_thread_create(coroutine_add, &v, 1, 0);
st_thread_t trd2 = st_thread_create(coroutine_add, &v, 1, 0);
EXPECT_TRUE(trd0 != NULL && trd1 != NULL && trd2 != NULL);
// Wait for joinable coroutine to quit.
st_thread_join(trd0, NULL);
st_thread_join(trd1, NULL);
st_thread_join(trd2, NULL);
EXPECT_EQ(300, v);
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// The utest for output params coroutine.
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
int coroutine_params_x4(int a, int b, int c, int d)
{
int e = 0;
st_usleep(0);
e += a + b + c + d;
e += 100;
return e;
}
void* coroutine_params(void* arg)
{
int r0 = coroutine_params_x4(1, 2, 3, 4);
*(int*)arg = r0;
return NULL;
}
VOID TEST(CoroutineTest, StartCoroutineParams)
{
int r0 = 0;
st_thread_t trd = st_thread_create(coroutine_params, &r0, 1, 0);
EXPECT_TRUE(trd != NULL);
// Wait for joinable coroutine to quit.
st_thread_join(trd, NULL);
EXPECT_EQ(110, r0);
}

View file

@ -0,0 +1,92 @@
/* SPDX-License-Identifier: MIT */
/* Copyright (c) 2021 Winlin */
#include <st_utest.hpp>
#include <st.h>
#include <assert.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define ST_UTEST_PORT 26878
#define ST_UTEST_TIMEOUT (100 * SRS_UTIME_MILLISECONDS)
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// The utest for ping-pong TCP server coroutine.
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void* tcp_server(void* /*arg*/)
{
int fd = -1;
st_netfd_t stfd = NULL;
StFdCleanup(fd, stfd);
fd = socket(AF_INET, SOCK_STREAM, 0);
ST_ASSERT_ERROR(fd == -1, fd, "Create socket");
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl(INADDR_ANY);
addr.sin_port = htons(ST_UTEST_PORT);
int v = 1;
int r0 = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &v, sizeof(int));
ST_ASSERT_ERROR(r0, r0, "Set SO_REUSEADDR");
r0 = ::bind(fd, (const sockaddr*)&addr, sizeof(addr));
ST_ASSERT_ERROR(r0, r0, "Bind socket");
r0 = ::listen(fd, 10);
ST_ASSERT_ERROR(r0, r0, "Listen socket");
stfd = st_netfd_open_socket(fd);
ST_ASSERT_ERROR(!stfd, fd, "Open ST socket");
st_netfd_t client = NULL;
StStfdCleanup(client);
client = st_accept(stfd, NULL, NULL, ST_UTEST_TIMEOUT);
ST_ASSERT_ERROR(!client, fd, "Accept client");
return NULL;
}
void* tcp_client(void* /*arg*/)
{
int fd = -1;
st_netfd_t stfd = NULL;
StFdCleanup(fd, stfd);
fd = socket(AF_INET, SOCK_STREAM, 0);
ST_ASSERT_ERROR(fd == -1, fd, "Create socket");
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
addr.sin_port = htons(ST_UTEST_PORT);
stfd = st_netfd_open_socket(fd);
ST_ASSERT_ERROR(!stfd, fd, "Open ST socket");
int r0 = st_connect(stfd, (const sockaddr*)&addr, sizeof(addr), ST_UTEST_TIMEOUT);
ST_ASSERT_ERROR(r0, r0, "Connect to server");
return NULL;
}
VOID TEST(TcpTest, TcpConnection)
{
st_thread_t svr = st_thread_create(tcp_server, NULL, 1, 0);
EXPECT_TRUE(svr != NULL);
st_thread_t client = st_thread_create(tcp_client, NULL, 1, 0);
EXPECT_TRUE(client != NULL);
ST_COROUTINE_JOIN(svr, r0);
ST_COROUTINE_JOIN(client, r1);
ST_EXPECT_SUCCESS(r0);
ST_EXPECT_SUCCESS(r1);
}