private static void RubyThreadStart(RubyContext/*!*/ context, BlockParam/*!*/ startRoutine, object[]/*!*/ args, ThreadGroup group) { RubyThreadInfo info = RubyThreadInfo.FromThread(Thread.CurrentThread); info.CreatedFromRuby = true; info.Group = group; try { object threadResult; // TODO: break/returns might throw LocalJumpError if the RFC that was created for startRoutine is not active anymore: if (startRoutine.Yield(args, out threadResult) && startRoutine.Returning(threadResult, out threadResult)) { info.Exception = new ThreadError("return can't jump across threads"); } info.Result = threadResult; } catch (MethodUnwinder) { info.Exception = new ThreadError("return can't jump across threads"); } catch (Exception e) { if (info.ExitRequested) { // Note that "e" may not be ThreadAbortException at this point If an exception was raised from a finally block, // we will get that here instead Utils.Log(String.Format("Thread {0} exited.", info.Thread.ManagedThreadId), "THREAD"); info.Result = false; #if FEATURE_EXCEPTION_STATE Thread.ResetAbort(); #endif } else { e = RubyUtils.GetVisibleException(e); RubyExceptionData.ActiveExceptionHandled(e); info.Exception = e; StringBuilder trace = new StringBuilder(); trace.Append(e.Message); trace.AppendLine(); trace.AppendLine(); trace.Append(e.StackTrace); trace.AppendLine(); trace.AppendLine(); RubyExceptionData data = RubyExceptionData.GetInstance(e); if (data.Backtrace != null) { foreach (var frame in data.Backtrace) { trace.Append(frame.ToString()); } } Utils.Log(trace.ToString(), "THREAD"); if (_globalAbortOnException || info.AbortOnException) { throw; } } } finally { // Its not a good idea to terminate a thread which has set Thread.critical=true, but its hard to predict // which thread will be scheduled next, even with green threads. However, ConditionVariable.create_timer // in monitor.rb explicitly does "Thread.critical=true; other_thread.raise" before exiting, and expects // other_thread to be scheduled immediately. // To deal with such code, we release the critical monitor here if the current thread is holding it if (context.RubyOptions.Compatibility < RubyCompatibility.Ruby19 && context.CriticalThread == Thread.CurrentThread) { SetCritical(context, false); } } }