static void check_cancel(void) { /* cancellation point */ struct coopth_thrdata_t *thdata = co_get_data(co_current()); if (thdata->cancelled) longjmp(thdata->exit_jmp, COOPTH_JMP_CANCEL); }
void coopth_exit(void) { struct coopth_thrdata_t *thdata; assert(_coopth_is_in_thread()); thdata = co_get_data(co_current()); longjmp(thdata->exit_jmp, COOPTH_JMP_EXIT); }
int coopth_get_tid(void) { struct coopth_thrdata_t *thdata; assert(_coopth_is_in_thread()); thdata = co_get_data(co_current()); return *thdata->tid; }
static void ensure_attached(void) { struct coopth_thrdata_t *thdata = co_get_data(co_current()); if (!thdata->attached) { dosemu_error("Not allowed for detached thread\n"); leavedos(2); } }
void *coopth_pop_user_data_cur(void) { struct coopth_thrdata_t *thdata; assert(_coopth_is_in_thread()); thdata = co_get_data(co_current()); assert(thdata->udata_num > 0); return thdata->udata[--thdata->udata_num]; }
void coopth_push_user_data_cur(void *udata) { struct coopth_thrdata_t *thdata; assert(_coopth_is_in_thread()); thdata = co_get_data(co_current()); assert(thdata->udata_num < MAX_UDATA); thdata->udata[thdata->udata_num++] = udata; }
int coopth_set_cleanup_handler(coopth_func_t func, void *arg) { struct coopth_thrdata_t *thdata; assert(_coopth_is_in_thread()); thdata = co_get_data(co_current()); thdata->clnup.func = func; thdata->clnup.arg = arg; return 0; }
static void ensure_attached(void) { struct coopth_thrdata_t *thdata = co_get_data(co_current(co_handle)); if (!thdata->attached) { dosemu_error("Not allowed for detached thread %i, %s\n", *thdata->tid, coopthreads[*thdata->tid].name); leavedos(2); } }
void coopth_detach(void) { struct coopth_thrdata_t *thdata; assert(_coopth_is_in_thread()); thdata = co_get_data(co_current()); ensure_single(thdata); if (!thdata->attached) return; switch_state(COOPTH_DETACH); }
/* 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); }
int coopth_set_post_handler(coopth_func_t func, void *arg) { struct coopth_thrdata_t *thdata; assert(_coopth_is_in_thread()); thdata = co_get_data(co_current()); assert(thdata->posth_num < MAX_POST_H); thdata->post[thdata->posth_num].func = func; thdata->post[thdata->posth_num].arg = arg; thdata->posth_num++; return 0; }
/* 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); }
static int is_detached(void) { struct coopth_thrdata_t *thdata = co_get_data(co_current()); assert(thdata); return (!thdata->attached); }
static void switch_state(enum CoopthRet ret) { struct coopth_thrdata_t *thdata = co_get_data(co_current()); thdata->ret = ret; co_resume(); }
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); }