Skip to content

Commit 0f8914b

Browse files
[SYCL] move more common utils from pi_level_zero (#7821)
to UR. --------- Co-authored-by: smaslov-intel <[email protected]>
1 parent 6ee5a60 commit 0f8914b

File tree

2 files changed

+96
-95
lines changed

2 files changed

+96
-95
lines changed

sycl/plugins/level_zero/pi_level_zero.hpp

Lines changed: 0 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -64,101 +64,6 @@ template <> uint32_t inline pi_cast(uint64_t Value) {
6464
return CastedValue;
6565
}
6666

67-
// The wrapper for immutable Level-Zero data.
68-
// The data is initialized only once at first access (via ->) with the
69-
// initialization function provided in Init. All subsequent access to
70-
// the data just returns the already stored data.
71-
//
72-
template <class T> struct ZeCache : private T {
73-
// The initialization function takes a reference to the data
74-
// it is going to initialize, since it is private here in
75-
// order to disallow access other than through "->".
76-
//
77-
using InitFunctionType = std::function<void(T &)>;
78-
InitFunctionType Compute{nullptr};
79-
bool Computed{false};
80-
pi_mutex ZeCacheMutex;
81-
82-
ZeCache() : T{} {}
83-
84-
// Access to the fields of the original T data structure.
85-
T *operator->() {
86-
std::unique_lock<pi_mutex> Lock(ZeCacheMutex);
87-
if (!Computed) {
88-
Compute(*this);
89-
Computed = true;
90-
}
91-
return this;
92-
}
93-
};
94-
95-
// This wrapper around std::atomic is created to limit operations with reference
96-
// counter and to make allowed operations more transparent in terms of
97-
// thread-safety in the plugin. increment() and load() operations do not need a
98-
// mutex guard around them since the underlying data is already atomic.
99-
// decrementAndTest() method is used to guard a code which needs to be
100-
// executed when object's ref count becomes zero after release. This method also
101-
// doesn't need a mutex guard because decrement operation is atomic and only one
102-
// thread can reach ref count equal to zero, i.e. only a single thread can pass
103-
// through this check.
104-
struct ReferenceCounter {
105-
ReferenceCounter() : RefCount{1} {}
106-
107-
// Reset the counter to the initial value.
108-
void reset() { RefCount = 1; }
109-
110-
// Used when retaining an object.
111-
void increment() { RefCount++; }
112-
113-
// Supposed to be used in pi*GetInfo* methods where ref count value is
114-
// requested.
115-
pi_uint32 load() { return RefCount.load(); }
116-
117-
// This method allows to guard a code which needs to be executed when object's
118-
// ref count becomes zero after release. It is important to notice that only a
119-
// single thread can pass through this check. This is true because of several
120-
// reasons:
121-
// 1. Decrement operation is executed atomically.
122-
// 2. It is not allowed to retain an object after its refcount reaches zero.
123-
// 3. It is not allowed to release an object more times than the value of
124-
// the ref count.
125-
// 2. and 3. basically means that we can't use an object at all as soon as its
126-
// refcount reaches zero. Using this check guarantees that code for deleting
127-
// an object and releasing its resources is executed once by a single thread
128-
// and we don't need to use any mutexes to guard access to this object in the
129-
// scope after this check. Of course if we access another objects in this code
130-
// (not the one which is being deleted) then access to these objects must be
131-
// guarded, for example with a mutex.
132-
bool decrementAndTest() { return --RefCount == 0; }
133-
134-
private:
135-
std::atomic<pi_uint32> RefCount;
136-
};
137-
138-
// Base class to store common data
139-
struct _pi_object {
140-
_pi_object() : RefCount{} {}
141-
142-
// Level Zero doesn't do the reference counting, so we have to do.
143-
// Must be atomic to prevent data race when incrementing/decrementing.
144-
ReferenceCounter RefCount;
145-
146-
// This mutex protects accesses to all the non-const member variables.
147-
// Exclusive access is required to modify any of these members.
148-
//
149-
// To get shared access to the object in a scope use std::shared_lock:
150-
// std::shared_lock Lock(Obj->Mutex);
151-
// To get exclusive access to the object in a scope use std::scoped_lock:
152-
// std::scoped_lock Lock(Obj->Mutex);
153-
//
154-
// If several pi objects are accessed in a scope then each object's mutex must
155-
// be locked. For example, to get write access to Obj1 and Obj2 and read
156-
// access to Obj3 in a scope use the following approach:
157-
// std::shared_lock Obj3Lock(Obj3->Mutex, std::defer_lock);
158-
// std::scoped_lock LockAll(Obj1->Mutex, Obj2->Mutex, Obj3Lock);
159-
pi_shared_mutex Mutex;
160-
};
161-
16267
// Record for a memory allocation. This structure is used to keep information
16368
// for each memory allocation.
16469
struct MemAllocRecord : _pi_object {

sycl/plugins/unified_runtime/ur/ur.hpp

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@
88
#pragma once
99

1010
#include <atomic>
11+
#include <cstdint>
1112
#include <iostream>
13+
#include <functional>
1214
#include <mutex>
1315
#include <shared_mutex>
1416
#include <string>
@@ -100,6 +102,100 @@ class SpinLock {
100102
std::atomic_flag MLock = ATOMIC_FLAG_INIT;
101103
};
102104

105+
// The wrapper for immutable data.
106+
// The data is initialized only once at first access (via ->) with the
107+
// initialization function provided in Init. All subsequent access to
108+
// the data just returns the already stored data.
109+
//
110+
template <class T> struct ZeCache : private T {
111+
// The initialization function takes a reference to the data
112+
// it is going to initialize, since it is private here in
113+
// order to disallow access other than through "->".
114+
//
115+
using InitFunctionType = std::function<void(T &)>;
116+
InitFunctionType Compute{nullptr};
117+
bool Computed{false};
118+
pi_mutex ZeCacheMutex;
119+
120+
ZeCache() : T{} {}
121+
122+
// Access to the fields of the original T data structure.
123+
T *operator->() {
124+
std::unique_lock<pi_mutex> Lock(ZeCacheMutex);
125+
if (!Computed) {
126+
Compute(*this);
127+
Computed = true;
128+
}
129+
return this;
130+
}
131+
};
132+
133+
// This wrapper around std::atomic is created to limit operations with reference
134+
// counter and to make allowed operations more transparent in terms of
135+
// thread-safety in the plugin. increment() and load() operations do not need a
136+
// mutex guard around them since the underlying data is already atomic.
137+
// decrementAndTest() method is used to guard a code which needs to be
138+
// executed when object's ref count becomes zero after release. This method also
139+
// doesn't need a mutex guard because decrement operation is atomic and only one
140+
// thread can reach ref count equal to zero, i.e. only a single thread can pass
141+
// through this check.
142+
struct ReferenceCounter {
143+
ReferenceCounter() : RefCount{1} {}
144+
145+
// Reset the counter to the initial value.
146+
void reset() { RefCount = 1; }
147+
148+
// Used when retaining an object.
149+
void increment() { RefCount++; }
150+
151+
// Supposed to be used in pi*GetInfo* methods where ref count value is
152+
// requested.
153+
uint32_t load() { return RefCount.load(); }
154+
155+
// This method allows to guard a code which needs to be executed when object's
156+
// ref count becomes zero after release. It is important to notice that only a
157+
// single thread can pass through this check. This is true because of several
158+
// reasons:
159+
// 1. Decrement operation is executed atomically.
160+
// 2. It is not allowed to retain an object after its refcount reaches zero.
161+
// 3. It is not allowed to release an object more times than the value of
162+
// the ref count.
163+
// 2. and 3. basically means that we can't use an object at all as soon as its
164+
// refcount reaches zero. Using this check guarantees that code for deleting
165+
// an object and releasing its resources is executed once by a single thread
166+
// and we don't need to use any mutexes to guard access to this object in the
167+
// scope after this check. Of course if we access another objects in this code
168+
// (not the one which is being deleted) then access to these objects must be
169+
// guarded, for example with a mutex.
170+
bool decrementAndTest() { return --RefCount == 0; }
171+
172+
private:
173+
std::atomic<uint32_t> RefCount;
174+
};
175+
176+
// Base class to store common data
177+
struct _pi_object {
178+
_pi_object() : RefCount{} {}
179+
180+
// Must be atomic to prevent data race when incrementing/decrementing.
181+
ReferenceCounter RefCount;
182+
183+
// This mutex protects accesses to all the non-const member variables.
184+
// Exclusive access is required to modify any of these members.
185+
//
186+
// To get shared access to the object in a scope use std::shared_lock:
187+
// std::shared_lock Lock(Obj->Mutex);
188+
// To get exclusive access to the object in a scope use std::scoped_lock:
189+
// std::scoped_lock Lock(Obj->Mutex);
190+
//
191+
// If several pi objects are accessed in a scope then each object's mutex must
192+
// be locked. For example, to get write access to Obj1 and Obj2 and read
193+
// access to Obj3 in a scope use the following approach:
194+
// std::shared_lock Obj3Lock(Obj3->Mutex, std::defer_lock);
195+
// std::scoped_lock LockAll(Obj1->Mutex, Obj2->Mutex, Obj3Lock);
196+
pi_shared_mutex Mutex;
197+
};
198+
103199
// Helper for one-liner validation
104200
#define PI_ASSERT(condition, error) \
105201
if (!(condition)) \

0 commit comments

Comments
 (0)