thread.hpp 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339
  1. /* Declarations for the threading interface.
  2. This file is part of khipu.
  3. khipu is free software: you can redistribute it and/or modify
  4. it under the terms of the GNU Lesser General Public License as published by
  5. the Free Software Foundation; either version 3 of the License, or
  6. (at your option) any later version.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU Lesser General Public License for more details.
  11. You should have received a copy of the GNU Lesser General Public License
  12. along with this program. If not, see <https://www.gnu.org/licenses/>. */
  13. #ifndef __KP_THREAD__
  14. #define __KP_THREAD__ 1
  15. #include "interp.hpp"
  16. #include "initop.hpp"
  17. #include "custom.hpp"
  18. #include "utils/lazy.hpp"
  19. #include "sysdeps/atomic.hpp"
  20. #ifdef KP_PLATFORM_UNIX
  21. #include <pthread.h>
  22. #endif
  23. KP_DECLS_BEGIN
  24. // Lightweight locks for internal use.
  25. // Initialize lightweight lock LOCKP.
  26. inline void lwlock_init (atomic_t *lockp)
  27. {
  28. *lockp = 0;
  29. }
  30. // Grab the lightweight lock LOCKP.
  31. extern result<void> lwlock_grab (interpreter *interp, atomic_t *lockp);
  32. // Grab the lightweight lock LOCKP, waiting up to TIMEOUT.
  33. extern result<bool> lwlock_grab (interpreter *interp,
  34. atomic_t *lockp, double timeout);
  35. // Same as above, only this doesn't check for interrupts when sleeping.
  36. extern void lwlock_grab_nointr (interpreter *interp, atomic_t *lockp);
  37. extern bool lwlock_grab_nointr (interpreter *interp,
  38. atomic_t *lockp, double timeout);
  39. // Drop the lightweight lock LOCKP.
  40. extern void lwlock_drop (atomic_t *lockp);
  41. // Try to grab the lightweight lock LOCKP.
  42. inline bool lwlock_trygrab (atomic_t *lockp)
  43. {
  44. return (atomic_cas_bool (lockp, 0, 1));
  45. }
  46. struct lwlock_guard
  47. {
  48. atomic_t *lockp;
  49. lwlock_guard () : lockp (nullptr)
  50. {
  51. }
  52. static result<lwlock_guard>
  53. make (interpreter *interp, atomic_t *lp)
  54. {
  55. lwlock_guard ret;
  56. KP_VTRY (ret.set (interp, lp));
  57. return (ret);
  58. }
  59. result<int> set (interpreter *interp, atomic_t *lp)
  60. {
  61. if (this->lockp)
  62. lwlock_drop (this->lockp);
  63. if ((this->lockp = lp) != nullptr)
  64. KP_VTRY (lwlock_grab (interp, this->lockp));
  65. return (0);
  66. }
  67. ~lwlock_guard ()
  68. {
  69. if (this->lockp)
  70. lwlock_drop (this->lockp);
  71. }
  72. };
  73. #ifdef KP_NO_THREADS
  74. struct lock : public custom<lock>
  75. {
  76. static const uint32_t recursive_flag = 1u << 16;
  77. void init (bool)
  78. {
  79. }
  80. result<int> grap (interpreter *);
  81. result<int> grab (interpreter *, object, bool);
  82. int trygrab (interpreter *);
  83. int drop (interpreter *);
  84. void force_release (interpreter *);
  85. };
  86. #else
  87. struct raw_condvar
  88. {
  89. atomic_t *lockp;
  90. atomic_t seq;
  91. void init ()
  92. {
  93. this->lockp = nullptr;
  94. this->seq = 0;
  95. }
  96. result<int> wait (interpreter *interp, atomic_t *lk,
  97. double *timeout = nullptr);
  98. int wait_nointr (interpreter *interp, atomic_t *lk,
  99. double *timeout = nullptr);
  100. void wake (bool wake_all);
  101. };
  102. struct lock : public custom<lock>
  103. {
  104. static const uint32_t recursive_flag = 1 << 16;
  105. atomic_t word;
  106. lazy<valref> ref;
  107. object owner;
  108. uint32_t lock_cnt;
  109. void do_visit (interpreter *interp, visitor& vs)
  110. {
  111. if (this->owner != UNBOUND)
  112. vs (interp, this->owner);
  113. }
  114. void init (bool recursive_p);
  115. result<int> grab (interpreter *interp);
  116. result<int> grab (interpreter *interp, object timeout, bool absolute);
  117. int trygrab (interpreter *interp);
  118. int drop (interpreter *interp);
  119. void force_release (interpreter *interp);
  120. };
  121. struct condvar : public custom<condvar>
  122. {
  123. raw_condvar base;
  124. result<int> wait (interpreter *interp, object lk);
  125. result<int> wait (interpreter *interp, object lk, object timeout, bool abs);
  126. void wake (interpreter *, bool wake_all)
  127. {
  128. this->base.wake (wake_all);
  129. }
  130. };
  131. #ifdef KP_PLATFORM_UNIX
  132. typedef pthread_t system_thread_t;
  133. #else
  134. typedef void* system_thread_t;
  135. #endif
  136. struct sleepq;
  137. struct thread : public varobj
  138. {
  139. static const int code = typecode::THREAD;
  140. atomic_t join_ev;
  141. system_thread_t handle;
  142. object retval;
  143. atomic_t ilock;
  144. interpreter *interp;
  145. dlist thr_link;
  146. #if !defined (KP_PLATFORM_LINUX)
  147. sleepq *sleep_q;
  148. #endif
  149. };
  150. struct sync_event
  151. {
  152. atomic_t lock;
  153. raw_condvar cv;
  154. uintptr_t waiters_sig;
  155. uintptr_t n_waiters;
  156. uintptr_t limit;
  157. void init ()
  158. {
  159. this->lock = 0;
  160. this->cv.init ();
  161. this->reset ();
  162. }
  163. void reset ()
  164. {
  165. this->waiters_sig = this->n_waiters = this->limit = 0;
  166. }
  167. void wait (interpreter *interp);
  168. void wake_all (interpreter *interp);
  169. };
  170. #endif
  171. struct lock_guard
  172. {
  173. interpreter *interp;
  174. lock *lockp;
  175. lock_guard () : interp (nullptr), lockp (nullptr)
  176. {
  177. }
  178. lock_guard (interpreter *ip, lock *lp) : interp (ip), lockp (lp)
  179. {
  180. }
  181. static result<lock_guard>
  182. make (interpreter *interp, lock *lp = nullptr)
  183. {
  184. lock_guard ret (interp, lp);
  185. if (lp)
  186. KP_VTRY (lp->grab (interp));
  187. return (ret);
  188. }
  189. result<int> set (lock *lp)
  190. {
  191. if (this->lockp)
  192. this->lockp->drop (this->interp);
  193. if ((this->lockp = lp) != nullptr)
  194. KP_VTRY (this->lockp->grab (this->interp));
  195. return (0);
  196. }
  197. ~lock_guard ()
  198. {
  199. if (this->lockp)
  200. this->lockp->drop (this->interp);
  201. }
  202. };
  203. inline lock* as_lock (object obj)
  204. {
  205. return ((lock *)as_custom (obj));
  206. }
  207. inline bool lock_p (object obj)
  208. {
  209. return (lock::type_p (obj));
  210. }
  211. KP_EXPORT result<object> alloc_lock (interpreter *interp, bool recursive_p);
  212. inline condvar* as_condvar (object obj)
  213. {
  214. return ((condvar *)as_custom (obj));
  215. }
  216. inline bool condvar_p (object obj)
  217. {
  218. return (condvar::type_p (obj));
  219. }
  220. KP_EXPORT result<object> alloc_condvar (interpreter *interp);
  221. inline thread* as_thread (object obj)
  222. {
  223. return ((thread *)unmask (obj));
  224. }
  225. inline bool thread_p (object obj)
  226. {
  227. return (itype (obj) == typecode::THREAD);
  228. }
  229. KP_EXPORT dlist all_threads;
  230. KP_EXPORT atomic_t all_threads_lock;
  231. KP_EXPORT uint32_t num_threads;
  232. // Return values for some thread calls.
  233. enum
  234. {
  235. THR_ERROR = -1,
  236. THR_TIMEOUT = -2,
  237. THR_DEADLK = -3,
  238. THR_PERM = -4,
  239. THR_INTR = -5
  240. };
  241. /* Test if only one thread is running. This test should be as cheap as
  242. * possible, in order to avoid locking when a thread knows it's alone. */
  243. #ifdef KP_NO_THREADS
  244. inline constexpr bool singlethr_p ()
  245. {
  246. return (true);
  247. }
  248. # define KP_MT_BEGIN(expr) (void)0
  249. # define KP_MT_END(expr) (void)0
  250. #else
  251. inline bool singlethr_p ()
  252. {
  253. return (num_threads <= 1);
  254. }
  255. # define KP_MT_BEGIN(expr) \
  256. bool __tst = singlethr_p (); \
  257. if (!__tst) expr
  258. # define KP_MT_END(expr) \
  259. if (!__tst) expr
  260. #endif
  261. // Init OP for the threading subsystem.
  262. KP_EXPORT init_op init_threads;
  263. KP_DECLS_END
  264. #endif