BlockRef.cpp   [plain text]


/*
 * Copyright (c) 2011 Apple Inc. All rights reserved.
 *
 * @APPLE_APACHE_LICENSE_HEADER_START@
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *     http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * 
 * @APPLE_APACHE_LICENSE_HEADER_END@
 */
/*
    BlockRef.cpp
    Block sieve helper classes.
    Copyright (c) 2010-2011 Apple Inc. All rights reserved.
 */

#include "BlockRef.h"

namespace Auto {
    
    // SubzoneBlockRef

    usword_t SubzoneBlockRef::refcount() const {
        int refcount = 0;
        Admin *admin = subzone()->admin();
        SpinLock lock(admin->lock());
        if (has_refcount()) {
            // non-zero reference count, check the overflow table.
            // BlockRef FIXME: use q instead of address in hash map?
            PtrIntHashMap &retains = admin->retains();
            PtrIntHashMap::iterator retain_iter = retains.find(address());
            if (retain_iter != retains.end() && retain_iter->first == address()) {
                refcount = retain_iter->second;
            } else {
                refcount = 1;
            }
        }
        return refcount;
    }
    
    usword_t SubzoneBlockRef::inc_refcount() const { 
        int refcount;
        Admin *admin = subzone()->admin();
        SpinLock lock(admin->lock());
        void *block = address();
        if (has_refcount()) {
            // non-trivial reference count, check the overflow table.
            PtrIntHashMap &retains = admin->retains();
            PtrIntHashMap::iterator retain_iter = retains.find(block);
            if (retain_iter != retains.end() && retain_iter->first == block) {
                refcount = ++retain_iter->second;
            } else {
                // transition from 1 -> 2
                refcount = (retains[block] = 2);
            }
        } else {
            // transition from 0 -> 1
            Thread &thread = admin->zone()->registered_thread();
            thread.block_escaped(*this);
            subzone()->set_has_refcount(q());
            refcount = 1;
        }
        return refcount;
    }
    
    usword_t SubzoneBlockRef::dec_refcount_no_lock() const {
        Admin *admin = subzone()->admin();
        if (has_refcount()) {
            // non-zero reference count, check the overflow table.
            PtrIntHashMap &retains = admin->retains();
            PtrIntHashMap::iterator retain_iter = retains.find(address());
            if (retain_iter != retains.end() && retain_iter->first == address()) {
                if (--retain_iter->second == 1) {
                    // transition from 2 -> 1
                    retains.erase(retain_iter);
                    return 1;
                } else {
                    return retain_iter->second;
                }
            } else {
                // transition from 1 -> 0
                subzone()->clear_has_refcount(q());
                return 0;
            }
        }
        // underflow.
        malloc_printf("reference count underflow for %p, break on auto_refcount_underflow_error to debug.\n", address());
        auto_refcount_underflow_error(address());
        return -1;
    }
    
    usword_t SubzoneBlockRef::dec_refcount() const {
        Admin *admin = subzone()->admin();
        SpinLock lock(admin->lock());
        return dec_refcount_no_lock();
    }
    
    
    
    // LargeBlockRef
    
    usword_t LargeBlockRef::inc_refcount() const { 
        SpinLock lock(zone()->large_lock());
        usword_t refcount = _large->refcount() + 1;
        _large->set_refcount(refcount);
        return refcount;
    }
    
    usword_t LargeBlockRef::dec_refcount_no_lock() const {
        usword_t rc = refcount();
        if (rc <= 0) {
            malloc_printf("reference count underflow for %p, break on auto_refcount_underflow_error to debug\n", address());
            auto_refcount_underflow_error(address());
        } else {
            rc = rc - 1;
            _large->set_refcount(rc);
        }
        return rc;
    }            
    
    usword_t LargeBlockRef::dec_refcount() const {
        SpinLock lock(zone()->large_lock());
        return dec_refcount_no_lock();
    }            
    
}