Like I promised, I will restart to write articles now that the evaluation period is over. After seeing how to develop using the JR programming language, we'll see now how to use monitors in JR. Monitors provide a higher-level abstraction than semaphores and produce a better code with several advantages : The mutual exclusion is implicit with monitors. Only one process is allowed in the monitor, so all the method are automatically guarded with synchronization code. The synchronization between threads is made using signaling system, with condition variables. A condition variable is a kind of queue of process who are waiting on the same condition. You have several operations available on a condition, the most important is to signal a process waiting to be awaken and to wait on a condition. There are some similitudes between signal/wait operations and P and V of semaphores, but this is a little different. The signal operation does nothing if the queue is empty and the wait operation put always the thread in the waiting queue. The process queue is served in a first come, first served mode. Now we'll see how to use them in JR. In JR, you doesn't have directly the possibility to create monitors, but there is a preprocessor that transform a file with both JR and monitors operations into a plain JR file. This processor is called m2jr (monitor to JR) and is directly available in the JR distribution. So you only have to use the m2jr command to translate a .m file (conventional monitor extension file) into several JR files (one for the monitor and one for the condition variables). This file is normal JR with several comments to make the debugs easier (corresponding between lines in m and JR). All the keywords of the m2jr language start with _ (underscore). To declare a monitor, it's as easily as use the _monitor keyword : ```java _monitor MonitorTest { //... } ``` To add methods to the monitor, you just have to create method prefixed with _proc : ```java _monitor MonitorTest { _proc void testA(){ //Some code } } ``` Only with that the mutual exclusion is guaranteed. Only one process is allowed into the monitor. If you want methods with a return type, you must use _return instead of return : ```java _monitor MonitorTest { _proc void testA(){ //Some code } _proc int testB(){ _return 1; } } ``` To declare condition variables, the keyword is _condvar. You don't have to initialize them, just declare them : ```java _monitor MonitorTest { _condvar condVar1; _condvar condVar2; } ``` To use global variables, you must prefix them with _var : ```java _monitor MonitorTest { _var var1; _var var2; } ``` And to make operations on condition variables, you have to use _signal and _wait methods inside a proc method : ```java _monitor MonitorTest { _condvar a; _condvar b; _proc void test(){ _wait(a); //Some computations _signal(b); } } ``` And you compile that to JR using the simple commmand : m2jr MonitorTest.m That will create two files (MonitorTest.jr and c_m_condvar.jr). Of course, you can use this monitor in a normal JR class, you don't have to compile classes who use monitors with m2jr, only with the JR compiler. There is just a single thing to worry about. m2jr generates a constructor that take a String in every monitors class, you when you instantiate the monitor, you have to provide a String representing its name as the first parameter. And if you want to create a new constructor in a monitor, you just have to call super with a String. We'll see an example later. Before going further, we must have more informations about the signal operations. When writing monitors, you have the choice between several philosophies for the signaling operation : By default, m2jr make the compilation with SC, but you can configure it to use other philosophies. Just add the abbreviation of the philosophy (lower case) as an option to the m2jr compiler. By example, to compile using Signal & Exit : m2jr -sx MonitorTest.m The main differences, is that the SC create a signal stealers problem. You will quickly understand with an example. With what we know now, we can create a monitor to manage a bounded buffer : ```java _monitor BoundedBuffer { private static final int N = 5; //Size of the buffer _var String[] buffer = new String[N]; _var int front; _var int rear; _var int count; _condvar notFull; _condvar notEmpty; _proc void deposit(String data){ while(count == N){ _wait(notFull); } buffer[rear] = data; rear = (rear + 1) % N; count++; _signal(notEmpty); } _proc String fetch(){ while(count == 0){ _wait(notEmpty); } String result = buffer[front]; front = (front + 1) % N; count--; _signal(notFull); _return result; } } ``` A thing that some of you will certainly find weird is the loop around the wait operation. Is to avoid the signal stealers problem. If there were not while loop, imagine that situation in SC :
  1. The thread 1 try to make a fetch(), there is no data, so it wait for the notEmpty condition variable
  2. The thread 2 make a deposit(), that awake the thread 1, but it needs to acquire again the mutual exclusion.
  3. Before the thread 2 has acquired the mutual exclusion, the thread 3 make a fetch(), there is enough data, so thread 3 make a fetch and get the data. So now, it's empty.
  4. The thread 2 acquire the right to go into the monitor and get a data. But wait a minute, there is no data and it will fetch a null data or perhaps an old data still fetched depending on the current state of the buffer
To avoid that situation, you just have to wrap the wait in a while loop instead of a if and that's done ! Or you can also use SW instead of SC. With that monitor, we can easily solve the producer and consumer problem : ```java public class ProducerConsumer { private static final int N = 12; //Number of producers and consumers private static BoundedBuffer bb = new BoundedBuffer("Bounded Buffer monitor"); //The monitor public static void main(String... args){} private static process Producer((int i = 0; i < N; i++)){ bb.deposit("Producer" + i); } private static process Consumer((int i = 0; i < N; i++)){ System.out.println("Consumer" + i + " : " + bb.fetch()); } } ``` That will provide output like that :
Consumer10 : Producer0
Consumer0 : Producer1
Consumer1 : Producer2
Consumer2 : Producer4
Consumer3 : Producer5
Consumer4 : Producer3
Consumer8 : Producer7
Consumer11 : Producer10
Consumer6 : Producer8
Consumer5 : Producer6
Consumer7 : Producer11
Consumer9 : Producer9
So it works well. More than signal and wait operations, m2jr provide also others operations on condition variables : With all that stuff, you can create monitors to solve almost all concurrency problems like barber shop, philosopher dinner, kwai cross or a lot of others problem. I hope you found that post interesting. The next post about JR will be about operations and capabilities.