EEEN30052 Concurrent Systems 2019-20 Assignment 1. Introduction This assignment is concerned with simulating some aspects of a simple embedded computer system. You may choose between two versions: A1 or A2. Each is worth a different percentage of the overall coursework mark: A1 is a simplified version of the assignment and is worth 45%, while A2 is the full version and is worth 90%. In addition there are two questions based on the coursework which must be answered during the assessment. These questions are worth 10%, thus the maximum mark available for A1 is 55%, and the maximum available for A2 is 100%. The system shown below in Figure 1 is responsible for gathering environmental data via a single analogue-to-digital converter (ADC), and transmitting it to another computer system via one of three communications links. The software in the embedded system consists initially of six threads, each of which is responsible for gathering data on different input channels, temporarily storing it, and then transmitting it to the other computer system in blocks. In order to acquire data, each thread must gain sole access to the ADC, configure it to sample from a specific channel, and sample the channel. In order to transmit a block of data, a thread must gain access to one of the communications links. Having gained access, it sends its block of data, and then releases the link. Each thread may use any communications link, but once it has gained access to a particular link, no other thread should be allowed to use that link until it is released. Analogue input channels ADC Threads Link Access Communications links Figure 1 If you choose to do A2 you should start by implementing A1, but you will not be required to demonstrate it or upload a copy (of A1). If you choose A1 you do not need to attempt A2, or demonstrate/upload it. However, in both cases your program should print out messages clearly indicating the identity of the calling thread and what action it is performing. 1
2. A1 worth 45% of total coursework marks Write a C++ program that creates six threads where each thread is identified by an integer and has a reference to an object of class ADC, which is described below. Each thread executes the following sequence 50 times: Request use of the ADC by calling the requestADC() method of the ADC object. This call passes an integer argument representing the channel to be sampled. The ADC has several channels (initially six), each represented by an instance of AdcInputChannel. Assume that each thread uses a unique AdcInputChannel. The thread should print out its identity when the call is successful. Request a sample from the ADC by calling sampleADC(). This returns a double value on the basis of the channel that was selected by the requestADC() call. To start with, simply make the sampleADC() method return a value that is twice the channel number passed in the call to requestADC(). Print out its identity together with the sample value obtained from the ADC. Call releaseADC() to enable other threads to use the ADC, printing out its identity once more. Delay for a random time between 0.1 and 0.5 second. A function called run() will be used to implement the above. Objects of class ADC have a reference to an instance of class Lock, to help ensure mutually exclusive access to the ADC. Hence when a thread wishestoaccesstheADCitcallsrequestADC(),whichinturncallsthelock() methodoftheLock object. If the ADC is not currently in use, it will be locked and the thread will be allowed to proceed to use the ADC. However, if the ADC is in use, then the thread calling requestADC() should be suspended until the ADC has been relinquished by the using thread calling releaseADC(), which in turn calls the unlock() method of the Lock object. Thus threads are suspended and resumed- in requestADC() and releaseADC(), depending on the result of the call to the Lock objects lock() method. The main program should create six threads and one object of class ADC. Initially the ADC has six AdcInputChannels (it is recommended to use a vector rather than an array for this purpose). It should initiate execution of the threads and should not terminate until all threads have terminated. To help you get started the following is a partially-complete skeleton of the program: //comment: authors name, ID, and date. //pre-processor directives: #include #include . using namespace std; //global constants: int const MAX_NUM_OF_CHAN = 6; int const MAX_NUM_OF_THREADS = 6; int const DATA_BLOCK_SIZE = 20; //number of AdcInputChannels continued.. 2
//global variables: . //function prototypes: (if used) . class AdcInputChannel { public: AdcInputChannel(int d) //constructor : currentSample(d) {} //constructor syntax for currentSample=d //used to request a sample from the sample channel: double getCurrentSample() { . } private: int currentSample; }; //end class AdcInputChannel class Lock { public: Lock() //constructor : open(true) {} //returns a flag to indicate when a thread should be blocked: bool lock() { . } void unlock() { . } private: bool open; }; //end class Lock class ADC { public: //constructor: initialises a vector of ADCInputChannels //passed in by reference: ADC(std::vector & channels) :adcChannels(channels) {} void requestADC(int c) { . } double sampleADC() { . } void releaseADC() { . } continued.. 3
private: Lock theADCLock; int sampleChannel; std::vector & adcChannels; std::mutex ADC_mu; //mutex }; //end class ADC //run function executed by each thread: void run(ADC& theADC, int id) { . //vector reference //to store the sampled data: (for part A2 only) double sampleBlock[DATA_BLOCK_SIZE] = {0.0}; //initialise all elements to 0 for (i=0; i<50; i++) { //replace with ‘i adcChannels; for (int i = 0; i < MAX_NUM_OF_CHAN; i++) {…. //each AdcInputChannel is initialised with a different value }// Instantiate the ADC:….//instantiate and start the threads:std::thread the_threads[MAX_NUM_OF_THREADS]; //array of threads for (int i = 0; i < MAX_NUM_OF_THREADS; i++) {//launch the threads:…. }//wait for the threads to finish:….cout << “All threads terminated” << endl;return 0; }4The code is incomplete in a number of different senses, and you must modify it in order to complete A1. You are free to include any additional methods/functions/variables/constants, as you think necessary. Note that a solution that is poorly commented, or that does not calculate and use the delay period correctly, will lose marks (see the marking sheet).Part of an example output is shown below:Notes on the Program2.1 Thread ‘Identity’Obtaining the ‘id’ value of a thread in C++11 is done with std::this_thread::get_id()-however this returns an object not an integer, and although this can be passed to coutfor printing (e.g. cout << std::this_thread::get_id() << endl;), unfortunately there’s no simple way to cast it to an int.For example, if you create say, three threads, and printed out their ids, you might get 234Ideally, we want to be able to associate a known integer ‘id’ with each thread so we can identify them as’thread 0′, thread 1′, etc..One solution is to create a std::map of thread ‘ids’ v. integers, e.g. thread ‘id’ int value20 31 42..etc. 5To associate the thread ‘id’ with an integer value in a map, the following might be helpful:std::mutex mu; //declare a mutexstd::unique_lock map_locker(mu); //lock the map via the mutex. //insert the threadID and id into the map: threadIDs.insert(std::make_pair(std::this_thread::get_id(), id)); map_locker.unlock(); //were done, unlock the map. A recommended way to search the map by thread id is to use an interator, e.g.: std::map ::iterator it = threadIDs.find(std::this_thread::get_id()); if (it == threadIDs.end()) return -1; //thread id NOT found else return it->second; //thread id found, return the //associated integer note the syntax. It might be useful to put the search code in a separate function. You will need the following preprocessor directive in order to use a map: #include
Reviews
There are no reviews yet.
Only logged in customers who have purchased this product may leave a review.
Reviews
There are no reviews yet.