## Description

Background

An embedded system is a computer system performing dedicated functions within a larger

mechanical or electrical system. Embedded systems range from portable devices such as

Google Glasses, to large stationary installations like traffic lights, factory controllers, and

complex systems like hybrid vehicles, and avionic. Typically, the software of an embedded

system consists of a set of tasks (threads) with timing constraints. Typical timing constraints

are release times and deadlines. A release time specifies the earliest time a task can start, and

a deadline is the latest time by which a task needs to finish. One major goal of embedded

system design is to find a feasible schedule for the task set such that all the timing constraints

are satisfied.

Task scheduler

We assume that the hardware platform of the target embedded systems is a single processor

with m identical cores, Core1, Core2, …, Corem. The task set V={v1, v2, …, vn} consists of n

independent, non-pre-emptible tasks. A non-pre-emptible task cannot be pre-empted by

another task during its execution, i.e., it runs to completion without being interrupted. Each

task vi (i=1, 2, …, n) has four attributes: a unique task name vi, an execution time ci, a release

time ri, and a deadline di (di>ri). All the execution times, the release times and deadlines are

non-negative integers. You need to design a task scheduler and implement it in C. Your task

scheduler uses EDF (Earliest Deadline First) heuristic to find a feasible schedule for a task set.

A schedule of a task set specifies when each task starts and on which core it is executed. A

feasible schedule is a schedule satisfying all the release time and deadline constraints.

The problem of finding a feasible schedule for this task scheduling problem is NP-complete. It

is widely believed that an NP-complete problem has no polynomial-time algorithm. However,

nobody can prove it.

First, we introduce two definitions: scheduling point and ready task.

• A scheduling point is a time point at which a task can be scheduled on a core, In other

words, a scheduling point is either the release time or the completion time of a task.

• A task vi (i=1, 2, …, n) is ready at a time t if t≥ri holds.

The EDF scheduling heuristic works as follows:

• At each scheduling point ti (ti≤ti+1, i=1, 2, …), among all the ready tasks, find a task

with the smallest deadline, and schedule it on an idle core such that its start time is

minimized. Ties are broken arbitrarily.

Since this task scheduling problem is NP-complete, the EDF heuristic is not guaranteed to find

a feasible schedule even if a feasible schedule exists.

Example One

Consider a set S1 of 6 independent tasks whose release times and deadlines are shown in Table

1. The target processor has two identical cores. A feasible schedule of the task set by using

EDF scheduling strategy is shown in Figure 1.

Table 1: A set S1 of 6 tasks with individual release times and deadlines

Task Execution

time

Release time Deadline

v1 4 0 4

v2 4 1 5

v3 5 3 10

v4 6 4 11

v5 4 6 13

v6 5 6 18

Core1 v1 v3 v5

Core2 v2 v4 v6

Time 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16

Figure 1: A feasible schedule for S1

Example Two

Consider a set S2 of 6 independent tasks whose release times and deadlines are shown in Table

2. The target processor has two identical cores. A schedule of the task set by using EDF

scheduling strategy is shown in Figure 2. As we can see, in the schedule, v6 finishes at time 16

and thus misses its deadline. Therefore, the schedule is not feasible. However, a feasible

schedule, as shown in Figure 3, does exist.

Table 2: A set of tasks with individual release times and deadlines

Task Execution

time

Release time Deadline

v1 4 0 4

v2 4 1 5

v3 5 3 10

v4 6 4 11

v5 4 9 16

v6 5 10 15

Core1 v1 v3 v5

Core2 v2 v4 v6

Time 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16

Figure 2: An infeasible schedule constructed by the EDF scheduling strategy

Core1 v1 v3 v6

Core2 v2 v4 v5

Time 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16

Figure 3: A feasible schedule for S2

The TaskScheduler function

You need to write a task scheduler function named TaskScheduler. A prototype of

TaskScheduler is shown as follows:

int TaskScheduler(char *file1, char *file2, int m) {};

This function gets a task set from a file named file1, constructs a feasible schedule for the task

set on a processor with m identical cores by using the EDF strategy, and writes the feasible

schedule to file2. If a feasible schedule is found, it returns 1. Otherwise, it returns 0.

Both file1 and file2 are text files. file1 contains a set of independent tasks each of which has a

name, an execution time, a release time and a deadline in that order. Task names, execution

times, release times and the deadlines are a string of digits between 0 and 9. All the release

times are non-negative integers, and all the execution times and the deadlines are natural

numbers. The format of file1 is as follows:

v1 c1 r1 d1 v2 c2 r2 d2 … vn cn rn dn

Two adjacent attributes (task name, execution time, release time and deadline) are separated

by one or more white space characters or a newline character. A sample file1 is shown here.

For simplicity, you may assume that all the task names in file1 are distinct. Therefore, you

don’t need to check for duplicates.

The TaskScheduler function needs to handle the following possible cases properly when

reading from file1 and writing to file2:

1.file1 does not exist. In this case, print “file1 does not exist” and the program

terminates.

2.file2 already exists. In this case, overwrite the old file2.

3. The task attributes (task name, execution time, release time and deadline) of file1 do

not follow the formats as shown before. In this case, print “input error when reading

the attribute of the task X” and the program terminates, where X is the name of the

task with an incorrect attribute.

file2 has the following format:

v1 p1 t1 v2 p2 t2 … vn pn tn

where each vi (i=1, 2, …, n) is the task name, pi is the name of the core where vi is scheduled,

and ti is the start time of the task vi in the schedule. In file2, all the tasks must be sorted in

non-decreasing order of start times. A sample file2 is shown here.

Your task scheduler needs to use binomial heap-based priority queues. A priority queue has a

header which stores the number of tasks in the heap and other implementation dependent

information. The data type of a heap header is defined as follows:

typedef struct BinomialHeap{

int size; // count of tasks in the heap

… // you need to add additional fields here

} BinomialHeap;

Each node in the heap stores the priority (key) and the attributes of a task. The attributes of a

task include its name, execution time, release time and deadline. For simplicity, we use an

integer to denote the name of task.

The data type for heap nodes is defined as follows:

typedef struct HeapNode {

int key; // key of this task

int TaskName; // task name

int Etime; // execution time of the task

int Rtime; // release time of the task

int Dline; // deadline of the task

… // you need to add additional fields here

} HeapNode;

Therefore, you also need to implement the following functions of a binomial heap-based

priority queue:

1. void Insert(BinomialHeap *T, int k, int n, int c, int r, int d). This function inserts a new

task into a heap T, where k, n, c, r and d are the key, name, execution time, release

time, and deadline of the task, respectively.

2. HeapNode *RemoveMin(BinomialHeap *T). This function removes a task with the

smallest key from the heap T and returns the node containing the task.

3. int Min(BinomialHeap *T). This function returns the smallest key of all the tasks in the

heap T without modifying the heap T.

The prototypes of all the above-mentioned function and some other basic functions are

included in the template file MyTaskScheduler.c. In addition to the above functions, you

may add your own helper functions and fields (variables) to the file MyTaskScheduler.c.

Note that you must implement a binomial heap-based priority queue. Any other priority

queues are not acceptable.

Time complexity requirement

You need to include the time complexity analysis of each function as comments in your

program. The time complexity of your scheduler is required to be no higher than O(n*log n),

where n is the number of tasks (Hints: use three binomial heap-based priority queues, one

priority queue with release times as keys, one priority queue with deadlines as keys and one

priority queue for the scheduling points of all the cores). There is no specific requirement on

space complexity. However, try your best to make your program space efficient.

When analysing the time complexity of your task scheduler, you can assume that the time

complexity of each function (insertion, deletion, removeMin and merge) on a binomial heap

is O(log n), where n is the number of tasks in the binomial heap.

How to submit your code?

a. Go to the Assignment page

b. Click on Assignment Specification

c. Click on Make Submission

d. Submit your MyTaskScheduler.c file that contains all the code.

Plagiarism

This is an individual assignment. Each student will have to develop their own solution without

help from other people. In particular, it is not permitted to exchange code or pseudocode.

You are not allowed to use code developed by persons other than yourself. All work

submitted for assessment must be entirely your own work. We regard unacknowledged

copying of material, in whole or part, as an extremely serious offence. For further information,

see the Course Information.