|
Description
|
FULL PRODUCT VERSION :
java version "1.4.1_01"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.1_01-b01)
Java HotSpot(TM) Client VM (build 1.4.1_01-b01, mixed mode)
FULL OPERATING SYSTEM VERSION :
Red Hat Linux release 7.1 (Seawolf)
glibc-2.2.2-10
Linux 2.4.2-2 #1 Sun Apr 8 20:41:30 EDT 2001 i686
EXTRA RELEVANT SYSTEM CONFIGURATION :
PostgreSQL version 7.2.1
A DESCRIPTION OF THE PROBLEM :
The ThreadPool included in the package
com.sun.corba.se.internal.orbutil may assign several
"works" for a unique thread. Those works may have some
dependencies, and a dead-lock may appear.
For instance, if one thread requests a "work" that hangs
due to a database conflict and if there is another thread
that requests a "work" that releases this conflict, and
the two threads are processed by the same PooledThread, the
system will hang. Because the request of the second thread
will never be processed (as the PooledThread is blocked
with the first request).
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
It is difficult to reproduce the problem, so what I
have done is add some debug lines to the ThreadPool
class to see how many available threads are there and
also to see the size of the work to do queue. In that
way we can see that sometimes there are more than one
work being performed by only one thread(that is
exactly what the bug is)
The steps to follow with the sample code I has given to
you are:
1. compile the idl file (idlj -oldImplBase -fall
server.idl)
2. compile the client and server classes.
3. modify the java runtime (rt.java) by adding the
ThreadPool class with the debug messages.
4. run the name server (tnameserv -ORBInitialPort 5000)
5. run the server (java ServerImpl -ORBInitialPort 5000)
6. run the client with the script I have given to
you(findBug "number of clients" "number of
invocations", for instance, you can run "findBug 10
10000").
7. You will see on screen when the bug appears (because
server will finish)
8. The result of executing "findBug 10 10000" is this
(you will have to wait
or maybe you will need to run it with more clients):
.......
-> Available workers = 1 Works in queue: 1
-> Available workers = 1 Works in queue: 2
BUG FOUND!!! Only one thread proccessing two different
works!!!!! If there are dependencies system will hang!
And system will exit...
EXPECTED VERSUS ACTUAL BEHAVIOR :
The system must allow invocations to any of the customer
servers, but it hangs and they are never called. The
invocation that releases some resources is never
called, and it must be called in order to continue
running the application.
ERROR MESSAGES/STACK TRACES THAT OCCUR :
The system hangs. No error messages.
REPRODUCIBILITY :
This bug can be reproduced often.
---------- BEGIN SOURCE ----------
We have modified the ThreadPool original class with some debug lines
to see the number of available workers and the size of the work to do queue
(a new method to get the size of the queue has been added)
Every time a work is added to the queue a message is printed on screen. If
there are more than one work to be processed by a unique Thread the system
will finish the server application.
-----------------------------------------------------------------------------
file "ThreadPool.java"
---------------------------------------------------------------------------
....
private static final class WorkQueue
{
....
final int size() { return workToDo.size();}
....
}
....
public synchronized void addWork(Work work)
{
....
System.out.println(" -> Available workers = "+availableWorkers+"
Works in queue: "+workToDo.size());
if(availableWorkers>0 && availableWorkers<workToDo.size()) {
System.out.println("BUG FOUND!!! Only one thread proccessing two
different works!!!!! If there are dependencies system will hang!");
System.exit(-1);
}
....
---------------------------------------------------------------------------
This is a test case where the bug appears:
------------------------------------------------------
file "server.idl"
------------------------------------------------------
typedef sequence<octet> Data;
interface Server {
long sendMessage(in Data msg);
};
------------------------------------------------------
file "ServerImpl.java"
--------------------------------------------------------------------------
// Input/Output
import java.io.*;
// The Server will use the naming service.
import org.omg.CosNaming.*;
// The package containing special exceptions thrown by the name service.
import org.omg.CosNaming.NamingContextPackage.*;
// All CORBA applications need these classes.
import org.omg.CORBA.*;
public class ServerImpl extends _ServerImplBase {
public ServerImpl() {}
public static void main(String args[])
{
try {
// Register CoplaManagerServer through ORB subsystem
register(args);
java.lang.Object sync = new java.lang.Object();
synchronized(sync){
sync.wait();
}
} catch(Throwable e) {
System.err.println("\nERROR: " + e +"\n");
e.printStackTrace();
}
}
private static void register(String[] args)
throws Exception {
// Create and initialize the ORB to register the CoplaManagerServer
ORB orb = ORB.init(args, null);
// Get the root naming context
org.omg.CORBA.Object objRef =
orb.resolve_initial_references("NameService");
NamingContext ncRef = NamingContextHelper.narrow(objRef);
ServerImpl server = new ServerImpl();
// Register the Copla Manager Server with the ORB
orb.connect(server);
// Bind the server customer reference in naming
NameComponent _nc = new NameComponent( "TestServer", "");
NameComponent _path[] = {_nc};
ncRef.rebind(_path, server);
}//register
// Send Message remote method
public int sendMessage(byte[] data) {
return 0;
}
}
-----------------------------------------------------------------------
file "Client.java"
-----------------------------------------------------------------------
// Input/Output
import java.io.*;
// The Server will use the naming service.
import org.omg.CosNaming.*;
// The package containing special exceptions thrown by the name service.
import org.omg.CosNaming.NamingContextPackage.*;
// All CORBA applications need these classes.
import org.omg.CORBA.*;
public class Client {
public Client() {}
public static void main(String args[])
{
try {
Server s = resolveServer();
for(int i=0; i<Integer.parseInt(args[0]); i++) {
s.sendMessage(new byte[2]);
}
} catch(Throwable e) {
System.err.println("\nERROR: " + e +"\n");
e.printStackTrace();
}
}
private static Server resolveServer() throws Exception {
// Create and initialize the ORB to resolve UDS
String[] ORBprops = new String[]{"-ORBInitialHost",
"localhost","-ORBInitialPort", "5000"};
ORB orb = ORB.init(ORBprops, null);
// Get the root naming context
org.omg.CORBA.Object objRef =
orb.resolve_initial_references("NameService");
NamingContext ncRef = NamingContextHelper.narrow(objRef);
// Resolve the UDSServer reference in naming
NameComponent nc = new NameComponent( "TestServer", "");
NameComponent path[] = {nc};
return ServerHelper.narrow(ncRef.resolve(path));
}//resolve
}
--------------------------------------------------------------------
file "findBug"
--------------------------------------------------------------------
i=0
while [ $i -lt $1 ]; do
echo launch $i ...
java Client $2 &
i=$[i+1]
done
--------------------------------------------------------------------
---------- END SOURCE ----------
CUSTOMER WORKAROUND :
The threads in the addWork method of the ThreadPool
class must be created when the queue is larger than the
number of the availableWorkers. If so, there will never
be one PooledThread processing different works.
A new method must be added in the WorkQueue to retrieve
the size of the queue. And the condition inside the
addWork method must be changed to:
if(workToDo.size() > availableWorkers) {
//here we must create all the threads needed.
... ... ...
}
(Review ID: 179200)
======================================================================
|