2 * Copyright 2008-2010, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Distributed under the terms of the MIT License.
7 #include "EntryCache.h"
12 static const int32 kEntriesPerGeneration
= 1024;
14 static const int32 kEntryNotInArray
= -1;
15 static const int32 kEntryRemoved
= -2;
18 // #pragma mark - EntryCacheGeneration
21 EntryCacheGeneration::EntryCacheGeneration()
29 EntryCacheGeneration::~EntryCacheGeneration()
36 EntryCacheGeneration::Init()
38 entries
= new(std::nothrow
) EntryCacheEntry
*[kEntriesPerGeneration
];
42 memset(entries
, 0, sizeof(EntryCacheEntry
*) * kEntriesPerGeneration
);
48 // #pragma mark - EntryCache
51 EntryCache::EntryCache()
55 rw_lock_init(&fLock
, "entry cache");
57 new(&fEntries
) EntryTable
;
61 EntryCache::~EntryCache()
64 EntryCacheEntry
* entry
= fEntries
.Clear(true);
65 while (entry
!= NULL
) {
66 EntryCacheEntry
* next
= entry
->hash_link
;
71 rw_lock_destroy(&fLock
);
78 status_t error
= fEntries
.Init();
82 for (int32 i
= 0; i
< kGenerationCount
; i
++) {
83 error
= fGenerations
[i
].Init();
93 EntryCache::Add(ino_t dirID
, const char* name
, ino_t nodeID
, bool missing
)
95 EntryCacheKey
key(dirID
, name
);
99 EntryCacheEntry
* entry
= fEntries
.Lookup(key
);
101 entry
->node_id
= nodeID
;
102 entry
->missing
= missing
;
103 if (entry
->generation
!= fCurrentGeneration
) {
104 if (entry
->index
>= 0) {
105 fGenerations
[entry
->generation
].entries
[entry
->index
] = NULL
;
106 _AddEntryToCurrentGeneration(entry
);
112 entry
= (EntryCacheEntry
*)malloc(sizeof(EntryCacheEntry
) + strlen(name
));
116 entry
->node_id
= nodeID
;
117 entry
->dir_id
= dirID
;
118 entry
->missing
= missing
;
119 entry
->generation
= fCurrentGeneration
;
120 entry
->index
= kEntryNotInArray
;
121 strcpy(entry
->name
, name
);
123 fEntries
.Insert(entry
);
125 _AddEntryToCurrentGeneration(entry
);
132 EntryCache::Remove(ino_t dirID
, const char* name
)
134 EntryCacheKey
key(dirID
, name
);
136 WriteLocker
writeLocker(fLock
);
138 EntryCacheEntry
* entry
= fEntries
.Lookup(key
);
140 return B_ENTRY_NOT_FOUND
;
142 fEntries
.Remove(entry
);
144 if (entry
->index
>= 0) {
145 // remove the entry from its generation and delete it
146 fGenerations
[entry
->generation
].entries
[entry
->index
] = NULL
;
149 // We can't free it, since another thread is about to try to move it
150 // to another generation. We mark it removed and the other thread will
151 // take care of deleting it.
152 entry
->index
= kEntryRemoved
;
160 EntryCache::Lookup(ino_t dirID
, const char* name
, ino_t
& _nodeID
,
163 EntryCacheKey
key(dirID
, name
);
165 ReadLocker
readLocker(fLock
);
167 EntryCacheEntry
* entry
= fEntries
.Lookup(key
);
171 int32 oldGeneration
= atomic_get_and_set(&entry
->generation
,
173 if (oldGeneration
== fCurrentGeneration
|| entry
->index
< 0) {
174 // The entry is already in the current generation or is being moved to
175 // it by another thread.
176 _nodeID
= entry
->node_id
;
177 _missing
= entry
->missing
;
181 // remove from old generation array
182 fGenerations
[oldGeneration
].entries
[entry
->index
] = NULL
;
183 entry
->index
= kEntryNotInArray
;
185 // add to the current generation
186 int32 index
= atomic_add(&fGenerations
[oldGeneration
].next_index
, 1);
187 if (index
< kEntriesPerGeneration
) {
188 fGenerations
[fCurrentGeneration
].entries
[index
] = entry
;
189 entry
->index
= index
;
190 _nodeID
= entry
->node_id
;
191 _missing
= entry
->missing
;
195 // The current generation is full, so we probably need to clear the oldest
196 // one to make room. We need the write lock for that.
198 WriteLocker
writeLocker(fLock
);
200 if (entry
->index
== kEntryRemoved
) {
201 // the entry has been removed in the meantime
206 _AddEntryToCurrentGeneration(entry
);
208 _nodeID
= entry
->node_id
;
209 _missing
= entry
->missing
;
215 EntryCache::DebugReverseLookup(ino_t nodeID
, ino_t
& _dirID
)
217 for (EntryTable::Iterator it
= fEntries
.GetIterator();
218 EntryCacheEntry
* entry
= it
.Next();) {
219 if (nodeID
== entry
->node_id
&& strcmp(entry
->name
, ".") != 0
220 && strcmp(entry
->name
, "..") != 0) {
221 _dirID
= entry
->dir_id
;
231 EntryCache::_AddEntryToCurrentGeneration(EntryCacheEntry
* entry
)
233 // the generation might not be full yet
234 int32 index
= fGenerations
[fCurrentGeneration
].next_index
++;
235 if (index
< kEntriesPerGeneration
) {
236 fGenerations
[fCurrentGeneration
].entries
[index
] = entry
;
237 entry
->generation
= fCurrentGeneration
;
238 entry
->index
= index
;
242 // we have to clear the oldest generation
243 int32 newGeneration
= (fCurrentGeneration
+ 1) % kGenerationCount
;
244 for (int32 i
= 0; i
< kEntriesPerGeneration
; i
++) {
245 EntryCacheEntry
* otherEntry
= fGenerations
[newGeneration
].entries
[i
];
246 if (otherEntry
== NULL
)
249 fGenerations
[newGeneration
].entries
[i
] = NULL
;
250 fEntries
.Remove(otherEntry
);
254 // set the new generation and add the entry
255 fCurrentGeneration
= newGeneration
;
256 fGenerations
[newGeneration
].next_index
= 1;
257 fGenerations
[newGeneration
].entries
[0] = entry
;
258 entry
->generation
= newGeneration
;