< Summary

Information
Class: Ice.Internal.ThreadPool
Assembly: Ice
File(s): /_/csharp/src/Ice/Internal/ThreadPool.cs
Tag: 105_25977636357
Line coverage
71%
Covered lines: 237
Uncovered lines: 95
Coverable lines: 332
Total lines: 776
Line coverage: 71.3%
Branch coverage
70%
Covered branches: 112
Total branches: 160
Branch coverage: 70%
Method coverage
80%
Covered methods: 25
Fully covered methods: 14
Total methods: 31
Method coverage: 80.6%
Full method coverage: 45.1%

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)57.14%672863.38%
destroy()50%2285.71%
updateObservers()100%22100%
initialize(...)100%11100%
register(...)100%11100%
update(...)100%1010100%
unregister(...)100%11100%
finish(...)100%22100%
executeFromThisThread(...)50%5455.56%
execute(...)50%2287.5%
joinWithAllThreads()100%22100%
prefix()100%210%
serialize()100%210%
get_canShrink()0%620%
QueueTask(...)100%11100%
TryExecuteTaskInline(...)100%22100%
TryDequeue(...)100%210%
GetScheduledTasks()100%210%
queueReadyForIOHandler(...)100%11100%
run(...)71.88%783264.44%
ioCompleted(...)75%311660.87%
startMessage(...)79.17%272483.33%
finishMessage(...)87.5%8887.5%
getCallback(...)100%11100%
.ctor(...)100%11100%
updateObserver()100%44100%
setState(...)100%44100%
getThread()100%210%
join()100%11100%
start(...)50%2287.5%
Run()33.33%731225%

File(s)

/_/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        {
 0117            _threadPrefix = _prefix;
 118        }
 119
 1120        int size = properties.getPropertyAsIntWithDefault(_prefix + ".Size", 1);
 1121        if (size < 1)
 122        {
 0123            string s = _prefix + ".Size < 1; Size adjusted to 1";
 0124            _instance.initializationData().logger.warning(s);
 0125            size = 1;
 126        }
 127
 1128        int sizeMax = properties.getPropertyAsIntWithDefault(_prefix + ".SizeMax", size);
 1129        if (sizeMax < size)
 130        {
 1131            string s = _prefix + ".SizeMax < " + _prefix + ".Size; SizeMax adjusted to Size (" + size + ")";
 1132            _instance.initializationData().logger.warning(s);
 1133            sizeMax = size;
 134        }
 135
 1136        int sizeWarn = properties.getPropertyAsInt(_prefix + ".SizeWarn");
 1137        if (sizeWarn != 0 && sizeWarn < size)
 138        {
 0139            string s = _prefix + ".SizeWarn < " + _prefix + ".Size; adjusted SizeWarn to Size (" + size + ")";
 0140            _instance.initializationData().logger.warning(s);
 0141            sizeWarn = size;
 142        }
 1143        else if (sizeWarn > sizeMax)
 144        {
 0145            string s = _prefix + ".SizeWarn > " + _prefix + ".SizeMax; adjusted SizeWarn to SizeMax ("
 0146                + sizeMax + ")";
 0147            _instance.initializationData().logger.warning(s);
 0148            sizeWarn = sizeMax;
 149        }
 150
 1151        int threadIdleTime = properties.getPropertyAsIntWithDefault(_prefix + ".ThreadIdleTime", 60);
 1152        if (threadIdleTime < 0)
 153        {
 0154            string s = _prefix + ".ThreadIdleTime < 0; ThreadIdleTime adjusted to 0";
 0155            _instance.initializationData().logger.warning(s);
 0156            threadIdleTime = 0;
 157        }
 158
 1159        _size = size;
 1160        _sizeMax = sizeMax;
 1161        _sizeWarn = sizeWarn;
 1162        _threadIdleTime = threadIdleTime <= 0 ? Timeout.InfiniteTimeSpan : TimeSpan.FromSeconds(threadIdleTime);
 163
 1164        int stackSize = properties.getPropertyAsInt(_prefix + ".StackSize");
 1165        if (stackSize < 0)
 166        {
 0167            string s = _prefix + ".StackSize < 0; Size adjusted to OS default";
 0168            _instance.initializationData().logger.warning(s);
 0169            stackSize = 0;
 170        }
 1171        _stackSize = stackSize;
 172
 1173        _priority = properties.getProperty(_prefix + ".ThreadPriority").Length > 0 ?
 1174            Util.stringToThreadPriority(properties.getProperty(_prefix + ".ThreadPriority")) :
 1175            Util.stringToThreadPriority(properties.getIceProperty("Ice.ThreadPriority"));
 176
 1177        if (_instance.traceLevels().threadPool >= 1)
 178        {
 0179            string s = "creating " + _prefix + ": Size = " + _size + ", SizeMax = " + _sizeMax + ", SizeWarn = " +
 0180                       _sizeWarn;
 0181            _instance.initializationData().logger.trace(_instance.traceLevels().threadPoolCat, s);
 182        }
 183
 1184        _workItems = new Queue<ThreadPoolWorkItem>();
 185
 186        try
 187        {
 1188            _threads = new List<WorkerThread>();
 1189            for (int i = 0; i < _size; ++i)
 190            {
 1191                var thread = new WorkerThread(this, _threadPrefix + "-" + _threadIndex++);
 1192                thread.start(_priority);
 1193                _threads.Add(thread);
 194            }
 1195        }
 0196        catch (System.Exception ex)
 197        {
 0198            string s = "cannot create thread for `" + _prefix + "':\n" + ex;
 0199            _instance.initializationData().logger.error(s);
 200
 0201            destroy();
 0202            joinWithAllThreads();
 0203            throw;
 204        }
 1205    }
 206
 207    public void destroy()
 208    {
 1209        lock (_mutex)
 210        {
 1211            if (_destroyed)
 212            {
 0213                return;
 214            }
 1215            _destroyed = true;
 1216            Monitor.PulseAll(_mutex);
 1217        }
 1218    }
 219
 220    public void updateObservers()
 221    {
 1222        lock (_mutex)
 223        {
 1224            foreach (WorkerThread t in _threads)
 225            {
 1226                t.updateObserver();
 227            }
 228        }
 1229    }
 230
 231    public void initialize(EventHandler handler)
 232    {
 1233        handler._ready = 0;
 1234        handler._pending = 0;
 1235        handler._started = 0;
 1236        handler._finish = false;
 1237        handler._hasMoreData = false;
 1238        handler._registered = 0;
 1239    }
 240
 1241    public void register(EventHandler handler, int op) => update(handler, SocketOperation.None, op);
 242
 243    public void update(EventHandler handler, int remove, int add)
 244    {
 1245        lock (_mutex)
 246        {
 247            Debug.Assert(!_destroyed);
 248
 249            // Don't remove what needs to be added
 1250            remove &= ~add;
 251
 252            // Don't remove/add if already un-registered or registered
 1253            remove &= handler._registered;
 1254            add &= ~handler._registered;
 1255            if (remove == add)
 256            {
 1257                return;
 258            }
 259
 1260            handler._registered &= ~remove;
 1261            handler._registered |= add;
 262
 1263            if ((add & SocketOperation.Read) != 0 && (handler._pending & SocketOperation.Read) == 0)
 264            {
 1265                handler._pending |= SocketOperation.Read;
 1266                queueReadyForIOHandler(handler, SocketOperation.Read);
 267            }
 1268            else if ((add & SocketOperation.Write) != 0 && (handler._pending & SocketOperation.Write) == 0)
 269            {
 1270                handler._pending |= SocketOperation.Write;
 1271                queueReadyForIOHandler(handler, SocketOperation.Write);
 272            }
 1273        }
 1274    }
 275
 1276    public void unregister(EventHandler handler, int op) => update(handler, op, SocketOperation.None);
 277
 278    public void finish(EventHandler handler)
 279    {
 1280        lock (_mutex)
 281        {
 282            Debug.Assert(!_destroyed);
 283
 1284            handler._registered = SocketOperation.None;
 285
 286            //
 287            // If there are no pending asynchronous operations, we can call finish on the handler now.
 288            //
 1289            if (handler._pending == 0)
 290            {
 1291                _workItems.Enqueue(current =>
 1292                    {
 1293                        current.operation = SocketOperation.None;
 1294                        current._handler = handler;
 1295                        handler.finished(current);
 1296                    });
 1297                Monitor.Pulse(_mutex);
 298            }
 299            else
 300            {
 1301                handler._finish = true;
 302            }
 1303        }
 1304    }
 305
 306    public void executeFromThisThread(System.Action call, Ice.Connection connection)
 307    {
 1308        if (_executor is not null)
 309        {
 310            try
 311            {
 1312                _executor(call, connection);
 1313            }
 0314            catch (System.Exception ex)
 315            {
 0316                if (_instance.initializationData().properties.getIcePropertyAsInt("Ice.Warn.Executor") > 0)
 317                {
 0318                    _instance.initializationData().logger.warning($"executor exception:\n{ex}");
 319                }
 0320            }
 321        }
 322        else
 323        {
 1324            call();
 325        }
 1326    }
 327
 328    public void execute(Action workItem, Ice.Connection connection)
 329    {
 1330        lock (_mutex)
 331        {
 1332            if (_destroyed)
 333            {
 0334                throw new Ice.CommunicatorDestroyedException();
 335            }
 1336            _workItems.Enqueue(current =>
 1337                {
 1338                    current.ioCompleted();
 1339                    executeFromThisThread(workItem, connection);
 1340                });
 1341            Monitor.Pulse(_mutex);
 1342        }
 1343    }
 344
 345    public void joinWithAllThreads()
 346    {
 347        //
 348        // _threads is immutable after destroy() has been called, therefore no synchronization is needed.
 349        // (Synchronization wouldn't be possible here anyway, because otherwise the other threads would never
 350        // terminate.)
 351        //
 352        Debug.Assert(_destroyed);
 1353        foreach (WorkerThread thread in _threads)
 354        {
 1355            thread.join();
 356        }
 1357    }
 358
 0359    public string prefix() => _prefix;
 360
 0361    public bool serialize() => _serialize;
 362
 363    // A worker thread can exit only when both conditions hold: the thread idle time is finite (so an idle worker
 364    // can time out), and the pool can have more than one worker (so a worker is eligible to be reaped without
 365    // dropping the pool below its floor of 1). Used by ConnectionI.startAsync to decide whether async I/O initiated
 366    // on an Ice worker thread needs to be hopped onto a .NET ThreadPool thread to survive the worker's exit.
 0367    public bool canShrink => _threadIdleTime != Timeout.InfiniteTimeSpan && _sizeMax > 1;
 368
 369    protected sealed override void QueueTask(
 1370        System.Threading.Tasks.Task task) => execute(
 1371            () => TryExecuteTask(task),
 1372            null);
 373
 374    protected sealed override bool TryExecuteTaskInline(System.Threading.Tasks.Task task, bool taskWasPreviouslyQueued)
 375    {
 1376        if (!taskWasPreviouslyQueued)
 377        {
 1378            executeFromThisThread(() => TryExecuteTask(task), null);
 1379            return true;
 380        }
 1381        return false;
 382    }
 383
 0384    protected sealed override bool TryDequeue(System.Threading.Tasks.Task task) => false;
 385
 386    protected sealed override IEnumerable<System.Threading.Tasks.Task> GetScheduledTasks() =>
 0387        Array.Empty<System.Threading.Tasks.Task>();
 388
 389    private void queueReadyForIOHandler(EventHandler handler, int operation)
 390    {
 1391        lock (_mutex)
 392        {
 393            Debug.Assert(!_destroyed);
 1394            _workItems.Enqueue(current =>
 1395                {
 1396                    current._handler = handler;
 1397                    current.operation = operation;
 1398                    try
 1399                    {
 1400                        current._handler.message(current);
 1401                    }
 0402                    catch (System.Exception ex)
 1403                    {
 0404                        string s = "exception in `" + _prefix + "':\n" + ex + "\nevent handler: " +
 0405                            current._handler.ToString();
 0406                        _instance.initializationData().logger.error(s);
 0407                    }
 1408                });
 1409            Monitor.Pulse(_mutex);
 1410        }
 1411    }
 412
 413    private void run(WorkerThread thread)
 414    {
 1415        var current = new ThreadPoolCurrent(this, thread);
 416        while (true)
 417        {
 1418            ThreadPoolWorkItem workItem = null;
 1419            lock (_mutex)
 420            {
 1421                while (_workItems.Count == 0)
 422                {
 1423                    if (_destroyed)
 424                    {
 1425                        return;
 426                    }
 427
 1428                    if (_threadIdleTime != Timeout.InfiniteTimeSpan)
 429                    {
 1430                        if (!Monitor.Wait(_mutex, _threadIdleTime) && _workItems.Count == 0) // If timeout
 431                        {
 1432                            if (_destroyed)
 433                            {
 0434                                return;
 435                            }
 1436                            else if (_inUse < _threads.Count - 1)
 437                            {
 0438                                if (_instance.traceLevels().threadPool >= 1)
 439                                {
 0440                                    string s = "shrinking " + _prefix + ": Size=" + (_threads.Count - 1);
 0441                                    _instance.initializationData().logger.trace(
 0442                                        _instance.traceLevels().threadPoolCat, s);
 443                                }
 444
 0445                                _threads.Remove(thread);
 0446                                _workItems.Enqueue(c =>
 0447                                        // No call to ioCompleted, this shouldn't block (and we don't want to cause
 0448                                        // a new thread to be started).
 0449                                        thread.join());
 0450                                Monitor.Pulse(_mutex);
 0451                                return;
 452                            }
 1453                            else if (_inUse > 0)
 454                            {
 455                                //
 456                                // If this is the last idle thread but there are still other threads
 457                                // busy dispatching, we go back waiting with _threadIdleTime. We only
 458                                // wait with _serverIdleTime when there's only one thread left.
 459                                //
 460                                continue;
 461                            }
 462
 463                            Debug.Assert(_threads.Count == 1);
 1464                            if (!Monitor.Wait(_mutex, _serverIdleTime) && !_destroyed)
 465                            {
 1466                                _workItems.Enqueue(c =>
 1467                                    {
 1468                                        c.ioCompleted();
 1469                                        try
 1470                                        {
 1471                                            _instance.objectAdapterFactory().shutdown();
 1472                                        }
 0473                                        catch (Ice.CommunicatorDestroyedException)
 1474                                        {
 0475                                        }
 1476                                    });
 477                            }
 478                        }
 479                    }
 480                    else
 481                    {
 0482                        Monitor.Wait(_mutex);
 483                    }
 484                }
 485
 486                Debug.Assert(_workItems.Count > 0);
 1487                workItem = _workItems.Dequeue();
 488
 1489                current._thread.setState(Ice.Instrumentation.ThreadState.ThreadStateInUseForIO);
 1490                current._ioCompleted = false;
 1491            }
 492
 493            try
 494            {
 1495                workItem(current);
 1496            }
 0497            catch (System.Exception ex)
 498            {
 0499                string s = "exception in `" + _prefix + "' while calling on work item:\n" + ex + '\n';
 0500                _instance.initializationData().logger.error(s);
 0501            }
 502
 1503            lock (_mutex)
 504            {
 1505                if (_sizeMax > 1 && current._ioCompleted)
 506                {
 507                    Debug.Assert(_inUse > 0);
 1508                    --_inUse;
 509                }
 1510                thread.setState(Ice.Instrumentation.ThreadState.ThreadStateIdle);
 1511            }
 512        }
 1513    }
 514
 515    public bool ioCompleted(ThreadPoolCurrent current)
 516    {
 1517        lock (_mutex)
 518        {
 1519            current._ioCompleted = true; // Set the IO completed flag to specify that ioCompleted() has been called.
 520
 1521            current._thread.setState(Ice.Instrumentation.ThreadState.ThreadStateInUseForUser);
 522
 1523            if (_sizeMax > 1)
 524            {
 525                Debug.Assert(_inUse >= 0);
 1526                ++_inUse;
 527
 1528                if (_sizeMax > 1 && _inUse == _sizeWarn)
 529                {
 0530                    string s = "thread pool `" + _prefix + "' is running low on threads\n"
 0531                        + "Size=" + _size + ", " + "SizeMax=" + _sizeMax + ", " + "SizeWarn=" + _sizeWarn;
 0532                    _instance.initializationData().logger.warning(s);
 533                }
 534
 1535                if (!_destroyed && _inUse < _sizeMax && _inUse == _threads.Count)
 536                {
 1537                    if (_instance.traceLevels().threadPool >= 1)
 538                    {
 0539                        string s = "growing " + _prefix + ": Size = " + (_threads.Count + 1);
 0540                        _instance.initializationData().logger.trace(_instance.traceLevels().threadPoolCat, s);
 541                    }
 542
 543                    try
 544                    {
 1545                        var t = new WorkerThread(this, _threadPrefix + "-" + _threadIndex++);
 1546                        t.start(_priority);
 1547                        _threads.Add(t);
 1548                    }
 0549                    catch (System.Exception ex)
 550                    {
 0551                        string s = "cannot create thread for `" + _prefix + "':\n" + ex;
 0552                        _instance.initializationData().logger.error(s);
 0553                    }
 554                }
 555            }
 1556        }
 1557        return _serialize;
 558    }
 559
 560    public bool startMessage(ThreadPoolCurrent current)
 561    {
 562        Debug.Assert((current._handler._pending & current.operation) != 0);
 563
 1564        if ((current._handler._started & current.operation) != 0)
 565        {
 566            Debug.Assert((current._handler._ready & current.operation) == 0);
 1567            current._handler._ready |= current.operation;
 1568            current._handler._started &= ~current.operation;
 1569            if (!current._handler.finishAsync(current.operation)) // Returns false if the handler is finished.
 570            {
 1571                current._handler._pending &= ~current.operation;
 1572                if (current._handler._pending == 0 && current._handler._finish)
 573                {
 1574                    finish(current._handler);
 575                }
 1576                return false;
 577            }
 578        }
 1579        else if ((current._handler._ready & current.operation) == 0 &&
 1580                (current._handler._registered & current.operation) != 0)
 581        {
 582            Debug.Assert((current._handler._started & current.operation) == 0);
 1583            if (!current._handler.startAsync(current.operation, getCallback(current.operation)))
 584            {
 0585                current._handler._pending &= ~current.operation;
 0586                if (current._handler._pending == 0 && current._handler._finish)
 587                {
 0588                    finish(current._handler);
 589                }
 0590                return false;
 591            }
 592            else
 593            {
 1594                current._handler._started |= current.operation;
 1595                return false;
 596            }
 597        }
 598
 1599        if ((current._handler._registered & current.operation) != 0)
 600        {
 601            Debug.Assert((current._handler._ready & current.operation) != 0);
 1602            current._handler._ready &= ~current.operation;
 1603            return true;
 604        }
 605        else
 606        {
 1607            current._handler._pending &= ~current.operation;
 1608            if (current._handler._pending == 0 && current._handler._finish)
 609            {
 1610                finish(current._handler);
 611            }
 1612            return false;
 613        }
 614    }
 615
 616    public void finishMessage(ThreadPoolCurrent current)
 617    {
 1618        if ((current._handler._registered & current.operation) != 0)
 619        {
 620            Debug.Assert((current._handler._ready & current.operation) == 0);
 1621            if (!current._handler.startAsync(current.operation, getCallback(current.operation)))
 622            {
 0623                current._handler._pending &= ~current.operation;
 624            }
 625            else
 626            {
 627                Debug.Assert((current._handler._pending & current.operation) != 0);
 1628                current._handler._started |= current.operation;
 629            }
 630        }
 631        else
 632        {
 1633            current._handler._pending &= ~current.operation;
 634        }
 635
 1636        if (current._handler._pending == 0 && current._handler._finish)
 637        {
 638            // There are no more pending async operations, it's time to call finish.
 1639            finish(current._handler);
 640        }
 1641    }
 642
 643    private AsyncCallback getCallback(int operation)
 644    {
 645        Debug.Assert(operation == SocketOperation.Read || operation == SocketOperation.Write);
 1646        return state => queueReadyForIOHandler((EventHandler)state, operation);
 647    }
 648
 649    private readonly Instance _instance;
 650    private readonly System.Action<System.Action, Ice.Connection> _executor;
 651    private bool _destroyed;
 652    private readonly string _prefix;
 653    private readonly string _threadPrefix;
 654
 1655    private readonly object _mutex = new object();
 656
 657    internal sealed class WorkerThread
 658    {
 659        private readonly ThreadPool _threadPool;
 660        private Ice.Instrumentation.ThreadObserver _observer;
 661        private Ice.Instrumentation.ThreadState _state;
 662
 1663        internal WorkerThread(ThreadPool threadPool, string name)
 664        {
 1665            _threadPool = threadPool;
 1666            _name = name;
 1667            _state = Ice.Instrumentation.ThreadState.ThreadStateIdle;
 1668            updateObserver();
 1669        }
 670
 671        public void updateObserver()
 672        {
 673            // Must be called with the thread pool mutex locked
 1674            Ice.Instrumentation.CommunicatorObserver obsv = _threadPool._instance.initializationData().observer;
 1675            if (obsv is not null)
 676            {
 1677                _observer = obsv.getThreadObserver(_threadPool._prefix, _name, _state, _observer);
 1678                _observer?.attach();
 679            }
 1680        }
 681
 682        public void setState(Ice.Instrumentation.ThreadState s)
 683        {
 684            // Must be called with the thread pool mutex locked
 1685            if (_observer is not null)
 686            {
 1687                if (_state != s)
 688                {
 1689                    _observer.stateChanged(_state, s);
 690                }
 691            }
 1692            _state = s;
 1693        }
 694
 0695        public Thread getThread() => _thread;
 696
 1697        public void join() => _thread.Join();
 698
 699        public void start(ThreadPriority priority)
 700        {
 1701            if (_threadPool._stackSize == 0)
 702            {
 1703                _thread = new Thread(new ThreadStart(Run));
 704            }
 705            else
 706            {
 0707                _thread = new Thread(new ThreadStart(Run), _threadPool._stackSize);
 708            }
 1709            _thread.IsBackground = true;
 1710            _thread.Name = _name;
 1711            _thread.Priority = priority;
 1712            _thread.Start();
 1713        }
 714
 715        public void Run()
 716        {
 1717            if (_threadPool._instance.initializationData().threadStart is not null)
 718            {
 719                try
 720                {
 0721                    _threadPool._instance.initializationData().threadStart();
 0722                }
 0723                catch (System.Exception ex)
 724                {
 0725                    string s = "thread hook start() method raised an unexpected exception in `";
 0726                    s += _threadPool._prefix + "' thread " + _thread.Name + ":\n" + ex;
 0727                    _threadPool._instance.initializationData().logger.error(s);
 0728                }
 729            }
 730
 731            try
 732            {
 1733                _threadPool.run(this);
 1734            }
 0735            catch (System.Exception ex)
 736            {
 0737                string s = "exception in `" + _threadPool._prefix + "' thread " + _thread.Name + ":\n" + ex;
 0738                _threadPool._instance.initializationData().logger.error(s);
 0739            }
 740
 1741            _observer?.detach();
 742
 1743            if (_threadPool._instance.initializationData().threadStop is not null)
 744            {
 745                try
 746                {
 0747                    _threadPool._instance.initializationData().threadStop();
 0748                }
 0749                catch (System.Exception ex)
 750                {
 0751                    string s = "thread hook stop() method raised an unexpected exception in `";
 0752                    s += _threadPool._prefix + "' thread " + _thread.Name + ":\n" + ex;
 0753                    _threadPool._instance.initializationData().logger.error(s);
 0754                }
 755            }
 1756        }
 757
 758        private readonly string _name;
 759        private Thread _thread;
 760    }
 761
 762    private readonly int _size; // Number of threads that are pre-created.
 763    private readonly int _sizeMax; // Maximum number of threads.
 764    private readonly int _sizeWarn; // If _inUse reaches _sizeWarn, a "low on threads" warning will be printed.
 765    private readonly bool _serialize; // True if requests need to be serialized over the connection.
 766    private readonly ThreadPriority _priority;
 767    private readonly TimeSpan _serverIdleTime;
 768    private readonly TimeSpan _threadIdleTime;
 769    private readonly int _stackSize;
 770
 771    private readonly List<WorkerThread> _threads; // All threads, running or not.
 772    private int _threadIndex; // For assigning thread names.
 773    private int _inUse; // Number of threads that are currently in use.
 774
 775    private readonly Queue<ThreadPoolWorkItem> _workItems;
 776}