📄 pth_sched.c
字号:
/*** GNU Pth - The GNU Portable Threads** Copyright (c) 1999-2004 Ralf S. Engelschall <rse@engelschall.com>**** This file is part of GNU Pth, a non-preemptive thread scheduling** library which can be found at http://www.gnu.org/software/pth/.**** This library is free software; you can redistribute it and/or** modify it under the terms of the GNU Lesser General Public** License as published by the Free Software Foundation; either** version 2.1 of the License, or (at your option) any later version.**** This library is distributed in the hope that it will be useful,** but WITHOUT ANY WARRANTY; without even the implied warranty of** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU** Lesser General Public License for more details.**** You should have received a copy of the GNU Lesser General Public** License along with this library; if not, write to the Free Software** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307** USA, or contact Ralf S. Engelschall <rse@engelschall.com>.**** pth_sched.c: Pth thread scheduler, the real heart of Pth*/ /* ``Recursive, adj.; see Recursive.'' -- Unknown */#include "pth_p.h"intern pth_t pth_main; /* the main thread */intern pth_t pth_sched; /* the permanent scheduler thread */intern pth_t pth_current; /* the currently running thread */intern pth_pqueue_t pth_NQ; /* queue of new threads */intern pth_pqueue_t pth_RQ; /* queue of threads ready to run */intern pth_pqueue_t pth_WQ; /* queue of threads waiting for an event */intern pth_pqueue_t pth_SQ; /* queue of suspended threads */intern pth_pqueue_t pth_DQ; /* queue of terminated threads */intern int pth_favournew; /* favour new threads on startup */intern float pth_loadval; /* average scheduler load value */static int pth_sigpipe[2]; /* internal signal occurrence pipe */static sigset_t pth_sigpending; /* mask of pending signals */static sigset_t pth_sigblock; /* mask of signals we block in scheduler */static sigset_t pth_sigcatch; /* mask of signals we have to catch */static sigset_t pth_sigraised; /* mask of raised signals */static pth_time_t pth_loadticknext;static pth_time_t pth_loadtickgap = PTH_TIME(1,0);/* initialize the scheduler ingredients */intern int pth_scheduler_init(void){ /* create the internal signal pipe */ if (pipe(pth_sigpipe) == -1) return pth_error(FALSE, errno); if (pth_fdmode(pth_sigpipe[0], PTH_FDMODE_NONBLOCK) == PTH_FDMODE_ERROR) return pth_error(FALSE, errno); if (pth_fdmode(pth_sigpipe[1], PTH_FDMODE_NONBLOCK) == PTH_FDMODE_ERROR) return pth_error(FALSE, errno); /* initialize the essential threads */ pth_sched = NULL; pth_current = NULL; /* initalize the thread queues */ pth_pqueue_init(&pth_NQ); pth_pqueue_init(&pth_RQ); pth_pqueue_init(&pth_WQ); pth_pqueue_init(&pth_SQ); pth_pqueue_init(&pth_DQ); /* initialize scheduling hints */ pth_favournew = 1; /* the default is the original behaviour */ /* initialize load support */ pth_loadval = 1.0; pth_time_set(&pth_loadticknext, PTH_TIME_NOW); return TRUE;}/* drop all threads (except for the currently active one) */intern void pth_scheduler_drop(void){ pth_t t; /* clear the new queue */ while ((t = pth_pqueue_delmax(&pth_NQ)) != NULL) pth_tcb_free(t); pth_pqueue_init(&pth_NQ); /* clear the ready queue */ while ((t = pth_pqueue_delmax(&pth_RQ)) != NULL) pth_tcb_free(t); pth_pqueue_init(&pth_RQ); /* clear the waiting queue */ while ((t = pth_pqueue_delmax(&pth_WQ)) != NULL) pth_tcb_free(t); pth_pqueue_init(&pth_WQ); /* clear the suspend queue */ while ((t = pth_pqueue_delmax(&pth_SQ)) != NULL) pth_tcb_free(t); pth_pqueue_init(&pth_SQ); /* clear the dead queue */ while ((t = pth_pqueue_delmax(&pth_DQ)) != NULL) pth_tcb_free(t); pth_pqueue_init(&pth_DQ); return;}/* kill the scheduler ingredients */intern void pth_scheduler_kill(void){ /* drop all threads */ pth_scheduler_drop(); /* remove the internal signal pipe */ close(pth_sigpipe[0]); close(pth_sigpipe[1]); return;}/* * Update the average scheduler load. * * This is called on every context switch, but we have to adjust the * average load value every second, only. If we're called more than * once per second we handle this by just calculating anything once * and then do NOPs until the next ticks is over. If the scheduler * waited for more than once second (or a thread CPU burst lasted for * more than once second) we simulate the missing calculations. That's * no problem because we can assume that the number of ready threads * then wasn't changed dramatically (or more context switched would have * been occurred and we would have been given more chances to operate). * The actual average load is calculated through an exponential average * formula. */#define pth_scheduler_load(now) \ if (pth_time_cmp((now), &pth_loadticknext) >= 0) { \ pth_time_t ttmp; \ int numready; \ numready = pth_pqueue_elements(&pth_RQ); \ pth_time_set(&ttmp, (now)); \ do { \ pth_loadval = (numready*0.25) + (pth_loadval*0.75); \ pth_time_sub(&ttmp, &pth_loadtickgap); \ } while (pth_time_cmp(&ttmp, &pth_loadticknext) >= 0); \ pth_time_set(&pth_loadticknext, (now)); \ pth_time_add(&pth_loadticknext, &pth_loadtickgap); \ }/* the heart of this library: the thread scheduler */intern void *pth_scheduler(void *dummy){ sigset_t sigs; pth_time_t running; pth_time_t snapshot; struct sigaction sa; sigset_t ss; int sig; pth_t t; /* * bootstrapping */ pth_debug1("pth_scheduler: bootstrapping"); /* mark this thread as the special scheduler thread */ pth_sched->state = PTH_STATE_SCHEDULER; /* block all signals in the scheduler thread */ sigfillset(&sigs); pth_sc(sigprocmask)(SIG_SETMASK, &sigs, NULL); /* initialize the snapshot time for bootstrapping the loop */ pth_time_set(&snapshot, PTH_TIME_NOW); /* * endless scheduler loop */ for (;;) { /* * Move threads from new queue to ready queue and optionally * give them maximum priority so they start immediately. */ while ((t = pth_pqueue_tail(&pth_NQ)) != NULL) { pth_pqueue_delete(&pth_NQ, t); t->state = PTH_STATE_READY; if (pth_favournew) pth_pqueue_insert(&pth_RQ, pth_pqueue_favorite_prio(&pth_RQ), t); else pth_pqueue_insert(&pth_RQ, PTH_PRIO_STD, t); pth_debug2("pth_scheduler: new thread \"%s\" moved to top of ready queue", t->name); } /* * Update average scheduler load */ pth_scheduler_load(&snapshot); /* * Find next thread in ready queue */ pth_current = pth_pqueue_delmax(&pth_RQ); if (pth_current == NULL) { fprintf(stderr, "**Pth** SCHEDULER INTERNAL ERROR: " "no more thread(s) available to schedule!?!?\n"); abort(); } pth_debug4("pth_scheduler: thread \"%s\" selected (prio=%d, qprio=%d)", pth_current->name, pth_current->prio, pth_current->q_prio); /* * Raise additionally thread-specific signals * (they are delivered when we switch the context) * * Situation is ('#' = signal pending): * process pending (pth_sigpending): ----#### * thread pending (pth_current->sigpending): --##--## * Result has to be: * process new pending: --###### */ if (pth_current->sigpendcnt > 0) { sigpending(&pth_sigpending); for (sig = 1; sig < PTH_NSIG; sig++) if (sigismember(&pth_current->sigpending, sig)) if (!sigismember(&pth_sigpending, sig)) kill(getpid(), sig); } /* * Set running start time for new thread * and perform a context switch to it */ pth_debug3("pth_scheduler: switching to thread 0x%lx (\"%s\")", (unsigned long)pth_current, pth_current->name); /* update thread times */ pth_time_set(&pth_current->lastran, PTH_TIME_NOW); /* update scheduler times */ pth_time_set(&running, &pth_current->lastran); pth_time_sub(&running, &snapshot); pth_time_add(&pth_sched->running, &running); /* ** ENTERING THREAD ** - by switching the machine context */ pth_current->dispatches++; pth_mctx_switch(&pth_sched->mctx, &pth_current->mctx); /* update scheduler times */ pth_time_set(&snapshot, PTH_TIME_NOW); pth_debug3("pth_scheduler: cameback from thread 0x%lx (\"%s\")", (unsigned long)pth_current, pth_current->name); /* * Calculate and update the time the previous thread was running */ pth_time_set(&running, &snapshot); pth_time_sub(&running, &pth_current->lastran); pth_time_add(&pth_current->running, &running); pth_debug3("pth_scheduler: thread \"%s\" ran %.6f", pth_current->name, pth_time_t2d(&running)); /* * Remove still pending thread-specific signals * (they are re-delivered next time) * * Situation is ('#' = signal pending): * thread old pending (pth_current->sigpending): --##--## * process old pending (pth_sigpending): ----#### * process still pending (sigstillpending): ---#-#-# * Result has to be: * process new pending: -----#-# * thread new pending (pth_current->sigpending): ---#---# */ if (pth_current->sigpendcnt > 0) { sigset_t sigstillpending; sigpending(&sigstillpending); for (sig = 1; sig < PTH_NSIG; sig++) { if (sigismember(&pth_current->sigpending, sig)) { if (!sigismember(&sigstillpending, sig)) { /* thread (and perhaps also process) signal delivered */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -