Skip to content

Commit

Permalink
add an emulation layer for state threads, passing its UT
Browse files Browse the repository at this point in the history
  • Loading branch information
lihuiba committed Sep 16, 2024
1 parent 44abd5c commit 340f9be
Show file tree
Hide file tree
Showing 9 changed files with 953 additions and 11 deletions.
413 changes: 413 additions & 0 deletions thread/st.cpp

Large diffs are not rendered by default.

141 changes: 141 additions & 0 deletions thread/st.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
/*
Copyright 2022 The Photon Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

#ifndef __STATE_THREADS__
#define __STATE_THREADS__
#include <time.h>
#include <sys/stat.h>
#include <sys/types.h>

// This file provides an emulation layer for state threads (https://state-threads.sourceforge.net)
// #include <photon/thread/thread.h>
// #include <photon/net/socket.h>

#ifdef __cplusplus
extern "C" {
#endif

typedef void * st_thread_t;
typedef void * st_cond_t;
typedef void * st_mutex_t;
typedef void * st_netfd_t;
typedef unsigned long long st_utime_t;
// typedef void (*st_switch_cb_t)(void);



#define ST_EVENTSYS_DEFAULT 0 // epoll in Linux, kqueue in MacOSX
#define ST_EVENTSYS_SELECT 1 // epoll in Linux, kqueue in MacOSX
#define ST_EVENTSYS_POLL 2 // epoll in Linux, kqueue in MacOSX
#define ST_EVENTSYS_ALT 3 // epoll in Linux, kqueue in MacOSX
#define ST_EVENTSYS_IOURING 4 // io_uring in Linux, kqueue in MacOSX

int st_set_eventsys(int eventsys);
int st_get_eventsys(void);
int st_init(void);
int st_getfdlimit(void);
const char *st_get_eventsys_name(void);
// st_switch_cb_t st_set_switch_in_cb(st_switch_cb_t cb);
// st_switch_cb_t st_set_switch_out_cb(st_switch_cb_t cb);

st_thread_t st_thread_create(void *(*start)(void *arg), void *arg,
int joinable, int stack_size);
void st_thread_exit(void *retval);
int st_thread_join(st_thread_t thread, void **retvalp);
st_thread_t st_thread_self(void);
void st_thread_interrupt(st_thread_t thread);
int st_sleep(int secs);
int st_usleep(st_utime_t usecs);
int st_randomize_stacks(int on);
int st_key_create(int *keyp, void (*destructor)(void *));
int st_key_getlimit(void);
int st_thread_setspecific(int key, void *value);
void *st_thread_getspecific(int key);

// Synchronization
st_cond_t st_cond_new(void);
int st_cond_destroy(st_cond_t cvar);
int st_cond_wait(st_cond_t cvar);
int st_cond_timedwait(st_cond_t cvar, st_utime_t timeout);
int st_cond_signal(st_cond_t cvar);
int st_cond_broadcast(st_cond_t cvar);

st_mutex_t st_mutex_new(void);
int st_mutex_destroy(st_mutex_t lock);
int st_mutex_lock(st_mutex_t lock);
int st_mutex_trylock(st_mutex_t lock);
int st_mutex_unlock(st_mutex_t lock);

time_t st_time(void);
st_utime_t st_utime(void);
int st_set_utime_function(st_utime_t (*func)(void));
int st_timecache_set(int on);


// I/O Functions
st_netfd_t st_netfd_open(int osfd);
st_netfd_t st_netfd_open_socket(int osfd);
void st_netfd_free(st_netfd_t fd);
int st_netfd_close(st_netfd_t fd);
int st_netfd_fileno(st_netfd_t fd);
void st_netfd_setspecific(st_netfd_t fd, void *value,
void (*destructor)(void *));
void *st_netfd_getspecific(st_netfd_t fd);
int st_netfd_serialize_accept(st_netfd_t fd);
int st_netfd_poll(st_netfd_t fd, int how, st_utime_t timeout);
st_netfd_t st_accept(st_netfd_t fd, struct sockaddr *addr, int *addrlen,
st_utime_t timeout);
int st_connect(st_netfd_t fd, const struct sockaddr *addr, int addrlen,
st_utime_t timeout);
ssize_t st_read(st_netfd_t fd, void *buf, size_t nbyte, st_utime_t timeout);
ssize_t st_read_fully(st_netfd_t fd, void *buf, size_t nbyte,
st_utime_t timeout);
int st_read_resid(st_netfd_t fd, void *buf, size_t *resid,
st_utime_t timeout);
ssize_t st_readv(st_netfd_t fd, const struct iovec *iov, int iov_size,
st_utime_t timeout);
int st_readv_resid(st_netfd_t fd, struct iovec **iov, int *iov_size,
st_utime_t timeout);
ssize_t st_write(st_netfd_t fd, const void *buf, size_t nbyte,
st_utime_t timeout);
int st_write_resid(st_netfd_t fd, const void *buf, size_t *resid,
st_utime_t timeout);
ssize_t st_writev(st_netfd_t fd, const struct iovec *iov, int iov_size,
st_utime_t timeout);
ssize_t st_writev(st_netfd_t fd, const struct iovec *iov, int iov_size,
st_utime_t timeout);
int st_writev_resid(st_netfd_t fd, struct iovec **iov, int *iov_size,
st_utime_t timeout);
int st_recvfrom(st_netfd_t fd, void *buf, int len, struct sockaddr *from,
int *fromlen, st_utime_t timeout);
int st_sendto(st_netfd_t fd, const void *msg, int len, struct sockaddr *to,
int tolen, st_utime_t timeout);
int st_recvmsg(st_netfd_t fd, struct msghdr *msg, int flags,
st_utime_t timeout);
int st_sendmsg(st_netfd_t fd, const struct msghdr *msg, int flags,
st_utime_t timeout);
st_netfd_t st_open(const char *path, int oflags, mode_t mode);
int st_poll(struct pollfd *pds, int npds, st_utime_t timeout);




#ifdef __cplusplus
}
#endif


#endif
6 changes: 5 additions & 1 deletion thread/test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,8 @@ add_test(NAME test-multi-vcpu-locking COMMAND $<TARGET_FILE:test-multi-vcpu-lock

add_executable(test-pooled-stack-allocator test-pooled-stack-allocator.cpp)
target_link_libraries(test-pooled-stack-allocator PRIVATE photon_shared)
add_test(NAME test-pooled-stack-allocator COMMAND $<TARGET_FILE:test-pooled-stack-allocator>)
add_test(NAME test-pooled-stack-allocator COMMAND $<TARGET_FILE:test-pooled-stack-allocator>)

add_executable(test-st-utest st_utest.cpp st_utest_tcp.cpp st_utest_coroutines.cpp)
target_link_libraries(test-st-utest PRIVATE photon_shared)
add_test(NAME test-st-utest COMMAND $<TARGET_FILE:test-st-utest>)
43 changes: 43 additions & 0 deletions thread/test/st_utest.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/* SPDX-License-Identifier: MIT */
/* Copyright (c) 2013-2024 The SRS Authors */

#include "st_utest.hpp"

// #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) {
// Select the best event system available on the OS. In Linux this is
// epoll(). On BSD it will be kqueue. On Cygwin it will be select.
#if __CYGWIN__
assert(st_set_eventsys(ST_EVENTSYS_SELECT) != -1);
#else
assert(st_set_eventsys(ST_EVENTSYS_ALT) != -1);
#endif

// Initialize state-threads, create idle coroutine.
assert(st_init() == 0);

testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}

// basic test and samples.
VOID TEST(SampleTest, ExampleIntSizeTest)
{
EXPECT_EQ(1, (int)sizeof(int8_t));
EXPECT_EQ(2, (int)sizeof(int16_t));
EXPECT_EQ(4, (int)sizeof(int32_t));
EXPECT_EQ(8, (int)sizeof(int64_t));
}

127 changes: 127 additions & 0 deletions thread/test/st_utest.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
/* SPDX-License-Identifier: MIT */
/* Copyright (c) 2013-2024 The SRS Authors */

#ifndef ST_UTEST_PUBLIC_HPP
#define ST_UTEST_PUBLIC_HPP

// Before define the private/protected, we must include some system header files.
// Or it may fail with:
// redeclared with different access struct __xfer_bufptrs
// @see https://stackoverflow.com/questions/47839718/sstream-redeclared-with-public-access-compiler-error
#include <gtest/gtest.h>

#include <photon/thread/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

Loading

0 comments on commit 340f9be

Please sign in to comment.