View Javadoc
1   /*
2    * Copyright 2012 The Netty Project
3    *
4    * The Netty Project licenses this file to you under the Apache License,
5    * version 2.0 (the "License"); you may not use this file except in compliance
6    * with the License. You may obtain a copy of the License at:
7    *
8    *   http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13   * License for the specific language governing permissions and limitations
14   * under the License.
15   */
16  package io.netty.util;
17  
18  import java.util.concurrent.atomic.AtomicReference;
19  import java.util.concurrent.atomic.AtomicReferenceArray;
20  import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
21  
22  /**
23   * Default {@link AttributeMap} implementation which use simple synchronization per bucket to keep the memory overhead
24   * as low as possible.
25   */
26  public class DefaultAttributeMap implements AttributeMap {
27  
28      @SuppressWarnings("rawtypes")
29      private static final AtomicReferenceFieldUpdater<DefaultAttributeMap, AtomicReferenceArray> updater =
30              AtomicReferenceFieldUpdater.newUpdater(DefaultAttributeMap.class, AtomicReferenceArray.class, "attributes");
31  
32      private static final int BUCKET_SIZE = 4;
33      private static final int MASK = BUCKET_SIZE  - 1;
34  
35      // Initialize lazily to reduce memory consumption; updated by AtomicReferenceFieldUpdater above.
36      @SuppressWarnings("UnusedDeclaration")
37      private volatile AtomicReferenceArray<DefaultAttribute<?>> attributes;
38  
39      @SuppressWarnings("unchecked")
40      @Override
41      public <T> Attribute<T> attr(AttributeKey<T> key) {
42          if (key == null) {
43              throw new NullPointerException("key");
44          }
45          AtomicReferenceArray<DefaultAttribute<?>> attributes = this.attributes;
46          if (attributes == null) {
47              // Not using ConcurrentHashMap due to high memory consumption.
48              attributes = new AtomicReferenceArray<DefaultAttribute<?>>(BUCKET_SIZE);
49  
50              if (!updater.compareAndSet(this, null, attributes)) {
51                  attributes = this.attributes;
52              }
53          }
54  
55          int i = index(key);
56          DefaultAttribute<?> head = attributes.get(i);
57          if (head == null) {
58              // No head exists yet which means we may be able to add the attribute without synchronization and just
59              // use compare and set. At worst we need to fallback to synchronization and waste two allocations.
60              head = new DefaultAttribute();
61              DefaultAttribute<T> attr = new DefaultAttribute<T>(head, key);
62              head.next = attr;
63              attr.prev = head;
64              if (attributes.compareAndSet(i, null, head)) {
65                  // we were able to add it so return the attr right away
66                  return attr;
67              } else {
68                  head = attributes.get(i);
69              }
70          }
71  
72          synchronized (head) {
73              DefaultAttribute<?> curr = head;
74              for (;;) {
75                  DefaultAttribute<?> next = curr.next;
76                  if (next == null) {
77                      DefaultAttribute<T> attr = new DefaultAttribute<T>(head, key);
78                      curr.next = attr;
79                      attr.prev = curr;
80                      return attr;
81                  }
82  
83                  if (next.key == key && !next.removed) {
84                      return (Attribute<T>) next;
85                  }
86                  curr = next;
87              }
88          }
89      }
90  
91      private static int index(AttributeKey<?> key) {
92          return key.id() & MASK;
93      }
94  
95      @SuppressWarnings("serial")
96      private static final class DefaultAttribute<T> extends AtomicReference<T> implements Attribute<T> {
97  
98          private static final long serialVersionUID = -2661411462200283011L;
99  
100         // The head of the linked-list this attribute belongs to
101         private final DefaultAttribute<?> head;
102         private final AttributeKey<T> key;
103 
104         // Double-linked list to prev and next node to allow fast removal
105         private DefaultAttribute<?> prev;
106         private DefaultAttribute<?> next;
107 
108         // Will be set to true one the attribute is removed via getAndRemove() or remove()
109         private volatile boolean removed;
110 
111         DefaultAttribute(DefaultAttribute<?> head, AttributeKey<T> key) {
112             this.head = head;
113             this.key = key;
114         }
115 
116         // Special constructor for the head of the linked-list.
117         DefaultAttribute() {
118             head = this;
119             key = null;
120         }
121 
122         @Override
123         public AttributeKey<T> key() {
124             return key;
125         }
126 
127         @Override
128         public T setIfAbsent(T value) {
129             while (!compareAndSet(null, value)) {
130                 T old = get();
131                 if (old != null) {
132                     return old;
133                 }
134             }
135             return null;
136         }
137 
138         @Override
139         public T getAndRemove() {
140             removed = true;
141             T oldValue = getAndSet(null);
142             remove0();
143             return oldValue;
144         }
145 
146         @Override
147         public void remove() {
148             removed = true;
149             set(null);
150             remove0();
151         }
152 
153         private void remove0() {
154             synchronized (head) {
155                 if (prev == null) {
156                     // Removed before.
157                     return;
158                 }
159 
160                 prev.next = next;
161 
162                 if (next != null) {
163                     next.prev = prev;
164                 }
165 
166                 // Null out prev and next - this will guard against multiple remove0() calls which may corrupt
167                 // the linked list for the bucket.
168                 prev = null;
169                 next = null;
170             }
171         }
172     }
173 }