shim_async.c 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330
  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 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 Lesser 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 Lesser General Public License for more details.
  13. You should have received a copy of the GNU Lesser 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. /* This variable can be read without the async_helper_lock held, but is always
  38. * modified with it held. */
  39. static enum { HELPER_NOTALIVE, HELPER_ALIVE } async_helper_state;
  40. static struct shim_thread * async_helper_thread;
  41. static AEVENTTYPE async_helper_event;
  42. static LOCKTYPE async_helper_lock;
  43. int install_async_event (PAL_HANDLE object, unsigned long time,
  44. void (*callback) (IDTYPE caller, void * arg),
  45. void * arg)
  46. {
  47. struct async_event * event =
  48. malloc(sizeof(struct async_event));
  49. unsigned long install_time = DkSystemTimeQuery();
  50. debug("install async event at %llu\n", install_time);
  51. event->callback = callback;
  52. event->arg = arg;
  53. event->caller = get_cur_tid();
  54. event->object = object;
  55. event->install_time = time ? install_time : 0;
  56. event->expire_time = time ? install_time + time : 0;
  57. lock(async_helper_lock);
  58. struct async_event * tmp;
  59. listp_for_each_entry(tmp, &async_list, list) {
  60. if (event->expire_time && tmp->expire_time > event->expire_time)
  61. break;
  62. }
  63. /*
  64. * man page of alarm system call :
  65. * DESCRIPTION
  66. * alarm() arranges for a SIGALRM signal to be delivered to the
  67. * calling process in seconds seconds.
  68. * If seconds is zero, any pending alarm is canceled.
  69. * In any event any previously set alarm() is canceled.
  70. */
  71. if (!listp_empty(&async_list)) {
  72. tmp = listp_first_entry(&async_list, struct async_event, list);
  73. tmp = tmp->list.prev;
  74. /*
  75. * any previously set alarm() is canceled.
  76. * There should be exactly only one timer pending
  77. */
  78. listp_del(tmp, &async_list, list);
  79. free(tmp);
  80. } else
  81. tmp = NULL;
  82. INIT_LIST_HEAD(event, list);
  83. if (!time) // If seconds is zero, any pending alarm is canceled.
  84. free(event);
  85. else
  86. listp_add_tail(event, &async_list, list);
  87. unlock(async_helper_lock);
  88. if (atomic_read(&async_helper_state) == HELPER_NOTALIVE)
  89. create_async_helper();
  90. unlock(async_helper_lock);
  91. set_event(&async_helper_event, 1);
  92. return 0;
  93. }
  94. int init_async (void)
  95. {
  96. /* This is early enough in init that we can write this variable without
  97. * the lock. */
  98. async_helper_state = HELPER_NOTALIVE;
  99. create_lock(async_helper_lock);
  100. create_event(&async_helper_event);
  101. return 0;
  102. }
  103. #define IDLE_SLEEP_TIME 1000
  104. #define MAX_IDLE_CYCLES 100
  105. static void shim_async_helper (void * arg)
  106. {
  107. struct shim_thread * self = (struct shim_thread *) arg;
  108. if (!arg)
  109. return;
  110. __libc_tcb_t tcb;
  111. allocate_tls(&tcb, false, self);
  112. debug_setbuf(&tcb.shim_tcb, true);
  113. debug("set tcb to %p\n", &tcb);
  114. lock(async_helper_lock);
  115. bool notme = (self != async_helper_thread);
  116. unlock(async_helper_lock);
  117. if (notme) {
  118. put_thread(self);
  119. DkThreadExit();
  120. return;
  121. }
  122. debug("async helper thread started\n");
  123. /* TSAI: we assume async helper thread will not drain the
  124. stack that PAL provides, so for efficiency, we don't
  125. swap any stack */
  126. unsigned long idle_cycles = 0;
  127. unsigned long latest_time;
  128. struct async_event * next_event = NULL;
  129. PAL_HANDLE async_event_handle = event_handle(&async_helper_event);
  130. int object_list_size = 32, object_num;
  131. PAL_HANDLE polled;
  132. PAL_HANDLE * local_objects =
  133. malloc(sizeof(PAL_HANDLE) * (1 + object_list_size));
  134. local_objects[0] = async_event_handle;
  135. goto update_status;
  136. /* This loop should be careful to use a barrier after sleeping
  137. * to ensure that the while breaks once async_helper_state changes.
  138. */
  139. while (async_helper_state == HELPER_ALIVE) {
  140. unsigned long sleep_time;
  141. if (next_event) {
  142. sleep_time = next_event->expire_time - latest_time;
  143. idle_cycles = 0;
  144. } else if (object_num) {
  145. sleep_time = NO_TIMEOUT;
  146. idle_cycles = 0;
  147. } else {
  148. sleep_time = IDLE_SLEEP_TIME;
  149. idle_cycles++;
  150. }
  151. polled = DkObjectsWaitAny(object_num + 1, local_objects, sleep_time);
  152. barrier();
  153. if (!polled) {
  154. if (next_event) {
  155. debug("async event trigger at %llu\n",
  156. next_event->expire_time);
  157. next_event->callback(next_event->caller, next_event->arg);
  158. lock(async_helper_lock);
  159. /* DEP: Events can only be on the async list */
  160. listp_del(next_event, &async_list, list);
  161. free(next_event);
  162. goto update_list;
  163. }
  164. continue;
  165. }
  166. if (polled == async_event_handle) {
  167. clear_event(&async_helper_event);
  168. update_status:
  169. latest_time = DkSystemTimeQuery();
  170. if (async_helper_state == HELPER_NOTALIVE) {
  171. break;
  172. } else {
  173. lock(async_helper_lock);
  174. goto update_list;
  175. }
  176. }
  177. struct async_event * tmp, * n;
  178. lock(async_helper_lock);
  179. listp_for_each_entry_safe(tmp, n, &async_list, list) {
  180. if (tmp->object == polled) {
  181. debug("async event trigger at %llu\n",
  182. latest_time);
  183. unlock(async_helper_lock);
  184. tmp->callback(tmp->caller, tmp->arg);
  185. lock(async_helper_lock);
  186. break;
  187. }
  188. }
  189. update_list:
  190. next_event = NULL;
  191. object_num = 0;
  192. if (!listp_empty(&async_list)) {
  193. struct async_event * tmp, * n;
  194. listp_for_each_entry_safe(tmp, n, &async_list, list) {
  195. if (tmp->object) {
  196. local_objects[object_num + 1] = tmp->object;
  197. object_num++;
  198. }
  199. if (!tmp->install_time)
  200. continue;
  201. if (tmp->expire_time > latest_time) {
  202. next_event = tmp;
  203. break;
  204. }
  205. debug("async event trigger at %llu (expire at %llu)\n",
  206. latest_time, tmp->expire_time);
  207. listp_del(tmp, &async_list, list);
  208. unlock(async_helper_lock);
  209. tmp->callback(tmp->caller, tmp->arg);
  210. free(tmp);
  211. lock(async_helper_lock);
  212. }
  213. idle_cycles = 0;
  214. }
  215. unlock(async_helper_lock);
  216. if (idle_cycles++ == MAX_IDLE_CYCLES) {
  217. debug("async helper thread reach helper cycle\n");
  218. /* walking away, if someone is issueing an event,
  219. they have to create another thread */
  220. break;
  221. }
  222. }
  223. lock(async_helper_lock);
  224. async_helper_state = HELPER_NOTALIVE;
  225. async_helper_thread = NULL;
  226. unlock(async_helper_lock);
  227. put_thread(self);
  228. debug("async helper thread terminated\n");
  229. DkThreadExit();
  230. }
  231. /* This should be called with the async_helper_lock held */
  232. int create_async_helper (void)
  233. {
  234. int ret = 0;
  235. if (async_helper_state == HELPER_ALIVE)
  236. return 0;
  237. enable_locking();
  238. struct shim_thread * new = get_new_internal_thread();
  239. if (!new)
  240. return -ENOMEM;
  241. PAL_HANDLE handle = thread_create(shim_async_helper, new, 0);
  242. if (!handle) {
  243. ret = -PAL_ERRNO;
  244. async_helper_thread = NULL;
  245. async_helper_state = HELPER_NOTALIVE;
  246. put_thread(new);
  247. return ret;
  248. }
  249. new->pal_handle = handle;
  250. /* Publish new and update the state once fully initialized */
  251. async_helper_thread = new;
  252. async_helper_state = HELPER_ALIVE;
  253. return 0;
  254. }
  255. int terminate_async_helper (void)
  256. {
  257. if (async_helper_state != HELPER_ALIVE)
  258. return 0;
  259. lock(async_helper_lock);
  260. async_helper_state = HELPER_NOTALIVE;
  261. unlock(async_helper_lock);
  262. set_event(&async_helper_event, 1);
  263. return 0;
  264. }