Guest User

Untitled

a guest
May 20th, 2018
244
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 5.31 KB | None | 0 0
  1. commit 7050bc18a592b779d8fb102ef7ce583902763e3a
  2. Author: Vladimir Sizikov <vsizikov@gmail.com>
  3. Date: Sat Mar 6 13:04:33 2010 +0100
  4.  
  5. JRUBY-4623: Tempfile does not clean up on GC run
  6.  
  7. Added unit test as well, but didn't wire it to the index,
  8. since these GC-related issues are not 100% reliably reproducible.
  9.  
  10. diff --git a/src/org/jruby/RubyTempfile.java b/src/org/jruby/RubyTempfile.java
  11. index b77b9f4..874b68d 100644
  12. --- a/src/org/jruby/RubyTempfile.java
  13. +++ b/src/org/jruby/RubyTempfile.java
  14. @@ -30,6 +30,9 @@ package org.jruby;
  15.  
  16. import java.io.File;
  17. import java.io.IOException;
  18. +import java.util.concurrent.ConcurrentHashMap;
  19. +import java.util.concurrent.ConcurrentMap;
  20. +
  21. import org.jruby.anno.JRubyClass;
  22. import org.jruby.anno.JRubyMethod;
  23. import org.jruby.platform.Platform;
  24. @@ -38,14 +41,22 @@ import org.jruby.runtime.ObjectAllocator;
  25. import org.jruby.runtime.ThreadContext;
  26. import org.jruby.runtime.Visibility;
  27. import org.jruby.runtime.builtin.IRubyObject;
  28. +import org.jruby.util.ReferenceReaper;
  29. import org.jruby.util.io.InvalidValueException;
  30. import org.jruby.util.io.ModeFlags;
  31. +import org.jruby.util.io.OpenFile;
  32.  
  33. /**
  34. * An implementation of tempfile.rb in Java.
  35. */
  36. @JRubyClass(name="Tempfile", parent="File")
  37. public class RubyTempfile extends RubyFile {
  38. +
  39. + /** Keep strong references to the Reaper until cleanup */
  40. + private static final ConcurrentMap<Reaper, Boolean> referenceSet
  41. + = new ConcurrentHashMap<Reaper, Boolean>();
  42. + private transient volatile Reaper reaper;
  43. +
  44. private static ObjectAllocator TEMPFILE_ALLOCATOR = new ObjectAllocator() {
  45. public IRubyObject allocate(Ruby runtime, RubyClass klass) {
  46. RubyFile instance = new RubyTempfile(runtime, klass);
  47. @@ -116,6 +127,7 @@ public class RubyTempfile extends RubyFile {
  48. path = tmp.getPath();
  49. tmpFile.deleteOnExit();
  50. initializeOpen();
  51. + referenceSet.put(reaper = new Reaper(this, runtime, tmpFile, openFile), Boolean.TRUE);
  52. return this;
  53. }
  54. } catch (IOException e) {
  55. @@ -203,6 +215,8 @@ public class RubyTempfile extends RubyFile {
  56.  
  57. @JRubyMethod(name = "close!", frame = true, visibility = Visibility.PUBLIC)
  58. public IRubyObject close_bang(ThreadContext context) {
  59. + referenceSet.remove(reaper);
  60. + reaper.released = true;
  61. _close(context);
  62. tmpFile.delete();
  63. return context.getRuntime().getNil();
  64. @@ -210,7 +224,10 @@ public class RubyTempfile extends RubyFile {
  65.  
  66. @JRubyMethod(name = {"unlink", "delete"}, frame = true)
  67. public IRubyObject unlink(ThreadContext context) {
  68. - if (tmpFile.exists()) tmpFile.delete();
  69. + if (!tmpFile.exists() || tmpFile.delete()) {
  70. + referenceSet.remove(reaper);
  71. + reaper.released = true;
  72. + }
  73. return context.getRuntime().getNil();
  74. }
  75.  
  76. @@ -241,4 +258,44 @@ public class RubyTempfile extends RubyFile {
  77.  
  78. return tempfile;
  79. }
  80. +
  81. + private static final class Reaper extends ReferenceReaper.Phantom<RubyTempfile> implements Runnable {
  82. + private volatile boolean released = false;
  83. + private final Ruby runtime;
  84. + private final File tmpFile;
  85. + private final OpenFile openFile;
  86. +
  87. + Reaper(RubyTempfile file, Ruby runtime, File tmpFile, OpenFile openFile) {
  88. + super(file);
  89. + this.runtime = runtime;
  90. + this.tmpFile = tmpFile;
  91. + this.openFile = openFile;
  92. + }
  93. +
  94. + public final void run() {
  95. + referenceSet.remove(this);
  96. + release();
  97. + clear();
  98. + }
  99. +
  100. + final void release() {
  101. + if (!released) {
  102. + released = true;
  103. + if (openFile != null) {
  104. + openFile.cleanup(runtime, false);
  105. + }
  106. + if (tmpFile.exists()) {
  107. + boolean deleted = tmpFile.delete();
  108. + if (runtime.getDebug().isTrue()) {
  109. + String msg = "removing " + tmpFile.getPath() + " ... ";
  110. + if (deleted) {
  111. + runtime.getErr().println(msg + "done");
  112. + } else {
  113. + runtime.getErr().println(msg + "can't delete");
  114. + }
  115. + }
  116. + }
  117. + }
  118. + }
  119. + }
  120. }
  121. diff --git a/test/test_tempfile_cleanup.rb b/test/test_tempfile_cleanup.rb
  122. new file mode 100644
  123. index 0000000..f95fff0
  124. --- /dev/null
  125. +++ b/test/test_tempfile_cleanup.rb
  126. @@ -0,0 +1,35 @@
  127. +require 'tempfile'
  128. +require 'java' if defined?(JRUBY_VERSION)
  129. +require 'test/unit'
  130. +require 'fileutils'
  131. +
  132. +class TestTempfilesCleanUp < Test::Unit::TestCase
  133. +
  134. + def setup
  135. + @tmpdir = "tmp_#{$$}"
  136. + FileUtils.rm_f @tmpdir rescue nil
  137. + Dir.mkdir @tmpdir rescue nil
  138. + end
  139. +
  140. + def teardown
  141. + FileUtils.rm_f @tmpdir
  142. + end
  143. +
  144. + def test_cleanup
  145. + 10.times { Tempfile.open('blah', @tmpdir) }
  146. +
  147. + # check that files were created
  148. + assert Dir["#{@tmpdir}/*"].size > 0
  149. +
  150. + 100.times do
  151. + if defined?(JRUBY_VERSION)
  152. + java.lang.System.gc
  153. + else
  154. + GC.start
  155. + end
  156. + end
  157. +
  158. + # test that the files are gone
  159. + assert_equal 0, Dir["#{@tmpdir}/*"].size, 'Files were not cleaned up'
  160. + end
  161. +end
Add Comment
Please, Sign In to add comment