$25
This lab introduces the student to concepts of concurrency. That is, how to make an application composed of several separate programs/processes all running together at the same time.
Introduction
Before tackling these questions, make sure you have downloaded and expanded (in the correct folder) the zip file from the web-site relating to the compressed RTExamples.
Also make sure you are familiar with the Visual C++ environment and know how to create projects, build, execute and run them. There is a handout on Canvas showing you how to create Concurrent Applications using Visual C++.
Part A – 15%
Load up the visual C++ solution Q1.sln from the RTExamples folder. The workspace/solution contains three independent projects/programs each with their own function main(). Build each of the projects Q1Parent, Q1child1, … etc. contained within it (right click on the solution name in the solution navigator and select build) and make sure they compile and link without errors.
Now set Q1 as the active project (right click on the project name and select “set as startup project”) then run it by clicking on the menu Debug->Start without Debugging (Ctrl + F5). You should see three child programs/processes run in their own DOS window and a parent which waits for them to finish. You may have to rearrange them on the screen to see them all.
Take a look at the code for Q1parent.cpp and see if you can figure out how it works. Study for example, the pathlists contained in the CProcess() call in this file.
Once you have succeeded with building and running the program, attempt to create a completely new Visual C++ project and re-create the setup and programs that you have seen here. This might seem a redundant exercise as we already have a solution, but try it, as it will teach you many things about using Visual C++ that you probably did not know and will need to do for your assignment.
NOTE do not use “spaces” in your child project names project pathlists, as spaces will be treated as delimiters by the Kernel between the “.exe” name and any optional parameters you want to pass to the child process.
Don’t forget to include the rt.cpp and rt.h files into those projects that make use of concurrency etc. as Q1 does. Also don’t forget to include the multi-threaded (debug) run time library into the project properties for your equivalent of Q1 (see handout on making concurrent applications with visual C++).
Parts B, C, D and E of this lab/tutorial follow the following section on Common Mistakes
Common Mistake 1
If you forget to add the rt.cpp and rt.h files into your Visual Studio Project you will see a number of compiler based link errors such as those shown below indicating unresolved calls to functions that cannot be found in the program. Check out the handout on Visual C++ to see how to add these two files to a project.
Common Mistake 2
Another common mistake is to incorrectly specify the pathlist used by the parent process in the CProcess() call. An example is shown below in red.
CProcess p1("C:\\RTExamples\\Q1multiTaskingUsingProcesses\\Q1Child1\\debug\\Q1Child1.exe", NORMAL_PRIORITY_CLASS,
OWN_WINDOW,
ACTIVE
) ;
Note the use of double ‘\’ i.e. ‘\\’ as a directory delimiter, this is because C/C++ reserves the ‘\’ character for special meanings e.g. ‘\n’ for new line, ‘\t’ for tab etc so if you want a single ‘\’ character you have to enter two of them as ‘\\’.
If the pathlist is incorrect and doesn’t enable the operating system to locate the executable file, the program will still compile with no error but when you run the program you will get a ‘BEEP’ and the following example error message appearing on your terminal window.
If you are unsure where Visual C++ has put the executable program, then do a search for the file ending in ‘.exe.’ and identify the pathlist to it and correct the source file before building and running it again.
Common Mistake 3
When you build a project solution under say VS 2019, you have the option of compiling to a release or a debug version of the executable (the latter has more information for debugging purposes and is bigger). However, the correct run time library has to be linked in at compile time. There are DLL (dynamic link library) and non-DLL versions appropriate for a release or debug version of the build. You may have to make the changes manually as shown below
Common mistake 4
When using the RT library for your Labs and Assignments, you have to make sure the language is set to multi-byte character set as shown below
Otherwise these kinds of errors occur
Part B – 35%
How would you modify the program so that each child process shared the same output window as its parent? Try creating a child program to run in the suspended state. Now control the activation of the child program using the CProcess member function Suspend() and Resume() as outlined in the course see notes.
In some ways using the CProcess function makes life very easy for us (as libraries are supposed to) but masks the complexity of what is going on behind the scenes in terms of real system level calls to the OS kernel.
This is the tricky bit of the lab, see if you can remove the CProcess classes from your parent source file (i.e. remove the rt.cpp and rt.h files from the parent project) and figure out how to call the Kernel directly to create a child process (see the lecture notes from lecture 3 as an example)
You will also have to replace the CProcess constructor call, WaitForProcess() and any Suspend() and Resume() calls with direct kernel calls of your own (consult the rt.cpp file to see how they translate into kernel calls).You will have to keep track of process and main thread ID’s and handles for use with other process/thread related calls
Doing this will give you an appreciation of how detailed the calls to a specific kernel can be when making new processes and how easy the rt.cpp library is to use.
Part C – 10%
Finally go back to the original version of Q1 and see if you can get the parent program Q1parent to pass parameters/information to the 3 child processes and verify that this works by getting the child processes to print out the data the parent process has passed to them (see notes on Argc, Argv in the lecture notes from lecture 3).
Note: using argc/argv (the traditional C/C++ method of passing arguments to a new process) only allows arguments to be passed as strings, so if you want to pass integers and floats for example, you have to convert the strings into these two types inside the process once it is up and running, as an example look up the function atoi() and atof() in the visual C++ help for examples of converting strings to integers and floats, you’ll have to include the relevant “header” file <stdlib.h> into your process in order to call these functions. Passing arguments to a child program is useful as it allows the parent process to influence/control the behaviour or execution of the child processes at run time.
Part D – 10%
In Part A we wrote a multi-tasking system using multiple child processes controlled by a parent process. In this part of the lab, start a new project/solution in Visual Studio and see if you can write a multi-threaded solution, i.e. write a single parent process comprising several child threads built into the same source file. These child threads should do the same thing they did in Part A, print simple messages.
Look at the CThread class notes from lecture 4 and the example project Q2.sln from the downloaded RTExamples zipped file. Thread functions have to be written with the special signature below, i.e. they take one parameter (a void pointer) and return a UINT or unsigned int
UINT _ _stdcall ChildThread1(void *args)
{
. . .
}
Modify the code so that the parent can pass data to the child threads. The data could be a simple int or float etc, but for this exercise, combine several items of data into a C style struct type of variable and pass a pointer to that as a single variable.
UINT _ _stdcall ChildThread1(void *args)
In the above thread signature, all data passed to the child by a parent thread has to be passed as a single “void” pointer, which can point to anything, but you will have to cast this pointer into a pointer to the actual type data that you pass to the thread before using the ‘*’ pointer indirection operation to get at the data, again see lecture notes 4 for examples.
Part E – 20%
As we did in Part B, remove the rt.cpp and rt.h files from the project you created in Part D above and see if you can replace the CThread class (i.e. all its member functions and variables), by variables and code in your own program that call the windows kernel directly.
Note you should not try to replicate the following function (thread constructor taking one argument) with the signature below, contained within the rt.cpp file. This constructor is used only with active classes not pure stand alone threads. Note ignore lines of //##ModelId as they were generated by a tool and are only comments anyway.
//##ModelId=3DE6123A0178
CThread::CThread(BOOL bCreateState) {
}
Part F – 10%
We mentioned that C++ ’11 has a threading library which is built into Visual Studio. See if you can rewrite the child threads and modify the parent to make use of the C++ library rather than the RT library.