shim_async.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318
  1. /* Copyright (C) 2014 Stony Brook University
  2. This file is part of Graphene Library OS.
  3. Graphene Library OS is free software: you can redistribute it and/or
  4. modify it under the terms of the GNU Lesser General Public License
  5. as published by the Free Software Foundation, either version 3 of the
  6. License, or (at your option) any later version.
  7. Graphene Library OS 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 <http://www.gnu.org/licenses/>. */
  13. /*
  14. * shim_async.c
  15. *
  16. * This file contains functions to add asyncronous events triggered by timer.
  17. */
  18. #include <shim_internal.h>
  19. #include <shim_utils.h>
  20. #include <shim_thread.h>
  21. #include <pal.h>
  22. #include <list.h>
  23. #define IDLE_SLEEP_TIME 1000
  24. #define MAX_IDLE_CYCLES 100
  25. DEFINE_LIST(async_event);
  26. struct async_event {
  27. IDTYPE caller; /* thread installing this event */
  28. LIST_TYPE(async_event) list;
  29. void (*callback) (IDTYPE caller, void * arg);
  30. void * arg;
  31. PAL_HANDLE object; /* handle (async IO) to wait on */
  32. uint64_t expire_time; /* alarm/timer to wait on */
  33. };
  34. DEFINE_LISTP(async_event);
  35. static LISTP_TYPE(async_event) async_list;
  36. /* can be read without async_helper_lock but always written with lock held */
  37. static enum { HELPER_NOTALIVE, HELPER_ALIVE } async_helper_state;
  38. static struct shim_thread* async_helper_thread;
  39. static struct shim_lock async_helper_lock;
  40. static AEVENTTYPE install_new_event;
  41. static int create_async_helper(void);
  42. /* Threads register async events like alarm(), setitimer(), ioctl(FIOASYNC)
  43. * using this function. These events are enqueued in async_list and delivered
  44. * to Async Helper thread by triggering install_new_event. When event is
  45. * triggered in Async Helper thread, the corresponding event's callback with
  46. * arguments `arg` is called. This callback typically sends a signal to the
  47. * thread who registered the event (saved in `event->caller`).
  48. *
  49. * We distinguish between alarm/timer events and async IO events:
  50. * - alarm/timer events set object = NULL and time = seconds
  51. * (time = 0 cancels all pending alarms/timers).
  52. * - async IO events set object = handle and time = 0.
  53. *
  54. * Function returns remaining usecs for alarm/timer events (same as alarm())
  55. * or 0 for async IO events. On error, it returns -1.
  56. */
  57. int64_t install_async_event(PAL_HANDLE object, uint64_t time,
  58. void (*callback) (IDTYPE caller, void * arg),
  59. void * arg) {
  60. /* if event happens on object, time must be zero */
  61. assert(!object || (object && !time));
  62. uint64_t now = DkSystemTimeQuery();
  63. uint64_t max_prev_expire_time = now;
  64. struct async_event* event = malloc(sizeof(struct async_event));
  65. event->callback = callback;
  66. event->arg = arg;
  67. event->caller = get_cur_tid();
  68. event->object = object;
  69. event->expire_time = time ? now + time : 0;
  70. lock(&async_helper_lock);
  71. if (!object) {
  72. /* This is alarm() or setitimer() emulation, treat both according to
  73. * alarm() syscall semantics: cancel any pending alarm/timer. */
  74. struct async_event * tmp, * n;
  75. LISTP_FOR_EACH_ENTRY_SAFE(tmp, n, &async_list, list) {
  76. if (tmp->expire_time) {
  77. /* this is a pending alarm/timer, cancel it and save its expiration time */
  78. if (max_prev_expire_time < tmp->expire_time)
  79. max_prev_expire_time = tmp->expire_time;
  80. LISTP_DEL(tmp, &async_list, list);
  81. free(tmp);
  82. }
  83. }
  84. if (!time) {
  85. /* This is alarm(0), we cancelled all pending alarms/timers
  86. * and user doesn't want to set a new alarm: we are done. */
  87. free(event);
  88. unlock(&async_helper_lock);
  89. return max_prev_expire_time - now;
  90. }
  91. }
  92. INIT_LIST_HEAD(event, list);
  93. LISTP_ADD_TAIL(event, &async_list, list);
  94. if (async_helper_state == HELPER_NOTALIVE) {
  95. int ret = create_async_helper();
  96. if (ret < 0)
  97. return ret;
  98. }
  99. unlock(&async_helper_lock);
  100. debug("Installed async event at %lu\n", now);
  101. set_event(&install_new_event, 1);
  102. return max_prev_expire_time - now;
  103. }
  104. int init_async(void) {
  105. /* early enough in init, can write global vars without the lock */
  106. async_helper_state = HELPER_NOTALIVE;
  107. create_lock(&async_helper_lock);
  108. create_event(&install_new_event);
  109. return 0;
  110. }
  111. static void shim_async_helper(void * arg) {
  112. struct shim_thread * self = (struct shim_thread *) arg;
  113. if (!arg)
  114. return;
  115. __libc_tcb_t tcb;
  116. allocate_tls(&tcb, false, self);
  117. debug_setbuf(&tcb.shim_tcb, true);
  118. debug("Set tcb to %p\n", &tcb);
  119. lock(&async_helper_lock);
  120. bool notme = (self != async_helper_thread);
  121. unlock(&async_helper_lock);
  122. if (notme) {
  123. put_thread(self);
  124. DkThreadExit();
  125. return;
  126. }
  127. /* Assume async helper thread will not drain the stack that PAL provides,
  128. * so for efficiency we don't swap the stack. */
  129. debug("Async helper thread started\n");
  130. /* Simple heuristic to not burn cycles when no async events are installed:
  131. * async helper thread sleeps IDLE_SLEEP_TIME for MAX_IDLE_CYCLES and
  132. * if nothing happens, dies. It will be re-spawned if some thread wants
  133. * to install a new event. */
  134. uint64_t idle_cycles = 0;
  135. PAL_HANDLE polled = NULL;
  136. /* init object_list so that it always contains at least install_new_event */
  137. size_t object_list_size = 32;
  138. PAL_HANDLE * object_list =
  139. malloc(sizeof(PAL_HANDLE) * (1 + object_list_size));
  140. PAL_HANDLE install_new_event_hdl = event_handle(&install_new_event);
  141. object_list[0] = install_new_event_hdl;
  142. while (async_helper_state == HELPER_ALIVE) {
  143. uint64_t now = DkSystemTimeQuery();
  144. if (polled == install_new_event_hdl) {
  145. /* Some thread wants to install new event; this event is found
  146. * in async_list below, so just re-init install_new_event. */
  147. clear_event(&install_new_event);
  148. }
  149. lock(&async_helper_lock);
  150. /* Iterate through all async IO events and alarm/timer events to:
  151. * - call callbacks for all triggered events, and
  152. * - repopulate object_list with async IO events (if any), and
  153. * - find the next expiring alarm/timer (if any) */
  154. uint64_t next_expire_time = 0;
  155. size_t object_num = 0;
  156. struct async_event * tmp, * n;
  157. LISTP_FOR_EACH_ENTRY_SAFE(tmp, n, &async_list, list) {
  158. /* First check if this event was triggered; note that IO events
  159. * stay in the list whereas alarms/timers are fired only once. */
  160. if (polled && tmp->object == polled) {
  161. debug("Async IO event triggered at %lu\n", now);
  162. unlock(&async_helper_lock);
  163. tmp->callback(tmp->caller, tmp->arg);
  164. lock(&async_helper_lock);
  165. } else if (tmp->expire_time && tmp->expire_time <= now) {
  166. debug("Async alarm/timer triggered at %lu (expired at %lu)\n",
  167. now, tmp->expire_time);
  168. LISTP_DEL(tmp, &async_list, list);
  169. unlock(&async_helper_lock);
  170. tmp->callback(tmp->caller, tmp->arg);
  171. free(tmp);
  172. lock(&async_helper_lock);
  173. continue;
  174. }
  175. /* Now re-add this IO event to the list or re-add this timer */
  176. if (tmp->object) {
  177. if (object_num == object_list_size) {
  178. /* grow object_list to accomodate more objects */
  179. PAL_HANDLE * tmp_array = malloc(
  180. sizeof(PAL_HANDLE) * (1 + object_list_size * 2));
  181. memcpy(tmp_array, object_list,
  182. sizeof(PAL_HANDLE) * (1 + object_list_size));
  183. object_list_size *= 2;
  184. free(object_list);
  185. object_list = tmp_array;
  186. }
  187. object_list[object_num + 1] = tmp->object;
  188. object_num++;
  189. } else if (tmp->expire_time && tmp->expire_time > now) {
  190. if (!next_expire_time || next_expire_time > tmp->expire_time) {
  191. /* use time of the next expiring alarm/timer */
  192. next_expire_time = tmp->expire_time;
  193. }
  194. }
  195. }
  196. unlock(&async_helper_lock);
  197. uint64_t sleep_time;
  198. if (next_expire_time) {
  199. sleep_time = next_expire_time - now;
  200. idle_cycles = 0;
  201. } else if (object_num) {
  202. sleep_time = NO_TIMEOUT;
  203. idle_cycles = 0;
  204. } else {
  205. /* no async IO events and no timers/alarms: thread is idling */
  206. sleep_time = IDLE_SLEEP_TIME;
  207. idle_cycles++;
  208. }
  209. if (idle_cycles == MAX_IDLE_CYCLES) {
  210. debug("Async helper thread has been idle for some time; stopping it\n");
  211. break;
  212. }
  213. /* wait on async IO events + install_new_event + next expiring alarm/timer */
  214. polled = DkObjectsWaitAny(object_num + 1, object_list, sleep_time);
  215. /* ensure that while loop breaks on async_helper_state change */
  216. COMPILER_BARRIER();
  217. }
  218. lock(&async_helper_lock);
  219. async_helper_state = HELPER_NOTALIVE;
  220. async_helper_thread = NULL;
  221. unlock(&async_helper_lock);
  222. put_thread(self);
  223. debug("Async helper thread terminated\n");
  224. free(object_list);
  225. DkThreadExit();
  226. }
  227. /* this should be called with the async_helper_lock held */
  228. static int create_async_helper(void) {
  229. if (async_helper_state == HELPER_ALIVE)
  230. return 0;
  231. enable_locking();
  232. struct shim_thread* new = get_new_internal_thread();
  233. if (!new)
  234. return -ENOMEM;
  235. PAL_HANDLE handle = thread_create(shim_async_helper, new, 0);
  236. if (!handle) {
  237. async_helper_thread = NULL;
  238. async_helper_state = HELPER_NOTALIVE;
  239. put_thread(new);
  240. return -PAL_ERRNO;
  241. }
  242. new->pal_handle = handle;
  243. async_helper_thread = new;
  244. async_helper_state = HELPER_ALIVE;
  245. return 0;
  246. }
  247. /* On success, the reference to async helper thread is returned with refcount
  248. * incremented. It is the responsibility of caller to wait for async helper's
  249. * exit and then release the final reference to free related resources (it is
  250. * problematic for the thread itself to release its own resources e.g. stack).
  251. */
  252. struct shim_thread* terminate_async_helper(void) {
  253. if (async_helper_state != HELPER_ALIVE)
  254. return NULL;
  255. lock(&async_helper_lock);
  256. struct shim_thread* ret = async_helper_thread;
  257. if (ret)
  258. get_thread(ret);
  259. async_helper_state = HELPER_NOTALIVE;
  260. unlock(&async_helper_lock);
  261. /* force wake up of async helper thread so that it exits */
  262. set_event(&install_new_event, 1);
  263. return ret;
  264. }