shim_async.c 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344
  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 <linux_list.h>
  25. struct async_event {
  26. IDTYPE caller;
  27. struct list_head list;
  28. void (*callback) (IDTYPE caller, void * arg);
  29. void * arg;
  30. PAL_HANDLE object;
  31. unsigned long install_time;
  32. unsigned long expire_time;
  33. };
  34. static LIST_HEAD(async_list);
  35. enum { HELPER_NOTALIVE, HELPER_ALIVE };
  36. static struct shim_atomic async_helper_state;
  37. static struct shim_thread * async_helper_thread;
  38. static AEVENTTYPE async_helper_event;
  39. static LOCKTYPE async_helper_lock;
  40. int install_async_event (PAL_HANDLE object, unsigned long time,
  41. void (*callback) (IDTYPE caller, void * arg),
  42. void * arg)
  43. {
  44. struct async_event * event =
  45. malloc(sizeof(struct async_event));
  46. unsigned long install_time = DkSystemTimeQuery();
  47. event->callback = callback;
  48. event->arg = arg;
  49. event->caller = get_cur_tid();
  50. event->object = object;
  51. event->install_time = time ? install_time : 0;
  52. event->expire_time = time ? install_time + time : 0;
  53. lock(async_helper_lock);
  54. struct async_event * tmp;
  55. struct list_head * prev = &async_list;
  56. list_for_each_entry(tmp, &async_list, list) {
  57. if (event->expire_time && tmp->expire_time > event->expire_time)
  58. break;
  59. prev = &tmp->list;
  60. }
  61. /*
  62. * man page of alarm system call :
  63. * DESCRIPTION
  64. * alarm() arranges for a SIGALRM signal to be delivered to the
  65. * calling process in seconds seconds.
  66. * If seconds is zero, any pending alarm is canceled.
  67. * In any event any previously set alarm() is canceled.
  68. */
  69. if (!list_empty(&async_list)) {
  70. tmp = list_entry((&async_list)->prev, struct async_event, list);
  71. /*
  72. * any previously set alarm() is canceled.
  73. * There should be exactly only one timer pending
  74. */
  75. list_del(&tmp->list);
  76. free(tmp);
  77. } else
  78. tmp = NULL;
  79. INIT_LIST_HEAD(&event->list);
  80. if (!time) // If seconds is zero, any pending alarm is canceled.
  81. free(event);
  82. else
  83. list_add_tail(&event->list, &async_list);
  84. unlock(async_helper_lock);
  85. if (atomic_read(&async_helper_state) == HELPER_NOTALIVE)
  86. create_async_helper();
  87. DkEventSet(async_helper_event);
  88. /*
  89. * man page of alarm system call :
  90. * RETURN VALUE
  91. * alarm() returns the number of seconds remaining until
  92. * any previously scheduled alarm was due to be delivered,
  93. * or zero if there was no previously scheduled alarm.
  94. * reference : lxr.free-electrons.com/source/kernel/itimer.c?v=2.6.35#L272
  95. */
  96. unsigned long ret;
  97. unsigned long sec = 0;
  98. unsigned long usec = 0;
  99. if (tmp) {
  100. ret = tmp->expire_time - install_time;
  101. sec = ret / 1000000;
  102. usec = ret % 1000000;
  103. if ((!sec && usec) || usec >=500000)
  104. sec++;
  105. }
  106. return sec;
  107. }
  108. int init_async (void)
  109. {
  110. atomic_set(&async_helper_state, HELPER_NOTALIVE);
  111. create_lock(async_helper_lock);
  112. create_event(&async_helper_event);
  113. return 0;
  114. }
  115. #define IDLE_SLEEP_TIME 1000
  116. #define MAX_IDLE_CYCLES 100
  117. static void shim_async_helper (void * arg)
  118. {
  119. struct shim_thread * self = (struct shim_thread *) arg;
  120. if (!arg)
  121. return;
  122. __libc_tcb_t tcb;
  123. allocate_tls(&tcb, false, self);
  124. debug_setbuf(&tcb.shim_tcb, true);
  125. debug("set tcb to %p\n", &tcb);
  126. lock(async_helper_lock);
  127. bool notme = (self != async_helper_thread);
  128. unlock(async_helper_lock);
  129. if (notme) {
  130. put_thread(self);
  131. DkThreadExit();
  132. return;
  133. }
  134. debug("async helper thread started\n");
  135. /* TSAI: we assume async helper thread will not drain the
  136. stack that PAL provides, so for efficiency, we don't
  137. swap any stack */
  138. unsigned long idle_cycles = 0;
  139. unsigned long latest_time;
  140. struct async_event * next_event = NULL;
  141. PAL_HANDLE async_event_handle = event_handle(&async_helper_event);
  142. int object_list_size = 32, object_num;
  143. PAL_HANDLE polled;
  144. PAL_HANDLE * local_objects =
  145. malloc(sizeof(PAL_HANDLE) * (1 + object_list_size));
  146. local_objects[0] = async_event_handle;
  147. goto update_status;
  148. while (atomic_read(&async_helper_state) == HELPER_ALIVE) {
  149. unsigned long sleep_time;
  150. if (next_event) {
  151. sleep_time = next_event->expire_time - latest_time;
  152. idle_cycles = 0;
  153. } else if (object_num) {
  154. sleep_time = NO_TIMEOUT;
  155. idle_cycles = 0;
  156. } else {
  157. sleep_time = IDLE_SLEEP_TIME;
  158. idle_cycles++;
  159. }
  160. polled = DkObjectsWaitAny(object_num + 1, local_objects, sleep_time);
  161. if (!polled) {
  162. if (next_event) {
  163. debug("async event trigger at %llu\n",
  164. next_event->expire_time);
  165. next_event->callback(next_event->caller, next_event->arg);
  166. lock(async_helper_lock);
  167. list_del(&next_event->list);
  168. free(next_event);
  169. goto update_list;
  170. }
  171. continue;
  172. }
  173. if (polled == async_event_handle) {
  174. clear_event(&async_helper_event);
  175. update_status:
  176. latest_time = DkSystemTimeQuery();
  177. if (atomic_read(&async_helper_state) == HELPER_NOTALIVE) {
  178. break;
  179. } else {
  180. lock(async_helper_lock);
  181. goto update_list;
  182. }
  183. }
  184. struct async_event * tmp, * n;
  185. lock(async_helper_lock);
  186. list_for_each_entry_safe(tmp, n, &async_list, list) {
  187. if (tmp->object == polled) {
  188. debug("async event trigger at %llu\n",
  189. latest_time);
  190. unlock(async_helper_lock);
  191. tmp->callback(tmp->caller, tmp->arg);
  192. lock(async_helper_lock);
  193. break;
  194. }
  195. }
  196. update_list:
  197. next_event = NULL;
  198. object_num = 0;
  199. if (!list_empty(&async_list)) {
  200. struct async_event * tmp, * n;
  201. list_for_each_entry_safe(tmp, n, &async_list, list) {
  202. if (tmp->object) {
  203. local_objects[object_num + 1] = tmp->object;
  204. object_num++;
  205. }
  206. if (!tmp->install_time)
  207. continue;
  208. if (tmp->expire_time > latest_time) {
  209. next_event = tmp;
  210. break;
  211. }
  212. debug("async event trigger at %llu (expire at %llu)\n",
  213. latest_time, tmp->expire_time);
  214. list_del(&tmp->list);
  215. unlock(async_helper_lock);
  216. tmp->callback(tmp->caller, tmp->arg);
  217. free(tmp);
  218. lock(async_helper_lock);
  219. }
  220. idle_cycles = 0;
  221. }
  222. unlock(async_helper_lock);
  223. if (idle_cycles++ == MAX_IDLE_CYCLES) {
  224. debug("async helper thread reach helper cycle\n");
  225. /* walking away, if someone is issueing an event,
  226. they have to create another thread */
  227. break;
  228. }
  229. }
  230. atomic_set(&async_helper_state, HELPER_NOTALIVE);
  231. lock(async_helper_lock);
  232. async_helper_thread = NULL;
  233. unlock(async_helper_lock);
  234. put_thread(self);
  235. debug("async helper thread terminated\n");
  236. DkThreadExit();
  237. }
  238. int create_async_helper (void)
  239. {
  240. int ret = 0;
  241. if (atomic_read(&async_helper_state) == HELPER_ALIVE)
  242. return 0;
  243. enable_locking();
  244. struct shim_thread * new = get_new_internal_thread();
  245. if (!new)
  246. return -ENOMEM;
  247. lock(async_helper_lock);
  248. if (atomic_read(&async_helper_state) == HELPER_ALIVE) {
  249. unlock(async_helper_lock);
  250. put_thread(new);
  251. return 0;
  252. }
  253. async_helper_thread = new;
  254. atomic_xchg(&async_helper_state, HELPER_ALIVE);
  255. unlock(async_helper_lock);
  256. PAL_HANDLE handle = thread_create(shim_async_helper, new, 0);
  257. if (!handle) {
  258. ret = -PAL_ERRNO;
  259. lock(async_helper_lock);
  260. async_helper_thread = NULL;
  261. atomic_xchg(&async_helper_state, HELPER_NOTALIVE);
  262. unlock(async_helper_lock);
  263. put_thread(new);
  264. return ret;
  265. }
  266. new->pal_handle = handle;
  267. return 0;
  268. }
  269. int terminate_async_helper (void)
  270. {
  271. if (atomic_read(&async_helper_state) != HELPER_ALIVE)
  272. return 0;
  273. lock(async_helper_lock);
  274. atomic_xchg(&async_helper_state, HELPER_NOTALIVE);
  275. unlock(async_helper_lock);
  276. set_event(&async_helper_event, 1);
  277. return 0;
  278. }