shim_async.c 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328
  1. /* -*- mode:c; c-file-style:"k&r"; c-basic-offset: 4; tab-width:4; indent-tabs-mode:nil; mode:auto-fill; fill-column:78; -*- */
  2. /* vim: set ts=4 sw=4 et tw=78 fo=cqt wm=0: */
  3. /* Copyright (C) 2014 OSCAR lab, Stony Brook University
  4. This file is part of Graphene Library OS.
  5. Graphene Library OS is free software: you can redistribute it and/or
  6. modify it under the terms of the GNU General Public License
  7. as published by the Free Software Foundation, either version 3 of the
  8. License, or (at your option) any later version.
  9. Graphene Library OS is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. GNU General Public License for more details.
  13. You should have received a copy of the GNU General Public License
  14. along with this program. If not, see <http://www.gnu.org/licenses/>. */
  15. /*
  16. * shim_async.c
  17. *
  18. * This file contains functions to add asyncronous events triggered by timer.
  19. */
  20. #include <shim_internal.h>
  21. #include <shim_utils.h>
  22. #include <shim_thread.h>
  23. #include <pal.h>
  24. #include <list.h>
  25. DEFINE_LIST(async_event);
  26. struct async_event {
  27. IDTYPE caller;
  28. LIST_TYPE(async_event) list;
  29. void (*callback) (IDTYPE caller, void * arg);
  30. void * arg;
  31. PAL_HANDLE object;
  32. unsigned long install_time;
  33. unsigned long expire_time;
  34. };
  35. DEFINE_LISTP(async_event);
  36. static LISTP_TYPE(async_event) async_list;
  37. enum { HELPER_NOTALIVE, HELPER_ALIVE };
  38. static struct shim_atomic async_helper_state;
  39. static struct shim_thread * async_helper_thread;
  40. static AEVENTTYPE async_helper_event;
  41. static LOCKTYPE async_helper_lock;
  42. int install_async_event (PAL_HANDLE object, unsigned long time,
  43. void (*callback) (IDTYPE caller, void * arg),
  44. void * arg)
  45. {
  46. struct async_event * event =
  47. malloc(sizeof(struct async_event));
  48. unsigned long install_time = DkSystemTimeQuery();
  49. debug("install async event at %llu\n", install_time);
  50. event->callback = callback;
  51. event->arg = arg;
  52. event->caller = get_cur_tid();
  53. event->object = object;
  54. event->install_time = time ? install_time : 0;
  55. event->expire_time = time ? install_time + time : 0;
  56. lock(async_helper_lock);
  57. struct async_event * tmp;
  58. listp_for_each_entry(tmp, &async_list, list) {
  59. if (event->expire_time && tmp->expire_time > event->expire_time)
  60. break;
  61. }
  62. /*
  63. * man page of alarm system call :
  64. * DESCRIPTION
  65. * alarm() arranges for a SIGALRM signal to be delivered to the
  66. * calling process in seconds seconds.
  67. * If seconds is zero, any pending alarm is canceled.
  68. * In any event any previously set alarm() is canceled.
  69. */
  70. if (!listp_empty(&async_list)) {
  71. tmp = listp_first_entry(&async_list, struct async_event, list);
  72. tmp = tmp->list.prev;
  73. /*
  74. * any previously set alarm() is canceled.
  75. * There should be exactly only one timer pending
  76. */
  77. listp_del(tmp, &async_list, list);
  78. free(tmp);
  79. } else
  80. tmp = NULL;
  81. INIT_LIST_HEAD(event, list);
  82. if (!time) // If seconds is zero, any pending alarm is canceled.
  83. free(event);
  84. else
  85. listp_add_tail(event, &async_list, list);
  86. unlock(async_helper_lock);
  87. if (atomic_read(&async_helper_state) == HELPER_NOTALIVE)
  88. create_async_helper();
  89. set_event(&async_helper_event, 1);
  90. return 0;
  91. }
  92. int init_async (void)
  93. {
  94. atomic_set(&async_helper_state, HELPER_NOTALIVE);
  95. create_lock(async_helper_lock);
  96. create_event(&async_helper_event);
  97. return 0;
  98. }
  99. #define IDLE_SLEEP_TIME 1000
  100. #define MAX_IDLE_CYCLES 100
  101. static void shim_async_helper (void * arg)
  102. {
  103. struct shim_thread * self = (struct shim_thread *) arg;
  104. if (!arg)
  105. return;
  106. __libc_tcb_t tcb;
  107. allocate_tls(&tcb, false, self);
  108. debug_setbuf(&tcb.shim_tcb, true);
  109. debug("set tcb to %p\n", &tcb);
  110. lock(async_helper_lock);
  111. bool notme = (self != async_helper_thread);
  112. unlock(async_helper_lock);
  113. if (notme) {
  114. put_thread(self);
  115. DkThreadExit();
  116. return;
  117. }
  118. debug("async helper thread started\n");
  119. /* TSAI: we assume async helper thread will not drain the
  120. stack that PAL provides, so for efficiency, we don't
  121. swap any stack */
  122. unsigned long idle_cycles = 0;
  123. unsigned long latest_time;
  124. struct async_event * next_event = NULL;
  125. PAL_HANDLE async_event_handle = event_handle(&async_helper_event);
  126. int object_list_size = 32, object_num;
  127. PAL_HANDLE polled;
  128. PAL_HANDLE * local_objects =
  129. malloc(sizeof(PAL_HANDLE) * (1 + object_list_size));
  130. local_objects[0] = async_event_handle;
  131. goto update_status;
  132. while (atomic_read(&async_helper_state) == HELPER_ALIVE) {
  133. unsigned long sleep_time;
  134. if (next_event) {
  135. sleep_time = next_event->expire_time - latest_time;
  136. idle_cycles = 0;
  137. } else if (object_num) {
  138. sleep_time = NO_TIMEOUT;
  139. idle_cycles = 0;
  140. } else {
  141. sleep_time = IDLE_SLEEP_TIME;
  142. idle_cycles++;
  143. }
  144. polled = DkObjectsWaitAny(object_num + 1, local_objects, sleep_time);
  145. if (!polled) {
  146. if (next_event) {
  147. debug("async event trigger at %llu\n",
  148. next_event->expire_time);
  149. next_event->callback(next_event->caller, next_event->arg);
  150. lock(async_helper_lock);
  151. /* DEP: Events can only be on the async list */
  152. listp_del(next_event, &async_list, list);
  153. free(next_event);
  154. goto update_list;
  155. }
  156. continue;
  157. }
  158. if (polled == async_event_handle) {
  159. clear_event(&async_helper_event);
  160. update_status:
  161. latest_time = DkSystemTimeQuery();
  162. if (atomic_read(&async_helper_state) == HELPER_NOTALIVE) {
  163. break;
  164. } else {
  165. lock(async_helper_lock);
  166. goto update_list;
  167. }
  168. }
  169. struct async_event * tmp, * n;
  170. lock(async_helper_lock);
  171. listp_for_each_entry_safe(tmp, n, &async_list, list) {
  172. if (tmp->object == polled) {
  173. debug("async event trigger at %llu\n",
  174. latest_time);
  175. unlock(async_helper_lock);
  176. tmp->callback(tmp->caller, tmp->arg);
  177. lock(async_helper_lock);
  178. break;
  179. }
  180. }
  181. update_list:
  182. next_event = NULL;
  183. object_num = 0;
  184. if (!listp_empty(&async_list)) {
  185. struct async_event * tmp, * n;
  186. listp_for_each_entry_safe(tmp, n, &async_list, list) {
  187. if (tmp->object) {
  188. local_objects[object_num + 1] = tmp->object;
  189. object_num++;
  190. }
  191. if (!tmp->install_time)
  192. continue;
  193. if (tmp->expire_time > latest_time) {
  194. next_event = tmp;
  195. break;
  196. }
  197. debug("async event trigger at %llu (expire at %llu)\n",
  198. latest_time, tmp->expire_time);
  199. listp_del(tmp, &async_list, list);
  200. unlock(async_helper_lock);
  201. tmp->callback(tmp->caller, tmp->arg);
  202. free(tmp);
  203. lock(async_helper_lock);
  204. }
  205. idle_cycles = 0;
  206. }
  207. unlock(async_helper_lock);
  208. if (idle_cycles++ == MAX_IDLE_CYCLES) {
  209. debug("async helper thread reach helper cycle\n");
  210. /* walking away, if someone is issueing an event,
  211. they have to create another thread */
  212. break;
  213. }
  214. }
  215. atomic_set(&async_helper_state, HELPER_NOTALIVE);
  216. lock(async_helper_lock);
  217. async_helper_thread = NULL;
  218. unlock(async_helper_lock);
  219. put_thread(self);
  220. debug("async helper thread terminated\n");
  221. DkThreadExit();
  222. }
  223. int create_async_helper (void)
  224. {
  225. int ret = 0;
  226. if (atomic_read(&async_helper_state) == HELPER_ALIVE)
  227. return 0;
  228. enable_locking();
  229. struct shim_thread * new = get_new_internal_thread();
  230. if (!new)
  231. return -ENOMEM;
  232. lock(async_helper_lock);
  233. if (atomic_read(&async_helper_state) == HELPER_ALIVE) {
  234. unlock(async_helper_lock);
  235. put_thread(new);
  236. return 0;
  237. }
  238. async_helper_thread = new;
  239. atomic_xchg(&async_helper_state, HELPER_ALIVE);
  240. unlock(async_helper_lock);
  241. PAL_HANDLE handle = thread_create(shim_async_helper, new, 0);
  242. if (!handle) {
  243. ret = -PAL_ERRNO;
  244. lock(async_helper_lock);
  245. async_helper_thread = NULL;
  246. atomic_xchg(&async_helper_state, HELPER_NOTALIVE);
  247. unlock(async_helper_lock);
  248. put_thread(new);
  249. return ret;
  250. }
  251. new->pal_handle = handle;
  252. return 0;
  253. }
  254. int terminate_async_helper (void)
  255. {
  256. if (atomic_read(&async_helper_state) != HELPER_ALIVE)
  257. return 0;
  258. lock(async_helper_lock);
  259. atomic_xchg(&async_helper_state, HELPER_NOTALIVE);
  260. unlock(async_helper_lock);
  261. set_event(&async_helper_event, 1);
  262. return 0;
  263. }