Skip to content

Commit 0e95e5a

Browse files
authored
[SYCL][L0] Fix EventPool memory leak (#4080)
We need to destroy event pools at the tear-down time regardless of its any live events. Signed-off-by: Byoungro So <[email protected]>
1 parent 84ee39a commit 0e95e5a

File tree

2 files changed

+38
-25
lines changed

2 files changed

+38
-25
lines changed

sycl/plugins/level_zero/pi_level_zero.cpp

Lines changed: 29 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -335,15 +335,16 @@ _pi_context::getFreeSlotInExistingOrNewPool(ze_event_pool_handle_t &ZePool,
335335
if ((ZeEventPool == nullptr) ||
336336
(NumEventsAvailableInEventPool[ZeEventPool] == 0)) {
337337
// Creation of the new ZePool with record in NumEventsAvailableInEventPool
338-
// and initialization of the record in NumEventsLiveInEventPool must be done
339-
// atomically. Otherwise it is possible that decrementAliveEventsInPool will
340-
// be called for the record in NumEventsLiveInEventPool before its
338+
// and initialization of the record in NumEventsUnreleasedInEventPool must
339+
// be done atomically. Otherwise it is possible that
340+
// decrementUnreleasedEventsInPool will be called for the record in
341+
// NumEventsUnreleasedInEventPool before its
341342
std::lock(NumEventsAvailableInEventPoolMutex,
342-
NumEventsLiveInEventPoolMutex);
343+
NumEventsUnreleasedInEventPoolMutex);
343344
std::lock_guard<std::mutex> NumEventsAvailableInEventPoolGuard(
344345
NumEventsAvailableInEventPoolMutex, std::adopt_lock);
345-
std::lock_guard<std::mutex> NumEventsLiveInEventPoolGuard(
346-
NumEventsLiveInEventPoolMutex, std::adopt_lock);
346+
std::lock_guard<std::mutex> NumEventsUnreleasedInEventPoolGuard(
347+
NumEventsUnreleasedInEventPoolMutex, std::adopt_lock);
347348

348349
ZeStruct<ze_event_pool_desc_t> ZeEventPoolDesc;
349350
ZeEventPoolDesc.count = MaxNumEventsPerPool;
@@ -362,7 +363,7 @@ _pi_context::getFreeSlotInExistingOrNewPool(ze_event_pool_handle_t &ZePool,
362363
ZE_CALL(zeEventPoolCreate, (ZeContext, &ZeEventPoolDesc, ZeDevices.size(),
363364
&ZeDevices[0], &ZeEventPool));
364365
NumEventsAvailableInEventPool[ZeEventPool] = MaxNumEventsPerPool - 1;
365-
NumEventsLiveInEventPool[ZeEventPool] = MaxNumEventsPerPool;
366+
NumEventsUnreleasedInEventPool[ZeEventPool] = MaxNumEventsPerPool;
366367
} else {
367368
std::lock_guard<std::mutex> NumEventsAvailableInEventPoolGuard(
368369
NumEventsAvailableInEventPoolMutex);
@@ -373,12 +374,24 @@ _pi_context::getFreeSlotInExistingOrNewPool(ze_event_pool_handle_t &ZePool,
373374
return PI_SUCCESS;
374375
}
375376

376-
pi_result
377-
_pi_context::decrementAliveEventsInPool(ze_event_pool_handle_t ZePool) {
378-
std::lock_guard<std::mutex> Lock(NumEventsLiveInEventPoolMutex);
379-
--NumEventsLiveInEventPool[ZePool];
380-
if (NumEventsLiveInEventPool[ZePool] == 0) {
377+
pi_result _pi_context::decrementUnreleasedEventsInPool(pi_event Event) {
378+
ze_event_pool_handle_t ZePool = Event->ZeEventPool;
379+
std::lock_guard<std::mutex> Lock(NumEventsUnreleasedInEventPoolMutex);
380+
--NumEventsUnreleasedInEventPool[ZePool];
381+
if (NumEventsUnreleasedInEventPool[ZePool] == 0) {
381382
ZE_CALL(zeEventPoolDestroy, (ZePool));
383+
// Nullify ZeEventPool pointer to indicate this pool is already destroyed
384+
// because we will call ZeEventPoolDestroy() if ZeEventPool is not null
385+
// in pi_context::finalize().
386+
// Note that calling ZeEventPoolDestroy() for the already destroyed pool
387+
// will cause a segmentation fault in L0.
388+
// We need to check the equality below because it is possible that
389+
// multiple pi_context::ZeEventPool can be created if all slots in the pool
390+
// are already used up. So nullifying pi_context::ZeEventPool may point
391+
// a different EventPool than Event->ZeEventPool.
392+
if (ZeEventPool == Event->ZeEventPool)
393+
ZeEventPool = nullptr;
394+
Event->ZeEventPool = nullptr;
382395
}
383396
return PI_SUCCESS;
384397
}
@@ -597,9 +610,9 @@ pi_result _pi_context::finalize() {
597610
// This function is called when pi_context is deallocated, piContextRelase.
598611
// There could be some memory that may have not been deallocated.
599612
// For example, zeEventPool could be still alive.
600-
std::lock_guard<std::mutex> NumEventsLiveInEventPoolGuard(
601-
NumEventsLiveInEventPoolMutex);
602-
if (ZeEventPool && NumEventsLiveInEventPool[ZeEventPool])
613+
std::lock_guard<std::mutex> NumEventsUnreleasedInEventPoolGuard(
614+
NumEventsUnreleasedInEventPoolMutex);
615+
if (ZeEventPool)
603616
ZE_CALL(zeEventPoolDestroy, (ZeEventPool));
604617

605618
// Destroy the command list used for initializations
@@ -4419,7 +4432,7 @@ pi_result piEventRelease(pi_event Event) {
44194432
ZE_CALL(zeEventDestroy, (Event->ZeEvent));
44204433

44214434
auto Context = Event->Context;
4422-
if (auto Res = Context->decrementAliveEventsInPool(Event->ZeEventPool))
4435+
if (auto Res = Context->decrementUnreleasedEventsInPool(Event))
44234436
return Res;
44244437

44254438
// We intentionally incremented the reference counter when an event is

sycl/plugins/level_zero/pi_level_zero.hpp

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -328,7 +328,7 @@ struct _pi_context : _pi_object {
328328
: ZeContext{ZeContext},
329329
OwnZeContext{OwnZeContext}, Devices{Devs, Devs + NumDevices},
330330
ZeCommandListInit{nullptr}, ZeEventPool{nullptr},
331-
NumEventsAvailableInEventPool{}, NumEventsLiveInEventPool{} {
331+
NumEventsAvailableInEventPool{}, NumEventsUnreleasedInEventPool{} {
332332
// Create USM allocator context for each pair (device, context).
333333
for (uint32_t I = 0; I < NumDevices; I++) {
334334
pi_device Device = Devs[I];
@@ -429,8 +429,8 @@ struct _pi_context : _pi_object {
429429
pi_result getFreeSlotInExistingOrNewPool(ze_event_pool_handle_t &, size_t &);
430430

431431
// If event is destroyed then decrement number of events living in the pool
432-
// and destroy the pool if there are no alive events.
433-
pi_result decrementAliveEventsInPool(ze_event_pool_handle_t pool);
432+
// and destroy the pool if there are no unreleased events.
433+
pi_result decrementUnreleasedEventsInPool(pi_event Event);
434434

435435
// Store USM allocator context(internal allocator structures)
436436
// for USM shared and device allocations. There is 1 allocator context
@@ -459,21 +459,21 @@ struct _pi_context : _pi_object {
459459
// by storing number of empty slots available in the pool.
460460
std::unordered_map<ze_event_pool_handle_t, pi_uint32>
461461
NumEventsAvailableInEventPool;
462-
// This map will be used to determine number of live events in the pool.
463-
// We use separate maps for number of event slots available in the pool.
464-
// number of events live in the pool live.
462+
// This map will be used to determine number of unreleased events in the pool.
463+
// We use separate maps for number of event slots available in the pool from
464+
// the number of events unreleased in the pool.
465465
// This will help when we try to make the code thread-safe.
466466
std::unordered_map<ze_event_pool_handle_t, pi_uint32>
467-
NumEventsLiveInEventPool;
467+
NumEventsUnreleasedInEventPool;
468468

469469
// TODO: we'd like to create a thread safe map class instead of mutex + map,
470470
// that must be carefully used together.
471471

472472
// Mutex to control operations on NumEventsAvailableInEventPool map.
473473
std::mutex NumEventsAvailableInEventPoolMutex;
474474

475-
// Mutex to control operations on NumEventsLiveInEventPool.
476-
std::mutex NumEventsLiveInEventPoolMutex;
475+
// Mutex to control operations on NumEventsUnreleasedInEventPool.
476+
std::mutex NumEventsUnreleasedInEventPoolMutex;
477477
};
478478

479479
// If doing dynamic batching, start batch size at 4.

0 commit comments

Comments
 (0)