Sicos1977 / ChromiumHtmlToPdf

Convert HTML to PDF with a Chromium based browser

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Crashes the entire program when trying to reinitialize the converter after disposing

ahmad-qamar opened this issue · comments

System.NullReferenceException: Object reference not set to an instance of an object.
   at ChromeHtmlToPdfLib.Converter._chromeProcess_Exited(Object sender, EventArgs e)
   at System.Diagnostics.Process.OnExited()
   at System.Diagnostics.Process.RaiseOnExited()
   at System.Diagnostics.Process.CompletionCallback(Object waitHandleContext, Boolean wasSignaled)
   at System.Threading._ThreadPoolWaitOrTimerCallback.WaitOrTimerCallback_Context_f(Object state)
   at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
--- End of stack trace from previous location ---
   at System.Threading._ThreadPoolWaitOrTimerCallback.PerformWaitOrTimerCallback(_ThreadPoolWaitOrTimerCallback helper, Boolean timedOut)
   at System.Threading.PortableThreadPool.CompleteWait(RegisteredWaitHandle handle, Boolean timedOut)
   at System.Threading.ThreadPoolWorkQueue.Dispatch()
   at System.Threading.PortableThreadPool.WorkerThread.WorkerThreadStart()
   at System.Threading.Thread.StartCallback()

Hey! I've been trying to use this library for image generation however upon running on a Linux VPS with low memory, running a Chrome instance for too long causes it to entirely freezing (probably bcs of Chrome memory leaks or something, don't know specifically), making me manually restart it several times which is definitely suboptimal but have to do it.

To mitigate that I've been trying to manually reinitialize the Converter instance, by disposing the earlier one first, and have come across this error. This just crashes the entire program/process.

commented

Can you turn on logging and see if Chrome gets shuts down correctly?

commented

I expect to see logging from the dispose method

        #region Dispose
        /// <summary>
        ///     Disposes the running <see cref="_chromeProcess" />
        /// </summary>
        public void Dispose()
        {
            if (_disposed) 
                return;

            _chromeWaitEvent?.Dispose();
            _chromeWaitEvent = null;

            if (_browser != null)
            {
                try
                {
                    WriteToLog("Closing Chrome browser gracefully");
                    _browser.Close();
                    _browser.Dispose();
                }
                catch (Exception exception)
                {
                    WriteToLog($"An error occurred while trying to close Chrome gracefully, error '{ExceptionHelpers.GetInnerException(exception)}'");
                }
            }

            var counter = 0;

            // Give Chrome 2 seconds to close
            while (counter < 200)
            {
                if (!IsChromeRunning)
                {
                    WriteToLog("Chrome closed gracefully");
                    break;
                }

                counter++;
                Thread.Sleep(10);
            }

            if (IsChromeRunning)
            {
                // Sometimes Chrome does not close all processes so kill them
                WriteToLog($"Chrome did not close gracefully, closing it by killing it's process on id '{_chromeProcess.Id}'");
                KillProcessAndChildren(_chromeProcess.Id);
                WriteToLog("Chrome killed");

                _chromeProcess = null;
            }

            _disposed = true;
        }
        #endregion

Well I've realized the issue I guess, I'd also been trying to exit the Chrome processes outside of the library itself via Process.GetProcessesByName("chrome"), because it doesn't exit other Chrome processes when the parent process is killed, and it just starts multiple chrome instances.

Nevertheless, logically the _chromeProcess_Exited shouldn't terminate the entire program itself regardless of what the user is doing, and the exceptions should be properly handled.

Could there also be any logic in place that kills the previous Chrome instances, could be done by adding an identifier (such as namespace/executable path of the running program) in the command line arguments while starting Chrome (arguments shouldn't have effect on chrome runtime) e.g the namespace or executable path of the starting application, and in a utility static method in the library iterate through the processes while checking those and end them?

Well nevermind that, I removed it and it still has a similar result.

So on the time of Disposing the Converter, it gives:

2022-12-31T17:50:10.380 - Closing Chrome browser gracefully
2022-12-31T17:50:10.384 - Disposing websocket connection to url 'ws://127.0.0.1:38853/devtools/page/x'
2022-12-31T17:50:10.384 - Closing websocket
2022-12-31T17:50:10.387 - Websocket connection disposed
2022-12-31T17:50:10.387 - Disposing websocket connection to url 'ws://127.0.0.1:38853/devtools/browser/x'
2022-12-31T17:50:10.387 - Closing websocket
2022-12-31T17:50:10.388 - Websocket connection disposed
2022-12-31T17:50:10.737 - Chrome exited unexpectedly, arguments used: --headless --disable-gpu --hide-scrollbars --mute-audio --disable-background-networking --disable-background-timer-throttling --disable-de$2022-12-31T17:50:10.737 - Process id: 5514
2022-12-31T17:50:10.737 - Process exit time: 2022-12-31T17:50:10.734
2022-12-31T17:50:10.737 - Exception:
2022-12-31T17:50:10.742 - Chrome closed gracefully

I'm forced to believe that Chrome does not exit over here, because getting the Chrome process count after re-initializing it shows that two Chrome processes are active, which might be the cause of all crashes & also has me use logic to custom close the Chrome processes (which it doesn't achieve properly either)

These are the logs involving the re-initialization afterwards, along with running a conversion, but the Exception closes the program at the last line probably.

2022-12-31T17:50:10.742 - Adding Chrome argument '--headless'
2022-12-31T17:50:10.742 - Adding Chrome argument '--disable-gpu'
2022-12-31T17:50:10.742 - Adding Chrome argument '--hide-scrollbars'
2022-12-31T17:50:10.742 - Adding Chrome argument '--mute-audio'
2022-12-31T17:50:10.742 - Adding Chrome argument '--disable-background-networking'
2022-12-31T17:50:10.742 - Adding Chrome argument '--disable-background-timer-throttling'
2022-12-31T17:50:10.742 - Adding Chrome argument '--disable-default-apps'
2022-12-31T17:50:10.742 - Adding Chrome argument '--disable-extensions'
2022-12-31T17:50:10.742 - Adding Chrome argument '--disable-hang-monitor'
2022-12-31T17:50:10.742 - Adding Chrome argument '--disable-prompt-on-repost'
2022-12-31T17:50:10.742 - Adding Chrome argument '--disable-sync'
2022-12-31T17:50:10.742 - Adding Chrome argument '--disable-translate'
2022-12-31T17:50:10.742 - Adding Chrome argument '--metrics-recording-only'
2022-12-31T17:50:10.742 - Adding Chrome argument '--no-first-run'
2022-12-31T17:50:10.742 - Adding Chrome argument '--disable-crash-reporter'
2022-12-31T17:50:10.742 - Adding Chrome argument '--remote-debugging-port="0"'
2022-12-31T17:50:10.742 - Detected Linux operating system, adding the parameter '--no-sandbox'
2022-12-31T17:50:10.742 - Adding Chrome argument '--no-sandbox'
2022-12-31T17:50:10.742 - Adding Chrome argument '--window-size="1366,768"'
2022-12-31T17:50:10.771 - Updating Chrome argument '--window-size="1366,768"' with value '1280,800'
2022-12-31T17:50:10.771 - Starting Chrome from location '/usr/bin/google-chrome' with working directory '/usr/bin'
2022-12-31T17:50:10.771 - "/usr/bin/google-chrome" --headless --disable-gpu --hide-scrollbars --mute-audio --disable-2022-2022-12-31T17:50:10.771 - "/usr/bin/google-chrome" --headless --disable-gpu --hide-scrollbars --mute-audio --disable-background-networking --disable-background-timer-throttling --disable-default-apps --disabl$background-networking --disable-background-timer-throttling --disable-default-apps --disabl$
2022-12-31T17:50:10.775 - Chrome process started
commented

Check this issue to see if you can do something with it, he had something simular --> #72

The fixes stated are for Docker/Kubernetes, however I'm using CentOS.

Well I just went with a janky solution that doesn't close the entire program, that instead of using Dispose I just set the instance to null, send a killall chrome command on Linux which closes all of the Chrome processes and reinitialize a new instance.

That said, it seriously needs to be worked upon.