private void AssociateWithJob(WindowsApi.PROCESS_INFORMATION processInformation, bool throwOnError)
        {
            if (_jobHandle == IntPtr.Zero)
            {
                lock (_jobHandleLock)
                {
                    if (_jobHandle == IntPtr.Zero)
                    {
                        _jobHandle = WindowsApi.CreateJobObject(IntPtr.Zero, null);

                        var basic = new WindowsApi.JOBOBJECT_BASIC_LIMIT_INFORMATION {
                            LimitFlags = WindowsApi.JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE
                        };
                        var extended = new WindowsApi.JOBOBJECT_EXTENDED_LIMIT_INFORMATION {
                            BasicLimitInformation = basic
                        };

                        int    length          = Marshal.SizeOf(typeof(WindowsApi.JOBOBJECT_EXTENDED_LIMIT_INFORMATION));
                        IntPtr extendedPointer = Marshal.AllocHGlobal(length);
                        Marshal.StructureToPtr(extended, extendedPointer, false);

                        if (!WindowsApi.SetInformationJobObject(_jobHandle, WindowsApi.JOBOBJECTINFOCLASS.ExtendedLimitInformation, extendedPointer, (uint)length))
                        {
                            throw new TorSharpException($"Unable to set information on the job object. Error: {WindowsUtility.GetLastErrorMessage()}.");
                        }
                    }
                }
            }

            lock (_processIdsLock)
            {
                _processIds.Add(processInformation.dwProcessId);
            }

            if (!WindowsApi.AssignProcessToJobObject(_jobHandle, processInformation.hProcess) && throwOnError)
            {
                throw new TorSharpException($"Unable to assign the process to the job object. Error: {WindowsUtility.GetLastErrorMessage()}.");
            }
        }
        public void Stop()
        {
            if (_jobHandle != IntPtr.Zero)
            {
                lock (_jobHandleLock)
                {
                    if (_jobHandle != IntPtr.Zero)
                    {
                        // terminate the job
                        if (!WindowsApi.TerminateJobObject(_jobHandle, 0))
                        {
                            throw new TorSharpException($"Unable to terminate the job object. Error: {WindowsUtility.GetLastErrorMessage()}.");
                        }

                        // wait for all jobs to complete
                        lock (_processIdsLock)
                        {
                            while (_processIds.Any())
                            {
                                if (Process.GetProcesses().All(p => p.Id != _processIds[0]))
                                {
                                    _processIds.RemoveAt(0);
                                }
                            }
                        }

                        _jobHandle = IntPtr.Zero;
                    }
                }
            }
        }
        public void Stop()
        {
            if (_jobHandle != IntPtr.Zero)
            {
                lock (_jobHandleLock)
                {
                    if (_jobHandle != IntPtr.Zero)
                    {
                        // terminate the job
                        if (!WindowsApi.TerminateJobObject(_jobHandle, 0))
                        {
                            throw new TorSharpException($"Unable to terminate the job object. Error: {WindowsUtility.GetLastErrorMessage()}.");
                        }

                        // wait for all jobs to complete
                        lock (_processIdsLock)
                        {
                            var firstAttempt = true;
                            while (_processIds.Any())
                            {
                                if (!firstAttempt)
                                {
                                    Thread.Sleep(100);
                                }

                                firstAttempt = false;

                                var allProcessIds = Process
                                                    .GetProcesses()
                                                    .Select(x => x.Id)
                                                    .ToList();

                                _processIds.IntersectWith(allProcessIds);
                            }
                        }

                        _jobHandle = IntPtr.Zero;
                    }
                }
            }
        }