Bug 10108

Summary: mono_jit_cleanup asserts/aborts on shutdown if soft debugging
Product: [Mono] Runtime Reporter: Jeff Slutter <Mr.Mustard>
Component: DebuggerAssignee: Bugzilla <bugzilla>
Status: RESOLVED NORESPONSE    
Severity: normal CC: cooper, david.karlas, johan.lorensson, kumpera, ludovic, miguel, mono-bugs+mono, mono-bugs+runtime, nam, pinggi, vargaz
Priority: ---    
Version: unspecified   
Target Milestone: ---   
Hardware: PC   
OS: Windows   
Tags: Is this bug a regression?: ---
Last known good build:

Description Jeff Slutter 2013-02-07 01:01:14 UTC
I am embedding Mono and using version 3.0.3, although, I also had this bug with 3.0.2. To the best of my knowledge it did not happen in 2.11.4.

If I add the necessary calls to setup my application for soft debugging support, I get an assertion failed (leading to an abort) on shutdown when I call mono_jit_cleanup.

The assertion message:
* Assertion at mono-threads-windows.c:99, condition `!mono_domain_get ()' not met

I was able to reproduce the bug in the test-invoke sample that comes with Mono's source. I changed the main function to be the following (and added necessary header includes):

int 
main (int argc, char* argv[]) {
	MonoDomain *domain;
	const char *file;
	int retval;
	const char *jitOptions[10] = { 0 };
	int         numJitOptions  = 0;
	
	if (argc < 2){
		fprintf (stderr, "Please provide an assembly to load\n");
		return 1;
	}
	file = argv [1];

	mono_set_dirs( PATH_TO_LIB, PATH_TO_ETC );

	jitOptions[numJitOptions++] = "--debugger-agent=transport=dt_socket,address=127.0.0.1:10000";
	mono_jit_parse_options( numJitOptions, (char**)jitOptions );

	/*
	 * mono_jit_init() creates a domain: each assembly is
	 * loaded and run in a MonoDomain.
	 */
	mono_debug_init( MONO_DEBUG_FORMAT_MONO );

	domain = mono_jit_init (file);
	mono_debug_domain_create( domain );

	main_function (domain, file, argc - 1, argv + 1);

	retval = mono_environment_exitcode_get ();
	
	mono_jit_cleanup (domain);
	return retval;
}
Comment 1 Zoltan Varga 2013-02-08 20:36:24 UTC
Is this on windows ? Also, what debugger are you connecting to at 127.0.0.1:10000 ?
Comment 2 Jeff Slutter 2013-02-08 21:54:57 UTC
Sorry, yes, this is on Windows, it happens on multiple machines, all of which I believe are Windows 7 x64 (I haven't tried it on a non-Windows 7 x64).

As for the debugger, I should have been more detailed. I took the source code of the soft debugger that comes with MonoDevelop (3.0.6), and modified it to fix its process spawning bug. This is the soft debugger plugin that is enabled via the MONODEVELOP_SDB_TEST environment variable. 

There is a bug in the original plugin for when it spawns a process, how it captures stdout/stderr of the process ("System.InvalidOperationException: StandardError has not been redirected.").

After fixing the bug I am able to spawn my process, actually using the jit option: "--debugger-agent=transport=dt_socket,address=127.0.0.1:10000,server=y". The MonoDevelop plugin spawns my process, and I can debug just fine. On exit, I call mono_jit_cleanup, and that is when I get the assert.

Trying to reproduce the assert using MonoDevelop with its soft debugger plugin, I can only use the "Listen" option (due to the process spawning bug it has). The assert still happens, as I can hear Windows *ding* as it brings up the abort messagebox (which immediately closes because the process is exiting), but I don't get to see the message detailing the abort.

While writing this reply, I just had the realization that this may have happened today without the soft-debugging code enabled - meaning it might have nothing to do with soft debugging, it's just that soft debugging makes it happen much much more often.

Would it help if I made a complete source code sample? Providing source code for a MonoDevelop plugin and an embedded sample?
Comment 3 cooper 2013-09-23 12:37:48 UTC
Also happens for me. Doesn't happen when running without debugging.
Comment 4 cooper 2013-09-23 12:39:53 UTC
Though, I don't use any special code for Mono soft debugger. Just a usual app with threads. Maybe it's important that my Main method completes before the threads do.
Comment 5 pinggi 2013-10-12 18:26:07 UTC
I confirm that too.

Message:

* Assertion at mono-threads-windows.c:99, condition `!mono_domain_get ()' not met

Code preview:

  calcThread = new CreateCalcThread();
  calcThread.Start();

  WindowApp.Run();

  calcThread.Stop();



System.Environment
  Non-public members:
    GacPath: "C:\\Program Files (x86)\\Mono-3.2.3\\lib\\mono\\gac"
    IsMacOS: false
    IsRunningOnwindows: true
    Is Unix: false
    mono_corlib_version: 110
    nl: "\r\n"
    os: {Microsoft Windows NT 6.1.7601.65536 Service Pack 1}
    Platform: Win32NT
Comment 6 pinggi 2014-03-23 12:50:27 UTC
The bug still exists...in Xamarin Studio: 4.2.3 (build 60), Mono 3.3 runtime.

I just created a new console app and run it in debug mode.

The console output changes every run. It looks like the sentence "Press any key to continue . . ." would overwrite the text on the screen in random position:

1.
Hello World!
* Assertion at mono-threads-windows.c:99, condition `!mono_dPress any key to continue . . .

2.
Hello World!
* Assertion at mono-threads-windows.c:99, condition `!mPress any key to continue . . .

3.
Hello World!
* Assertion at mono-threads-windows.c:99, condition `!moPress any key to continue . . .


Tried to switch to Microsoft.NET runtime and it was ok. So the problem is with mono in debug mode when asserts are checked.
Comment 7 Nam Duong 2014-04-09 03:50:38 UTC
I have this issue too.

Does not happen with .NET run time.
Does not happen with release build.

Windows 7 Home Premium 64bit
Xamarin Studio 4.2.3 Build 60. (Just installed, all in default settings)
Mono 3.2.3
Comment 8 David Karlaš 2014-05-09 08:37:29 UTC
When I say debugger I mean XS 5.0(doesn't really matter).

Here is code sample :
using System;
using System.Diagnostics;
using System.Threading;
using System.Linq;

namespace ConsoleProject34
{
	class MainClass
	{
		public static void Main (string[] args)
		{
			var manEvent = new ManualResetEvent (false);
			var threads = new Thread [100];
			for (int i = 0; i < threads.Length; i++) {
				threads [i] = new Thread (new ThreadStart (delegate {
					manEvent.WaitOne ();
				}));
				threads [i].Start ();
			}
			Thread.Sleep (1000);
			Stopwatch SW = Stopwatch.StartNew ();
			for (int i = 0; i < 20; i++) {
				Console.WriteLine (SW.ElapsedMilliseconds + " " + threads.Count ((t) => t.ThreadState != System.Threading.ThreadState.Stopped));
				Thread.Sleep (1000);
				if (i == 5)
					manEvent.Set ();
			}
		}
	}
}

Here is result without debugger connected:(all good)
0 100
1126 100
2127 100
3129 100
4130 100
5131 100
6137 0
7138 0
/*trimmed rest of steps with 1 sec delay*/

Here is result with debugger connected on 3.4.1(I built this):(notice Thread.Sleep being interrupted because threads are stopping)
0 100
1080 100
2081 100
3081 100
4082 100
5083 100
6145 26
6151 19
6162 13
6170 5
6178 0
7179 0
8180 0
/*trimmed rest of steps with 1 sec delay*/
* Assertion at mono-threads-windows.c:102, condition `!mono_domain_get ()' not met

Here is result with debugger on 2.10.9(from website) This probably means Bug 2190 is fixed in Mono 3.4 but notice that first Thread.Sleep was interrupted at 236 ms.
0 0
236 0
1237 0
2237 0
/*trimmed rest of steps with 1 sec delay*/
Press any key to continue . . .
Comment 9 Zoltan Varga 2014-05-09 19:01:23 UTC
Removed the assertion, it doesn't seem to be needed.
Comment 10 Zoltan Varga 2014-05-09 19:03:21 UTC
The sleep problem seems to be caused by 
 ves_icall_System_Threading_Thread_Sleep_internal () not handling Sleep () returning WAIT_IO_COMPLETION. This doesn't happen on unix because our Sleep () implementation handles this internally.
Comment 11 Miguel de Icaza [MSFT] 2014-09-07 10:31:45 UTC
Odd, the ves_icall_System_Threading_Thread_Sleep_internal does handle WAIT_IO_COMPLETION:

                res = SleepEx(ms,TRUE);

                mono_thread_clr_state (thread, ThreadState_WaitSleepJoin);

                if (res == WAIT_IO_COMPLETION) { /* we might have been interrupted */
Comment 12 Zoltan Varga 2014-09-10 22:21:35 UTC
That code does handle the interruption, but it should restart the sleep if there is time left, just as our SleepEx () implementation does on unix.
Comment 13 johan.lorensson 2016-07-08 07:13:49 UTC
This could be related and fixed by the following PR:

https://github.com/mono/mono/pull/3008
Comment 14 Rodrigo Kumpera 2017-07-14 23:54:10 UTC
Can you reproduce the issue with a recent mono? Mono 5.2 includes the above fix.
Comment 15 Ludovic Henry 2017-08-17 17:27:47 UTC
Please reopen if you can still reproduce. Thank you.