/* -*- mode:c; c-file-style:"k&r"; c-basic-offset: 4; tab-width:4; indent-tabs-mode:nil; mode:auto-fill; fill-column:78; -*- */ /* vim: set ts=4 sw=4 et tw=78 fo=cqt wm=0: */ /* Copyright (C) 2014 OSCAR lab, Stony Brook University This file is part of Graphene Library OS. Graphene Library OS is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Graphene Library OS is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ /* * shim_async.c * * This file contains functions to add asyncronous events triggered by timer. */ #include #include #include #include #include struct async_event { IDTYPE caller; struct list_head list; void (*callback) (IDTYPE caller, void * arg); void * arg; unsigned long install_time; unsigned long expire_time; }; static LIST_HEAD(async_list); enum { HELPER_NOTALIVE, HELPER_ALIVE }; static struct shim_atomic async_helper_state; static struct shim_thread * async_helper_thread; static PAL_HANDLE async_helper_event; static LOCKTYPE async_helper_lock; int install_async_event (unsigned long time, void (*callback) (IDTYPE caller, void * arg), void * arg) { struct async_event * event = malloc(sizeof(struct async_event)); unsigned long install_time = DkSystemTimeQuery(); debug("install async event at %llu\n", install_time); event->callback = callback; event->arg = arg; event->caller = get_cur_tid(); event->install_time = install_time; event->expire_time = install_time + time; lock(async_helper_lock); struct async_event * tmp; struct list_head * prev = &async_list; list_for_each_entry(tmp, &async_list, list) { if (tmp->expire_time > event->expire_time) break; prev = &tmp->list; } INIT_LIST_HEAD(&event->list); list_add(&event->list, prev); unlock(async_helper_lock); if (atomic_read(&async_helper_state) == HELPER_NOTALIVE) create_async_helper(); DkEventSet(async_helper_event); return 0; } int init_async (void) { atomic_set(&async_helper_state, HELPER_NOTALIVE); create_lock(async_helper_lock); async_helper_event = DkSynchronizationEventCreate(0); return 0; } #define IDLE_SLEEP_TIME 1000 #define MAX_IDLE_CYCLES 100 static void shim_async_helper (void * arg) { struct shim_thread * self = (struct shim_thread *) arg; if (!arg) return; __libc_tcb_t tcb; allocate_tls(&tcb, self); debug_setbuf(&tcb.shim_tcb, true); debug("set tcb to %p\n", &tcb); lock(async_helper_lock); if (self != async_helper_thread) { put_thread(self); DkThreadExit(); return; } debug("async helper thread started\n"); /* TSAI: we assume async helper thread will not drain the stack that PAL provides, so for efficiency, we don't swap any stack */ unsigned long idle_cycles = 0; unsigned long latest_time; struct async_event * next_event, * finished_event = NULL; goto update; while (atomic_read(&async_helper_state) == HELPER_ALIVE) { lock(async_helper_lock); update: latest_time = DkSystemTimeQuery(); next_event = NULL; if (!list_empty(&async_list)) { if (finished_event) { list_del(&finished_event->list); free(finished_event); finished_event = NULL; } struct async_event * tmp, * n; list_for_each_entry_safe(tmp, n, &async_list, list) { if (tmp->expire_time > latest_time) { next_event = tmp; break; } debug("async event trigger at %llu (expect expiring at %llu)\n", latest_time, tmp->expire_time); list_del(&tmp->list); tmp->callback(tmp->caller, tmp->arg); free(tmp); } idle_cycles = 0; } unlock(async_helper_lock); if (!next_event && idle_cycles++ == MAX_IDLE_CYCLES) { debug("async helper thread reach helper cycle\n"); /* walking away, if someone is issueing an event, they have to create another thread */ break; } unsigned long sleep_time = next_event ? next_event->expire_time - latest_time : IDLE_SLEEP_TIME; PAL_HANDLE notify = DkObjectsWaitAny(1, &async_helper_event, sleep_time); /* if we are not waken up by someone, the waiting has finished */ if (!notify && next_event) { debug("async event trigger at %llu\n", next_event->expire_time); finished_event = next_event; next_event->callback(next_event->caller, next_event->arg); } } atomic_set(&async_helper_state, HELPER_NOTALIVE); lock(async_helper_lock); async_helper_thread = NULL; unlock(async_helper_lock); put_thread(self); debug("async helper thread terminated\n"); DkThreadExit(); } int create_async_helper (void) { int ret = 0; if (atomic_read(&async_helper_state) == HELPER_ALIVE) return 0; enable_locking(); struct shim_thread * new = get_new_internal_thread(); if (!new) return -ENOMEM; lock(async_helper_lock); if (atomic_read(&async_helper_state) == HELPER_ALIVE) { unlock(async_helper_lock); put_thread(new); return 0; } async_helper_thread = new; atomic_xchg(&async_helper_state, HELPER_ALIVE); unlock(async_helper_lock); PAL_HANDLE handle = thread_create(shim_async_helper, new, 0); if (!handle) { ret = -PAL_ERRNO; lock(async_helper_lock); async_helper_thread = NULL; atomic_xchg(&async_helper_state, HELPER_NOTALIVE); unlock(async_helper_lock); put_thread(new); return ret; } new->pal_handle = handle; return 0; } int terminate_async_helper (void) { if (atomic_read(&async_helper_state) != HELPER_ALIVE) return 0; lock(async_helper_lock); atomic_xchg(&async_helper_state, HELPER_NOTALIVE); unlock(async_helper_lock); DkEventSet(async_helper_event); return 0; }