1/*
2 * Copyright (c) 2011 Apple Inc. All rights reserved.
3 *
4 * @APPLE_APACHE_LICENSE_HEADER_START@
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 *     http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 *
18 * @APPLE_APACHE_LICENSE_HEADER_END@
19 */
20/*
21    BlockRef.cpp
22    Block sieve helper classes.
23    Copyright (c) 2010-2011 Apple Inc. All rights reserved.
24 */
25
26#include "BlockRef.h"
27
28namespace Auto {
29
30    // SubzoneBlockRef
31
32    usword_t SubzoneBlockRef::refcount() const {
33        int refcount = 0;
34        Admin *admin = subzone()->admin();
35        SpinLock lock(admin->lock());
36        if (has_refcount()) {
37            // non-zero reference count, check the overflow table.
38            // BlockRef FIXME: use q instead of address in hash map?
39            PtrIntHashMap &retains = admin->retains();
40            PtrIntHashMap::iterator retain_iter = retains.find(address());
41            if (retain_iter != retains.end() && retain_iter->first == address()) {
42                refcount = retain_iter->second;
43            } else {
44                refcount = 1;
45            }
46        }
47        return refcount;
48    }
49
50    usword_t SubzoneBlockRef::inc_refcount() const {
51        int refcount;
52        Admin *admin = subzone()->admin();
53        SpinLock lock(admin->lock());
54        void *block = address();
55        if (has_refcount()) {
56            // non-trivial reference count, check the overflow table.
57            PtrIntHashMap &retains = admin->retains();
58            PtrIntHashMap::iterator retain_iter = retains.find(block);
59            if (retain_iter != retains.end() && retain_iter->first == block) {
60                refcount = ++retain_iter->second;
61            } else {
62                // transition from 1 -> 2
63                refcount = (retains[block] = 2);
64            }
65        } else {
66            // transition from 0 -> 1
67            Thread &thread = admin->zone()->registered_thread();
68            thread.block_escaped(*this);
69            subzone()->set_has_refcount(q());
70            refcount = 1;
71        }
72        return refcount;
73    }
74
75    usword_t SubzoneBlockRef::dec_refcount_no_lock() const {
76        Admin *admin = subzone()->admin();
77        if (has_refcount()) {
78            // non-zero reference count, check the overflow table.
79            PtrIntHashMap &retains = admin->retains();
80            PtrIntHashMap::iterator retain_iter = retains.find(address());
81            if (retain_iter != retains.end() && retain_iter->first == address()) {
82                if (--retain_iter->second == 1) {
83                    // transition from 2 -> 1
84                    retains.erase(retain_iter);
85                    return 1;
86                } else {
87                    return retain_iter->second;
88                }
89            } else {
90                // transition from 1 -> 0
91                subzone()->clear_has_refcount(q());
92                return 0;
93            }
94        }
95        // underflow.
96        malloc_printf("reference count underflow for %p, break on auto_refcount_underflow_error to debug.\n", address());
97        auto_refcount_underflow_error(address());
98        return -1;
99    }
100
101    usword_t SubzoneBlockRef::dec_refcount() const {
102        Admin *admin = subzone()->admin();
103        SpinLock lock(admin->lock());
104        return dec_refcount_no_lock();
105    }
106
107
108
109    // LargeBlockRef
110
111    usword_t LargeBlockRef::inc_refcount() const {
112        SpinLock lock(zone()->large_lock());
113        usword_t refcount = _large->refcount() + 1;
114        _large->set_refcount(refcount);
115        return refcount;
116    }
117
118    usword_t LargeBlockRef::dec_refcount_no_lock() const {
119        usword_t rc = refcount();
120        if (rc <= 0) {
121            malloc_printf("reference count underflow for %p, break on auto_refcount_underflow_error to debug\n", address());
122            auto_refcount_underflow_error(address());
123        } else {
124            rc = rc - 1;
125            _large->set_refcount(rc);
126        }
127        return rc;
128    }
129
130    usword_t LargeBlockRef::dec_refcount() const {
131        SpinLock lock(zone()->large_lock());
132        return dec_refcount_no_lock();
133    }
134
135}
136