Modifing C Programs on Brazos: HOW TO Andréa M. Matsunaga & Maurício
O. Tsugawa |
Index
Modifying a C program to run on Brazos is relatively easy. You only need to make a few additions to your existing code.
(These code fragments are taken from an example solution to the Dining Philosophers problem. It might be useful to see the fragments in context as you read; just follow the link.)
In Microsoft Visual C++, create your Project as either a Win32 console application or a Win32 application.
Include the header files "windows.h" and "brazosinc.h".
In your main() function, you should first call INITENV to initialize the Brazos environment.
void main (int argc, char**argv) {
INITENV(argc, argv, 0);
/* your code here */
}After INITENV is called, use the variables ARGC and ARGV instead of argc and argv if you need to access them in the remainder of main().
Allocate all shared data with G_MALLOC to make the data concurrent over multiple machines. Be careful that only one thread performs the allocation. It's easiest to restrict initialization duties to one particular thread, say thread #0, and follow the initialization block with a barrier so all other threads wait until the allocation is complete. Any non-shared data can be allocated using malloc as usual. Pointers contained within regions allocated with G_MALLOC should also be allocated with calls to G_MALLOC.
int* freeForks;
G_MALLOC
((char*) freeForks, 10*sizeof(int)); /* make an integer array with 10 slots be shared across all machines. */
Create the threads with the CREATE_THREAD function. CREATE_THREAD takes a reference to a HANDLE and a function to begin executing. The threads function cannot take any parameters.
int k;
/* array of handles to be returned by Create_Thread */
HANDLE philosophers[MAX_NUM_PHILOSOPHERS]
for(k=0; k< NUM_LOCAL_USER_THREADS; k++) {
CREATE_THREAD(&philosophers[k], dining_main);
}
/* dining_main is the function for each thread to run */
As the thread's function takes no parameters, an easy way to identify threads within the function is the GETMYGLOBALID call. All of the threads in the Brazos process are assigned global ids, unique across all machines, and local ids, unique on the thread's machine. Use GETMYGLOBALID to make sure that only one thread does the initialization of the global data. The easiest way to accomplish this is to have only the thread with global id 0 enter the initialization block. Then put a barrier immediately following the initialization block, so all other threads wait until the initialization is complete.
int MyId;
GETMYGLOBALID (&MyId);
if (MyId == 0) {
/* initialization*/
}
BARRIER(0);
For concurrent synchronization, you can use barriers, or one of the two kinds of locks: scope consistency (SCC) or release consistency (RC). In the following Dining Philosophers example, Brazos RCLOCKs are used to control access to the shared data.
USHORT mutex=2;
RCLOCK(mutex); /* (SCCLOCK(mutex)) */
/* critical section */
RCUNLOCK(mutex); /* (SCCUNLOCK(mutex)) */
When you run Brazos, you can have messages printed to the screen by using OUTPUT. To exit upon an error and print out a message, use ERREXIT. Both use printf syntax.
OUTPUT("Philosopher #%d has picked up the forks\n", i);
ERREXIT will attempt to kill off all the processes (remote and local), not just the calling thread.
ERREXIT("out of shared memory\n");
Call EXIT at the end of your main() program so Brazos will shut down properly. This is the only place EXIT should be called.
For more information, see the Brazos C API.
Dining Philosophers
This is a pretty standard solution to the Dining Philosophers problem. You have n philosophers seated around a circular table. There are n forks, one to the left of each philosopher. Philosophers only eat and think; they think until they are hungry and then they must get two forks next to them in order to eat from the large platter of spaghetti in the middle of the table. They eat until full and then put down the forks, freeing them up for the adjacent philosophers to use. This solution keeps an array, "freeForks", of the number of forks available to philosopher #i. Since we want this data to be accessed by all threads, we used Brazos locks to provide the mutual exclusion and G_MALLOCed the array, so it would be concurrent over all threads. When hungry, a philosopher tries to obtain the forks by gaining sole access to the forks, seeing if they are available, and taking them if they are. If unavailable, they relinquish the access rights and repeat this process. When a philosopher is done eating, they relinquish the forks. To prevent starvation, we keep track of how many times each philosopher gets to eat (the array mealsEaten), and if a philosopher is hogging the forks too much (i.e., if a neighbor has eaten at least 3 fewer meals), they don't take the forks.
If you are using Microsoft Visual C++ 5.0 or later to write your code, you need to edit your project settings in order to compile and link properly. Go to the Project menu and select Settings. Under the Link tab, add the following to the Object/Library modules list:
libcmt.lib
ws2_32.lib
b_reg.lib
(included in the "include" directory in the distribution)
brazos.lib
for the dynamically linked library (recommended) or
brazos_s.lib
for the static library Also check the "Ignore all default libraries" box.
In Developer Studio, under Tools
Options
Directories include files and library files, add the path where you installed the Brazos library and header files.