Readers/Writers on Java RMI

Readme/index.html file


Copyright (C) 2007-2009 Fernando G. Tinetti (my web page at UNLP)

License
This code is released under the GNU GPL version 2 or any later version (http://www.gnu.org/copyleft/gpl.html)

Different ways for solving the readers/writers problems in the context of rmi, i.e., having an "access" server implemented with rmi. This server is used just for controlling the access to the shared resource (database, filesystem, etc.). Technical details on rmi are assumed to be known.

The only a priori requirement is that one or more readers could access concurrently and at most one writer (without any other concurrent process) may access the resource. On other words: a reader process must wait if there is a writer process accessing and a writer process must wait if there is/are other process/es accessing (one or more readers, or another writer).

Most of the code for this series is related to that found in http://beg.projects.cis.ksu.edu/examples/small/readerswriters/ and references therein:
Dr. Mizuno's paper "A Structured Approach for Developing Concurrent Programs in Java"
Doug Lea's book, Concurrent Programming in Java

Every version is defined separately trying to be self contained.

Every reader process should ask for accessing the resource for reading via
accessReader()
and should warn when ending the read access with
leaveReader()

Analogously, every writer process should make similar asking/warning through
accessWriter() and leaveWriter()
respectively

In all of the cases, the process is assumed to get the access when the rmi returns successfully (not on exception)

Files
-----

Readme/index.html: this file

remoteMethod.java: the interface defining the four methods to be remotely invoked. According to the comments on the file:
* remoteMethod.java
* Interface defined for the class with the remote methods to be
* used from the "client" side and implemented on the "server".
* Thus, only the four methods (access/leave Reader/Writer) are
* defined, all of them throwing RemoteException

startRemoteObject.java: the class having a main() to create and register one "server" object (this object belongs to the class which implements the interface with "remote" methods). According to the comments on the file:
* startRemoteObject.java
* Starts the remote object. More specifically:
* 1) Creates the object which has the remote methods to be
* invoked
* 2) Registers the object so that it becomes avaliable
* The specific object to be created and registered depends on
* the line
* remoteClassv<i> robject = new remoteClassv<i>();
* where <i> is the number (version) of the "server" implemented
* Expects a local rmiregistry running and waiting on the
* default registry port.
* Used with
* java startRemoteObject

askRemote.java: the "client" process, acting as a reader process or a writer process depending on a command line parameter. According to the comments on the file:
* askRemote.java
* Expected to be used as a "client", behaving as a reader
* process or a writer process depending upon the "command
* line argument".
* Access the resource by 5 secs. (hardcoded), this is the
* elapsed time between the return from access...() and the
* call to leave...()
* Used with
* java askRemote <hostname> <Read/Write>
* <hostname>: hostname or IP of the host where the remote
* object ("server") is "waiting"
* <Read/Write>: Read ==> reader process
* Write ==> writer process

remoteClassv<i>.java (i = 1, 2, ..., 7): different implementations of the remoteMethod interface, i.e. different "server" policies. According to the comments on each one of them:
remoteClassv1.java
* This is "Version 1 server" i.e.:
* 1) No priorities.
* 2) With a unique "waiting queue" for every process that should wait.
* 3) Added synchronized to each remote method, to avoid race conditions.
* Used from startService, which creates and registers one object of
* this class.
* Note that keyword "synchronized" is added to each remote method and
* this does not violates the specification, since "synchronized" is not
* part of the method's signature.
* Used from startService, which creates and registers one object of
* this class.

remoteClassv2.java
* This is "Version 2 server" i.e.:
* 1) Readers do not enter if there are writers waiting.
* 2) With a unique "waiting queue" for every process that should wait.
* 3) Added synchronized to each remote method, to avoid race conditions.
* Note that keyword "synchronized" is added to each remote method and
* this does not violates the specification, since "synchronized" is not
* part of the method's signature.
* Used from startService, which creates and registers one object of
* this class.

remoteClassv3.java
* This is "Version 3 server" i.e.:
* 1) Readers do not enter if there are writers waiting.
* 2) After a writer, a waiting reader get access (just one) if there is one.
* 3) With a unique "waiting queue" for every process that should wait.
* 4) Added synchronized to each remote method, to avoid race conditions.
* Note that keyword "synchronized" is added to each remote method and
* this does not violates the specification, since "synchronized" is not
* part of the method's signature.
* Used from startService, which creates and registers one object of
* this class.

remoteClassv4.java
* This is "Version 4 server" i.e.:
* 1) Readers do not enter if there are writers waiting.
* 2) After a writer, a waiting reader get access (just one) if there is one.
* 3) The conditions under which readers or writers get access to the
* resource are checked in a separate function.
* 4) Other policies could be defined and checked in the proper function as
* well. Also, it could be needed (for those policies) to define and use
* specific local variables in order to enforce the policy (as variable
* "last" in the current example).
* 5) With a unique "waiting queue" for every process that should wait.
* 6) Added synchronized to each remote method, to avoid race conditions.
* Note that keyword "synchronized" is added to each remote method and
* this does not violates the specification, since "synchronized" is not
* part of the method's signature.
* Used from startService, which creates and registers one object of
* this class.

remoteClassv5.java
* This is "Version 5 server" i.e.:
* 1) Readers do not enter if there are writers waiting.
* 2) After a writer, a waiting reader get access (just one) if there is one.
* 3) The conditions under which readers or writers get access to the
* resource are checked in a separate function.
* 4) Other policies could be defined and checked in the proper function as
* well. Also, it could be needed (for those policies) to define and use
* specific local variables in order to enforce the policy (as variable
* "last" in the current example).
* 5) Each kind of process (i.e. readers or writers) now wait on a separate
* "watiting queue" associated to a specific monitor. An instance of the
* Object class is used directly as a monitor (which in fact it is).
* More specifically, these monitors are used just as waiting and
* synchronizing queues, just to make wait/notify calls on them.
* 6) The remote methods are not "synchronized" now, since two monitors are
* used: one to test and set for access conditions and one to wait
* (well, two in general, one for readers and one for writers). The
* monitor to test and set for access is "this", the access server
* itself.

remoteClassv6.java
* This is "Version 6 server" i.e.:
* A rather different approach (from the previous ones), since
* concurrent readers are allowed for every (reader) request
* previous to a writer request and after a writer leaving the
* shared resource the next process having access is the one
* that asked immediately after (and, thus, had to wait) the
* writer process.
* A complete waiting queue is implemented with a Vector, where index
* i indicates the i_th access request. Thus, the Vector reflects the
* order in which access requests have been made to this server.

remoteClassv7.java
* This is "Version 7 server" i.e.:
* Fix a "low probability error" for version 6, since on accessReader()
* after the code (lines 81-98)
* synchronized (this)
* {
* ...
* waitingProcesses.addElement(waitingreader);
* ...
* }
*
* another thread could execute on leaveWriter() (line 199)
*
* synchronized(waitingProcesses.firstElement()) { waitingProcesses.firstElement().notify(); } // Wake up a reader
*
* and the reader is not really waiting on the corresponding monitor
* object. This is because monitors are not semaphores (ok, already
* known). Thus, semaphores should be used, since they hold "permits"
* instead of being "mutexed" by the JVM as monitors are.
* In this version, the vector of waiting processes holds semaphores
* instead of objects (monitors) thus when a process calls acquire()
* instead of wait(), the semaphore has "memory" if a previous
* release() has been made, and this is not the behaviour of notify(),
* since a notify() whithout a previous release does not imply anything.
* Why is it possible that another thread execute on leaveWriter? Well,
* it's because synchronized (this) releases the monitor and other
* process is now allowed to continue/enter the "synchronized" code of
* this object.

tocompile: shell script to compile everything (including several non necessary remoteClassv<i>). Used with
./tocompile
Uncomment the line with the rmic command if running with jre previous to version 1.5

torunask: shell script to start a "client" object (the one making the rmi).Used with
./torunask <Read/Write>
where Read ==> start a reader process
where Write ==> start a writer process

torunremote: shell script to start a "server" object (the one with the remote object to be rmi'ed). In fact, this script starts the rmiregistry as well. Used with
./torunremote
CAUTION: this script ends only with ^C or kill, and the rmiregistry should be ended by something like
killall rmiregistry

Install on a single computer
----------------------------
(Enough to see the different "servers" behavior)

1) Untar the file:
tar -xvf rmirw.tar

2) Go to the proper directory
cd rmirw

3) Compile every source file (uncomment the the line with the rmic command if running with jre previous to version 1.5):
./tocompile

4) Start the server process on a terminal:
./torunremote

(the server version/behavior is hardcoded in the file startRemoteObject.java)

Running experiments (different number/sequence of readers-writers)
--------------------------------------------------------------------

Start as many readers/writers as needed for an experiment (and in the proper sequence):
./torunask Read
(a reader process is started)

./torunask Write
(a writer process is started)

Since every process displays some (ore many) messages, it is recommended to start each one of them on a different terminal on some X interface in order to see the evolution. If the 5 hardcoded seconds are too small/large for clients (readers and/or writers), just change the lines 75 and/or 84

Thread.currentThread().sleep(5000);

on askRemote.java and recompile. The file Screenshot-full.jpg shows a run with two reader processes and a writer process, started in the sequence: reader - writer - reader

Experimenting with different server versions
--------------------------------------------

Change line 47

remoteClassv<i> robject = new remoteClassv<i>();

on startRemoteObject.java with the proper "i" (i = 1, 2, ..., 7), recompile and run as explained above ("Running experiments...")

Installing on different computers
---------------------------------

Follow the steps of "Install on a single computer" above or just copy .class files in each host to be used.

Experimenting with different computers
--------------------------------------

Follow the steps above on "Running experiments..." and "Experimenting with different..." and change line 35

java askRemote localhost $1

on torunask replacing "localhost" by the name of the host (or its IP number) on which the torunremote is running.

Report bugs/comments: ftinetti @ gmail . com (remove whitespaces), with subject "rmirw"