findify / s3mock

Embedded S3 server for easy mocking
MIT License
387 stars 107 forks source link

The Java process does not terminate after executing S3Mock.stop() from main method #67

Closed csdivi closed 6 years ago

csdivi commented 6 years ago

Trying to use S3Mock in a standalone Java program, with the following main method.

public static void main(String args[]) { S3Mock api = new S3Mock.Builder().withPort(8001).withInMemoryBackend().build(); api.start(); api.stop(); }

The expectation here is, the Java program should terminate once the execution of the main method is complete.

The process hangs even after executing api.stop();

Is there a way to solve this issue?

Brahma-Nath commented 6 years ago

I am facing the same issue. Were you able to get it working

Brahma-Nath commented 6 years ago

Following code worked for me ActorSystem actor = S3Mock.$lessinit$greater$default$3(8050, new InMemoryProvider()); s3Client= new S3Mock(8050, new InMemoryProvider(), actor); s3Client.start();

    cs = CoordinatedShutdown.get(actor);

. . . . . CompletionStage result = cs.runAll();

    s3Client.stop();

This worked with java 8

spinscale commented 6 years ago

this hit me as well, but the above mentioned workaround is incomplete or does not work, at least on kotlin

spinscale commented 6 years ago

FYI, here is the jstack output, that might help identify which resources have not been closed properly, evne though api.stop() returned successfully

2017-11-11 23:24:41
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.111-b14 mixed mode):

"Attach Listener" #20 daemon prio=9 os_prio=31 tid=0x00007fd66c0d3000 nid=0x5507 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

   Locked ownable synchronizers:
    - None

"s3mock-akka.actor.default-dispatcher-8" #19 prio=5 os_prio=31 tid=0x00007fd667f90000 nid=0x1307 waiting on condition [0x000070000e373000]
   java.lang.Thread.State: WAITING (parking)
    at sun.misc.Unsafe.park(Native Method)
    - parking to wait for  <0x00000006c0690400> (a akka.dispatch.ForkJoinExecutorConfigurator$AkkaForkJoinPool)
    at akka.dispatch.forkjoin.ForkJoinPool.scan(ForkJoinPool.java:2075)
    at akka.dispatch.forkjoin.ForkJoinPool.runWorker(ForkJoinPool.java:1979)
    at akka.dispatch.forkjoin.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:107)

   Locked ownable synchronizers:
    - None

"DestroyJavaVM" #17 prio=5 os_prio=31 tid=0x00007fd66c0c4000 nid=0x1c03 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

   Locked ownable synchronizers:
    - None

"s3mock-akka.io.pinned-dispatcher-6" #16 prio=5 os_prio=31 tid=0x00007fd66a2f7800 nid=0x5703 runnable [0x000070000e270000]
   java.lang.Thread.State: RUNNABLE
    at sun.nio.ch.KQueueArrayWrapper.kevent0(Native Method)
    at sun.nio.ch.KQueueArrayWrapper.poll(KQueueArrayWrapper.java:198)
    at sun.nio.ch.KQueueSelectorImpl.doSelect(KQueueSelectorImpl.java:117)
    at sun.nio.ch.SelectorImpl.lockAndDoSelect(SelectorImpl.java:86)
    - locked <0x0000000773a7d738> (a sun.nio.ch.Util$3)
    - locked <0x0000000773a7d728> (a java.util.Collections$UnmodifiableSet)
    - locked <0x0000000773a7bf50> (a sun.nio.ch.KQueueSelectorImpl)
    at sun.nio.ch.SelectorImpl.select(SelectorImpl.java:97)
    at akka.io.SelectionHandler$ChannelRegistryImpl$$anon$3.tryRun(SelectionHandler.scala:128)
    at akka.io.SelectionHandler$ChannelRegistryImpl$Task.run(SelectionHandler.scala:246)
    at akka.io.SelectionHandler$ChannelRegistryImpl$$anon$3.run(SelectionHandler.scala:161)
    at akka.util.SerializedSuspendableExecutionContext.run$1(SerializedSuspendableExecutionContext.scala:67)
    at akka.util.SerializedSuspendableExecutionContext.run(SerializedSuspendableExecutionContext.scala:71)
    at akka.dispatch.TaskInvocation.run(AbstractDispatcher.scala:40)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:745)

   Locked ownable synchronizers:
    - <0x0000000773a7bb70> (a java.util.concurrent.ThreadPoolExecutor$Worker)

"s3mock-scheduler-1" #10 prio=5 os_prio=31 tid=0x00007fd667eef800 nid=0x4d03 sleeping[0x000070000dd61000]
   java.lang.Thread.State: TIMED_WAITING (sleeping)
    at java.lang.Thread.sleep(Native Method)
    at akka.actor.LightArrayRevolverScheduler.waitNanos(LightArrayRevolverScheduler.scala:85)
    at akka.actor.LightArrayRevolverScheduler$$anon$4.nextTick(LightArrayRevolverScheduler.scala:265)
    at akka.actor.LightArrayRevolverScheduler$$anon$4.run(LightArrayRevolverScheduler.scala:235)
    at java.lang.Thread.run(Thread.java:745)

   Locked ownable synchronizers:
    - None

"Service Thread" #8 daemon prio=9 os_prio=31 tid=0x00007fd668821000 nid=0x4903 runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

   Locked ownable synchronizers:
    - None

"C1 CompilerThread2" #7 daemon prio=9 os_prio=31 tid=0x00007fd667807800 nid=0x4703 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

   Locked ownable synchronizers:
    - None

"C2 CompilerThread1" #6 daemon prio=9 os_prio=31 tid=0x00007fd667809800 nid=0x4503 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

   Locked ownable synchronizers:
    - None

"C2 CompilerThread0" #5 daemon prio=9 os_prio=31 tid=0x00007fd667041800 nid=0x4303 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

   Locked ownable synchronizers:
    - None

"Signal Dispatcher" #4 daemon prio=9 os_prio=31 tid=0x00007fd668037000 nid=0x4103 runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

   Locked ownable synchronizers:
    - None

"Finalizer" #3 daemon prio=8 os_prio=31 tid=0x00007fd66706d800 nid=0x3103 in Object.wait() [0x000070000d64c000]
   java.lang.Thread.State: WAITING (on object monitor)
    at java.lang.Object.wait(Native Method)
    - waiting on <0x00000006c0013e78> (a java.lang.ref.ReferenceQueue$Lock)
    at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:143)
    - locked <0x00000006c0013e78> (a java.lang.ref.ReferenceQueue$Lock)
    at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:164)
    at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:209)

   Locked ownable synchronizers:
    - None

"Reference Handler" #2 daemon prio=10 os_prio=31 tid=0x00007fd66706a800 nid=0x2f03 in Object.wait() [0x000070000d549000]
   java.lang.Thread.State: WAITING (on object monitor)
    at java.lang.Object.wait(Native Method)
    - waiting on <0x00000006c0004928> (a java.lang.ref.Reference$Lock)
    at java.lang.Object.wait(Object.java:502)
    at java.lang.ref.Reference.tryHandlePending(Reference.java:191)
    - locked <0x00000006c0004928> (a java.lang.ref.Reference$Lock)
    at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153)

   Locked ownable synchronizers:
    - None

"VM Thread" os_prio=31 tid=0x00007fd667066000 nid=0x2d03 runnable 

"GC task thread#0 (ParallelGC)" os_prio=31 tid=0x00007fd668805000 nid=0x2503 runnable 

"GC task thread#1 (ParallelGC)" os_prio=31 tid=0x00007fd668805800 nid=0x2703 runnable 

"GC task thread#2 (ParallelGC)" os_prio=31 tid=0x00007fd668806800 nid=0x2903 runnable 

"GC task thread#3 (ParallelGC)" os_prio=31 tid=0x00007fd668807000 nid=0x2b03 runnable 

"VM Periodic Task Thread" os_prio=31 tid=0x00007fd6680a5000 nid=0x4b03 waiting on condition 

JNI global references: 2415
spinscale commented 6 years ago

correction to my above statement. The workaround does work, I must have done something wrong in the first place, thanks for writing that!

schoeneu commented 6 years ago

Ok, so the issue here is that S3Mock::stop does not terminate the actor system. Unfortunately, I don't think the fix is quite as easy as adding Await.result(system.terminate(), Duration.Inf) to the stop() method:

S3Mock has an implicit ActorSystem parameter with a default value, so (correct me if I'm wrong here) there is no way to know whether we were provided with an ActorSystem from the outside or whether we created one ourself. In the former case, we must not terminate the system - it belongs to the caller - while in the latter case, we should not leak it.

Can anyone (@shuttie ?) think of a use case in which you'd want to inject an ActorSystem ? I think the cleanest solution would be to always create/destroy it locally, but that's a breaking change to the API and restricts functionality.

Alternatively, we could change the implicit parameter to an explicit Option[ActorSystem] parameter. This breaks the API for users of new S3Mock(...) (but really, should there be any outside of the project itself? There is S3Mock::apply for callers), but in a way that is easy to fix via Some(implicitly[ActorSystem[).

Thoughts?

shuttie commented 6 years ago

Should be fixed in 0.2.5, via the separate shutdown() call. At least this approach doesn't break the api :)