public <S> S getService(Bundle bundle, ServiceReference<S> ref)
    {
        UsageCount usage = null;
        Object svcObj = null;

        // Get the service registration.
        ServiceRegistrationImpl<S> reg =
            ((ServiceRegistrationImpl.ServiceReferenceImpl) ref).getRegistration();

        synchronized (this)
        {
            // First make sure that no existing operation is currently
            // being performed by another thread on the service registration.
            for (Object o = m_lockedRegsMap.get(reg); (o != null); o = m_lockedRegsMap.get(reg))
            {
                // We don't allow cycles when we call out to the service factory.
                if (o.equals(Thread.currentThread()))
                {
                    throw new ServiceException(
                        "ServiceFactory.getService() resulted in a cycle.",
                        ServiceException.FACTORY_ERROR,
                        null);
                }

                // Otherwise, wait for it to be freed.
                try
                {
                    wait();
                }
                catch (InterruptedException ex)
                {
                }
            }

            // Lock the service registration.
            m_lockedRegsMap.put(reg, Thread.currentThread());

            // Make sure the service registration is still valid.
            if (reg.isValid())
            {
                // Get the usage count, if any.
                usage = getUsageCount(bundle, ref);

                // If we don't have a usage count, then create one and
                // since the spec says we increment usage count before
                // actually getting the service object.
                if (usage == null)
                {
                    usage = addUsageCount(bundle, ref);
                }

                // Increment the usage count and grab the already retrieved
                // service object, if one exists.
                usage.m_count++;
                svcObj = usage.m_svcObj;
            }
        }

        // If we have a usage count, but no service object, then we haven't
        // cached the service object yet, so we need to create one now without
        // holding the lock, since we will potentially call out to a service
        // factory.
        try
        {
            if ((usage != null) && (svcObj == null))
            {
                svcObj = reg.getService(bundle);
            }
        }
        finally
        {
            // If we successfully retrieved a service object, then we should
            // cache it in the usage count. If not, we should flush the usage
            // count. Either way, we need to unlock the service registration
            // so that any threads waiting for it can continue.
            synchronized (this)
            {
                // Before caching the service object, double check to see if
                // the registration is still valid, since it may have been
                // unregistered while we didn't hold the lock.
                if (!reg.isValid() || (svcObj == null))
                {
                    flushUsageCount(bundle, ref);
                }
                else
                {
                    usage.m_svcObj = svcObj;
                }
                m_lockedRegsMap.remove(reg);
                notifyAll();
            }
        }

        return (S) svcObj;
    }