/* SPDX-License-Identifier: MPL-1.1 OR GPL-2.0-or-later */ /* * The contents of this file are subject to the Mozilla Public * License Version 1.1 (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.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or * implied. See the License for the specific language governing * rights and limitations under the License. * * The Original Code is the Netscape Portable Runtime library. * * The Initial Developer of the Original Code is Netscape * Communications Corporation. Portions created by Netscape are * Copyright (C) 1994-2000 Netscape Communications Corporation. All * Rights Reserved. * * Contributor(s): Silicon Graphics, Inc. * * Portions created by SGI are Copyright (C) 2000-2001 Silicon * Graphics, Inc. All Rights Reserved. * * Alternatively, the contents of this file may be used under the * terms of the GNU General Public License Version 2 or later (the * "GPL"), in which case the provisions of the GPL are applicable * instead of those above. If you wish to allow use of your * version of this file only under the terms of the GPL and not to * allow others to use your version of this file under the MPL, * indicate your decision by deleting the provisions above and * replace them with the notice and other provisions required by * the GPL. If you do not delete the provisions above, a recipient * may use your version of this file under either the MPL or the * GPL. */ /* * This file is derived directly from Netscape Communications Corporation, * and consists of extensive modifications made during the year(s) 1999-2000. */ #ifndef __ST_COMMON_H__ #define __ST_COMMON_H__ #include #include #include #include #include /* Enable assertions only if DEBUG is defined */ #ifndef DEBUG #define NDEBUG #endif #include #define ST_ASSERT(expr) assert(expr) #define ST_BEGIN_MACRO { #define ST_END_MACRO } #ifdef DEBUG #define ST_HIDDEN /*nothing*/ #else #define ST_HIDDEN static #endif #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 #endif /***************************************** * Circular linked list definitions */ typedef struct _st_clist { struct _st_clist *next; 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 /* 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)) /* Initialize a circular list */ #define ST_INIT_CLIST(_l) \ ST_BEGIN_MACRO \ (_l)->next = (_l); \ (_l)->prev = (_l); \ ST_END_MACRO #define ST_INIT_STATIC_CLIST(_l) \ {(_l), (_l)} /***************************************** * Basic types definitions */ typedef void (*_st_destructor_t)(void *); typedef struct _st_stack { _st_clist_t links; char *vaddr; /* Base of stack's allocated memory */ int vaddr_size; /* Size of stack's allocated memory */ int stk_size; /* Size of usable portion of the stack */ char *stk_bottom; /* Lowest address of stack's usable portion */ 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 /* id returned by VALGRIND_STACK_REGISTER */ /* http://valgrind.org/docs/manual/manual-core-adv.html */ unsigned long valgrind_stack_id; #endif } _st_stack_t; typedef struct _st_cond { _st_clist_t wait_q; /* Condition variable wait queue */ } _st_cond_t; typedef struct _st_thread _st_thread_t; struct _st_thread { int state; /* Thread's state */ int flags; /* Thread's flags */ void *(*start)(void *arg); /* The start function of the thread */ void *arg; /* Argument of the start function */ void *retval; /* Return value of the start function */ _st_stack_t *stack; /* Info about thread's stack */ _st_clist_t links; /* For putting on run/sleep/zombie queue */ _st_clist_t wait_links; /* For putting on mutex/condvar wait queue */ #ifdef DEBUG _st_clist_t tlink; /* For putting on thread queue */ #endif st_utime_t due; /* Wakeup time when thread is sleeping */ _st_thread_t *left; /* For putting in timeout heap */ _st_thread_t *right; /* -- see docs/timeout_heap.txt for details */ int heap_index; void **private_data; /* Per thread private data */ _st_cond_t *term; /* Termination condition variable for join */ _st_jmp_buf_t context; /* Thread's context */ }; typedef struct _st_mutex { _st_thread_t *owner; /* Current mutex owner */ _st_clist_t wait_q; /* Mutex wait queue */ } _st_mutex_t; typedef struct _st_pollq { _st_clist_t links; /* For putting on io queue */ _st_thread_t *thread; /* Polling thread */ struct pollfd *pds; /* Array of poll descriptors */ int npds; /* Length of the array */ int on_ioq; /* Is it on ioq? */ } _st_pollq_t; typedef struct _st_eventsys_ops { const char *name; /* Name of this event system */ int val; /* Type of this event system */ int (*init)(void); /* Initialization */ void (*dispatch)(void); /* Dispatch function */ int (*pollset_add)(struct pollfd *, int); /* Add descriptor set */ void (*pollset_del)(struct pollfd *, int); /* Delete descriptor set */ int (*fd_new)(int); /* New descriptor allocated */ int (*fd_close)(int); /* Descriptor closed */ int (*fd_getlimit)(void); /* Descriptor hard limit */ void (*destroy)(void); /* Destroy the event object */ } _st_eventsys_t; typedef struct _st_vp { _st_thread_t *idle_thread; /* Idle thread for this vp */ st_utime_t last_clock; /* The last time we went into vp_check_clock() */ _st_clist_t run_q; /* run queue for this vp */ _st_clist_t io_q; /* io queue for this vp */ _st_clist_t zombie_q; /* zombie queue for this vp */ #ifdef DEBUG _st_clist_t thread_q; /* all threads of this vp */ #endif int pagesize; _st_thread_t *sleep_q; /* sleep queue for this vp */ int sleepq_size; /* number of threads on sleep queue */ #ifdef ST_SWITCH_CB st_switch_cb_t switch_out_cb; /* called when a thread is switched out */ st_switch_cb_t switch_in_cb; /* called when a thread is switched in */ #endif } _st_vp_t; typedef struct _st_netfd { int osfd; /* Underlying OS file descriptor */ int inuse; /* In-use flag */ void *private_data; /* Per descriptor private data */ _st_destructor_t destructor; /* Private data destructor function */ void *aux_data; /* Auxiliary data for internal use */ struct _st_netfd *next; /* For putting on the free list */ } _st_netfd_t; /***************************************** * Current vp, thread, and event system */ 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 */ #define _ST_ST_RUNNING 0 #define _ST_ST_RUNNABLE 1 #define _ST_ST_IO_WAIT 2 #define _ST_ST_LOCK_WAIT 3 #define _ST_ST_COND_WAIT 4 #define _ST_ST_SLEEPING 5 #define _ST_ST_ZOMBIE 6 #define _ST_ST_SUSPENDED 7 #define _ST_FL_PRIMORDIAL 0x01 #define _ST_FL_IDLE_THREAD 0x02 #define _ST_FL_ON_SLEEPQ 0x04 #define _ST_FL_INTERRUPT 0x08 #define _ST_FL_TIMEDOUT 0x10 /***************************************** * Pointer conversion */ #ifndef offsetof #define offsetof(type, identifier) ((size_t)&(((type *)0)->identifier)) #endif #define _ST_THREAD_PTR(_qp) \ ((_st_thread_t *)((char *)(_qp) - offsetof(_st_thread_t, links))) #define _ST_THREAD_WAITQ_PTR(_qp) \ ((_st_thread_t *)((char *)(_qp) - offsetof(_st_thread_t, wait_links))) #define _ST_THREAD_STACK_PTR(_qp) \ ((_st_stack_t *)((char*)(_qp) - offsetof(_st_stack_t, links))) #define _ST_POLLQUEUE_PTR(_qp) \ ((_st_pollq_t *)((char *)(_qp) - offsetof(_st_pollq_t, links))) #ifdef DEBUG #define _ST_THREAD_THREADQ_PTR(_qp) \ ((_st_thread_t *)((char *)(_qp) - offsetof(_st_thread_t, tlink))) #endif /***************************************** * Constants */ #ifndef ST_UTIME_NO_TIMEOUT #define ST_UTIME_NO_TIMEOUT ((st_utime_t) -1LL) #endif #define ST_DEFAULT_STACK_SIZE (128*1024) /* Includes register stack size */ #ifndef ST_KEYS_MAX #define ST_KEYS_MAX 16 #endif #ifndef ST_MIN_POLLFDS_SIZE #define ST_MIN_POLLFDS_SIZE 64 #endif /***************************************** * Threads context switching */ #ifdef DEBUG void _st_iterate_threads(void); #define ST_DEBUG_ITERATE_THREADS() _st_iterate_threads() #else #define ST_DEBUG_ITERATE_THREADS() #endif #ifdef ST_SWITCH_CB #define ST_SWITCH_OUT_CB(_thread) \ if (_st_this_vp.switch_out_cb != NULL && \ _thread != _st_this_vp.idle_thread && \ _thread->state != _ST_ST_ZOMBIE) { \ _st_this_vp.switch_out_cb(); \ } #define ST_SWITCH_IN_CB(_thread) \ if (_st_this_vp.switch_in_cb != NULL && \ _thread != _st_this_vp.idle_thread && \ _thread->state != _ST_ST_ZOMBIE) { \ _st_this_vp.switch_in_cb(); \ } #else #define ST_SWITCH_OUT_CB(_thread) #define ST_SWITCH_IN_CB(_thread) #endif /* * 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 /* * 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 /* * Number of bytes reserved under the stack "bottom" */ #define _ST_STACK_PAD_SIZE MD_STACK_PAD_SIZE /***************************************** * Forward declarations */ void _st_vp_schedule(void); void _st_vp_check_clock(void); void *_st_idle_thread_start(void *arg); void _st_thread_main(void); void _st_thread_cleanup(_st_thread_t *thread); void _st_add_sleep_q(_st_thread_t *thread, st_utime_t timeout); void _st_del_sleep_q(_st_thread_t *thread); _st_stack_t *_st_stack_new(int stack_size); void _st_stack_free(_st_stack_t *ts); int _st_io_init(void); st_utime_t st_utime(void); _st_cond_t *st_cond_new(void); int st_cond_destroy(_st_cond_t *cvar); int st_cond_timedwait(_st_cond_t *cvar, st_utime_t timeout); int st_cond_signal(_st_cond_t *cvar); ssize_t st_read(_st_netfd_t *fd, void *buf, size_t nbyte, st_utime_t timeout); ssize_t st_write(_st_netfd_t *fd, const void *buf, size_t nbyte, st_utime_t timeout); 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); #endif /* !__ST_COMMON_H__ */