< Summary

Information
Class: Ice.Internal.LocatorInfo.Request
Assembly: Ice
File(s): /home/runner/work/ice/ice/csharp/src/Ice/Internal/LocatorInfo.cs
Tag: 71_18251537082
Line coverage
100%
Covered lines: 41
Uncovered lines: 0
Coverable lines: 41
Total lines: 913
Line coverage: 100%
Branch coverage
100%
Covered branches: 14
Total branches: 14
Branch coverage: 100%
Method coverage
100%
Covered methods: 4
Total methods: 4
Method coverage: 100%

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
addCallback(...)100%1010100%
.ctor(...)100%11100%
response(...)100%22100%
exception(...)100%22100%

File(s)

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

#LineLine coverage
 1// Copyright (c) ZeroC, Inc.
 2
 3using System.Diagnostics;
 4
 5namespace Ice.Internal;
 6
 7public sealed class LocatorInfo : IEquatable<LocatorInfo>
 8{
 9    public interface GetEndpointsCallback
 10    {
 11        void setEndpoints(EndpointI[] endpoints, bool cached);
 12
 13        void setException(Ice.LocalException ex);
 14    }
 15
 16    private class RequestCallback
 17    {
 18        public void
 19        response(LocatorInfo locatorInfo, Ice.ObjectPrx proxy)
 20        {
 21            EndpointI[] endpoints = null;
 22            if (proxy != null)
 23            {
 24                Reference r = ((Ice.ObjectPrxHelperBase)proxy).iceReference();
 25                if (_ref.isWellKnown() && !Protocol.isSupported(_ref.getEncoding(), r.getEncoding()))
 26                {
 27                    //
 28                    // If a well-known proxy and the returned
 29                    // proxy encoding isn't supported, we're done:
 30                    // there's no compatible endpoint we can use.
 31                    //
 32                }
 33                else if (!r.isIndirect())
 34                {
 35                    endpoints = r.getEndpoints();
 36                }
 37                else if (_ref.isWellKnown() && !r.isWellKnown())
 38                {
 39                    //
 40                    // We're resolving the endpoints of a well-known object and the proxy returned
 41                    // by the locator is an indirect proxy. We now need to resolve the endpoints
 42                    // of this indirect proxy.
 43                    //
 44                    if (_ref.getInstance().traceLevels().location >= 1)
 45                    {
 46                        locatorInfo.trace(
 47                            "retrieved adapter for well-known object from locator, adding to locator cache",
 48                            _ref,
 49                            r);
 50                    }
 51                    locatorInfo.getEndpoints(r, _ref, _ttl, _callback);
 52                    return;
 53                }
 54            }
 55
 56            if (_ref.getInstance().traceLevels().location >= 1)
 57            {
 58                locatorInfo.getEndpointsTrace(_ref, endpoints, false);
 59            }
 60            _callback?.setEndpoints(endpoints ?? [], false);
 61        }
 62
 63        public void
 64        exception(LocatorInfo locatorInfo, Ice.Exception exc)
 65        {
 66            try
 67            {
 68                locatorInfo.getEndpointsException(_ref, exc); // This throws.
 69            }
 70            catch (Ice.LocalException ex)
 71            {
 72                _callback?.setException(ex);
 73            }
 74        }
 75
 76        public
 77        RequestCallback(Reference @ref, TimeSpan ttl, GetEndpointsCallback cb)
 78        {
 79            _ref = @ref;
 80            _ttl = ttl;
 81            _callback = cb;
 82        }
 83
 84        private readonly Reference _ref;
 85        private readonly TimeSpan _ttl;
 86        private readonly GetEndpointsCallback _callback;
 87    }
 88
 89    private abstract class Request
 90    {
 91        public void
 92        addCallback(Reference @ref, Reference wellKnownRef, TimeSpan ttl, GetEndpointsCallback cb)
 93        {
 194            var callback = new RequestCallback(@ref, ttl, cb);
 195            lock (_mutex)
 96            {
 197                if (!_response && _exception == null)
 98                {
 199                    _callbacks.Add(callback);
 1100                    if (wellKnownRef != null)
 101                    {
 102                        // This request is to resolve the endpoints of a cached well-known object ref
 1103                        _wellKnownRefs.Add(wellKnownRef);
 104                    }
 1105                    if (!_sent)
 106                    {
 1107                        _sent = true;
 1108                        send();
 109                    }
 1110                    return;
 111                }
 1112            }
 113
 1114            if (_response)
 115            {
 1116                callback.response(_locatorInfo, _proxy);
 117            }
 118            else
 119            {
 120                Debug.Assert(_exception != null);
 1121                callback.exception(_locatorInfo, _exception);
 122            }
 1123        }
 124
 1125        public Request(LocatorInfo locatorInfo, Reference @ref)
 126        {
 1127            _locatorInfo = locatorInfo;
 1128            _ref = @ref;
 1129            _sent = false;
 1130            _response = false;
 1131        }
 132
 133        public void
 134        response(Ice.ObjectPrx proxy)
 135        {
 1136            lock (_mutex)
 137            {
 1138                _locatorInfo.finishRequest(_ref, _wellKnownRefs, proxy, false);
 1139                _response = true;
 1140                _proxy = proxy;
 1141                Monitor.PulseAll(_mutex);
 1142            }
 1143            foreach (RequestCallback callback in _callbacks)
 144            {
 1145                callback.response(_locatorInfo, proxy);
 146            }
 1147        }
 148
 149        public void
 150        exception(Ice.Exception ex)
 151        {
 1152            lock (_mutex)
 153            {
 1154                _locatorInfo.finishRequest(_ref, _wellKnownRefs, null, ex is Ice.UserException);
 1155                _exception = ex;
 1156                Monitor.PulseAll(_mutex);
 1157            }
 158
 1159            foreach (RequestCallback callback in _callbacks)
 160            {
 1161                callback.exception(_locatorInfo, ex);
 162            }
 1163        }
 164
 165        protected abstract void send();
 166
 167        protected readonly LocatorInfo _locatorInfo;
 168        protected readonly Reference _ref;
 169
 1170        private readonly List<RequestCallback> _callbacks = new List<RequestCallback>();
 1171        private readonly List<Reference> _wellKnownRefs = new List<Reference>();
 172        private bool _sent;
 173        private bool _response;
 174        private Ice.ObjectPrx _proxy;
 175        private Ice.Exception _exception;
 1176        private readonly object _mutex = new();
 177    }
 178
 179    private class ObjectRequest : Request
 180    {
 181        public ObjectRequest(LocatorInfo locatorInfo, Reference reference)
 182            : base(locatorInfo, reference)
 183        {
 184        }
 185
 186        protected override void
 187        send()
 188        {
 189            _ = Task.Run(
 190                async () =>
 191                {
 192                    try
 193                    {
 194                        LocatorPrx locator = _locatorInfo.getLocator();
 195                        ObjectPrx proxy = await locator.findObjectByIdAsync(_ref.getIdentity()).ConfigureAwait(false);
 196                        response(proxy);
 197                    }
 198                    catch (Ice.Exception ex)
 199                    {
 200                        exception(ex);
 201                    }
 202                });
 203        }
 204    }
 205
 206    private class AdapterRequest : Request
 207    {
 208        public AdapterRequest(LocatorInfo locatorInfo, Reference reference)
 209            : base(locatorInfo, reference)
 210        {
 211        }
 212
 213        protected override void
 214        send()
 215        {
 216            _ = Task.Run(
 217                async () =>
 218                {
 219                    try
 220                    {
 221                        LocatorPrx locator = _locatorInfo.getLocator();
 222                        ObjectPrx proxy =
 223                            await locator.findAdapterByIdAsync(_ref.getAdapterId()).ConfigureAwait(false);
 224                        response(proxy);
 225                    }
 226                    catch (Ice.Exception ex)
 227                    {
 228                        exception(ex);
 229                    }
 230                });
 231        }
 232    }
 233
 234    internal LocatorInfo(Ice.LocatorPrx locator, LocatorTable table, bool background)
 235    {
 236        _locator = locator;
 237        _table = table;
 238        _background = background;
 239    }
 240
 241    public void destroy()
 242    {
 243        lock (_mutex)
 244        {
 245            _locatorRegistry = null;
 246            _table.clear();
 247        }
 248    }
 249
 250    public static bool operator ==(LocatorInfo lhs, LocatorInfo rhs) => lhs is not null ? lhs.Equals(rhs) : rhs is null;
 251
 252    public static bool operator !=(LocatorInfo lhs, LocatorInfo rhs) => !(lhs == rhs);
 253
 254    public bool Equals(LocatorInfo other) =>
 255        ReferenceEquals(this, other) || (other is not null && _locator.Equals(other._locator));
 256
 257    public override bool Equals(object obj) => Equals(obj as LocatorInfo);
 258
 259    public override int GetHashCode() => _locator.GetHashCode();
 260
 261    public Ice.LocatorPrx getLocator() =>
 262        //
 263        // No synchronization necessary, _locator is immutable.
 264        //
 265        _locator;
 266
 267    public Ice.LocatorRegistryPrx getLocatorRegistry()
 268    {
 269        lock (_mutex)
 270        {
 271            if (_locatorRegistry != null)
 272            {
 273                return _locatorRegistry;
 274            }
 275        }
 276
 277        //
 278        // Do not make locator calls from within sync.
 279        //
 280        Ice.LocatorRegistryPrx locatorRegistry = _locator.getRegistry();
 281        if (locatorRegistry == null)
 282        {
 283            return null;
 284        }
 285
 286        lock (_mutex)
 287        {
 288            //
 289            // The locator registry can't be located. We use ordered
 290            // endpoint selection in case the locator returned a proxy
 291            // with some endpoints which are preferred to be tried first.
 292            //
 293            _locatorRegistry = (Ice.LocatorRegistryPrx)locatorRegistry.ice_locator(null).ice_endpointSelection(
 294                Ice.EndpointSelectionType.Ordered);
 295            return _locatorRegistry;
 296        }
 297    }
 298
 299    public void
 300    getEndpoints(Reference @ref, TimeSpan ttl, GetEndpointsCallback callback) =>
 301        getEndpoints(@ref, null, ttl, callback);
 302
 303    public void
 304    getEndpoints(Reference @ref, Reference wellKnownRef, TimeSpan ttl, GetEndpointsCallback callback)
 305    {
 306        Debug.Assert(@ref.isIndirect());
 307        EndpointI[] endpoints = null;
 308        bool cached;
 309        if (!@ref.isWellKnown())
 310        {
 311            endpoints = _table.getAdapterEndpoints(@ref.getAdapterId(), ttl, out cached);
 312            if (!cached)
 313            {
 314                if (_background && endpoints != null)
 315                {
 316                    getAdapterRequest(@ref).addCallback(@ref, wellKnownRef, ttl, null);
 317                }
 318                else
 319                {
 320                    getAdapterRequest(@ref).addCallback(@ref, wellKnownRef, ttl, callback);
 321                    return;
 322                }
 323            }
 324        }
 325        else
 326        {
 327            Reference r = _table.getObjectReference(@ref.getIdentity(), ttl, out cached);
 328            if (!cached)
 329            {
 330                if (_background && r != null)
 331                {
 332                    getObjectRequest(@ref).addCallback(@ref, null, ttl, null);
 333                }
 334                else
 335                {
 336                    getObjectRequest(@ref).addCallback(@ref, null, ttl, callback);
 337                    return;
 338                }
 339            }
 340
 341            if (!r.isIndirect())
 342            {
 343                endpoints = r.getEndpoints();
 344            }
 345            else if (!r.isWellKnown())
 346            {
 347                if (@ref.getInstance().traceLevels().location >= 1)
 348                {
 349                    trace("found adapter for well-known object in locator cache", @ref, r);
 350                }
 351                getEndpoints(r, @ref, ttl, callback);
 352                return;
 353            }
 354        }
 355
 356        Debug.Assert(endpoints != null);
 357        if (@ref.getInstance().traceLevels().location >= 1)
 358        {
 359            getEndpointsTrace(@ref, endpoints, true);
 360        }
 361        callback?.setEndpoints(endpoints, true);
 362    }
 363
 364    public void clearCache(Reference rf)
 365    {
 366        Debug.Assert(rf.isIndirect());
 367        if (!rf.isWellKnown())
 368        {
 369            EndpointI[] endpoints = _table.removeAdapterEndpoints(rf.getAdapterId());
 370
 371            if (endpoints != null && rf.getInstance().traceLevels().location >= 2)
 372            {
 373                trace("removed endpoints for adapter from locator cache", rf, endpoints);
 374            }
 375        }
 376        else
 377        {
 378            Reference r = _table.removeObjectReference(rf.getIdentity());
 379            if (r != null)
 380            {
 381                if (!r.isIndirect())
 382                {
 383                    if (rf.getInstance().traceLevels().location >= 2)
 384                    {
 385                        trace("removed endpoints for well-known object from locator cache", rf, r.getEndpoints());
 386                    }
 387                }
 388                else if (!r.isWellKnown())
 389                {
 390                    if (rf.getInstance().traceLevels().location >= 2)
 391                    {
 392                        trace("removed adapter for well-known object from locator cache", rf, r);
 393                    }
 394                    clearCache(r);
 395                }
 396            }
 397        }
 398    }
 399
 400    private void trace(string msg, Reference r, EndpointI[] endpoints)
 401    {
 402        var s = new System.Text.StringBuilder();
 403        s.Append(msg + "\n");
 404        if (r.getAdapterId().Length > 0)
 405        {
 406            s.Append("adapter = " + r.getAdapterId() + "\n");
 407        }
 408        else
 409        {
 410            s.Append("well-known proxy = " + r.ToString() + "\n");
 411        }
 412
 413        s.Append("endpoints = ");
 414        int sz = endpoints.Length;
 415        for (int i = 0; i < sz; i++)
 416        {
 417            s.Append(endpoints[i].ToString());
 418            if (i + 1 < sz)
 419            {
 420                s.Append(':');
 421            }
 422        }
 423
 424        r.getInstance().initializationData().logger.trace(r.getInstance().traceLevels().locationCat, s.ToString());
 425    }
 426
 427    private void trace(string msg, Reference r, Reference resolved)
 428    {
 429        Debug.Assert(r.isWellKnown());
 430
 431        var s = new System.Text.StringBuilder();
 432        s.Append(msg);
 433        s.Append('\n');
 434        s.Append("well-known proxy = ");
 435        s.Append(r.ToString());
 436        s.Append('\n');
 437        s.Append("adapter = ");
 438        s.Append(resolved.getAdapterId());
 439
 440        r.getInstance().initializationData().logger.trace(r.getInstance().traceLevels().locationCat, s.ToString());
 441    }
 442
 443    private void getEndpointsException(Reference @ref, System.Exception exc)
 444    {
 445        try
 446        {
 447            throw exc;
 448        }
 449        catch (Ice.AdapterNotFoundException)
 450        {
 451            Instance instance = @ref.getInstance();
 452            if (instance.traceLevels().location >= 1)
 453            {
 454                var s = new System.Text.StringBuilder();
 455                s.Append("adapter not found\n");
 456                s.Append("adapter = " + @ref.getAdapterId());
 457                instance.initializationData().logger.trace(instance.traceLevels().locationCat, s.ToString());
 458            }
 459
 460            throw new NotRegisteredException("object adapter", @ref.getAdapterId());
 461        }
 462        catch (Ice.ObjectNotFoundException)
 463        {
 464            Instance instance = @ref.getInstance();
 465            if (instance.traceLevels().location >= 1)
 466            {
 467                var s = new System.Text.StringBuilder();
 468                s.Append("object not found\n");
 469                s.Append("object = " + Ice.Util.identityToString(@ref.getIdentity(), instance.toStringMode()));
 470                instance.initializationData().logger.trace(instance.traceLevels().locationCat, s.ToString());
 471            }
 472
 473            throw new NotRegisteredException(
 474                "object",
 475                Ice.Util.identityToString(@ref.getIdentity(), instance.toStringMode()));
 476        }
 477        catch (Ice.NotRegisteredException)
 478        {
 479            throw;
 480        }
 481        catch (Ice.LocalException ex)
 482        {
 483            Instance instance = @ref.getInstance();
 484            if (instance.traceLevels().location >= 1)
 485            {
 486                var s = new System.Text.StringBuilder();
 487                s.Append("couldn't contact the locator to retrieve endpoints\n");
 488                if (@ref.getAdapterId().Length > 0)
 489                {
 490                    s.Append("adapter = " + @ref.getAdapterId() + "\n");
 491                }
 492                else
 493                {
 494                    s.Append("well-known proxy = " + @ref.ToString() + "\n");
 495                }
 496                s.Append("reason = " + ex);
 497                instance.initializationData().logger.trace(instance.traceLevels().locationCat, s.ToString());
 498            }
 499            throw;
 500        }
 501        catch (System.Exception)
 502        {
 503            Debug.Assert(false);
 504        }
 505    }
 506
 507    private void getEndpointsTrace(Reference @ref, EndpointI[] endpoints, bool cached)
 508    {
 509        if (endpoints != null && endpoints.Length > 0)
 510        {
 511            if (cached)
 512            {
 513                if (@ref.isWellKnown())
 514                {
 515                    trace("found endpoints for well-known proxy in locator cache", @ref, endpoints);
 516                }
 517                else
 518                {
 519                    trace("found endpoints for adapter in locator cache", @ref, endpoints);
 520                }
 521            }
 522            else
 523            {
 524                if (@ref.isWellKnown())
 525                {
 526                    trace(
 527                        "retrieved endpoints for well-known proxy from locator, adding to locator cache",
 528                        @ref,
 529                        endpoints);
 530                }
 531                else
 532                {
 533                    trace(
 534                        "retrieved endpoints for adapter from locator, adding to locator cache",
 535                        @ref,
 536                        endpoints);
 537                }
 538            }
 539        }
 540        else
 541        {
 542            Instance instance = @ref.getInstance();
 543            var s = new System.Text.StringBuilder();
 544            s.Append("no endpoints configured for ");
 545            if (@ref.getAdapterId().Length > 0)
 546            {
 547                s.Append("adapter\n");
 548                s.Append("adapter = " + @ref.getAdapterId());
 549            }
 550            else
 551            {
 552                s.Append("well-known object\n");
 553                s.Append("well-known proxy = " + @ref.ToString());
 554            }
 555            instance.initializationData().logger.trace(instance.traceLevels().locationCat, s.ToString());
 556        }
 557    }
 558
 559    private Request
 560    getAdapterRequest(Reference @ref)
 561    {
 562        if (@ref.getInstance().traceLevels().location >= 1)
 563        {
 564            Instance instance = @ref.getInstance();
 565            var s = new System.Text.StringBuilder();
 566            s.Append("searching for adapter by id\nadapter = ");
 567            s.Append(@ref.getAdapterId());
 568            instance.initializationData().logger.trace(instance.traceLevels().locationCat, s.ToString());
 569        }
 570
 571        lock (_mutex)
 572        {
 573            if (_adapterRequests.TryGetValue(@ref.getAdapterId(), out Request request))
 574            {
 575                return request;
 576            }
 577
 578            request = new AdapterRequest(this, @ref);
 579            _adapterRequests.Add(@ref.getAdapterId(), request);
 580            return request;
 581        }
 582    }
 583
 584    private Request
 585    getObjectRequest(Reference @ref)
 586    {
 587        if (@ref.getInstance().traceLevels().location >= 1)
 588        {
 589            Instance instance = @ref.getInstance();
 590            var s = new System.Text.StringBuilder();
 591            s.Append("searching for well-known object\nwell-known proxy = ");
 592            s.Append(@ref.ToString());
 593            instance.initializationData().logger.trace(instance.traceLevels().locationCat, s.ToString());
 594        }
 595
 596        lock (_mutex)
 597        {
 598            if (_objectRequests.TryGetValue(@ref.getIdentity(), out Request request))
 599            {
 600                return request;
 601            }
 602
 603            request = new ObjectRequest(this, @ref);
 604            _objectRequests.Add(@ref.getIdentity(), request);
 605            return request;
 606        }
 607    }
 608
 609    private void
 610    finishRequest(Reference @ref, List<Reference> wellKnownRefs, Ice.ObjectPrx proxy, bool notRegistered)
 611    {
 612        var @base = proxy as Ice.ObjectPrxHelperBase;
 613        if (proxy == null || @base.iceReference().isIndirect())
 614        {
 615            //
 616            // Remove the cached references of well-known objects for which we tried
 617            // to resolved the endpoints if these endpoints are empty.
 618            //
 619            foreach (Reference r in wellKnownRefs)
 620            {
 621                _table.removeObjectReference(r.getIdentity());
 622            }
 623        }
 624
 625        if (!@ref.isWellKnown())
 626        {
 627            if (proxy != null && !@base.iceReference().isIndirect())
 628            {
 629                // Cache the adapter endpoints.
 630                _table.addAdapterEndpoints(@ref.getAdapterId(), @base.iceReference().getEndpoints());
 631            }
 632            else if (notRegistered) // If the adapter isn't registered anymore, remove it from the cache.
 633            {
 634                _table.removeAdapterEndpoints(@ref.getAdapterId());
 635            }
 636
 637            lock (_mutex)
 638            {
 639                Debug.Assert(_adapterRequests.ContainsKey(@ref.getAdapterId()));
 640                _adapterRequests.Remove(@ref.getAdapterId());
 641            }
 642        }
 643        else
 644        {
 645            if (proxy != null && !@base.iceReference().isWellKnown())
 646            {
 647                // Cache the well-known object reference.
 648                _table.addObjectReference(@ref.getIdentity(), @base.iceReference());
 649            }
 650            else if (notRegistered) // If the well-known object isn't registered anymore, remove it from the cache.
 651            {
 652                _table.removeObjectReference(@ref.getIdentity());
 653            }
 654
 655            lock (_mutex)
 656            {
 657                Debug.Assert(_objectRequests.ContainsKey(@ref.getIdentity()));
 658                _objectRequests.Remove(@ref.getIdentity());
 659            }
 660        }
 661    }
 662
 663    private readonly Ice.LocatorPrx _locator;
 664    private Ice.LocatorRegistryPrx _locatorRegistry;
 665    private readonly LocatorTable _table;
 666    private readonly bool _background;
 667
 668    private readonly Dictionary<string, Request> _adapterRequests = new Dictionary<string, Request>();
 669    private readonly Dictionary<Ice.Identity, Request> _objectRequests = new Dictionary<Ice.Identity, Request>();
 670    private readonly object _mutex = new();
 671}
 672
 673public sealed class LocatorManager
 674{
 675    private struct LocatorKey
 676    {
 677        public LocatorKey(Ice.LocatorPrx prx)
 678        {
 679            Reference r = ((Ice.ObjectPrxHelperBase)prx).iceReference();
 680            _id = r.getIdentity();
 681            _encoding = r.getEncoding();
 682        }
 683
 684        public override bool Equals(object o)
 685        {
 686            var k = (LocatorKey)o;
 687            if (!k._id.Equals(_id))
 688            {
 689                return false;
 690            }
 691            if (!k._encoding.Equals(_encoding))
 692            {
 693                return false;
 694            }
 695            return true;
 696        }
 697
 698        public override int GetHashCode() => HashCode.Combine(_id, _encoding);
 699
 700        private readonly Ice.Identity _id;
 701        private Ice.EncodingVersion _encoding;
 702    }
 703
 704    internal LocatorManager(Ice.Properties properties)
 705    {
 706        _table = new Dictionary<Ice.LocatorPrx, LocatorInfo>();
 707        _locatorTables = new Dictionary<LocatorKey, LocatorTable>();
 708        _background = properties.getIcePropertyAsInt("Ice.BackgroundLocatorCacheUpdates") > 0;
 709    }
 710
 711    internal void destroy()
 712    {
 713        lock (_mutex)
 714        {
 715            foreach (LocatorInfo info in _table.Values)
 716            {
 717                info.destroy();
 718            }
 719            _table.Clear();
 720            _locatorTables.Clear();
 721        }
 722    }
 723
 724    //
 725    // Returns locator info for a given locator. Automatically creates
 726    // the locator info if it doesn't exist yet.
 727    //
 728    public LocatorInfo get(Ice.LocatorPrx loc)
 729    {
 730        if (loc == null)
 731        {
 732            return null;
 733        }
 734
 735        //
 736        // The locator can't be located.
 737        //
 738        Ice.LocatorPrx locator = Ice.LocatorPrxHelper.uncheckedCast(loc.ice_locator(null));
 739
 740        //
 741        // TODO: reap unused locator info objects?
 742        //
 743        lock (_mutex)
 744        {
 745            if (!_table.TryGetValue(locator, out LocatorInfo info))
 746            {
 747                //
 748                // Rely on locator identity for the adapter table. We want to
 749                // have only one table per locator (not one per locator
 750                // proxy).
 751                //
 752                var key = new LocatorKey(locator);
 753                if (!_locatorTables.TryGetValue(key, out LocatorTable table))
 754                {
 755                    table = new LocatorTable();
 756                    _locatorTables[key] = table;
 757                }
 758
 759                info = new LocatorInfo(locator, table, _background);
 760                _table[locator] = info;
 761            }
 762
 763            return info;
 764        }
 765    }
 766
 767    private readonly Dictionary<Ice.LocatorPrx, LocatorInfo> _table;
 768    private readonly Dictionary<LocatorKey, LocatorTable> _locatorTables;
 769    private readonly bool _background;
 770    private readonly object _mutex = new();
 771}
 772
 773internal sealed class LocatorTable
 774{
 775    internal LocatorTable()
 776    {
 777        _adapterEndpointsTable = new Dictionary<string, EndpointTableEntry>();
 778        _objectTable = new Dictionary<Ice.Identity, ReferenceTableEntry>();
 779    }
 780
 781    internal void clear()
 782    {
 783        lock (_mutex)
 784        {
 785            _adapterEndpointsTable.Clear();
 786            _objectTable.Clear();
 787        }
 788    }
 789
 790    internal EndpointI[] getAdapterEndpoints(string adapter, TimeSpan ttl, out bool cached)
 791    {
 792        if (ttl == TimeSpan.Zero) // Locator cache disabled.
 793        {
 794            cached = false;
 795            return null;
 796        }
 797
 798        lock (_mutex)
 799        {
 800            if (_adapterEndpointsTable.TryGetValue(adapter, out EndpointTableEntry entry))
 801            {
 802                cached = checkTTL(entry.time, ttl);
 803                return entry.endpoints;
 804            }
 805            cached = false;
 806            return null;
 807        }
 808    }
 809
 810    internal void addAdapterEndpoints(string adapter, EndpointI[] endpoints)
 811    {
 812        lock (_mutex)
 813        {
 814            _adapterEndpointsTable[adapter] =
 815                new EndpointTableEntry(Time.currentMonotonicTimeMillis(), endpoints);
 816        }
 817    }
 818
 819    internal EndpointI[] removeAdapterEndpoints(string adapter)
 820    {
 821        lock (_mutex)
 822        {
 823            if (_adapterEndpointsTable.TryGetValue(adapter, out EndpointTableEntry entry))
 824            {
 825                _adapterEndpointsTable.Remove(adapter);
 826                return entry.endpoints;
 827            }
 828            return null;
 829        }
 830    }
 831
 832    internal Reference getObjectReference(Ice.Identity id, TimeSpan ttl, out bool cached)
 833    {
 834        if (ttl == TimeSpan.Zero) // Locator cache disabled.
 835        {
 836            cached = false;
 837            return null;
 838        }
 839
 840        lock (_mutex)
 841        {
 842            if (_objectTable.TryGetValue(id, out ReferenceTableEntry entry))
 843            {
 844                cached = checkTTL(entry.time, ttl);
 845                return entry.reference;
 846            }
 847            cached = false;
 848            return null;
 849        }
 850    }
 851
 852    internal void addObjectReference(Ice.Identity id, Reference reference)
 853    {
 854        lock (_mutex)
 855        {
 856            _objectTable[id] = new ReferenceTableEntry(Time.currentMonotonicTimeMillis(), reference);
 857        }
 858    }
 859
 860    internal Reference removeObjectReference(Ice.Identity id)
 861    {
 862        lock (_mutex)
 863        {
 864            if (_objectTable.TryGetValue(id, out ReferenceTableEntry entry))
 865            {
 866                _objectTable.Remove(id);
 867                return entry.reference;
 868            }
 869            return null;
 870        }
 871    }
 872
 873    private bool checkTTL(long time, TimeSpan ttl)
 874    {
 875        Debug.Assert(ttl != TimeSpan.Zero);
 876        if (ttl < TimeSpan.Zero) // TTL = infinite
 877        {
 878            return true;
 879        }
 880        else
 881        {
 882            return Time.currentMonotonicTimeMillis() - time <= ttl.TotalMilliseconds;
 883        }
 884    }
 885
 886    private sealed class EndpointTableEntry
 887    {
 888        public EndpointTableEntry(long time, EndpointI[] endpoints)
 889        {
 890            this.time = time;
 891            this.endpoints = endpoints;
 892        }
 893
 894        public long time;
 895        public EndpointI[] endpoints;
 896    }
 897
 898    private sealed class ReferenceTableEntry
 899    {
 900        public ReferenceTableEntry(long time, Reference reference)
 901        {
 902            this.time = time;
 903            this.reference = reference;
 904        }
 905
 906        public long time;
 907        public Reference reference;
 908    }
 909
 910    private readonly Dictionary<string, EndpointTableEntry> _adapterEndpointsTable;
 911    private readonly Dictionary<Ice.Identity, ReferenceTableEntry> _objectTable;
 912    private readonly object _mutex = new();
 913}