Deadlines

First deadline: 7 March
Final deadline: 17 March

chat bubbles

Lab 4: CCHAT Workers

Having a working chat server is cool, but sending text messages back and forth quickly gets boring. Let's use our network of chat clients to do something interesting!

For this lab, we want to use the clients connected to a server as workers, each computing some small tasks which form part of a larger job. This kind of distributed computing is often used for computationally intensive tasks, such as mining BitCoins or computing protein folding.

Specification

We will not use the client GUI for this task. Instead, you will implement the function cchat:send_job/3 with the following arguments:

  1. Server name as a string
  2. Function (of arity 1) to apply to each input
  3. List of inputs

Calling this function should take care of splitting your job into sub-tasks, distributing these amongst the clients connected to the chat server, and collecting and returning the final results. Assuming you have a server running with some clients connected, a job can be sent as follows:

> cchat:send_job("shire", fun(X) -> X * 3 end, [10,5,8]).
[30,15,24]

Note that the function is not restricted to numbers. You can pass any functions you want! Here for example we use a function from the string library:

> cchat:send_job("shire", fun string:to_upper/1, ["erlang","is","fun"]).
["ERLANG","IS","FUN"]

The idea is that the function could be computationally intensive, therefore the server should not block while waiting for the answer (but it's ok for each of the workers to block while computing their sub-tasks).

Assigning tasks

You will need to share the tasks amongst the available clients already connected to the server. To simpify things, unlike the workers pattern shown in the lectures, the assignment of tasks does not need to be dynamic. If you want you can use or adapt the following function:

assign_tasks([], _) -> [] ;
assign_tasks(Users, Tasks) ->
  [  {lists:nth(((N-1) rem length(Users)) + 1, Users), Task}
  || {N,Task} <- lists:zip(lists:seq(1,length(Tasks)), Tasks) ].

Which works as follows:

> assign_tasks([u1,u2],[t1,t2,t3]).
[{u1,t1},{u2,t2},{u1,t3}]

Order of results

It is important that the order of the results is correct! You should explicity make sure that the following does not happen:

> cchat:send_job("shire", fun(X) -> X * 3 end, [1,5,10]).
[15,3,30]

The test case described below specifically tests for this.

Timeouts

The system shouldn't timeout if the task takes long to compute. Think asynchronously! When using genserver:request, you can extend the default timeout of 3000ms like so:

genserver:request(Pid, Data, 5000)

or alternatively remove the timeout altogether with:

genserver:request(Pid, Data, infinity)

Error handling

There are a few things which can go wrong:

We recommend that you come up with suitable behaviour for these cases, but for this lab it is not required for you to handle them.


Code skeleton

You should use the same one given for CCHAT. You can get the code skeleton here. Note that it has been slightly updated, specifically the files Makefile and test_client.erl.

Requirements

Test cases

Update 1 March: Not all changes were pushed to the skeleton repo from the start, and the run_workers_tests makefile target was missing. Please update your code.

Now, your software should pass the same test cases as before together with some extra ones designed to check the new functionality. The test cases are given in the same testing framework (eunit) as we did previously.

To run the new tests, make sure to get the latest version of the code skeleton and execute the following:

$ make -s run_workers_tests

You should see the following kind of output:

# Test: workers
server startup: Ok
spawning 3 clients
sending input: [4,1,7,3,-2,5,2] (7 tasks)
results: [8,2,14,6,-4,10,4] received in 2413ms
results received correctly: Ok
  Test passed.

The total amount of time taken to receive the results is not that important. What is important is that the results arrive in the correct order, and that your server did not block while the job was being calculated.

Alternatives to make

If your system doesn't have the make command, you can run the tests like so:

$ erl -noshell -eval "eunit:test({timeout, 10, {test,test_client,workers}}), halt()"

Submission

You should submit your code based on the skeleton code and whatever other files are needed for your solution to work. In addition, you should comment your code so that graders can easily review your submission.

Please do not submit compressed archives. Just upload the individual source files which you have worked in.



Concurrent Programming 2016 - Chalmers University of Technology & Gothenburg University