ST: Replace macros with explicit code for better understanding. v7.0.7 (#4149)

Improvements for ST(State Threads):

1. ST: Use g++ for CXX compiler.
2. ST: Remove macros for clist.
3. ST: Remove macros for global thread and vp.
4. ST: Remove macros for vp queue operations.
5. ST: Remove macros for context switch.
6. ST: Remove macros for setjmp/longjmp.
7. ST: Remove macro for stack pad.
8. ST: Refine macro for valgrind.

---------

Co-authored-by: Jacob Su <suzp1984@gmail.com>
pull/4152/head
Winlin 5 months ago committed by GitHub
parent 0d76081430
commit ff6a608099
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -46,6 +46,7 @@ VERSION = 1.9
##########################
CC = cc
CXX = g++
AR = ar
LD = ld
RANLIB = ranlib
@ -207,7 +208,8 @@ OBJS = $(TARGETDIR)/sched.o \
$(TARGETDIR)/sync.o \
$(TARGETDIR)/key.o \
$(TARGETDIR)/io.o \
$(TARGETDIR)/event.o
$(TARGETDIR)/event.o \
$(TARGETDIR)/common.o
OBJS += $(EXTRA_OBJS)
HEADER = $(TARGETDIR)/st.h
SLIBRARY = $(TARGETDIR)/libst.a
@ -270,21 +272,16 @@ $(HEADER): public.h
rm -f $@
cp public.h $@
$(TARGETDIR)/md_linux.o: md_linux.S
$(CC) $(CFLAGS) -c $< -o $@
$(TARGETDIR)/md_linux2.o: md_linux2.S
$(CC) $(CFLAGS) -c $< -o $@
$(TARGETDIR)/md_darwin.o: md_darwin.S
$(CC) $(CFLAGS) -c $< -o $@
$(TARGETDIR)/md_cygwin64.o: md_cygwin64.S
$(TARGETDIR)/%.o: %.S
$(CC) $(CFLAGS) -c $< -o $@
$(TARGETDIR)/%.o: %.c common.h md.h
$(CC) $(CFLAGS) -c $< -o $@
# Note that we use C++98 standard for the C++ files.
$(TARGETDIR)/%.o: %.cc common.h md.h
$(CXX) $(CFLAGS) -c $< -o $@ -std=c++98
clean:
rm -rf *_OPT *_DBG obj st.pc

@ -0,0 +1,23 @@
/* SPDX-License-Identifier: MIT */
/* Copyright (c) 2021-2022 The SRS Authors */
#include "common.h"
void _st_switch_context(_st_thread_t *thread)
{
ST_SWITCH_OUT_CB(thread);
if (!_st_md_cxt_save(thread->context)) {
_st_vp_schedule();
}
ST_DEBUG_ITERATE_THREADS();
ST_SWITCH_IN_CB(thread);
}
void _st_restore_context(_st_thread_t *thread)
{
_st_this_thread = thread;
_st_md_cxt_restore(thread->context, 1);
}

@ -69,13 +69,8 @@
#include "public.h"
#include "md.h"
/* merge from https://github.com/toffaletti/state-threads/commit/7f57fc9acc05e657bca1223f1e5b9b1a45ed929b */
#ifndef MD_VALGRIND
#ifndef NVALGRIND
#define NVALGRIND
#endif
#else
#undef NVALGRIND
#ifdef __cplusplus
extern "C" {
#endif
@ -88,58 +83,37 @@ typedef struct _st_clist {
struct _st_clist *prev;
} _st_clist_t;
/* Insert element "_e" into the list, before "_l" */
#define ST_INSERT_BEFORE(_e,_l) \
ST_BEGIN_MACRO \
(_e)->next = (_l); \
(_e)->prev = (_l)->prev; \
(_l)->prev->next = (_e); \
(_l)->prev = (_e); \
ST_END_MACRO
/* Insert element "_e" into the list, after "_l" */
#define ST_INSERT_AFTER(_e,_l) \
ST_BEGIN_MACRO \
(_e)->next = (_l)->next; \
(_e)->prev = (_l); \
(_l)->next->prev = (_e); \
(_l)->next = (_e); \
ST_END_MACRO
/* Return the element following element "_e" */
#define ST_NEXT_LINK(_e) ((_e)->next)
/* Append an element "_e" to the end of the list "_l" */
#define ST_APPEND_LINK(_e,_l) ST_INSERT_BEFORE(_e,_l)
/* Insert an element "_e" at the head of the list "_l" */
#define ST_INSERT_LINK(_e,_l) ST_INSERT_AFTER(_e,_l)
/* Return the head/tail of the list */
#define ST_LIST_HEAD(_l) (_l)->next
#define ST_LIST_TAIL(_l) (_l)->prev
/* Initialize a circular list */
static inline void st_clist_init(_st_clist_t *l)
{
l->next = l;
l->prev = l;
}
/* Remove the element "_e" from it's circular list */
#define ST_REMOVE_LINK(_e) \
ST_BEGIN_MACRO \
(_e)->prev->next = (_e)->next; \
(_e)->next->prev = (_e)->prev; \
ST_END_MACRO
/* Return non-zero if the given circular list "_l" is empty, */
/* zero if the circular list is not empty */
#define ST_CLIST_IS_EMPTY(_l) \
((_l)->next == (_l))
static inline void st_clist_remove(_st_clist_t *e)
{
e->prev->next = e->next;
e->next->prev = e->prev;
}
/* Initialize a circular list */
#define ST_INIT_CLIST(_l) \
ST_BEGIN_MACRO \
(_l)->next = (_l); \
(_l)->prev = (_l); \
ST_END_MACRO
/* Insert element "_e" into the list, before "_l" */
static inline void st_clist_insert_before(_st_clist_t *e, _st_clist_t *l)
{
e->next = l;
e->prev = l->prev;
l->prev->next = e;
l->prev = e;
}
#define ST_INIT_STATIC_CLIST(_l) \
{(_l), (_l)}
/* Insert element "_e" into the list, after "_l" */
static inline void st_clist_insert_after(_st_clist_t *e, _st_clist_t *l)
{
e->next = l->next;
e->prev = l;
l->next->prev = e;
l->next = e;
}
/*****************************************
@ -158,7 +132,7 @@ typedef struct _st_stack {
char *stk_top; /* Highest address of stack's usable portion */
void *sp; /* Stack pointer from C's point of view */
/* merge from https://github.com/toffaletti/state-threads/commit/7f57fc9acc05e657bca1223f1e5b9b1a45ed929b */
#ifndef NVALGRIND
#ifdef MD_VALGRIND
/* id returned by VALGRIND_STACK_REGISTER */
/* http://valgrind.org/docs/manual/manual-core-adv.html */
unsigned long valgrind_stack_id;
@ -270,48 +244,6 @@ extern __thread _st_vp_t _st_this_vp;
extern __thread _st_thread_t *_st_this_thread;
extern __thread _st_eventsys_t *_st_eventsys;
#define _ST_CURRENT_THREAD() (_st_this_thread)
#define _ST_SET_CURRENT_THREAD(_thread) (_st_this_thread = (_thread))
#define _ST_LAST_CLOCK (_st_this_vp.last_clock)
#define _ST_RUNQ (_st_this_vp.run_q)
#define _ST_IOQ (_st_this_vp.io_q)
#define _ST_ZOMBIEQ (_st_this_vp.zombie_q)
#ifdef DEBUG
#define _ST_THREADQ (_st_this_vp.thread_q)
#endif
#define _ST_PAGE_SIZE (_st_this_vp.pagesize)
#define _ST_SLEEPQ (_st_this_vp.sleep_q)
#define _ST_SLEEPQ_SIZE (_st_this_vp.sleepq_size)
#define _ST_VP_IDLE() (*_st_eventsys->dispatch)()
/*****************************************
* vp queues operations
*/
#define _ST_ADD_IOQ(_pq) ST_APPEND_LINK(&_pq.links, &_ST_IOQ)
#define _ST_DEL_IOQ(_pq) ST_REMOVE_LINK(&_pq.links)
#define _ST_ADD_RUNQ(_thr) ST_APPEND_LINK(&(_thr)->links, &_ST_RUNQ)
#define _ST_INSERT_RUNQ(_thr) ST_INSERT_LINK(&(_thr)->links, &_ST_RUNQ)
#define _ST_DEL_RUNQ(_thr) ST_REMOVE_LINK(&(_thr)->links)
#define _ST_ADD_SLEEPQ(_thr, _timeout) _st_add_sleep_q(_thr, _timeout)
#define _ST_DEL_SLEEPQ(_thr) _st_del_sleep_q(_thr)
#define _ST_ADD_ZOMBIEQ(_thr) ST_APPEND_LINK(&(_thr)->links, &_ST_ZOMBIEQ)
#define _ST_DEL_ZOMBIEQ(_thr) ST_REMOVE_LINK(&(_thr)->links)
#ifdef DEBUG
#define _ST_ADD_THREADQ(_thr) ST_APPEND_LINK(&(_thr)->tlink, &_ST_THREADQ)
#define _ST_DEL_THREADQ(_thr) ST_REMOVE_LINK(&(_thr)->tlink)
#endif
/*****************************************
* Thread states and flags
@ -411,39 +343,18 @@ extern __thread _st_eventsys_t *_st_eventsys;
* Switch away from the current thread context by saving its state and
* calling the thread scheduler
*/
#define _ST_SWITCH_CONTEXT(_thread) \
ST_BEGIN_MACRO \
ST_SWITCH_OUT_CB(_thread); \
if (!MD_SETJMP((_thread)->context)) { \
_st_vp_schedule(); \
} \
ST_DEBUG_ITERATE_THREADS(); \
ST_SWITCH_IN_CB(_thread); \
ST_END_MACRO
void _st_switch_context(_st_thread_t *thread);
/*
* Restore a thread context that was saved by _ST_SWITCH_CONTEXT or
* Restore a thread context that was saved by _st_switch_context or
* initialized by _ST_INIT_CONTEXT
*/
#define _ST_RESTORE_CONTEXT(_thread) \
ST_BEGIN_MACRO \
_ST_SET_CURRENT_THREAD(_thread); \
MD_LONGJMP((_thread)->context, 1); \
ST_END_MACRO
/*
* Initialize the thread context preparing it to execute _main
*/
#ifdef MD_INIT_CONTEXT
#define _ST_INIT_CONTEXT MD_INIT_CONTEXT
#else
#error Unknown OS
#endif
void _st_restore_context(_st_thread_t *thread);
/*
* Number of bytes reserved under the stack "bottom"
*/
#define _ST_STACK_PAD_SIZE MD_STACK_PAD_SIZE
#define _ST_STACK_PAD_SIZE 128
/*****************************************
@ -471,5 +382,9 @@ ssize_t st_write(_st_netfd_t *fd, const void *buf, size_t nbyte, st_utime_t time
int st_poll(struct pollfd *pds, int npds, st_utime_t timeout);
_st_thread_t *st_thread_create(void *(*start)(void *arg), void *arg, int joinable, int stk_size);
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* !__ST_COMMON_H__ */

@ -22,7 +22,7 @@ appropriate for ST than introducing a separate array.
Thus, the new ST timeout heap works by organizing the existing
_st_thread_t objects in a balanced binary tree, just as they were
previously organized into a doubly-linked, sorted list. The global
_ST_SLEEPQ variable, formerly a linked list head, is now simply a
_st_this_vp.sleep_q variable, formerly a linked list head, is now simply a
pointer to the root of this tree, and the root node of the tree is the
thread with the earliest timeout. Each thread object has two child
pointers, "left" and "right", pointing to threads with later timeouts.

@ -233,7 +233,7 @@ ST_HIDDEN void _st_select_find_bad_fd(void)
_ST_SELECT_MAX_OSFD = -1;
for (q = _ST_IOQ.next; q != &_ST_IOQ; q = q->next) {
for (q = _st_this_vp.io_q.next; q != &_st_this_vp.io_q; q = q->next) {
pq = _ST_POLLQUEUE_PTR(q);
notify = 0;
epds = pq->pds + pq->npds;
@ -254,7 +254,7 @@ ST_HIDDEN void _st_select_find_bad_fd(void)
}
if (notify) {
ST_REMOVE_LINK(&pq->links);
st_clist_remove(&pq->links);
pq->on_ioq = 0;
/*
* Decrement the count of descriptors for each descriptor/event
@ -281,9 +281,9 @@ ST_HIDDEN void _st_select_find_bad_fd(void)
}
if (pq->thread->flags & _ST_FL_ON_SLEEPQ)
_ST_DEL_SLEEPQ(pq->thread);
_st_del_sleep_q(pq->thread);
pq->thread->state = _ST_ST_RUNNABLE;
_ST_ADD_RUNQ(pq->thread);
st_clist_insert_before(&pq->thread->links, &_st_this_vp.run_q);
} else {
if (_ST_SELECT_MAX_OSFD < pq_max_osfd)
_ST_SELECT_MAX_OSFD = pq_max_osfd;
@ -315,11 +315,11 @@ ST_HIDDEN void _st_select_dispatch(void)
wp = &w;
ep = &e;
if (_ST_SLEEPQ == NULL) {
if (_st_this_vp.sleep_q == NULL) {
tvp = NULL;
} else {
min_timeout = (_ST_SLEEPQ->due <= _ST_LAST_CLOCK) ? 0 :
(_ST_SLEEPQ->due - _ST_LAST_CLOCK);
min_timeout = (_st_this_vp.sleep_q->due <= _st_this_vp.last_clock) ? 0 :
(_st_this_vp.sleep_q->due - _st_this_vp.last_clock);
timeout.tv_sec = (int) (min_timeout / 1000000);
timeout.tv_usec = (int) (min_timeout % 1000000);
tvp = &timeout;
@ -331,7 +331,7 @@ ST_HIDDEN void _st_select_dispatch(void)
/* Notify threads that are associated with the selected descriptors */
if (nfd > 0) {
_ST_SELECT_MAX_OSFD = -1;
for (q = _ST_IOQ.next; q != &_ST_IOQ; q = q->next) {
for (q = _st_this_vp.io_q.next; q != &_st_this_vp.io_q; q = q->next) {
pq = _ST_POLLQUEUE_PTR(q);
notify = 0;
epds = pq->pds + pq->npds;
@ -359,7 +359,7 @@ ST_HIDDEN void _st_select_dispatch(void)
}
}
if (notify) {
ST_REMOVE_LINK(&pq->links);
st_clist_remove(&pq->links);
pq->on_ioq = 0;
/*
* Decrement the count of descriptors for each descriptor/event
@ -386,9 +386,9 @@ ST_HIDDEN void _st_select_dispatch(void)
}
if (pq->thread->flags & _ST_FL_ON_SLEEPQ)
_ST_DEL_SLEEPQ(pq->thread);
_st_del_sleep_q(pq->thread);
pq->thread->state = _ST_ST_RUNNABLE;
_ST_ADD_RUNQ(pq->thread);
st_clist_insert_before(&pq->thread->links, &_st_this_vp.run_q);
} else {
if (_ST_SELECT_MAX_OSFD < pq_max_osfd)
_ST_SELECT_MAX_OSFD = pq_max_osfd;
@ -697,10 +697,10 @@ ST_HIDDEN void _st_kq_dispatch(void)
int nfd, i, osfd, notify, filter;
short events, revents;
if (_ST_SLEEPQ == NULL) {
if (_st_this_vp.sleep_q == NULL) {
tsp = NULL;
} else {
min_timeout = (_ST_SLEEPQ->due <= _ST_LAST_CLOCK) ? 0 : (_ST_SLEEPQ->due - _ST_LAST_CLOCK);
min_timeout = (_st_this_vp.sleep_q->due <= _st_this_vp.last_clock) ? 0 : (_st_this_vp.sleep_q->due - _st_this_vp.last_clock);
timeout.tv_sec = (time_t) (min_timeout / 1000000);
timeout.tv_nsec = (long) ((min_timeout % 1000000) * 1000);
tsp = &timeout;
@ -735,7 +735,7 @@ ST_HIDDEN void _st_kq_dispatch(void)
_st_kq_data->dellist_cnt = 0;
for (q = _ST_IOQ.next; q != &_ST_IOQ; q = q->next) {
for (q = _st_this_vp.io_q.next; q != &_st_this_vp.io_q; q = q->next) {
pq = _ST_POLLQUEUE_PTR(q);
notify = 0;
epds = pq->pds + pq->npds;
@ -756,7 +756,7 @@ ST_HIDDEN void _st_kq_dispatch(void)
}
}
if (notify) {
ST_REMOVE_LINK(&pq->links);
st_clist_remove(&pq->links);
pq->on_ioq = 0;
for (pds = pq->pds; pds < epds; pds++) {
osfd = pds->fd;
@ -782,9 +782,9 @@ ST_HIDDEN void _st_kq_dispatch(void)
}
if (pq->thread->flags & _ST_FL_ON_SLEEPQ)
_ST_DEL_SLEEPQ(pq->thread);
_st_del_sleep_q(pq->thread);
pq->thread->state = _ST_ST_RUNNABLE;
_ST_ADD_RUNQ(pq->thread);
st_clist_insert_before(&pq->thread->links, &_st_this_vp.run_q);
}
}
@ -811,7 +811,7 @@ ST_HIDDEN void _st_kq_dispatch(void)
_st_kq_data->pid = getpid();
/* Re-register all descriptors on ioq with new kqueue */
memset(_st_kq_data->fd_data, 0, _st_kq_data->fd_data_size * sizeof(_kq_fd_data_t));
for (q = _ST_IOQ.next; q != &_ST_IOQ; q = q->next) {
for (q = _st_this_vp.io_q.next; q != &_st_this_vp.io_q; q = q->next) {
pq = _ST_POLLQUEUE_PTR(q);
_st_kq_pollset_add(pq->pds, pq->npds);
}
@ -1064,10 +1064,10 @@ ST_HIDDEN void _st_epoll_dispatch(void)
++_st_stat_epoll;
#endif
if (_ST_SLEEPQ == NULL) {
if (_st_this_vp.sleep_q == NULL) {
timeout = -1;
} else {
min_timeout = (_ST_SLEEPQ->due <= _ST_LAST_CLOCK) ? 0 : (_ST_SLEEPQ->due - _ST_LAST_CLOCK);
min_timeout = (_st_this_vp.sleep_q->due <= _st_this_vp.last_clock) ? 0 : (_st_this_vp.sleep_q->due - _st_this_vp.last_clock);
timeout = (int) (min_timeout / 1000);
// At least wait 1ms when <1ms, to avoid epoll_wait spin loop.
@ -1105,7 +1105,7 @@ ST_HIDDEN void _st_epoll_dispatch(void)
}
}
for (q = _ST_IOQ.next; q != &_ST_IOQ; q = q->next) {
for (q = _st_this_vp.io_q.next; q != &_st_this_vp.io_q; q = q->next) {
pq = _ST_POLLQUEUE_PTR(q);
notify = 0;
epds = pq->pds + pq->npds;
@ -1135,7 +1135,7 @@ ST_HIDDEN void _st_epoll_dispatch(void)
}
}
if (notify) {
ST_REMOVE_LINK(&pq->links);
st_clist_remove(&pq->links);
pq->on_ioq = 0;
/*
* Here we will only delete/modify descriptors that
@ -1144,9 +1144,9 @@ ST_HIDDEN void _st_epoll_dispatch(void)
_st_epoll_pollset_del(pq->pds, pq->npds);
if (pq->thread->flags & _ST_FL_ON_SLEEPQ)
_ST_DEL_SLEEPQ(pq->thread);
_st_del_sleep_q(pq->thread);
pq->thread->state = _ST_ST_RUNNABLE;
_ST_ADD_RUNQ(pq->thread);
st_clist_insert_before(&pq->thread->links, &_st_this_vp.run_q);
}
}

@ -78,7 +78,7 @@ int st_key_getlimit(void)
int st_thread_setspecific(int key, void *value)
{
_st_thread_t *me = _ST_CURRENT_THREAD();
_st_thread_t *me = _st_this_thread;
return st_thread_setspecific2(me, key, value);
}
@ -107,7 +107,7 @@ void *st_thread_getspecific(int key)
if (key < 0 || key >= key_max)
return NULL;
return ((_ST_CURRENT_THREAD())->private_data[key]);
return _st_this_thread->private_data[key];
}

@ -74,13 +74,11 @@ typedef struct _st_jmp_buf {
long __jmpbuf[22];
} _st_jmp_buf_t[1];
/* Defined in *.S file and implemented by ASM. */
extern int _st_md_cxt_save(_st_jmp_buf_t env);
extern void _st_md_cxt_restore(_st_jmp_buf_t env, int val);
/* Always use builtin setjmp/longjmp, use asm code. */
#define MD_USE_BUILTIN_SETJMP
#define MD_SETJMP(env) _st_md_cxt_save(env)
#define MD_LONGJMP(env, val) _st_md_cxt_restore(env, val)
#if defined(USE_LIBC_SETJMP)
#error The libc setjmp is not supported now
#endif
@ -102,13 +100,6 @@ extern void _st_md_cxt_restore(_st_jmp_buf_t env, int val);
#else
#error Unknown CPU architecture
#endif
#define MD_INIT_CONTEXT(_thread, _sp, _main) \
ST_BEGIN_MACRO \
if (MD_SETJMP((_thread)->context)) \
_main(); \
MD_GET_SP(_thread) = (long) (_sp); \
ST_END_MACRO
#define MD_GET_UTIME() \
struct timeval tv; \
@ -161,14 +152,7 @@ extern void _st_md_cxt_restore(_st_jmp_buf_t env, int val);
#define MD_GET_SP(_t) *((long *)&((_t)->context[0].__jmpbuf[0]))
#else
#error "Unknown CPU architecture"
#endif /* Cases with common MD_INIT_CONTEXT and different SP locations */
#define MD_INIT_CONTEXT(_thread, _sp, _main) \
ST_BEGIN_MACRO \
if (MD_SETJMP((_thread)->context)) \
_main(); \
MD_GET_SP(_thread) = (long) (_sp); \
ST_END_MACRO
#endif
#elif defined (CYGWIN64)
@ -177,21 +161,12 @@ extern void _st_md_cxt_restore(_st_jmp_buf_t env, int val);
#define MD_ACCEPT_NB_INHERITED
#define MD_HAVE_SOCKLEN_T
#define MD_USE_BUILTIN_SETJMP
#if defined(__amd64__) || defined(__x86_64__)
#define MD_GET_SP(_t) *((long *)&((_t)->context[0].__jmpbuf[6]))
#else
#error Unknown CPU architecture
#endif
#define MD_INIT_CONTEXT(_thread, _sp, _main) \
ST_BEGIN_MACRO \
if (MD_SETJMP((_thread)->context)) \
_main(); \
MD_GET_SP(_thread) = (long) (_sp); \
ST_END_MACRO
#define MD_GET_UTIME() \
struct timeval tv; \
(void) gettimeofday(&tv, NULL); \
@ -201,10 +176,6 @@ extern void _st_md_cxt_restore(_st_jmp_buf_t env, int val);
#error Unknown OS
#endif /* OS */
#ifndef MD_STACK_PAD_SIZE
#define MD_STACK_PAD_SIZE 128
#endif
#if !defined(MD_HAVE_SOCKLEN_T) && !defined(socklen_t)
#define socklen_t int
#endif

@ -50,7 +50,7 @@
#include "common.h"
/* merge from https://github.com/toffaletti/state-threads/commit/7f57fc9acc05e657bca1223f1e5b9b1a45ed929b */
#ifndef NVALGRIND
#ifdef MD_VALGRIND
#include <valgrind/valgrind.h>
#endif
@ -89,7 +89,7 @@ int st_poll(struct pollfd *pds, int npds, st_utime_t timeout)
struct pollfd *pd;
struct pollfd *epd = pds + npds;
_st_pollq_t pq;
_st_thread_t *me = _ST_CURRENT_THREAD();
_st_thread_t *me = _st_this_thread;
int n;
if (me->flags & _ST_FL_INTERRUPT) {
@ -105,17 +105,17 @@ int st_poll(struct pollfd *pds, int npds, st_utime_t timeout)
pq.npds = npds;
pq.thread = me;
pq.on_ioq = 1;
_ST_ADD_IOQ(pq);
st_clist_insert_before(&pq.links, &_st_this_vp.io_q);
if (timeout != ST_UTIME_NO_TIMEOUT)
_ST_ADD_SLEEPQ(me, timeout);
_st_add_sleep_q(me, timeout);
me->state = _ST_ST_IO_WAIT;
_ST_SWITCH_CONTEXT(me);
_st_switch_context(me);
n = 0;
if (pq.on_ioq) {
/* If we timed out, the pollq might still be on the ioq. Remove it */
_ST_DEL_IOQ(pq);
st_clist_remove(&pq.links);
(*_st_eventsys->pollset_del)(pds, npds);
} else {
/* Count the number of ready descriptors */
@ -139,14 +139,14 @@ void _st_vp_schedule(void)
{
_st_thread_t *thread;
if (_ST_RUNQ.next != &_ST_RUNQ) {
if (_st_this_vp.run_q.next != &_st_this_vp.run_q) {
#if defined(DEBUG) && defined(DEBUG_STATS)
++_st_stat_thread_run;
#endif
/* Pull thread off of the run queue */
thread = _ST_THREAD_PTR(_ST_RUNQ.next);
_ST_DEL_RUNQ(thread);
thread = _ST_THREAD_PTR(_st_this_vp.run_q.next);
st_clist_remove(&thread->links);
} else {
#if defined(DEBUG) && defined(DEBUG_STATS)
++_st_stat_thread_idle;
@ -159,7 +159,7 @@ void _st_vp_schedule(void)
/* Resume the thread */
thread->state = _ST_ST_RUNNING;
_ST_RESTORE_CONTEXT(thread);
_st_restore_context(thread);
}
@ -182,16 +182,16 @@ int st_init(void)
return -1;
// Initialize the thread-local variables.
ST_INIT_CLIST(&_st_free_stacks);
st_clist_init(&_st_free_stacks);
// Initialize ST.
memset(&_st_this_vp, 0, sizeof(_st_vp_t));
ST_INIT_CLIST(&_ST_RUNQ);
ST_INIT_CLIST(&_ST_IOQ);
ST_INIT_CLIST(&_ST_ZOMBIEQ);
st_clist_init(&_st_this_vp.run_q);
st_clist_init(&_st_this_vp.io_q);
st_clist_init(&_st_this_vp.zombie_q);
#ifdef DEBUG
ST_INIT_CLIST(&_ST_THREADQ);
st_clist_init(&_st_this_vp.thread_q);
#endif
if ((*_st_eventsys->init)() < 0)
@ -208,7 +208,7 @@ int st_init(void)
return -1;
_st_this_vp.idle_thread->flags = _ST_FL_IDLE_THREAD;
_st_active_count--;
_ST_DEL_RUNQ(_st_this_vp.idle_thread);
st_clist_remove(&_st_this_vp.idle_thread->links);
/*
* Initialize primordial thread
@ -219,10 +219,10 @@ int st_init(void)
thread->private_data = (void **) (thread + 1);
thread->state = _ST_ST_RUNNING;
thread->flags = _ST_FL_PRIMORDIAL;
_ST_SET_CURRENT_THREAD(thread);
_st_this_thread = thread;
_st_active_count++;
#ifdef DEBUG
_ST_ADD_THREADQ(thread);
st_clist_insert_before(&thread->tlink, &_st_this_vp.thread_q);
#endif
return 0;
@ -261,17 +261,17 @@ st_switch_cb_t st_set_switch_out_cb(st_switch_cb_t cb)
/* ARGSUSED */
void *_st_idle_thread_start(void *arg)
{
_st_thread_t *me = _ST_CURRENT_THREAD();
_st_thread_t *me = _st_this_thread;
while (_st_active_count > 0) {
/* Idle vp till I/O is ready or the smallest timeout expired */
_ST_VP_IDLE();
(*_st_eventsys->dispatch)();
/* Check sleep queue for expired threads */
_st_vp_check_clock();
me->state = _ST_ST_RUNNABLE;
_ST_SWITCH_CONTEXT(me);
_st_switch_context(me);
}
/* No more threads */
@ -284,7 +284,7 @@ void *_st_idle_thread_start(void *arg)
void st_thread_exit(void *retval)
{
_st_thread_t *thread = _ST_CURRENT_THREAD();
_st_thread_t *thread = _st_this_thread;
thread->retval = retval;
_st_thread_cleanup(thread);
@ -292,13 +292,13 @@ void st_thread_exit(void *retval)
if (thread->term) {
/* Put thread on the zombie queue */
thread->state = _ST_ST_ZOMBIE;
_ST_ADD_ZOMBIEQ(thread);
st_clist_insert_before(&thread->links, &_st_this_vp.zombie_q);
/* Notify on our termination condition variable */
st_cond_signal(thread->term);
/* Switch context and come back later */
_ST_SWITCH_CONTEXT(thread);
_st_switch_context(thread);
/* Continue the cleanup */
st_cond_destroy(thread->term);
@ -306,11 +306,11 @@ void st_thread_exit(void *retval)
}
#ifdef DEBUG
_ST_DEL_THREADQ(thread);
st_clist_remove(&thread->tlink);
#endif
/* merge from https://github.com/toffaletti/state-threads/commit/7f57fc9acc05e657bca1223f1e5b9b1a45ed929b */
#ifndef NVALGRIND
#ifdef MD_VALGRIND
if (!(thread->flags & _ST_FL_PRIMORDIAL)) {
VALGRIND_STACK_DEREGISTER(thread->stack->valgrind_stack_id);
}
@ -320,7 +320,7 @@ void st_thread_exit(void *retval)
_st_stack_free(thread->stack);
/* Find another thread to run */
_ST_SWITCH_CONTEXT(thread);
_st_switch_context(thread);
/* Not going to land here */
}
@ -334,7 +334,7 @@ int st_thread_join(_st_thread_t *thread, void **retvalp)
errno = EINVAL;
return -1;
}
if (_ST_CURRENT_THREAD() == thread) {
if (_st_this_thread == thread) {
errno = EDEADLK;
return -1;
}
@ -358,8 +358,8 @@ int st_thread_join(_st_thread_t *thread, void **retvalp)
* When it gets scheduled later, it will do the clean up.
*/
thread->state = _ST_ST_RUNNABLE;
_ST_DEL_ZOMBIEQ(thread);
_ST_ADD_RUNQ(thread);
st_clist_remove(&thread->links);
st_clist_insert_before(&thread->links, &_st_this_vp.run_q);
return 0;
}
@ -367,7 +367,7 @@ int st_thread_join(_st_thread_t *thread, void **retvalp)
void _st_thread_main(void)
{
_st_thread_t *thread = _ST_CURRENT_THREAD();
_st_thread_t *thread = _st_this_thread;
/*
* Cap the stack by zeroing out the saved return address register
@ -392,7 +392,7 @@ void _st_thread_main(void)
static _st_thread_t **heap_insert(_st_thread_t *thread) {
int target = thread->heap_index;
int s = target;
_st_thread_t **p = &_ST_SLEEPQ;
_st_thread_t **p = &_st_this_vp.sleep_q;
int bits = 0;
int bit;
int index = 1;
@ -434,14 +434,14 @@ static void heap_delete(_st_thread_t *thread) {
int s, bit;
/* First find and unlink the last heap element */
p = &_ST_SLEEPQ;
s = _ST_SLEEPQ_SIZE;
p = &_st_this_vp.sleep_q;
s = _st_this_vp.sleepq_size;
while (s) {
s >>= 1;
bits++;
}
for (bit = bits - 2; bit >= 0; bit--) {
if (_ST_SLEEPQ_SIZE & (1 << bit)) {
if (_st_this_vp.sleepq_size & (1 << bit)) {
p = &((*p)->right);
} else {
p = &((*p)->left);
@ -449,7 +449,7 @@ static void heap_delete(_st_thread_t *thread) {
}
t = *p;
*p = NULL;
--_ST_SLEEPQ_SIZE;
--_st_this_vp.sleepq_size;
if (t != thread) {
/*
* Insert the unlinked last element in place of the element we are deleting
@ -503,9 +503,9 @@ static void heap_delete(_st_thread_t *thread) {
void _st_add_sleep_q(_st_thread_t *thread, st_utime_t timeout)
{
thread->due = _ST_LAST_CLOCK + timeout;
thread->due = _st_this_vp.last_clock + timeout;
thread->flags |= _ST_FL_ON_SLEEPQ;
thread->heap_index = ++_ST_SLEEPQ_SIZE;
thread->heap_index = ++_st_this_vp.sleepq_size;
heap_insert(thread);
}
@ -527,9 +527,9 @@ void _st_vp_check_clock(void)
now = st_utime();
#if defined(DEBUG) && defined(DEBUG_STATS)
elapsed = now < _ST_LAST_CLOCK? 0 : now - _ST_LAST_CLOCK; // Might step back.
elapsed = now < _st_this_vp.last_clock? 0 : now - _st_this_vp.last_clock; // Might step back.
#endif
_ST_LAST_CLOCK = now;
_st_this_vp.last_clock = now;
#if defined(DEBUG) && defined(DEBUG_STATS)
if (elapsed <= 10000) {
@ -558,12 +558,12 @@ void _st_vp_check_clock(void)
_st_last_tset = now;
}
while (_ST_SLEEPQ != NULL) {
thread = _ST_SLEEPQ;
while (_st_this_vp.sleep_q != NULL) {
thread = _st_this_vp.sleep_q;
ST_ASSERT(thread->flags & _ST_FL_ON_SLEEPQ);
if (thread->due > now)
break;
_ST_DEL_SLEEPQ(thread);
_st_del_sleep_q(thread);
/* If thread is waiting on condition variable, set the time out flag */
if (thread->state == _ST_ST_COND_WAIT)
@ -573,14 +573,14 @@ void _st_vp_check_clock(void)
ST_ASSERT(!(thread->flags & _ST_FL_IDLE_THREAD));
thread->state = _ST_ST_RUNNABLE;
// Insert at the head of RunQ, to execute timer first.
_ST_INSERT_RUNQ(thread);
st_clist_insert_after(&thread->links, &_st_this_vp.run_q);
}
}
void st_thread_yield()
{
_st_thread_t *me = _ST_CURRENT_THREAD();
_st_thread_t *me = _st_this_thread;
#if defined(DEBUG) && defined(DEBUG_STATS)
++_st_stat_thread_yield;
@ -590,7 +590,7 @@ void st_thread_yield()
_st_vp_check_clock();
// If not thread in RunQ to yield to, ignore and continue to run.
if (_ST_RUNQ.next == &_ST_RUNQ) {
if (_st_this_vp.run_q.next == &_st_this_vp.run_q) {
return;
}
@ -600,10 +600,10 @@ void st_thread_yield()
// Append thread to the tail of RunQ, we will back after all threads executed.
me->state = _ST_ST_RUNNABLE;
_ST_ADD_RUNQ(me);
st_clist_insert_before(&me->links, &_st_this_vp.run_q);
// Yield to other threads in the RunQ.
_ST_SWITCH_CONTEXT(me);
_st_switch_context(me);
}
@ -619,11 +619,11 @@ void st_thread_interrupt(_st_thread_t *thread)
return;
if (thread->flags & _ST_FL_ON_SLEEPQ)
_ST_DEL_SLEEPQ(thread);
_st_del_sleep_q(thread);
/* Make thread runnable */
thread->state = _ST_ST_RUNNABLE;
_ST_ADD_RUNQ(thread);
st_clist_insert_before(&thread->links, &_st_this_vp.run_q);
}
@ -637,7 +637,7 @@ _st_thread_t *st_thread_create(void *(*start)(void *arg), void *arg, int joinabl
/* Adjust stack size */
if (stk_size == 0)
stk_size = ST_DEFAULT_STACK_SIZE;
stk_size = ((stk_size + _ST_PAGE_SIZE - 1) / _ST_PAGE_SIZE) * _ST_PAGE_SIZE;
stk_size = ((stk_size + _st_this_vp.pagesize - 1) / _st_this_vp.pagesize) * _st_this_vp.pagesize;
stack = _st_stack_new(stk_size);
if (!stack)
return NULL;
@ -663,7 +663,10 @@ _st_thread_t *st_thread_create(void *(*start)(void *arg), void *arg, int joinabl
thread->start = start;
thread->arg = arg;
_ST_INIT_CONTEXT(thread, stack->sp, _st_thread_main);
/* Note that we must directly call rather than call any functions. */
if (_st_md_cxt_save(thread->context))
_st_thread_main();
MD_GET_SP(thread) = (long)(stack->sp);
/* If thread is joinable, allocate a termination condition variable */
if (joinable) {
@ -677,13 +680,13 @@ _st_thread_t *st_thread_create(void *(*start)(void *arg), void *arg, int joinabl
/* Make thread runnable */
thread->state = _ST_ST_RUNNABLE;
_st_active_count++;
_ST_ADD_RUNQ(thread);
st_clist_insert_before(&thread->links, &_st_this_vp.run_q);
#ifdef DEBUG
_ST_ADD_THREADQ(thread);
st_clist_insert_before(&thread->tlink, &_st_this_vp.thread_q);
#endif
/* merge from https://github.com/toffaletti/state-threads/commit/7f57fc9acc05e657bca1223f1e5b9b1a45ed929b */
#ifndef NVALGRIND
#ifdef MD_VALGRIND
if (!(thread->flags & _ST_FL_PRIMORDIAL)) {
thread->stack->valgrind_stack_id = VALGRIND_STACK_REGISTER(thread->stack->stk_top, thread->stack->stk_bottom);
}
@ -695,7 +698,7 @@ _st_thread_t *st_thread_create(void *(*start)(void *arg), void *arg, int joinabl
_st_thread_t *st_thread_self(void)
{
return _ST_CURRENT_THREAD();
return _st_this_thread;
}
#ifdef DEBUG
@ -717,7 +720,7 @@ void _st_iterate_threads(void)
if (!_st_iterate_threads_flag) {
if (thread) {
memcpy(thread->context, save_jb, sizeof(_st_jmp_buf_t));
MD_LONGJMP(orig_jb, 1);
_st_md_cxt_restore(orig_jb, 1);
}
return;
}
@ -726,25 +729,25 @@ void _st_iterate_threads(void)
memcpy(thread->context, save_jb, sizeof(_st_jmp_buf_t));
_st_show_thread_stack(thread, NULL);
} else {
if (MD_SETJMP(orig_jb)) {
if (_st_md_cxt_save(orig_jb)) {
_st_iterate_threads_flag = 0;
thread = NULL;
_st_show_thread_stack(thread, "Iteration completed");
return;
}
thread = _ST_CURRENT_THREAD();
thread = _st_this_thread;
_st_show_thread_stack(thread, "Iteration started");
}
q = thread->tlink.next;
if (q == &_ST_THREADQ)
if (q == &_st_this_vp.thread_q)
q = q->next;
ST_ASSERT(q != &_ST_THREADQ);
ST_ASSERT(q != &_st_this_vp.thread_q);
thread = _ST_THREAD_THREADQ_PTR(q);
if (thread == _ST_CURRENT_THREAD())
MD_LONGJMP(orig_jb, 1);
if (thread == _st_this_thread)
_st_md_cxt_restore(orig_jb, 1);
memcpy(save_jb, thread->context, sizeof(_st_jmp_buf_t));
MD_LONGJMP(thread->context, 1);
_st_md_cxt_restore(thread->context, 1);
}
#endif /* DEBUG */

@ -50,7 +50,7 @@
/* How much space to leave between the stacks, at each end */
#define REDZONE _ST_PAGE_SIZE
#define REDZONE _st_this_vp.pagesize
__thread _st_clist_t _st_free_stacks;
__thread int _st_num_free_stacks = 0;
@ -71,7 +71,7 @@ _st_stack_t *_st_stack_new(int stack_size)
ts = _ST_THREAD_STACK_PTR(qp);
if (ts->stk_size >= stack_size) {
/* Found a stack that is big enough */
ST_REMOVE_LINK(&ts->links);
st_clist_remove(&ts->links);
_st_num_free_stacks--;
ts->links.next = NULL;
ts->links.prev = NULL;
@ -80,7 +80,7 @@ _st_stack_t *_st_stack_new(int stack_size)
}
#endif
extra = _st_randomize_stacks ? _ST_PAGE_SIZE : 0;
extra = _st_randomize_stacks ? _st_this_vp.pagesize : 0;
/* If not cache stack, we will free all stack in the list, which contains the stack to be freed.
* Note that we should never directly free it at _st_stack_free, because it is still be used,
* and will cause crash. */
@ -90,7 +90,7 @@ _st_stack_t *_st_stack_new(int stack_size)
/* Before qp is freed, move to next one, because the qp will be freed when free the ts. */
qp = qp->next;
ST_REMOVE_LINK(&ts->links);
st_clist_remove(&ts->links);
_st_num_free_stacks--;
#if defined(DEBUG) && !defined(MD_NO_PROTECT)
@ -142,7 +142,7 @@ void _st_stack_free(_st_stack_t *ts)
return;
/* Put the stack on the free list */
ST_APPEND_LINK(&ts->links, _st_free_stacks.prev);
st_clist_insert_before(&ts->links, _st_free_stacks.prev);
_st_num_free_stacks++;
}

@ -87,7 +87,7 @@ int st_set_utime_function(st_utime_t (*func)(void))
st_utime_t st_utime_last_clock(void)
{
return _ST_LAST_CLOCK;
return _st_this_vp.last_clock;
}
@ -116,7 +116,7 @@ time_t st_time(void)
int st_usleep(st_utime_t usecs)
{
_st_thread_t *me = _ST_CURRENT_THREAD();
_st_thread_t *me = _st_this_thread;
if (me->flags & _ST_FL_INTERRUPT) {
me->flags &= ~_ST_FL_INTERRUPT;
@ -126,11 +126,11 @@ int st_usleep(st_utime_t usecs)
if (usecs != ST_UTIME_NO_TIMEOUT) {
me->state = _ST_ST_SLEEPING;
_ST_ADD_SLEEPQ(me, usecs);
_st_add_sleep_q(me, usecs);
} else
me->state = _ST_ST_SUSPENDED;
_ST_SWITCH_CONTEXT(me);
_st_switch_context(me);
if (me->flags & _ST_FL_INTERRUPT) {
me->flags &= ~_ST_FL_INTERRUPT;
@ -158,7 +158,7 @@ _st_cond_t *st_cond_new(void)
cvar = (_st_cond_t *) calloc(1, sizeof(_st_cond_t));
if (cvar) {
ST_INIT_CLIST(&cvar->wait_q);
st_clist_init(&cvar->wait_q);
}
return cvar;
@ -180,7 +180,7 @@ int st_cond_destroy(_st_cond_t *cvar)
int st_cond_timedwait(_st_cond_t *cvar, st_utime_t timeout)
{
_st_thread_t *me = _ST_CURRENT_THREAD();
_st_thread_t *me = _st_this_thread;
int rv;
if (me->flags & _ST_FL_INTERRUPT) {
@ -191,14 +191,14 @@ int st_cond_timedwait(_st_cond_t *cvar, st_utime_t timeout)
/* Put caller thread on the condition variable's wait queue */
me->state = _ST_ST_COND_WAIT;
ST_APPEND_LINK(&me->wait_links, &cvar->wait_q);
st_clist_insert_before(&me->wait_links, &cvar->wait_q);
if (timeout != ST_UTIME_NO_TIMEOUT)
_ST_ADD_SLEEPQ(me, timeout);
_st_add_sleep_q(me, timeout);
_ST_SWITCH_CONTEXT(me);
_st_switch_context(me);
ST_REMOVE_LINK(&me->wait_links);
st_clist_remove(&me->wait_links);
rv = 0;
if (me->flags & _ST_FL_TIMEDOUT) {
@ -231,11 +231,11 @@ static int _st_cond_signal(_st_cond_t *cvar, int broadcast)
thread = _ST_THREAD_WAITQ_PTR(q);
if (thread->state == _ST_ST_COND_WAIT) {
if (thread->flags & _ST_FL_ON_SLEEPQ)
_ST_DEL_SLEEPQ(thread);
_st_del_sleep_q(thread);
/* Make thread runnable */
thread->state = _ST_ST_RUNNABLE;
_ST_ADD_RUNQ(thread);
st_clist_insert_before(&thread->links, &_st_this_vp.run_q);
if (!broadcast)
break;
}
@ -267,7 +267,7 @@ _st_mutex_t *st_mutex_new(void)
lock = (_st_mutex_t *) calloc(1, sizeof(_st_mutex_t));
if (lock) {
ST_INIT_CLIST(&lock->wait_q);
st_clist_init(&lock->wait_q);
lock->owner = NULL;
}
@ -290,7 +290,7 @@ int st_mutex_destroy(_st_mutex_t *lock)
int st_mutex_lock(_st_mutex_t *lock)
{
_st_thread_t *me = _ST_CURRENT_THREAD();
_st_thread_t *me = _st_this_thread;
if (me->flags & _ST_FL_INTERRUPT) {
me->flags &= ~_ST_FL_INTERRUPT;
@ -311,11 +311,11 @@ int st_mutex_lock(_st_mutex_t *lock)
/* Put caller thread on the mutex's wait queue */
me->state = _ST_ST_LOCK_WAIT;
ST_APPEND_LINK(&me->wait_links, &lock->wait_q);
st_clist_insert_before(&me->wait_links, &lock->wait_q);
_ST_SWITCH_CONTEXT(me);
_st_switch_context(me);
ST_REMOVE_LINK(&me->wait_links);
st_clist_remove(&me->wait_links);
if ((me->flags & _ST_FL_INTERRUPT) && lock->owner != me) {
me->flags &= ~_ST_FL_INTERRUPT;
@ -332,7 +332,7 @@ int st_mutex_unlock(_st_mutex_t *lock)
_st_thread_t *thread;
_st_clist_t *q;
if (lock->owner != _ST_CURRENT_THREAD()) {
if (lock->owner != _st_this_thread) {
errno = EPERM;
return -1;
}
@ -343,7 +343,7 @@ int st_mutex_unlock(_st_mutex_t *lock)
lock->owner = thread;
/* Make thread runnable */
thread->state = _ST_ST_RUNNABLE;
_ST_ADD_RUNQ(thread);
st_clist_insert_before(&thread->links, &_st_this_vp.run_q);
return 0;
}
}
@ -363,7 +363,7 @@ int st_mutex_trylock(_st_mutex_t *lock)
}
/* Got the mutex */
lock->owner = _ST_CURRENT_THREAD();
lock->owner = _st_this_thread;
return 0;
}

6
trunk/configure vendored

@ -128,7 +128,7 @@ fi
# Start to generate the Makefile.
cat << END >> ${SRS_OBJS}/Makefile
GCC = ${SRS_TOOL_CC}
CC = ${SRS_TOOL_CC}
CXX = ${SRS_TOOL_CXX}
AR = ${SRS_TOOL_AR}
LINK = ${SRS_TOOL_CXX}
@ -514,7 +514,7 @@ cat << END > ${SRS_MAKEFILE}
.PHONY: clean_srs clean_modules clean_openssl clean_srtp2 clean_opus clean_ffmpeg clean_st
.PHONY: st ffmpeg
GCC = ${SRS_TOOL_CC}
CC = ${SRS_TOOL_CC}
CXX = ${SRS_TOOL_CXX}
AR = ${SRS_TOOL_AR}
LINK = ${SRS_TOOL_LD}
@ -606,7 +606,7 @@ clean_st:
st:
@rm -f ${SRS_OBJS}/srs srs_utest
@\$(MAKE)\$(JOBS) -C ${SRS_OBJS}/${SRS_PLATFORM}/st-srs clean
@env EXTRA_CFLAGS="${_ST_EXTRA_CFLAGS}" \$(MAKE)\$(JOBS) -C ${SRS_OBJS}/${SRS_PLATFORM}/st-srs ${_ST_MAKE_ARGS} CC=\$(GCC) AR=\$(AR) LD=\$(LINK) RANDLIB=\$(RANDLIB)
@env EXTRA_CFLAGS="${_ST_EXTRA_CFLAGS}" \$(MAKE)\$(JOBS) -C ${SRS_OBJS}/${SRS_PLATFORM}/st-srs ${_ST_MAKE_ARGS} CC=\$(CC) CXX=\$(CXX) AR=\$(AR) LD=\$(LINK) RANDLIB=\$(RANDLIB)
@echo "Please rebuild srs by: make"
ffmpeg:

@ -7,6 +7,7 @@ The changelog for SRS.
<a name="v7-changes"></a>
## SRS 7.0 Changelog
* v7.0, 2024-08-21, Merge [#4149](https://github.com/ossrs/srs/pull/4149): ST: Replace macros with explicit code for better understanding. v7.0.7 (#4149)
* v7.0, 2024-08-21, Merge [#4150](https://github.com/ossrs/srs/pull/4150): API: Support new HTTP API for VALGRIND. v7.0.6 (#4150)
* v7.0, 2024-08-15, Merge [#4144](https://github.com/ossrs/srs/pull/4144): HTTP-FLV: Crash when multiple viewers. v7.0.5 (#4144)
* v7.0, 2024-08-15, Merge [#4142](https://github.com/ossrs/srs/pull/4142): Config: Add more utest for env config. v7.0.4 (#4142)

@ -3,3 +3,10 @@ udp-client
cost
cost.log
thread-join
st-cond
hello-st
hello-world
huge-threads
exceptions
hello
pthreads

@ -0,0 +1,26 @@
/*
g++ st-cond.cpp ../../objs/st/libst.a -g -O0 -o st-cond && ./st-cond
*/
#include <stdio.h>
#include "../../objs/st/st.h"
st_cond_t lock;
void* foo(void*) {
st_cond_wait(lock);
printf("Hello World, ST!\n");
return NULL;
}
int main() {
st_init();
lock = st_cond_new();
st_thread_create(foo, NULL, 0, 0);
st_sleep(1);
st_cond_signal(lock);
st_sleep(1);
st_cond_destroy(lock);
return 0;
}

@ -9,6 +9,6 @@
#define VERSION_MAJOR 7
#define VERSION_MINOR 0
#define VERSION_REVISION 6
#define VERSION_REVISION 7
#endif
Loading…
Cancel
Save