Assignment updates

11/10/22 Added error checking and clarified task creation section.

29/09/22 Added and updated config.h. Clarified some parts of the assignment.

1   Assignment

Imagine you are an owner of a building company. You order your workers to dig out the base for a new building. Your company is small and has neither excavator nor enough shovels. Therefore your diggers must borrow shovels from each other. It is not a big deal since every digger gets tired after some time of working and must take a break afterward.

In this task, you will simulate the work of diggers in the VxWorks OS. The activity of each digger will be simulated by a stand-alone task. Use a counting semaphore (see below) to simulate the repository of shovels. Every digger should carry out these actions:

  1. Take a shovel,
  2. dig for a while,
  3. return the shovel and
  4. take a break for another while.

There are two types of diggers:

  • ones who dig the ground and throw the soil out of the hole and
  • others who throw the soil from the previous group to the truck.

The latter group can only work if there is some soil dug by the former group.

images/diggers.png

Implement the following features:

  1. The number of shovels is constant (e.g. 3) but the number of diggers can be changed during runtime.
  2. After the start of the application, two diggers are working – one in the hole and one on the ground.
  3. By pressing i or I key, a new digger comes to the workplace (capital letter for upper diggers, small letter for lower ones). There will never be more than 50 diggers at once.
  4. By pressing o or O key, one digger (lower or upper) leaves the workplace. The diggers should leave the workplace in the same order as they came.
  5. Pressing the E key signals the end of working hours. It means that all diggers go home. But that does not mean that another digger cannot come back to work!

It is not necessary for the diggers to go home immediately after pressing the key. The currently working diggers can leave after getting tired, the other diggers can leave after finishing their break.

1.1   Solution guidelines

  1. Create a Downloadable Kernel Module project.

  2. Your source code should include our header file config.h. Implement all functions declared in it.

    #include "config.h"
    
    • The number of shovels is constant and set to 3 (you can use NUM_OF_SHOVELS constant).
    • Each worker should both work and rest for 50 ticks (WORK_TIME and BREAK_TIME).
  3. Entry point of the application is named CreateTasks. Inside this function you should:

    1. Initialize all variables (e.g., semaphores).
    2. Spawn one lower and one upper digger tasks.
    3. Read user input and act accordingly.
  4. Each worker has a unique task name in the form of tWorkerL%d for lower digger and tWorkerU%d for upper digger, where %d is an integer (e.g., tWorkerL0 and tWorkerU0 are created on startup).

  5. Each worker reports a change in its activity:

    • when arriving to the workplace: lower digger %d: arriving or upper digger %d: arriving
    • when working: lower digger %d: working or upper digger %d: working
    • when resting: lower digger %d: resting or upper digger %d: resting
    • when leaving the workspace: lower digger %d: leaving or upper digger %d: leaving

2   Hints

2.1   Sending input to the program

When running the application via the IDE, check Redirect I/O to Workbench Terminal in the Run/Debug Kernel Task dialog. Without that, you might have problems with entering the input, because the input is randomly consumed by your program and the shell.

2.2   Semaphores

Semaphore is a synchronization object composed of an internal variable that represents the actual state of the semaphore.

  • In VxWorks, semaphore is initialized like this:

    SEM_ID semShovels;
    semShovels = semCCreate(SEM_Q_FIFO, NUM_OF_SHOVELS);
    
    if (semShovels == NULL) {
        perror("semCCreate");
    }
    
  • a function to take a semaphore is called semTake:

    ret = semTake(semShovels, WAIT_FOREVER);
    
    if (ret == ERROR) {
        perror("semTake");
    }
    
  • a function to release a semaphore is called semGive:

    semGive(semShovels);
    

If the state of the semaphore equals to zero, then the semTake call blocks. If the state of the semaphore is greater than zero, then the call to semTake decrements the semaphore's state and does not block. The semGive call never blocks, it always increments the semaphore's state and wakes tasks (if any) waiting for the semaphore.

VxWorks OS provides three types of semaphores:

binary
The state of semaphore can take values 1 or 0.
counting
The state of semaphore can take an integer value. In this exercises, you should use counting semaphores to represent both shovels and heap.
mutex
The state of semaphore can take values 1 or 0 and it is optimized for mutually exclusive access. This semaphore must be returned (semGive) by the same task which has taken it (semTake).

Description of semaphores and examples of their use can be found in VxWorks Application Programmer's Guide (in menu Help choose Help Contents and then Wind River Documentation→Guides→Operating System→VxWorks Application Programmer's Guide→Multitasking→Semaphores) and in the introduction to given libraries (see reference manual Wind River Documentation→References→Operating System→VxWorks Application API Reference).

The following table summarizes the most common function for working with semaphores in VxWorks. Do not forget to include semLib.h header file at the beginning of your program. Header files semBLib.h, semMLib.h and semCLib.h are already included in semLib.h.

Function Library
semBCreate semBLib
semMCreate semMLib
semCCreate semCLib
semDelete semLib
semTake semLib
semGive semLib
semFlush semLib

2.3   Digger code

We recommend using the following code to represent the "lower" digger:

#include "config.h"

void LowerDigger(int n)
{
  while (1) {
    if (CHECK(semTake(semShovels, WAIT_FOREVER)) == OK) {
        printf("lower digger %d: working\n", n);
        taskDelay(WORK_TIME);
        semGive(semSoilHeap);
        semGive(semShovels);
        printf("lower digger %d: resting\n", n);
        taskDelay(BREAK_TIME);
    }
  }
}

2.4   Task creation

Tasks are created by the taskSpawn function. The parameters are:

  1. task name
  2. priority
  3. options
  4. stack size
  5. function, where the task is supposed to start
  6. arguments

You can use sprintf to construct numbered task names:

char taskName[16];
sprintf(taskName, "tWorkerL%d", i);
TASK_ID id1 = taskSpawn(taskName, 210, 0, 4096, (FUNCPTR) LowerDigger, i, 0, 0, 0, 0, 0, 0, 0, 0, 0);

if (id1 == ERROR) {
    perror("taskSpawn");
}