/*
   *  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;
  }
Example #2
0
  /**
   * Sets the properties of a new task, passed as an argument.
   *
   * <p>Creates a new thread list, sets TaskLive status and creation time, creates and opens the
   * task's swap file of the size equal to the size (in bytes) of the addressable virtual memory.
   *
   * @return task or null @OSPProject Tasks
   */
  public static TaskCB do_create() {
    TaskCB new_task = new TaskCB(); // criação da nova task
    HClock new_clock = new HClock(); // instância do relógio do sistema
    String path = SwapDeviceMountPoint + String.valueOf(new_task.getID()); // path do swap file

    new_task.new_thread = new ArrayList<ThreadCB>();
    new_task.new_port = new ArrayList<PortCB>();
    new_task.new_file = new ArrayList<OpenFile>();

    new_task.setPageTable(new PageTable(new_task));

    new_task.setCreationTime(
        new_clock.get()); // usa a instância do relógio, para setar o horário de criação da task

    new_task.setStatus(TaskLive); // seta o estado atual da task (TaskLive)
    new_task.setPriority(1); // como não foi especificado nenhuma prioridade na descrição, usamos 1

    new_task.new_swap.create(
        path,
        (int)
            Math.pow(
                2.0D,
                MMU.getVirtualAddressBits())); // tamanho da página deve ser 2^(número de bits do
    // ambiente)
    OpenFile save_swap = OpenFile.open(path, new_task); // abrimos o swap file

    new_task.setSwapFile(
        save_swap); // dizemos que o swap criado acima deve ser alocado a nossa task

    ThreadCB.create(new_task); // criamos a thread inicial

    if (save_swap == null) { // verificamos se o swap foi criado com sucesso
      new_task
          .new_thread
          .get(0)
          .dispatch(); // caso não tenha, dispachamos a thread criada acima (posição 0 do vetor)
      return null; // e retornamos uma task nula
    }

    return new_task; // se tudo ocorreu como esperado, retornamos a task criada inicialmente
  }
 /**
  * This basically only needs to reset the times and dispatch another process. @OSPProject Threads
  */
 public void do_handleInterrupt() {
   // your code goes here
   ThreadCB.dispatch();
 }
  /**
   * 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.
  }