001/*
002 * JavaCL - Java API and utilities for OpenCL
003 * http://javacl.googlecode.com/
004 *
005 * Copyright (c) 2009-2013, Olivier Chafik (http://ochafik.com/)
006 * All rights reserved.
007 *
008 * Redistribution and use in source and binary forms, with or without
009 * modification, are permitted provided that the following conditions are met:
010 * 
011 *     * Redistributions of source code must retain the above copyright
012 *       notice, this list of conditions and the following disclaimer.
013 *     * Redistributions in binary form must reproduce the above copyright
014 *       notice, this list of conditions and the following disclaimer in the
015 *       documentation and/or other materials provided with the distribution.
016 *     * Neither the name of Olivier Chafik nor the
017 *       names of its contributors may be used to endorse or promote products
018 *       derived from this software without specific prior written permission.
019 * 
020 * THIS SOFTWARE IS PROVIDED BY OLIVIER CHAFIK AND CONTRIBUTORS ``AS IS'' AND ANY
021 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
022 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
023 * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
024 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
025 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
026 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
027 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
028 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
029 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
030 */
031        
032
033
034
035
036        
037
038        
039        
040
041
042
043        
044
045        
046        
047        
048
049        
050package com.nativelibs4java.opencl;
051import static com.nativelibs4java.opencl.CLException.error;
052import static com.nativelibs4java.opencl.JavaCL.CL;
053import static com.nativelibs4java.opencl.library.OpenCLLibrary.*;
054import static com.nativelibs4java.opencl.library.IOpenCLLibrary.*;
055
056import java.util.Arrays;
057
058import com.nativelibs4java.opencl.library.OpenCLLibrary;
059import com.nativelibs4java.opencl.library.IOpenCLLibrary.cl_event;
060import com.nativelibs4java.util.EnumValue;
061import com.nativelibs4java.util.EnumValues;
062import org.bridj.*;
063import org.bridj.ann.Ptr;
064import static org.bridj.Pointer.*;
065import static com.nativelibs4java.opencl.proxy.PointerUtils.*;
066
067/**
068 * OpenCL event object.<br>
069 * Event objects can be used to refer to a kernel execution command (clEnqueueNDRangeKernel, clEnqueueTask, clEnqueueNativeKernel), or read, write, map and copy commands on memory objects (clEnqueue{Read|Write|Map}{Buffer|Image}, clEnqueueCopy{Buffer|Image}, clEnqueueCopyBufferToImage, or clEnqueueCopyImageToBuffer).<br>
070 * An event object can be used to track the execution status of a command. <br>
071 * The API calls that enqueue commands to a command-queue create a new event object that is returned in the event argument. <br>
072 * In case of an error enqueuing the command in the command-queue the event argument does not return an event object.<br>
073 * The execution status of an enqueued command at any given point in time can be CL_QUEUED (command has been enqueued in the command-queue), CL_SUBMITTED (enqueued command has been submitted by the host to the device associated with the command-queue), CL_RUNNING (device is currently executing this command), CL_COMPLETE (command has successfully completed) or the appropriate error code if the command was abnormally terminated (this may be caused by a bad memory access etc.). <br>
074 * The error code returned by a terminated command is a negative integer value. <br>
075 * A command is considered to be complete if its execution status is CL_COMPLETE or is a negative integer value.<br>
076 * If the execution of a command is terminated, the command-queue associated with this terminated command, and the associated context (and all other command-queues in this context) may no longer be available. <br>
077 * The behavior of OpenCL API calls that use this context (and command-queues associated with this context) are now considered to be implementation- defined. <br>
078 * The user registered callback function specified when context is created can be used to report appropriate error information.<br>
079 * 
080 * @author ochafik
081 */
082public class CLEvent extends CLAbstractEntity {
083
084        /**
085         * Pass this to special value to any method that expects a variable number of events to wait for and that returns an event, to completely avoid returning the completion event (will return null instead of the event). 
086         */
087        public static final CLEvent FIRE_AND_FORGET = new CLEvent(null, -1);
088        
089                protected static CLInfoGetter infos = new CLInfoGetter() {
090                @Override
091                protected int getInfo(long entity, int infoTypeEnum, long size, Pointer out, Pointer<SizeT> sizeOut) {
092                        return CL.clGetEventInfo(entity, infoTypeEnum, size, getPeer(out), getPeer(sizeOut));
093                }
094        };
095
096                protected static CLInfoGetter profilingInfos = new CLInfoGetter() {
097                @Override
098                protected int getInfo(long entity, int infoTypeEnum, long size, Pointer out, Pointer<SizeT> sizeOut) {
099                        return CL.clGetEventProfilingInfo(entity, infoTypeEnum, size, getPeer(out), getPeer(sizeOut));
100                }
101        };
102        
103        private final CLQueue queue;
104        
105    CLEvent(CLQueue queue, long evt) {
106                super(evt, false);
107                this.queue = queue;
108        }
109        
110        public CLQueue getQueue() {
111                return queue;
112        }
113        
114    public interface EventCallback {
115        public void callback(int executionStatus);
116    }
117    
118    /**
119     * Registers a user callback function for the completion execution status (CL_COMPLETE). <br>
120     * @param callback
121     * @throws UnsupportedOperationException in OpenCL 1.0
122     * @since OpenCL 1.1
123     */
124    public void setCompletionCallback(final EventCallback callback) {
125        setCallback(CL_COMPLETE, callback);
126    }
127    
128        private static final clSetEventCallback_arg1_callback eventCallback = new clSetEventCallback_arg1_callback() {
129                public void apply(@Ptr long evt, int executionStatus, @Ptr long callbackPeer) {
130                        EventCallback callback = (EventCallback)JNI.refToObject(callbackPeer);
131                        try {
132                                callback.callback(executionStatus);
133                        } finally {
134                                JNI.deleteGlobalRef(callbackPeer);
135                        }
136                }
137        };
138        private static final long eventCallbackPeer = getPeer(getPointer(eventCallback));
139        
140    /**
141  * Calls <a href="http://www.khronos.org/registry/cl/sdk/1.2/docs/man/xhtml/clSetEventCallback.html">clSetEventCallback</a>.<br>
142     * Registers a user callback function for a specific command execution status. <br>
143     * The registered callback function will be called when the execution status of command associated with event changes to the execution status specified by command_exec_status.
144     * @param commandExecStatus specifies the command execution status for which the callback is registered. The command execution callback values for which a callback can be registered are: CL_COMPLETE. There is no guarantee that the callback functions registered for various execution status values for an event will be called in the exact order that the execution status of a command changes.
145     * @param callback
146     * @throws UnsupportedOperationException in OpenCL 1.0
147     * @since OpenCL 1.1
148     */
149    public void setCallback(int commandExecStatus, final EventCallback callback) {
150                queue.getContext().getPlatform().requireMinVersionValue("clSetEventCallback", 1.1);
151        error(CL.clSetEventCallback(getEntity(), commandExecStatus, eventCallbackPeer, JNI.newGlobalRef(callback)));
152    }
153    
154    static CLEvent createEvent(final CLQueue queue, long evt) {
155                return createEvent(queue, evt, false);
156    }
157    
158        static CLEvent createEvent(final CLQueue queue, long evt, boolean isUserEvent) {
159                if (evt == 0)
160                        return null;
161
162        return isUserEvent ? 
163                        new CLUserEvent(queue, evt) : 
164                        new CLEvent(queue, evt);
165        }
166
167    static CLEvent createEventFromPointer(CLQueue queue, Pointer<cl_event> evt1) {
168                if (evt1 == null)
169                        return null;
170        
171        long peer = evt1.getSizeT();
172        if (peer == 0)
173            return null;
174        
175        return new CLEvent(queue, peer);
176        }
177
178
179        /**
180         * Wait for this event, blocking the caller thread independently of any queue until all of the command associated with this events completes.
181         */
182        public void waitFor() {
183                waitFor(this);
184        }
185
186        /**
187  * Calls <a href="http://www.khronos.org/registry/cl/sdk/1.2/docs/man/xhtml/clWaitForEvents.html">clWaitForEvents</a>.<br>
188         * Wait for events, blocking the caller thread independently of any queue until all of the commands associated with the events completed.
189         * @param eventsToWaitFor List of events which completion is to be waited for
190         */
191        public static void waitFor(CLEvent... eventsToWaitFor) {
192                if (eventsToWaitFor.length == 0)
193                        return;
194                
195                try {
196                                ReusablePointers ptrs = ReusablePointers.get();
197                                int[] eventsInCount = ptrs.int1Array;
198        Pointer<cl_event> eventsIn = 
199                CLAbstractEntity.copyNonNullEntities(eventsToWaitFor, eventsInCount, ptrs.events_in);
200            if (eventsIn == null)
201                return;
202            error(CL.clWaitForEvents( eventsInCount[0], getPeer(eventsIn) ));
203                } catch (Exception ex) {
204                        throw new RuntimeException("Exception while waiting for events " + Arrays.asList(eventsToWaitFor), ex);
205                }
206        }
207
208        /**
209         * Invoke an action in a separate thread only after completion of the command associated with this event.<br>
210         * Returns immediately.
211         * @param action an action to be ran
212         * @throws IllegalArgumentException if action is null
213         */
214        public void invokeUponCompletion(final Runnable action) {
215                invokeUponCompletion(action, this);
216        }
217
218        /**
219         * Invoke an action in a separate thread only after completion of all of the commands associated with the specified events.<br>
220         * Returns immediately.
221         * @param action an action to be ran
222         * @param eventsToWaitFor list of events which commands's completion should be waited for before the action is ran
223         * @throws IllegalArgumentException if action is null
224         */
225        public static void invokeUponCompletion(final Runnable action, final CLEvent... eventsToWaitFor) {
226                if (action == null)
227                        throw new IllegalArgumentException("Null action !");
228
229                new Thread() {
230                        public void run() {
231                                waitFor(eventsToWaitFor);
232                                action.run();
233                        }
234                }.start();
235        }
236
237        static boolean containsFireAndForget(CLEvent[] eventsToWaitFor) {
238                for (int i = eventsToWaitFor.length; i-- != 0;) {
239                        if (eventsToWaitFor[i] == FIRE_AND_FORGET)
240                                return true;
241                }
242                return false;
243    }
244    
245        @Override
246        protected void clear() {
247                error(CL.clReleaseEvent(getEntity()));
248        }
249
250        /** Values for CL_EVENT_COMMAND_EXECUTION_STATUS */
251        public enum CommandExecutionStatus implements com.nativelibs4java.util.ValuedEnum {
252                /** command has been enqueued in the command-queue                                             */ 
253                Queued(CL_QUEUED),
254                /** enqueued command has been submitted by the host to the device associated with the command-queue */ 
255                Submitted(CL_SUBMITTED),
256                /** device is currently executing this command */ 
257                Running(CL_RUNNING),
258                /** the command has completed */ 
259                Complete(CL_COMPLETE);
260                
261                CommandExecutionStatus(long value) { this.value = value; }
262                long value;
263                @Override
264                public long value() { return value; }
265                public static CommandExecutionStatus getEnum(long v) { return EnumValues.getEnum(v, CommandExecutionStatus.class); }
266        }
267        
268        /**
269         * Return the execution status of the command identified by event.  <br>
270         * @throws CLException is the execution status denotes an error
271         */
272        public CommandExecutionStatus getCommandExecutionStatus() {
273                int v = infos.getInt(getEntity(), CL_EVENT_COMMAND_EXECUTION_STATUS);
274                CommandExecutionStatus status =  CommandExecutionStatus.getEnum(v);
275                if (status == null)
276                        error(v);
277                return status;
278        }
279        /**
280         * Return the execution status of the command identified by event.  <br>
281         * @throws CLException is the execution status denotes an error
282         */
283        @InfoName("CL_EVENT_COMMAND_EXECUTION_STATUS")
284        public int getCommandExecutionStatusValue() {
285                return infos.getInt(getEntity(), CL_EVENT_COMMAND_EXECUTION_STATUS);
286        }
287
288        /** Values for CL_EVENT_COMMAND_TYPE */
289        public enum CommandType implements com.nativelibs4java.util.ValuedEnum {
290                NDRangeKernel(CL_COMMAND_NDRANGE_KERNEL),
291                Task(CL_COMMAND_TASK),
292                NativeKernel(CL_COMMAND_NATIVE_KERNEL),
293                ReadBuffer(CL_COMMAND_READ_BUFFER),
294                WriteBuffer(CL_COMMAND_WRITE_BUFFER),
295                CopyBuffer(CL_COMMAND_COPY_BUFFER),
296                ReadImage(CL_COMMAND_READ_IMAGE),
297                WriteImage(CL_COMMAND_WRITE_IMAGE),
298                CopyImage(CL_COMMAND_COPY_IMAGE),
299                CopyBufferToImage(CL_COMMAND_COPY_BUFFER_TO_IMAGE),
300                CopyImageToBuffer(CL_COMMAND_COPY_IMAGE_TO_BUFFER),
301                MapBuffer(CL_COMMAND_MAP_BUFFER),
302                CommandMapImage(CL_COMMAND_MAP_IMAGE),
303                UnmapMemObject(CL_COMMAND_UNMAP_MEM_OBJECT),             
304                Marker(CL_COMMAND_MARKER),             
305                AcquireGLObjects(CL_COMMAND_ACQUIRE_GL_OBJECTS),             
306                ReleaseGLObjects(CL_COMMAND_RELEASE_GL_OBJECTS);
307
308                CommandType(long value) { this.value = value; }
309                long value;
310                @Override
311                public long value() { return value; }
312                public static CommandType getEnum(long v) { return EnumValues.getEnum(v, CommandType.class); }
313        }
314
315        /**
316         * Return the command associated with event.
317         */
318        @InfoName("CL_EVENT_COMMAND_TYPE")
319        public CommandType getCommandType() {
320                return CommandType.getEnum(infos.getInt(getEntity(), CL_EVENT_COMMAND_TYPE));
321        }
322
323
324        /**
325         * A 64-bit value that describes the current device time counter in nanoseconds when the command identified by event is enqueued in a command-queue by the host.
326         */
327        @InfoName("CL_CL_PROFILING_COMMAND_QUEUED")
328        public long getProfilingCommandQueued() {
329                return profilingInfos.getIntOrLong(getEntity(), CL_PROFILING_COMMAND_QUEUED);
330        }
331
332        /**
333         * A 64-bit value that describes the current device time counter in nanoseconds when the command identified by event that has been enqueued is submitted by the host to the device associated with the command- queue.
334         */
335        @InfoName("CL_CL_PROFILING_COMMAND_SUBMIT")
336        public long getProfilingCommandSubmit() {
337                return profilingInfos.getIntOrLong(getEntity(), CL_PROFILING_COMMAND_SUBMIT);
338        }
339
340        /**
341         * A 64-bit value that describes the current device time counter in nanoseconds when the command identified by event starts execution on the device.
342         */
343        @InfoName("CL_CL_PROFILING_COMMAND_START")
344        public long getProfilingCommandStart() {
345                return profilingInfos.getIntOrLong(getEntity(), CL_PROFILING_COMMAND_START);
346        }
347
348        /**
349         * A 64-bit value that describes the current device time counter in nanoseconds when the command identified by event has finished execution on the device.
350         */
351        @InfoName("CL_CL_PROFILING_COMMAND_END")
352        public long getProfilingCommandEnd() {
353                return profilingInfos.getIntOrLong(getEntity(), CL_PROFILING_COMMAND_END);
354        }
355
356
357        @Override
358        public String toString() {
359                return "Event {commandType: " + getCommandType() + "}";
360        }
361}