123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339 |
- /* Declarations for the threading interface.
- This file is part of khipu.
- khipu is free software: you can redistribute it and/or modify
- it under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 3 of the License, or
- (at your option) any later version.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Lesser General Public License for more details.
- You should have received a copy of the GNU Lesser General Public License
- along with this program. If not, see <https://www.gnu.org/licenses/>. */
- #ifndef __KP_THREAD__
- #define __KP_THREAD__ 1
- #include "interp.hpp"
- #include "initop.hpp"
- #include "custom.hpp"
- #include "utils/lazy.hpp"
- #include "sysdeps/atomic.hpp"
- #ifdef KP_PLATFORM_UNIX
- #include <pthread.h>
- #endif
- KP_DECLS_BEGIN
- // Lightweight locks for internal use.
- // Initialize lightweight lock LOCKP.
- inline void lwlock_init (atomic_t *lockp)
- {
- *lockp = 0;
- }
- // Grab the lightweight lock LOCKP.
- extern result<void> lwlock_grab (interpreter *interp, atomic_t *lockp);
- // Grab the lightweight lock LOCKP, waiting up to TIMEOUT.
- extern result<bool> lwlock_grab (interpreter *interp,
- atomic_t *lockp, double timeout);
- // Same as above, only this doesn't check for interrupts when sleeping.
- extern void lwlock_grab_nointr (interpreter *interp, atomic_t *lockp);
- extern bool lwlock_grab_nointr (interpreter *interp,
- atomic_t *lockp, double timeout);
- // Drop the lightweight lock LOCKP.
- extern void lwlock_drop (atomic_t *lockp);
- // Try to grab the lightweight lock LOCKP.
- inline bool lwlock_trygrab (atomic_t *lockp)
- {
- return (atomic_cas_bool (lockp, 0, 1));
- }
- struct lwlock_guard
- {
- atomic_t *lockp;
- lwlock_guard () : lockp (nullptr)
- {
- }
- static result<lwlock_guard>
- make (interpreter *interp, atomic_t *lp)
- {
- lwlock_guard ret;
- KP_VTRY (ret.set (interp, lp));
- return (ret);
- }
- result<int> set (interpreter *interp, atomic_t *lp)
- {
- if (this->lockp)
- lwlock_drop (this->lockp);
- if ((this->lockp = lp) != nullptr)
- KP_VTRY (lwlock_grab (interp, this->lockp));
- return (0);
- }
- ~lwlock_guard ()
- {
- if (this->lockp)
- lwlock_drop (this->lockp);
- }
- };
- #ifdef KP_NO_THREADS
- struct lock : public custom<lock>
- {
- static const uint32_t recursive_flag = 1u << 16;
- void init (bool)
- {
- }
- result<int> grap (interpreter *);
- result<int> grab (interpreter *, object, bool);
- int trygrab (interpreter *);
- int drop (interpreter *);
- void force_release (interpreter *);
- };
- #else
- struct raw_condvar
- {
- atomic_t *lockp;
- atomic_t seq;
- void init ()
- {
- this->lockp = nullptr;
- this->seq = 0;
- }
- result<int> wait (interpreter *interp, atomic_t *lk,
- double *timeout = nullptr);
- int wait_nointr (interpreter *interp, atomic_t *lk,
- double *timeout = nullptr);
- void wake (bool wake_all);
- };
- struct lock : public custom<lock>
- {
- static const uint32_t recursive_flag = 1 << 16;
- atomic_t word;
- lazy<valref> ref;
- object owner;
- uint32_t lock_cnt;
- void do_visit (interpreter *interp, visitor& vs)
- {
- if (this->owner != UNBOUND)
- vs (interp, this->owner);
- }
- void init (bool recursive_p);
- result<int> grab (interpreter *interp);
- result<int> grab (interpreter *interp, object timeout, bool absolute);
- int trygrab (interpreter *interp);
- int drop (interpreter *interp);
- void force_release (interpreter *interp);
- };
- struct condvar : public custom<condvar>
- {
- raw_condvar base;
- result<int> wait (interpreter *interp, object lk);
- result<int> wait (interpreter *interp, object lk, object timeout, bool abs);
- void wake (interpreter *, bool wake_all)
- {
- this->base.wake (wake_all);
- }
- };
- #ifdef KP_PLATFORM_UNIX
- typedef pthread_t system_thread_t;
- #else
- typedef void* system_thread_t;
- #endif
- struct sleepq;
- struct thread : public varobj
- {
- static const int code = typecode::THREAD;
- atomic_t join_ev;
- system_thread_t handle;
- object retval;
- atomic_t ilock;
- interpreter *interp;
- dlist thr_link;
- #if !defined (KP_PLATFORM_LINUX)
- sleepq *sleep_q;
- #endif
- };
- struct sync_event
- {
- atomic_t lock;
- raw_condvar cv;
- uintptr_t waiters_sig;
- uintptr_t n_waiters;
- uintptr_t limit;
- void init ()
- {
- this->lock = 0;
- this->cv.init ();
- this->reset ();
- }
- void reset ()
- {
- this->waiters_sig = this->n_waiters = this->limit = 0;
- }
- void wait (interpreter *interp);
- void wake_all (interpreter *interp);
- };
- #endif
- struct lock_guard
- {
- interpreter *interp;
- lock *lockp;
- lock_guard () : interp (nullptr), lockp (nullptr)
- {
- }
- lock_guard (interpreter *ip, lock *lp) : interp (ip), lockp (lp)
- {
- }
- static result<lock_guard>
- make (interpreter *interp, lock *lp = nullptr)
- {
- lock_guard ret (interp, lp);
- if (lp)
- KP_VTRY (lp->grab (interp));
- return (ret);
- }
- result<int> set (lock *lp)
- {
- if (this->lockp)
- this->lockp->drop (this->interp);
- if ((this->lockp = lp) != nullptr)
- KP_VTRY (this->lockp->grab (this->interp));
- return (0);
- }
- ~lock_guard ()
- {
- if (this->lockp)
- this->lockp->drop (this->interp);
- }
- };
- inline lock* as_lock (object obj)
- {
- return ((lock *)as_custom (obj));
- }
- inline bool lock_p (object obj)
- {
- return (lock::type_p (obj));
- }
- KP_EXPORT result<object> alloc_lock (interpreter *interp, bool recursive_p);
- inline condvar* as_condvar (object obj)
- {
- return ((condvar *)as_custom (obj));
- }
- inline bool condvar_p (object obj)
- {
- return (condvar::type_p (obj));
- }
- KP_EXPORT result<object> alloc_condvar (interpreter *interp);
- inline thread* as_thread (object obj)
- {
- return ((thread *)unmask (obj));
- }
- inline bool thread_p (object obj)
- {
- return (itype (obj) == typecode::THREAD);
- }
- KP_EXPORT dlist all_threads;
- KP_EXPORT atomic_t all_threads_lock;
- KP_EXPORT uint32_t num_threads;
- // Return values for some thread calls.
- enum
- {
- THR_ERROR = -1,
- THR_TIMEOUT = -2,
- THR_DEADLK = -3,
- THR_PERM = -4,
- THR_INTR = -5
- };
- /* Test if only one thread is running. This test should be as cheap as
- * possible, in order to avoid locking when a thread knows it's alone. */
- #ifdef KP_NO_THREADS
- inline constexpr bool singlethr_p ()
- {
- return (true);
- }
- # define KP_MT_BEGIN(expr) (void)0
- # define KP_MT_END(expr) (void)0
- #else
- inline bool singlethr_p ()
- {
- return (num_threads <= 1);
- }
- # define KP_MT_BEGIN(expr) \
- bool __tst = singlethr_p (); \
- if (!__tst) expr
- # define KP_MT_END(expr) \
- if (!__tst) expr
- #endif
- // Init OP for the threading subsystem.
- KP_EXPORT init_op init_threads;
- KP_DECLS_END
- #endif
|