< Summary

Information
Class: Ice.Internal.ThreadPool
Assembly: Ice
File(s): /home/runner/work/ice/ice/csharp/src/Ice/Internal/ThreadPool.cs
Tag: 71_18251537082
Line coverage
71%
Covered lines: 238
Uncovered lines: 93
Coverable lines: 331
Total lines: 775
Line coverage: 71.9%
Branch coverage
71%
Covered branches: 113
Total branches: 158
Branch coverage: 71.5%
Method coverage
83%
Covered methods: 25
Total methods: 30
Method coverage: 83.3%

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)60.71%62.222864.79%
destroy()50%2.01285.71%
updateObservers()100%22100%
initialize(...)100%11100%
register(...)100%11100%
update(...)100%1010100%
unregister(...)100%11100%
finish(...)100%22100%
executeFromThisThread(...)50%5.4455.56%
execute(...)50%2.01287.5%
joinWithAllThreads()100%22100%
prefix()100%210%
serialize()100%210%
QueueTask(...)100%11100%
TryExecuteTaskInline(...)100%22100%
TryDequeue(...)100%210%
GetScheduledTasks()100%210%
queueReadyForIOHandler(...)100%11100%
run(...)71.88%78.053264.44%
ioCompleted(...)75%31.341660.87%
startMessage(...)79.17%26.672483.33%
finishMessage(...)87.5%8.13887.5%
getCallback(...)100%11100%
.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{
 197    public ThreadPool(Instance instance, string prefix, int timeout)
 98    {
 199        Ice.Properties properties = instance.initializationData().properties;
 100
 1101        _instance = instance;
 1102        _executor = instance.initializationData().executor;
 1103        _destroyed = false;
 1104        _prefix = prefix;
 1105        _threadIndex = 0;
 1106        _inUse = 0;
 1107        _serialize = properties.getPropertyAsInt(_prefix + ".Serialize") > 0;
 1108        _serverIdleTime = timeout <= 0 ? Timeout.InfiniteTimeSpan : TimeSpan.FromSeconds(timeout);
 109
 1110        string programName = properties.getIceProperty("Ice.ProgramName");
 1111        if (programName.Length > 0)
 112        {
 1113            _threadPrefix = programName + "-" + _prefix;
 114        }
 115        else
 116        {
 1117            _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        //
 1125        int size = properties.getPropertyAsIntWithDefault(_prefix + ".Size", 1);
 1126        if (size < 1)
 127        {
 0128            string s = _prefix + ".Size < 1; Size adjusted to 1";
 0129            _instance.initializationData().logger.warning(s);
 0130            size = 1;
 131        }
 132
 1133        int sizeMax = properties.getPropertyAsIntWithDefault(_prefix + ".SizeMax", size);
 1134        if (sizeMax < size)
 135        {
 1136            string s = _prefix + ".SizeMax < " + _prefix + ".Size; SizeMax adjusted to Size (" + size + ")";
 1137            _instance.initializationData().logger.warning(s);
 1138            sizeMax = size;
 139        }
 140
 1141        int sizeWarn = properties.getPropertyAsInt(_prefix + ".SizeWarn");
 1142        if (sizeWarn != 0 && sizeWarn < size)
 143        {
 0144            string s = _prefix + ".SizeWarn < " + _prefix + ".Size; adjusted SizeWarn to Size (" + size + ")";
 0145            _instance.initializationData().logger.warning(s);
 0146            sizeWarn = size;
 147        }
 1148        else if (sizeWarn > sizeMax)
 149        {
 0150            string s = _prefix + ".SizeWarn > " + _prefix + ".SizeMax; adjusted SizeWarn to SizeMax ("
 0151                + sizeMax + ")";
 0152            _instance.initializationData().logger.warning(s);
 0153            sizeWarn = sizeMax;
 154        }
 155
 1156        int threadIdleTime = properties.getPropertyAsIntWithDefault(_prefix + ".ThreadIdleTime", 60);
 1157        if (threadIdleTime < 0)
 158        {
 0159            string s = _prefix + ".ThreadIdleTime < 0; ThreadIdleTime adjusted to 0";
 0160            _instance.initializationData().logger.warning(s);
 0161            threadIdleTime = 0;
 162        }
 163
 1164        _size = size;
 1165        _sizeMax = sizeMax;
 1166        _sizeWarn = sizeWarn;
 1167        _threadIdleTime = threadIdleTime <= 0 ? Timeout.InfiniteTimeSpan : TimeSpan.FromSeconds(threadIdleTime);
 168
 1169        int stackSize = properties.getPropertyAsInt(_prefix + ".StackSize");
 1170        if (stackSize < 0)
 171        {
 0172            string s = _prefix + ".StackSize < 0; Size adjusted to OS default";
 0173            _instance.initializationData().logger.warning(s);
 0174            stackSize = 0;
 175        }
 1176        _stackSize = stackSize;
 177
 1178        _priority = properties.getProperty(_prefix + ".ThreadPriority").Length > 0 ?
 1179            Util.stringToThreadPriority(properties.getProperty(_prefix + ".ThreadPriority")) :
 1180            Util.stringToThreadPriority(properties.getIceProperty("Ice.ThreadPriority"));
 181
 1182        if (_instance.traceLevels().threadPool >= 1)
 183        {
 0184            string s = "creating " + _prefix + ": Size = " + _size + ", SizeMax = " + _sizeMax + ", SizeWarn = " +
 0185                       _sizeWarn;
 0186            _instance.initializationData().logger.trace(_instance.traceLevels().threadPoolCat, s);
 187        }
 188
 1189        _workItems = new Queue<ThreadPoolWorkItem>();
 190
 191        try
 192        {
 1193            _threads = new List<WorkerThread>();
 1194            for (int i = 0; i < _size; ++i)
 195            {
 1196                var thread = new WorkerThread(this, _threadPrefix + "-" + _threadIndex++);
 1197                thread.start(_priority);
 1198                _threads.Add(thread);
 199            }
 1200        }
 0201        catch (System.Exception ex)
 202        {
 0203            string s = "cannot create thread for `" + _prefix + "':\n" + ex;
 0204            _instance.initializationData().logger.error(s);
 205
 0206            destroy();
 0207            joinWithAllThreads();
 0208            throw;
 209        }
 1210    }
 211
 212    public void destroy()
 213    {
 1214        lock (_mutex)
 215        {
 1216            if (_destroyed)
 217            {
 0218                return;
 219            }
 1220            _destroyed = true;
 1221            Monitor.PulseAll(_mutex);
 1222        }
 1223    }
 224
 225    public void updateObservers()
 226    {
 1227        lock (_mutex)
 228        {
 1229            foreach (WorkerThread t in _threads)
 230            {
 1231                t.updateObserver();
 232            }
 233        }
 1234    }
 235
 236    public void initialize(EventHandler handler)
 237    {
 1238        handler._ready = 0;
 1239        handler._pending = 0;
 1240        handler._started = 0;
 1241        handler._finish = false;
 1242        handler._hasMoreData = false;
 1243        handler._registered = 0;
 1244    }
 245
 1246    public void register(EventHandler handler, int op) => update(handler, SocketOperation.None, op);
 247
 248    public void update(EventHandler handler, int remove, int add)
 249    {
 1250        lock (_mutex)
 251        {
 252            Debug.Assert(!_destroyed);
 253
 254            // Don't remove what needs to be added
 1255            remove &= ~add;
 256
 257            // Don't remove/add if already un-registered or registered
 1258            remove &= handler._registered;
 1259            add &= ~handler._registered;
 1260            if (remove == add)
 261            {
 1262                return;
 263            }
 264
 1265            handler._registered &= ~remove;
 1266            handler._registered |= add;
 267
 1268            if ((add & SocketOperation.Read) != 0 && (handler._pending & SocketOperation.Read) == 0)
 269            {
 1270                handler._pending |= SocketOperation.Read;
 1271                queueReadyForIOHandler(handler, SocketOperation.Read);
 272            }
 1273            else if ((add & SocketOperation.Write) != 0 && (handler._pending & SocketOperation.Write) == 0)
 274            {
 1275                handler._pending |= SocketOperation.Write;
 1276                queueReadyForIOHandler(handler, SocketOperation.Write);
 277            }
 1278        }
 1279    }
 280
 1281    public void unregister(EventHandler handler, int op) => update(handler, op, SocketOperation.None);
 282
 283    public void finish(EventHandler handler)
 284    {
 1285        lock (_mutex)
 286        {
 287            Debug.Assert(!_destroyed);
 288
 1289            handler._registered = SocketOperation.None;
 290
 291            //
 292            // If there are no pending asynchronous operations, we can call finish on the handler now.
 293            //
 1294            if (handler._pending == 0)
 295            {
 1296                _workItems.Enqueue(current =>
 1297                    {
 1298                        current.operation = SocketOperation.None;
 1299                        current._handler = handler;
 1300                        handler.finished(current);
 1301                    });
 1302                Monitor.Pulse(_mutex);
 303            }
 304            else
 305            {
 1306                handler._finish = true;
 307            }
 1308        }
 1309    }
 310
 311    public void executeFromThisThread(System.Action call, Ice.Connection connection)
 312    {
 1313        if (_executor is not null)
 314        {
 315            try
 316            {
 1317                _executor(call, connection);
 1318            }
 0319            catch (System.Exception ex)
 320            {
 0321                if (_instance.initializationData().properties.getIcePropertyAsInt("Ice.Warn.Executor") > 0)
 322                {
 0323                    _instance.initializationData().logger.warning($"executor exception:\n{ex}");
 324                }
 0325            }
 326        }
 327        else
 328        {
 1329            call();
 330        }
 1331    }
 332
 333    public void execute(Action workItem, Ice.Connection connection)
 334    {
 1335        lock (_mutex)
 336        {
 1337            if (_destroyed)
 338            {
 0339                throw new Ice.CommunicatorDestroyedException();
 340            }
 1341            _workItems.Enqueue(current =>
 1342                {
 1343                    current.ioCompleted();
 1344                    executeFromThisThread(workItem, connection);
 1345                });
 1346            Monitor.Pulse(_mutex);
 1347        }
 1348    }
 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);
 1358        foreach (WorkerThread thread in _threads)
 359        {
 1360            thread.join();
 361        }
 1362    }
 363
 0364    public string prefix() => _prefix;
 365
 0366    public bool serialize() => _serialize;
 367
 368    protected sealed override void QueueTask(
 1369        System.Threading.Tasks.Task task) => execute(
 1370            () => TryExecuteTask(task),
 1371            null);
 372
 373    protected sealed override bool TryExecuteTaskInline(System.Threading.Tasks.Task task, bool taskWasPreviouslyQueued)
 374    {
 1375        if (!taskWasPreviouslyQueued)
 376        {
 1377            executeFromThisThread(() => TryExecuteTask(task), null);
 1378            return true;
 379        }
 1380        return false;
 381    }
 382
 0383    protected sealed override bool TryDequeue(System.Threading.Tasks.Task task) => false;
 384
 385    protected sealed override IEnumerable<System.Threading.Tasks.Task> GetScheduledTasks() =>
 0386        Array.Empty<System.Threading.Tasks.Task>();
 387
 388    private void queueReadyForIOHandler(EventHandler handler, int operation)
 389    {
 1390        lock (_mutex)
 391        {
 392            Debug.Assert(!_destroyed);
 1393            _workItems.Enqueue(current =>
 1394                {
 1395                    current._handler = handler;
 1396                    current.operation = operation;
 1397                    try
 1398                    {
 1399                        current._handler.message(current);
 1400                    }
 0401                    catch (System.Exception ex)
 1402                    {
 0403                        string s = "exception in `" + _prefix + "':\n" + ex + "\nevent handler: " +
 0404                            current._handler.ToString();
 0405                        _instance.initializationData().logger.error(s);
 0406                    }
 1407                });
 1408            Monitor.Pulse(_mutex);
 1409        }
 1410    }
 411
 412    private void run(WorkerThread thread)
 413    {
 1414        var current = new ThreadPoolCurrent(this, thread);
 415        while (true)
 416        {
 1417            ThreadPoolWorkItem workItem = null;
 1418            lock (_mutex)
 419            {
 1420                while (_workItems.Count == 0)
 421                {
 1422                    if (_destroyed)
 423                    {
 1424                        return;
 425                    }
 426
 1427                    if (_threadIdleTime != Timeout.InfiniteTimeSpan)
 428                    {
 1429                        if (!Monitor.Wait(_mutex, _threadIdleTime) && _workItems.Count == 0) // If timeout
 430                        {
 1431                            if (_destroyed)
 432                            {
 0433                                return;
 434                            }
 1435                            else if (_inUse < _threads.Count - 1)
 436                            {
 0437                                if (_instance.traceLevels().threadPool >= 1)
 438                                {
 0439                                    string s = "shrinking " + _prefix + ": Size=" + (_threads.Count - 1);
 0440                                    _instance.initializationData().logger.trace(
 0441                                        _instance.traceLevels().threadPoolCat, s);
 442                                }
 443
 0444                                _threads.Remove(thread);
 0445                                _workItems.Enqueue(c =>
 0446                                        // No call to ioCompleted, this shouldn't block (and we don't want to cause
 0447                                        // a new thread to be started).
 0448                                        thread.join());
 0449                                Monitor.Pulse(_mutex);
 0450                                return;
 451                            }
 1452                            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);
 1463                            if (!Monitor.Wait(_mutex, _serverIdleTime) && !_destroyed)
 464                            {
 1465                                _workItems.Enqueue(c =>
 1466                                    {
 1467                                        c.ioCompleted();
 1468                                        try
 1469                                        {
 1470                                            _instance.objectAdapterFactory().shutdown();
 1471                                        }
 0472                                        catch (Ice.CommunicatorDestroyedException)
 1473                                        {
 0474                                        }
 1475                                    });
 476                            }
 477                        }
 478                    }
 479                    else
 480                    {
 0481                        Monitor.Wait(_mutex);
 482                    }
 483                }
 484
 485                Debug.Assert(_workItems.Count > 0);
 1486                workItem = _workItems.Dequeue();
 487
 1488                current._thread.setState(Ice.Instrumentation.ThreadState.ThreadStateInUseForIO);
 1489                current._ioCompleted = false;
 1490            }
 491
 492            try
 493            {
 1494                workItem(current);
 1495            }
 0496            catch (System.Exception ex)
 497            {
 0498                string s = "exception in `" + _prefix + "' while calling on work item:\n" + ex + '\n';
 0499                _instance.initializationData().logger.error(s);
 0500            }
 501
 1502            lock (_mutex)
 503            {
 1504                if (_sizeMax > 1 && current._ioCompleted)
 505                {
 506                    Debug.Assert(_inUse > 0);
 1507                    --_inUse;
 508                }
 1509                thread.setState(Ice.Instrumentation.ThreadState.ThreadStateIdle);
 1510            }
 511        }
 1512    }
 513
 514    public bool ioCompleted(ThreadPoolCurrent current)
 515    {
 1516        lock (_mutex)
 517        {
 1518            current._ioCompleted = true; // Set the IO completed flag to specify that ioCompleted() has been called.
 519
 1520            current._thread.setState(Ice.Instrumentation.ThreadState.ThreadStateInUseForUser);
 521
 1522            if (_sizeMax > 1)
 523            {
 524                Debug.Assert(_inUse >= 0);
 1525                ++_inUse;
 526
 1527                if (_sizeMax > 1 && _inUse == _sizeWarn)
 528                {
 0529                    string s = "thread pool `" + _prefix + "' is running low on threads\n"
 0530                        + "Size=" + _size + ", " + "SizeMax=" + _sizeMax + ", " + "SizeWarn=" + _sizeWarn;
 0531                    _instance.initializationData().logger.warning(s);
 532                }
 533
 1534                if (!_destroyed && _inUse < _sizeMax && _inUse == _threads.Count)
 535                {
 1536                    if (_instance.traceLevels().threadPool >= 1)
 537                    {
 0538                        string s = "growing " + _prefix + ": Size = " + (_threads.Count + 1);
 0539                        _instance.initializationData().logger.trace(_instance.traceLevels().threadPoolCat, s);
 540                    }
 541
 542                    try
 543                    {
 1544                        var t = new WorkerThread(this, _threadPrefix + "-" + _threadIndex++);
 1545                        t.start(_priority);
 1546                        _threads.Add(t);
 1547                    }
 0548                    catch (System.Exception ex)
 549                    {
 0550                        string s = "cannot create thread for `" + _prefix + "':\n" + ex;
 0551                        _instance.initializationData().logger.error(s);
 0552                    }
 553                }
 554            }
 1555        }
 1556        return _serialize;
 557    }
 558
 559    public bool startMessage(ThreadPoolCurrent current)
 560    {
 561        Debug.Assert((current._handler._pending & current.operation) != 0);
 562
 1563        if ((current._handler._started & current.operation) != 0)
 564        {
 565            Debug.Assert((current._handler._ready & current.operation) == 0);
 1566            current._handler._ready |= current.operation;
 1567            current._handler._started &= ~current.operation;
 1568            if (!current._handler.finishAsync(current.operation)) // Returns false if the handler is finished.
 569            {
 1570                current._handler._pending &= ~current.operation;
 1571                if (current._handler._pending == 0 && current._handler._finish)
 572                {
 1573                    finish(current._handler);
 574                }
 1575                return false;
 576            }
 577        }
 1578        else if ((current._handler._ready & current.operation) == 0 &&
 1579                (current._handler._registered & current.operation) != 0)
 580        {
 581            Debug.Assert((current._handler._started & current.operation) == 0);
 1582            if (!current._handler.startAsync(current.operation, getCallback(current.operation)))
 583            {
 0584                current._handler._pending &= ~current.operation;
 0585                if (current._handler._pending == 0 && current._handler._finish)
 586                {
 0587                    finish(current._handler);
 588                }
 0589                return false;
 590            }
 591            else
 592            {
 1593                current._handler._started |= current.operation;
 1594                return false;
 595            }
 596        }
 597
 1598        if ((current._handler._registered & current.operation) != 0)
 599        {
 600            Debug.Assert((current._handler._ready & current.operation) != 0);
 1601            current._handler._ready &= ~current.operation;
 1602            return true;
 603        }
 604        else
 605        {
 1606            current._handler._pending &= ~current.operation;
 1607            if (current._handler._pending == 0 && current._handler._finish)
 608            {
 1609                finish(current._handler);
 610            }
 1611            return false;
 612        }
 613    }
 614
 615    public void finishMessage(ThreadPoolCurrent current)
 616    {
 1617        if ((current._handler._registered & current.operation) != 0)
 618        {
 619            Debug.Assert((current._handler._ready & current.operation) == 0);
 1620            if (!current._handler.startAsync(current.operation, getCallback(current.operation)))
 621            {
 0622                current._handler._pending &= ~current.operation;
 623            }
 624            else
 625            {
 626                Debug.Assert((current._handler._pending & current.operation) != 0);
 1627                current._handler._started |= current.operation;
 628            }
 629        }
 630        else
 631        {
 1632            current._handler._pending &= ~current.operation;
 633        }
 634
 1635        if (current._handler._pending == 0 && current._handler._finish)
 636        {
 637            // There are no more pending async operations, it's time to call finish.
 1638            finish(current._handler);
 639        }
 1640    }
 641
 642    private AsyncCallback getCallback(int operation)
 643    {
 644        Debug.Assert(operation == SocketOperation.Read || operation == SocketOperation.Write);
 1645        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
 1654    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}