⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 threads.cpp

📁 基于DOS系统下的多任务类库。Class DOS Thread provides a framework for writing DOS applications
💻 CPP
📖 第 1 页 / 共 2 页
字号:

//--------------------------------------------------------------------------
//
//      DOSThreadManager::destroy.
//
//      This registers the destruction of a thread by decrementing the
//      number of threads, and shuts down the threading mechanism if the
//      last thread is being destroyed.
//
void interrupt DOSThreadManager::destroy ()
{
    if (--threadcount == 0)
    {   
        //--- unhook interrupt vectors
        cleanup ();

        //--- terminate main & null threads
        current = 0;
        ready->thread->state = DOSThread::TERMINATED;
        mainthread->state    = DOSThread::TERMINATED;

        //--- delete main & null threads and the delay queue
        delete mainthread;
        delete ready->thread;
        delete delayed;
    }
}

//--------------------------------------------------------------------------
//
//      DOSThreadManager::schedule.
//
//      Save the current thread and restore another one.  Do nothing
//      if there is no current thread, or if the one to be scheduled
//      is already the current thread.
//
void interrupt DOSThreadManager::schedule ()
{
    //--- disable interrupts (original state will be restored on exit)
    asm { cli; }

    //--- on a 386 or above, save the extended registers
    if (i386)
    {   PUSH_FS;
        PUSH_GS;
        PUSHAD;
    }

    //--- switch threads if necessary
    if (current != ready->next && current != 0)
    {   
        //--- set time for end of timeslice
        nextslice = *currtime + slicesize;

        //--- save current thread's stack pointer
        current->stkptr = (unsigned far*) MK_FP(_SS,_SP);

        //--- select new current thread
        current = ready->next;

        //--- restore its stack (other registers will be restored on exit)
        _SS = FP_SEG (current->stkptr);
        _SP = FP_OFF (current->stkptr);
    }

    //--- on a 386 or above, restore the extended registers
    if (i386)
    {   POPAD;
        POP_GS;
        POP_FS;
    }
}

//--------------------------------------------------------------------------
//
//      DOSThread::DOSThread.
//
//      Construct a new thread.  All new threads are kept terminated
//      until it is certain they can be started, and are then left in
//      limbo until they are explicitly started using "run".
//
DOSThread::DOSThread (unsigned stacksize)
    : stack (mainsetup ? 0 :
             new char [stacksize > MIN_STACK ? stacksize : MIN_STACK]),
      entry (new DOSThreadManager (this)),
      state (TERMINATED)
{
    //--- leave thread terminated if any allocation failures have occurred
    if (!mainsetup && stack == 0)
        return;                         // stack not allocated
    if (entry == 0)
        return;                         // thread queue entry not allocated
        
    //--- register thread creation
    DOSThreadManager::create ();

    //--- initialise new thread (for all but main thread)
    if (!mainsetup)
    {
        //--- set up stack pointer
        entry->stkptr = (unsigned*)(stack + stacksize);

        //--- create initial stack
        asm { sti; }                            // ensure interrupts enabled!
        *--(DOSThread**)(entry->stkptr) = this; // parameter for "start"
        entry->stkptr -= 2;                     // dummy return address
        *--(entry->stkptr) = _FLAGS;            // flags
        *--(entry->stkptr) = FP_SEG (&DOSThreadManager::start);   // cs
        *--(entry->stkptr) = FP_OFF (&DOSThreadManager::start);   // ip
        entry->stkptr -= 5;                     // ax, bx, cx, dx, es
        *--(entry->stkptr) = _DS;               // ds
        entry->stkptr -= 2;                     // si, di
        *--(entry->stkptr) = _BP;               // bp

        //--- stack extended registers on a 386 or above
        if (i386)
            entry->stkptr -= 18;                // 8 x 32-bit regs, fs, gs
    }

    //--- allow thread to live (but don't move it into any queue)
    state = CREATED;
}

//--------------------------------------------------------------------------
//
//      DOSThread::~DOSThread.
//
//      Wait for current thread to terminate, and then destroy the evidence.
//
DOSThread::~DOSThread ()
{
    //--- wait for thread to terminate (normal threads only)
    if (this != mainthread && this != ready->thread)
        wait ();

    //--- delete associated structures
    delete entry;
    delete stack;

    //--- register thread destruction (normal threads only)
    if (this != mainthread && this != ready->thread)
        DOSThreadManager::destroy ();
}


//--------------------------------------------------------------------------
//
//      DOSThread::wait.
//
//      Wait for thread to terminate.  This is needed to allow destructors
//      to avoid destroying threads while they are still running.
//
void DOSThread::wait ()
{
    //--- make sure a thread is not trying to wait on itself
    if (this == current->thread)
        return;

    //--- terminate task if it hasn't started yet
    if (state == CREATED)
        state = TERMINATED;

    //--- wait for thread to terminate
    while (state != TERMINATED)
        pause ();
}

//--------------------------------------------------------------------------
//
//      DOSThread::userbreak.
//
//      This function returns the value of the flag which indicates if
//      control-break has been pressed.
//
int DOSThread::userbreak ()
{
    return breakflag;
}


//--------------------------------------------------------------------------
//
//      DOSThread::cancelbreak.
//
//      This function resets the flag which indicates if control-break has
//      been pressed.  It also returns the original value of the flag.
//
int DOSThread::cancelbreak ()
{
    DISABLE;
    int b = breakflag;
    breakflag = 0;
    ENABLE;

    return b;
}


//--------------------------------------------------------------------------
//
//      DOSThread::run.
//
//      Start a new thread running.
//
int DOSThread::run ()
{
    //--- error if thread is not newly created
    if (state != CREATED)
        return 0;

    //--- make thread ready to run and start it running
    entry->move (ready->next, READY);
    DOSThreadManager::schedule ();
    return 1;
}


//--------------------------------------------------------------------------
//
//      DOSThread::terminate.
//
//      Immediately terminate a thread.  The thread is detached from its
//      current queue ready to be destroyed.
//
void DOSThread::terminate ()
{
    entry->move (0, TERMINATED);
    DOSThreadManager::schedule ();
}

//--------------------------------------------------------------------------
//
//      DOSThread::delay.
//
//      Delay for "n" clock ticks.  The current thread is moved to the
//      correct position in the "delayed" queue and will be woken up
//      by the timer interrupt handler.
//
void DOSThread::delay (int n)
{
    //--- don't delay if no current thread, or if tick count is non-positive
    if (current == 0 || n <= 0)
        return;

    //--- set wake-up time
    current->wakeup = *currtime + n;

    //--- find correct position in delay queue
    DOSThreadManager* t = delayed->next;
    while (t != delayed && t->wakeup < current->wakeup)
        t = t->next;

    //--- put thread in delay queue and reschedule
    current->move (t, DELAYED);
    DOSThreadManager::schedule ();
}


//--------------------------------------------------------------------------
//
//      DOSThread::pause.
//
//      Move the current thread to the back of the ready queue.
//
void DOSThread::pause ()
{
    //--- don't pause if no current thread
    if (current == 0)
        return;

    //--- move current thread to back of ready queue and reschedule
    current->move (ready, READY);
    DOSThreadManager::schedule ();
}

 
//--------------------------------------------------------------------------
//
//      DOSThread::timeslice.
//
//      Set the timeslice.  This is ignored once the first thread has
//      been created.
//
void DOSThread::timeslice (unsigned n)
{
    if (threadcount == 0)
        slicesize = n;
}

//--------------------------------------------------------------------------
//
//      Constructors and destructors for DOSMonitorQueue and DOSMonitor.
//
//      These are trivial but involve knowledge of DOSThreadManager, and
//      so cannot go in the header file.
//
DOSMonitorQueue::DOSMonitorQueue ()     : queue (new DOSThreadManager)  { }
DOSMonitorQueue::~DOSMonitorQueue ()    { delete queue; }
DOSMonitor::DOSMonitor ()               : lockq (new DOSThreadManager),
                                          lockholder (0)                { }
DOSMonitor::~DOSMonitor ()              { delete lockq; }


//--------------------------------------------------------------------------
//
//      DOSMonitor::lock.
//
//      This ensures that only one thread at a time is executing in a
//      monitor object, and must be called before any other monitor
//      functions (suspend, resume or unlock) can be used.  If another
//      (non-terminated) thread already holds the lock, the current
//      thread is suspended by being placed at the back of the monitor
//      lock queue.  When the current lock holder calls "unlock", all
//      queued threads are made ready, and the thread can then attempt
//      to acquire the lock again.  The current thread must not be the
//      current lock holder (i.e. no recursive monitor calls are allowed).
//
void DOSMonitor::lock ()
{
    //--- check for errors
    if (lockq == 0)
        error (NEW_FAIL);               // lock queue doesn't exist
    if (current == 0)
        error (NO_THREAD);              // no current thread
    if (lockholder == current)
        error (LOCK_FAIL);              // current thread already holds lock

    DISABLE;

    //--- suspend repeatedly until lock is available
    while (lockholder != 0 &&
           lockholder->thread->status() != DOSThread::TERMINATED)
    {   current->move (lockq, DOSThread::WAITING);
        DOSThreadManager::schedule ();
    }

    //--- make current thread the new lock holder
    lockholder = current;

    ENABLE;
}

//--------------------------------------------------------------------------
//
//      DOSMonitor::unlock.
//
//      The calling thread must be the current lock holder.  The lock is
//      released, and all threads requesting a lock are moved to the front
//      of the ready queue.
//
void DOSMonitor::unlock ()
{
    //--- check for errors
    if (lockholder != current)
        error (UNLOCK_FAIL);            // current thread isn't the lock holder

    DISABLE;

    //--- release lock
    lockholder = 0;

    //--- make pending threads ready to run
    DOSThreadManager* t = ready->next;
    while (lockq->next != lockq)
        lockq->next->move (t, DOSThread::READY);

    //--- reschedule
    DOSThreadManager::schedule ();

    ENABLE;
}

//--------------------------------------------------------------------------
//
//      DOSMonitor::suspend.
//
//      This function allows a monitor to suspend the current thread on
//      a monitor queue until another thread resumes it.  The current
//      thread must be the current lock holder for the monitor.  The
//      lock is released while the thread is suspended.
//
void DOSMonitor::suspend (DOSMonitorQueue& q)
{
    //--- check for errors
    if (q.queue == 0)
        error (NEW_FAIL);               // monitor queue doesn't exist
    if (lockholder != current)
        error (SUSPEND_FAIL);           // current thread isn't the lock holder

    //--- release lock
    unlock ();

    //--- suspend current thread and reschedule
    current->move (q.queue, DOSThread::SUSPENDED);
    DOSThreadManager::schedule ();

    //--- lock the monitor again
    lock ();
}


//--------------------------------------------------------------------------
//
//      DOSMonitor::resume.
//
//      This function allows a monitor to resume any threads suspended
//      on a monitor queue.  The current thread must be the current lock
//      holder for the monitor.
//
void DOSMonitor::resume (DOSMonitorQueue& q)
{
    //--- check for errors
    if (q.queue == 0)
        error (NEW_FAIL);               // monitor queue doesn't exist
    if (lockholder != current)
        error (RESUME_FAIL);            // current thread isn't the lock holder

    //--- make any suspended threads wait for the lock to be released
    DOSThreadManager* lq = lockq->next;
    while (q.queue != q.queue->next)
        q.queue->next->move (lq, DOSThread::WAITING);
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -