static int run_traverser(int (*pred)(struct coopth_per_thread_t *), void (*post)(struct coopth_per_thread_t *)) { int i; int cnt = 0; for (i = 0; i < threads_active; i++) { int tid = active_tids[i]; struct coopth_t *thr = &coopthreads[tid]; struct coopth_per_thread_t *pth = current_thr(thr); /* only run detached threads here */ if (pth->data.attached) continue; if (pth->data.left) { if (!left_running) error("coopth: switching to left thread?\n"); continue; } if (pred && !pred(pth)) continue; thread_run(thr, pth); if (post) post(pth); cnt++; } return cnt; }
void coopth_wake_up(int tid) { struct coopth_t *thr; struct coopth_per_thread_t *pth; check_tid(tid); thr = &coopthreads[tid]; pth = current_thr(thr); do_awake(pth); }
void coopth_ensure_sleeping(int tid) { struct coopth_t *thr; struct coopth_per_thread_t *pth; check_tid(tid); thr = &coopthreads[tid]; pth = current_thr(thr); assert(pth->st.state == COOPTHS_SLEEPING); }
int coopth_wants_sleep(void) { struct coopth_t *thr = on_thread(); struct coopth_per_thread_t *pth; if (!thr) return 0; pth = current_thr(thr); return (pth->st.state == COOPTHS_SLEEPING || pth->st.state == COOPTHS_SWITCH); }
/* attach some thread to current context */ void coopth_attach_to_cur(int tid) { struct coopth_t *thr; struct coopth_per_thread_t *pth; check_tid(tid); thr = &coopthreads[tid]; pth = current_thr(thr); assert(!pth->data.attached); coopth_callf(thr, pth); }
void *coopth_pop_user_data(int tid) { struct coopth_t *thr; struct coopth_per_thread_t *pth; check_tid(tid); thr = &coopthreads[tid]; pth = current_thr(thr); assert(pth->data.udata_num > 0); return pth->data.udata[--pth->data.udata_num]; }
void coopth_push_user_data(int tid, void *udata) { struct coopth_t *thr; struct coopth_per_thread_t *pth; check_tid(tid); thr = &coopthreads[tid]; pth = current_thr(thr); assert(pth->data.udata_num < MAX_UDATA); pth->data.udata[pth->data.udata_num++] = udata; }
void coopth_run_tid(int tid) { struct coopth_t *thr = &coopthreads[tid]; struct coopth_per_thread_t *pth = current_thr(thr); assert(DETACHED_RUNNING >= 0); if (DETACHED_RUNNING) return; assert(!pth->data.attached && !pth->data.left); thread_run(thr, pth); }
void coopth_cancel(int tid) { struct coopth_t *thr; struct coopth_per_thread_t *pth; check_tid(tid); thr = &coopthreads[tid]; pth = current_thr(thr); if (_coopth_is_in_thread_nowarn()) assert(tid != coopth_get_tid()); do_cancel(thr, pth); }
static void coopth_hlt(Bit16u offs, void *arg) { struct coopth_t *thr = (struct coopth_t *)arg + offs; struct coopth_per_thread_t *pth = current_thr(thr); if (!pth->data.attached) { /* someone used coopth_unsafe_detach()? */ error("HLT on detached thread\n"); leavedos(2); return; } thread_run(thr, pth); }
int coopth_unsafe_detach(int tid) { struct coopth_t *thr; struct coopth_per_thread_t *pth; check_tid(tid); dosemu_error("coopth_unsafe_detach() called\n"); thr = &coopthreads[tid]; pth = current_thr(thr); assert(pth->data.attached); /* this is really unsafe and should be used only if * the DOS side of the thread have disappeared. */ pth->data.attached = 0; return 0; }
void coopth_join(int tid, void (*helper)(void)) { struct coopth_t *thr; struct coopth_per_thread_t *pth; /* since main thread can call this, we have to use helper * function instead of just coopth_sched(). As a result, * recursion into run_vm86() can happen. Hope its safe. */ assert(!_coopth_is_in_thread_nowarn() || is_detached()); check_tid(tid); thr = &coopthreads[tid]; pth = current_thr(thr); assert(pth->data.attached); do_join(pth, helper); }
void coopth_cancel(int tid) { struct coopth_t *thr; struct coopth_per_thread_t *pth; check_tid(tid); thr = &coopthreads[tid]; pth = current_thr(thr); if (_coopth_is_in_thread_nowarn()) { if (tid == coopth_get_tid()) { assert(pth->data.left); return; } } do_cancel(thr, pth); }
/* desperate cleanup attempt, not extremely reliable */ int coopth_flush(void (*helper)(void)) { struct coopth_t *thr; assert(!_coopth_is_in_thread_nowarn() || is_detached()); while (threads_joinable) { struct coopth_per_thread_t *pth; /* the sleeping threads are unlikely to be found here. * This is mainly to flush zombies. */ thr = on_thread(); if (!thr) break; pth = current_thr(thr); assert(pth->data.attached); do_cancel(thr, pth); do_join(pth, helper); } if (threads_joinable) g_printf("Coopth: %i threads stalled\n", threads_joinable); return threads_joinable; }
void coopth_run(void) { int i; assert(DETACHED_RUNNING >= 0); if (DETACHED_RUNNING) return; for (i = 0; i < threads_active; i++) { int tid = active_tids[i]; struct coopth_t *thr = &coopthreads[tid]; struct coopth_per_thread_t *pth = current_thr(thr); /* only run detached threads here */ if (pth->data.attached) continue; if (pth->data.left) { if (!left_running) error("coopth: switching to left thread?\n"); continue; } thread_run(thr, pth); } }
void coopth_done(void) { int i, tt, itd, it; struct coopth_thrdata_t *thdata = NULL; it = _coopth_is_in_thread_nowarn(); itd = it; // assert(!it || is_detached()); if (it) { thdata = co_get_data(co_current()); assert(thdata); /* unfortunately the shutdown can run from signal handler - * in this case we can be in a joinable thread interrupted * by signal, and there is no way to leave that thread. */ if (!is_detached()) itd = 0; } /* there is no safe way to delete joinable threads without joining, * so print error only if there are also detached threads left */ if (threads_total > threads_joinable + itd) error("Coopth: not all detached threads properly shut down\n"); again: tt = threads_total; for (i = 0; i < threads_active; i++) { int tid = active_tids[i]; struct coopth_t *thr = &coopthreads[tid]; struct coopth_per_thread_t *pth = current_thr(thr); /* dont cancel own thread */ if (thdata && *thdata->tid == tid) continue; if (!pth->data.attached) { error("\ttid=%i state=%i name=\"%s\" off=%#x\n", tid, pth->st.state, thr->name, thr->off); do_cancel(thr, pth); assert(threads_total == tt - 1); /* retry the loop as the array changed */ goto again; } else { g_printf("\ttid=%i state=%i name=%s off=%#x\n", tid, pth->st.state, thr->name, thr->off); } } /* at this point all detached threads should be killed, * except perhaps current one */ assert(threads_total == threads_joinable + itd); for (i = 0; i < coopth_num; i++) { struct coopth_t *thr = &coopthreads[i]; int j; /* dont free own thread */ if (thdata && *thdata->tid == i) continue; for (j = thr->cur_thr; j < thr->max_thr; j++) { struct coopth_per_thread_t *pth = &thr->pth[j]; munmap(pth->stack, pth->stk_size); } } if (!threads_total) co_thread_cleanup(); else g_printf("coopth: leaked %i threads\n", threads_total); }