Skip to content

Commit dcf3dba

Browse files
committed
[SYCL] move more common utils from pi_level_zero
to UR.
1 parent 22610c6 commit dcf3dba

File tree

2 files changed

+95
-95
lines changed

2 files changed

+95
-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
@@ -65,101 +65,6 @@ template <> uint32_t inline pi_cast(uint64_t Value) {
6565
return CastedValue;
6666
}
6767

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

sycl/plugins/unified_runtime/ur/ur.hpp

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,101 @@ class SpinLock {
9999
std::atomic_flag MLock = ATOMIC_FLAG_INIT;
100100
};
101101

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

0 commit comments

Comments
 (0)