static void coopth_callf_chk(struct coopth_t *thr, struct coopth_per_thread_t *pth) { if (!thr->ctxh.pre) dosemu_error("coopth: unsafe attach\n"); coopth_callf(thr, pth); }
static void check_tid(int tid) { if (tid < 0 || tid >= coopth_num) { dosemu_error("Wrong tid\n"); leavedos(2); } }
static void do_awake(struct coopth_per_thread_t *pth) { if (pth->st.state != COOPTHS_SLEEPING) { dosemu_error("wakeup on non-sleeping thread %i\n", *pth->data.tid); return; } pth->st = SW_ST(AWAKEN); }
void coopth_sleep(void) { assert(_coopth_is_in_thread()); if (!is_detached() && !isset_IF()) dosemu_error("sleep with interrupts disabled\n"); switch_state(COOPTH_SLEEP); check_cancel(); }
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_wait(void) { assert(_coopth_is_in_thread()); ensure_attached(); if (!isset_IF()) dosemu_error("sleep with interrupts disabled\n"); switch_state(COOPTH_WAIT); check_cancel(); }
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); } }
int register_render_system(struct render_system *render_system) { if (Render) { dosemu_error("multiple gfx renderers not supported, please report a bug!\n"); return 0; } Render = render_system; return 1; }
static void do_awake(struct coopth_per_thread_t *pth) { if (pth->st.state != COOPTHS_SLEEPING) { dosemu_error("wakeup on non-sleeping thread %i\n", *pth->data.tid); return; } pth->st = SW_ST(AWAKEN); if (!pth->data.attached) pth->quick_sched = 1; // optimize DPMI switches }
static int __coopth_is_in_thread(int warn, const char *f) { if (!thread_running && warn) { static int warned; if (!warned) { warned = 1; dosemu_error("Coopth: %s: not in thread!\n", f); } } return thread_running; }
static void render_text_lock(void) { if (!render_text || text_locked) { dosemu_error("render not in text mode!\n"); leavedos(95); return; } text_locked++; if (!text_really_locked) { dst_image = render_lock(); text_really_locked = 1; } }
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; }
static void coopth_callf(struct coopth_t *thr, struct coopth_per_thread_t *pth) { assert(!pth->data.attached); if (thr->ctxh.pre) thr->ctxh.pre(thr->tid); if (ctx_is_valid) { int ok = ctx_is_valid(); if (!ok) dosemu_error("coopth: unsafe context switch\n"); } pth->ret_cs = SREG(cs); pth->ret_ip = LWORD(eip); SREG(cs) = BIOS_HLT_BLK_SEG; LWORD(eip) = thr->hlt_off; threads_joinable++; pth->data.attached = 1; }
static void ensure_single(struct coopth_thrdata_t *thdata) { struct coopth_t *thr = &coopthreads[*thdata->tid]; if (thr->cur_thr != 1) dosemu_error("coopth: nested=%i (expected 1)\n", thr->cur_thr); }
int coopth_start(int tid, coopth_func_t func, void *arg) { struct coopth_t *thr; struct coopth_per_thread_t *pth; int tn; check_tid(tid); thr = &coopthreads[tid]; assert(thr->tid == tid); if (thr->cur_thr >= MAX_COOP_RECUR_DEPTH) { int i; error("Coopthreads recursion depth exceeded, %s off=%x\n", thr->name, thr->off); for (i = 0; i < thr->cur_thr; i++) { error("\tthread %i state %i dbg %#x\n", i, thr->pth[i].st.state, thr->pth[i].dbg); } leavedos(2); return -1; } tn = thr->cur_thr++; pth = &thr->pth[tn]; if (thr->cur_thr > thr->max_thr) { size_t stk_size = COOP_STK_SIZE(); thr->max_thr = thr->cur_thr; #ifndef MAP_STACK #define MAP_STACK 0 #endif pth->stack = mmap(NULL, stk_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0); if (pth->stack == MAP_FAILED) { error("Unable to allocate stack\n"); leavedos(21); return 1; } pth->stk_size = stk_size; } pth->data.tid = &thr->tid; pth->data.attached = 0; pth->data.posth_num = 0; pth->data.sleep.func = NULL; pth->data.clnup.func = NULL; pth->data.udata_num = 0; pth->data.cancelled = 0; pth->data.left = 0; pth->args.thr.func = func; pth->args.thr.arg = arg; pth->args.thrdata = &pth->data; pth->dbg = LWORD(eax); // for debug pth->thread = co_create(coopth_thread, &pth->args, pth->stack, pth->stk_size); if (!pth->thread) { error("Thread create failure\n"); leavedos(2); return -1; } pth->st = ST(RUNNING); if (tn == 0) { assert(threads_active < MAX_ACT_THRS); active_tids[threads_active++] = tid; } else if (thr->pth[tn - 1].st.state == COOPTHS_SLEEPING) { static int logged; /* will have problems with wake-up by tid. It is possible * to do a wakeup-specific lookup, but this is nasty, and * the recursion itself is nasty too. Lets just print an * error to force the caller to create a separate thread. * vc.c does this to not sleep in the sighandling thread. */ if (!logged) { dosemu_error("thread %s recursed (%i) over sleep\n", thr->name, thr->cur_thr); logged = 1; } } threads_total++; if (!thr->detached) coopth_callf(thr, pth); return 0; }
static void __thread_run(struct coopth_t *thr, struct coopth_per_thread_t *pth) { switch (pth->st.state) { case COOPTHS_NONE: error("Coopthreads error switch to inactive thread, exiting\n"); leavedos(2); break; case COOPTHS_RUNNING: { int jr, lr; enum CoopthRet tret; /* We have 2 kinds of recursion: * * 1. (call it recursive thread invocation) * main_thread -> coopth_start(thread1_func) -> return * thread1_func() -> coopth_start(thread2_func) -> return * (thread 1 returned, became zombie) * thread2_func() -> return * thread2 joined * thread1 joined * main_thread... * * 2. (call it nested thread invocation) * main_thread -> coopth_start(thread1_func) -> return * thread1_func() -> do_int_call_back() -> * run_int_from_hlt() -> * coopth_start(thread2_func) -> return * thread2_func() -> return * thread2 joined * -> return from do_int_call_back() -> * return from thread1_func() * thread1 joined * main_thread... * * Both cases are supported here, but the nested invocation * is not supposed to be used as being too complex. * Since do_int_call_back() was converted * to coopth API, the nesting is avoided. * If not true, we print an error. */ if (joinable_running) { static int warned; if (!warned) { warned = 1; dosemu_error("Nested thread invocation detected, please fix! " "(at=%i)\n", pth->data.attached); } } jr = joinable_running; if (pth->data.attached) joinable_running++; lr = left_running; if (pth->data.left) { assert(!pth->data.attached); left_running++; } thread_running++; tret = do_run_thread(thr, pth); thread_running--; left_running = lr; joinable_running = jr; if (tret == COOPTH_WAIT && pth->data.attached) dosemu_sleep(); if (tret == COOPTH_SLEEP || tret == COOPTH_WAIT || tret == COOPTH_YIELD) { if (pth->data.sleep.func) { /* oneshot sleep handler */ pth->data.sleep.func(pth->data.sleep.arg); pth->data.sleep.func = NULL; } if (thr->sleeph.pre) thr->sleeph.pre(thr->tid); } /* normally we don't exit with RUNNING state any longer. * this was happening in prev implementations though, so * remove that assert if it ever hurts. */ assert(pth->st.state != COOPTHS_RUNNING); break; } case COOPTHS_SLEEPING: if (pth->data.attached) dosemu_sleep(); break; case COOPTHS_SWITCH: pth->st.switch_fn(thr, pth); break; } }
static void check_locked(void) { if (!render_locked) dosemu_error("render not locked!\n"); }