Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Digging deeper into the excessive unexport problem by adding more
diagnostics.

We want to track where the unexport command is created on the other side
of the channel.
  • Loading branch information
kohsuke committed May 20, 2014
1 parent 6434c43 commit 1eeccd4
Show file tree
Hide file tree
Showing 9 changed files with 41 additions and 21 deletions.
4 changes: 2 additions & 2 deletions src/main/java/hudson/remoting/Channel.java
Expand Up @@ -596,8 +596,8 @@ public <T> T export(Class<T> type, T instance) {
return exportedObjects.get(oid);
}

/*package*/ void unexport(int id) {
exportedObjects.unexportByOid(id);
/*package*/ void unexport(int id, Throwable cause) {
exportedObjects.unexportByOid(id,cause);
}

/**
Expand Down
36 changes: 26 additions & 10 deletions src/main/java/hudson/remoting/ExportTable.java
Expand Up @@ -104,15 +104,15 @@ void pin() {
referenceCount += Integer.MAX_VALUE/2;
}

void release() {
void release(Throwable callSite) {
if(--referenceCount==0) {
table.remove(id);
reverse.remove(object);

// hack to store some information about the object that got unexported
// don't want to keep the object alive, and toString() seems bit risky
object = (T)object.getClass().getName();
releaseTrace = new ReleasedAt();
releaseTrace = new ReleasedAt(callSite);

unexportLog.add(this);
while (unexportLog.size()>UNEXPORT_LOG_SIZE)
Expand Down Expand Up @@ -148,20 +148,34 @@ String dump() {
static class Source extends Exception {
protected final long timestamp = System.currentTimeMillis();

Source() {
/**
* @param callSite
* Optional location that indicates where the actual call site was that triggered the activity,
* in case it was requested from the other side of the channel.
*/
Source(Throwable callSite) {
super(callSite);
// force the computation of the stack trace in a Java friendly data structure,
// so that the call stack can be seen from the heap dump after the fact.
getStackTrace();
}
}

static class CreatedAt extends Source {
CreatedAt() {
super(null);
}

public String toString() {
return " Created at "+new Date(timestamp);
}
}

static class ReleasedAt extends Source {
ReleasedAt(Throwable callSite) {
super(callSite);
}

public String toString() {
return " Released at "+new Date(timestamp);
}
Expand All @@ -179,10 +193,10 @@ private ExportList() {
old=lists.get();
lists.set(this);
}
void release() {
void release(Throwable callSite) {
synchronized(ExportTable.this) {
for (Entry e : this)
e.release();
e.release(callSite);
}
}
void stopRecording() {
Expand Down Expand Up @@ -215,7 +229,7 @@ public boolean isRecording() {
* Exports the given object.
*
* <p>
* Until the object is {@link #unexport(Object) unexported}, it will
* Until the object is {@link #unexport(Object,Throwable) unexported}, it will
* not be subject to GC.
*
* @return
Expand Down Expand Up @@ -280,27 +294,29 @@ private synchronized IllegalStateException diagnoseInvalidId(int id) {
/**
* Removes the exported object from the table.
*/
public synchronized void unexport(T t) {
synchronized void unexport(T t, Throwable callSite) {
if(t==null) return;
Entry e = reverse.get(t);
if(e==null) {
LOGGER.log(SEVERE, "Trying to unexport an object that's not exported: "+t);
return;
}
e.release();
e.release(callSite);
}

/**
* Removes the exported object for the specified oid from the table.
*/
public synchronized void unexportByOid(Integer oid) {
public synchronized void unexportByOid(Integer oid, Throwable callSite) {
if(oid==null) return;
Entry e = table.get(oid);
if(e==null) {
LOGGER.log(SEVERE, "Trying to unexport an object that's already unexported", diagnoseInvalidId(oid));
if (callSite!=null)
LOGGER.log(SEVERE, "2nd unexport attempt is here", callSite);
return;
}
e.release();
e.release(callSite);
}

/**
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/hudson/remoting/Pipe.java
Expand Up @@ -216,7 +216,7 @@ protected void execute(final Channel channel) {
public void run() {
try {
final ProxyOutputStream ros = (ProxyOutputStream) channel.getExportedObject(oidRos);
channel.unexport(oidRos);
channel.unexport(oidRos,createdAt);
// the above unexport cancels out the export in writeObject above
ros.connect(channel, oidPos);
} catch (IOException e) {
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/hudson/remoting/ProxyInputStream.java
Expand Up @@ -149,7 +149,7 @@ public EOF(int oid) {

protected void execute(Channel channel) {
InputStream in = (InputStream) channel.getExportedObject(oid);
channel.unexport(oid);
channel.unexport(oid,createdAt);
try {
in.close();
} catch (IOException e) {
Expand Down
4 changes: 2 additions & 2 deletions src/main/java/hudson/remoting/ProxyOutputStream.java
Expand Up @@ -349,7 +349,7 @@ public Unexport(int ioId, int oid) {
protected void execute(final Channel channel) {
channel.pipeWriter.submit(ioId,new Runnable() {
public void run() {
channel.unexport(oid);
channel.unexport(oid,createdAt);
}
});
}
Expand Down Expand Up @@ -381,7 +381,7 @@ protected void execute(final Channel channel) {
final OutputStream os = (OutputStream) channel.getExportedObject(oid);
markForIoSync(channel,requestId,channel.pipeWriter.submit(ioId,new Runnable() {
public void run() {
channel.unexport(oid);
channel.unexport(oid,createdAt);
try {
if (error!=null && os instanceof ErrorPropagatingOutputStream)
((ErrorPropagatingOutputStream) os).error(error);
Expand Down
4 changes: 2 additions & 2 deletions src/main/java/hudson/remoting/ProxyWriter.java
Expand Up @@ -319,7 +319,7 @@ public Unexport(int ioId, int oid) {
protected void execute(final Channel channel) {
channel.pipeWriter.submit(ioId,new Runnable() {
public void run() {
channel.unexport(oid);
channel.unexport(oid,createdAt);
}
});
}
Expand Down Expand Up @@ -347,7 +347,7 @@ protected void execute(final Channel channel) {
final Writer os = (Writer) channel.getExportedObject(oid);
channel.pipeWriter.submit(ioId, new Runnable() {
public void run() {
channel.unexport(oid);
channel.unexport(oid,createdAt);
try {
os.close();
} catch (IOException e) {
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/hudson/remoting/UnexportCommand.java
Expand Up @@ -35,7 +35,7 @@ public UnexportCommand(int oid) {
}

protected void execute(Channel channel) {
channel.unexport(oid);
channel.unexport(oid,createdAt);
}

private static final long serialVersionUID = 1L;
Expand Down
6 changes: 5 additions & 1 deletion src/main/java/hudson/remoting/UserRequest.java
Expand Up @@ -183,7 +183,11 @@ private byte[] serialize(Object o, Channel localChannel) throws IOException {
}

public void releaseExports() {
exports.release();
releaseExports(null);
}

/*package*/ void releaseExports(Throwable callSite) {
exports.release(callSite);
}

public String toString() {
Expand Down
2 changes: 1 addition & 1 deletion src/test/java/hudson/remoting/ExportTableTest.java
Expand Up @@ -15,7 +15,7 @@ public void testDiagnosis() throws Exception {
int i = e.export("foo");
assertEquals("foo", e.get(i));

e.unexportByOid(i);
e.unexportByOid(i,null);
try {
e.get(i);
fail();
Expand Down

0 comments on commit 1eeccd4

Please sign in to comment.