/* * Create a thread object using the default constructor ThreadCB() and * associate this newly created thread with a task (provided as an * argument to the do create() method). * */ public static ThreadCB do_create(TaskCB task) { // It is important to keep in mind that each time control is transferred to // the operating system, it is seen as an opportunity to schedule a thread // to run. Therefore, regardless of whether the new thread was created // successfully, the dispatcher must be called (or else a warning will be issued) // There is a global constant (in IflThreadCB), called MaxThreadsPerTask. // If this number of threads per task is exceeded, no new thread should be // created for that task, and null should be returned. if (MaxThreadsPerTask <= task.getThreadCount()) { dispatch(); return null; } // To link a thread to its task, the method addThread() of class // IflTaskCB should be used and the thread’s task must be set using // the method setTask() of IflThreadCB.*/ ThreadCB thread = new ThreadCB(); // null should also be returned if addThread() returns FAILURE. You can // find out the number of threads a task currently has by calling the // method getThreadCount() on that task. if (task.addThread(thread) == FAILURE) { dispatch(); return null; } // Apparently I needed to add this after testing. 2 hours of debugging. Wow task.addThread(thread); thread.setTask(task); // If priority scheduling needs to be implemented, the do create() method must cor- // rectly assign the thread’s initial priority. The actual value of the priority depends // on the particular scheduling policy used. OSP 2 provides methods for setting and // querying the priority of both tasks and threads. The methods are setPriority() and // getPriority() in classes TaskCB and ThreadCB, respectively. thread.setPriority(randomGeneratedNumber.nextInt((10 - 1) + 1)); // Finally, the status of the new thread should be set to ThreadReady // and it should be placed in the ready queue. thread.setStatus(ThreadReady); // If priority is above below 10% or equal to move into the low priority thread. if (task.getPriority() <= 1) ThreadCB.lowPriorityQueue.addElement(thread); else ThreadCB.highPriorityQueue.addElement(thread); // If all is well, the thread object created by this method should be returned. dispatch(); return thread; }
/** * Selects a thread from the run queue and dispatches it. * * <p>If there is just one theread ready to run, reschedule the thread currently on the processor. * * <p>In addition to setting the correct thread status it must update the PTBR. * * @return SUCCESS or FAILURE @OSPProject Threads */ public static int do_dispatch() { // Context switching. Passing control of the CPU from one thread to another is called // context switching. This has two distinct phases: preempting the currently running // thread and dispatching another thread. Preempting a thread involves the following steps: // 1. Changing of the state of the currently running thread from ThreadRunning to whatever // is appropriate in the particular case. For instance, if a thread looses control of the CPU // because it has to wait for I/O, then its status might become ThreadWaiting. If the // thread has used up its time quantum, then the new status should become ThreadReady. // Changing the status is done using the method setStatus() described later. // This step requires knowing the currently running thread. The call MMU.getPTBR() // (described below) lets you find the page table of the currently scheduled task. The // task itself can be obtained by applying the method getTask() to this page table. The // currently running thread is then determined using the method getCurrentThread(). // 2. Setting the page table base register (PTBR) to null. PTBR is a register of the // memory management unit (a piece of hardware that controls memory access), or MMU, // which always points to the page table of the running thread. This is how MMU knows // which page table to use for address translation. In OSP 2 , PTBR can be accessed // using the static methods getPTBR() and setPTBR() of class MMU. // 3. Changing the current thread of the previously running task to null. The current // thread of a task can be set using the method setCurrentThread(). // When a thread, t, is selected to run, it must be given control of the CPU. This is called // dispatching a thread and involves a sequence of steps similar to the steps for preempting // threads: // 1. The status of t is changed from ThreadReady to ThreadRunning. // 2. PTBR is set to point to the page table of the task that owns t. The page table of a // task can be obtained via the method getPageTable(), and the PTBR is set using the // method setPTBR() of class MMU. // 3. The current thread of the above task must be set to t using the method setCurrentThread(). // In practice, context switch is performed as part of the dispatch() operation, and steps 2 // and 3 in the first list above can be combined with steps 2 and 3 of the second list. // In the degenerate case, when the running thread, t, is suspended and no other thread // takes control of the CPU, consider it as a context switch from t to the imaginary “null // thread.” Likewise, if no process is running and the dispatcher chooses some ready-to-run // thread for execution, we can view it as a context switch from the null thread to t. ThreadCB queueThread = null; // First, some thread should be chosen from the ready queue (or the currently running // thread can be allowed to continue). // highPriorityQueue is not empty and it has been selected to retrieve queue item randomly // Pick an item from a queue. First case is when both queues are not empty. Choose a thread // from one of the queues where 90% of the time the one selected will be from highPriority. if (!(ThreadCB.lowPriorityQueue.isEmpty()) && !(ThreadCB.highPriorityQueue.isEmpty())) { if ((randomGeneratedNumber.nextInt((10 - 1) + 1) > 1)) queueThread = ThreadCB.highPriorityQueue.elementAt(0); else queueThread = ThreadCB.lowPriorityQueue.elementAt(0); } // Case 2 only low priority has anything in its queue. Thread will be selected from the // lowPriority queue. else if (!(ThreadCB.lowPriorityQueue.isEmpty()) && ThreadCB.highPriorityQueue.isEmpty()) queueThread = ThreadCB.lowPriorityQueue.elementAt(0); // High priority queue is the only queue containing any threads. Thread chosen from it. else if (ThreadCB.lowPriorityQueue.isEmpty() && !(ThreadCB.highPriorityQueue.isEmpty())) queueThread = ThreadCB.highPriorityQueue.elementAt(0); // Process currently in CPU and in a queue. Context switch with use of timer at end of time // quantum. if (MMU.getPTBR() != null && queueThread != null /*(!(ThreadCB.highPriorityQueue.isEmpty()) || !(ThreadCB.lowPriorityQueue.isEmpty()))*/) { // Context switching with steps 1-3 given above. We already know that a thread is running on // the CPU from getPTBR() // so we need to switch the currently running thread with the one on the queue after time // quantum has ended. if (HTimer.get() <= 0) { // Add the thread currently on the CPU to the end of the readyQueue if (MMU.getPTBR().getTask().getPriority() > 1) ThreadCB.highPriorityQueue.addElement(MMU.getPTBR().getTask().getCurrentThread()); else ThreadCB.lowPriorityQueue.addElement(MMU.getPTBR().getTask().getCurrentThread()); MMU.getPTBR().getTask().getCurrentThread().setStatus(ThreadReady); MMU.getPTBR().getTask().setCurrentThread(null); MMU.setPTBR(null); MMU.setPTBR(queueThread.getTask().getPageTable()); queueThread.getTask().setCurrentThread(queueThread); MMU.getPTBR().getTask().getCurrentThread().setStatus(ThreadRunning); // Which queue contains the thread? Remove recently set thread to CPU from readyQueue if (ThreadCB.highPriorityQueue.contains(queueThread)) ThreadCB.highPriorityQueue.remove(0); else ThreadCB.lowPriorityQueue.remove(0); } } // There is a thread running in the CPU, but there is nothing left in the queues. Continue // running thread. Return SUCCESS // No switch needed as only thread is already in the CPU. else if ( /*ThreadCB.highPriorityQueue.isEmpty() && ThreadCB.lowPriorityQueue.isEmpty()*/ queueThread == null && !(MMU.getPTBR() == null)) return SUCCESS; // Process is in a queue but not in the CPU else if (MMU.getPTBR() == null && queueThread != null /*(!(ThreadCB.highPriorityQueue.isEmpty()) || !(ThreadCB.lowPriorityQueue.isEmpty()))*/) { MMU.setPTBR(queueThread.getTask().getPageTable()); MMU.getPTBR().getTask().setCurrentThread(queueThread); MMU.getPTBR().getTask().getCurrentThread().setStatus(ThreadRunning); // Which queue contains the thread? Remove recently set thread to CPU from readyQueue if (ThreadCB.highPriorityQueue.contains(queueThread)) ThreadCB.highPriorityQueue.remove(queueThread); else ThreadCB.lowPriorityQueue.remove(queueThread); return SUCCESS; } // No thread is running and there are no threads in the queues. Return FAILURE else if ( /*ThreadCB.highPriorityQueue.isEmpty() && ThreadCB.lowPriorityQueue.isEmpty()*/ queueThread == null && MMU.getPTBR() == null) return FAILURE; return FAILURE; // None of the above cases were true. Something went wrong. }