Homework 3 Submission date: 31/12/2003 17:00 Teaching assistants in charge: Vadim Drabkin, dvadim@cs.technion.ac.il, phone 04-8293371 Artiom Myaskovskey artiom@cs.technion.ac.il, phone 04-8294619 All emails regarding this assignment must have the subject cs234120hw3. 1. Introduction In this assignment your task is to write a job dispatcher. Every job will contain several tasks. The dispatcher will read the jobs from the pipe and (immediately) distribute them among its workers. The workers should perform the tasks according to the rules below. From this assignment you will learn how to work with threads, protect and synchronize the common data structures, and to use pipes to communicate between threads. 2. General Description You must write a dispatcher with a configurable number of workers. The dispatcher reads job messages from the input pipe and forwards the tasks that appear in the job description to the workers according to the instructions that appear in the job message. “Tasks pool” is a data structure that contains the tasks that the worker has to execute. You should decide how to implement it. Every worker chooses from its “tasks pool” a task according to the rules described below and executes it. The worker that finishes the last task of the job writes the output of the whole job (output of all tasks in the job) to the output pipe. The user can stop all the workers by sending “PAUSE” to the dispatcher input pipe. It will cause all the workers to stop accepting any new tasks and wait (the workers will finish the task they worked on and stop). The user can resume all the workers by sending “RESUME” to the dispatcher’s input pipe. It will cause all the workers to start looking for the next task to perform. The user can abort dispatcher by sending "ABORT" to the dispatcher's input pipe. It will cause all the workers finish all their tasks and then exit. The dispatcher will not add any new tasks and it will wait until all workers finish their tasks and then exit. 3. Detailed Description Your program will consist of a number of threads: “main thread” – “dispatcher” – reads messages from the input pipe. The message that the dispatcher gets from the pipe is either a command (“PAUSE” or “RESUME”) or job description. The “dispatcher” parses the message and if the message type is “JOB”, forwards the tasks to the workers according to the rules described below. The “dispatcher” has it’s own FIFO (input pipe). “workers” – threads that perform the tasks. The number of “workers” is a configurable. Every “worker” has its own ID and “tasks pool”. The “dispatcher” adds the tasks to worker’s “tasks pool”. The “worker” chooses the task from the “tasks pool” according to the rules described below. Every “worker” has its own FIFO pipe. The “workers” communicate only via these pipes. There are two kinds of jobs that the “worker” can perform: “Regular job” “Master job” A “Master job” is a special kind of job. A “Master job” contains only one task. We will call it “Master task”. When a “Master task” is executed no other task is allowed to be executed. After the dispatcher parses a “Master job”, all workers should finish executing the current task and then the worker that should execute the “Master task”, executes it, while the other workers wait for its completion. “Master jobs” should be executed in the order, they were received. Two “Master jobs” can’t be executed at the same time. A “Regular job” can contain several tasks. Every “Regular job” contains: JOB_PRIORITY – the priority of the job (All tasks in a job have the job’s priority). N – the number of tasks to perform. T1 T2… TN (where Ti is Task_i). Every task contains: function_index (index of the function that should be performed) ID of the worker Index of the data set Input/Output: The input pipe from which the dispatcher reads the messages will be FIFO. It’s name will be “/tmp/hw3_inputpipe”. The output pipe to which the workers write the output will also be FIFO. It’s name will be “/tmp/hw3_outputpipe”. Both pipes should be created by the dispatcher. Program arguments: Synopsis: ./dispatcher nw file1 file2 file3 file4 Where nw – number of “workers” file1 – name of file that contains file2 – name of file that contains file3 – name of file that contains file4 – name of file that contains Data Data Data Data Set Set Set Set 1 2 3 4 Data sets: A data Set is a binary file that contains 4 bytes of data (Note that the size of int is exactly 4 bytes). Functions: There will be 4 different functions. Each of the functions gets as input an integer and returns an integer. Each function is of the following type: typedef int (*func)(int input); In the program, the functions should be declared as follows: typedef int (*func_type)(int input); extern func_type func_arr[4]; Function definitions will be defined in external file “func.o”. For your tests you should write these functions by yourself. Message format conventions: Bold is used for constant strings that appear in message Italic is for parameters numbers after underscores are parameter lengths (in bytes) <SEP> stands for ';' - separates between fields in message <EOM> stands for '#' – end of message mark. Message example: MESSAGE_TYPE=VALUE1<SEP> where VALUE1={PAUSE,RESUME} Here 'MESSAGE_TYPE=' is a constant string and VALUE1 is a string parameter that can get one of two values: 'PAUSE' or 'RESUME'. The dispatcher can get messages of one of 2 types: 1. Command Format: MESSAGE_TYPE=VALUE <EOM> where VALUE ={PAUSE,RESUME,ABORT}; 2. Job message Format: MESSAGE_TYPE=VALUE1 <SEP> JOB_TYPE=VALUE2 <SEP> JOB_PRIORITY=VALUE3<SEP> TASKS_NUMBER=VALUE4 <SEP> Description of each task. Appears VALUE4 times TASK_START<SEP> WORKER_ID=VALUE5 <SEP> FUNCTION_INDEX=VALUE6<SEP> DATASET_INDEX=VALUE7<SEP> TASK_END<SEP> <EOM> Where : VALUE1 = {JOB} VALUE2 = { REGULAR,MASTER} VALUE3 = 1..100 . VALUE4 = integer. Remark: This parameter sets the number of tasks that appear in this job. VALUE5 = 1..{maximum number of “workers”}. VALUE6 = 1..4 VALUE7 = 1..4 Remark: all numbers are written in ASCII. For example: if VALUE5 = 48 then string in the message will be 'VALUE5=48' and not 'VALUE5=0' ('0' ASCII value is 48) Rules: 1) The tasks in each job should be executed in the order they appear within the job. Message example: MESSAGE_TYPE=JOB; JOB_TYPE=REGULAR; JOB_PRIORITY=4 TASKS_NUMBER=2; TASK_START; WORKER_ID=9; FUNCTION_INDEX=4; DATASET_INDEX=3; TASK_END; TASK_START; WORKER_ID=7; FUNCTION_INDEX=1; DATASET_INDEX=2; TASK_END;# It means that the job contains 2 tasks and that the first task should execute function number 4 on data set number 3 and it will be executed on worker number 9.The second task should execute function number 1 on data set number 2 and it will be executed on worker number 7. The second task can’t be started before the first task of the same job is finished. You should think how to implement it. 2) If there is no “Master job” waiting, the worker chooses from its “tasks pool”, the task with highest priority and starts executing it (after it checked the conditions of rule number 1). Notice that a task with low priority can be starved by tasks with higher priority. The worker won’t start executing a new task until it finishes executing the current task. 3) If the worker has in its queue two tasks with the same priority, the tasks should be started in the order they were received. 4. Remarks As soon as the dispatcher gets the input (jobs) from the pipe, it forwards the tasks to the workers’ queues and the workers remove the tasks from the queue after they have performed them. The dispatcher shouldn’t communicate with workers in any way except for adding the tasks to the workers’ queues. 5. Submission You should electronically submit a zip file that contains the source files and the makefile. It’s name should be “Makefile”. The makefile will create an executable with name “dispatcher”. You should also submit a printed version of the source code. A file named “submitters.txt” which includes students ID's and names in the following format: i. Israeli Israel 123456789 ii. Israeli Israela 987654321 iii. Ben-Israeli Zion 543627189 May God Be With You, The course staff.