-
Notifications
You must be signed in to change notification settings - Fork 38
/
event.jl
324 lines (279 loc) · 9.46 KB
/
event.jl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
# OpenCL.Event
abstract CLEvent <: CLObject
type Event <: CLEvent
id :: CL_event
function Event(evt_id::CL_event; retain=false)
if retain
@check api.clRetainEvent(evt_id)
end
evt = new(evt_id)
finalizer(evt, _finalize)
return evt
end
end
# wait for completion before running finalizer
type NannyEvent <: CLEvent
id::CL_event
obj::Any
function NannyEvent(evt_id::CL_event, obj::Any; retain=false)
if retain
@check api.clRetainEvent(evt_id)
end
nanny_evt = new(evt_id, obj)
finalizer(nanny_evt, x -> begin
x.id != C_NULL && wait(x)
x.obj = nothing
_finalize(x)
end)
nanny_evt
end
end
function _finalize(evt::CLEvent)
if evt.id != C_NULL
@check api.clReleaseEvent(evt.id)
evt.id = C_NULL
end
end
NannyEvent(evt::Event, obj::Any; retain=false) = NannyEvent(evt.id, obj, retain=retain)
Base.pointer(evt::CLEvent) = evt.id
function Base.show(io::IO, evt::Event)
ptr_val = convert(UInt, Base.pointer(evt))
ptr_address = "0x$(hex(ptr_val, WORD_SIZE>>2))"
print(io, "OpenCL.Event(@$ptr_address)")
end
Base.getindex(evt::CLEvent, evt_info::Symbol) = info(evt, evt_info)
@ocl_v1_1_only begin
type UserEvent <: CLEvent
id :: CL_event
function UserEvent(evt_id::CL_event, retain=false)
if retain
@check api.clRetainEvent(evt_id)
end
evt = new(evt_id)
finalizer(evt, _finalize)
return evt
end
end
function UserEvent(ctx::Context; retain=false)
status = Array(CL_int, 1)
evt_id = api.clCreateUserEvent(ctx.id, status)
if status[1] != CL_SUCCESS
throw(CLError(status[1]))
end
try
return UserEvent(evt_id, retain)
catch err
@check api.clReleaseEvent(evt_id)
throw(err)
end
end
function Base.show(io::IO, evt::UserEvent)
ptr_val = convert(UInt, Base.pointer(evt))
ptr_address = "0x$(hex(ptr_val, WORD_SIZE>>2))"
print(io, "OpenCL.UserEvent(@$ptr_address)")
end
function complete(evt::UserEvent)
@check api.clSetUserEventStatus(evt.id, CL_COMPLETE)
return evt
end
end
immutable _EventCB
handle :: Ptr{Void}
evt_id :: CL_event
status :: CL_int
end
function event_notify(evt_id::CL_event, status::CL_int, payload::Ptr{Void})
ptr = convert(Ptr{_EventCB}, payload)
handle = unsafe_load(ptr, 1).handle
val = _EventCB(handle, evt_id, status)
unsafe_store!(ptr, val, 1)
# Use uv_async_send to notify the main thread
ccall(:uv_async_send, Void, (Ptr{Void},), handle)
nothing
end
function add_callback(evt::CLEvent, callback::Function)
event_notify_ptr = cfunction(event_notify, Void,
(CL_event, CL_int, Ptr{Void}))
# The uv_callback is going to notify a task that,
# then executes the real callback.
cond = Condition()
cb = Base.SingleAsyncWork(data -> notify(cond))
# Storing the results of our c_callback needs to be
# isbits && isimmutable
r_ecb = Ref(_EventCB(cb.handle, 0, 0))
@check api.clSetEventCallback(evt.id, CL_COMPLETE, event_notify_ptr, r_ecb)
@async begin
try
Base.wait(cond)
ecb = r_ecb[]
callback(ecb.evt_id, ecb.status)
catch
rethrow()
finally
Base.close(cb)
end
end
end
function wait(evt::CLEvent)
evt_id = [evt.id]
@check api.clWaitForEvents(cl_uint(1), pointer(evt_id))
return evt
end
function wait(evts::Vector{CLEvent})
evt_ids = [evt.id for evt in evts]
if !isempty(evt_ids)
nevents = cl_uint(length(evt_ids))
@check api.clWaitForEvents(nevents, pointer(evt_ids))
end
return evts
end
@ocl_v1_2_only begin
function enqueue_marker_with_wait_list(q::CmdQueue,
wait_for::Vector{CLEvent})
n_wait_events = cl_uint(length(wait_for))
wait_evt_ids = [evt.id for evt in wait_for]
ret_evt = Array(CL_event, 1)
@check api.clEnqueueMarkerWithWaitList(q.id, n_wait_events,
isempty(wait_evt_ids)? C_NULL : wait_evt_ids,
ret_evt)
@return_event ret_evt[1]
end
function enqueue_barrier_with_wait_list(q::CmdQueue,
wait_for::Vector{CLEvent})
n_wait_events = cl_uint(length(wait_for))
wait_evt_ids = [evt.id for evt in wait_for]
ret_evt = Array(CL_event, 1)
@check api.clEnqueueBarrierWithWaitList(q.id, n_wait_events,
isempty(wait_evt_ids)? C_NULL : wait_evt_ids,
ret_evt)
@return_event ret_evt[1]
end
end
function enqueue_marker(q::CmdQueue)
evt = Array(CL_event, 1)
@check api.clEnqueueMarker(q.id, evt)
@return_event evt[1]
end
@deprecate enqueue_marker enqueue_marker_with_wait_list
function enqueue_wait_for_events{T<:CLEvent}(q::CmdQueue, wait_for::Vector{T})
n_wait_events = cl_uint(length(wait_for))
wait_evt_ids = [evt.id for evt in wait_for]
@check api.clEnqueueWaitForEvents(q.id, n_wait_events,
isempty(wait_evt_ids) ? C_NULL : pointer(wait_evt_ids))
end
function enqueue_wait_for_events(q::CmdQueue, wait_for::CLEvent)
enqueue_wait_for_events(q, [wait_for])
end
function enqueue_barrier(q::CmdQueue)
@check api.clEnqueueBarrier(q.id)
return q
end
@deprecate enqueue_barrier enqueue_barrier_with_wait_list
cl_event_status(s::Symbol) = begin
if s == :queued
return CL_QUEUED
elseif s == :submitted
return CL_SUBMITTED
elseif s == :running
return CL_RUNNING
elseif s == :complete
return CL_COMPLETE
else
throw(ArgumentError("unrecognized status symbol :$s"))
end
end
macro profile_info(func, profile_info)
quote
function $(esc(func))(evt::CLEvent)
time = CL_long[0]
err_code = api.clGetEventProfilingInfo(evt.id, $profile_info,
sizeof(CL_ulong), time, C_NULL)
if err_code != CL_SUCCESS
if err_code == CL_PROFILING_INFO_NOT_AVAILABLE
if evt[:status] != :complete
#TODO: evt must have status complete before it can be profiled
throw(CLError(err_code))
else
#TODO: queue must be created with :profile argument
throw(CLError(err_code))
end
end
throw(CLError(err_code))
end
return time[1]
end
end
end
let command_queue(evt::CLEvent) = begin
cmd_q = Array(CL_command_queue, 1)
@check api.clGetEventInfo(evt.id, CL_EVENT_COMMAND_QUEUE,
sizeof(CL_command_queue), cmd_q, C_NULL)
return CmdQueue(cmd_q[1])
end
command_type(evt::CLEvent) = begin
cmd_t = Array(CL_int , 1)
@check api.clGetEventInfo(evt.id, CL_EVENT_COMMAND_TYPE,
sizeof(CL_int), cmd_t, C_NULL)
return cmd_t[1]
end
reference_count(evt::CLEvent) = begin
cnt = Array(CL_uint, 1)
@check api.clGetEventInfo(evt.id, CL_EVENT_REFERENCE_COUNT,
sizeof(CL_uint), cnt, C_NULL)
return cnt[1]
end
context(evt::CLEvent) = begin
ctx = Array(CL_context, 1)
@check api.clGetEventInfo(evt.id, CL_EVENT_CONTEXT,
sizeof(CL_context), CL_context, C_NULL)
Context(ctx[1])
end
status(evt::CLEvent) = begin
st = Array(CL_int, 1)
@check api.clGetEventInfo(evt.id, CL_EVENT_COMMAND_EXECUTION_STATUS,
sizeof(CL_int), st, C_NULL)
status = st[1]
if status == CL_QUEUED
return :queued
elseif status == CL_SUBMITTED
return :submitted
elseif status == CL_RUNNING
return :running
elseif status == CL_COMPLETE
return :complete
else
throw(ArgumentError("Unknown status value: $status"))
end
end
@profile_info(profile_start, CL_PROFILING_COMMAND_START)
@profile_info(profile_end, CL_PROFILING_COMMAND_END)
@profile_info(profile_queued, CL_PROFILING_COMMAND_QUEUED)
@profile_info(profile_submit, CL_PROFILING_COMMAND_SUBMIT)
profile_duration(evt::Event) = begin
return evt[:profile_end] - evt[:profile_start]
end
const info_map = Dict{Symbol, Function}(
:context => context,
:command_queue => command_queue,
:reference_count => reference_count,
:command_type => command_type,
:status => status,
:profile_start => profile_start,
:profile_end => profile_end,
:profile_queued => profile_queued,
:profile_submit => profile_submit,
:profile_duration => profile_duration,
)
function info(evt::CLEvent, evt_info::Symbol)
try
func = info_map[evt_info]
func(evt)
catch err
if isa(err, KeyError)
throw(ArgumentError("OpenCL.Event has no info for: $evt_info"))
else
throw(err)
end
end
end
end