< Summary

Information
Class: Ice.Internal.ThreadPool.WorkerThread
Assembly: Ice
File(s): /home/runner/work/ice/ice/csharp/src/Ice/Internal/ThreadPool.cs
Tag: 71_18251537082
Line coverage
60%
Covered lines: 30
Uncovered lines: 20
Coverable lines: 50
Total lines: 775
Line coverage: 60%
Branch coverage
59%
Covered branches: 13
Total branches: 22
Branch coverage: 59%
Method coverage
85%
Covered methods: 6
Total methods: 7
Method coverage: 85.7%

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
updateObserver()100%44100%
setState(...)100%44100%
getThread()100%210%
join()100%11100%
start(...)50%2.01287.5%
Run()33.33%72.751225%

File(s)

/home/runner/work/ice/ice/csharp/src/Ice/Internal/ThreadPool.cs

#LineLine coverage
 1// Copyright (c) ZeroC, Inc.
 2
 3using System.Diagnostics;
 4
 5namespace Ice.Internal;
 6
 7public delegate void ThreadPoolWorkItem(ThreadPoolCurrent current);
 8
 9public delegate void AsyncCallback(object state);
 10
 11internal class ThreadPoolMessage : IDisposable
 12{
 13    public ThreadPoolMessage(ThreadPoolCurrent current, object mutex)
 14    {
 15        _current = current;
 16        _mutex = mutex;
 17        _finish = false;
 18        _finishWithIO = false;
 19    }
 20
 21    public bool startIOScope()
 22    {
 23        // This must be called with the handler locked.
 24        _finishWithIO = _current.startMessage();
 25        return _finishWithIO;
 26    }
 27
 28    public void finishIOScope()
 29    {
 30        if (_finishWithIO)
 31        {
 32            // This must be called with the handler locked.
 33            _current.finishMessage();
 34        }
 35    }
 36
 37    public void ioCompleted()
 38    {
 39        //
 40        // Call finishMessage once IO is completed only if serialization is not enabled.
 41        // Otherwise, finishMessage will be called when the event handler is done with
 42        // the message (it will be called from Dispose below).
 43        //
 44        Debug.Assert(_finishWithIO);
 45        if (_current.ioCompleted())
 46        {
 47            _finishWithIO = false;
 48            _finish = true;
 49        }
 50    }
 51
 52    public void Dispose()
 53    {
 54        if (_finish)
 55        {
 56            //
 57            // A ThreadPoolMessage instance must be created outside the synchronization of the event handler. We
 58            // need to lock the event handler here to call finishMessage.
 59            //
 60            lock (_mutex)
 61            {
 62                _current.finishMessage();
 63            }
 64        }
 65    }
 66
 67    private readonly ThreadPoolCurrent _current;
 68    private readonly object _mutex;
 69    private bool _finish;
 70    private bool _finishWithIO;
 71}
 72
 73public class ThreadPoolCurrent
 74{
 75    internal ThreadPoolCurrent(ThreadPool threadPool, ThreadPool.WorkerThread thread)
 76    {
 77        _threadPool = threadPool;
 78        _thread = thread;
 79    }
 80
 81    public int operation;
 82
 83    public bool ioCompleted() => _threadPool.ioCompleted(this);
 84
 85    public bool startMessage() => _threadPool.startMessage(this);
 86
 87    public void finishMessage() => _threadPool.finishMessage(this);
 88
 89    internal readonly ThreadPool _threadPool;
 90    internal readonly ThreadPool.WorkerThread _thread;
 91    internal bool _ioCompleted;
 92    internal EventHandler _handler;
 93}
 94
 95public sealed class ThreadPool : System.Threading.Tasks.TaskScheduler
 96{
 97    public ThreadPool(Instance instance, string prefix, int timeout)
 98    {
 99        Ice.Properties properties = instance.initializationData().properties;
 100
 101        _instance = instance;
 102        _executor = instance.initializationData().executor;
 103        _destroyed = false;
 104        _prefix = prefix;
 105        _threadIndex = 0;
 106        _inUse = 0;
 107        _serialize = properties.getPropertyAsInt(_prefix + ".Serialize") > 0;
 108        _serverIdleTime = timeout <= 0 ? Timeout.InfiniteTimeSpan : TimeSpan.FromSeconds(timeout);
 109
 110        string programName = properties.getIceProperty("Ice.ProgramName");
 111        if (programName.Length > 0)
 112        {
 113            _threadPrefix = programName + "-" + _prefix;
 114        }
 115        else
 116        {
 117            _threadPrefix = _prefix;
 118        }
 119
 120        //
 121        // We use just one thread as the default. This is the fastest
 122        // possible setting, still allows one level of nesting, and
 123        // doesn't require to make the servants thread safe.
 124        //
 125        int size = properties.getPropertyAsIntWithDefault(_prefix + ".Size", 1);
 126        if (size < 1)
 127        {
 128            string s = _prefix + ".Size < 1; Size adjusted to 1";
 129            _instance.initializationData().logger.warning(s);
 130            size = 1;
 131        }
 132
 133        int sizeMax = properties.getPropertyAsIntWithDefault(_prefix + ".SizeMax", size);
 134        if (sizeMax < size)
 135        {
 136            string s = _prefix + ".SizeMax < " + _prefix + ".Size; SizeMax adjusted to Size (" + size + ")";
 137            _instance.initializationData().logger.warning(s);
 138            sizeMax = size;
 139        }
 140
 141        int sizeWarn = properties.getPropertyAsInt(_prefix + ".SizeWarn");
 142        if (sizeWarn != 0 && sizeWarn < size)
 143        {
 144            string s = _prefix + ".SizeWarn < " + _prefix + ".Size; adjusted SizeWarn to Size (" + size + ")";
 145            _instance.initializationData().logger.warning(s);
 146            sizeWarn = size;
 147        }
 148        else if (sizeWarn > sizeMax)
 149        {
 150            string s = _prefix + ".SizeWarn > " + _prefix + ".SizeMax; adjusted SizeWarn to SizeMax ("
 151                + sizeMax + ")";
 152            _instance.initializationData().logger.warning(s);
 153            sizeWarn = sizeMax;
 154        }
 155
 156        int threadIdleTime = properties.getPropertyAsIntWithDefault(_prefix + ".ThreadIdleTime", 60);
 157        if (threadIdleTime < 0)
 158        {
 159            string s = _prefix + ".ThreadIdleTime < 0; ThreadIdleTime adjusted to 0";
 160            _instance.initializationData().logger.warning(s);
 161            threadIdleTime = 0;
 162        }
 163
 164        _size = size;
 165        _sizeMax = sizeMax;
 166        _sizeWarn = sizeWarn;
 167        _threadIdleTime = threadIdleTime <= 0 ? Timeout.InfiniteTimeSpan : TimeSpan.FromSeconds(threadIdleTime);
 168
 169        int stackSize = properties.getPropertyAsInt(_prefix + ".StackSize");
 170        if (stackSize < 0)
 171        {
 172            string s = _prefix + ".StackSize < 0; Size adjusted to OS default";
 173            _instance.initializationData().logger.warning(s);
 174            stackSize = 0;
 175        }
 176        _stackSize = stackSize;
 177
 178        _priority = properties.getProperty(_prefix + ".ThreadPriority").Length > 0 ?
 179            Util.stringToThreadPriority(properties.getProperty(_prefix + ".ThreadPriority")) :
 180            Util.stringToThreadPriority(properties.getIceProperty("Ice.ThreadPriority"));
 181
 182        if (_instance.traceLevels().threadPool >= 1)
 183        {
 184            string s = "creating " + _prefix + ": Size = " + _size + ", SizeMax = " + _sizeMax + ", SizeWarn = " +
 185                       _sizeWarn;
 186            _instance.initializationData().logger.trace(_instance.traceLevels().threadPoolCat, s);
 187        }
 188
 189        _workItems = new Queue<ThreadPoolWorkItem>();
 190
 191        try
 192        {
 193            _threads = new List<WorkerThread>();
 194            for (int i = 0; i < _size; ++i)
 195            {
 196                var thread = new WorkerThread(this, _threadPrefix + "-" + _threadIndex++);
 197                thread.start(_priority);
 198                _threads.Add(thread);
 199            }
 200        }
 201        catch (System.Exception ex)
 202        {
 203            string s = "cannot create thread for `" + _prefix + "':\n" + ex;
 204            _instance.initializationData().logger.error(s);
 205
 206            destroy();
 207            joinWithAllThreads();
 208            throw;
 209        }
 210    }
 211
 212    public void destroy()
 213    {
 214        lock (_mutex)
 215        {
 216            if (_destroyed)
 217            {
 218                return;
 219            }
 220            _destroyed = true;
 221            Monitor.PulseAll(_mutex);
 222        }
 223    }
 224
 225    public void updateObservers()
 226    {
 227        lock (_mutex)
 228        {
 229            foreach (WorkerThread t in _threads)
 230            {
 231                t.updateObserver();
 232            }
 233        }
 234    }
 235
 236    public void initialize(EventHandler handler)
 237    {
 238        handler._ready = 0;
 239        handler._pending = 0;
 240        handler._started = 0;
 241        handler._finish = false;
 242        handler._hasMoreData = false;
 243        handler._registered = 0;
 244    }
 245
 246    public void register(EventHandler handler, int op) => update(handler, SocketOperation.None, op);
 247
 248    public void update(EventHandler handler, int remove, int add)
 249    {
 250        lock (_mutex)
 251        {
 252            Debug.Assert(!_destroyed);
 253
 254            // Don't remove what needs to be added
 255            remove &= ~add;
 256
 257            // Don't remove/add if already un-registered or registered
 258            remove &= handler._registered;
 259            add &= ~handler._registered;
 260            if (remove == add)
 261            {
 262                return;
 263            }
 264
 265            handler._registered &= ~remove;
 266            handler._registered |= add;
 267
 268            if ((add & SocketOperation.Read) != 0 && (handler._pending & SocketOperation.Read) == 0)
 269            {
 270                handler._pending |= SocketOperation.Read;
 271                queueReadyForIOHandler(handler, SocketOperation.Read);
 272            }
 273            else if ((add & SocketOperation.Write) != 0 && (handler._pending & SocketOperation.Write) == 0)
 274            {
 275                handler._pending |= SocketOperation.Write;
 276                queueReadyForIOHandler(handler, SocketOperation.Write);
 277            }
 278        }
 279    }
 280
 281    public void unregister(EventHandler handler, int op) => update(handler, op, SocketOperation.None);
 282
 283    public void finish(EventHandler handler)
 284    {
 285        lock (_mutex)
 286        {
 287            Debug.Assert(!_destroyed);
 288
 289            handler._registered = SocketOperation.None;
 290
 291            //
 292            // If there are no pending asynchronous operations, we can call finish on the handler now.
 293            //
 294            if (handler._pending == 0)
 295            {
 296                _workItems.Enqueue(current =>
 297                    {
 298                        current.operation = SocketOperation.None;
 299                        current._handler = handler;
 300                        handler.finished(current);
 301                    });
 302                Monitor.Pulse(_mutex);
 303            }
 304            else
 305            {
 306                handler._finish = true;
 307            }
 308        }
 309    }
 310
 311    public void executeFromThisThread(System.Action call, Ice.Connection connection)
 312    {
 313        if (_executor is not null)
 314        {
 315            try
 316            {
 317                _executor(call, connection);
 318            }
 319            catch (System.Exception ex)
 320            {
 321                if (_instance.initializationData().properties.getIcePropertyAsInt("Ice.Warn.Executor") > 0)
 322                {
 323                    _instance.initializationData().logger.warning($"executor exception:\n{ex}");
 324                }
 325            }
 326        }
 327        else
 328        {
 329            call();
 330        }
 331    }
 332
 333    public void execute(Action workItem, Ice.Connection connection)
 334    {
 335        lock (_mutex)
 336        {
 337            if (_destroyed)
 338            {
 339                throw new Ice.CommunicatorDestroyedException();
 340            }
 341            _workItems.Enqueue(current =>
 342                {
 343                    current.ioCompleted();
 344                    executeFromThisThread(workItem, connection);
 345                });
 346            Monitor.Pulse(_mutex);
 347        }
 348    }
 349
 350    public void joinWithAllThreads()
 351    {
 352        //
 353        // _threads is immutable after destroy() has been called, therefore no synchronization is needed.
 354        // (Synchronization wouldn't be possible here anyway, because otherwise the other threads would never
 355        // terminate.)
 356        //
 357        Debug.Assert(_destroyed);
 358        foreach (WorkerThread thread in _threads)
 359        {
 360            thread.join();
 361        }
 362    }
 363
 364    public string prefix() => _prefix;
 365
 366    public bool serialize() => _serialize;
 367
 368    protected sealed override void QueueTask(
 369        System.Threading.Tasks.Task task) => execute(
 370            () => TryExecuteTask(task),
 371            null);
 372
 373    protected sealed override bool TryExecuteTaskInline(System.Threading.Tasks.Task task, bool taskWasPreviouslyQueued)
 374    {
 375        if (!taskWasPreviouslyQueued)
 376        {
 377            executeFromThisThread(() => TryExecuteTask(task), null);
 378            return true;
 379        }
 380        return false;
 381    }
 382
 383    protected sealed override bool TryDequeue(System.Threading.Tasks.Task task) => false;
 384
 385    protected sealed override IEnumerable<System.Threading.Tasks.Task> GetScheduledTasks() =>
 386        Array.Empty<System.Threading.Tasks.Task>();
 387
 388    private void queueReadyForIOHandler(EventHandler handler, int operation)
 389    {
 390        lock (_mutex)
 391        {
 392            Debug.Assert(!_destroyed);
 393            _workItems.Enqueue(current =>
 394                {
 395                    current._handler = handler;
 396                    current.operation = operation;
 397                    try
 398                    {
 399                        current._handler.message(current);
 400                    }
 401                    catch (System.Exception ex)
 402                    {
 403                        string s = "exception in `" + _prefix + "':\n" + ex + "\nevent handler: " +
 404                            current._handler.ToString();
 405                        _instance.initializationData().logger.error(s);
 406                    }
 407                });
 408            Monitor.Pulse(_mutex);
 409        }
 410    }
 411
 412    private void run(WorkerThread thread)
 413    {
 414        var current = new ThreadPoolCurrent(this, thread);
 415        while (true)
 416        {
 417            ThreadPoolWorkItem workItem = null;
 418            lock (_mutex)
 419            {
 420                while (_workItems.Count == 0)
 421                {
 422                    if (_destroyed)
 423                    {
 424                        return;
 425                    }
 426
 427                    if (_threadIdleTime != Timeout.InfiniteTimeSpan)
 428                    {
 429                        if (!Monitor.Wait(_mutex, _threadIdleTime) && _workItems.Count == 0) // If timeout
 430                        {
 431                            if (_destroyed)
 432                            {
 433                                return;
 434                            }
 435                            else if (_inUse < _threads.Count - 1)
 436                            {
 437                                if (_instance.traceLevels().threadPool >= 1)
 438                                {
 439                                    string s = "shrinking " + _prefix + ": Size=" + (_threads.Count - 1);
 440                                    _instance.initializationData().logger.trace(
 441                                        _instance.traceLevels().threadPoolCat, s);
 442                                }
 443
 444                                _threads.Remove(thread);
 445                                _workItems.Enqueue(c =>
 446                                        // No call to ioCompleted, this shouldn't block (and we don't want to cause
 447                                        // a new thread to be started).
 448                                        thread.join());
 449                                Monitor.Pulse(_mutex);
 450                                return;
 451                            }
 452                            else if (_inUse > 0)
 453                            {
 454                                //
 455                                // If this is the last idle thread but there are still other threads
 456                                // busy dispatching, we go back waiting with _threadIdleTime. We only
 457                                // wait with _serverIdleTime when there's only one thread left.
 458                                //
 459                                continue;
 460                            }
 461
 462                            Debug.Assert(_threads.Count == 1);
 463                            if (!Monitor.Wait(_mutex, _serverIdleTime) && !_destroyed)
 464                            {
 465                                _workItems.Enqueue(c =>
 466                                    {
 467                                        c.ioCompleted();
 468                                        try
 469                                        {
 470                                            _instance.objectAdapterFactory().shutdown();
 471                                        }
 472                                        catch (Ice.CommunicatorDestroyedException)
 473                                        {
 474                                        }
 475                                    });
 476                            }
 477                        }
 478                    }
 479                    else
 480                    {
 481                        Monitor.Wait(_mutex);
 482                    }
 483                }
 484
 485                Debug.Assert(_workItems.Count > 0);
 486                workItem = _workItems.Dequeue();
 487
 488                current._thread.setState(Ice.Instrumentation.ThreadState.ThreadStateInUseForIO);
 489                current._ioCompleted = false;
 490            }
 491
 492            try
 493            {
 494                workItem(current);
 495            }
 496            catch (System.Exception ex)
 497            {
 498                string s = "exception in `" + _prefix + "' while calling on work item:\n" + ex + '\n';
 499                _instance.initializationData().logger.error(s);
 500            }
 501
 502            lock (_mutex)
 503            {
 504                if (_sizeMax > 1 && current._ioCompleted)
 505                {
 506                    Debug.Assert(_inUse > 0);
 507                    --_inUse;
 508                }
 509                thread.setState(Ice.Instrumentation.ThreadState.ThreadStateIdle);
 510            }
 511        }
 512    }
 513
 514    public bool ioCompleted(ThreadPoolCurrent current)
 515    {
 516        lock (_mutex)
 517        {
 518            current._ioCompleted = true; // Set the IO completed flag to specify that ioCompleted() has been called.
 519
 520            current._thread.setState(Ice.Instrumentation.ThreadState.ThreadStateInUseForUser);
 521
 522            if (_sizeMax > 1)
 523            {
 524                Debug.Assert(_inUse >= 0);
 525                ++_inUse;
 526
 527                if (_sizeMax > 1 && _inUse == _sizeWarn)
 528                {
 529                    string s = "thread pool `" + _prefix + "' is running low on threads\n"
 530                        + "Size=" + _size + ", " + "SizeMax=" + _sizeMax + ", " + "SizeWarn=" + _sizeWarn;
 531                    _instance.initializationData().logger.warning(s);
 532                }
 533
 534                if (!_destroyed && _inUse < _sizeMax && _inUse == _threads.Count)
 535                {
 536                    if (_instance.traceLevels().threadPool >= 1)
 537                    {
 538                        string s = "growing " + _prefix + ": Size = " + (_threads.Count + 1);
 539                        _instance.initializationData().logger.trace(_instance.traceLevels().threadPoolCat, s);
 540                    }
 541
 542                    try
 543                    {
 544                        var t = new WorkerThread(this, _threadPrefix + "-" + _threadIndex++);
 545                        t.start(_priority);
 546                        _threads.Add(t);
 547                    }
 548                    catch (System.Exception ex)
 549                    {
 550                        string s = "cannot create thread for `" + _prefix + "':\n" + ex;
 551                        _instance.initializationData().logger.error(s);
 552                    }
 553                }
 554            }
 555        }
 556        return _serialize;
 557    }
 558
 559    public bool startMessage(ThreadPoolCurrent current)
 560    {
 561        Debug.Assert((current._handler._pending & current.operation) != 0);
 562
 563        if ((current._handler._started & current.operation) != 0)
 564        {
 565            Debug.Assert((current._handler._ready & current.operation) == 0);
 566            current._handler._ready |= current.operation;
 567            current._handler._started &= ~current.operation;
 568            if (!current._handler.finishAsync(current.operation)) // Returns false if the handler is finished.
 569            {
 570                current._handler._pending &= ~current.operation;
 571                if (current._handler._pending == 0 && current._handler._finish)
 572                {
 573                    finish(current._handler);
 574                }
 575                return false;
 576            }
 577        }
 578        else if ((current._handler._ready & current.operation) == 0 &&
 579                (current._handler._registered & current.operation) != 0)
 580        {
 581            Debug.Assert((current._handler._started & current.operation) == 0);
 582            if (!current._handler.startAsync(current.operation, getCallback(current.operation)))
 583            {
 584                current._handler._pending &= ~current.operation;
 585                if (current._handler._pending == 0 && current._handler._finish)
 586                {
 587                    finish(current._handler);
 588                }
 589                return false;
 590            }
 591            else
 592            {
 593                current._handler._started |= current.operation;
 594                return false;
 595            }
 596        }
 597
 598        if ((current._handler._registered & current.operation) != 0)
 599        {
 600            Debug.Assert((current._handler._ready & current.operation) != 0);
 601            current._handler._ready &= ~current.operation;
 602            return true;
 603        }
 604        else
 605        {
 606            current._handler._pending &= ~current.operation;
 607            if (current._handler._pending == 0 && current._handler._finish)
 608            {
 609                finish(current._handler);
 610            }
 611            return false;
 612        }
 613    }
 614
 615    public void finishMessage(ThreadPoolCurrent current)
 616    {
 617        if ((current._handler._registered & current.operation) != 0)
 618        {
 619            Debug.Assert((current._handler._ready & current.operation) == 0);
 620            if (!current._handler.startAsync(current.operation, getCallback(current.operation)))
 621            {
 622                current._handler._pending &= ~current.operation;
 623            }
 624            else
 625            {
 626                Debug.Assert((current._handler._pending & current.operation) != 0);
 627                current._handler._started |= current.operation;
 628            }
 629        }
 630        else
 631        {
 632            current._handler._pending &= ~current.operation;
 633        }
 634
 635        if (current._handler._pending == 0 && current._handler._finish)
 636        {
 637            // There are no more pending async operations, it's time to call finish.
 638            finish(current._handler);
 639        }
 640    }
 641
 642    private AsyncCallback getCallback(int operation)
 643    {
 644        Debug.Assert(operation == SocketOperation.Read || operation == SocketOperation.Write);
 645        return state => queueReadyForIOHandler((EventHandler)state, operation);
 646    }
 647
 648    private readonly Instance _instance;
 649    private readonly System.Action<System.Action, Ice.Connection> _executor;
 650    private bool _destroyed;
 651    private readonly string _prefix;
 652    private readonly string _threadPrefix;
 653
 654    private readonly object _mutex = new object();
 655
 656    internal sealed class WorkerThread
 657    {
 658        private readonly ThreadPool _threadPool;
 659        private Ice.Instrumentation.ThreadObserver _observer;
 660        private Ice.Instrumentation.ThreadState _state;
 661
 1662        internal WorkerThread(ThreadPool threadPool, string name)
 663        {
 1664            _threadPool = threadPool;
 1665            _name = name;
 1666            _state = Ice.Instrumentation.ThreadState.ThreadStateIdle;
 1667            updateObserver();
 1668        }
 669
 670        public void updateObserver()
 671        {
 672            // Must be called with the thread pool mutex locked
 1673            Ice.Instrumentation.CommunicatorObserver obsv = _threadPool._instance.initializationData().observer;
 1674            if (obsv is not null)
 675            {
 1676                _observer = obsv.getThreadObserver(_threadPool._prefix, _name, _state, _observer);
 1677                _observer?.attach();
 678            }
 1679        }
 680
 681        public void setState(Ice.Instrumentation.ThreadState s)
 682        {
 683            // Must be called with the thread pool mutex locked
 1684            if (_observer is not null)
 685            {
 1686                if (_state != s)
 687                {
 1688                    _observer.stateChanged(_state, s);
 689                }
 690            }
 1691            _state = s;
 1692        }
 693
 0694        public Thread getThread() => _thread;
 695
 1696        public void join() => _thread.Join();
 697
 698        public void start(ThreadPriority priority)
 699        {
 1700            if (_threadPool._stackSize == 0)
 701            {
 1702                _thread = new Thread(new ThreadStart(Run));
 703            }
 704            else
 705            {
 0706                _thread = new Thread(new ThreadStart(Run), _threadPool._stackSize);
 707            }
 1708            _thread.IsBackground = true;
 1709            _thread.Name = _name;
 1710            _thread.Priority = priority;
 1711            _thread.Start();
 1712        }
 713
 714        public void Run()
 715        {
 1716            if (_threadPool._instance.initializationData().threadStart is not null)
 717            {
 718                try
 719                {
 0720                    _threadPool._instance.initializationData().threadStart();
 0721                }
 0722                catch (System.Exception ex)
 723                {
 0724                    string s = "thread hook start() method raised an unexpected exception in `";
 0725                    s += _threadPool._prefix + "' thread " + _thread.Name + ":\n" + ex;
 0726                    _threadPool._instance.initializationData().logger.error(s);
 0727                }
 728            }
 729
 730            try
 731            {
 1732                _threadPool.run(this);
 1733            }
 0734            catch (System.Exception ex)
 735            {
 0736                string s = "exception in `" + _threadPool._prefix + "' thread " + _thread.Name + ":\n" + ex;
 0737                _threadPool._instance.initializationData().logger.error(s);
 0738            }
 739
 1740            _observer?.detach();
 741
 1742            if (_threadPool._instance.initializationData().threadStop is not null)
 743            {
 744                try
 745                {
 0746                    _threadPool._instance.initializationData().threadStop();
 0747                }
 0748                catch (System.Exception ex)
 749                {
 0750                    string s = "thread hook stop() method raised an unexpected exception in `";
 0751                    s += _threadPool._prefix + "' thread " + _thread.Name + ":\n" + ex;
 0752                    _threadPool._instance.initializationData().logger.error(s);
 0753                }
 754            }
 1755        }
 756
 757        private readonly string _name;
 758        private Thread _thread;
 759    }
 760
 761    private readonly int _size; // Number of threads that are pre-created.
 762    private readonly int _sizeMax; // Maximum number of threads.
 763    private readonly int _sizeWarn; // If _inUse reaches _sizeWarn, a "low on threads" warning will be printed.
 764    private readonly bool _serialize; // True if requests need to be serialized over the connection.
 765    private readonly ThreadPriority _priority;
 766    private readonly TimeSpan _serverIdleTime;
 767    private readonly TimeSpan _threadIdleTime;
 768    private readonly int _stackSize;
 769
 770    private readonly List<WorkerThread> _threads; // All threads, running or not.
 771    private int _threadIndex; // For assigning thread names.
 772    private int _inUse; // Number of threads that are currently in use.
 773
 774    private readonly Queue<ThreadPoolWorkItem> _workItems;
 775}