Skip to content

Commit 92abe83

Browse files
committed
Fixes memory leaks in the eventhandler
These probably only happen when the VM is going down after an unhandled exception, but I want to fix them anyway. [email protected] Review URL: https://codereview.chromium.org/2228503007 .
1 parent 9ce171a commit 92abe83

File tree

8 files changed

+113
-43
lines changed

8 files changed

+113
-43
lines changed

runtime/bin/eventhandler.h

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,8 @@ class CircularLinkedList {
132132
public:
133133
CircularLinkedList() : head_(NULL) {}
134134

135+
typedef void (*ClearFun) (void* value);
136+
135137
// Returns true if the list was empty.
136138
bool Add(T t) {
137139
Entry* e = new Entry(t);
@@ -151,7 +153,7 @@ class CircularLinkedList {
151153
}
152154
}
153155

154-
void RemoveHead() {
156+
void RemoveHead(ClearFun clear = NULL) {
155157
ASSERT(head_ != NULL);
156158

157159
Entry* e = head_;
@@ -162,6 +164,9 @@ class CircularLinkedList {
162164
e->next_->prev_ = e->prev_;
163165
head_ = e->next_;
164166
}
167+
if (clear != NULL) {
168+
clear(reinterpret_cast<void*>(e->t));
169+
}
165170
delete e;
166171
}
167172

@@ -195,9 +200,9 @@ class CircularLinkedList {
195200
}
196201
}
197202

198-
void RemoveAll() {
203+
void RemoveAll(ClearFun clear = NULL) {
199204
while (HasHead()) {
200-
RemoveHead();
205+
RemoveHead(clear);
201206
}
202207
}
203208

@@ -413,7 +418,9 @@ class DescriptorInfoMultipleMixin : public DI {
413418
: DI(fd), tokens_map_(&SamePortValue, kTokenCount),
414419
disable_tokens_(disable_tokens) {}
415420

416-
virtual ~DescriptorInfoMultipleMixin() {}
421+
virtual ~DescriptorInfoMultipleMixin() {
422+
RemoveAllPorts();
423+
}
417424

418425
virtual bool IsListeningSocket() const { return true; }
419426

@@ -497,14 +504,16 @@ class DescriptorInfoMultipleMixin : public DI {
497504
}
498505

499506
virtual void RemoveAllPorts() {
500-
active_readers_.RemoveAll();
501507
for (HashMap::Entry *entry = tokens_map_.Start();
502508
entry != NULL;
503509
entry = tokens_map_.Next(entry)) {
504510
PortEntry* pentry = reinterpret_cast<PortEntry*>(entry->value);
511+
entry->value = NULL;
512+
active_readers_.Remove(pentry);
505513
delete pentry;
506514
}
507515
tokens_map_.Clear();
516+
active_readers_.RemoveAll(DeletePortEntry);
508517
}
509518

510519
virtual Dart_Port NextNotifyDartPort(intptr_t events_ready) {
@@ -585,6 +594,11 @@ class DescriptorInfoMultipleMixin : public DI {
585594
}
586595

587596
private:
597+
static void DeletePortEntry(void* data) {
598+
PortEntry* entry = reinterpret_cast<PortEntry*>(data);
599+
delete entry;
600+
}
601+
588602
// The [Dart_Port]s which are not paused (i.e. are interested in read events,
589603
// i.e. `mask == (1 << kInEvent)`) and we have enough tokens to communicate
590604
// with them.

runtime/bin/eventhandler_android.cc

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,15 @@ EventHandlerImplementation::EventHandlerImplementation()
118118
}
119119

120120

121+
static void DeleteDescriptorInfo(void* info) {
122+
DescriptorInfo* di = reinterpret_cast<DescriptorInfo*>(info);
123+
di->Close();
124+
delete di;
125+
}
126+
127+
121128
EventHandlerImplementation::~EventHandlerImplementation() {
129+
socket_map_.Clear(DeleteDescriptorInfo);
122130
VOID_TEMP_FAILURE_RETRY(close(epoll_fd_));
123131
VOID_TEMP_FAILURE_RETRY(close(interrupt_fds_[0]));
124132
VOID_TEMP_FAILURE_RETRY(close(interrupt_fds_[1]));

runtime/bin/eventhandler_linux.cc

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,15 @@ EventHandlerImplementation::EventHandlerImplementation()
127127
}
128128

129129

130+
static void DeleteDescriptorInfo(void* info) {
131+
DescriptorInfo* di = reinterpret_cast<DescriptorInfo*>(info);
132+
di->Close();
133+
delete di;
134+
}
135+
136+
130137
EventHandlerImplementation::~EventHandlerImplementation() {
138+
socket_map_.Clear(DeleteDescriptorInfo);
131139
VOID_TEMP_FAILURE_RETRY(close(epoll_fd_));
132140
VOID_TEMP_FAILURE_RETRY(close(timer_fd_));
133141
VOID_TEMP_FAILURE_RETRY(close(interrupt_fds_[0]));

runtime/bin/eventhandler_macos.cc

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,15 @@ EventHandlerImplementation::EventHandlerImplementation()
140140
}
141141

142142

143+
static void DeleteDescriptorInfo(void* info) {
144+
DescriptorInfo* di = reinterpret_cast<DescriptorInfo*>(info);
145+
di->Close();
146+
delete di;
147+
}
148+
149+
143150
EventHandlerImplementation::~EventHandlerImplementation() {
151+
socket_map_.Clear(DeleteDescriptorInfo);
144152
VOID_TEMP_FAILURE_RETRY(close(kqueue_fd_));
145153
VOID_TEMP_FAILURE_RETRY(close(interrupt_fds_[0]));
146154
VOID_TEMP_FAILURE_RETRY(close(interrupt_fds_[1]));

runtime/bin/socket.cc

Lines changed: 46 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -127,42 +127,57 @@ Dart_Handle ListeningSocketRegistry::CreateBindListen(Dart_Handle socket_object,
127127
}
128128

129129

130-
bool ListeningSocketRegistry::CloseSafe(intptr_t socketfd) {
130+
bool ListeningSocketRegistry::CloseOneSafe(OSSocket* os_socket) {
131131
ASSERT(!mutex_->TryLock());
132+
ASSERT(os_socket != NULL);
133+
ASSERT(os_socket->ref_count > 0);
134+
os_socket->ref_count--;
135+
if (os_socket->ref_count > 0) {
136+
return false;
137+
}
138+
// We free the OS socket by removing it from two datastructures.
139+
sockets_by_fd_.erase(os_socket->socketfd);
132140

133-
SocketsIterator it = sockets_by_fd_.find(socketfd);
134-
if (it != sockets_by_fd_.end()) {
135-
OSSocket *os_socket = it->second;
141+
OSSocket *prev = NULL;
142+
OSSocket *current = sockets_by_port_[os_socket->port];
143+
while (current != os_socket) {
144+
ASSERT(current != NULL);
145+
prev = current;
146+
current = current->next;
147+
}
136148

137-
ASSERT(os_socket->ref_count > 0);
138-
os_socket->ref_count--;
139-
if (os_socket->ref_count == 0) {
140-
// We free the OS socket by removing it from two datastructures.
141-
sockets_by_fd_.erase(socketfd);
142-
143-
OSSocket *prev = NULL;
144-
OSSocket *current = sockets_by_port_[os_socket->port];
145-
while (current != os_socket) {
146-
ASSERT(current != NULL);
147-
prev = current;
148-
current = current->next;
149-
}
149+
if ((prev == NULL) && (current->next == NULL)) {
150+
// Remove last element from the list.
151+
sockets_by_port_.erase(os_socket->port);
152+
} else if (prev == NULL) {
153+
// Remove first element of the list.
154+
sockets_by_port_[os_socket->port] = current->next;
155+
} else {
156+
// Remove element from the list which is not the first one.
157+
prev->next = os_socket->next;
158+
}
150159

151-
if ((prev == NULL) && (current->next == NULL)) {
152-
// Remove last element from the list.
153-
sockets_by_port_.erase(os_socket->port);
154-
} else if (prev == NULL) {
155-
// Remove first element of the list.
156-
sockets_by_port_[os_socket->port] = current->next;
157-
} else {
158-
// Remove element from the list which is not the first one.
159-
prev->next = os_socket->next;
160-
}
160+
ASSERT(os_socket->ref_count == 0);
161+
delete os_socket;
162+
return true;
163+
}
161164

162-
delete os_socket;
163-
return true;
164-
}
165-
return false;
165+
166+
void ListeningSocketRegistry::CloseAllSafe() {
167+
MutexLocker ml(mutex_);
168+
SocketsIterator it = sockets_by_fd_.begin();
169+
while (it != sockets_by_fd_.end()) {
170+
CloseOneSafe(it->second);
171+
it++;
172+
}
173+
}
174+
175+
176+
bool ListeningSocketRegistry::CloseSafe(intptr_t socketfd) {
177+
ASSERT(!mutex_->TryLock());
178+
SocketsIterator it = sockets_by_fd_.find(socketfd);
179+
if (it != sockets_by_fd_.end()) {
180+
return CloseOneSafe(it->second);
166181
} else {
167182
// It should be impossible for the event handler to close something that
168183
// hasn't been created before.

runtime/bin/socket.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -402,6 +402,7 @@ class ListeningSocketRegistry {
402402
ListeningSocketRegistry() : mutex_(new Mutex()) {}
403403

404404
~ListeningSocketRegistry() {
405+
CloseAllSafe();
405406
delete mutex_;
406407
mutex_ = NULL;
407408
}
@@ -437,6 +438,10 @@ class ListeningSocketRegistry {
437438
return NULL;
438439
}
439440

441+
bool CloseOneSafe(OSSocket* os_socket);
442+
void CloseAllSafe();
443+
444+
// TODO(zra): Replace std::map with the HashMap in platform/hashmap.h.
440445
std::map<intptr_t, OSSocket*> sockets_by_port_;
441446
std::map<intptr_t, OSSocket*> sockets_by_fd_;
442447
Mutex *mutex_;

runtime/platform/hashmap.cc

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ HashMap::HashMap(MatchFun match, uint32_t initial_capacity) {
1515

1616

1717
HashMap::~HashMap() {
18-
free(map_);
18+
delete[] map_;
1919
}
2020

2121

@@ -106,14 +106,19 @@ void HashMap::Remove(void* key, uint32_t hash) {
106106

107107
// Clear the candidate which will not break searching the hash table.
108108
candidate->key = NULL;
109+
candidate->value = NULL;
109110
occupancy_--;
110111
}
111112

112113

113-
void HashMap::Clear() {
114+
void HashMap::Clear(ClearFun clear) {
114115
// Mark all entries as empty.
115116
const Entry* end = map_end();
116117
for (Entry* p = map_; p < end; p++) {
118+
if ((clear != NULL) && (p->key != NULL)) {
119+
clear(p->value);
120+
}
121+
p->value = NULL;
117122
p->key = NULL;
118123
}
119124
occupancy_ = 0;
@@ -159,14 +164,14 @@ HashMap::Entry* HashMap::Probe(void* key, uint32_t hash) {
159164

160165
void HashMap::Initialize(uint32_t capacity) {
161166
ASSERT(dart::Utils::IsPowerOfTwo(capacity));
162-
map_ = reinterpret_cast<Entry*>(malloc(capacity * sizeof(Entry)));
167+
map_ = new Entry[capacity];
163168
if (map_ == NULL) {
164169
// TODO(sgjesse): Handle out of memory.
165170
FATAL("Cannot allocate memory for hashmap");
166171
return;
167172
}
168173
capacity_ = capacity;
169-
Clear();
174+
occupancy_ = 0;
170175
}
171176

172177

@@ -186,7 +191,7 @@ void HashMap::Resize() {
186191
}
187192

188193
// Delete old map.
189-
free(map);
194+
delete[] map;
190195
}
191196

192197
} // namespace dart

runtime/platform/hashmap.h

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ class HashMap {
1313
public:
1414
typedef bool (*MatchFun) (void* key1, void* key2);
1515

16+
typedef void (*ClearFun) (void* value);
17+
1618
static bool SamePointerValue(void* key1, void* key2) {
1719
return key1 == key2;
1820
}
@@ -48,6 +50,7 @@ class HashMap {
4850
// Some clients may not need to use the value slot
4951
// (e.g. implementers of sets, where the key is the value).
5052
struct Entry {
53+
Entry() : key(NULL), value(NULL), hash(0) {}
5154
void* key;
5255
void* value;
5356
uint32_t hash; // The full hash value for key.
@@ -63,8 +66,12 @@ class HashMap {
6366
// Removes the entry with matching key.
6467
void Remove(void* key, uint32_t hash);
6568

66-
// Empties the hash map (occupancy() == 0).
67-
void Clear();
69+
// Empties the hash map (occupancy() == 0), and calls the function 'clear' on
70+
// each of the values if given.
71+
void Clear(ClearFun clear = NULL);
72+
73+
// The number of entries stored in the table.
74+
intptr_t size() const { return occupancy_; }
6875

6976
// The capacity of the table. The implementation
7077
// makes sure that occupancy is at most 80% of

0 commit comments

Comments
 (0)