@@ -575,8 +575,20 @@ def createRackServletWrapper(runtime, rackup, filename)
575575
576576describe org . jruby . rack . PoolingRackApplicationFactory do
577577
578+ # Workaround rspec mocks/proxies not being thread-safe which causes occasional failures
579+ class Synchronized
580+ def initialize ( obj )
581+ @delegate = obj
582+ @lock = Mutex . new
583+ end
584+
585+ def method_missing ( name , *args , &block )
586+ @lock . synchronize { @delegate . send ( name , *args , &block ) }
587+ end
588+ end
589+
578590 before :each do
579- @factory = double "factory"
591+ @factory = Synchronized . new ( double ( "factory" ) . as_null_object )
580592 @pooling_factory = org . jruby . rack . PoolingRackApplicationFactory . new @factory
581593 @pooling_factory . context = @rack_context
582594 end
@@ -626,7 +638,7 @@ def createRackServletWrapper(runtime, rackup, filename)
626638 it "creates applications during initialization according to the jruby.min.runtimes context parameter" do
627639 allow ( @factory ) . to receive ( :init )
628640 allow ( @factory ) . to receive ( :newApplication ) do
629- app = double "app"
641+ app = Synchronized . new ( double ( "app" ) . as_null_object )
630642 expect ( app ) . to receive ( :init )
631643 app
632644 end
@@ -659,7 +671,7 @@ def createRackServletWrapper(runtime, rackup, filename)
659671 it "forces the maximum size to be greater or equal to the initial size" do
660672 allow ( @factory ) . to receive ( :init )
661673 allow ( @factory ) . to receive ( :newApplication ) do
662- app = double "app"
674+ app = Synchronized . new ( double ( "app" ) . as_null_object )
663675 expect ( app ) . to receive ( :init )
664676 app
665677 end
@@ -673,15 +685,15 @@ def createRackServletWrapper(runtime, rackup, filename)
673685 end
674686
675687 it "retrieves the error application from the delegate factory" do
676- app = double ( "app" )
688+ app = double "app"
677689 expect ( @factory ) . to receive ( :getErrorApplication ) . and_return app
678690 expect ( @pooling_factory . getErrorApplication ) . to eq app
679691 end
680692
681693 it "waits till initial runtimes get initialized (with wait set to true)" do
682694 allow ( @factory ) . to receive ( :init )
683695 allow ( @factory ) . to receive ( :newApplication ) do
684- app = double "app"
696+ app = Synchronized . new ( double ( "app" ) . as_null_object )
685697 allow ( app ) . to receive ( :init ) do
686698 sleep ( 0.05 )
687699 end
@@ -701,7 +713,7 @@ def createRackServletWrapper(runtime, rackup, filename)
701713 allow ( @factory ) . to receive ( :init )
702714 app_count = java . util . concurrent . atomic . AtomicInteger . new ( 0 )
703715 allow ( @factory ) . to receive ( :newApplication ) do
704- app = double "app"
716+ app = Synchronized . new ( double ( "app" ) . as_null_object )
705717 allow ( app ) . to receive ( :init ) do
706718 if app_count . addAndGet ( 1 ) == 2
707719 raise org . jruby . rack . RackInitializationException . new ( 'failed app init' )
@@ -739,7 +751,7 @@ def createRackServletWrapper(runtime, rackup, filename)
739751 app_init_secs = 0.2
740752 allow ( @factory ) . to receive ( :init )
741753 allow ( @factory ) . to receive ( :newApplication ) do
742- app = double "app"
754+ app = Synchronized . new ( double ( "app" ) . as_null_object )
743755 allow ( app ) . to receive ( :init ) { sleep ( app_init_secs ) }
744756 app
745757 end
@@ -757,7 +769,7 @@ def createRackServletWrapper(runtime, rackup, filename)
757769 app_init_secs = 0.2
758770 allow ( @factory ) . to receive ( :init )
759771 expect ( @factory ) . to receive ( :newApplication ) . twice do
760- app = double "app"
772+ app = Synchronized . new ( double ( "app" ) . as_null_object )
761773 expect ( app ) . to receive ( :init ) { sleep ( app_init_secs ) }
762774 app
763775 end
@@ -786,7 +798,7 @@ def createRackServletWrapper(runtime, rackup, filename)
786798 app_init_secs = 0.1
787799 allow ( @factory ) . to receive ( :init )
788800 expect ( @factory ) . to receive ( :newApplication ) . twice do
789- app = double "app (new)"
801+ app = Synchronized . new ( double ( "app (new)" ) . as_null_object )
790802 expect ( app ) . to receive ( :init ) { sleep ( app_init_secs ) }
791803 app
792804 end
@@ -802,7 +814,9 @@ def createRackServletWrapper(runtime, rackup, filename)
802814
803815 app_get_secs = 0.15
804816 expect ( @factory ) . to receive ( :getApplication ) . twice do
805- app = double "app (get)" ; sleep ( app_get_secs ) ; app
817+ app = Synchronized . new ( double ( "app (get)" ) . as_null_object )
818+ sleep ( app_get_secs )
819+ app
806820 end
807821
808822 start = java . lang . System . currentTimeMillis
@@ -817,33 +831,37 @@ def createRackServletWrapper(runtime, rackup, filename)
817831 end
818832
819833 it "initializes initial runtimes in parallel (with wait set to false)" do
834+ app_init_secs = 0.15
820835 allow ( @factory ) . to receive ( :init )
821836 allow ( @factory ) . to receive ( :newApplication ) do
822- app = double "app"
823- allow ( app ) . to receive ( :init ) do
824- sleep ( 0.15 )
825- end
837+ app = Synchronized . new ( double ( "app" ) . as_null_object )
838+ allow ( app ) . to receive ( :init ) { sleep ( app_init_secs ) }
826839 app
827840 end
841+
842+ init_threads = 4
843+ init_runtimes = 6
828844 allow ( @rack_config ) . to receive ( :getBooleanProperty ) . with ( "jruby.runtime.init.wait" ) . and_return false
829- allow ( @rack_config ) . to receive ( :getInitialRuntimes ) . and_return 6
845+ allow ( @rack_config ) . to receive ( :getRuntimeInitThreads ) . and_return init_threads
846+ allow ( @rack_config ) . to receive ( :getInitialRuntimes ) . and_return init_runtimes
830847 allow ( @rack_config ) . to receive ( :getMaximumRuntimes ) . and_return 8
831848
849+ expected_initial_init_time = 1.1 * ( init_runtimes . to_f / init_threads . to_f ) . ceil * app_init_secs # 10% margin
832850 @pooling_factory . init ( @rack_context )
833- sleep ( 0.10 )
834- expect ( @pooling_factory . getApplicationPool . size ) . to be < 6
835- sleep ( 0.9 )
836- expect ( @pooling_factory . getApplicationPool . size ) . to be >= 6
851+ sleep ( app_init_secs ) # wait for at some (but not possibly all) to finish
852+ expect ( @pooling_factory . getApplicationPool . size ) . to be < init_runtimes
853+ sleep ( expected_initial_init_time - app_init_secs ) # remaining time
854+ expect ( @pooling_factory . getApplicationPool . size ) . to be >= init_runtimes
837855
838856 expect ( @pooling_factory . getManagedApplications ) . to_not be_empty
839- expect ( @pooling_factory . getManagedApplications . size ) . to eql 6
857+ expect ( @pooling_factory . getManagedApplications . size ) . to eql init_runtimes
840858 end
841859
842860 it "throws from init when application initialization in thread failed" do
843861 app_init_secs = 0.05
844862 allow ( @factory ) . to receive ( :init )
845863 allow ( @factory ) . to receive ( :newApplication ) do
846- app = double "app"
864+ app = Synchronized . new ( double ( "app" ) . as_null_object )
847865 allow ( app ) . to receive ( :init ) { sleep ( app_init_secs ) ; raise "app.init raising" }
848866 app
849867 end
0 commit comments