📄 pth_lib.c
字号:
} } /* finally insert it into the "new queue" where the scheduler will pick it up for dispatching */ if (func != pth_scheduler) { t->state = PTH_STATE_NEW; pth_pqueue_insert(&pth_NQ, t->prio, t); } pth_debug1("pth_spawn: leave"); /* the returned thread id is just the pointer to the thread control block... */ return t;}/* returns the current thread */pth_t pth_self(void){ return pth_current;}/* raise a signal for a thread */int pth_raise(pth_t t, int sig){ struct sigaction sa; if (t == NULL || t == pth_current || (sig < 0 || sig > PTH_NSIG)) return pth_error(FALSE, EINVAL); if (sig == 0) /* just test whether thread exists */ return pth_thread_exists(t); else { /* raise signal for thread */ if (sigaction(sig, NULL, &sa) != 0) return FALSE; if (sa.sa_handler == SIG_IGN) return TRUE; /* fine, nothing to do, sig is globally ignored */ if (!sigismember(&t->sigpending, sig)) { sigaddset(&t->sigpending, sig); t->sigpendcnt++; } pth_yield(t); return TRUE; }}/* check whether a thread exists */intern int pth_thread_exists(pth_t t){ if (!pth_pqueue_contains(&pth_NQ, t)) if (!pth_pqueue_contains(&pth_RQ, t)) if (!pth_pqueue_contains(&pth_WQ, t)) if (!pth_pqueue_contains(&pth_SQ, t)) if (!pth_pqueue_contains(&pth_DQ, t)) return pth_error(FALSE, ESRCH); /* not found */ return TRUE;}/* cleanup a particular thread */intern void pth_thread_cleanup(pth_t thread){ /* run the cleanup handlers */ if (thread->cleanups != NULL) pth_cleanup_popall(thread, TRUE); /* run the specific data destructors */ if (thread->data_value != NULL) pth_key_destroydata(thread); /* release still acquired mutex variables */ pth_mutex_releaseall(thread); return;}/* terminate the current thread */static int pth_exit_cb(void *arg){ int rc; /* BE CAREFUL HERE: THIS FUNCTION EXECUTES FROM WITHIN THE _SCHEDULER_ THREAD! */ /* calculate number of still existing threads in system. Only skipped queue is pth_DQ (dead queue). This queue does not count here, because those threads are non-detached but already terminated ones -- and if we are the only remaining thread (which also wants to terminate and not join those threads) we can signal us through the scheduled event (for which we are running as the test function inside the scheduler) that the whole process can terminate now. */ rc = 0; rc += pth_pqueue_elements(&pth_NQ); rc += pth_pqueue_elements(&pth_RQ); rc += pth_pqueue_elements(&pth_WQ); rc += pth_pqueue_elements(&pth_SQ); if (rc == 1 /* just our main thread */) return TRUE; else return FALSE;}void pth_exit(void *value){ pth_event_t ev; pth_debug2("pth_exit: marking thread \"%s\" as dead", pth_current->name); /* the main thread is special, because its termination would terminate the whole process, so we have to delay its termination until it is really the last thread */ if (pth_current == pth_main) { if (!pth_exit_cb(NULL)) { ev = pth_event(PTH_EVENT_FUNC, pth_exit_cb); pth_wait(ev); pth_event_free(ev, PTH_FREE_THIS); } } /* execute cleanups */ pth_thread_cleanup(pth_current); if (pth_current != pth_main) { /* * Now mark the current thread as dead, explicitly switch into the * scheduler and let it reap the current thread structure; we can't * free it here, or we'd be running on a stack which malloc() regards * as free memory, which would be a somewhat perilous situation. */ pth_current->join_arg = value; pth_current->state = PTH_STATE_DEAD; pth_debug2("pth_exit: switching from thread \"%s\" to scheduler", pth_current->name); pth_mctx_switch(&pth_current->mctx, &pth_sched->mctx); } else { /* * main thread is special: exit the _process_ * [double-casted to avoid warnings because of size] */ pth_kill(); exit((int)((long)value)); } /* NOTREACHED */ abort();}/* waits for the termination of the specified thread */int pth_join(pth_t tid, void **value){ pth_event_t ev; static pth_key_t ev_key = PTH_KEY_INIT; pth_debug2("pth_join: joining thread \"%s\"", tid == NULL ? "-ANY-" : tid->name); if (tid == pth_current) return pth_error(FALSE, EDEADLK); if (tid != NULL && !tid->joinable) return pth_error(FALSE, EINVAL); if (pth_ctrl(PTH_CTRL_GETTHREADS) == 1) return pth_error(FALSE, EDEADLK); if (tid == NULL) tid = pth_pqueue_head(&pth_DQ); if (tid == NULL || (tid != NULL && tid->state != PTH_STATE_DEAD)) { ev = pth_event(PTH_EVENT_TID|PTH_UNTIL_TID_DEAD|PTH_MODE_STATIC, &ev_key, tid); pth_wait(ev); } if (tid == NULL) tid = pth_pqueue_head(&pth_DQ); if (tid == NULL || (tid != NULL && tid->state != PTH_STATE_DEAD)) return pth_error(FALSE, EIO); if (value != NULL) *value = tid->join_arg; pth_pqueue_delete(&pth_DQ, tid); pth_tcb_free(tid); return TRUE;}/* delegates control back to scheduler for context switches */int pth_yield(pth_t to){ pth_pqueue_t *q = NULL; pth_debug2("pth_yield: enter from thread \"%s\"", pth_current->name); /* a given thread has to be new or ready or we ignore the request */ if (to != NULL) { switch (to->state) { case PTH_STATE_NEW: q = &pth_NQ; break; case PTH_STATE_READY: q = &pth_RQ; break; default: q = NULL; } if (q == NULL || !pth_pqueue_contains(q, to)) return pth_error(FALSE, EINVAL); } /* give a favored thread maximum priority in his queue */ if (to != NULL && q != NULL) pth_pqueue_favorite(q, to); /* switch to scheduler */ if (to != NULL) pth_debug2("pth_yield: give up control to scheduler " "in favour of thread \"%s\"", to->name); else pth_debug1("pth_yield: give up control to scheduler"); pth_mctx_switch(&pth_current->mctx, &pth_sched->mctx); pth_debug1("pth_yield: got back control from scheduler"); pth_debug2("pth_yield: leave to thread \"%s\"", pth_current->name); return TRUE;}/* suspend a thread until its again manually resumed */int pth_suspend(pth_t t){ pth_pqueue_t *q; if (t == NULL) return pth_error(FALSE, EINVAL); if (t == pth_sched || t == pth_current) return pth_error(FALSE, EPERM); switch (t->state) { case PTH_STATE_NEW: q = &pth_NQ; break; case PTH_STATE_READY: q = &pth_RQ; break; case PTH_STATE_WAITING: q = &pth_WQ; break; default: q = NULL; } if (q == NULL) return pth_error(FALSE, EPERM); if (!pth_pqueue_contains(q, t)) return pth_error(FALSE, ESRCH); pth_pqueue_delete(q, t); pth_pqueue_insert(&pth_SQ, PTH_PRIO_STD, t); pth_debug2("pth_suspend: suspend thread \"%s\"\n", t->name); return TRUE;}/* resume a previously suspended thread */int pth_resume(pth_t t){ pth_pqueue_t *q; if (t == NULL) return pth_error(FALSE, EINVAL); if (t == pth_sched || t == pth_current) return pth_error(FALSE, EPERM); if (!pth_pqueue_contains(&pth_SQ, t)) return pth_error(FALSE, EPERM); pth_pqueue_delete(&pth_SQ, t); switch (t->state) { case PTH_STATE_NEW: q = &pth_NQ; break; case PTH_STATE_READY: q = &pth_RQ; break; case PTH_STATE_WAITING: q = &pth_WQ; break; default: q = NULL; } pth_pqueue_insert(q, PTH_PRIO_STD, t); pth_debug2("pth_resume: resume thread \"%s\"\n", t->name); return TRUE;}/* switch a filedescriptor's I/O mode */int pth_fdmode(int fd, int newmode){ int fdmode; int oldmode; /* retrieve old mode (usually a very cheap operation) */ if ((fdmode = fcntl(fd, F_GETFL, NULL)) == -1) oldmode = PTH_FDMODE_ERROR; else if (fdmode & O_NONBLOCKING) oldmode = PTH_FDMODE_NONBLOCK; else oldmode = PTH_FDMODE_BLOCK; /* set new mode (usually a more expensive operation) */ if (oldmode == PTH_FDMODE_BLOCK && newmode == PTH_FDMODE_NONBLOCK) fcntl(fd, F_SETFL, (fdmode | O_NONBLOCKING)); if (oldmode == PTH_FDMODE_NONBLOCK && newmode == PTH_FDMODE_BLOCK) fcntl(fd, F_SETFL, (fdmode & ~(O_NONBLOCKING))); /* return old mode */ return oldmode;}/* wait for specific amount of time */int pth_nap(pth_time_t naptime){ pth_time_t until; pth_event_t ev; static pth_key_t ev_key = PTH_KEY_INIT; if (pth_time_cmp(&naptime, PTH_TIME_ZERO) == 0) return pth_error(FALSE, EINVAL); pth_time_set(&until, PTH_TIME_NOW); pth_time_add(&until, &naptime); ev = pth_event(PTH_EVENT_TIME|PTH_MODE_STATIC, &ev_key, until); pth_wait(ev); return TRUE;}/* runs a constructor once */int pth_once(pth_once_t *oncectrl, void (*constructor)(void *), void *arg){ if (oncectrl == NULL || constructor == NULL) return pth_error(FALSE, EINVAL); if (*oncectrl != TRUE) constructor(arg); *oncectrl = TRUE; return TRUE;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -