/* for some time coopth_leave() was implemented on top of coopth_detach(). * This appeared not the best implementation. In particular, the commit * 551371689 was needed to make leaving operation atomic, but this is * not needed for detached threads at all. While the detached threads * has a separate entry point (via coopth_run()), the left thread must * not have a separate entry point. So it appeared better to return the * special type "left" threads. * Additionally the leave operation now calls the post handler immediately. */ void coopth_leave(void) { struct coopth_thrdata_t *thdata; if (!_coopth_is_in_thread_nowarn()) return; thdata = co_get_data(co_current()); ensure_single(thdata); if (thdata->left) return; switch_state(COOPTH_LEAVE); }
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); }
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); }
/* for some time coopth_leave() was implemented on top of coopth_detach(). * This appeared not the best implementation. In particular, the commit * 551371689 was needed to make leaving operation atomic, but this is * not needed for detached threads at all. While the detached threads * has a separate entry point (via coopth_run()), the left thread must * not have a separate entry point. So it appeared better to return the * special type "left" threads. * Additionally the leave operation now calls the post handler immediately, * and it should be called from the context of the main thread. This is * the reason why coopth_leave() for detached thread cannot be a no-op. */ void coopth_leave(void) { struct coopth_thrdata_t *thdata; if (!_coopth_is_in_thread_nowarn()) return; thdata = co_get_data(co_current()); ensure_single(thdata); if (thdata->left) return; /* leaving detached thread should be atomic even wrt other detached * threads. This is needed so that DPMI cannot run concurrently with * leavedos(). * for joinable threads leaving should be atomic only wrt DOS code, * but, because of an optimization loop in run_vm86(), it is actually * also atomic wrt detached threads. */ if (!thdata->attached) thdata->atomic_switch = 1; switch_state(COOPTH_LEAVE); }
/* 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_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); }